From 9c0d7c027884630ec7d372f56a6f47cae5eb15ee Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Mon, 24 Feb 2025 14:47:53 +0000 Subject: [PATCH 1/7] Fix bug with branches when no conditions are set. --- .../src/automations/tests/branching.spec.ts | 26 ++++++++++++++++++- packages/server/src/threads/automation.ts | 2 ++ packages/types/src/sdk/search.ts | 3 ++- 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/packages/server/src/automations/tests/branching.spec.ts b/packages/server/src/automations/tests/branching.spec.ts index bf9b9ce3f8..70aa4cbaa7 100644 --- a/packages/server/src/automations/tests/branching.spec.ts +++ b/packages/server/src/automations/tests/branching.spec.ts @@ -1,5 +1,10 @@ import * as automation from "../index" -import { Table, AutomationStatus } from "@budibase/types" +import { + Table, + AutomationStatus, + AutomationStepStatus, + EmptyFilterOption, +} from "@budibase/types" import { createAutomationBuilder } from "./utilities/AutomationTestBuilder" import TestConfiguration from "../../tests/utilities/TestConfiguration" @@ -280,4 +285,23 @@ describe("Branching automations", () => { expect(results.steps[2].outputs.message).toContain("Special user") }) + + it("should not fail with empty conditions", async () => { + const results = await createAutomationBuilder(config) + .onAppAction() + .branch({ + specialBranch: { + steps: stepBuilder => stepBuilder.serverLog({ text: "Hello!" }), + condition: { + onEmptyFilter: EmptyFilterOption.RETURN_NONE, + }, + }, + }) + .test({ fields: { test_trigger: true } }) + + expect(results.steps[0].outputs.success).toEqual(false) + expect(results.steps[0].outputs.status).toEqual( + AutomationStatus.NO_CONDITION_MET + ) + }) }) diff --git a/packages/server/src/threads/automation.ts b/packages/server/src/threads/automation.ts index 762da1cbc1..def2ab4201 100644 --- a/packages/server/src/threads/automation.ts +++ b/packages/server/src/threads/automation.ts @@ -367,6 +367,8 @@ class Orchestrator { if (e.errno === "ETIME") { span?.addTags({ timedOut: true }) console.warn(`Automation execution timed out after ${timeout}ms`) + } else { + throw e } } diff --git a/packages/types/src/sdk/search.ts b/packages/types/src/sdk/search.ts index 992e9961d4..99c3658fa8 100644 --- a/packages/types/src/sdk/search.ts +++ b/packages/types/src/sdk/search.ts @@ -3,6 +3,7 @@ import { Row, DocumentType, Table, Datasource } from "../documents" import { SortOrder, SortType } from "../api" import { Knex } from "knex" import { Aggregation } from "./row" +import _ from "lodash" export enum BasicOperator { EQUAL = "equal", @@ -83,7 +84,7 @@ type RangeFilter = Record< type LogicalFilter = { conditions: SearchFilters[] } export function isLogicalFilter(filter: any): filter is LogicalFilter { - return "conditions" in filter + return _.isPlainObject(filter) && "conditions" in filter } export type AnySearchFilter = BasicFilter | ArrayFilter | RangeFilter From 68b72acb717249059bad54360794a521c8519e8d Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Mon, 24 Feb 2025 15:06:12 +0000 Subject: [PATCH 2/7] Fix lint. --- packages/server/src/automations/tests/branching.spec.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/packages/server/src/automations/tests/branching.spec.ts b/packages/server/src/automations/tests/branching.spec.ts index 70aa4cbaa7..4572871c44 100644 --- a/packages/server/src/automations/tests/branching.spec.ts +++ b/packages/server/src/automations/tests/branching.spec.ts @@ -1,10 +1,5 @@ import * as automation from "../index" -import { - Table, - AutomationStatus, - AutomationStepStatus, - EmptyFilterOption, -} from "@budibase/types" +import { Table, AutomationStatus, EmptyFilterOption } from "@budibase/types" import { createAutomationBuilder } from "./utilities/AutomationTestBuilder" import TestConfiguration from "../../tests/utilities/TestConfiguration" From a959a26d60a4fae91e939382ed447b991577c49d Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Mon, 24 Feb 2025 16:15:59 +0000 Subject: [PATCH 3/7] Support filtering relationships by _id --- .../src/api/routes/tests/search.spec.ts | 22 +++++++++++++++++++ .../server/src/sdk/app/rows/queryUtils.ts | 7 +++--- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/packages/server/src/api/routes/tests/search.spec.ts b/packages/server/src/api/routes/tests/search.spec.ts index ee372914d7..ba2b3f0acf 100644 --- a/packages/server/src/api/routes/tests/search.spec.ts +++ b/packages/server/src/api/routes/tests/search.spec.ts @@ -3553,6 +3553,28 @@ if (descriptions.length) { limit: 1, }).toContainExactly([row]) }) + + it("can filter by the related _id", async () => { + await expectSearch({ + query: { + equal: { "rel._id": row.rel[0]._id }, + }, + }).toContainExactly([row]) + + await expectSearch({ + query: { + equal: { "rel._id": row.rel[1]._id }, + }, + }).toContainExactly([row]) + }) + + it("can filter by the related _id and find nothing", async () => { + await expectSearch({ + query: { + equal: { "rel._id": "rel_none" }, + }, + }).toFindNothing() + }) }) !isInternal && diff --git a/packages/server/src/sdk/app/rows/queryUtils.ts b/packages/server/src/sdk/app/rows/queryUtils.ts index ddd32870be..12e724b5d8 100644 --- a/packages/server/src/sdk/app/rows/queryUtils.ts +++ b/packages/server/src/sdk/app/rows/queryUtils.ts @@ -69,7 +69,8 @@ export const getQueryableFields = async ( fromTables: string[], opts?: { noRelationships?: boolean } ): Promise => { - const result = [] + // Querying by _id is always allowed, even if it's never part of the schema + const result = ["_id"] for (const field of Object.keys(table.schema).filter( f => allowedFields.includes(f) && table.schema[f].visible !== false )) { @@ -113,9 +114,7 @@ export const getQueryableFields = async ( return result } - const result = [ - "_id", // Querying by _id is always allowed, even if it's never part of the schema - ] + const result = [] if (fields == null) { fields = Object.keys(table.schema) From a88530438c8be7e5ebed22e09c748ed315c12c55 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Mon, 24 Feb 2025 16:36:08 +0000 Subject: [PATCH 4/7] Remove view enrichment unit test, covered by integration tests. --- .../src/sdk/app/tables/tests/tables.spec.ts | 108 ------------------ 1 file changed, 108 deletions(-) delete mode 100644 packages/server/src/sdk/app/tables/tests/tables.spec.ts diff --git a/packages/server/src/sdk/app/tables/tests/tables.spec.ts b/packages/server/src/sdk/app/tables/tests/tables.spec.ts deleted file mode 100644 index 41ac808f5c..0000000000 --- a/packages/server/src/sdk/app/tables/tests/tables.spec.ts +++ /dev/null @@ -1,108 +0,0 @@ -import { - FieldType, - INTERNAL_TABLE_SOURCE_ID, - Table, - TableSourceType, - ViewV2, -} from "@budibase/types" -import { generator } from "@budibase/backend-core/tests" -import sdk from "../../.." - -jest.mock("../../views", () => ({ - ...jest.requireActual("../../views"), - enrichSchema: jest.fn().mockImplementation(v => ({ ...v, mocked: true })), -})) - -describe("table sdk", () => { - describe("enrichViewSchemas", () => { - const basicTable: Table = { - _id: generator.guid(), - name: "TestTable", - type: "table", - sourceId: INTERNAL_TABLE_SOURCE_ID, - sourceType: TableSourceType.INTERNAL, - schema: { - name: { - type: FieldType.STRING, - name: "name", - visible: true, - width: 80, - order: 2, - constraints: { - type: "string", - }, - }, - description: { - type: FieldType.STRING, - name: "description", - visible: true, - width: 200, - constraints: { - type: "string", - }, - }, - id: { - type: FieldType.NUMBER, - name: "id", - visible: true, - order: 1, - constraints: { - type: "number", - }, - }, - hiddenField: { - type: FieldType.STRING, - name: "hiddenField", - visible: false, - constraints: { - type: "string", - }, - }, - }, - } - - it("should fetch the default schema if not overriden", async () => { - const tableId = basicTable._id! - function getTable() { - const view: ViewV2 = { - version: 2, - id: generator.guid(), - name: generator.guid(), - tableId, - } - return view - } - const view1 = getTable() - const view2 = getTable() - const view3 = getTable() - const res = await sdk.tables.enrichViewSchemas({ - ...basicTable, - views: { - [view1.name]: view1, - [view2.name]: view2, - [view3.name]: view3, - }, - }) - - expect(sdk.views.enrichSchema).toHaveBeenCalledTimes(3) - - expect(res).toEqual({ - ...basicTable, - views: { - [view1.name]: { - ...view1, - mocked: true, - }, - [view2.name]: { - ...view2, - mocked: true, - }, - [view3.name]: { - ...view3, - mocked: true, - }, - }, - }) - }) - }) -}) From e6fc3dbff42922285b197f271845528f73684225 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Tue, 25 Feb 2025 09:19:25 +0000 Subject: [PATCH 5/7] Fix queryUtils tests. --- .../src/sdk/app/rows/tests/queryUtils.spec.ts | 31 ++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/packages/server/src/sdk/app/rows/tests/queryUtils.spec.ts b/packages/server/src/sdk/app/rows/tests/queryUtils.spec.ts index f399801f1e..100581cb54 100644 --- a/packages/server/src/sdk/app/rows/tests/queryUtils.spec.ts +++ b/packages/server/src/sdk/app/rows/tests/queryUtils.spec.ts @@ -250,6 +250,8 @@ describe("query utils", () => { expect(result).toEqual([ "_id", "name", + "aux._id", + "auxTable._id", "aux.title", "auxTable.title", "aux.name", @@ -284,7 +286,14 @@ describe("query utils", () => { const result = await config.doInContext(config.appId, () => { return getQueryableFields(table) }) - expect(result).toEqual(["_id", "name", "aux.name", "auxTable.name"]) + expect(result).toEqual([ + "_id", + "name", + "aux._id", + "auxTable._id", + "aux.name", + "auxTable.name", + ]) }) it("excludes all relationship fields if hidden", async () => { @@ -387,10 +396,14 @@ describe("query utils", () => { "_id", "name", // aux1 primitive props + "aux1._id", + "aux1Table._id", "aux1.name", "aux1Table.name", // aux2 primitive props + "aux2._id", + "aux2Table._id", "aux2.title", "aux2Table.title", ]) @@ -405,14 +418,20 @@ describe("query utils", () => { "name", // aux2_1 primitive props + "aux2_1._id", + "aux2Table._id", "aux2_1.title", "aux2Table.title", // aux2_2 primitive props + "aux2_2._id", + "aux2Table._id", "aux2_2.title", "aux2Table.title", // table primitive props + "table._id", + "TestTable._id", "table.name", "TestTable.name", ]) @@ -427,14 +446,20 @@ describe("query utils", () => { "title", // aux1_1 primitive props + "aux1_1._id", + "aux1Table._id", "aux1_1.name", "aux1Table.name", // aux1_2 primitive props + "aux1_2._id", + "aux1Table._id", "aux1_2.name", "aux1Table.name", // table primitive props + "table._id", + "TestTable._id", "table.name", "TestTable.name", ]) @@ -481,6 +506,8 @@ describe("query utils", () => { "name", // deep 1 aux primitive props + "aux._id", + "auxTable._id", "aux.title", "auxTable.title", ]) @@ -495,6 +522,8 @@ describe("query utils", () => { "title", // deep 1 dependency primitive props + "table._id", + "TestTable._id", "table.name", "TestTable.name", ]) From 3c6e2ff2d784cd79fc42df2c9955ebdba1897e2c Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Tue, 25 Feb 2025 09:54:10 +0000 Subject: [PATCH 6/7] Fix search tests. --- .../src/api/routes/tests/search.spec.ts | 41 ++++++++++--------- .../server/src/sdk/app/rows/queryUtils.ts | 11 +++-- 2 files changed, 30 insertions(+), 22 deletions(-) diff --git a/packages/server/src/api/routes/tests/search.spec.ts b/packages/server/src/api/routes/tests/search.spec.ts index ba2b3f0acf..76ce4a0243 100644 --- a/packages/server/src/api/routes/tests/search.spec.ts +++ b/packages/server/src/api/routes/tests/search.spec.ts @@ -3554,27 +3554,30 @@ if (descriptions.length) { }).toContainExactly([row]) }) - it("can filter by the related _id", async () => { - await expectSearch({ - query: { - equal: { "rel._id": row.rel[0]._id }, - }, - }).toContainExactly([row]) + isInternal && + describe("search by _id for relations", () => { + it("can filter by the related _id", async () => { + await expectSearch({ + query: { + equal: { "rel._id": row.rel[0]._id }, + }, + }).toContainExactly([row]) - await expectSearch({ - query: { - equal: { "rel._id": row.rel[1]._id }, - }, - }).toContainExactly([row]) - }) + await expectSearch({ + query: { + equal: { "rel._id": row.rel[1]._id }, + }, + }).toContainExactly([row]) + }) - it("can filter by the related _id and find nothing", async () => { - await expectSearch({ - query: { - equal: { "rel._id": "rel_none" }, - }, - }).toFindNothing() - }) + it("can filter by the related _id and find nothing", async () => { + await expectSearch({ + query: { + equal: { "rel._id": "rel_none" }, + }, + }).toFindNothing() + }) + }) }) !isInternal && diff --git a/packages/server/src/sdk/app/rows/queryUtils.ts b/packages/server/src/sdk/app/rows/queryUtils.ts index 12e724b5d8..629fb50545 100644 --- a/packages/server/src/sdk/app/rows/queryUtils.ts +++ b/packages/server/src/sdk/app/rows/queryUtils.ts @@ -7,6 +7,7 @@ import { } from "@budibase/types" import { cloneDeep } from "lodash/fp" import sdk from "../../../sdk" +import { isInternal } from "../tables/utils" export const removeInvalidFilters = ( filters: SearchFilters, @@ -69,8 +70,11 @@ export const getQueryableFields = async ( fromTables: string[], opts?: { noRelationships?: boolean } ): Promise => { - // Querying by _id is always allowed, even if it's never part of the schema - const result = ["_id"] + const result = [] + if (isInternal({ table })) { + result.push("_id") + } + for (const field of Object.keys(table.schema).filter( f => allowedFields.includes(f) && table.schema[f].visible !== false )) { @@ -114,7 +118,8 @@ export const getQueryableFields = async ( return result } - const result = [] + // Querying by _id is always allowed, even if it's never part of the schema + const result = ["_id"] if (fields == null) { fields = Object.keys(table.schema) From da8c5be6857c0190f3ec3c3cd99c51a994425ef0 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Tue, 25 Feb 2025 11:08:18 +0000 Subject: [PATCH 7/7] Fix queryUtils tests. --- packages/server/src/sdk/app/rows/queryUtils.ts | 2 +- packages/server/src/sdk/app/rows/tests/queryUtils.spec.ts | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/server/src/sdk/app/rows/queryUtils.ts b/packages/server/src/sdk/app/rows/queryUtils.ts index 629fb50545..8ab50ea1b7 100644 --- a/packages/server/src/sdk/app/rows/queryUtils.ts +++ b/packages/server/src/sdk/app/rows/queryUtils.ts @@ -126,5 +126,5 @@ export const getQueryableFields = async ( } result.push(...(await extractTableFields(table, fields, [table._id!]))) - return result + return Array.from(new Set(result)) } diff --git a/packages/server/src/sdk/app/rows/tests/queryUtils.spec.ts b/packages/server/src/sdk/app/rows/tests/queryUtils.spec.ts index 100581cb54..26a8431446 100644 --- a/packages/server/src/sdk/app/rows/tests/queryUtils.spec.ts +++ b/packages/server/src/sdk/app/rows/tests/queryUtils.spec.ts @@ -425,9 +425,7 @@ describe("query utils", () => { // aux2_2 primitive props "aux2_2._id", - "aux2Table._id", "aux2_2.title", - "aux2Table.title", // table primitive props "table._id", @@ -453,9 +451,7 @@ describe("query utils", () => { // aux1_2 primitive props "aux1_2._id", - "aux1Table._id", "aux1_2.name", - "aux1Table.name", // table primitive props "table._id",