budibase/packages/core/src/templateApi/upgradeData.js

209 lines
5.7 KiB
JavaScript

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"
import { getApplicationDefinition } from "../templateApi/getApplicationDefinition"
export const upgradeData = app => async newHierarchy => {
const currentAppDef = await getApplicationDefinition(app.datastore)()
app.hierarchy = currentAppDef.hierarchy
newHierarchy = constructHierarchy(newHierarchy)
const diff = diffHierarchy(app.hierarchy, newHierarchy)
const changeActions = gatherChangeActions(diff)
if (changeActions.length === 0) return
const newApp =
newHierarchy &&
cloneApp(app, {
hierarchy: newHierarchy,
})
await doUpgrade(app, newApp, changeActions)
await _saveApplicationHierarchy(newApp.datastore, 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, diff.newNode)
}