From 10a669e1d7a68093fa36f603d951ba653246ab73 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 22 Jan 2025 17:16:11 +0100 Subject: [PATCH 01/11] Support searching for multiple types --- packages/builder/src/helpers/screen.ts | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/packages/builder/src/helpers/screen.ts b/packages/builder/src/helpers/screen.ts index 71623844de..296a597adb 100644 --- a/packages/builder/src/helpers/screen.ts +++ b/packages/builder/src/helpers/screen.ts @@ -1,7 +1,12 @@ import { Component, Screen, ScreenProps } from "@budibase/types" import clientManifest from "@budibase/client/manifest.json" -export function findComponentsBySettingsType(screen: Screen, type: string) { +export function findComponentsBySettingsType( + screen: Screen, + type: string | string[] +) { + const typesArray = Array.isArray(type) ? type : [type] + const result: { component: Component setting: { @@ -9,10 +14,7 @@ export function findComponentsBySettingsType(screen: Screen, type: string) { key: string } }[] = [] - function recurseFieldComponentsInChildren( - component: ScreenProps, - type: string - ) { + function recurseFieldComponentsInChildren(component: ScreenProps) { if (!component) { return } @@ -20,7 +22,7 @@ export function findComponentsBySettingsType(screen: Screen, type: string) { const definition = getManifestDefinition(component) const setting = "settings" in definition && - definition.settings.find((s: any) => s.type === type) + definition.settings.find((s: any) => typesArray.includes(s.type)) if (setting && "type" in setting) { result.push({ component, @@ -28,11 +30,11 @@ export function findComponentsBySettingsType(screen: Screen, type: string) { }) } component._children?.forEach(child => { - recurseFieldComponentsInChildren(child, type) + recurseFieldComponentsInChildren(child) }) } - recurseFieldComponentsInChildren(screen?.props, type) + recurseFieldComponentsInChildren(screen?.props) return result } From 1c23763813e49d9b320a12faa821f09d0f815c45 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 22 Jan 2025 17:38:08 +0100 Subject: [PATCH 02/11] Handle dataSources as well --- packages/builder/src/stores/builder/screenComponent.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/builder/src/stores/builder/screenComponent.ts b/packages/builder/src/stores/builder/screenComponent.ts index a061158e6a..426fcbc58a 100644 --- a/packages/builder/src/stores/builder/screenComponent.ts +++ b/packages/builder/src/stores/builder/screenComponent.ts @@ -40,7 +40,7 @@ export const screenComponentErrors = derived( const result: Record = {} for (const { component, setting } of findComponentsBySettingsType( screen, - "table" + ["table", "dataSource"] )) { const { resourceId, type, label } = component[setting.key] if (!datasources[resourceId]) { From 1f3c466028ca1166ea94d4714e2e27e80803d4cf Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 22 Jan 2025 18:00:03 +0100 Subject: [PATCH 03/11] DRY --- .../src/stores/builder/screenComponent.ts | 44 +++++++++---------- 1 file changed, 20 insertions(+), 24 deletions(-) diff --git a/packages/builder/src/stores/builder/screenComponent.ts b/packages/builder/src/stores/builder/screenComponent.ts index 426fcbc58a..6db9f43241 100644 --- a/packages/builder/src/stores/builder/screenComponent.ts +++ b/packages/builder/src/stores/builder/screenComponent.ts @@ -3,37 +3,29 @@ import { tables } from "./tables" import { selectedScreen } from "./screens" import { viewsV2 } from "./viewsV2" import { findComponentsBySettingsType } from "@/helpers/screen" -import { Screen, Table, ViewV2 } from "@budibase/types" +import { Screen } from "@budibase/types" + +function reduceBy( + key: TKey, + list: TItem[] +) { + return list.reduce( + (result, item) => ({ + ...result, + [item[key] as string]: item, + }), + {} + ) +} export const screenComponentErrors = derived( [selectedScreen, tables, viewsV2], ([$selectedScreen, $tables, $viewsV2]): Record => { - function flattenTablesAndViews(tables: Table[], views: ViewV2[]) { - return { - ...tables.reduce( - (list, table) => ({ - ...list, - [table._id!]: table, - }), - {} - ), - ...views.reduce( - (list, view) => ({ - ...list, - [view.id]: view, - }), - {} - ), - } - } - function getInvalidDatasources( screen: Screen, datasources: Record ) { const friendlyNameByType = { - table: "table", - view: "view", viewV2: "view", } @@ -45,7 +37,7 @@ export const screenComponentErrors = derived( const { resourceId, type, label } = component[setting.key] if (!datasources[resourceId]) { const friendlyTypeName = - friendlyNameByType[type as keyof typeof friendlyNameByType] + friendlyNameByType[type as keyof typeof friendlyNameByType] ?? type result[component._id!] = [ `The ${friendlyTypeName} named "${label}" does not exist`, ] @@ -55,7 +47,11 @@ export const screenComponentErrors = derived( return result } - const datasources = flattenTablesAndViews($tables.list, $viewsV2.list) + const datasources = { + ...reduceBy("_id", $tables.list), + ...reduceBy("id", $viewsV2.list), + } + return getInvalidDatasources($selectedScreen, datasources) } ) From 41b65a6b1df471d37fd04e9146f2ea7e9057f7dc Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Thu, 23 Jan 2025 11:03:52 +0100 Subject: [PATCH 04/11] Validate queries --- .../src/stores/builder/screenComponent.ts | 29 +++++++++++++++---- .../types/src/documents/app/datasource.ts | 2 ++ 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/packages/builder/src/stores/builder/screenComponent.ts b/packages/builder/src/stores/builder/screenComponent.ts index 6db9f43241..a9444aee8c 100644 --- a/packages/builder/src/stores/builder/screenComponent.ts +++ b/packages/builder/src/stores/builder/screenComponent.ts @@ -3,7 +3,8 @@ import { tables } from "./tables" import { selectedScreen } from "./screens" import { viewsV2 } from "./viewsV2" import { findComponentsBySettingsType } from "@/helpers/screen" -import { Screen } from "@budibase/types" +import { DatasourceType, Screen } from "@budibase/types" +import { queries } from "./queries" function reduceBy( key: TKey, @@ -19,22 +20,39 @@ function reduceBy( } export const screenComponentErrors = derived( - [selectedScreen, tables, viewsV2], - ([$selectedScreen, $tables, $viewsV2]): Record => { + [selectedScreen, tables, viewsV2, queries], + ([$selectedScreen, $tables, $viewsV2, $queries]): Record< + string, + string[] + > => { function getInvalidDatasources( screen: Screen, datasources: Record ) { - const friendlyNameByType = { + const friendlyNameByType: Partial> = { viewV2: "view", } + const primaryKeyByType: Record = { + table: "resourceId", + view: "TODO", + viewV2: "resourceId", + query: "_id", + custom: "" as never, + } + const result: Record = {} for (const { component, setting } of findComponentsBySettingsType( screen, ["table", "dataSource"] )) { - const { resourceId, type, label } = component[setting.key] + const componentSettings = component[setting.key] + const { type, label } = componentSettings + if (type === "custom") { + continue + } + const resourceId = + componentSettings[primaryKeyByType[type as DatasourceType]] if (!datasources[resourceId]) { const friendlyTypeName = friendlyNameByType[type as keyof typeof friendlyNameByType] ?? type @@ -50,6 +68,7 @@ export const screenComponentErrors = derived( const datasources = { ...reduceBy("_id", $tables.list), ...reduceBy("id", $viewsV2.list), + ...reduceBy("_id", $queries.list), } return getInvalidDatasources($selectedScreen, datasources) diff --git a/packages/types/src/documents/app/datasource.ts b/packages/types/src/documents/app/datasource.ts index a0be7bd80d..27828c5455 100644 --- a/packages/types/src/documents/app/datasource.ts +++ b/packages/types/src/documents/app/datasource.ts @@ -57,3 +57,5 @@ export interface RestConfig { } dynamicVariables?: DynamicVariable[] } + +export type DatasourceType = "table" | "view" | "viewV2" | "query" | "custom" From 6e615a9907b0dc4508ccb921e5113bf3fa93779e Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Thu, 23 Jan 2025 11:19:49 +0100 Subject: [PATCH 05/11] Validate views v1 --- .../builder/src/stores/builder/screenComponent.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/packages/builder/src/stores/builder/screenComponent.ts b/packages/builder/src/stores/builder/screenComponent.ts index a9444aee8c..2803acc953 100644 --- a/packages/builder/src/stores/builder/screenComponent.ts +++ b/packages/builder/src/stores/builder/screenComponent.ts @@ -5,6 +5,7 @@ import { viewsV2 } from "./viewsV2" import { findComponentsBySettingsType } from "@/helpers/screen" import { DatasourceType, Screen } from "@budibase/types" import { queries } from "./queries" +import { views } from "./views" function reduceBy( key: TKey, @@ -20,8 +21,8 @@ function reduceBy( } export const screenComponentErrors = derived( - [selectedScreen, tables, viewsV2, queries], - ([$selectedScreen, $tables, $viewsV2, $queries]): Record< + [selectedScreen, tables, views, viewsV2, queries], + ([$selectedScreen, $tables, $views, $viewsV2, $queries]): Record< string, string[] > => { @@ -34,9 +35,9 @@ export const screenComponentErrors = derived( } const primaryKeyByType: Record = { - table: "resourceId", - view: "TODO", - viewV2: "resourceId", + table: "tableId", + view: "name", + viewV2: "id", query: "_id", custom: "" as never, } @@ -67,6 +68,7 @@ export const screenComponentErrors = derived( const datasources = { ...reduceBy("_id", $tables.list), + ...reduceBy("name", $views.list), ...reduceBy("id", $viewsV2.list), ...reduceBy("_id", $queries.list), } From 92e2ae46f54f7e2f88baec2a72a08613106a4c76 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Thu, 23 Jan 2025 16:17:08 +0100 Subject: [PATCH 06/11] Center error message --- .../src/components/error-states/ComponentErrorState.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/src/components/error-states/ComponentErrorState.svelte b/packages/client/src/components/error-states/ComponentErrorState.svelte index 9eace07018..7069b7a431 100644 --- a/packages/client/src/components/error-states/ComponentErrorState.svelte +++ b/packages/client/src/components/error-states/ComponentErrorState.svelte @@ -38,7 +38,7 @@ .component-placeholder { display: flex; flex-direction: row; - justify-content: flex-start; + justify-content: center; align-items: center; color: var(--spectrum-global-color-gray-600); font-size: var(--font-size-s); From cf9b61c8c80a889c41a3cc017e431239aa70d3fc Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Thu, 23 Jan 2025 17:35:50 +0000 Subject: [PATCH 07/11] Fix selecting 'old row' in row updated trigger. --- .../automation/SetupPanel/AutomationBlockSetup.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/builder/src/components/automation/SetupPanel/AutomationBlockSetup.svelte b/packages/builder/src/components/automation/SetupPanel/AutomationBlockSetup.svelte index 16183ea59a..e713d9bc85 100644 --- a/packages/builder/src/components/automation/SetupPanel/AutomationBlockSetup.svelte +++ b/packages/builder/src/components/automation/SetupPanel/AutomationBlockSetup.svelte @@ -293,7 +293,7 @@ type: RowSelector, props: { row: inputData["oldRow"] || { - tableId: inputData["row"].tableId, + tableId: inputData["row"]?.tableId, }, meta: { fields: inputData["meta"]?.oldFields || {}, From 388a94aee1f7b13e2baf3527a89103efff649499 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Thu, 23 Jan 2025 19:34:03 +0100 Subject: [PATCH 08/11] Add feature flag for datasource setting checks --- packages/builder/src/helpers/index.ts | 1 + packages/builder/src/stores/builder/screenComponent.ts | 6 ++++++ packages/types/src/sdk/featureFlag.ts | 2 ++ 3 files changed, 9 insertions(+) diff --git a/packages/builder/src/helpers/index.ts b/packages/builder/src/helpers/index.ts index 687723e361..81afc696a3 100644 --- a/packages/builder/src/helpers/index.ts +++ b/packages/builder/src/helpers/index.ts @@ -9,3 +9,4 @@ export { lowercase, isBuilderInputFocused, } from "./helpers" +export * as featureFlag from "./featureFlags" diff --git a/packages/builder/src/stores/builder/screenComponent.ts b/packages/builder/src/stores/builder/screenComponent.ts index a061158e6a..079401891c 100644 --- a/packages/builder/src/stores/builder/screenComponent.ts +++ b/packages/builder/src/stores/builder/screenComponent.ts @@ -5,9 +5,15 @@ import { viewsV2 } from "./viewsV2" import { findComponentsBySettingsType } from "@/helpers/screen" import { Screen, Table, ViewV2 } from "@budibase/types" +import { featureFlag } from "@/helpers" + export const screenComponentErrors = derived( [selectedScreen, tables, viewsV2], ([$selectedScreen, $tables, $viewsV2]): Record => { + if (!featureFlag.isEnabled("CHECK_SCREEN_COMPONENT_SETTINGS_ERRORS")) { + return {} + } + function flattenTablesAndViews(tables: Table[], views: ViewV2[]) { return { ...tables.reduce( diff --git a/packages/types/src/sdk/featureFlag.ts b/packages/types/src/sdk/featureFlag.ts index 996d3bba8d..d9f092c80a 100644 --- a/packages/types/src/sdk/featureFlag.ts +++ b/packages/types/src/sdk/featureFlag.ts @@ -1,5 +1,6 @@ export enum FeatureFlag { USE_ZOD_VALIDATOR = "USE_ZOD_VALIDATOR", + CHECK_SCREEN_COMPONENT_SETTINGS_ERRORS = "CHECK_SCREEN_COMPONENT_SETTINGS_ERRORS", // Account-portal DIRECT_LOGIN_TO_ACCOUNT_PORTAL = "DIRECT_LOGIN_TO_ACCOUNT_PORTAL", @@ -7,6 +8,7 @@ export enum FeatureFlag { export const FeatureFlagDefaults = { [FeatureFlag.USE_ZOD_VALIDATOR]: false, + [FeatureFlag.CHECK_SCREEN_COMPONENT_SETTINGS_ERRORS]: false, // Account-portal [FeatureFlag.DIRECT_LOGIN_TO_ACCOUNT_PORTAL]: false, From afee1ac993a4547791f8504d975f447ac40fd70b Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Fri, 24 Jan 2025 09:42:29 +0100 Subject: [PATCH 09/11] Whitelist checks --- .../src/stores/builder/screenComponent.ts | 39 ++++++++++--------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/packages/builder/src/stores/builder/screenComponent.ts b/packages/builder/src/stores/builder/screenComponent.ts index 2803acc953..6578334310 100644 --- a/packages/builder/src/stores/builder/screenComponent.ts +++ b/packages/builder/src/stores/builder/screenComponent.ts @@ -20,6 +20,18 @@ function reduceBy( ) } +const friendlyNameByType: Partial> = { + viewV2: "view", +} + +const validationKeyByType: Record = { + table: "tableId", + view: "name", + viewV2: "id", + query: "_id", + custom: null, +} + export const screenComponentErrors = derived( [selectedScreen, tables, views, viewsV2, queries], ([$selectedScreen, $tables, $views, $viewsV2, $queries]): Record< @@ -30,35 +42,24 @@ export const screenComponentErrors = derived( screen: Screen, datasources: Record ) { - const friendlyNameByType: Partial> = { - viewV2: "view", - } - - const primaryKeyByType: Record = { - table: "tableId", - view: "name", - viewV2: "id", - query: "_id", - custom: "" as never, - } - const result: Record = {} for (const { component, setting } of findComponentsBySettingsType( screen, ["table", "dataSource"] )) { const componentSettings = component[setting.key] - const { type, label } = componentSettings - if (type === "custom") { + const { label } = componentSettings + const type = componentSettings as DatasourceType + + const validationKey = validationKeyByType[type] + if (!validationKey) { continue } - const resourceId = - componentSettings[primaryKeyByType[type as DatasourceType]] + const resourceId = componentSettings[validationKey] if (!datasources[resourceId]) { - const friendlyTypeName = - friendlyNameByType[type as keyof typeof friendlyNameByType] ?? type + const friendlyTypeName = friendlyNameByType[type] ?? type result[component._id!] = [ - `The ${friendlyTypeName} named "${label}" does not exist`, + `The ${friendlyTypeName} named "${label}" could not be found`, ] } } From 8497a060407870973d8fe0494b71056df00d932c Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Fri, 24 Jan 2025 09:53:37 +0100 Subject: [PATCH 10/11] Fix --- packages/builder/src/stores/builder/screenComponent.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/builder/src/stores/builder/screenComponent.ts b/packages/builder/src/stores/builder/screenComponent.ts index 6578334310..6fb1a7d134 100644 --- a/packages/builder/src/stores/builder/screenComponent.ts +++ b/packages/builder/src/stores/builder/screenComponent.ts @@ -49,7 +49,7 @@ export const screenComponentErrors = derived( )) { const componentSettings = component[setting.key] const { label } = componentSettings - const type = componentSettings as DatasourceType + const type = componentSettings.type as DatasourceType const validationKey = validationKeyByType[type] if (!validationKey) { From 1f28bf978b79e804d6dbf5bb44db9a4f887574e8 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Fri, 24 Jan 2025 10:23:02 +0100 Subject: [PATCH 11/11] Move type to ui --- packages/builder/src/stores/builder/screenComponent.ts | 8 ++++---- packages/types/src/documents/app/datasource.ts | 2 -- packages/types/src/ui/datasource.ts | 1 + packages/types/src/ui/index.ts | 1 + 4 files changed, 6 insertions(+), 6 deletions(-) create mode 100644 packages/types/src/ui/datasource.ts diff --git a/packages/builder/src/stores/builder/screenComponent.ts b/packages/builder/src/stores/builder/screenComponent.ts index 547a8170a8..d8169fdedb 100644 --- a/packages/builder/src/stores/builder/screenComponent.ts +++ b/packages/builder/src/stores/builder/screenComponent.ts @@ -3,7 +3,7 @@ import { tables } from "./tables" import { selectedScreen } from "./screens" import { viewsV2 } from "./viewsV2" import { findComponentsBySettingsType } from "@/helpers/screen" -import { DatasourceType, Screen } from "@budibase/types" +import { UIDatasourceType, Screen } from "@budibase/types" import { queries } from "./queries" import { views } from "./views" import { featureFlag } from "@/helpers" @@ -21,11 +21,11 @@ function reduceBy( ) } -const friendlyNameByType: Partial> = { +const friendlyNameByType: Partial> = { viewV2: "view", } -const validationKeyByType: Record = { +const validationKeyByType: Record = { table: "tableId", view: "name", viewV2: "id", @@ -53,7 +53,7 @@ export const screenComponentErrors = derived( )) { const componentSettings = component[setting.key] const { label } = componentSettings - const type = componentSettings.type as DatasourceType + const type = componentSettings.type as UIDatasourceType const validationKey = validationKeyByType[type] if (!validationKey) { diff --git a/packages/types/src/documents/app/datasource.ts b/packages/types/src/documents/app/datasource.ts index 27828c5455..a0be7bd80d 100644 --- a/packages/types/src/documents/app/datasource.ts +++ b/packages/types/src/documents/app/datasource.ts @@ -57,5 +57,3 @@ export interface RestConfig { } dynamicVariables?: DynamicVariable[] } - -export type DatasourceType = "table" | "view" | "viewV2" | "query" | "custom" diff --git a/packages/types/src/ui/datasource.ts b/packages/types/src/ui/datasource.ts new file mode 100644 index 0000000000..53740e8c4d --- /dev/null +++ b/packages/types/src/ui/datasource.ts @@ -0,0 +1 @@ +export type UIDatasourceType = "table" | "view" | "viewV2" | "query" | "custom" diff --git a/packages/types/src/ui/index.ts b/packages/types/src/ui/index.ts index 907f4ec0b5..6e5f37608c 100644 --- a/packages/types/src/ui/index.ts +++ b/packages/types/src/ui/index.ts @@ -2,3 +2,4 @@ export * from "./stores" export * from "./bindings" export * from "./components" export * from "./dataFetch" +export * from "./datasource"