From a55573451975e3e00bffa24531c125d912d2cf7d Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Fri, 26 Jan 2024 10:52:35 +0100 Subject: [PATCH 01/25] Use api calls for createExternalTable test helper --- .../server/src/tests/utilities/TestConfiguration.ts | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/packages/server/src/tests/utilities/TestConfiguration.ts b/packages/server/src/tests/utilities/TestConfiguration.ts index 4785994da4..226dfc7663 100644 --- a/packages/server/src/tests/utilities/TestConfiguration.ts +++ b/packages/server/src/tests/utilities/TestConfiguration.ts @@ -618,7 +618,7 @@ class TestConfiguration { async createExternalTable( config?: TableToBuild, options = { skipReassigning: false } - ) { + ): Promise { if (config != null && config._id) { delete config._id } @@ -627,7 +627,16 @@ class TestConfiguration { config.sourceId = this.datasource._id config.sourceType = TableSourceType.EXTERNAL } - return this.updateTable(config, options) + const table = await this.api.table.create({ + ...config, + sourceType: config.sourceType || TableSourceType.INTERNAL, + sourceId: config.sourceId || INTERNAL_TABLE_SOURCE_ID, + }) + if (!options.skipReassigning) { + this.table = table + } + + return table } async getTable(tableId?: string) { From cb53d31833f4715a950748ed78cfe8ff25736b65 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Fri, 26 Jan 2024 10:56:38 +0100 Subject: [PATCH 02/25] Renames --- packages/server/src/api/routes/tests/row.spec.ts | 4 ++-- packages/server/src/api/routes/tests/table.spec.ts | 10 +++++----- .../server/src/automations/tests/updateRow.spec.ts | 4 ++-- packages/server/src/sdk/tests/tables.spec.ts | 2 +- packages/server/src/tests/utilities/api/table.ts | 2 +- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/server/src/api/routes/tests/row.spec.ts b/packages/server/src/api/routes/tests/row.spec.ts index 5b39652976..f13d645be3 100644 --- a/packages/server/src/api/routes/tests/row.spec.ts +++ b/packages/server/src/api/routes/tests/row.spec.ts @@ -581,7 +581,7 @@ describe.each([ tableId: InternalTable.USER_METADATA, } - let table = await config.api.table.create({ + let table = await config.api.table.save({ name: "TestTable", type: "table", sourceType: TableSourceType.INTERNAL, @@ -1690,7 +1690,7 @@ describe.each([ tableConfig.sourceType = TableSourceType.EXTERNAL } } - const table = await config.api.table.create({ + const table = await config.api.table.save({ ...tableConfig, schema: { ...tableConfig.schema, diff --git a/packages/server/src/api/routes/tests/table.spec.ts b/packages/server/src/api/routes/tests/table.spec.ts index 4743bca814..704b30a7ed 100644 --- a/packages/server/src/api/routes/tests/table.spec.ts +++ b/packages/server/src/api/routes/tests/table.spec.ts @@ -438,7 +438,7 @@ describe("/tables", () => { }) it("should successfully migrate a one-to-many user relationship to a user column", async () => { - const table = await config.api.table.create({ + const table = await config.api.table.save({ name: "table", type: "table", sourceId: INTERNAL_TABLE_SOURCE_ID, @@ -496,7 +496,7 @@ describe("/tables", () => { // We found a bug just after releasing this feature where if the row was created from the // users table, not the table linking to it, the migration would succeed but lose the data. // This happened because the order of the documents in the link was reversed. - const table = await config.api.table.create({ + const table = await config.api.table.save({ name: "table", type: "table", sourceId: INTERNAL_TABLE_SOURCE_ID, @@ -554,7 +554,7 @@ describe("/tables", () => { }) it("should successfully migrate a many-to-many user relationship to a users column", async () => { - const table = await config.api.table.create({ + const table = await config.api.table.save({ name: "table", type: "table", sourceId: INTERNAL_TABLE_SOURCE_ID, @@ -611,7 +611,7 @@ describe("/tables", () => { }) it("should successfully migrate a many-to-one user relationship to a users column", async () => { - const table = await config.api.table.create({ + const table = await config.api.table.save({ name: "table", type: "table", sourceId: INTERNAL_TABLE_SOURCE_ID, @@ -670,7 +670,7 @@ describe("/tables", () => { describe("unhappy paths", () => { let table: Table beforeAll(async () => { - table = await config.api.table.create({ + table = await config.api.table.save({ name: "table", type: "table", sourceId: INTERNAL_TABLE_SOURCE_ID, diff --git a/packages/server/src/automations/tests/updateRow.spec.ts b/packages/server/src/automations/tests/updateRow.spec.ts index 7e369f1ecb..b64c52147d 100644 --- a/packages/server/src/automations/tests/updateRow.spec.ts +++ b/packages/server/src/automations/tests/updateRow.spec.ts @@ -67,7 +67,7 @@ describe("test the update row action", () => { tableId: InternalTable.USER_METADATA, } - let table = await config.api.table.create({ + let table = await config.api.table.save({ name: uuid.v4(), type: "table", sourceType: TableSourceType.INTERNAL, @@ -120,7 +120,7 @@ describe("test the update row action", () => { tableId: InternalTable.USER_METADATA, } - let table = await config.api.table.create({ + let table = await config.api.table.save({ name: uuid.v4(), type: "table", sourceType: TableSourceType.INTERNAL, diff --git a/packages/server/src/sdk/tests/tables.spec.ts b/packages/server/src/sdk/tests/tables.spec.ts index 0e3cd73cfd..00099b0df6 100644 --- a/packages/server/src/sdk/tests/tables.spec.ts +++ b/packages/server/src/sdk/tests/tables.spec.ts @@ -9,7 +9,7 @@ describe("tables", () => { beforeAll(async () => { await config.init() - table = await config.api.table.create(basicTable()) + table = await config.api.table.save(basicTable()) }) describe("getTables", () => { diff --git a/packages/server/src/tests/utilities/api/table.ts b/packages/server/src/tests/utilities/api/table.ts index ffd9e19ee8..5a9654e3bc 100644 --- a/packages/server/src/tests/utilities/api/table.ts +++ b/packages/server/src/tests/utilities/api/table.ts @@ -13,7 +13,7 @@ export class TableAPI extends TestAPI { super(config) } - create = async ( + save = async ( data: SaveTableRequest, { expectStatus } = { expectStatus: 200 } ): Promise => { From da9f367962809e0b49be5fa046cf41fd7cdf5137 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Fri, 26 Jan 2024 10:57:05 +0100 Subject: [PATCH 03/25] Use api calls for updateTable test helper --- .../src/tests/utilities/TestConfiguration.ts | 21 +++++++------------ 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/packages/server/src/tests/utilities/TestConfiguration.ts b/packages/server/src/tests/utilities/TestConfiguration.ts index 226dfc7663..90fa535ac8 100644 --- a/packages/server/src/tests/utilities/TestConfiguration.ts +++ b/packages/server/src/tests/utilities/TestConfiguration.ts @@ -592,9 +592,11 @@ class TestConfiguration { { skipReassigning } = { skipReassigning: false } ): Promise
{ config = config || basicTable() - config.sourceType = config.sourceType || TableSourceType.INTERNAL - config.sourceId = config.sourceId || INTERNAL_TABLE_SOURCE_ID - const response = await this._req(config, null, controllers.table.save) + const response = await this.api.table.save({ + ...config, + sourceType: config.sourceType || TableSourceType.INTERNAL, + sourceId: config.sourceId || INTERNAL_TABLE_SOURCE_ID, + }) if (!skipReassigning) { this.table = response } @@ -618,7 +620,7 @@ class TestConfiguration { async createExternalTable( config?: TableToBuild, options = { skipReassigning: false } - ): Promise
{ + ) { if (config != null && config._id) { delete config._id } @@ -627,16 +629,7 @@ class TestConfiguration { config.sourceId = this.datasource._id config.sourceType = TableSourceType.EXTERNAL } - const table = await this.api.table.create({ - ...config, - sourceType: config.sourceType || TableSourceType.INTERNAL, - sourceId: config.sourceId || INTERNAL_TABLE_SOURCE_ID, - }) - if (!options.skipReassigning) { - this.table = table - } - - return table + return this.updateTable(config, options) } async getTable(tableId?: string) { From c0581e41d31eca08efa07556e5cfa9129c29289c Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Fri, 26 Jan 2024 10:57:35 +0100 Subject: [PATCH 04/25] Renames --- packages/server/src/api/routes/public/tests/compare.spec.ts | 6 +++--- packages/server/src/tests/utilities/TestConfiguration.ts | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/server/src/api/routes/public/tests/compare.spec.ts b/packages/server/src/api/routes/public/tests/compare.spec.ts index f9616b06e1..ccf248f360 100644 --- a/packages/server/src/api/routes/public/tests/compare.spec.ts +++ b/packages/server/src/api/routes/public/tests/compare.spec.ts @@ -12,7 +12,7 @@ let apiKey: string, table: Table, app: App, makeRequest: any beforeAll(async () => { app = await config.init() - table = await config.updateTable() + table = await config.upsertTable() apiKey = await config.generateApiKey() makeRequest = generateMakeRequest(apiKey) }) @@ -69,7 +69,7 @@ describe("check the applications endpoints", () => { describe("check the tables endpoints", () => { it("should allow retrieving tables through search", async () => { await config.createApp("new app 1") - table = await config.updateTable() + table = await config.upsertTable() const res = await makeRequest("post", "/tables/search") expect(res).toSatisfyApiSpec() }) @@ -108,7 +108,7 @@ describe("check the tables endpoints", () => { describe("check the rows endpoints", () => { let row: Row it("should allow retrieving rows through search", async () => { - table = await config.updateTable() + table = await config.upsertTable() const res = await makeRequest("post", `/tables/${table._id}/rows/search`, { query: {}, }) diff --git a/packages/server/src/tests/utilities/TestConfiguration.ts b/packages/server/src/tests/utilities/TestConfiguration.ts index 90fa535ac8..2db5a81674 100644 --- a/packages/server/src/tests/utilities/TestConfiguration.ts +++ b/packages/server/src/tests/utilities/TestConfiguration.ts @@ -587,7 +587,7 @@ class TestConfiguration { // TABLE - async updateTable( + async upsertTable( config?: TableToBuild, { skipReassigning } = { skipReassigning: false } ): Promise
{ @@ -614,7 +614,7 @@ class TestConfiguration { if (!config.sourceId) { config.sourceId = INTERNAL_TABLE_SOURCE_ID } - return this.updateTable(config, options) + return this.upsertTable(config, options) } async createExternalTable( @@ -629,7 +629,7 @@ class TestConfiguration { config.sourceId = this.datasource._id config.sourceType = TableSourceType.EXTERNAL } - return this.updateTable(config, options) + return this.upsertTable(config, options) } async getTable(tableId?: string) { From ce81248e664ccc991c257b07aa8f9e670ba193bb Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Fri, 26 Jan 2024 10:58:46 +0100 Subject: [PATCH 05/25] Use api calls for getTable test helper --- packages/server/src/tests/utilities/TestConfiguration.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/src/tests/utilities/TestConfiguration.ts b/packages/server/src/tests/utilities/TestConfiguration.ts index 2db5a81674..7bdfd98c7f 100644 --- a/packages/server/src/tests/utilities/TestConfiguration.ts +++ b/packages/server/src/tests/utilities/TestConfiguration.ts @@ -634,7 +634,7 @@ class TestConfiguration { async getTable(tableId?: string) { tableId = tableId || this.table!._id! - return this._req(null, { tableId }, controllers.table.find) + return this.api.table.get(tableId) } async createLinkedTable( From b3a59dfa7f9b5668bf0102e31fa94804c4864487 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Fri, 26 Jan 2024 11:00:03 +0100 Subject: [PATCH 06/25] Use api calls for createRow test helper --- packages/server/src/tests/utilities/TestConfiguration.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/server/src/tests/utilities/TestConfiguration.ts b/packages/server/src/tests/utilities/TestConfiguration.ts index 7bdfd98c7f..52e75d4443 100644 --- a/packages/server/src/tests/utilities/TestConfiguration.ts +++ b/packages/server/src/tests/utilities/TestConfiguration.ts @@ -682,9 +682,9 @@ class TestConfiguration { if (!this.table) { throw "Test requires table to be configured." } - const tableId = (config && config.tableId) || this.table._id + const tableId = (config && config.tableId) || this.table._id! config = config || basicRow(tableId!) - return this._req(config, { tableId }, controllers.row.save) + return this.api.row.save(tableId, config) } async getRow(tableId: string, rowId: string): Promise { From 9592f25b66255fcc0f3ea08e26c0027ffbe077ae Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Fri, 26 Jan 2024 11:01:16 +0100 Subject: [PATCH 07/25] Use api calls for getRow/s test helper --- packages/server/src/tests/utilities/TestConfiguration.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/server/src/tests/utilities/TestConfiguration.ts b/packages/server/src/tests/utilities/TestConfiguration.ts index 52e75d4443..8029a04914 100644 --- a/packages/server/src/tests/utilities/TestConfiguration.ts +++ b/packages/server/src/tests/utilities/TestConfiguration.ts @@ -688,14 +688,14 @@ class TestConfiguration { } async getRow(tableId: string, rowId: string): Promise { - return this._req(null, { tableId, rowId }, controllers.row.find) + return this.api.row.get(tableId, rowId) } async getRows(tableId: string) { if (!tableId && this.table) { tableId = this.table._id! } - return this._req(null, { tableId }, controllers.row.fetch) + return this.api.row.fetch(tableId) } async searchRows(tableId: string, searchParams: SearchFilters = {}) { From 12a08c6864b5141d6abc573b7e8d1bd6697a3185 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Fri, 26 Jan 2024 11:07:06 +0100 Subject: [PATCH 08/25] Import specific controllers --- .../src/tests/utilities/TestConfiguration.ts | 41 +++++++++++-------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/packages/server/src/tests/utilities/TestConfiguration.ts b/packages/server/src/tests/utilities/TestConfiguration.ts index 8029a04914..e097f2ca27 100644 --- a/packages/server/src/tests/utilities/TestConfiguration.ts +++ b/packages/server/src/tests/utilities/TestConfiguration.ts @@ -27,6 +27,18 @@ import { sessions, tenancy, } from "@budibase/backend-core" +import { + app as appController, + deploy as deployController, + role as roleController, + automation as automationController, + webhook as webhookController, + query as queryController, + screen as screenController, + layout as layoutController, + view as viewController, +} from "./controllers" + import * as controllers from "./controllers" import { cleanup } from "../../utilities/fileSystem" import newid from "../../db/newid" @@ -543,11 +555,7 @@ class TestConfiguration { // clear any old app this.appId = null this.app = await context.doInTenant(this.tenantId!, async () => { - const app = await this._req( - { name: appName }, - null, - controllers.app.create - ) + const app = await this._req({ name: appName }, null, appController.create) this.appId = app.appId! return app }) @@ -563,7 +571,7 @@ class TestConfiguration { } async publish() { - await this._req(null, null, controllers.deploy.publishApp) + await this._req(null, null, deployController.publishApp) // @ts-ignore const prodAppId = this.getAppId().replace("_dev", "") this.prodAppId = prodAppId @@ -578,7 +586,7 @@ class TestConfiguration { const response = await this._req( null, { appId: this.appId }, - controllers.app.unpublish + appController.unpublish ) this.prodAppId = null this.prodApp = null @@ -712,7 +720,7 @@ class TestConfiguration { async createRole(config?: any) { config = config || basicRole() - return this._req(config, null, controllers.role.save) + return this._req(config, null, roleController.save) } // VIEW @@ -725,7 +733,7 @@ class TestConfiguration { tableId: this.table!._id, name: generator.guid(), } - return this._req(view, null, controllers.view.v1.save) + return this._req(view, null, viewController.v1.save) } async createView( @@ -755,13 +763,13 @@ class TestConfiguration { delete config._rev } this.automation = ( - await this._req(config, null, controllers.automation.create) + await this._req(config, null, automationController.create) ).automation return this.automation } async getAllAutomations() { - return this._req(null, null, controllers.automation.fetch) + return this._req(null, null, automationController.fetch) } async deleteAutomation(automation?: any) { @@ -772,7 +780,7 @@ class TestConfiguration { return this._req( null, { id: automation._id, rev: automation._rev }, - controllers.automation.destroy + automationController.destroy ) } @@ -781,7 +789,8 @@ class TestConfiguration { throw "Must create an automation before creating webhook." } config = config || basicWebhook(this.automation._id) - return (await this._req(config, null, controllers.webhook.save)).webhook + + return (await this._req(config, null, webhookController.save)).webhook } // DATASOURCE @@ -888,21 +897,21 @@ class TestConfiguration { throw "No datasource created for query." } config = config || basicQuery(this.datasource!._id!) - return this._req(config, null, controllers.query.save) + return this._req(config, null, queryController.save) } // SCREEN async createScreen(config?: any) { config = config || basicScreen() - return this._req(config, null, controllers.screen.save) + return this._req(config, null, screenController.save) } // LAYOUT async createLayout(config?: any) { config = config || basicLayout() - return await this._req(config, null, controllers.layout.save) + return await this._req(config, null, layoutController.save) } } From 39d0382a568069cdf2e8da33704b8150b39f323a Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Fri, 26 Jan 2024 11:11:54 +0100 Subject: [PATCH 09/25] Remove controller.datasource usages --- .../server/src/tests/utilities/TestConfiguration.ts | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/packages/server/src/tests/utilities/TestConfiguration.ts b/packages/server/src/tests/utilities/TestConfiguration.ts index e097f2ca27..adb36ee491 100644 --- a/packages/server/src/tests/utilities/TestConfiguration.ts +++ b/packages/server/src/tests/utilities/TestConfiguration.ts @@ -799,18 +799,14 @@ class TestConfiguration { datasource: Datasource }): Promise { config = config || basicDatasource() - const response = await this._req(config, null, controllers.datasource.save) - this.datasource = response.datasource + const response = await this.api.datasource.create(config.datasource) + this.datasource = response return this.datasource! } async updateDatasource(datasource: Datasource): Promise { - const response = await this._req( - datasource, - { datasourceId: datasource._id }, - controllers.datasource.update - ) - this.datasource = response.datasource + const response = await this.api.datasource.update(datasource) + this.datasource = response return this.datasource! } From f1e90b8b016d81b0a68f1a8a69dca64a0faafac0 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Fri, 26 Jan 2024 11:24:32 +0100 Subject: [PATCH 10/25] Remove last controller.row usages --- .../server/src/tests/utilities/TestConfiguration.ts | 10 +++------- packages/server/src/tests/utilities/api/row.ts | 3 +++ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/packages/server/src/tests/utilities/TestConfiguration.ts b/packages/server/src/tests/utilities/TestConfiguration.ts index adb36ee491..f7f49e1338 100644 --- a/packages/server/src/tests/utilities/TestConfiguration.ts +++ b/packages/server/src/tests/utilities/TestConfiguration.ts @@ -39,7 +39,6 @@ import { view as viewController, } from "./controllers" -import * as controllers from "./controllers" import { cleanup } from "../../utilities/fileSystem" import newid from "../../db/newid" import { generateUserMetadataID } from "../../db/utils" @@ -56,7 +55,7 @@ import { RelationshipFieldMetadata, RelationshipType, Row, - SearchFilters, + SearchParams, SourceName, Table, TableSourceType, @@ -706,14 +705,11 @@ class TestConfiguration { return this.api.row.fetch(tableId) } - async searchRows(tableId: string, searchParams: SearchFilters = {}) { + async searchRows(tableId: string, searchParams?: SearchParams) { if (!tableId && this.table) { tableId = this.table._id! } - const body = { - query: searchParams, - } - return this._req(body, { tableId }, controllers.row.search) + return this.api.row.search(tableId, searchParams) } // ROLE diff --git a/packages/server/src/tests/utilities/api/row.ts b/packages/server/src/tests/utilities/api/row.ts index 3d4cf6c82c..936c906f9f 100644 --- a/packages/server/src/tests/utilities/api/row.ts +++ b/packages/server/src/tests/utilities/api/row.ts @@ -7,6 +7,7 @@ import { BulkImportRequest, BulkImportResponse, SearchRowResponse, + SearchParams, } from "@budibase/types" import TestConfiguration from "../TestConfiguration" import { TestAPI } from "./base" @@ -154,10 +155,12 @@ export class RowAPI extends TestAPI { search = async ( sourceId: string, + params?: SearchParams, { expectStatus } = { expectStatus: 200 } ): Promise => { const request = this.request .post(`/api/${sourceId}/search`) + .send(params) .set(this.config.defaultHeaders()) .expect(expectStatus) From aca7b50dc571b6582615f5933d538135e50f77c8 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Mon, 29 Jan 2024 15:27:09 +0100 Subject: [PATCH 11/25] Fix tests --- packages/server/src/db/tests/linkController.spec.ts | 2 +- packages/server/src/sdk/app/rows/tests/internal.spec.ts | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/server/src/db/tests/linkController.spec.ts b/packages/server/src/db/tests/linkController.spec.ts index 7f575c7974..ae1922db27 100644 --- a/packages/server/src/db/tests/linkController.spec.ts +++ b/packages/server/src/db/tests/linkController.spec.ts @@ -324,7 +324,7 @@ describe("test the link controller", () => { name: "link", autocolumn: true, } - await config.updateTable(table) + await config.upsertTable(table) }) it("should be able to remove a linked field from a table, even if the linked table does not exist", async () => { diff --git a/packages/server/src/sdk/app/rows/tests/internal.spec.ts b/packages/server/src/sdk/app/rows/tests/internal.spec.ts index f472608ca0..dda41d5720 100644 --- a/packages/server/src/sdk/app/rows/tests/internal.spec.ts +++ b/packages/server/src/sdk/app/rows/tests/internal.spec.ts @@ -8,10 +8,10 @@ import { FieldType, Table, AutoFieldSubType, + AutoColumnFieldMetadata, } from "@budibase/types" import TestConfiguration from "../../../../tests/utilities/TestConfiguration" -import { cache } from "@budibase/backend-core" tk.freeze(Date.now()) @@ -213,8 +213,10 @@ describe("sdk >> rows >> internal", () => { ) const persistedTable = await config.getTable(table._id) - expect((table as any).schema.id.lastID).toBe(0) - expect(persistedTable.schema.id.lastID).toBe(20) + expect((table.schema.id as AutoColumnFieldMetadata).lastID).toBe(0) + expect((persistedTable.schema.id as AutoColumnFieldMetadata).lastID).toBe( + 20 + ) }) }) }) From f3f6e0c41b8375604a5f88d901aff36c1b0d53a5 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Mon, 29 Jan 2024 15:43:35 +0100 Subject: [PATCH 12/25] Fix tests --- packages/server/src/tests/utilities/TestConfiguration.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/server/src/tests/utilities/TestConfiguration.ts b/packages/server/src/tests/utilities/TestConfiguration.ts index f7f49e1338..61ab750c4a 100644 --- a/packages/server/src/tests/utilities/TestConfiguration.ts +++ b/packages/server/src/tests/utilities/TestConfiguration.ts @@ -695,7 +695,8 @@ class TestConfiguration { } async getRow(tableId: string, rowId: string): Promise { - return this.api.row.get(tableId, rowId) + const res = await this.api.row.get(tableId, rowId) + return res.body } async getRows(tableId: string) { From 51406a80da0cdee5813c51c30f7408a1b7c12c20 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Mon, 29 Jan 2024 16:18:12 +0100 Subject: [PATCH 13/25] Fix test --- .../server/src/api/controllers/query/import/tests/index.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/src/api/controllers/query/import/tests/index.spec.js b/packages/server/src/api/controllers/query/import/tests/index.spec.js index cef27abb08..fcbd4509ee 100644 --- a/packages/server/src/api/controllers/query/import/tests/index.spec.js +++ b/packages/server/src/api/controllers/query/import/tests/index.spec.js @@ -42,7 +42,7 @@ const datasets = { } describe("Rest Importer", () => { - const config = new TestConfig(false) + const config = new TestConfig() beforeAll(async () => { await config.init() From 9e4a2542e10162f7c4afd1dbc03ec1fb5348f9b5 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Mon, 29 Jan 2024 19:17:07 +0100 Subject: [PATCH 14/25] Fix test --- .../src/migrations/functions/usageQuotas/tests/syncRows.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/src/migrations/functions/usageQuotas/tests/syncRows.spec.ts b/packages/server/src/migrations/functions/usageQuotas/tests/syncRows.spec.ts index fc0f9e1aa9..e644d605b6 100644 --- a/packages/server/src/migrations/functions/usageQuotas/tests/syncRows.spec.ts +++ b/packages/server/src/migrations/functions/usageQuotas/tests/syncRows.spec.ts @@ -5,7 +5,7 @@ import { QuotaUsageType, StaticQuotaName } from "@budibase/types" import { db as dbCore, context } from "@budibase/backend-core" describe("syncRows", () => { - let config = new TestConfig(false) + const config = new TestConfig() beforeEach(async () => { await config.init() From ff75fbf99e9ce748f8f7cf82278cf47f16296060 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Mon, 29 Jan 2024 22:57:20 +0100 Subject: [PATCH 15/25] Convert query.sec.spec.ts to ts --- .../{query.seq.spec.js => query.seq.spec.ts} | 68 ++++++++++++------- .../src/tests/utilities/TestConfiguration.ts | 4 +- .../server/src/tests/utilities/structures.ts | 5 +- 3 files changed, 49 insertions(+), 28 deletions(-) rename packages/server/src/api/routes/tests/{query.seq.spec.js => query.seq.spec.ts} (91%) diff --git a/packages/server/src/api/routes/tests/query.seq.spec.js b/packages/server/src/api/routes/tests/query.seq.spec.ts similarity index 91% rename from packages/server/src/api/routes/tests/query.seq.spec.js rename to packages/server/src/api/routes/tests/query.seq.spec.ts index b15a5f4d6d..5a3219d08f 100644 --- a/packages/server/src/api/routes/tests/query.seq.spec.js +++ b/packages/server/src/api/routes/tests/query.seq.spec.ts @@ -1,4 +1,4 @@ -const tk = require("timekeeper") +import tk from "timekeeper" tk.freeze(Date.now()) // Mock out postgres for this @@ -17,16 +17,21 @@ jest.mock("@budibase/backend-core", () => { }, } }) -const setup = require("./utilities") -const { checkBuilderEndpoint } = require("./utilities/TestFunctions") -const { checkCacheForDynamicVariable } = require("../../../threads/utils") +import * as setup from "./utilities" +import { checkBuilderEndpoint } from "./utilities/TestFunctions" +import { checkCacheForDynamicVariable } from "../../../threads/utils" const { basicQuery, basicDatasource } = setup.structures -const { events, db: dbCore } = require("@budibase/backend-core") +import { events, db as dbCore } from "@budibase/backend-core" +import { Datasource, Query, SourceName } from "@budibase/types" + +const mockIsProdAppID = dbCore.isProdAppID as jest.MockedFunction< + typeof dbCore.isProdAppID +> describe("/queries", () => { let request = setup.getRequest() let config = setup.getConfig() - let datasource, query + let datasource: Datasource & Required>, query: Query afterAll(setup.afterAll) @@ -44,14 +49,14 @@ describe("/queries", () => { const datasource = await config.createDatasource({ datasource: { ...basicDatasource().datasource, - source: "INVALID_INTEGRATION", + source: "INVALID_INTEGRATION" as SourceName, }, }) const query = await config.createQuery() return { datasource, query } } - const createQuery = async query => { + const createQuery = async (query: Query) => { return request .post(`/api/queries`) .send(query) @@ -67,7 +72,7 @@ describe("/queries", () => { jest.clearAllMocks() const res = await createQuery(query) - expect(res.res.statusMessage).toEqual( + expect((res as any).res.statusMessage).toEqual( `Query ${query.name} saved successfully.` ) expect(res.body).toEqual({ @@ -92,7 +97,7 @@ describe("/queries", () => { query._rev = res.body._rev await createQuery(query) - expect(res.res.statusMessage).toEqual( + expect((res as any).res.statusMessage).toEqual( `Query ${query.name} saved successfully.` ) expect(res.body).toEqual({ @@ -168,8 +173,8 @@ describe("/queries", () => { it("should remove sensitive info for prod apps", async () => { // Mock isProdAppID to pretend we are using a prod app - dbCore.isProdAppID.mockClear() - dbCore.isProdAppID.mockImplementation(() => true) + mockIsProdAppID.mockClear() + mockIsProdAppID.mockImplementation(() => true) const query = await config.createQuery() const res = await request @@ -184,7 +189,7 @@ describe("/queries", () => { // Reset isProdAppID mock expect(dbCore.isProdAppID).toHaveBeenCalledTimes(1) - dbCore.isProdAppID.mockImplementation(() => false) + mockIsProdAppID.mockImplementation(() => false) }) }) @@ -211,10 +216,11 @@ describe("/queries", () => { }) it("should apply authorization to endpoint", async () => { + const query = await config.createQuery() await checkBuilderEndpoint({ config, method: "DELETE", - url: `/api/queries/${config._id}/${config._rev}`, + url: `/api/queries/${query._id}/${query._rev}`, }) }) }) @@ -284,8 +290,8 @@ describe("/queries", () => { }) describe("variables", () => { - async function preview(datasource, fields) { - return config.previewQuery(request, config, datasource, fields) + async function preview(datasource: Datasource, fields: any) { + return config.previewQuery(request, config, datasource, fields, undefined) } it("should work with static variables", async () => { @@ -370,11 +376,19 @@ describe("/queries", () => { }) describe("Current User Request Mapping", () => { - async function previewGet(datasource, fields, params) { + async function previewGet( + datasource: Datasource, + fields: any, + params: any + ) { return config.previewQuery(request, config, datasource, fields, params) } - async function previewPost(datasource, fields, params) { + async function previewPost( + datasource: Datasource, + fields: any, + params: any + ) { return config.previewQuery( request, config, @@ -394,14 +408,18 @@ describe("/queries", () => { emailHdr: "{{[user].[email]}}", }, }) - const res = await previewGet(datasource, { - path: "www.google.com", - queryString: "email={{[user].[email]}}", - headers: { - queryHdr: "{{[user].[firstName]}}", - secondHdr: "1234", + const res = await previewGet( + datasource, + { + path: "www.google.com", + queryString: "email={{[user].[email]}}", + headers: { + queryHdr: "{{[user].[firstName]}}", + secondHdr: "1234", + }, }, - }) + undefined + ) const parsedRequest = JSON.parse(res.body.extra.raw) expect(parsedRequest.opts.headers).toEqual({ diff --git a/packages/server/src/tests/utilities/TestConfiguration.ts b/packages/server/src/tests/utilities/TestConfiguration.ts index 61ab750c4a..58e1ae9e0e 100644 --- a/packages/server/src/tests/utilities/TestConfiguration.ts +++ b/packages/server/src/tests/utilities/TestConfiguration.ts @@ -794,11 +794,11 @@ class TestConfiguration { async createDatasource(config?: { datasource: Datasource - }): Promise { + }): Promise>> { config = config || basicDatasource() const response = await this.api.datasource.create(config.datasource) this.datasource = response - return this.datasource! + return { ...this.datasource, _id: this.datasource!._id! } } async updateDatasource(datasource: Datasource): Promise { diff --git a/packages/server/src/tests/utilities/structures.ts b/packages/server/src/tests/utilities/structures.ts index 80aad3c1e2..cd2389e2eb 100644 --- a/packages/server/src/tests/utilities/structures.ts +++ b/packages/server/src/tests/utilities/structures.ts @@ -23,6 +23,7 @@ import { TableSourceType, AutomationIOType, } from "@budibase/types" +import { Query } from "@budibase/types" const { BUILTIN_ROLE_IDS } = roles @@ -360,7 +361,7 @@ export function basicDatasource(): { datasource: Datasource } { } } -export function basicQuery(datasourceId: string) { +export function basicQuery(datasourceId: string): Query { return { datasourceId: datasourceId, name: "New Query", @@ -368,6 +369,8 @@ export function basicQuery(datasourceId: string) { fields: {}, schema: {}, queryVerb: "read", + transformer: null, + readable: true, } } From 7394a7cd64f43581b1ecd5725fb81dcd9ddf41ca Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Mon, 29 Jan 2024 23:19:07 +0100 Subject: [PATCH 16/25] Smart typing --- .../src/api/routes/tests/query.seq.spec.ts | 28 ++++++------------- .../src/tests/utilities/api/datasource.ts | 18 ++++++++---- 2 files changed, 21 insertions(+), 25 deletions(-) diff --git a/packages/server/src/api/routes/tests/query.seq.spec.ts b/packages/server/src/api/routes/tests/query.seq.spec.ts index 5a3219d08f..eea856f465 100644 --- a/packages/server/src/api/routes/tests/query.seq.spec.ts +++ b/packages/server/src/api/routes/tests/query.seq.spec.ts @@ -45,17 +45,6 @@ describe("/queries", () => { await setupTest() }) - async function createInvalidIntegration() { - const datasource = await config.createDatasource({ - datasource: { - ...basicDatasource().datasource, - source: "INVALID_INTEGRATION" as SourceName, - }, - }) - const query = await config.createQuery() - return { datasource, query } - } - const createQuery = async (query: Query) => { return request .post(`/api/queries`) @@ -278,14 +267,15 @@ describe("/queries", () => { }) it("should fail with invalid integration type", async () => { - let error - try { - await createInvalidIntegration() - } catch (err) { - error = err - } - expect(error).toBeDefined() - expect(error.message).toBe("No datasource implementation found.") + const response = await config.api.datasource.create( + { + ...basicDatasource().datasource, + source: "INVALID_INTEGRATION" as SourceName, + }, + { expectStatus: 500, rawResponse: true } + ) + + expect(response.body.message).toBe("No datasource implementation found.") }) }) diff --git a/packages/server/src/tests/utilities/api/datasource.ts b/packages/server/src/tests/utilities/api/datasource.ts index ee698334f2..7f4af6f3da 100644 --- a/packages/server/src/tests/utilities/api/datasource.ts +++ b/packages/server/src/tests/utilities/api/datasource.ts @@ -2,20 +2,23 @@ import { CreateDatasourceRequest, Datasource, VerifyDatasourceRequest, - VerifyDatasourceResponse, } from "@budibase/types" import TestConfiguration from "../TestConfiguration" import { TestAPI } from "./base" +import supertest from "supertest" export class DatasourceAPI extends TestAPI { constructor(config: TestConfiguration) { super(config) } - create = async ( + create = async ( config: Datasource, - { expectStatus } = { expectStatus: 200 } - ): Promise => { + { + expectStatus, + rawResponse, + }: { expectStatus?: number; rawResponse?: B } = {} + ): Promise => { const body: CreateDatasourceRequest = { datasource: config, tablesFilter: [], @@ -25,8 +28,11 @@ export class DatasourceAPI extends TestAPI { .send(body) .set(this.config.defaultHeaders()) .expect("Content-Type", /json/) - .expect(expectStatus) - return result.body.datasource as Datasource + .expect(expectStatus || 200) + if (rawResponse) { + return result as any + } + return result.body.datasource } update = async ( From 9da9935c95a94d570d0208b06463e6bec3790061 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Mon, 29 Jan 2024 23:25:12 +0100 Subject: [PATCH 17/25] Types --- .../server/src/tests/utilities/TestConfiguration.ts | 10 +++++++--- packages/server/src/tests/utilities/api/datasource.ts | 2 +- packages/types/src/shared/typeUtils.ts | 2 ++ 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/packages/server/src/tests/utilities/TestConfiguration.ts b/packages/server/src/tests/utilities/TestConfiguration.ts index 58e1ae9e0e..d96655af43 100644 --- a/packages/server/src/tests/utilities/TestConfiguration.ts +++ b/packages/server/src/tests/utilities/TestConfiguration.ts @@ -62,6 +62,7 @@ import { User, UserRoles, View, + WithRequired, } from "@budibase/types" import API from "./api" @@ -794,17 +795,19 @@ class TestConfiguration { async createDatasource(config?: { datasource: Datasource - }): Promise>> { + }): Promise> { config = config || basicDatasource() const response = await this.api.datasource.create(config.datasource) this.datasource = response return { ...this.datasource, _id: this.datasource!._id! } } - async updateDatasource(datasource: Datasource): Promise { + async updateDatasource( + datasource: Datasource + ): Promise> { const response = await this.api.datasource.update(datasource) this.datasource = response - return this.datasource! + return { ...this.datasource, _id: this.datasource!._id! } } async restDatasource(cfg?: any) { @@ -819,6 +822,7 @@ class TestConfiguration { async dynamicVariableDatasource() { let datasource = await this.restDatasource() + const basedOnQuery = await this.createQuery({ ...basicQuery(datasource._id!), fields: { diff --git a/packages/server/src/tests/utilities/api/datasource.ts b/packages/server/src/tests/utilities/api/datasource.ts index 7f4af6f3da..bcd7a71089 100644 --- a/packages/server/src/tests/utilities/api/datasource.ts +++ b/packages/server/src/tests/utilities/api/datasource.ts @@ -12,7 +12,7 @@ export class DatasourceAPI extends TestAPI { super(config) } - create = async ( + create = async ( config: Datasource, { expectStatus, diff --git a/packages/types/src/shared/typeUtils.ts b/packages/types/src/shared/typeUtils.ts index df0e049455..c7ecebed0a 100644 --- a/packages/types/src/shared/typeUtils.ts +++ b/packages/types/src/shared/typeUtils.ts @@ -7,3 +7,5 @@ export type ISO8601 = string export type RequiredKeys = { [K in keyof Required]: T[K] } + +export type WithRequired = T & Required> From 1024bd1dbf6132d18701a346fbd8775e5f1f2c34 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 30 Jan 2024 09:23:28 +0100 Subject: [PATCH 18/25] Lint --- packages/server/src/api/routes/tests/query.seq.spec.ts | 4 +++- packages/server/src/tests/utilities/structures.ts | 3 +-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/server/src/api/routes/tests/query.seq.spec.ts b/packages/server/src/api/routes/tests/query.seq.spec.ts index eea856f465..2790a9d8bf 100644 --- a/packages/server/src/api/routes/tests/query.seq.spec.ts +++ b/packages/server/src/api/routes/tests/query.seq.spec.ts @@ -1,5 +1,4 @@ import tk from "timekeeper" -tk.freeze(Date.now()) // Mock out postgres for this jest.mock("pg") @@ -20,10 +19,13 @@ jest.mock("@budibase/backend-core", () => { import * as setup from "./utilities" import { checkBuilderEndpoint } from "./utilities/TestFunctions" import { checkCacheForDynamicVariable } from "../../../threads/utils" + const { basicQuery, basicDatasource } = setup.structures import { events, db as dbCore } from "@budibase/backend-core" import { Datasource, Query, SourceName } from "@budibase/types" +tk.freeze(Date.now()) + const mockIsProdAppID = dbCore.isProdAppID as jest.MockedFunction< typeof dbCore.isProdAppID > diff --git a/packages/server/src/tests/utilities/structures.ts b/packages/server/src/tests/utilities/structures.ts index cd2389e2eb..b1c2c494a5 100644 --- a/packages/server/src/tests/utilities/structures.ts +++ b/packages/server/src/tests/utilities/structures.ts @@ -21,9 +21,8 @@ import { Table, INTERNAL_TABLE_SOURCE_ID, TableSourceType, - AutomationIOType, + Query, } from "@budibase/types" -import { Query } from "@budibase/types" const { BUILTIN_ROLE_IDS } = roles From 4ced91affc9872168e89e810620dafe1de369c38 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 30 Jan 2024 09:33:39 +0100 Subject: [PATCH 19/25] Update account-portal submodule --- packages/account-portal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/account-portal b/packages/account-portal index 64290ce895..485ec16a9e 160000 --- a/packages/account-portal +++ b/packages/account-portal @@ -1 +1 @@ -Subproject commit 64290ce8957d093bc997190402922df10d092953 +Subproject commit 485ec16a9eed48c548a5f1239772139f3319f028 From 669b0743ac5a6db0418f7d29333f0db27a558aaf Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Tue, 30 Jan 2024 10:00:44 +0000 Subject: [PATCH 20/25] Typing improvements around automation loop tests. --- packages/server/src/automations/automationUtils.ts | 5 +++-- packages/server/src/automations/tests/loop.spec.ts | 12 +++++++----- packages/server/src/definitions/automations.ts | 6 ++---- packages/server/src/tests/utilities/structures.ts | 8 ++++++-- packages/server/src/threads/automation.ts | 9 +++------ 5 files changed, 21 insertions(+), 19 deletions(-) diff --git a/packages/server/src/automations/automationUtils.ts b/packages/server/src/automations/automationUtils.ts index 3e25665a60..256f46f19f 100644 --- a/packages/server/src/automations/automationUtils.ts +++ b/packages/server/src/automations/automationUtils.ts @@ -5,7 +5,7 @@ import { } from "@budibase/string-templates" import sdk from "../sdk" import { Row } from "@budibase/types" -import { LoopStep, LoopStepType, LoopInput } from "../definitions/automations" +import { LoopStep, LoopStepType } from "../definitions/automations" /** * When values are input to the system generally they will be of type string as this is required for template strings. @@ -139,7 +139,8 @@ export function stringSplit(value: string | string[]) { return value } -export function typecastForLooping(loopStep: LoopStep, input: LoopInput) { +export function typecastForLooping(loopStep: LoopStep) { + const input = loopStep.inputs if (!input || !input.binding) { return null } diff --git a/packages/server/src/automations/tests/loop.spec.ts b/packages/server/src/automations/tests/loop.spec.ts index b64f7b16f8..70b771c445 100644 --- a/packages/server/src/automations/tests/loop.spec.ts +++ b/packages/server/src/automations/tests/loop.spec.ts @@ -3,11 +3,13 @@ import * as triggers from "../triggers" import { loopAutomation } from "../../tests/utilities/structures" import { context } from "@budibase/backend-core" import * as setup from "./utilities" +import { Row, Table } from "@budibase/types" +import { LoopInput, LoopStepType } from "../../definitions/automations" describe("Attempt to run a basic loop automation", () => { let config = setup.getConfig(), - table: any, - row: any + table: Table, + row: Row beforeEach(async () => { await automation.init() @@ -18,12 +20,12 @@ describe("Attempt to run a basic loop automation", () => { afterAll(setup.afterAll) - async function runLoop(loopOpts?: any) { + async function runLoop(loopOpts?: LoopInput) { const appId = config.getAppId() return await context.doInAppContext(appId, async () => { const params = { fields: { appId } } return await triggers.externalTrigger( - loopAutomation(table._id, loopOpts), + loopAutomation(table._id!, loopOpts), params, { getResponses: true } ) @@ -37,7 +39,7 @@ describe("Attempt to run a basic loop automation", () => { it("test a loop with a string", async () => { const resp = await runLoop({ - type: "String", + option: LoopStepType.STRING, binding: "a,b,c", }) expect(resp.steps[2].outputs.iterations).toBe(3) diff --git a/packages/server/src/definitions/automations.ts b/packages/server/src/definitions/automations.ts index 7e86608bf3..48a66408ca 100644 --- a/packages/server/src/definitions/automations.ts +++ b/packages/server/src/definitions/automations.ts @@ -6,13 +6,11 @@ export enum LoopStepType { } export interface LoopStep extends AutomationStep { - inputs: { - option: LoopStepType - [key: string]: any - } + inputs: LoopInput } export interface LoopInput { + option: LoopStepType binding: string[] | string } diff --git a/packages/server/src/tests/utilities/structures.ts b/packages/server/src/tests/utilities/structures.ts index b1c2c494a5..fe82311810 100644 --- a/packages/server/src/tests/utilities/structures.ts +++ b/packages/server/src/tests/utilities/structures.ts @@ -23,6 +23,7 @@ import { TableSourceType, Query, } from "@budibase/types" +import { LoopInput, LoopStepType } from "../../definitions/automations" const { BUILTIN_ROLE_IDS } = roles @@ -204,10 +205,13 @@ export function serverLogAutomation(appId?: string): Automation { } } -export function loopAutomation(tableId: string, loopOpts?: any): Automation { +export function loopAutomation( + tableId: string, + loopOpts?: LoopInput +): Automation { if (!loopOpts) { loopOpts = { - option: "Array", + option: LoopStepType.ARRAY, binding: "{{ steps.1.rows }}", } } diff --git a/packages/server/src/threads/automation.ts b/packages/server/src/threads/automation.ts index 4447899f96..1c3f921147 100644 --- a/packages/server/src/threads/automation.ts +++ b/packages/server/src/threads/automation.ts @@ -23,7 +23,6 @@ import { } from "@budibase/types" import { AutomationContext, - LoopInput, LoopStep, TriggerOutput, } from "../definitions/automations" @@ -47,9 +46,8 @@ function getLoopIterations(loopStep: LoopStep) { if (!binding) { return 0 } - const isString = typeof binding === "string" try { - if (isString) { + if (typeof binding === "string") { binding = JSON.parse(binding) } } catch (err) { @@ -58,7 +56,7 @@ function getLoopIterations(loopStep: LoopStep) { if (Array.isArray(binding)) { return binding.length } - if (isString) { + if (typeof binding === "string") { return automationUtils.stringSplit(binding).length } return 0 @@ -331,8 +329,7 @@ class Orchestrator { } try { loopStep.inputs.binding = automationUtils.typecastForLooping( - loopStep as LoopStep, - loopStep.inputs as LoopInput + loopStep as LoopStep ) } catch (err) { this.updateContextAndOutput( From 67a848bb868530ab2d00af4c248d53c0670fe481 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Tue, 30 Jan 2024 10:23:11 +0000 Subject: [PATCH 21/25] Fix tests. --- packages/server/scripts/test.sh | 8 +-- .../unitTests/automationUtils.spec.ts | 50 +++++++++---------- .../server/src/definitions/automations.ts | 2 +- 3 files changed, 30 insertions(+), 30 deletions(-) diff --git a/packages/server/scripts/test.sh b/packages/server/scripts/test.sh index eb1cf67b01..7f871ac337 100644 --- a/packages/server/scripts/test.sh +++ b/packages/server/scripts/test.sh @@ -5,10 +5,10 @@ if [[ -n $CI ]] then # Running in ci, where resources are limited export NODE_OPTIONS="--max-old-space-size=4096" - echo "jest --coverage --maxWorkers=2 --forceExit --workerIdleMemoryLimit=2000MB --bail" - jest --coverage --maxWorkers=2 --forceExit --workerIdleMemoryLimit=2000MB --bail + echo "jest --coverage --maxWorkers=2 --forceExit --workerIdleMemoryLimit=2000MB --bail $@" + jest --coverage --maxWorkers=2 --forceExit --workerIdleMemoryLimit=2000MB --bail $@ else # --maxWorkers performs better in development - echo "jest --coverage --maxWorkers=2 --forceExit" - jest --coverage --maxWorkers=2 --forceExit + echo "jest --coverage --maxWorkers=2 --forceExit $@" + jest --coverage --maxWorkers=2 --forceExit $@ fi \ No newline at end of file diff --git a/packages/server/src/automations/unitTests/automationUtils.spec.ts b/packages/server/src/automations/unitTests/automationUtils.spec.ts index 2291df9bc2..af1dea4070 100644 --- a/packages/server/src/automations/unitTests/automationUtils.spec.ts +++ b/packages/server/src/automations/unitTests/automationUtils.spec.ts @@ -1,10 +1,15 @@ -const automationUtils = require("../automationUtils") +import { LoopStep, LoopStepType } from "../../definitions/automations" +import { + typecastForLooping, + cleanInputValues, + substituteLoopStep, +} from "../automationUtils" describe("automationUtils", () => { describe("substituteLoopStep", () => { it("should allow multiple loop binding substitutes", () => { expect( - automationUtils.substituteLoopStep( + substituteLoopStep( `{{ loop.currentItem._id }} {{ loop.currentItem._id }} {{ loop.currentItem._id }}`, "step.2" ) @@ -15,7 +20,7 @@ describe("automationUtils", () => { it("should handle not subsituting outside of curly braces", () => { expect( - automationUtils.substituteLoopStep( + substituteLoopStep( `loop {{ loop.currentItem._id }}loop loop{{ loop.currentItem._id }}loop`, "step.2" ) @@ -28,37 +33,32 @@ describe("automationUtils", () => { describe("typeCastForLooping", () => { it("should parse to correct type", () => { expect( - automationUtils.typecastForLooping( - { inputs: { option: "Array" } }, - { binding: [1, 2, 3] } - ) + typecastForLooping({ + inputs: { option: LoopStepType.ARRAY, binding: [1, 2, 3] }, + } as LoopStep) ).toEqual([1, 2, 3]) expect( - automationUtils.typecastForLooping( - { inputs: { option: "Array" } }, - { binding: "[1, 2, 3]" } - ) + typecastForLooping({ + inputs: { option: LoopStepType.ARRAY, binding: "[1,2,3]" }, + } as LoopStep) ).toEqual([1, 2, 3]) expect( - automationUtils.typecastForLooping( - { inputs: { option: "String" } }, - { binding: [1, 2, 3] } - ) + typecastForLooping({ + inputs: { option: LoopStepType.STRING, binding: [1, 2, 3] }, + } as LoopStep) ).toEqual("1,2,3") }) it("should handle null values", () => { // expect it to handle where the binding is null expect( - automationUtils.typecastForLooping( - { inputs: { option: "Array" } }, - { binding: null } - ) + typecastForLooping({ + inputs: { option: LoopStepType.ARRAY }, + } as LoopStep) ).toEqual(null) expect(() => - automationUtils.typecastForLooping( - { inputs: { option: "Array" } }, - { binding: "test" } - ) + typecastForLooping({ + inputs: { option: LoopStepType.ARRAY, binding: "test" }, + } as LoopStep) ).toThrow() }) }) @@ -80,7 +80,7 @@ describe("automationUtils", () => { }, } expect( - automationUtils.cleanInputValues( + cleanInputValues( { row: { relationship: `[{"_id": "ro_ta_users_us_3"}]`, @@ -113,7 +113,7 @@ describe("automationUtils", () => { }, } expect( - automationUtils.cleanInputValues( + cleanInputValues( { row: { relationship: `ro_ta_users_us_3`, diff --git a/packages/server/src/definitions/automations.ts b/packages/server/src/definitions/automations.ts index 48a66408ca..b0b25e5cfa 100644 --- a/packages/server/src/definitions/automations.ts +++ b/packages/server/src/definitions/automations.ts @@ -11,7 +11,7 @@ export interface LoopStep extends AutomationStep { export interface LoopInput { option: LoopStepType - binding: string[] | string + binding?: string[] | string | number[] } export interface TriggerOutput { From 456817ee7b582ce399b9970ce054ccaf1f79f396 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Tue, 30 Jan 2024 10:37:23 +0000 Subject: [PATCH 22/25] More loop step typing improvements. --- .../server/src/automations/automationUtils.ts | 7 +++--- .../unitTests/automationUtils.spec.ts | 22 +++++-------------- .../server/src/definitions/automations.ts | 2 ++ packages/server/src/threads/automation.ts | 12 +++++----- 4 files changed, 17 insertions(+), 26 deletions(-) diff --git a/packages/server/src/automations/automationUtils.ts b/packages/server/src/automations/automationUtils.ts index 256f46f19f..227122bccc 100644 --- a/packages/server/src/automations/automationUtils.ts +++ b/packages/server/src/automations/automationUtils.ts @@ -5,7 +5,7 @@ import { } from "@budibase/string-templates" import sdk from "../sdk" import { Row } from "@budibase/types" -import { LoopStep, LoopStepType } from "../definitions/automations" +import { LoopInput, LoopStep, LoopStepType } from "../definitions/automations" /** * When values are input to the system generally they will be of type string as this is required for template strings. @@ -139,13 +139,12 @@ export function stringSplit(value: string | string[]) { return value } -export function typecastForLooping(loopStep: LoopStep) { - const input = loopStep.inputs +export function typecastForLooping(input: LoopInput) { if (!input || !input.binding) { return null } try { - switch (loopStep.inputs.option) { + switch (input.option) { case LoopStepType.ARRAY: if (typeof input.binding === "string") { return JSON.parse(input.binding) diff --git a/packages/server/src/automations/unitTests/automationUtils.spec.ts b/packages/server/src/automations/unitTests/automationUtils.spec.ts index af1dea4070..7de4a2e35b 100644 --- a/packages/server/src/automations/unitTests/automationUtils.spec.ts +++ b/packages/server/src/automations/unitTests/automationUtils.spec.ts @@ -33,32 +33,20 @@ describe("automationUtils", () => { describe("typeCastForLooping", () => { it("should parse to correct type", () => { expect( - typecastForLooping({ - inputs: { option: LoopStepType.ARRAY, binding: [1, 2, 3] }, - } as LoopStep) + typecastForLooping({ option: LoopStepType.ARRAY, binding: [1, 2, 3] }) ).toEqual([1, 2, 3]) expect( - typecastForLooping({ - inputs: { option: LoopStepType.ARRAY, binding: "[1,2,3]" }, - } as LoopStep) + typecastForLooping({ option: LoopStepType.ARRAY, binding: "[1,2,3]" }) ).toEqual([1, 2, 3]) expect( - typecastForLooping({ - inputs: { option: LoopStepType.STRING, binding: [1, 2, 3] }, - } as LoopStep) + typecastForLooping({ option: LoopStepType.STRING, binding: [1, 2, 3] }) ).toEqual("1,2,3") }) it("should handle null values", () => { // expect it to handle where the binding is null - expect( - typecastForLooping({ - inputs: { option: LoopStepType.ARRAY }, - } as LoopStep) - ).toEqual(null) + expect(typecastForLooping({ option: LoopStepType.ARRAY })).toEqual(null) expect(() => - typecastForLooping({ - inputs: { option: LoopStepType.ARRAY, binding: "test" }, - } as LoopStep) + typecastForLooping({ option: LoopStepType.ARRAY, binding: "test" }) ).toThrow() }) }) diff --git a/packages/server/src/definitions/automations.ts b/packages/server/src/definitions/automations.ts index b0b25e5cfa..c205149a5b 100644 --- a/packages/server/src/definitions/automations.ts +++ b/packages/server/src/definitions/automations.ts @@ -12,6 +12,8 @@ export interface LoopStep extends AutomationStep { export interface LoopInput { option: LoopStepType binding?: string[] | string | number[] + iterations?: string + failure?: any } export interface TriggerOutput { diff --git a/packages/server/src/threads/automation.ts b/packages/server/src/threads/automation.ts index 1c3f921147..56d9280f31 100644 --- a/packages/server/src/threads/automation.ts +++ b/packages/server/src/threads/automation.ts @@ -23,6 +23,7 @@ import { } from "@budibase/types" import { AutomationContext, + LoopInput, LoopStep, TriggerOutput, } from "../definitions/automations" @@ -254,7 +255,7 @@ class Orchestrator { this._context.env = await sdkUtils.getEnvironmentVariables() let automation = this._automation let stopped = false - let loopStep: AutomationStep | undefined = undefined + let loopStep: LoopStep | undefined = undefined let stepCount = 0 let loopStepNumber: any = undefined @@ -309,7 +310,7 @@ class Orchestrator { stepCount++ if (step.stepId === LOOP_STEP_ID) { - loopStep = step + loopStep = step as LoopStep loopStepNumber = stepCount continue } @@ -329,7 +330,7 @@ class Orchestrator { } try { loopStep.inputs.binding = automationUtils.typecastForLooping( - loopStep as LoopStep + loopStep.inputs as LoopInput ) } catch (err) { this.updateContextAndOutput( @@ -345,7 +346,7 @@ class Orchestrator { loopStep = undefined break } - let item = [] + let item: any[] = [] if ( typeof loopStep.inputs.binding === "string" && loopStep.inputs.option === "String" @@ -396,7 +397,8 @@ class Orchestrator { if ( index === env.AUTOMATION_MAX_ITERATIONS || - index === parseInt(loopStep.inputs.iterations) + (loopStep.inputs.iterations && + index === parseInt(loopStep.inputs.iterations)) ) { this.updateContextAndOutput( loopStepNumber, From 9a9ac12ee5beac15939359e24a310d2d1770a49b Mon Sep 17 00:00:00 2001 From: Budibase Staging Release Bot <> Date: Tue, 30 Jan 2024 10:48:36 +0000 Subject: [PATCH 23/25] Bump version to 2.16.0 --- lerna.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lerna.json b/lerna.json index 65f04ecf2c..f91c51d4bb 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "2.15.7", + "version": "2.16.0", "npmClient": "yarn", "packages": [ "packages/*", From 72d63d0c006379096fcaa6a78992715d01978333 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Tue, 30 Jan 2024 10:13:45 +0000 Subject: [PATCH 24/25] Rename executeSynchronously to be a bit less confusing, as it does not execute synchronously. --- packages/server/src/automations/triggers.ts | 5 ++--- packages/server/src/threads/automation.ts | 6 +++--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/packages/server/src/automations/triggers.ts b/packages/server/src/automations/triggers.ts index 17a33e4394..08e3199a11 100644 --- a/packages/server/src/automations/triggers.ts +++ b/packages/server/src/automations/triggers.ts @@ -9,7 +9,7 @@ import * as utils from "./utils" import env from "../environment" import { context, db as dbCore } from "@budibase/backend-core" import { Automation, Row, AutomationData, AutomationJob } from "@budibase/types" -import { executeSynchronously } from "../threads/automation" +import { executeInThread } from "../threads/automation" export const TRIGGER_DEFINITIONS = definitions const JOB_OPTS = { @@ -117,8 +117,7 @@ export async function externalTrigger( appId: context.getAppId(), automation, } - const job = { data } as AutomationJob - return executeSynchronously(job) + return executeInThread({ data } as AutomationJob) } else { return automationQueue.add(data, JOB_OPTS) } diff --git a/packages/server/src/threads/automation.ts b/packages/server/src/threads/automation.ts index 56d9280f31..ed0203797d 100644 --- a/packages/server/src/threads/automation.ts +++ b/packages/server/src/threads/automation.ts @@ -614,7 +614,7 @@ export function execute(job: Job, callback: WorkerCallback) { }) } -export function executeSynchronously(job: Job) { +export async function executeInThread(job: Job) { const appId = job.data.event.appId if (!appId) { throw new Error("Unable to execute, event doesn't contain app ID.") @@ -626,10 +626,10 @@ export function executeSynchronously(job: Job) { }, job.data.event.timeout || 12000) }) - return context.doInAppContext(appId, async () => { + return await context.doInAppContext(appId, async () => { const envVars = await sdkUtils.getEnvironmentVariables() // put into automation thread for whole context - return context.doInEnvironmentContext(envVars, async () => { + return await context.doInEnvironmentContext(envVars, async () => { const automationOrchestrator = new Orchestrator(job) return await Promise.race([ automationOrchestrator.execute(), From b3c949b091fbd7a162e08ea75cebdc9ae9f431e8 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Tue, 30 Jan 2024 11:06:09 +0000 Subject: [PATCH 25/25] Fix case where if a binding returned an int it would throw an error. --- packages/server/src/automations/tests/loop.spec.ts | 8 ++++++++ packages/server/src/threads/automation.ts | 10 ++++------ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/packages/server/src/automations/tests/loop.spec.ts b/packages/server/src/automations/tests/loop.spec.ts index 70b771c445..68ab694c5d 100644 --- a/packages/server/src/automations/tests/loop.spec.ts +++ b/packages/server/src/automations/tests/loop.spec.ts @@ -44,4 +44,12 @@ describe("Attempt to run a basic loop automation", () => { }) expect(resp.steps[2].outputs.iterations).toBe(3) }) + + it("test a loop with a binding that returns an integer", async () => { + const resp = await runLoop({ + option: LoopStepType.ARRAY, + binding: "{{ 1 }}", + }) + expect(resp.steps[2].outputs.iterations).toBe(1) + }) }) diff --git a/packages/server/src/threads/automation.ts b/packages/server/src/threads/automation.ts index ed0203797d..a828af5d19 100644 --- a/packages/server/src/threads/automation.ts +++ b/packages/server/src/threads/automation.ts @@ -43,20 +43,18 @@ const CRON_STEP_ID = triggerDefs.CRON.stepId const STOPPED_STATUS = { success: true, status: AutomationStatus.STOPPED } function getLoopIterations(loopStep: LoopStep) { - let binding = loopStep.inputs.binding + const binding = loopStep.inputs.binding if (!binding) { return 0 } try { - if (typeof binding === "string") { - binding = JSON.parse(binding) + const json = typeof binding === "string" ? JSON.parse(binding) : binding + if (Array.isArray(json)) { + return json.length } } catch (err) { // ignore error - wasn't able to parse } - if (Array.isArray(binding)) { - return binding.length - } if (typeof binding === "string") { return automationUtils.stringSplit(binding).length }