From cad066c6681e101716ea8c0ebce2ff8470d6e77d Mon Sep 17 00:00:00 2001 From: Dean Date: Tue, 30 Jul 2024 10:03:24 +0100 Subject: [PATCH 01/13] Reduced the number of unnecessary calls to update the automation store or fetch automations. The contant updates appeared to cause issues with routify --- .../builder/src/stores/builder/automations.js | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/packages/builder/src/stores/builder/automations.js b/packages/builder/src/stores/builder/automations.js index 57c823da9b..cab8090424 100644 --- a/packages/builder/src/stores/builder/automations.js +++ b/packages/builder/src/stores/builder/automations.js @@ -87,8 +87,6 @@ const automationActions = store => ({ disabled: false, } const response = await store.actions.save(automation) - await store.actions.fetch() - store.actions.select(response._id) return response }, duplicate: async automation => { @@ -98,14 +96,13 @@ const automationActions = store => ({ _id: undefined, _ref: undefined, }) - await store.actions.fetch() - store.actions.select(response._id) return response }, save: async automation => { const response = await API.updateAutomation(automation) await store.actions.fetch() + store.actions.select(response._id) return response.automation }, delete: async automation => { @@ -113,18 +110,22 @@ const automationActions = store => ({ automationId: automation?._id, automationRev: automation?._rev, }) + store.update(state => { // Remove the automation state.automations = state.automations.filter( x => x._id !== automation._id ) + // Select a new automation if required if (automation._id === state.selectedAutomationId) { - store.actions.select(state.automations[0]?._id) + state.selectedAutomationId = state.automations[0]?._id || null } + + // Clear out automationDisplayData for the automation + delete state.automationDisplayData[automation._id] return state }) - await store.actions.fetch() }, toggleDisabled: async automationId => { let automation @@ -381,7 +382,7 @@ export const selectedAutomation = derived(automationStore, $automationStore => { export const selectedAutomationDisplayData = derived( [automationStore, selectedAutomation], ([$automationStore, $selectedAutomation]) => { - if (!$selectedAutomation._id) { + if (!$selectedAutomation?._id) { return null } return $automationStore.automationDisplayData[$selectedAutomation._id] From 5269258532e969172aaaf1e0057379ebe28932dc Mon Sep 17 00:00:00 2001 From: Dean Date: Tue, 30 Jul 2024 11:37:47 +0100 Subject: [PATCH 02/13] Ensure the builder is functional if an app automation does not contain a trigger --- .../AutomationBuilder/FlowChart/FlowChart.svelte | 2 ++ .../automation/AutomationPanel/AutomationNavItem.svelte | 8 +++++--- .../automation/AutomationPanel/AutomationPanel.svelte | 9 +++++---- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowChart.svelte b/packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowChart.svelte index f79b36b1ca..c263468f3b 100644 --- a/packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowChart.svelte +++ b/packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowChart.svelte @@ -54,6 +54,7 @@
{ testDataModal.show() }} @@ -80,6 +81,7 @@ automation._id, automation.disabled )} + disabled={!$selectedAutomation?.definition?.trigger} value={!automation.disabled} />
diff --git a/packages/builder/src/components/automation/AutomationPanel/AutomationNavItem.svelte b/packages/builder/src/components/automation/AutomationPanel/AutomationNavItem.svelte index df5ac3bd98..6e4d7c0099 100644 --- a/packages/builder/src/components/automation/AutomationPanel/AutomationNavItem.svelte +++ b/packages/builder/src/components/automation/AutomationPanel/AutomationNavItem.svelte @@ -54,7 +54,7 @@ name: "Edit", keyBind: null, visible: true, - disabled: false, + disabled: !automation.definition.trigger, callback: updateAutomationDialog.show, }, { @@ -62,7 +62,9 @@ name: "Duplicate", keyBind: null, visible: true, - disabled: automation.definition.trigger.name === "Webhook", + disabled: + !automation.definition.trigger || + automation.definition.trigger?.name === "Webhook", callback: duplicateAutomation, }, ] @@ -74,7 +76,7 @@ name: automation.disabled ? "Activate" : "Pause", keyBind: null, visible: true, - disabled: false, + disabled: !automation.definition.trigger, callback: () => { automationStore.actions.toggleDisabled( automation._id, diff --git a/packages/builder/src/components/automation/AutomationPanel/AutomationPanel.svelte b/packages/builder/src/components/automation/AutomationPanel/AutomationPanel.svelte index e017e6a26a..e51e6ab2be 100644 --- a/packages/builder/src/components/automation/AutomationPanel/AutomationPanel.svelte +++ b/packages/builder/src/components/automation/AutomationPanel/AutomationPanel.svelte @@ -30,12 +30,13 @@ }) $: groupedAutomations = filteredAutomations.reduce((acc, auto) => { - acc[auto.definition.trigger.event] ??= { - icon: auto.definition.trigger.icon, - name: (auto.definition.trigger?.name || "").toUpperCase(), + const catName = auto.definition?.trigger?.event || "No Trigger" + acc[catName] ??= { + icon: auto.definition?.trigger?.icon || "AlertCircle", + name: (auto.definition?.trigger?.name || "No Trigger").toUpperCase(), entries: [], } - acc[auto.definition.trigger.event].entries.push(auto) + acc[catName].entries.push(auto) return acc }, {}) From 76652ddab55cb59713a0316a33e2dba6d1f301b9 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Tue, 30 Jul 2024 15:07:34 +0100 Subject: [PATCH 03/13] Fixes some issues with row actions which were allowing them to be added from the automation section (which is not allowed) as well as breaking app access once they were added - this hides them properly so they can't be added as well as fixing the issue when they exist. --- .../AutomationPanel/AutomationPanel.svelte | 2 +- .../server/src/api/controllers/automation.ts | 9 ++++++--- packages/server/src/automations/utils.ts | 20 +++++++++++++++---- .../server/src/sdk/app/automations/crud.ts | 8 ++++---- .../server/src/sdk/app/automations/utils.ts | 7 ++++--- .../types/src/documents/app/automation.ts | 4 +--- 6 files changed, 32 insertions(+), 18 deletions(-) diff --git a/packages/builder/src/components/automation/AutomationPanel/AutomationPanel.svelte b/packages/builder/src/components/automation/AutomationPanel/AutomationPanel.svelte index e017e6a26a..65a48a59f0 100644 --- a/packages/builder/src/components/automation/AutomationPanel/AutomationPanel.svelte +++ b/packages/builder/src/components/automation/AutomationPanel/AutomationPanel.svelte @@ -20,7 +20,7 @@ .map(automation => ({ ...automation, displayName: - $automationStore.automationDisplayData[automation._id].displayName || + $automationStore.automationDisplayData[automation._id]?.displayName || automation.name, })) .sort((a, b) => { diff --git a/packages/server/src/api/controllers/automation.ts b/packages/server/src/api/controllers/automation.ts index 6177868303..f2b9428f69 100644 --- a/packages/server/src/api/controllers/automation.ts +++ b/packages/server/src/api/controllers/automation.ts @@ -1,7 +1,10 @@ import * as triggers from "../../automations/triggers" import { sdk as coreSdk } from "@budibase/shared-core" import { DocumentType } from "../../db/utils" -import { updateTestHistory, removeDeprecated } from "../../automations/utils" +import { + updateTestHistory, + removeInvalidDefinitions, +} from "../../automations/utils" import { setTestFlag, clearTestFlag } from "../../utilities/redis" import { context, cache, events, db as dbCore } from "@budibase/backend-core" import { automations, features } from "@budibase/pro" @@ -20,11 +23,11 @@ import { builderSocket } from "../../websockets" import env from "../../environment" async function getActionDefinitions() { - return removeDeprecated(await actionDefs()) + return removeInvalidDefinitions(await actionDefs()) } function getTriggerDefinitions() { - return removeDeprecated(triggers.TRIGGER_DEFINITIONS) + return removeInvalidDefinitions(triggers.TRIGGER_DEFINITIONS) } /************************* diff --git a/packages/server/src/automations/utils.ts b/packages/server/src/automations/utils.ts index c75cc5e8dc..e7f3dad225 100644 --- a/packages/server/src/automations/utils.ts +++ b/packages/server/src/automations/utils.ts @@ -3,11 +3,17 @@ import { definitions } from "./triggerInfo" import { automationQueue } from "./bullboard" import { updateEntityMetadata } from "../utilities" import { MetadataTypes } from "../constants" -import { db as dbCore, context, utils } from "@budibase/backend-core" +import { context, db as dbCore, utils } from "@budibase/backend-core" import { getAutomationMetadataParams } from "../db/utils" import { cloneDeep } from "lodash/fp" import { quotas } from "@budibase/pro" -import { Automation, AutomationJob } from "@budibase/types" +import { + Automation, + AutomationActionStepId, + AutomationJob, + AutomationStepSchema, + AutomationTriggerStepId, +} from "@budibase/types" import { automationsEnabled } from "../features" import { helpers, REBOOT_CRON } from "@budibase/shared-core" import tracer from "dd-trace" @@ -111,10 +117,16 @@ export async function updateTestHistory( ) } -export function removeDeprecated(definitions: any) { +export function removeInvalidDefinitions( + definitions: Record +) { + const disallowedStepIds: ( + | AutomationTriggerStepId + | AutomationActionStepId + )[] = [AutomationTriggerStepId.ROW_ACTION] const base = cloneDeep(definitions) for (let key of Object.keys(base)) { - if (base[key].deprecated) { + if (base[key].deprecated || disallowedStepIds.includes(base[key].stepId)) { delete base[key] } } diff --git a/packages/server/src/sdk/app/automations/crud.ts b/packages/server/src/sdk/app/automations/crud.ts index 2b36e32397..3888e6882a 100644 --- a/packages/server/src/sdk/app/automations/crud.ts +++ b/packages/server/src/sdk/app/automations/crud.ts @@ -87,10 +87,10 @@ export async function fetch() { include_docs: true, }) ) - return response.rows - .map(row => row.doc) - .filter(doc => !!doc) - .map(trimUnexpectedObjectFields) + const automations: PersistedAutomation[] = response.rows + .filter(row => !!row.doc) + .map(row => row.doc!) + return automations.map(trimUnexpectedObjectFields) } export async function get(automationId: string) { diff --git a/packages/server/src/sdk/app/automations/utils.ts b/packages/server/src/sdk/app/automations/utils.ts index e89006d618..5d057697ca 100644 --- a/packages/server/src/sdk/app/automations/utils.ts +++ b/packages/server/src/sdk/app/automations/utils.ts @@ -29,8 +29,7 @@ export async function getBuilderData( const rowActionNameCache: Record = {} async function getRowActionName(tableId: string, rowActionId: string) { if (!rowActionNameCache[tableId]) { - const rowActions = await sdk.rowActions.get(tableId) - rowActionNameCache[tableId] = rowActions + rowActionNameCache[tableId] = await sdk.rowActions.get(tableId) } return rowActionNameCache[tableId].actions[rowActionId]?.name @@ -45,9 +44,11 @@ export async function getBuilderData( } const { tableId, rowActionId } = automation.definition.trigger.inputs + if (!tableId || !rowActionId) { + continue + } const tableName = await getTableName(tableId) - const rowActionName = await getRowActionName(tableId, rowActionId) result[automation._id!] = { diff --git a/packages/types/src/documents/app/automation.ts b/packages/types/src/documents/app/automation.ts index b53895e57b..d5d7fe667c 100644 --- a/packages/types/src/documents/app/automation.ts +++ b/packages/types/src/documents/app/automation.ts @@ -174,9 +174,7 @@ export interface AutomationStepSchema { deprecated?: boolean stepId: AutomationTriggerStepId | AutomationActionStepId blockToLoop?: string - inputs: { - [key: string]: any - } + inputs: Record schema: { inputs: InputOutputBlock outputs: InputOutputBlock From 7a68db4274d5461f4eb22f4dc46496fba5fdd2cb Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Tue, 30 Jul 2024 15:33:43 +0100 Subject: [PATCH 04/13] Fix test to remove invalid definitions. --- packages/server/src/api/routes/tests/automation.spec.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/server/src/api/routes/tests/automation.spec.ts b/packages/server/src/api/routes/tests/automation.spec.ts index 990828dcde..f420c57cf7 100644 --- a/packages/server/src/api/routes/tests/automation.spec.ts +++ b/packages/server/src/api/routes/tests/automation.spec.ts @@ -14,6 +14,7 @@ import sdk from "../../../sdk" import { Automation, FieldType, Table } from "@budibase/types" import { mocks } from "@budibase/backend-core/tests" import { FilterConditions } from "../../../automations/steps/filter" +import { removeInvalidDefinitions } from "../../../automations/utils" const MAX_RETRIES = 4 let { @@ -69,14 +70,15 @@ describe("/automations", () => { .expect("Content-Type", /json/) .expect(200) - let definitionsLength = Object.keys(BUILTIN_ACTION_DEFINITIONS).length - definitionsLength-- // OUTGOING_WEBHOOK is deprecated + let definitionsLength = Object.keys( + removeInvalidDefinitions(BUILTIN_ACTION_DEFINITIONS) + ).length expect(Object.keys(res.body.action).length).toBeGreaterThanOrEqual( definitionsLength ) expect(Object.keys(res.body.trigger).length).toEqual( - Object.keys(TRIGGER_DEFINITIONS).length + Object.keys(removeInvalidDefinitions(TRIGGER_DEFINITIONS)).length ) }) }) From 8f7a88ce862e86f96469cff1bb5c8b07a55c3ad2 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Tue, 30 Jul 2024 16:04:33 +0100 Subject: [PATCH 05/13] Moving logic to frontend - definitions are returned unless they are deprecated, frontend decides which it can use to create. --- .../CreateAutomationModal.svelte | 4 +- .../grid/GridCreateAutomationButton.svelte | 2 +- .../builder/src/stores/builder/automations.js | 37 ++++++++++++++----- .../server/src/api/controllers/automation.ts | 9 ++--- .../src/api/routes/tests/automation.spec.ts | 6 +-- packages/server/src/automations/utils.ts | 8 +--- 6 files changed, 39 insertions(+), 27 deletions(-) diff --git a/packages/builder/src/components/automation/AutomationPanel/CreateAutomationModal.svelte b/packages/builder/src/components/automation/AutomationPanel/CreateAutomationModal.svelte index 41799cd7f3..365d3d358f 100644 --- a/packages/builder/src/components/automation/AutomationPanel/CreateAutomationModal.svelte +++ b/packages/builder/src/components/automation/AutomationPanel/CreateAutomationModal.svelte @@ -21,7 +21,9 @@ $: nameError = nameTouched && !name ? "Please specify a name for the automation." : null - $: triggers = Object.entries($automationStore.blockDefinitions.TRIGGER) + $: triggers = Object.entries( + $automationStore.blockDefinitions.CREATABLE_TRIGGER + ) async function createAutomation() { try { diff --git a/packages/builder/src/components/backend/DataTable/buttons/grid/GridCreateAutomationButton.svelte b/packages/builder/src/components/backend/DataTable/buttons/grid/GridCreateAutomationButton.svelte index 8e3d90be41..148db7554c 100644 --- a/packages/builder/src/components/backend/DataTable/buttons/grid/GridCreateAutomationButton.svelte +++ b/packages/builder/src/components/backend/DataTable/buttons/grid/GridCreateAutomationButton.svelte @@ -13,7 +13,7 @@ const { datasource } = getContext("grid") - $: triggers = $automationStore.blockDefinitions.TRIGGER + $: triggers = $automationStore.blockDefinitions.CREATABLE_TRIGGER $: table = $tables.list.find(table => table._id === $datasource.tableId) diff --git a/packages/builder/src/stores/builder/automations.js b/packages/builder/src/stores/builder/automations.js index cab8090424..fdb0991911 100644 --- a/packages/builder/src/stores/builder/automations.js +++ b/packages/builder/src/stores/builder/automations.js @@ -5,14 +5,16 @@ import { generate } from "shortid" import { createHistoryStore } from "stores/builder/history" import { notifications } from "@budibase/bbui" import { updateReferencesInObject } from "dataBinding" +import { AutomationTriggerStepId } from "@budibase/types" const initialAutomationState = { automations: [], testResults: null, showTestPanel: false, blockDefinitions: { - TRIGGER: [], - ACTION: [], + TRIGGER: {}, + CREATABLE_TRIGGER: {}, + ACTION: {}, }, selectedAutomationId: null, automationDisplayData: {}, @@ -46,14 +48,29 @@ const updateStepReferences = (steps, modifiedIndex, action) => { }) } +const getFinalDefinitions = (triggers, actions) => { + const creatable = {} + Object.entries(triggers).forEach(entry => { + if (entry[0] === AutomationTriggerStepId.ROW_ACTION) { + return + } + creatable[entry[0]] = entry[1] + }) + return { + TRIGGER: triggers, + CREATABLE_TRIGGER: creatable, + ACTION: actions, + } +} + const automationActions = store => ({ definitions: async () => { const response = await API.getAutomationDefinitions() store.update(state => { - state.blockDefinitions = { - TRIGGER: response.trigger, - ACTION: response.action, - } + state.blockDefinitions = getFinalDefinitions( + response.trigger, + response.action + ) return state }) return response @@ -69,10 +86,10 @@ const automationActions = store => ({ return a.name < b.name ? -1 : 1 }) state.automationDisplayData = automationResponse.builderData - state.blockDefinitions = { - TRIGGER: definitions.trigger, - ACTION: definitions.action, - } + state.blockDefinitions = getFinalDefinitions( + definitions.trigger, + definitions.action + ) return state }) }, diff --git a/packages/server/src/api/controllers/automation.ts b/packages/server/src/api/controllers/automation.ts index f2b9428f69..6177868303 100644 --- a/packages/server/src/api/controllers/automation.ts +++ b/packages/server/src/api/controllers/automation.ts @@ -1,10 +1,7 @@ import * as triggers from "../../automations/triggers" import { sdk as coreSdk } from "@budibase/shared-core" import { DocumentType } from "../../db/utils" -import { - updateTestHistory, - removeInvalidDefinitions, -} from "../../automations/utils" +import { updateTestHistory, removeDeprecated } from "../../automations/utils" import { setTestFlag, clearTestFlag } from "../../utilities/redis" import { context, cache, events, db as dbCore } from "@budibase/backend-core" import { automations, features } from "@budibase/pro" @@ -23,11 +20,11 @@ import { builderSocket } from "../../websockets" import env from "../../environment" async function getActionDefinitions() { - return removeInvalidDefinitions(await actionDefs()) + return removeDeprecated(await actionDefs()) } function getTriggerDefinitions() { - return removeInvalidDefinitions(triggers.TRIGGER_DEFINITIONS) + return removeDeprecated(triggers.TRIGGER_DEFINITIONS) } /************************* diff --git a/packages/server/src/api/routes/tests/automation.spec.ts b/packages/server/src/api/routes/tests/automation.spec.ts index f420c57cf7..d9d48ede38 100644 --- a/packages/server/src/api/routes/tests/automation.spec.ts +++ b/packages/server/src/api/routes/tests/automation.spec.ts @@ -14,7 +14,7 @@ import sdk from "../../../sdk" import { Automation, FieldType, Table } from "@budibase/types" import { mocks } from "@budibase/backend-core/tests" import { FilterConditions } from "../../../automations/steps/filter" -import { removeInvalidDefinitions } from "../../../automations/utils" +import { removeDeprecated } from "../../../automations/utils" const MAX_RETRIES = 4 let { @@ -71,14 +71,14 @@ describe("/automations", () => { .expect(200) let definitionsLength = Object.keys( - removeInvalidDefinitions(BUILTIN_ACTION_DEFINITIONS) + removeDeprecated(BUILTIN_ACTION_DEFINITIONS) ).length expect(Object.keys(res.body.action).length).toBeGreaterThanOrEqual( definitionsLength ) expect(Object.keys(res.body.trigger).length).toEqual( - Object.keys(removeInvalidDefinitions(TRIGGER_DEFINITIONS)).length + Object.keys(removeDeprecated(TRIGGER_DEFINITIONS)).length ) }) }) diff --git a/packages/server/src/automations/utils.ts b/packages/server/src/automations/utils.ts index e7f3dad225..775fdfe7fb 100644 --- a/packages/server/src/automations/utils.ts +++ b/packages/server/src/automations/utils.ts @@ -117,16 +117,12 @@ export async function updateTestHistory( ) } -export function removeInvalidDefinitions( +export function removeDeprecated( definitions: Record ) { - const disallowedStepIds: ( - | AutomationTriggerStepId - | AutomationActionStepId - )[] = [AutomationTriggerStepId.ROW_ACTION] const base = cloneDeep(definitions) for (let key of Object.keys(base)) { - if (base[key].deprecated || disallowedStepIds.includes(base[key].stepId)) { + if (base[key].deprecated) { delete base[key] } } From 8a7e0793d479203f95d069b9de85ce33539d8ab4 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Tue, 30 Jul 2024 16:07:09 +0100 Subject: [PATCH 06/13] Linting. --- packages/server/src/automations/utils.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/server/src/automations/utils.ts b/packages/server/src/automations/utils.ts index 775fdfe7fb..93b8f907fd 100644 --- a/packages/server/src/automations/utils.ts +++ b/packages/server/src/automations/utils.ts @@ -9,10 +9,8 @@ import { cloneDeep } from "lodash/fp" import { quotas } from "@budibase/pro" import { Automation, - AutomationActionStepId, AutomationJob, AutomationStepSchema, - AutomationTriggerStepId, } from "@budibase/types" import { automationsEnabled } from "../features" import { helpers, REBOOT_CRON } from "@budibase/shared-core" From 8280c2ccfd59c074f164d7e510875b8a0172d080 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 30 Jul 2024 17:22:21 +0200 Subject: [PATCH 07/13] Add databaseImpl.put tests --- .../src/db/couch/tests/DatabaseImpl.spec.ts | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 packages/backend-core/src/db/couch/tests/DatabaseImpl.spec.ts diff --git a/packages/backend-core/src/db/couch/tests/DatabaseImpl.spec.ts b/packages/backend-core/src/db/couch/tests/DatabaseImpl.spec.ts new file mode 100644 index 0000000000..8e86cd7f74 --- /dev/null +++ b/packages/backend-core/src/db/couch/tests/DatabaseImpl.spec.ts @@ -0,0 +1,47 @@ +import tk from "timekeeper" + +import { DatabaseImpl } from ".." + +import { generator, structures } from "../../../../tests" + +const initialTime = new Date() +tk.freeze(initialTime) + +describe("DatabaseImpl", () => { + const db = new DatabaseImpl(structures.db.id()) + + beforeEach(() => { + tk.freeze(initialTime) + }) + + describe("put", () => { + it("persists createdAt and updatedAt fields", async () => { + const id = generator.guid() + await db.put({ _id: id }) + + expect(await db.get(id)).toEqual({ + _id: id, + _rev: expect.any(String), + createdAt: initialTime.toISOString(), + updatedAt: initialTime.toISOString(), + }) + }) + + it("updates updated at fields", async () => { + const id = generator.guid() + + await db.put({ _id: id }) + tk.travel(100) + + await db.put({ ...(await db.get(id)), newValue: 123 }) + + expect(await db.get(id)).toEqual({ + _id: id, + _rev: expect.any(String), + newValue: 123, + createdAt: initialTime.toISOString(), + updatedAt: new Date().toISOString(), + }) + }) + }) +}) From e07bc5b5720546e9c5229ff4ca88bfb0f4182078 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 30 Jul 2024 17:26:29 +0200 Subject: [PATCH 08/13] Add bulkDocs test --- .../src/db/couch/tests/DatabaseImpl.spec.ts | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/packages/backend-core/src/db/couch/tests/DatabaseImpl.spec.ts b/packages/backend-core/src/db/couch/tests/DatabaseImpl.spec.ts index 8e86cd7f74..039564b243 100644 --- a/packages/backend-core/src/db/couch/tests/DatabaseImpl.spec.ts +++ b/packages/backend-core/src/db/couch/tests/DatabaseImpl.spec.ts @@ -44,4 +44,42 @@ describe("DatabaseImpl", () => { }) }) }) + + describe("bulkDocs", () => { + it("persists createdAt and updatedAt fields", async () => { + const ids = generator.unique(() => generator.guid(), 5) + await db.bulkDocs(ids.map(id => ({ _id: id }))) + + for (const id of ids) { + expect(await db.get(id)).toEqual({ + _id: id, + _rev: expect.any(String), + createdAt: initialTime.toISOString(), + updatedAt: initialTime.toISOString(), + }) + } + }) + + it("updates updated at fields", async () => { + const ids = generator.unique(() => generator.guid(), 5) + + await db.bulkDocs(ids.map(id => ({ _id: id }))) + tk.travel(100) + + const docsToUpdate = await Promise.all( + ids.map(async id => ({ ...(await db.get(id)), newValue: 123 })) + ) + await db.bulkDocs(docsToUpdate) + + for (const id of ids) { + expect(await db.get(id)).toEqual({ + _id: id, + _rev: expect.any(String), + newValue: 123, + createdAt: initialTime.toISOString(), + updatedAt: new Date().toISOString(), + }) + } + }) + }) }) From 61d73b1f598eaa82d1dfc22242c819af7c5ef620 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 30 Jul 2024 17:26:49 +0200 Subject: [PATCH 09/13] Add createdAd and updatedAt on bulkDocs --- packages/backend-core/src/db/couch/DatabaseImpl.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/backend-core/src/db/couch/DatabaseImpl.ts b/packages/backend-core/src/db/couch/DatabaseImpl.ts index feeba6061e..61fbb3d61e 100644 --- a/packages/backend-core/src/db/couch/DatabaseImpl.ts +++ b/packages/backend-core/src/db/couch/DatabaseImpl.ts @@ -308,8 +308,12 @@ export class DatabaseImpl implements Database { } async bulkDocs(documents: AnyDocument[]) { + const now = new Date().toISOString() return this.performCall(db => { - return () => db.bulk({ docs: documents }) + return () => + db.bulk({ + docs: documents.map(d => ({ createdAt: now, ...d, updatedAt: now })), + }) }) } From 643d57b58323172669e86cfe0e5005a96dfdc528 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 30 Jul 2024 17:29:03 +0200 Subject: [PATCH 10/13] Add extra tests --- .../src/db/couch/tests/DatabaseImpl.spec.ts | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/packages/backend-core/src/db/couch/tests/DatabaseImpl.spec.ts b/packages/backend-core/src/db/couch/tests/DatabaseImpl.spec.ts index 039564b243..89eecc3785 100644 --- a/packages/backend-core/src/db/couch/tests/DatabaseImpl.spec.ts +++ b/packages/backend-core/src/db/couch/tests/DatabaseImpl.spec.ts @@ -81,5 +81,38 @@ describe("DatabaseImpl", () => { }) } }) + + it("keeps existing createdAt", async () => { + const ids = generator.unique(() => generator.guid(), 2) + + await db.bulkDocs(ids.map(id => ({ _id: id }))) + tk.travel(100) + + const newDocs = generator + .unique(() => generator.guid(), 3) + .map(id => ({ _id: id })) + const docsToUpdate = await Promise.all( + ids.map(async id => ({ ...(await db.get(id)), newValue: 123 })) + ) + await db.bulkDocs([...newDocs, ...docsToUpdate]) + + for (const { _id } of docsToUpdate) { + expect(await db.get(_id)).toEqual({ + _id, + _rev: expect.any(String), + newValue: 123, + createdAt: initialTime.toISOString(), + updatedAt: new Date().toISOString(), + }) + } + for (const { _id } of newDocs) { + expect(await db.get(_id)).toEqual({ + _id, + _rev: expect.any(String), + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + }) + } + }) }) }) From 9cf50d1f725b76f007782b8851e352790f9df618 Mon Sep 17 00:00:00 2001 From: Budibase Staging Release Bot <> Date: Tue, 30 Jul 2024 15:38:19 +0000 Subject: [PATCH 11/13] Bump version to 2.29.26 --- lerna.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lerna.json b/lerna.json index 5e28c36166..1728208df0 100644 --- a/lerna.json +++ b/lerna.json @@ -1,6 +1,6 @@ { "$schema": "node_modules/lerna/schemas/lerna-schema.json", - "version": "2.29.25", + "version": "2.29.26", "npmClient": "yarn", "packages": [ "packages/*", From 0a35ae012f6cd3f1a7fce8c63738c706cc28a5f9 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 30 Jul 2024 11:53:00 +0200 Subject: [PATCH 12/13] Prevent test container name conflicts --- packages/server/src/integrations/tests/utils/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/src/integrations/tests/utils/index.ts b/packages/server/src/integrations/tests/utils/index.ts index 8e317d9099..5d893fda9c 100644 --- a/packages/server/src/integrations/tests/utils/index.ts +++ b/packages/server/src/integrations/tests/utils/index.ts @@ -77,7 +77,7 @@ export async function startContainer(container: GenericContainer) { container = container .withReuse() .withLabels({ "com.budibase": "true" }) - .withName(key) + .withName(`${key}_testcontainer`) let startedContainer: StartedTestContainer | undefined = undefined let lastError = undefined From cc1e466db91082d04f5d7e24b23b41876fb024aa Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Tue, 30 Jul 2024 17:57:21 +0100 Subject: [PATCH 13/13] Add Oracle to table.spec.ts --- .../server/src/api/routes/tests/table.spec.ts | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/packages/server/src/api/routes/tests/table.spec.ts b/packages/server/src/api/routes/tests/table.spec.ts index 20c83549d2..a8bf9447e8 100644 --- a/packages/server/src/api/routes/tests/table.spec.ts +++ b/packages/server/src/api/routes/tests/table.spec.ts @@ -9,6 +9,7 @@ import { RelationshipType, Row, SaveTableRequest, + SourceName, Table, TableSourceType, User, @@ -33,7 +34,8 @@ describe.each([ [DatabaseName.MYSQL, getDatasource(DatabaseName.MYSQL)], [DatabaseName.SQL_SERVER, getDatasource(DatabaseName.SQL_SERVER)], [DatabaseName.MARIADB, getDatasource(DatabaseName.MARIADB)], -])("/tables (%s)", (_, dsProvider) => { + [DatabaseName.ORACLE, getDatasource(DatabaseName.ORACLE)], +])("/tables (%s)", (name, dsProvider) => { const isInternal: boolean = !dsProvider let datasource: Datasource | undefined let config = setup.getConfig() @@ -52,15 +54,20 @@ describe.each([ jest.clearAllMocks() }) - it.each([ + let names = [ "alphanum", "with spaces", "with-dashes", "with_underscores", - 'with "double quotes"', - "with 'single quotes'", "with `backticks`", - ])("creates a table with name: %s", async name => { + ] + + if (name !== DatabaseName.ORACLE) { + names.push(`with "double quotes"`) + names.push(`with 'single quotes'`) + } + + it.each(names)("creates a table with name: %s", async name => { const table = await config.api.table.save( tableForDatasource(datasource, { name }) )