From 00336cecc9ae473584968833926eaec114973c73 Mon Sep 17 00:00:00 2001 From: Michael Shanks Date: Tue, 10 Mar 2020 22:50:34 +0000 Subject: [PATCH 1/4] diffHierarchy started --- .../core/src/templateApi/diffHierarchy.js | 144 ++++++++++++++++++ packages/core/src/templateApi/upgradeData.js | 17 +++ .../test/templateApi.diffHierarchy.spec.js | 34 +++++ 3 files changed, 195 insertions(+) create mode 100644 packages/core/src/templateApi/diffHierarchy.js create mode 100644 packages/core/src/templateApi/upgradeData.js create mode 100644 packages/core/test/templateApi.diffHierarchy.spec.js diff --git a/packages/core/src/templateApi/diffHierarchy.js b/packages/core/src/templateApi/diffHierarchy.js new file mode 100644 index 0000000000..aa2dec91ac --- /dev/null +++ b/packages/core/src/templateApi/diffHierarchy.js @@ -0,0 +1,144 @@ +import { getFlattenedHierarchy, isRecord, isIndex } from "./hierarchy" +import { $, none } from "../common" +import { map, filter, some, find } from "lodash/fp" + +export const HierarchyChangeTypes = { + recordCreated: "Record Created", + recordDeleted: "Record Deleted", + recordRenamed: "Record Renamed", + recordFieldsChanged: "Record Fields Changed", + recordEstimatedRecordTypeChanged: "Record's Estimated Record Count Changed", + indexCreated: "Index Created", + indexDeleted: "Index Deleted", + indexChanged: "index Changed", +} + +export const diffHierarchy = (oldHierarchy, newHierarchy) => { + const oldHierarchyFlat = getFlattenedHierarchy(oldHierarchy) + const newHierarchyFlat = getFlattenedHierarchy(newHierarchy) + + return [ + ...createdRecords(oldHierarchyFlat, newHierarchyFlat), + ...deletedRecords(oldHierarchyFlat, newHierarchyFlat), + ...renamedRecords(oldHierarchyFlat, newHierarchyFlat), + ...recordsWithFieldsChanged(oldHierarchyFlat, newHierarchyFlat), + ...recordsWithEstimatedRecordTypeChanged(oldHierarchyFlat, newHierarchyFlat), + ...createdIndexes(oldHierarchyFlat, newHierarchyFlat), + ...deletedIndexes(oldHierarchyFlat, newHierarchyFlat), + ...updatedIndexes(oldHierarchyFlat, newHierarchyFlat), + ] +} + +const changeItem = (type, oldNode, newNode) => ({ + type, oldNode, newNode, +}) + +const createdRecords = (oldHierarchyFlat, newHierarchyFlat) => + $(newHierarchyFlat, [ + filter(isRecord), + filter(nodeDoesNotExistIn(oldHierarchyFlat)), + map(n => changeItem(HierarchyChangeTypes.recordCreated, null, n)) + ]) + +const deletedRecords = (oldHierarchyFlat, newHierarchyFlat) => + $(oldHierarchyFlat, [ + filter(isRecord), + filter(nodeDoesNotExistIn(newHierarchyFlat)), + map(n => changeItem(HierarchyChangeTypes.recordDeleted, n, null)) + ]) + +const renamedRecords = (oldHierarchyFlat, newHierarchyFlat) => + $(oldHierarchyFlat, [ + filter(isRecord), + filter(nodeExistsIn(newHierarchyFlat)), + filter(nodeChanged(newHierarchyFlat, (_new,old) =>_new.collectionKey !== old.collectionKey )), + map(n => changeItem( + HierarchyChangeTypes.recordDeleted, + n, + findNodeIn(n, newHierarchyFlat)) + ) + ]) + +const recordsWithFieldsChanged = (oldHierarchyFlat, newHierarchyFlat) => + $(oldHierarchyFlat, [ + filter(isRecord), + filter(nodeExistsIn(newHierarchyFlat)), + filter(hasDifferentFields(newHierarchyFlat)), + map(n => changeItem( + HierarchyChangeTypes.recordFieldsChanged, + n, + findNodeIn(n, newHierarchyFlat)) + ) + ]) + +const recordsWithEstimatedRecordTypeChanged = (oldHierarchyFlat, newHierarchyFlat) => + $(oldHierarchyFlat, [ + filter(isRecord), + filter(nodeExistsIn(newHierarchyFlat)), + filter(nodeChanged(newHierarchyFlat, (_new,old) =>_new.estimatedRecordCount !== old.estimatedRecordCount)), + map(n => changeItem( + HierarchyChangeTypes.recordEstimatedRecordTypeChanged, + n, + findNodeIn(n, newHierarchyFlat)) + ) + ]) + +const createdIndexes = (oldHierarchyFlat, newHierarchyFlat) => + $(newHierarchyFlat, [ + filter(isIndex), + filter(nodeDoesNotExistIn(oldHierarchyFlat)), + map(n => changeItem(HierarchyChangeTypes.indexCreated, null, n)) + ]) + +const deletedIndexes = (oldHierarchyFlat, newHierarchyFlat) => + $(oldHierarchyFlat, [ + filter(isIndex), + filter(nodeDoesNotExistIn(newHierarchyFlat)), + map(n => changeItem(HierarchyChangeTypes.indexDeleted, n, null)) + ]) + + +const updatedIndexes = (oldHierarchyFlat, newHierarchyFlat) => + $(oldHierarchyFlat, [ + filter(isRecord), + filter(nodeExistsIn(newHierarchyFlat)), + filter(nodeChanged(newHierarchyFlat, indexHasChanged)), + map(n => changeItem( + HierarchyChangeTypes.indexChanged, + n, + findNodeIn(n, newHierarchyFlat)) + ) + ]) + +const hasDifferentFields = otherFlatHierarchy => record1 => { + + const record2 = findNodeIn(record1, otherFlatHierarchy) + + if(record1.fields.length !== record2.fields.length) return false + + for(let f1 of record1.fields) { + if (none(isFieldSame(f1))(record2.fields)) return true + } + + return false +} + +const indexHasChanged = (_new, old) => + _new.map !== old.map + || _new.filter !== old.filter + || _new.getShardName !== old.getShardName + +const isFieldSame = f1 => f2 => + f1.name === f2.name && f1.type === f2.type + +const nodeDoesNotExistIn = inThis => node => + none(n => n.nodeId === node.nodeId)(inThis) + +const nodeExistsIn = inThis => node => + some(n => n.nodeId === node.nodeId)(inThis) + +const nodeChanged = (inThis, isChanged) => node => + some(n => n.nodeId === node.nodeId && isChanged(n, node))(inThis) + +const findNodeIn = (node, inThis) => + find(n => n.nodeId === node.nodeId)(inThis) diff --git a/packages/core/src/templateApi/upgradeData.js b/packages/core/src/templateApi/upgradeData.js new file mode 100644 index 0000000000..197252f890 --- /dev/null +++ b/packages/core/src/templateApi/upgradeData.js @@ -0,0 +1,17 @@ + /* +const changeActions = { + rebuildIndex: indexNodeKey => ({ + type: "rebuildIndex", + indexNodeKey, + }), + reshardRecords: recordNodeKey => ({ + type: "reshardRecords", + recordNodeKey, + }), + deleteRecords: recordNodeKey => ({ + type: "reshardRecords", + recordNodeKey, + }), + renameRecord +} +*/ \ No newline at end of file diff --git a/packages/core/test/templateApi.diffHierarchy.spec.js b/packages/core/test/templateApi.diffHierarchy.spec.js new file mode 100644 index 0000000000..1506975aca --- /dev/null +++ b/packages/core/test/templateApi.diffHierarchy.spec.js @@ -0,0 +1,34 @@ +import { getMemoryTemplateApi } from "./specHelpers" +import { diffHierarchy } from "../src/templateApi/diffHierarchy" +import { getFlattenedHierarchy } from "../src/templateApi/hierarchy" + +describe("diffHierarchy", () => { + + it("should not show any changes, when hierarchy is unchanged", async () => { + const oldHierarchy = (await setup()).root; + const newHierarchy = (await setup()).root; + const diff = diffHierarchy(oldHierarchy, newHierarchy) + expect(diff).toEqual([]) + }) + + it("should detect record created", async () => { + + }) +}) + + +const setup = async () => { + const { templateApi } = await getMemoryTemplateApi() + const root = templateApi.getNewRootLevel() + const contact = templateApi.getNewRecordTemplate(root, "contact", true) + const lead = templateApi.getNewRecordTemplate(root, "lead", true) + const deal = templateApi.getNewRecordTemplate(contact, "deal", true) + + getFlattenedHierarchy(root) + return { + root, contact, lead, deal, + all_contacts: root.indexes[0], + all_leads: root.indexes[1], + deals_for_contacts: contact.indexes[0] + } +} \ No newline at end of file From a286385e5712daf4dfdcc4a6c50552fa543d85bd Mon Sep 17 00:00:00 2001 From: Michael Shanks Date: Wed, 11 Mar 2020 16:42:19 +0000 Subject: [PATCH 2/4] bugfixes... incorrect routing from builder to instances --- packages/builder/rollup.config.js | 7 +++++- .../builderStore/loadComponentLibraries.js | 21 ++++++++++++++++++ packages/builder/src/builderStore/store.js | 11 ++++++++-- .../userInterface/CurrentItemPreview.svelte | 4 ++-- .../middleware/routeHandlers/appDefault.js | 4 +++- .../middleware/routeHandlers/helpers.js | 20 ++++++++++++----- packages/server/middleware/routers.js | 22 +++++++++++++++---- packages/server/utilities/builder/index.js | 4 +++- .../server/utilities/masterAppInternal.js | 19 ++++++++++++---- 9 files changed, 91 insertions(+), 21 deletions(-) diff --git a/packages/builder/rollup.config.js b/packages/builder/rollup.config.js index aaca6e3c8f..5a607720d8 100644 --- a/packages/builder/rollup.config.js +++ b/packages/builder/rollup.config.js @@ -18,7 +18,12 @@ const _builderProxy = proxy("/_builder", { }) const apiProxy = proxy( - ["/_builder/assets/**", "/_builder/api/**", "/_builder/**/componentlibrary"], + [ + "/_builder/assets/**", + "/_builder/api/**", + "/_builder/**/componentlibrary", + "/_builder/instance/**", + ], { target, logLevel: "debug", diff --git a/packages/builder/src/builderStore/loadComponentLibraries.js b/packages/builder/src/builderStore/loadComponentLibraries.js index e844ba77de..5a142f2798 100644 --- a/packages/builder/src/builderStore/loadComponentLibraries.js +++ b/packages/builder/src/builderStore/loadComponentLibraries.js @@ -32,3 +32,24 @@ export const makeLibraryUrl = (appName, lib) => export const libsFromPages = pages => pipe(pages, [values, map(p => p.componentLibraries), flatten, uniq]) + +export const libUrlsForPreview = (appPackage, pageName) => { + const resolve = path => { + let file = appPackage.components.libraryPaths[path] + if (file.startsWith("./")) file = file.substring(2) + if (file.startsWith("/")) file = file.substring(1) + + let newPath = path + + if (!newPath.startsWith("./") && !newPath.startsWith("/")) { + newPath = `/node_modules/${path}` + } + + return { + importPath: `/lib${newPath}/${file}`, + libName: path, + } + } + + return pipe([appPackage.pages[pageName]], [libsFromPages, map(resolve)]) +} diff --git a/packages/builder/src/builderStore/store.js b/packages/builder/src/builderStore/store.js index 1653cdb6dd..7753fa255d 100644 --- a/packages/builder/src/builderStore/store.js +++ b/packages/builder/src/builderStore/store.js @@ -29,7 +29,7 @@ import { getBuiltin, } from "../userInterface/pagesParsing/createProps" import { expandComponentDefinition } from "../userInterface/pagesParsing/types" -import { loadLibs, loadLibUrls } from "./loadComponentLibraries" +import { loadLibs, libUrlsForPreview } from "./loadComponentLibraries" import { buildCodeForScreens } from "./buildCodeForScreens" import { generate_screen_css } from "./generate_css" import { insertCodeMetadata } from "./insertCodeMetadata" @@ -153,9 +153,16 @@ const initialise = (store, initial) => async () => { } initial.libraries = await loadLibs(appname, pkg) - initial.loadLibraryUrls = () => loadLibUrls(appname, pkg) + initial.loadLibraryUrls = pageName => { + const libs = libUrlsForPreview(pkg, pageName) + return libs + } initial.appname = appname initial.pages = pkg.pages + initial.currentInstanceId = + pkg.application.instances && pkg.application.instances.length > 0 + ? pkg.application.instances[0].id + : "" initial.hasAppPackage = true initial.hierarchy = pkg.appDefinition.hierarchy initial.accessLevels = pkg.accessLevels diff --git a/packages/builder/src/userInterface/CurrentItemPreview.svelte b/packages/builder/src/userInterface/CurrentItemPreview.svelte index 7b27a6cb75..429534ceb4 100644 --- a/packages/builder/src/userInterface/CurrentItemPreview.svelte +++ b/packages/builder/src/userInterface/CurrentItemPreview.svelte @@ -30,7 +30,7 @@ ) $: frontendDefinition = { - componentLibraries: $store.loadLibraryUrls(), + componentLibraries: $store.loadLibraryUrls($store.currentPageName), page: $store.currentPreviewItem, screens: [{ name: "Screen Placeholder", @@ -63,7 +63,7 @@ ] } }], - appRootPath: "", + appRootPath: `/_builder/instance/${$store.appname}/${$store.currentInstanceId}/`, } $: backendDefinition = { diff --git a/packages/server/middleware/routeHandlers/appDefault.js b/packages/server/middleware/routeHandlers/appDefault.js index 61dd9520de..6bbb352abf 100644 --- a/packages/server/middleware/routeHandlers/appDefault.js +++ b/packages/server/middleware/routeHandlers/appDefault.js @@ -1,7 +1,9 @@ +const { getAppRelativePath } = require("./helpers") + const send = require("koa-send") module.exports = async (ctx, next) => { - const path = ctx.path.replace(`/${ctx.params.appname}`, "") + const path = getAppRelativePath(ctx.params.appname, ctx.path) if (path.startsWith("/api/")) { await next() diff --git a/packages/server/middleware/routeHandlers/helpers.js b/packages/server/middleware/routeHandlers/helpers.js index 22a29d3d84..92995fb7e6 100644 --- a/packages/server/middleware/routeHandlers/helpers.js +++ b/packages/server/middleware/routeHandlers/helpers.js @@ -1,7 +1,15 @@ exports.getRecordKey = (appname, wholePath) => - wholePath - .replace(`/${appname}/api/files/`, "") - .replace(`/${appname}/api/lookup_field/`, "") - .replace(`/${appname}/api/record/`, "") - .replace(`/${appname}/api/listRecords/`, "") - .replace(`/${appname}/api/aggregates/`, "") + this.getAppRelativePath(appname, wholePath) + .replace(`/api/files/`, "/") + .replace(`/api/lookup_field/`, "/") + .replace(`/api/record/`, "/") + .replace(`/api/listRecords/`, "/") + .replace(`/api/aggregates/`, "/") + +exports.getAppRelativePath = (appname, wholePath) => { + const builderInstanceRegex = new RegExp( + `\\/_builder\\/instance\\/[^\\/]*\\/[^\\/]*\\/` + ) + + return wholePath.replace(builderInstanceRegex, "/").replace(`/${appname}`, "") +} diff --git a/packages/server/middleware/routers.js b/packages/server/middleware/routers.js index 2933ca0a55..dd77706f5f 100644 --- a/packages/server/middleware/routers.js +++ b/packages/server/middleware/routers.js @@ -46,15 +46,29 @@ module.exports = (config, app) => { } if (ctx.path.startsWith("/_builder/instance/_master")) { - ctx.instance = ctx.master.getFullAccessApiForMaster() + const { + instance, + publicPath, + sharedPath, + } = await ctx.master.getFullAccessApiForMaster() + ctx.instance = instance + ctx.publicPath = publicPath + ctx.sharedPath = sharedPath ctx.isAuthenticated = !!ctx.instance } else if (ctx.path.startsWith("/_builder/instance")) { const builderAppName = pathParts[3] const instanceId = pathParts[4] - ctx.instance = ctx.master.getFullAccessApiForInstanceId( + const { + bbInstance, + publicPath, + sharedPath, + } = await ctx.master.getFullAccessApiForInstanceId( builderAppName, instanceId - ).bbInstance + ) + ctx.instance = bbInstance + ctx.publicPath = publicPath + ctx.sharedPath = sharedPath ctx.isAuthenticated = !!ctx.instance } @@ -87,7 +101,7 @@ module.exports = (config, app) => { .get("/_builder/*", async (ctx, next) => { const path = ctx.path.replace("/_builder", "") - if (path.startsWith("/api/")) { + if (path.startsWith("/api/") || path.startsWith("/instance/")) { await next() } else { await send(ctx, path, { root: builderPath }) diff --git a/packages/server/utilities/builder/index.js b/packages/server/utilities/builder/index.js index 4dcdefa32b..26daf8aa87 100644 --- a/packages/server/utilities/builder/index.js +++ b/packages/server/utilities/builder/index.js @@ -147,17 +147,19 @@ const getComponentDefinitions = async (appPath, pages, componentLibrary) => { const components = {} const templates = {} + const libraryPaths = {} for (let library of componentLibraries) { const info = await componentLibraryInfo(appPath, library) merge(components, info.components) merge(templates, info.templates) + libraryPaths[library] = components._lib } if (components._lib) delete components._lib if (templates._lib) delete templates._lib - return { components, templates } + return { components, templates, libraryPaths } } module.exports.getComponentDefinitions = getComponentDefinitions diff --git a/packages/server/utilities/masterAppInternal.js b/packages/server/utilities/masterAppInternal.js index 462e628d59..88a7bdfb75 100644 --- a/packages/server/utilities/masterAppInternal.js +++ b/packages/server/utilities/masterAppInternal.js @@ -156,7 +156,8 @@ module.exports = async context => { const getFullAccessApiForInstanceId = async (appname, instanceId, appId) => { if (!appId) { - appId = (await getApplication(appname)).id + const app = await getApplication(appname) + appId = app.id } const instanceKey = `/applications/${appId}/instances/${instanceId}` const instance = await bb.recordApi.load(instanceKey) @@ -172,15 +173,25 @@ module.exports = async context => { ) return { bbInstance: await getApisWithFullAccess( - datastoreModule.getDatastore(dsConfig), + getInstanceDatastore(dsConfig), appPackage ), instance, + publicPath: appPackage.mainUiPath, + sharedPath: appPackage.sharedPath, } } - const getFullAccessApiForMaster = async () => - await getApisWithFullAccess(masterDatastore, masterAppPackage(context)) + const getFullAccessApiForMaster = async () => { + const masterPkg = masterAppPackage(context) + const instance = await getApisWithFullAccess(masterDatastore, masterPkg) + + return { + instance, + publicPath: masterPkg.unauthenticatedUiPath, + sharedPath: masterPkg.sharedPath, + } + } const getInstanceApiForSession = async (appname, sessionId) => { if (isMaster(appname)) { From 18539213306a180cc066b74f1c2680a544963f39 Mon Sep 17 00:00:00 2001 From: Michael Shanks Date: Wed, 11 Mar 2020 16:42:53 +0000 Subject: [PATCH 3/4] hierarchy diff tests --- .../core/src/templateApi/diffHierarchy.js | 69 ++++--- .../test/templateApi.diffHierarchy.spec.js | 193 +++++++++++++++++- 2 files changed, 234 insertions(+), 28 deletions(-) diff --git a/packages/core/src/templateApi/diffHierarchy.js b/packages/core/src/templateApi/diffHierarchy.js index aa2dec91ac..5206a3526a 100644 --- a/packages/core/src/templateApi/diffHierarchy.js +++ b/packages/core/src/templateApi/diffHierarchy.js @@ -1,4 +1,4 @@ -import { getFlattenedHierarchy, isRecord, isIndex } from "./hierarchy" +import { getFlattenedHierarchy, isRecord, isIndex, isAncestor } from "./hierarchy" import { $, none } from "../common" import { map, filter, some, find } from "lodash/fp" @@ -17,15 +17,18 @@ export const diffHierarchy = (oldHierarchy, newHierarchy) => { const oldHierarchyFlat = getFlattenedHierarchy(oldHierarchy) const newHierarchyFlat = getFlattenedHierarchy(newHierarchy) + const createdRecords = findCreatedRecords(oldHierarchyFlat, newHierarchyFlat) + const deletedRecords = findDeletedRecords(oldHierarchyFlat, newHierarchyFlat) + return [ - ...createdRecords(oldHierarchyFlat, newHierarchyFlat), - ...deletedRecords(oldHierarchyFlat, newHierarchyFlat), - ...renamedRecords(oldHierarchyFlat, newHierarchyFlat), - ...recordsWithFieldsChanged(oldHierarchyFlat, newHierarchyFlat), - ...recordsWithEstimatedRecordTypeChanged(oldHierarchyFlat, newHierarchyFlat), - ...createdIndexes(oldHierarchyFlat, newHierarchyFlat), - ...deletedIndexes(oldHierarchyFlat, newHierarchyFlat), - ...updatedIndexes(oldHierarchyFlat, newHierarchyFlat), + ...createdRecords, + ...deletedRecords, + ...findRenamedRecords(oldHierarchyFlat, newHierarchyFlat), + ...findRecordsWithFieldsChanged(oldHierarchyFlat, newHierarchyFlat), + ...findRecordsWithEstimatedRecordTypeChanged(oldHierarchyFlat, newHierarchyFlat), + ...findCreatedIndexes(oldHierarchyFlat, newHierarchyFlat, createdRecords), + ...findDeletedIndexes(oldHierarchyFlat, newHierarchyFlat, deletedRecords), + ...findUpdatedIndexes(oldHierarchyFlat, newHierarchyFlat), ] } @@ -33,33 +36,43 @@ const changeItem = (type, oldNode, newNode) => ({ type, oldNode, newNode, }) -const createdRecords = (oldHierarchyFlat, newHierarchyFlat) => - $(newHierarchyFlat, [ +const findCreatedRecords = (oldHierarchyFlat, newHierarchyFlat) => { + const allCreated = $(newHierarchyFlat, [ filter(isRecord), filter(nodeDoesNotExistIn(oldHierarchyFlat)), map(n => changeItem(HierarchyChangeTypes.recordCreated, null, n)) ]) -const deletedRecords = (oldHierarchyFlat, newHierarchyFlat) => - $(oldHierarchyFlat, [ + return $(allCreated, [ + filter(r => none(r2 => isAncestor(r.newNode)(r2.newNode))(allCreated)) + ]) +} + +const findDeletedRecords = (oldHierarchyFlat, newHierarchyFlat) => { + const allDeleted = $(oldHierarchyFlat, [ filter(isRecord), filter(nodeDoesNotExistIn(newHierarchyFlat)), map(n => changeItem(HierarchyChangeTypes.recordDeleted, n, null)) ]) -const renamedRecords = (oldHierarchyFlat, newHierarchyFlat) => + return $(allDeleted, [ + filter(r => none(r2 => isAncestor(r.oldNode)(r2.oldNode))(allDeleted)) + ]) +} + +const findRenamedRecords = (oldHierarchyFlat, newHierarchyFlat) => $(oldHierarchyFlat, [ filter(isRecord), filter(nodeExistsIn(newHierarchyFlat)), filter(nodeChanged(newHierarchyFlat, (_new,old) =>_new.collectionKey !== old.collectionKey )), map(n => changeItem( - HierarchyChangeTypes.recordDeleted, + HierarchyChangeTypes.recordRenamed, n, findNodeIn(n, newHierarchyFlat)) ) ]) -const recordsWithFieldsChanged = (oldHierarchyFlat, newHierarchyFlat) => +const findRecordsWithFieldsChanged = (oldHierarchyFlat, newHierarchyFlat) => $(oldHierarchyFlat, [ filter(isRecord), filter(nodeExistsIn(newHierarchyFlat)), @@ -71,7 +84,7 @@ const recordsWithFieldsChanged = (oldHierarchyFlat, newHierarchyFlat) => ) ]) -const recordsWithEstimatedRecordTypeChanged = (oldHierarchyFlat, newHierarchyFlat) => +const findRecordsWithEstimatedRecordTypeChanged = (oldHierarchyFlat, newHierarchyFlat) => $(oldHierarchyFlat, [ filter(isRecord), filter(nodeExistsIn(newHierarchyFlat)), @@ -83,22 +96,32 @@ const recordsWithEstimatedRecordTypeChanged = (oldHierarchyFlat, newHierarchyFla ) ]) -const createdIndexes = (oldHierarchyFlat, newHierarchyFlat) => - $(newHierarchyFlat, [ +const findCreatedIndexes = (oldHierarchyFlat, newHierarchyFlat, createdRecords) => { + const allCreated = $(newHierarchyFlat, [ filter(isIndex), filter(nodeDoesNotExistIn(oldHierarchyFlat)), map(n => changeItem(HierarchyChangeTypes.indexCreated, null, n)) ]) -const deletedIndexes = (oldHierarchyFlat, newHierarchyFlat) => - $(oldHierarchyFlat, [ + return $(allCreated, [ + filter(r => none(r2 => isAncestor(r.newNode)(r2.newNode))(createdRecords)) + ]) +} + +const findDeletedIndexes = (oldHierarchyFlat, newHierarchyFlat, deletedRecords) => { + const allDeleted = $(oldHierarchyFlat, [ filter(isIndex), filter(nodeDoesNotExistIn(newHierarchyFlat)), map(n => changeItem(HierarchyChangeTypes.indexDeleted, n, null)) ]) + return $(allDeleted, [ + filter(r => none(r2 => isAncestor(r.oldNode)(r2.oldNode))(deletedRecords)) + ]) +} -const updatedIndexes = (oldHierarchyFlat, newHierarchyFlat) => + +const findUpdatedIndexes = (oldHierarchyFlat, newHierarchyFlat) => $(oldHierarchyFlat, [ filter(isRecord), filter(nodeExistsIn(newHierarchyFlat)), @@ -114,7 +137,7 @@ const hasDifferentFields = otherFlatHierarchy => record1 => { const record2 = findNodeIn(record1, otherFlatHierarchy) - if(record1.fields.length !== record2.fields.length) return false + if(record1.fields.length !== record2.fields.length) return true for(let f1 of record1.fields) { if (none(isFieldSame(f1))(record2.fields)) return true diff --git a/packages/core/test/templateApi.diffHierarchy.spec.js b/packages/core/test/templateApi.diffHierarchy.spec.js index 1506975aca..6f9cbce4e1 100644 --- a/packages/core/test/templateApi.diffHierarchy.spec.js +++ b/packages/core/test/templateApi.diffHierarchy.spec.js @@ -1,5 +1,5 @@ import { getMemoryTemplateApi } from "./specHelpers" -import { diffHierarchy } from "../src/templateApi/diffHierarchy" +import { diffHierarchy, HierarchyChangeTypes } from "../src/templateApi/diffHierarchy" import { getFlattenedHierarchy } from "../src/templateApi/hierarchy" describe("diffHierarchy", () => { @@ -11,22 +11,205 @@ describe("diffHierarchy", () => { expect(diff).toEqual([]) }) - it("should detect record created", async () => { - + it("should detect root record created", async () => { + const oldHierarchy = (await setup()).root; + const newSetup = (await setup()); + const opportunity = newSetup.templateApi.getNewRecordTemplate(newSetup.root, "opportunity", false) + const diff = diffHierarchy(oldHierarchy, newSetup.root) + expect(diff).toEqual([{ + newNode: opportunity, + oldNode: null, + type: HierarchyChangeTypes.recordCreated + }]) }) -}) + it("should only detect root record, when newly created root record has children ", async () => { + const oldHierarchy = (await setup()).root; + const newSetup = (await setup()); + const opportunity = newSetup.templateApi.getNewRecordTemplate(newSetup.root, "opportunity", false) + newSetup.templateApi.getNewRecordTemplate(opportunity, "invoice", true) + const diff = diffHierarchy(oldHierarchy, newSetup.root) + expect(diff).toEqual([{ + newNode: opportunity, + oldNode: null, + type: HierarchyChangeTypes.recordCreated + }]) + }) + + it("should detect child record created", async () => { + const oldHierarchy = (await setup()).root; + const newSetup = (await setup()); + const opportunity = newSetup.templateApi.getNewRecordTemplate(newSetup.contact, "opportunity", false) + const diff = diffHierarchy(oldHierarchy, newSetup.root) + expect(diff).toEqual([{ + newNode: opportunity, + oldNode: null, + type: HierarchyChangeTypes.recordCreated + }]) + }) + + it("should detect root record deleted", async () => { + const oldSetup = (await setup()); + const newSetup = (await setup()); + newSetup.root.children = newSetup.root.children.filter(n => n.name !== "contact") + const diff = diffHierarchy(oldSetup.root, newSetup.root) + expect(diff).toEqual([{ + newNode: null, + oldNode: oldSetup.contact, + type: HierarchyChangeTypes.recordDeleted + }]) + }) + + it("should detect child record deleted", async () => { + const oldSetup = (await setup()); + const newSetup = (await setup()); + newSetup.contact.children = newSetup.contact.children.filter(n => n.name !== "deal") + const diff = diffHierarchy(oldSetup.root, newSetup.root) + expect(diff).toEqual([{ + newNode: null, + oldNode: oldSetup.deal, + type: HierarchyChangeTypes.recordDeleted + }]) + }) + + it("should detect root record renamed", async () => { + const oldSetup = (await setup()); + const newSetup = (await setup()); + newSetup.contact.collectionKey = "CONTACTS" + const diff = diffHierarchy(oldSetup.root, newSetup.root) + expect(diff).toEqual([{ + newNode: newSetup.contact, + oldNode: oldSetup.contact, + type: HierarchyChangeTypes.recordRenamed + }]) + }) + + it("should detect child record renamed", async () => { + const oldSetup = (await setup()); + const newSetup = (await setup()); + newSetup.deal.collectionKey = "CONTACTS" + const diff = diffHierarchy(oldSetup.root, newSetup.root) + expect(diff).toEqual([{ + newNode: newSetup.deal, + oldNode: oldSetup.deal, + type: HierarchyChangeTypes.recordRenamed + }]) + }) + + it("should detect root record field removed", async () => { + const oldSetup = (await setup()); + const newSetup = (await setup()); + newSetup.contact.fields = newSetup.contact.fields.filter(f => f.name !== "name") + const diff = diffHierarchy(oldSetup.root, newSetup.root) + expect(diff).toEqual([{ + newNode: newSetup.contact, + oldNode: oldSetup.contact, + type: HierarchyChangeTypes.recordFieldsChanged + }]) + }) + + it("should detect child record field removed", async () => { + const oldSetup = (await setup()); + const newSetup = (await setup()); + newSetup.deal.fields = newSetup.deal.fields.filter(f => f.name !== "name") + const diff = diffHierarchy(oldSetup.root, newSetup.root) + expect(diff).toEqual([{ + newNode: newSetup.deal, + oldNode: oldSetup.deal, + type: HierarchyChangeTypes.recordFieldsChanged + }]) + }) + + it("should detect record field added", async () => { + const oldSetup = (await setup()); + const newSetup = (await setup()); + const notesField = newSetup.templateApi.getNewField("string") + notesField.name = "notes" + newSetup.templateApi.addField(newSetup.contact, notesField) + + const diff = diffHierarchy(oldSetup.root, newSetup.root) + expect(diff).toEqual([{ + newNode: newSetup.contact, + oldNode: oldSetup.contact, + type: HierarchyChangeTypes.recordFieldsChanged + }]) + }) + + it("should detect 1 record field added and 1 removed (total no. fields unchanged)", async () => { + const oldSetup = (await setup()); + const newSetup = (await setup()); + const notesField = newSetup.templateApi.getNewField("string") + notesField.name = "notes" + newSetup.templateApi.addField(newSetup.contact, notesField) + newSetup.contact.fields = newSetup.contact.fields.filter(f => f.name !== "name") + const diff = diffHierarchy(oldSetup.root, newSetup.root) + expect(diff).toEqual([{ + newNode: newSetup.contact, + oldNode: oldSetup.contact, + type: HierarchyChangeTypes.recordFieldsChanged + }]) + }) + + it("should detect root record estimated record count changed", async () => { + const oldSetup = (await setup()); + const newSetup = (await setup()); + newSetup.contact.estimatedRecordCount = 987 + const diff = diffHierarchy(oldSetup.root, newSetup.root) + expect(diff).toEqual([{ + newNode: newSetup.contact, + oldNode: oldSetup.contact, + type: HierarchyChangeTypes.recordEstimatedRecordTypeChanged + }]) + }) + + it("should detect root record estimated record count changed", async () => { + const oldSetup = (await setup()); + const newSetup = (await setup()); + newSetup.deal.estimatedRecordCount = 987 + const diff = diffHierarchy(oldSetup.root, newSetup.root) + expect(diff).toEqual([{ + newNode: newSetup.deal, + oldNode: oldSetup.deal, + type: HierarchyChangeTypes.recordEstimatedRecordTypeChanged + }]) + }) + + it("should detect root record created", async () => { + const oldHierarchy = (await setup()).root; + const newSetup = (await setup()); + const opportunity = newSetup.templateApi.getNewRecordTemplate(newSetup.root, "opportunity", false) + const diff = diffHierarchy(oldHierarchy, newSetup.root) + expect(diff).toEqual([{ + newNode: opportunity, + oldNode: null, + type: HierarchyChangeTypes.recordCreated + }]) + }) + +}) const setup = async () => { const { templateApi } = await getMemoryTemplateApi() const root = templateApi.getNewRootLevel() const contact = templateApi.getNewRecordTemplate(root, "contact", true) + + const nameField = templateApi.getNewField("string") + nameField.name = "name" + const statusField = templateApi.getNewField("string") + statusField.name = "status" + + templateApi.addField(contact, nameField) + templateApi.addField(contact, statusField) + const lead = templateApi.getNewRecordTemplate(root, "lead", true) const deal = templateApi.getNewRecordTemplate(contact, "deal", true) + templateApi.addField(deal, {...nameField}) + templateApi.addField(deal, {...statusField}) + getFlattenedHierarchy(root) return { - root, contact, lead, deal, + root, contact, lead, deal, templateApi, all_contacts: root.indexes[0], all_leads: root.indexes[1], deals_for_contacts: contact.indexes[0] From c44f658ebf118a43b264bd16999830f60137cf7f Mon Sep 17 00:00:00 2001 From: Michael Shanks Date: Wed, 11 Mar 2020 17:07:18 +0000 Subject: [PATCH 4/4] broken tests --- packages/server/tests/builder.spec.js | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/packages/server/tests/builder.spec.js b/packages/server/tests/builder.spec.js index ff8954dd7f..c6ac17b5d2 100644 --- a/packages/server/tests/builder.spec.js +++ b/packages/server/tests/builder.spec.js @@ -10,11 +10,19 @@ const screen2 = require("../appPackages/testApp/pages/main/screens/screen2.json" const { readJSON, pathExists, unlink, readFile } = require("fs-extra") const { getHashedCssPaths } = require("../utilities/builder/convertCssToFiles") const listScreens = require("../utilities/builder/listScreens") +const { getApisWithFullAccess } = require("../utilities/budibaseApi") const app = require("./testApp")() testComponents.textbox.name = `./customComponents/textbox` testMoreComponents.textbox.name = `./moreCustomComponents/textbox` +let _master +const getmaster = async () => { + if (!_master) + _master = await getApisWithFullAccess({}, app.masterAppPackage) + return _master +} + beforeAll(async () => { const testScreen = "./appPackages/testApp/pages/main/screens/newscreen.json" const testScreenAfterMove = @@ -24,6 +32,23 @@ beforeAll(async () => { if (await pathExists(testScreenAfterMove)) await unlink(testScreenAfterMove) await app.start() + + const response = await app + .post(`/_master/api/authenticate`, { + username: app.credentials.masterOwner.username, + password: app.credentials.masterOwner.password, + }) + .expect(statusCodes.OK) + + app.credentials.masterOwner.cookie = response.header["set-cookie"] + + const master = await getmaster() + const newApp = master.recordApi.getNew("/applications", "application") + newApp.name = "testApp" + await app + .post(`/_master/api/record/${newApp.key}`, newApp) + .set("cookie", app.credentials.masterOwner.cookie) + .expect(statusCodes.OK) }) afterAll(async () => await app.destroy())