Merge branch 'master' into BUDI-9016/extract-componenterrors-from-client

This commit is contained in:
Adria Navarro 2025-01-27 13:27:25 +01:00
commit a3e69b752d
6 changed files with 123 additions and 69 deletions

View File

@ -31,6 +31,11 @@
import IntegrationQueryEditor from "@/components/integration/index.svelte" import IntegrationQueryEditor from "@/components/integration/index.svelte"
import { makePropSafe as safe } from "@budibase/string-templates" import { makePropSafe as safe } from "@budibase/string-templates"
import { findAllComponents } from "@/helpers/components" import { findAllComponents } from "@/helpers/components"
import {
extractFields,
extractJSONArrayFields,
extractRelationships,
} from "@/helpers/bindings"
import ClientBindingPanel from "@/components/common/bindings/ClientBindingPanel.svelte" import ClientBindingPanel from "@/components/common/bindings/ClientBindingPanel.svelte"
import DataSourceCategory from "@/components/design/settings/controls/DataSourceSelect/DataSourceCategory.svelte" import DataSourceCategory from "@/components/design/settings/controls/DataSourceSelect/DataSourceCategory.svelte"
import { API } from "@/api" import { API } from "@/api"
@ -81,67 +86,9 @@
value: `{{ literal ${safe(provider._id)} }}`, value: `{{ literal ${safe(provider._id)} }}`,
type: "provider", type: "provider",
})) }))
$: links = bindings $: links = extractRelationships(bindings)
// Get only link bindings $: fields = extractFields(bindings)
.filter(x => x.fieldSchema?.type === "link") $: jsonArrays = extractJSONArrayFields(bindings)
// Filter out bindings provided by forms
.filter(x => !x.component?.endsWith("/form"))
.map(binding => {
const { providerId, readableBinding, fieldSchema } = binding || {}
const { name, tableId } = fieldSchema || {}
const safeProviderId = safe(providerId)
return {
providerId,
label: readableBinding,
fieldName: name,
tableId,
type: "link",
// These properties will be enriched by the client library and provide
// details of the parent row of the relationship field, from context
rowId: `{{ ${safeProviderId}.${safe("_id")} }}`,
rowTableId: `{{ ${safeProviderId}.${safe("tableId")} }}`,
}
})
$: fields = bindings
.filter(
x =>
x.fieldSchema?.type === "attachment" ||
(x.fieldSchema?.type === "array" && x.tableId)
)
.map(binding => {
const { providerId, readableBinding, runtimeBinding } = binding
const { name, type, tableId } = binding.fieldSchema
return {
providerId,
label: readableBinding,
fieldName: name,
fieldType: type,
tableId,
type: "field",
value: `{{ literal ${runtimeBinding} }}`,
}
})
$: jsonArrays = bindings
.filter(
x =>
x.fieldSchema?.type === "jsonarray" ||
(x.fieldSchema?.type === "json" && x.fieldSchema?.subtype === "array")
)
.map(binding => {
const { providerId, readableBinding, runtimeBinding, tableId } = binding
const { name, type, prefixKeys, subtype } = binding.fieldSchema
return {
providerId,
label: readableBinding,
fieldName: name,
fieldType: type,
tableId,
prefixKeys,
type: type === "jsonarray" ? "jsonarray" : "queryarray",
subtype,
value: `{{ literal ${runtimeBinding} }}`,
}
})
$: custom = { $: custom = {
type: "custom", type: "custom",
label: "JSON / CSV", label: "JSON / CSV",

View File

@ -0,0 +1,74 @@
import { makePropSafe } from "@budibase/string-templates"
import { UIBinding } from "@budibase/types"
export function extractRelationships(bindings: UIBinding[]) {
return (
bindings
// Get only link bindings
.filter(x => x.fieldSchema?.type === "link")
// Filter out bindings provided by forms
.filter(x => !x.component?.endsWith("/form"))
.map(binding => {
const { providerId, readableBinding, fieldSchema } = binding || {}
const { name, tableId } = fieldSchema || {}
const safeProviderId = makePropSafe(providerId)
return {
providerId,
label: readableBinding,
fieldName: name,
tableId,
type: "link",
// These properties will be enriched by the client library and provide
// details of the parent row of the relationship field, from context
rowId: `{{ ${safeProviderId}.${makePropSafe("_id")} }}`,
rowTableId: `{{ ${safeProviderId}.${makePropSafe("tableId")} }}`,
}
})
)
}
export function extractFields(bindings: UIBinding[]) {
return bindings
.filter(
x =>
x.fieldSchema?.type === "attachment" ||
(x.fieldSchema?.type === "array" && x.tableId)
)
.map(binding => {
const { providerId, readableBinding, runtimeBinding } = binding
const { name, type, tableId } = binding.fieldSchema!
return {
providerId,
label: readableBinding,
fieldName: name,
fieldType: type,
tableId,
type: "field",
value: `{{ literal ${runtimeBinding} }}`,
}
})
}
export function extractJSONArrayFields(bindings: UIBinding[]) {
return bindings
.filter(
x =>
x.fieldSchema?.type === "jsonarray" ||
(x.fieldSchema?.type === "json" && x.fieldSchema?.subtype === "array")
)
.map(binding => {
const { providerId, readableBinding, runtimeBinding, tableId } = binding
const { name, type, prefixKeys, subtype } = binding.fieldSchema!
return {
providerId,
label: readableBinding,
fieldName: name,
fieldType: type,
tableId,
prefixKeys,
type: type === "jsonarray" ? "jsonarray" : "queryarray",
subtype,
value: `{{ literal ${runtimeBinding} }}`,
}
})
}

View File

@ -10,3 +10,4 @@ export {
isBuilderInputFocused, isBuilderInputFocused,
} from "./helpers" } from "./helpers"
export * as featureFlag from "./featureFlags" export * as featureFlag from "./featureFlags"
export * as bindings from "./bindings"

View File

@ -14,13 +14,14 @@ import {
} from "@budibase/types" } from "@budibase/types"
import { queries } from "./queries" import { queries } from "./queries"
import { views } from "./views" import { views } from "./views"
import { featureFlag } from "@/helpers"
import { findAllComponents } from "@/helpers/components" import { findAllComponents } from "@/helpers/components"
import { bindings, featureFlag } from "@/helpers"
import { getBindableProperties } from "@/dataBinding"
function reduceBy<TItem extends {}, TKey extends keyof TItem>( function reduceBy<TItem extends {}, TKey extends keyof TItem>(
key: TKey, key: TKey,
list: TItem[] list: TItem[]
) { ): Record<string, any> {
return list.reduce( return list.reduce(
(result, item) => ({ (result, item) => ({
...result, ...result,
@ -40,6 +41,9 @@ const validationKeyByType: Record<UIDatasourceType, string | null> = {
viewV2: "id", viewV2: "id",
query: "_id", query: "_id",
custom: null, custom: null,
link: "rowId",
field: "value",
jsonarray: "value",
} }
export const screenComponentErrors = derived( export const screenComponentErrors = derived(
@ -77,10 +81,6 @@ function getInvalidDatasources(
"dataSource", "dataSource",
])) { ])) {
const componentSettings = component[setting.key] const componentSettings = component[setting.key]
if (!componentSettings) {
continue
}
const { label } = componentSettings const { label } = componentSettings
const type = componentSettings.type as UIDatasourceType const type = componentSettings.type as UIDatasourceType
@ -88,8 +88,17 @@ function getInvalidDatasources(
if (!validationKey) { if (!validationKey) {
continue continue
} }
const componentBindings = getBindableProperties(screen, component._id)
const componentDatasources = {
...reduceBy("rowId", bindings.extractRelationships(componentBindings)),
...reduceBy("value", bindings.extractFields(componentBindings)),
...reduceBy("value", bindings.extractJSONArrayFields(componentBindings)),
}
const resourceId = componentSettings[validationKey] const resourceId = componentSettings[validationKey]
if (!datasources[resourceId]) { if (!{ ...datasources, ...componentDatasources }[resourceId]) {
const friendlyTypeName = friendlyNameByType[type] ?? type const friendlyTypeName = friendlyNameByType[type] ?? type
result[component._id!] = [ result[component._id!] = [
{ {

View File

@ -24,3 +24,18 @@ export type InsertAtPositionFn = (_: {
value: string value: string
cursor?: { anchor: number } cursor?: { anchor: number }
}) => void }) => void
export interface UIBinding {
tableId?: string
fieldSchema?: {
name: string
tableId: string
type: string
subtype?: string
prefixKeys?: string
}
component?: string
providerId: string
readableBinding?: string
runtimeBinding?: string
}

View File

@ -1 +1,9 @@
export type UIDatasourceType = "table" | "view" | "viewV2" | "query" | "custom" export type UIDatasourceType =
| "table"
| "view"
| "viewV2"
| "query"
| "custom"
| "link"
| "field"
| "jsonarray"