From a06b1b4310860efba76d9f932208c9e8d7c5ed36 Mon Sep 17 00:00:00 2001 From: Peter Clement Date: Wed, 15 Dec 2021 14:32:26 +0000 Subject: [PATCH 01/14] fixing issue 3489 where default role wasn't saving correctly --- .../src/components/backend/DataTable/modals/EditRoles.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/builder/src/components/backend/DataTable/modals/EditRoles.svelte b/packages/builder/src/components/backend/DataTable/modals/EditRoles.svelte index 588a3a8486..7fa9482fbe 100644 --- a/packages/builder/src/components/backend/DataTable/modals/EditRoles.svelte +++ b/packages/builder/src/components/backend/DataTable/modals/EditRoles.svelte @@ -6,7 +6,7 @@ import ErrorsBox from "components/common/ErrorsBox.svelte" import { roles } from "stores/backend" - const BASE_ROLE = { _id: "", inherits: "BASIC", permissionId: "Read/Write" } + const BASE_ROLE = { _id: "", inherits: "BASIC", permissionId: "write" } let basePermissions = [] let selectedRole = BASE_ROLE From 18671d24b7c0216c6bfaa339c66fc1082b6230d3 Mon Sep 17 00:00:00 2001 From: Peter Clement Date: Wed, 15 Dec 2021 16:32:04 +0000 Subject: [PATCH 02/14] fix issue with 0 evaluating to false in query builder --- packages/server/src/api/controllers/row/internalSearch.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/server/src/api/controllers/row/internalSearch.js b/packages/server/src/api/controllers/row/internalSearch.js index 793454e601..d8ae77156a 100644 --- a/packages/server/src/api/controllers/row/internalSearch.js +++ b/packages/server/src/api/controllers/row/internalSearch.js @@ -191,7 +191,8 @@ class QueryBuilder { } if (this.query.equal) { build(this.query.equal, (key, value) => { - if (!value) { + // 0 evaluates to false, which means we would return all rows if we don't check it + if (!value && value !== 0) { return null } return `${key}:${builder.preprocess(value, allPreProcessingOpts)}` From 3c0b783b55a45f789efdbbcab9201e07fb0d69fc Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Mon, 20 Dec 2021 10:50:58 -0500 Subject: [PATCH 03/14] Fix dynamic variable deletion and move utils --- packages/builder/src/helpers/data/utils.js | 60 ------------------ .../rest/[query]/index.svelte | 63 +++++++++++++++---- 2 files changed, 52 insertions(+), 71 deletions(-) diff --git a/packages/builder/src/helpers/data/utils.js b/packages/builder/src/helpers/data/utils.js index 6d7843e0e5..b6478680ec 100644 --- a/packages/builder/src/helpers/data/utils.js +++ b/packages/builder/src/helpers/data/utils.js @@ -120,62 +120,6 @@ export function flipHeaderState(headersActivity) { return enabled } -// convert dynamic variables list to simple key/val object -export function getDynamicVariables(datasource, queryId) { - const variablesList = datasource?.config?.dynamicVariables - if (variablesList && variablesList.length > 0) { - const filtered = queryId - ? variablesList.filter(variable => variable.queryId === queryId) - : variablesList - return filtered.reduce( - (acc, next) => ({ ...acc, [next.name]: next.value }), - {} - ) - } - return {} -} - -// convert dynamic variables object back to a list, enrich with query id -export function rebuildVariables(datasource, queryId, variables) { - let newVariables = [] - if (variables) { - newVariables = Object.entries(variables).map(entry => { - return { - name: entry[0], - value: entry[1], - queryId, - } - }) - } - let existing = datasource?.config?.dynamicVariables || [] - // filter out any by same name - existing = existing.filter( - variable => - !newVariables.find( - newVar => newVar.name.toLowerCase() === variable.name.toLowerCase() - ) - ) - return [...existing, ...newVariables] -} - -export function shouldShowVariables(dynamicVariables, variablesReadOnly) { - return !!( - dynamicVariables && - // show when editable or when read only and not empty - (!variablesReadOnly || Object.keys(dynamicVariables).length > 0) - ) -} - -export function buildAuthConfigs(datasource) { - if (datasource?.config?.authConfigs) { - return datasource.config.authConfigs.map(c => ({ - label: c.name, - value: c._id, - })) - } - return [] -} - export default { breakQueryString, buildQueryString, @@ -184,8 +128,4 @@ export default { keyValueToQueryParameters, queryParametersToKeyValue, schemaToFields, - getDynamicVariables, - rebuildVariables, - shouldShowVariables, - buildAuthConfigs, } diff --git a/packages/builder/src/pages/builder/app/[application]/data/datasource/[selectedDatasource]/rest/[query]/index.svelte b/packages/builder/src/pages/builder/app/[application]/data/datasource/[selectedDatasource]/rest/[query]/index.svelte index a54a4f205a..1d53b64c2d 100644 --- a/packages/builder/src/pages/builder/app/[application]/data/datasource/[selectedDatasource]/rest/[query]/index.svelte +++ b/packages/builder/src/pages/builder/app/[application]/data/datasource/[selectedDatasource]/rest/[query]/index.svelte @@ -52,13 +52,10 @@ $: checkQueryName(url) $: responseSuccess = response?.info?.code >= 200 && response?.info?.code < 400 $: isGet = query?.queryVerb === "read" - $: authConfigs = restUtils.buildAuthConfigs(datasource) + $: authConfigs = buildAuthConfigs(datasource) $: schemaReadOnly = !responseSuccess $: variablesReadOnly = !responseSuccess - $: showVariablesTab = restUtils.shouldShowVariables( - dynamicVariables, - variablesReadOnly - ) + $: showVariablesTab = shouldShowVariables(dynamicVariables, variablesReadOnly) function getSelectedQuery() { return cloneDeep( @@ -115,11 +112,7 @@ notifications.success(`Request saved successfully.`) if (dynamicVariables) { - datasource.config.dynamicVariables = restUtils.rebuildVariables( - datasource, - saveId, - dynamicVariables - ) + datasource.config.dynamicVariables = rebuildVariables(saveId) datasource = await datasources.save(datasource) } } catch (err) { @@ -158,6 +151,16 @@ return id } + const buildAuthConfigs = datasource => { + if (datasource?.config?.authConfigs) { + return datasource.config.authConfigs.map(c => ({ + label: c.name, + value: c._id, + })) + } + return [] + } + const schemaMenuItems = [ { text: "Create dynamic variable", @@ -177,6 +180,44 @@ }, ] + // convert dynamic variables list to simple key/val object + const getDynamicVariables = (datasource, queryId) => { + const variablesList = datasource?.config?.dynamicVariables + if (variablesList && variablesList.length > 0) { + const filtered = queryId + ? variablesList.filter(variable => variable.queryId === queryId) + : variablesList + return filtered.reduce( + (acc, next) => ({ ...acc, [next.name]: next.value }), + {} + ) + } + return {} + } + + // convert dynamic variables object back to a list, enrich with query id + const rebuildVariables = queryId => { + let variables = [] + if (dynamicVariables) { + variables = Object.entries(dynamicVariables).map(entry => { + return { + name: entry[0], + value: entry[1], + queryId, + } + }) + } + return variables + } + + const shouldShowVariables = (dynamicVariables, variablesReadOnly) => { + return !!( + dynamicVariables && + // show when editable or when read only and not empty + (!variablesReadOnly || Object.keys(dynamicVariables).length > 0) + ) + } + onMount(async () => { query = getSelectedQuery() // clear any unsaved changes to the datasource @@ -214,7 +255,7 @@ if (query && !query.fields.bodyType) { query.fields.bodyType = "none" } - dynamicVariables = restUtils.getDynamicVariables(datasource, query._id) + dynamicVariables = getDynamicVariables(datasource, query._id) }) From 9bd41a3284cb41b18b7cf165d79a2a04b1cba141 Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Mon, 20 Dec 2021 11:46:01 -0500 Subject: [PATCH 04/14] Default rest body type to json when there is an existing body --- .../[selectedDatasource]/rest/[query]/index.svelte | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/builder/src/pages/builder/app/[application]/data/datasource/[selectedDatasource]/rest/[query]/index.svelte b/packages/builder/src/pages/builder/app/[application]/data/datasource/[selectedDatasource]/rest/[query]/index.svelte index 1d53b64c2d..15b2b574a6 100644 --- a/packages/builder/src/pages/builder/app/[application]/data/datasource/[selectedDatasource]/rest/[query]/index.svelte +++ b/packages/builder/src/pages/builder/app/[application]/data/datasource/[selectedDatasource]/rest/[query]/index.svelte @@ -36,6 +36,7 @@ import DynamicVariableModal from "../../_components/DynamicVariableModal.svelte" import Placeholder from "assets/bb-spaceship.svg" import { cloneDeep } from "lodash/fp" + import { RawRestBodyTypes } from "constants/backend" let query, datasource let breakQs = {}, @@ -253,7 +254,11 @@ } } if (query && !query.fields.bodyType) { - query.fields.bodyType = "none" + if (query.fields.requestBody) { + query.fields.bodyType = RawRestBodyTypes.JSON + } else { + query.fields.bodyType = RawRestBodyTypes.NONE + } } dynamicVariables = getDynamicVariables(datasource, query._id) }) From 90eac00aa095022af285ab5f235c36219dfdcc5f Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Tue, 21 Dec 2021 12:25:37 +0000 Subject: [PATCH 05/14] Allowing switching between body types without losing state in REST UI. --- .../_components/RestBodyInput.svelte | 32 +++++++++++++------ 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/packages/builder/src/pages/builder/app/[application]/data/datasource/[selectedDatasource]/_components/RestBodyInput.svelte b/packages/builder/src/pages/builder/app/[application]/data/datasource/[selectedDatasource]/_components/RestBodyInput.svelte index c387f0b492..4e3b645373 100644 --- a/packages/builder/src/pages/builder/app/[application]/data/datasource/[selectedDatasource]/_components/RestBodyInput.svelte +++ b/packages/builder/src/pages/builder/app/[application]/data/datasource/[selectedDatasource]/_components/RestBodyInput.svelte @@ -16,17 +16,33 @@ export let query export let bodyType + let text = "" + let json = "" + $: checkRequestBody(bodyType) + $: updateRequestBody(bodyType, text, json) function checkRequestBody(type) { if (!bodyType || !query) { return } const currentType = typeof query?.fields.requestBody - if (objectTypes.includes(type) && currentType !== "object") { - query.fields.requestBody = {} - } else if (textTypes.includes(type) && currentType !== "string") { - query.fields.requestBody = "" + const isObject = objectTypes.includes(type) + const isText = textTypes.includes(type) + if (isText && currentType === "string") { + text = query.fields.requestBody + } else if (isObject && currentType === "object") { + json = query.fields.requestBody + } + } + + function updateRequestBody(type, text, json) { + if (type === RawRestBodyTypes.NONE) { + query.fields.requestBody = null + } else if (objectTypes.includes(type)) { + query.fields.requestBody = json + } else { + query.fields.requestBody = text } } @@ -49,16 +65,12 @@ THE REQUEST DOES NOT HAVE A BODY {:else if objectTypes.includes(bodyType)} - + {:else if textTypes.includes(bodyType)} (query.fields.requestBody = e.detail)} /> From 006df467cdbd426988cd527e7cd9b03a2bf7a649 Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Tue, 21 Dec 2021 09:39:34 -0500 Subject: [PATCH 06/14] Update curl import to support encoded characters in request body --- .../server/src/api/controllers/query/import/sources/curl.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/server/src/api/controllers/query/import/sources/curl.ts b/packages/server/src/api/controllers/query/import/sources/curl.ts index 61e1ae0215..b55d24403b 100644 --- a/packages/server/src/api/controllers/query/import/sources/curl.ts +++ b/packages/server/src/api/controllers/query/import/sources/curl.ts @@ -17,8 +17,12 @@ const parseBody = (curl: any) => { if (curl.data) { const keys = Object.keys(curl.data) if (keys.length) { - const key = keys[0] + let key = keys[0] try { + // filter out the dollar syntax used by curl for shell support + if (key.startsWith("$")) { + key = key.substring(1) + } return JSON.parse(key) } catch (e) { // do nothing From 70e19d40bcbdaf82c06f6e5f8e8291e5123886ff Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Tue, 21 Dec 2021 11:26:45 -0500 Subject: [PATCH 07/14] Further fix for dynamic variable deletion across different queries --- .../[selectedDatasource]/rest/[query]/index.svelte | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/builder/src/pages/builder/app/[application]/data/datasource/[selectedDatasource]/rest/[query]/index.svelte b/packages/builder/src/pages/builder/app/[application]/data/datasource/[selectedDatasource]/rest/[query]/index.svelte index 15b2b574a6..43037242f3 100644 --- a/packages/builder/src/pages/builder/app/[application]/data/datasource/[selectedDatasource]/rest/[query]/index.svelte +++ b/packages/builder/src/pages/builder/app/[application]/data/datasource/[selectedDatasource]/rest/[query]/index.svelte @@ -208,7 +208,12 @@ } }) } - return variables + + let existing = datasource?.config?.dynamicVariables || [] + // remove existing query variables (for changes and deletions) + existing = existing.filter(variable => variable.queryId !== queryId) + // re-add the new query variables + return [...existing, ...variables] } const shouldShowVariables = (dynamicVariables, variablesReadOnly) => { From e6d8b81ac4610094e694a1c88b8906a16662cfb8 Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Tue, 21 Dec 2021 13:48:54 -0500 Subject: [PATCH 08/14] Prevent escaping in query parameter bindings --- packages/server/src/integrations/rest.ts | 7 +++++-- packages/server/src/threads/utils.js | 10 +++++++++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/packages/server/src/integrations/rest.ts b/packages/server/src/integrations/rest.ts index 1817f780d3..63d7795a6d 100644 --- a/packages/server/src/integrations/rest.ts +++ b/packages/server/src/integrations/rest.ts @@ -48,7 +48,10 @@ module RestModule { const { performance } = require("perf_hooks") const FormData = require("form-data") const { URLSearchParams } = require("url") - const { parseStringPromise: xmlParser, Builder: XmlBuilder } = require("xml2js") + const { + parseStringPromise: xmlParser, + Builder: XmlBuilder, + } = require("xml2js") const SCHEMA: Integration = { docs: "https://github.com/node-fetch/node-fetch", @@ -211,7 +214,7 @@ module RestModule { break case BodyTypes.XML: if (object != null) { - string = (new XmlBuilder()).buildObject(object) + string = new XmlBuilder().buildObject(object) } input.body = string input.headers["Content-Type"] = "application/xml" diff --git a/packages/server/src/threads/utils.js b/packages/server/src/threads/utils.js index fee1e19b67..ffea596abd 100644 --- a/packages/server/src/threads/utils.js +++ b/packages/server/src/threads/utils.js @@ -8,6 +8,9 @@ const { processStringSync } = require("@budibase/string-templates") const VARIABLE_TTL_SECONDS = 3600 let client +const IS_TRIPLE_BRACE = new RegExp(/^{{3}.*}{3}$/) +const IS_HANDLEBARS = new RegExp(/^{{2}.*}{2}$/) + async function getClient() { if (!client) { client = await new redis.Client(redis.utils.Databases.QUERY_VARS).init() @@ -90,7 +93,12 @@ exports.enrichQueryFields = (fields, parameters = {}) => { enrichedQuery[key] = this.enrichQueryFields(fields[key], parameters) } else if (typeof fields[key] === "string") { // enrich string value as normal - enrichedQuery[key] = processStringSync(fields[key], parameters, { + let value = fields[key] + // add triple brace to avoid escaping e.g. '=' in cookie header + if (IS_HANDLEBARS.test(value) && !IS_TRIPLE_BRACE.test(value)) { + value = `{${value}}` + } + enrichedQuery[key] = processStringSync(value, parameters, { noHelpers: true, }) } else { From 2af1a6e116c4f419282f5316f52b99d736bf4222 Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Tue, 21 Dec 2021 14:08:09 -0500 Subject: [PATCH 09/14] Add bold and code cell renderers --- packages/bbui/src/Table/BoldRenderer.svelte | 11 +++++++++++ packages/bbui/src/Table/CodeRenderer.svelte | 5 +++++ packages/bbui/src/index.js | 4 ++++ .../rest/variables/ViewDynamicVariables.svelte | 8 ++++++-- 4 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 packages/bbui/src/Table/BoldRenderer.svelte create mode 100644 packages/bbui/src/Table/CodeRenderer.svelte diff --git a/packages/bbui/src/Table/BoldRenderer.svelte b/packages/bbui/src/Table/BoldRenderer.svelte new file mode 100644 index 0000000000..ea882d538e --- /dev/null +++ b/packages/bbui/src/Table/BoldRenderer.svelte @@ -0,0 +1,11 @@ + + +
{value}
+ + diff --git a/packages/bbui/src/Table/CodeRenderer.svelte b/packages/bbui/src/Table/CodeRenderer.svelte new file mode 100644 index 0000000000..a75bec663c --- /dev/null +++ b/packages/bbui/src/Table/CodeRenderer.svelte @@ -0,0 +1,5 @@ + + +{value} diff --git a/packages/bbui/src/index.js b/packages/bbui/src/index.js index c30047f73f..3e86d01a0d 100644 --- a/packages/bbui/src/index.js +++ b/packages/bbui/src/index.js @@ -61,6 +61,10 @@ export { default as ColorPicker } from "./ColorPicker/ColorPicker.svelte" export { default as InlineAlert } from "./InlineAlert/InlineAlert.svelte" export { default as Banner } from "./Banner/Banner.svelte" +// Renderers +export { default as BoldRenderer } from "./Table/BoldRenderer.svelte" +export { default as CodeRenderer } from "./Table/CodeRenderer.svelte" + // Typography export { default as Body } from "./Typography/Body.svelte" export { default as Heading } from "./Typography/Heading.svelte" diff --git a/packages/builder/src/components/backend/DatasourceNavigator/TableIntegrationMenu/rest/variables/ViewDynamicVariables.svelte b/packages/builder/src/components/backend/DatasourceNavigator/TableIntegrationMenu/rest/variables/ViewDynamicVariables.svelte index b000258399..a1e209f846 100644 --- a/packages/builder/src/components/backend/DatasourceNavigator/TableIntegrationMenu/rest/variables/ViewDynamicVariables.svelte +++ b/packages/builder/src/components/backend/DatasourceNavigator/TableIntegrationMenu/rest/variables/ViewDynamicVariables.svelte @@ -1,5 +1,5 @@ - onClick(detail)} - schema={dynamicVariableSchema} - data={dynamicVariables} - allowEditColumns={false} - allowEditRows={false} - allowSelectRows={false} - customRenderers={[ - { column: "name", component: BoldRenderer }, - { column: "value", component: CodeRenderer }, - ]} -/> - +{#if dynamicVariables.length > 0} +
onClick(detail)} + schema={dynamicVariableSchema} + data={dynamicVariables} + allowEditColumns={false} + allowEditRows={false} + allowSelectRows={false} + customRenderers={[ + { column: "name", component: BoldRenderer }, + { column: "value", component: CodeRenderer }, + ]} + /> +{:else} + No dynamic variables specified. +{/if} diff --git a/packages/client/src/components/app/DataProvider.svelte b/packages/client/src/components/app/DataProvider.svelte index 9c35de7870..ec328c60c6 100644 --- a/packages/client/src/components/app/DataProvider.svelte +++ b/packages/client/src/components/app/DataProvider.svelte @@ -123,7 +123,7 @@ rows, info, schema, - rowsLength: rows.length, + rowsLength: rows?.length, // Undocumented properties. These aren't supposed to be used in builder // bindings, but are used internally by other components From 8b6875aa9909c5e93fe3621072152156c4491604 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Tue, 4 Jan 2022 14:46:10 +0000 Subject: [PATCH 12/14] Hiding all tables from data sources when there is no data for them (no placeholder table). --- .../PlusConfigForm.svelte | 42 +++++++++++-------- .../variables/ViewDynamicVariables.svelte | 2 +- 2 files changed, 26 insertions(+), 18 deletions(-) diff --git a/packages/builder/src/components/backend/DatasourceNavigator/TableIntegrationMenu/PlusConfigForm.svelte b/packages/builder/src/components/backend/DatasourceNavigator/TableIntegrationMenu/PlusConfigForm.svelte index f736d29bee..b97f23d7a3 100644 --- a/packages/builder/src/components/backend/DatasourceNavigator/TableIntegrationMenu/PlusConfigForm.svelte +++ b/packages/builder/src/components/backend/DatasourceNavigator/TableIntegrationMenu/PlusConfigForm.svelte @@ -175,15 +175,19 @@ onConfirm={datasources.removeSchemaError} /> {/if} -
onClickTable(detail)} - schema={tableSchema} - data={Object.values(plusTables)} - allowEditColumns={false} - allowEditRows={false} - allowSelectRows={false} - customRenderers={[{ column: "primary", component: ArrayRenderer }]} -/> +{#if plusTables && Object.values(plusTables).length > 0} +
onClickTable(detail)} + schema={tableSchema} + data={Object.values(plusTables)} + allowEditColumns={false} + allowEditRows={false} + allowSelectRows={false} + customRenderers={[{ column: "primary", component: ArrayRenderer }]} + /> +{:else} + No tables found. +{/if} {#if plusTables?.length !== 0}
@@ -196,14 +200,18 @@ Tell budibase how your tables are related to get even more smart features. {/if} -
openRelationshipModal(detail.from, detail.to)} - schema={relationshipSchema} - data={relationshipInfo} - allowEditColumns={false} - allowEditRows={false} - allowSelectRows={false} -/> +{#if relationshipInfo && relationshipInfo.length > 0} +
openRelationshipModal(detail.from, detail.to)} + schema={relationshipSchema} + data={relationshipInfo} + allowEditColumns={false} + allowEditRows={false} + allowSelectRows={false} + /> +{:else} + No relationships configured. +{/if}