diff --git a/packages/builder/src/builderStore/dataBinding.js b/packages/builder/src/builderStore/dataBinding.js index 9cf00be3d4..9a41ad2afc 100644 --- a/packages/builder/src/builderStore/dataBinding.js +++ b/packages/builder/src/builderStore/dataBinding.js @@ -207,11 +207,11 @@ const getProviderContextBindings = (asset, dataProviders) => { const keys = Object.keys(schema).sort() // Generate safe unique runtime prefix - let runtimeId = component._id + let providerId = component._id if (runtimeSuffix) { - runtimeId += `-${runtimeSuffix}` + providerId += `-${runtimeSuffix}` } - const safeComponentId = makePropSafe(runtimeId) + const safeComponentId = makePropSafe(providerId) // Create bindable properties for each schema field keys.forEach(key => { @@ -235,7 +235,7 @@ const getProviderContextBindings = (asset, dataProviders) => { // Field schema and provider are required to construct relationship // datasource options, based on bindable properties fieldSchema, - providerId: component._id, + providerId, }) }) }) @@ -333,8 +333,11 @@ const getUrlBindings = asset => { */ export const getSchemaForDatasource = (asset, datasource, isForm = false) => { let schema, table + if (datasource) { const { type } = datasource + + // Determine the source table from the datasource type if (type === "provider") { const component = findComponent(asset.props, datasource.providerId) const source = getDatasourceForProvider(asset, component) @@ -342,11 +345,32 @@ export const getSchemaForDatasource = (asset, datasource, isForm = false) => { } else if (type === "query") { const queries = get(queriesStores).list table = queries.find(query => query._id === datasource._id) + } else if (type === "field") { + table = { name: datasource.fieldName } + const { fieldType } = datasource + if (fieldType === "attachment") { + schema = { + url: { + type: "string", + }, + name: { + type: "string", + }, + } + } else if (fieldType === "array") { + schema = { + value: { + type: "string", + }, + } + } } else { const tables = get(tablesStore).list table = tables.find(table => table._id === datasource.tableId) } - if (table) { + + // Determine the schema from the table if not already determined + if (table && !schema) { if (type === "view") { schema = cloneDeep(table.views?.[datasource.name]?.schema) } else if (type === "query" && isForm) { @@ -525,7 +549,7 @@ function bindingReplacement(bindableProperties, textWithBindings, convertTo) { * {{ literal [componentId] }} */ function extractLiteralHandlebarsID(value) { - return value?.match(/{{\s*literal[\s[]+([a-fA-F0-9]+)[\s\]]*}}/)?.[1] + return value?.match(/{{\s*literal\s*\[+([^\]]+)].*}}/)?.[1] } /** diff --git a/packages/builder/src/components/design/AppPreview/componentStructure.json b/packages/builder/src/components/design/AppPreview/componentStructure.json index c4fd33b084..357ea5a7be 100644 --- a/packages/builder/src/components/design/AppPreview/componentStructure.json +++ b/packages/builder/src/components/design/AppPreview/componentStructure.json @@ -4,7 +4,8 @@ "icon": "Article", "children": [ "tableblock", - "cardsblock" + "cardsblock", + "repeaterblock" ] }, "section", diff --git a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/DataProviderSelect.svelte b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/DataProviderSelect.svelte index d27a542f47..979443a403 100644 --- a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/DataProviderSelect.svelte +++ b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/DataProviderSelect.svelte @@ -11,10 +11,7 @@ const getValue = component => `{{ literal ${makePropSafe(component._id)} }}` $: path = findComponentPath($currentAsset.props, $store.selectedComponentId) - $: providers = path.filter( - component => - component._component === "@budibase/standard-components/dataprovider" - ) + $: providers = path.filter(c => c._component?.endsWith("/dataprovider")) // Set initial value to closest data provider onMount(() => { diff --git a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/DataSourceSelect.svelte b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/DataSourceSelect.svelte index d86f13e100..bc15110c09 100644 --- a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/DataSourceSelect.svelte +++ b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/DataSourceSelect.svelte @@ -20,16 +20,18 @@ import { notifications } from "@budibase/bbui" import ParameterBuilder from "components/integration/QueryParameterBuilder.svelte" import IntegrationQueryEditor from "components/integration/index.svelte" - - const dispatch = createEventDispatcher() - let anchorRight, dropdownRight - let drawer + import { makePropSafe as safe } from "@budibase/string-templates" export let value = {} export let otherSources export let showAllQueries export let bindings = [] + const dispatch = createEventDispatcher() + const arrayTypes = ["attachment", "array"] + let anchorRight, dropdownRight + let drawer + $: text = value?.label ?? "Choose an option" $: tables = $tablesStore.list.map(m => ({ label: m.name, @@ -54,8 +56,6 @@ name: query.name, tableId: query._id, ...query, - schema: query.schema, - parameters: query.parameters, type: "query", })) $: dataProviders = getDataProviderComponents( @@ -65,29 +65,40 @@ label: provider._instanceName, name: provider._instanceName, providerId: provider._id, - value: `{{ literal [${provider._id}] }}`, + value: `{{ literal ${safe(provider._id)} }}`, type: "provider", - schema: provider.schema, - })) - $: queryBindableProperties = bindings.map(property => ({ - ...property, - category: property.type === "instance" ? "Component" : "Table", - label: property.readableBinding, - path: property.readableBinding, })) $: links = bindings .filter(x => x.fieldSchema?.type === "link") - .map(property => { + .map(binding => { + const { providerId, readableBinding, fieldSchema } = binding || {} + const { name, tableId } = fieldSchema || {} + const safeProviderId = safe(providerId) return { - providerId: property.providerId, - label: property.readableBinding, - fieldName: property.fieldSchema.name, - tableId: property.fieldSchema.tableId, + 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: `{{ ${property.providerId}._id }}`, - rowTableId: `{{ ${property.providerId}.tableId }}`, + rowId: `{{ ${safeProviderId}.${safe("_id")} }}`, + rowTableId: `{{ ${safeProviderId}.${safe("tableId")} }}`, + } + }) + $: fields = bindings + .filter(x => arrayTypes.includes(x.fieldSchema?.type)) + .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} }}`, } }) @@ -102,6 +113,14 @@ ).source return $integrations[source].query[query.queryVerb] } + + const getQueryParams = query => { + return $queriesStore.list.find(q => q._id === query?._id)?.parameters || [] + } + + const getQueryDatasource = query => { + return $datasources.list.find(ds => ds._id === query?.datasourceId) + }