Budibase is an open-source low-code platform for creating internal apps in minutes. Supports PostgreSQL, MySQL, MSSQL, MongoDB, Rest API, Docker, K8s 🚀
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

194 lines
5.6 KiB

import { diffHierarchy, HierarchyChangeTypes } from "./diffHierarchy"
import { $, switchCase } from "../common"
import {
differenceBy,
isEqual,
some,
map,
filter,
uniqBy,
flatten
} from "lodash/fp"
import {
findRoot,
getDependantIndexes,
isTopLevelRecord,
isAncestorIndex
} from "./hierarchy"
import { generateSchema } from "../indexing/indexSchemaCreator"
import { _buildIndex } from "../indexApi/buildIndex"
import { constructHierarchy } from "./createNodes"
import { deleteAllRecordsForNode } from "./deleteAllRecordsForNode"
import { deleteAllIndexFilesForNode } from "./deleteAllIndexFilesForNode"
import { cloneApp } from "../appInitialise/cloneApp"
import { initialiseData } from "../appInitialise/initialiseData"
import { initialiseChildrenForNode } from "../recordApi/initialiseChildren"
import { initialiseNewIndex } from "./initialiseNewIndex"
import { saveApplicationHierarchy } from "../templateApi/saveApplicationHierarchy"
export const upgradeData = app => async newHierarchy => {
const diff = diffHierarchy(app.hierarchy, newHierarchy)
const changeActions = gatherChangeActions(diff)
if (changeActions.length === 0) return
newHierarchy = constructHierarchy(newHierarchy)
const newApp = newHierarchy && cloneApp(app, {
hierarchy: newHierarchy
})
await doUpgrade(app, newApp, changeActions)
await saveApplicationHierarchy(newApp)(newHierarchy)
}
const gatherChangeActions = (diff) =>
$(diff, [
map(actionForChange),
flatten,
uniqBy(a => a.compareKey)
])
const doUpgrade = async (oldApp, newApp, changeActions) => {
for(let action of changeActions) {
await action.run(oldApp, newApp, action.diff)
}
}
const actionForChange = diff =>
switchCase(
[isChangeType(HierarchyChangeTypes.recordCreated), recordCreatedAction],
[isChangeType(HierarchyChangeTypes.recordDeleted), deleteRecordsAction],
[
isChangeType(HierarchyChangeTypes.recordFieldsChanged),
rebuildAffectedIndexesAction
],
[isChangeType(HierarchyChangeTypes.recordRenamed), renameRecordAction],
[
isChangeType(HierarchyChangeTypes.recordEstimatedRecordTypeChanged),
reshardRecordsAction
],
[isChangeType(HierarchyChangeTypes.indexCreated), newIndexAction],
[isChangeType(HierarchyChangeTypes.indexDeleted), deleteIndexAction],
[isChangeType(HierarchyChangeTypes.indexChanged), rebuildIndexAction],
)(diff)
const isChangeType = changeType => change =>
change.type === changeType
const action = (diff, compareKey, run) => ({
diff,
compareKey,
run,
})
const reshardRecordsAction = diff =>
[action(diff, `reshardRecords-${diff.oldNode.nodeKey()}`, runReshardRecords)]
const rebuildIndexAction = diff =>
[action(diff, `rebuildIndex-${diff.newNode.nodeKey()}`, runRebuildIndex)]
const newIndexAction = diff => {
if (isAncestorIndex(diff.newNode)) {
return [action(diff, `rebuildIndex-${diff.newNode.nodeKey()}`, runRebuildIndex)]
} else {
return [action(diff, `newIndex-${diff.newNode.nodeKey()}`, runNewIndex)]
}
}
const deleteIndexAction = diff =>
[action(diff, `deleteIndex-${diff.oldNode.nodeKey()}`, runDeleteIndex)]
const deleteRecordsAction = diff =>
[action(diff, `deleteRecords-${diff.oldNode.nodeKey()}`, runDeleteRecords)]
const renameRecordAction = diff =>
[action(diff, `renameRecords-${diff.oldNode.nodeKey()}`, runRenameRecord)]
const recordCreatedAction = diff => {
if (isTopLevelRecord(diff.newNode)) {
return [action(diff, `initialiseRoot`, runInitialiseRoot)]
}
return [action(diff, `initialiseChildRecord-${diff.newNode.nodeKey()}`, runInitialiseChildRecord)]
}
const rebuildAffectedIndexesAction = diff =>{
const newHierarchy = findRoot(diff.newNode)
const oldHierarchy = findRoot(diff.oldNode)
const indexes = getDependantIndexes(newHierarchy, diff.newNode)
const changedFields = (() => {
const addedFields = differenceBy(f => f.name)
(diff.oldNode.fields)
(diff.newNode.fields)
const removedFields = differenceBy(f => f.name)
(diff.newNode.fields)
(diff.oldNode.fields)
return map(f => f.name)([...addedFields, ...removedFields])
})()
const isIndexAffected = i => {
if (!isEqual(
generateSchema(oldHierarchy, i),
generateSchema(newHierarchy, i))) return true
if (some(f => indexes.filter.indexOf(`record.${f}`) > -1)(changedFields))
return true
if (some(f => indexes.getShardName.indexOf(`record.${f}`) > -1)(changedFields))
return true
return false
}
return $(indexes, [
filter(isIndexAffected),
map(i => action({ newNode:i }, `rebuildIndex-${i.nodeKey()}`, runRebuildIndex))
])
}
const runReshardRecords = async change => {
throw new Error("Resharding of records is not supported yet")
}
const runRebuildIndex = async (_, newApp, diff) => {
await _buildIndex(newApp, diff.newNode.nodeKey())
}
const runDeleteIndex = async (oldApp, _, diff) => {
await deleteAllIndexFilesForNode(oldApp, diff.oldNode)
}
const runDeleteRecords = async (oldApp, _, diff) => {
await deleteAllRecordsForNode(oldApp, diff.oldNode)
}
const runNewIndex = async (_, newApp, diff) => {
await initialiseNewIndex(newApp, diff.newNode)
}
const runRenameRecord = change => {
/*
Going to disllow this in the builder. once a collection key is set... its done
*/
}
const runInitialiseRoot = async (_, newApp) => {
await initialiseData(newApp.datastore, newApp)
}
const runInitialiseChildRecord = async (_, newApp, diff) => {
await initialiseChildrenForNode(newApp.datastore, diff.newNode)
}