From 73d5d1289b1aab25e46991fad2dc1c24d03fe347 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Wed, 3 Nov 2021 11:57:47 +0000 Subject: [PATCH] Standardise fetching schema from client apps to fix issues with determing schema for certain data sources --- packages/client/src/api/datasources.js | 36 +++++++++++++++- packages/client/src/api/queries.js | 9 +++- .../src/components/app/DataProvider.svelte | 20 +++------ .../src/components/app/forms/Form.svelte | 41 +++++++++++-------- 4 files changed, 72 insertions(+), 34 deletions(-) diff --git a/packages/client/src/api/datasources.js b/packages/client/src/api/datasources.js index 508e1e8db0..467f9d8683 100644 --- a/packages/client/src/api/datasources.js +++ b/packages/client/src/api/datasources.js @@ -1,8 +1,8 @@ import { cloneDeep } from "lodash/fp" -import { fetchTableData } from "./tables" +import { fetchTableData, fetchTableDefinition } from "./tables" import { fetchViewData } from "./views" import { fetchRelationshipData } from "./relationships" -import { executeQuery } from "./queries" +import { executeQuery, fetchQueryDefinition } from "./queries" /** * Fetches all rows for a particular Budibase data source. @@ -39,3 +39,35 @@ export const fetchDatasource = async dataSource => { // Enrich the result is always an array return Array.isArray(rows) ? rows : [] } + +/** + * Fetches the schema of any kind of datasource. + */ +export const fetchDatasourceSchema = async dataSource => { + if (!dataSource) { + return null + } + const { type } = dataSource + + // Nested providers should already have exposed their own schema + if (type === "provider") { + return dataSource.value?.schema + } + + // Tables, views and links can be fetched by table ID + if ( + (type === "table" || type === "view" || type === "link") && + dataSource.tableId + ) { + const table = await fetchTableDefinition(dataSource.tableId) + return table?.schema + } + + // Queries can be fetched by query ID + if (type === "query" && dataSource._id) { + const definition = await fetchQueryDefinition(dataSource._id) + return definition?.schema + } + + return null +} diff --git a/packages/client/src/api/queries.js b/packages/client/src/api/queries.js index 8735d9587b..257d07f8ef 100644 --- a/packages/client/src/api/queries.js +++ b/packages/client/src/api/queries.js @@ -5,7 +5,7 @@ import API from "./api" * Executes a query against an external data connector. */ export const executeQuery = async ({ queryId, parameters }) => { - const query = await API.get({ url: `/api/queries/${queryId}` }) + const query = await fetchQueryDefinition(queryId) if (query?.datasourceId == null) { notificationStore.actions.error("That query couldn't be found") return @@ -24,3 +24,10 @@ export const executeQuery = async ({ queryId, parameters }) => { } return res } + +/** + * Fetches the definition of an external query. + */ +export const fetchQueryDefinition = async queryId => { + return await API.get({ url: `/api/queries/${queryId}`, cache: true }) +} diff --git a/packages/client/src/components/app/DataProvider.svelte b/packages/client/src/components/app/DataProvider.svelte index df59820506..54e758ad3c 100644 --- a/packages/client/src/components/app/DataProvider.svelte +++ b/packages/client/src/components/app/DataProvider.svelte @@ -156,34 +156,24 @@ } const getSchema = async dataSource => { - if (dataSource?.schema) { - schema = dataSource.schema - } else if (dataSource?.tableId) { - const definition = await API.fetchTableDefinition(dataSource.tableId) - schema = definition?.schema ?? {} - } else if (dataSource?.type === "provider") { - schema = dataSource.value?.schema ?? {} - } else { - schema = {} - } + let newSchema = (await API.fetchDatasourceSchema(dataSource)) || {} // Ensure there are "name" properties for all fields and that field schema // are objects - let fixedSchema = {} - Object.entries(schema || {}).forEach(([fieldName, fieldSchema]) => { + Object.entries(newSchema).forEach(([fieldName, fieldSchema]) => { if (typeof fieldSchema === "string") { - fixedSchema[fieldName] = { + newSchema[fieldName] = { type: fieldSchema, name: fieldName, } } else { - fixedSchema[fieldName] = { + newSchema[fieldName] = { ...fieldSchema, name: fieldName, } } }) - schema = fixedSchema + schema = newSchema schemaLoaded = true } diff --git a/packages/client/src/components/app/forms/Form.svelte b/packages/client/src/components/app/forms/Form.svelte index 1fa8d7aa15..65cf7dc88b 100644 --- a/packages/client/src/components/app/forms/Form.svelte +++ b/packages/client/src/components/app/forms/Form.svelte @@ -34,25 +34,34 @@ return closestContext || {} } - // Fetches the form schema from this form's dataSource, if one exists + // Fetches the form schema from this form's dataSource const fetchSchema = async () => { - if (!dataSource?.tableId) { + if (!dataSource) { schema = {} - table = null - } else { - table = await API.fetchTableDefinition(dataSource?.tableId) - if (table) { - if (dataSource?.type === "query") { - schema = {} - const params = table.parameters || [] - params.forEach(param => { - schema[param.name] = { ...param, type: "string" } - }) - } else { - schema = table.schema || {} - } - } } + + // If the datasource is a query, then we instead use a schema of the query + // parameters rather than the output schema + else if ( + dataSource.type === "query" && + dataSource._id && + actionType === "Create" + ) { + const query = await API.fetchQueryDefinition(dataSource._id) + let paramSchema = {} + const params = query.parameters || [] + params.forEach(param => { + paramSchema[param.name] = { ...param, type: "string" } + }) + schema = paramSchema + } + + // For all other cases, just grab the normal schema + else { + const dataSourceSchema = await API.fetchDatasourceSchema(dataSource) + schema = dataSourceSchema || {} + } + loaded = true }