From 09ff8a06624c3c99ac0c5b5dfc046f6538a08a43 Mon Sep 17 00:00:00 2001 From: Mitch-Budibase Date: Wed, 31 Jan 2024 15:00:32 +0000 Subject: [PATCH 01/34] License Test Changes License.manage.spec.ts/StripeAPI.ts - Test updated and now successfully updates from Free plan to premium - createCheckoutSession updated to support this plan upgrade --- qa-core/src/account-api/api/apis/StripeAPI.ts | 4 ++-- .../tests/licensing/license.manage.spec.ts | 11 ++++++----- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/qa-core/src/account-api/api/apis/StripeAPI.ts b/qa-core/src/account-api/api/apis/StripeAPI.ts index 5a4e810655..aeb027f428 100644 --- a/qa-core/src/account-api/api/apis/StripeAPI.ts +++ b/qa-core/src/account-api/api/apis/StripeAPI.ts @@ -11,12 +11,12 @@ export default class StripeAPI extends BaseAPI { } async createCheckoutSession( - priceId: string, + price: object, opts: APIRequestOpts = { status: 200 } ) { return this.doRequest(() => { return this.client.post(`/api/stripe/checkout-session`, { - body: { priceId }, + body: { prices: [price] }, }) }, opts) } diff --git a/qa-core/src/account-api/tests/licensing/license.manage.spec.ts b/qa-core/src/account-api/tests/licensing/license.manage.spec.ts index 9a8662ea3b..2d5b9332c6 100644 --- a/qa-core/src/account-api/tests/licensing/license.manage.spec.ts +++ b/qa-core/src/account-api/tests/licensing/license.manage.spec.ts @@ -36,11 +36,11 @@ describe("license management", () => { const [plansRes, planBody] = await config.api.licenses.getPlans() // Select priceId from premium plan - let premiumPriceId = null - let businessPriceId = "" + let premiumPrice = null + let businessPriceId: "" for (const plan of planBody) { if (plan.type === PlanType.PREMIUM_PLUS) { - premiumPriceId = plan.prices[0].priceId + premiumPrice = plan.prices[0] } if (plan.type === PlanType.ENTERPRISE_BASIC) { businessPriceId = plan.prices[0].priceId @@ -49,7 +49,7 @@ describe("license management", () => { // Create checkout session for price const checkoutSessionRes = await config.api.stripe.createCheckoutSession( - premiumPriceId + { id: premiumPrice.priceId, type: premiumPrice.type } ) const checkoutSessionUrl = checkoutSessionRes[1].url expect(checkoutSessionUrl).toContain("checkout.stripe.com") @@ -84,7 +84,7 @@ describe("license management", () => { customer: customer.id, items: [ { - price: premiumPriceId, + price: premiumPrice.priceId, quantity: 1, }, ], @@ -105,6 +105,7 @@ describe("license management", () => { expect(portalSessionBody.url).toContain("billing.stripe.com") // Update subscription from premium to business license + //await config.api.licenses.updatePlan(businessPriceId.priceId) await config.api.licenses.updatePlan(businessPriceId) // License updated to Business From d7ae4c04b9e84e48ab3bc3553ed5e39aa6e3e414 Mon Sep 17 00:00:00 2001 From: Mitch-Budibase Date: Tue, 6 Feb 2024 17:15:11 +0000 Subject: [PATCH 02/34] Removing commented line --- qa-core/src/account-api/tests/licensing/license.manage.spec.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/qa-core/src/account-api/tests/licensing/license.manage.spec.ts b/qa-core/src/account-api/tests/licensing/license.manage.spec.ts index 2d5b9332c6..baed5734cf 100644 --- a/qa-core/src/account-api/tests/licensing/license.manage.spec.ts +++ b/qa-core/src/account-api/tests/licensing/license.manage.spec.ts @@ -105,7 +105,6 @@ describe("license management", () => { expect(portalSessionBody.url).toContain("billing.stripe.com") // Update subscription from premium to business license - //await config.api.licenses.updatePlan(businessPriceId.priceId) await config.api.licenses.updatePlan(businessPriceId) // License updated to Business From 3726e10f3a6bee0f40e9f3f86da6bd6d7c309af5 Mon Sep 17 00:00:00 2001 From: Mitch-Budibase Date: Tue, 6 Feb 2024 17:28:00 +0000 Subject: [PATCH 03/34] lint --- .../src/account-api/tests/licensing/license.manage.spec.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/qa-core/src/account-api/tests/licensing/license.manage.spec.ts b/qa-core/src/account-api/tests/licensing/license.manage.spec.ts index baed5734cf..85ee530bb7 100644 --- a/qa-core/src/account-api/tests/licensing/license.manage.spec.ts +++ b/qa-core/src/account-api/tests/licensing/license.manage.spec.ts @@ -48,9 +48,10 @@ describe("license management", () => { } // Create checkout session for price - const checkoutSessionRes = await config.api.stripe.createCheckoutSession( - { id: premiumPrice.priceId, type: premiumPrice.type } - ) + const checkoutSessionRes = await config.api.stripe.createCheckoutSession({ + id: premiumPrice.priceId, + type: premiumPrice.type, + }) const checkoutSessionUrl = checkoutSessionRes[1].url expect(checkoutSessionUrl).toContain("checkout.stripe.com") From 4f05bc619b4c2cd8627b965326389bfaee6bf199 Mon Sep 17 00:00:00 2001 From: Peter Clement Date: Mon, 19 Feb 2024 09:51:10 +0000 Subject: [PATCH 04/34] Handle branding cookies with different tenant ids --- .../builder/src/pages/builder/auth/_layout.svelte | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/packages/builder/src/pages/builder/auth/_layout.svelte b/packages/builder/src/pages/builder/auth/_layout.svelte index f5484182e8..8902e9ac61 100644 --- a/packages/builder/src/pages/builder/auth/_layout.svelte +++ b/packages/builder/src/pages/builder/auth/_layout.svelte @@ -10,13 +10,18 @@ $redirect("../") } - if ($admin?.checklist?.branding) { + if ($admin.cloud && $admin?.checklist?.branding) { let url = new URL(window.location.href) let hostname = url.hostname let parts = hostname.split(".") - let tenantId = parts[0] + let newTenantId = parts[0] let domain = parts.slice(-2).join(".") - CookieUtils.setCookie("tenantId", tenantId, domain) + + let existingTenantId = CookieUtils.getCookie("tenantId") + + if (!existingTenantId || existingTenantId !== newTenantId) { + CookieUtils.setCookie("tenantId", newTenantId, domain) + } } if ( From 2e53be5bd841b12b46f79b429d642e600a6aa4b1 Mon Sep 17 00:00:00 2001 From: Peter Clement Date: Wed, 21 Feb 2024 16:13:43 +0000 Subject: [PATCH 05/34] add null check --- packages/builder/src/pages/builder/auth/_layout.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/builder/src/pages/builder/auth/_layout.svelte b/packages/builder/src/pages/builder/auth/_layout.svelte index 8902e9ac61..1a40847aa0 100644 --- a/packages/builder/src/pages/builder/auth/_layout.svelte +++ b/packages/builder/src/pages/builder/auth/_layout.svelte @@ -10,7 +10,7 @@ $redirect("../") } - if ($admin.cloud && $admin?.checklist?.branding) { + if ($admin?.cloud && $admin?.checklist?.branding) { let url = new URL(window.location.href) let hostname = url.hostname let parts = hostname.split(".") From b6b39375a1a1fdb800aae8cc8e4ccb40ae59b512 Mon Sep 17 00:00:00 2001 From: Peter Clement Date: Wed, 21 Feb 2024 16:16:25 +0000 Subject: [PATCH 06/34] account portal ref --- packages/account-portal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/account-portal b/packages/account-portal index 8c446c4ba3..92129b0d62 160000 --- a/packages/account-portal +++ b/packages/account-portal @@ -1 +1 @@ -Subproject commit 8c446c4ba385592127fa31755d3b64467b291882 +Subproject commit 92129b0d6251a7b4dabb367c15b09c079399b763 From fe28ae50e587302189998ed71f367e0c433ccca9 Mon Sep 17 00:00:00 2001 From: Peter Clement Date: Wed, 21 Feb 2024 16:18:12 +0000 Subject: [PATCH 07/34] ref --- packages/pro | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/pro b/packages/pro index 60e47a8249..336bf2184c 160000 --- a/packages/pro +++ b/packages/pro @@ -1 +1 @@ -Subproject commit 60e47a8249fd6291a6bc20fe3fe6776b11938fa1 +Subproject commit 336bf2184cf632fdc2bffbad5628e8b15dd381bd From 25d59d0be0b1d9f74d353ae63077e41cd5b780f8 Mon Sep 17 00:00:00 2001 From: Peter Clement Date: Wed, 21 Feb 2024 16:31:32 +0000 Subject: [PATCH 08/34] Revert "ref" This reverts commit fe28ae50e587302189998ed71f367e0c433ccca9. --- packages/pro | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/pro b/packages/pro index 336bf2184c..60e47a8249 160000 --- a/packages/pro +++ b/packages/pro @@ -1 +1 @@ -Subproject commit 336bf2184cf632fdc2bffbad5628e8b15dd381bd +Subproject commit 60e47a8249fd6291a6bc20fe3fe6776b11938fa1 From c94cf7c12c5273571a05f3d3a50c9c212c92bb63 Mon Sep 17 00:00:00 2001 From: Peter Clement Date: Wed, 21 Feb 2024 16:31:45 +0000 Subject: [PATCH 09/34] update --- packages/pro | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/pro b/packages/pro index 60e47a8249..336bf2184c 160000 --- a/packages/pro +++ b/packages/pro @@ -1 +1 @@ -Subproject commit 60e47a8249fd6291a6bc20fe3fe6776b11938fa1 +Subproject commit 336bf2184cf632fdc2bffbad5628e8b15dd381bd From 13b34cf77eff99b2111f74c167a4a07008593821 Mon Sep 17 00:00:00 2001 From: melohagan <101575380+melohagan@users.noreply.github.com> Date: Wed, 21 Feb 2024 17:02:55 +0000 Subject: [PATCH 10/34] Add nestedSchemaFields to RestQueryViewer (#13102) --- .../builder/src/components/integration/RestQueryViewer.svelte | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/builder/src/components/integration/RestQueryViewer.svelte b/packages/builder/src/components/integration/RestQueryViewer.svelte index 33946d16dc..1a12c1fa47 100644 --- a/packages/builder/src/components/integration/RestQueryViewer.svelte +++ b/packages/builder/src/components/integration/RestQueryViewer.svelte @@ -60,6 +60,7 @@ let authConfigId let dynamicVariables, addVariableModal, varBinding, globalDynamicBindings let restBindings = getRestBindings() + let nestedSchemaFields = {} $: staticVariables = datasource?.config?.staticVariables || {} @@ -160,6 +161,7 @@ newQuery.fields.authConfigId = authConfigId newQuery.fields.disabledHeaders = restUtils.flipHeaderState(enabledHeaders) newQuery.schema = schema || {} + newQuery.nestedSchemaFields = nestedSchemaFields || {} return newQuery } @@ -238,6 +240,7 @@ } } schema = response.schema + nestedSchemaFields = response.nestedSchemaFields notifications.success("Request sent successfully") } } catch (error) { From 722b81cc90e58f94dcc2b2cd99d4f2dea975eb9f Mon Sep 17 00:00:00 2001 From: Budibase Staging Release Bot <> Date: Wed, 21 Feb 2024 17:03:41 +0000 Subject: [PATCH 11/34] Bump version to 2.20.7 --- lerna.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lerna.json b/lerna.json index 843d64fe67..67dd274074 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "2.20.6", + "version": "2.20.7", "npmClient": "yarn", "packages": [ "packages/*", From 6545af12fa982b4b0a69ef1ba4113a56fb4e7ffc Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Wed, 21 Feb 2024 21:28:25 +0000 Subject: [PATCH 12/34] Revert "Add nestedSchemaFields to RestQueryViewer (#13102)" This reverts commit 13b34cf77eff99b2111f74c167a4a07008593821. --- .../builder/src/components/integration/RestQueryViewer.svelte | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/builder/src/components/integration/RestQueryViewer.svelte b/packages/builder/src/components/integration/RestQueryViewer.svelte index 1a12c1fa47..33946d16dc 100644 --- a/packages/builder/src/components/integration/RestQueryViewer.svelte +++ b/packages/builder/src/components/integration/RestQueryViewer.svelte @@ -60,7 +60,6 @@ let authConfigId let dynamicVariables, addVariableModal, varBinding, globalDynamicBindings let restBindings = getRestBindings() - let nestedSchemaFields = {} $: staticVariables = datasource?.config?.staticVariables || {} @@ -161,7 +160,6 @@ newQuery.fields.authConfigId = authConfigId newQuery.fields.disabledHeaders = restUtils.flipHeaderState(enabledHeaders) newQuery.schema = schema || {} - newQuery.nestedSchemaFields = nestedSchemaFields || {} return newQuery } @@ -240,7 +238,6 @@ } } schema = response.schema - nestedSchemaFields = response.nestedSchemaFields notifications.success("Request sent successfully") } } catch (error) { From ae4bc362c325807a8d73ab07797572b261fd25eb Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Wed, 21 Feb 2024 21:30:22 +0000 Subject: [PATCH 13/34] =?UTF-8?q?Revert=20"Dynamic=20schema=20generation?= =?UTF-8?q?=20for=20query=20arrays:=20bug=20fix=20and=20refactor=20(#13?= =?UTF-8?q?=E2=80=A6"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 0c9d13c79a5b11f4dc19017e3983bc28a295717b. --- .../builder/src/stores/builder/queries.js | 12 +- .../server/src/api/controllers/query/index.ts | 145 ++++----- .../src/api/controllers/query/validation.ts | 40 +-- packages/server/src/api/routes/query.ts | 2 +- .../src/api/routes/tests/datasource.spec.ts | 18 +- .../routes/tests/environmentVariables.spec.ts | 12 +- .../routes/tests/queries/query.seq.spec.ts | 305 +++++------------- .../src/tests/utilities/TestConfiguration.ts | 22 ++ .../server/src/tests/utilities/api/query.ts | 16 - .../server/src/tests/utilities/structures.ts | 2 +- packages/server/src/threads/definitions.ts | 4 +- packages/server/src/threads/query.ts | 2 +- packages/types/src/documents/app/query.ts | 2 +- 13 files changed, 203 insertions(+), 379 deletions(-) diff --git a/packages/builder/src/stores/builder/queries.js b/packages/builder/src/stores/builder/queries.js index 32e8501452..7f5f83a792 100644 --- a/packages/builder/src/stores/builder/queries.js +++ b/packages/builder/src/stores/builder/queries.js @@ -75,7 +75,17 @@ export function createQueriesStore() { } const preview = async query => { - const result = await API.previewQuery(query) + const parameters = query.parameters.reduce( + (acc, next) => ({ + ...acc, + [next.name]: next.default, + }), + {} + ) + const result = await API.previewQuery({ + ...query, + parameters, + }) // Assume all the fields are strings and create a basic schema from the // unique fields returned by the server const schema = {} diff --git a/packages/server/src/api/controllers/query/index.ts b/packages/server/src/api/controllers/query/index.ts index 768c921150..89330f3216 100644 --- a/packages/server/src/api/controllers/query/index.ts +++ b/packages/server/src/api/controllers/query/index.ts @@ -20,7 +20,6 @@ import { type ExecuteQueryRequest, type ExecuteQueryResponse, type Row, - QueryParameter, } from "@budibase/types" import { ValidQueryNameRegex, utils as JsonUtils } from "@budibase/shared-core" @@ -119,21 +118,6 @@ function getAuthConfig(ctx: UserCtx) { return authConfigCtx } -function enrichParameters( - queryParameters: QueryParameter[], - requestParameters: { [key: string]: string } = {} -): { - [key: string]: string -} { - // make sure parameters are fully enriched with defaults - for (let parameter of queryParameters) { - if (!requestParameters[parameter.name]) { - requestParameters[parameter.name] = parameter.default - } - } - return requestParameters -} - export async function preview(ctx: UserCtx) { const { datasource, envVars } = await sdk.datasources.getWithEnvVars( ctx.request.body.datasourceId @@ -158,68 +142,6 @@ export async function preview(ctx: UserCtx) { const authConfigCtx: any = getAuthConfig(ctx) - function getFieldMetadata(field: any, key: string): QuerySchema { - const makeQuerySchema = ( - type: FieldType, - name: string, - subtype?: string - ): QuerySchema => ({ - type, - name, - subtype, - }) - // Because custom queries have no fixed schema, we dynamically determine the schema, - // however types cannot be determined from null. We have no 'unknown' type, so we default to string. - let type = typeof field, - fieldMetadata = makeQuerySchema(FieldType.STRING, key) - if (field != null) - switch (type) { - case "boolean": - fieldMetadata = makeQuerySchema(FieldType.BOOLEAN, key) - break - case "object": - if (field instanceof Date) { - fieldMetadata = makeQuerySchema(FieldType.DATETIME, key) - } else if (Array.isArray(field)) { - if (field.some(item => JsonUtils.hasSchema(item))) { - fieldMetadata = makeQuerySchema( - FieldType.JSON, - key, - JsonFieldSubType.ARRAY - ) - } else { - fieldMetadata = makeQuerySchema(FieldType.ARRAY, key) - } - } else { - fieldMetadata = makeQuerySchema(FieldType.JSON, key) - } - break - case "number": - fieldMetadata = makeQuerySchema(FieldType.NUMBER, key) - break - } - return fieldMetadata - } - - function buildNestedSchema( - nestedSchemaFields: { - [key: string]: Record - }, - key: string, - fieldArray: any[] - ) { - let schema: { [key: string]: any } = {} - // build the schema by aggregating all row objects in the array - for (const item of fieldArray) { - if (JsonUtils.hasSchema(item)) { - for (const [key, value] of Object.entries(item)) { - schema[key] = getFieldMetadata(value, key) - } - } - } - nestedSchemaFields[key] = schema - } - function getSchemaFields( rows: any[], keys: string[] @@ -233,16 +155,51 @@ export async function preview(ctx: UserCtx) { const nestedSchemaFields: { [key: string]: Record } = {} + const makeQuerySchema = ( + type: FieldType, + name: string, + subtype?: string + ): QuerySchema => ({ + type, + name, + subtype, + }) if (rows?.length > 0) { - for (let key of new Set(keys)) { - const fieldMetadata = getFieldMetadata(rows[0][key], key) + for (let key of [...new Set(keys)] as string[]) { + const field = rows[0][key] + let type = typeof field, + fieldMetadata = makeQuerySchema(FieldType.STRING, key) + if (field) + switch (type) { + case "boolean": + fieldMetadata = makeQuerySchema(FieldType.BOOLEAN, key) + break + case "object": + if (field instanceof Date) { + fieldMetadata = makeQuerySchema(FieldType.DATETIME, key) + } else if (Array.isArray(field)) { + if (JsonUtils.hasSchema(field[0])) { + fieldMetadata = makeQuerySchema( + FieldType.JSON, + key, + JsonFieldSubType.ARRAY + ) + } else { + fieldMetadata = makeQuerySchema(FieldType.ARRAY, key) + } + nestedSchemaFields[key] = getSchemaFields( + field, + Object.keys(field[0]) + ).previewSchema + } else { + fieldMetadata = makeQuerySchema(FieldType.JSON, key) + } + break + case "number": + fieldMetadata = makeQuerySchema(FieldType.NUMBER, key) + break + } previewSchema[key] = fieldMetadata - if ( - fieldMetadata.type === FieldType.JSON && - fieldMetadata.subtype === JsonFieldSubType.ARRAY - ) { - buildNestedSchema(nestedSchemaFields, key, rows[0][key]) - } } } return { previewSchema, nestedSchemaFields } @@ -254,7 +211,7 @@ export async function preview(ctx: UserCtx) { datasource, queryVerb, fields, - parameters: enrichParameters(parameters), + parameters, transformer, queryId, schema, @@ -309,6 +266,15 @@ async function execute( if (!opts.isAutomation) { authConfigCtx = getAuthConfig(ctx) } + const enrichedParameters = ctx.request.body.parameters || {} + // make sure parameters are fully enriched with defaults + if (query && query.parameters) { + for (let parameter of query.parameters) { + if (!enrichedParameters[parameter.name]) { + enrichedParameters[parameter.name] = parameter.default + } + } + } // call the relevant CRUD method on the integration class try { @@ -318,10 +284,7 @@ async function execute( queryVerb: query.queryVerb, fields: query.fields, pagination: ctx.request.body.pagination, - parameters: enrichParameters( - query.parameters, - ctx.request.body.parameters - ), + parameters: enrichedParameters, transformer: query.transformer, queryId: ctx.params.queryId, // have to pass down to the thread runner - can't put into context now diff --git a/packages/server/src/api/controllers/query/validation.ts b/packages/server/src/api/controllers/query/validation.ts index 7d4958f1e6..339035c945 100644 --- a/packages/server/src/api/controllers/query/validation.ts +++ b/packages/server/src/api/controllers/query/validation.ts @@ -3,10 +3,11 @@ import Joi from "joi" const OPTIONAL_STRING = Joi.string().optional().allow(null).allow("") -function baseQueryValidation() { - return { - _id: OPTIONAL_STRING, - _rev: OPTIONAL_STRING, +export function queryValidation() { + return Joi.object({ + _id: Joi.string(), + _rev: Joi.string(), + name: Joi.string().required(), fields: Joi.object().required(), datasourceId: Joi.string().required(), readable: Joi.boolean(), @@ -16,19 +17,11 @@ function baseQueryValidation() { default: Joi.string().allow(""), }) ), - queryVerb: Joi.string().required(), + queryVerb: Joi.string().allow().required(), extra: Joi.object().optional(), schema: Joi.object({}).required().unknown(true), transformer: OPTIONAL_STRING, flags: Joi.object().optional(), - queryId: OPTIONAL_STRING, - } -} - -export function queryValidation() { - return Joi.object({ - ...baseQueryValidation(), - name: Joi.string().required(), }).unknown(true) } @@ -39,10 +32,19 @@ export function generateQueryValidation() { export function generateQueryPreviewValidation() { // prettier-ignore - return auth.joiValidator.body( - Joi.object({ - ...baseQueryValidation(), - name: OPTIONAL_STRING, - }).unknown(true) - ) + return auth.joiValidator.body(Joi.object({ + _id: OPTIONAL_STRING, + _rev: OPTIONAL_STRING, + readable: Joi.boolean().optional(), + fields: Joi.object().required(), + queryVerb: Joi.string().required(), + name: OPTIONAL_STRING, + flags: Joi.object().optional(), + schema: Joi.object().optional(), + extra: Joi.object().optional(), + datasourceId: Joi.string().required(), + transformer: OPTIONAL_STRING, + parameters: Joi.object({}).required().unknown(true), + queryId: OPTIONAL_STRING, + }).unknown(true)) } diff --git a/packages/server/src/api/routes/query.ts b/packages/server/src/api/routes/query.ts index eb857d0637..fd9c51da4d 100644 --- a/packages/server/src/api/routes/query.ts +++ b/packages/server/src/api/routes/query.ts @@ -8,8 +8,8 @@ import { paramResource, } from "../../middleware/resourceId" import { - generateQueryValidation, generateQueryPreviewValidation, + generateQueryValidation, } from "../controllers/query/validation" const { BUILDER, PermissionType, PermissionLevel } = permissions diff --git a/packages/server/src/api/routes/tests/datasource.spec.ts b/packages/server/src/api/routes/tests/datasource.spec.ts index 41229b0a2a..73bb5056ce 100644 --- a/packages/server/src/api/routes/tests/datasource.spec.ts +++ b/packages/server/src/api/routes/tests/datasource.spec.ts @@ -7,7 +7,6 @@ import sdk from "../../../sdk" import tk from "timekeeper" import { mocks } from "@budibase/backend-core/tests" -import { QueryPreview } from "@budibase/types" tk.freeze(mocks.date.MOCK_DATE) @@ -64,17 +63,14 @@ describe("/datasources", () => { datasource: any, fields: { path: string; queryString: string } ) { - const queryPreview: QueryPreview = { + return config.previewQuery( + request, + config, + datasource, fields, - datasourceId: datasource._id, - parameters: [], - transformer: null, - queryVerb: "read", - name: datasource.name, - schema: {}, - readable: true, - } - return config.api.query.previewQuery(queryPreview) + undefined, + "" + ) } it("should invalidate changed or removed variables", async () => { diff --git a/packages/server/src/api/routes/tests/environmentVariables.spec.ts b/packages/server/src/api/routes/tests/environmentVariables.spec.ts index 22114a1da3..aacf89ea6d 100644 --- a/packages/server/src/api/routes/tests/environmentVariables.spec.ts +++ b/packages/server/src/api/routes/tests/environmentVariables.spec.ts @@ -14,7 +14,6 @@ jest.mock("pg", () => { import * as setup from "./utilities" import { mocks } from "@budibase/backend-core/tests" import { env, events } from "@budibase/backend-core" -import { QueryPreview } from "@budibase/types" const structures = setup.structures @@ -121,19 +120,16 @@ describe("/api/env/variables", () => { .expect(200) expect(response.body.datasource._id).toBeDefined() - const queryPreview: QueryPreview = { + const query = { datasourceId: response.body.datasource._id, - parameters: [], + parameters: {}, fields: {}, queryVerb: "read", name: response.body.datasource.name, - transformer: null, - schema: {}, - readable: true, } const res = await request .post(`/api/queries/preview`) - .send(queryPreview) + .send(query) .set(config.defaultHeaders()) .expect("Content-Type", /json/) .expect(200) @@ -143,7 +139,7 @@ describe("/api/env/variables", () => { delete response.body.datasource.config expect(events.query.previewed).toBeCalledWith( response.body.datasource, - queryPreview + query ) expect(pg.Client).toHaveBeenCalledWith({ password: "test", ssl: undefined }) }) diff --git a/packages/server/src/api/routes/tests/queries/query.seq.spec.ts b/packages/server/src/api/routes/tests/queries/query.seq.spec.ts index 52d35fa782..ba41ba3d16 100644 --- a/packages/server/src/api/routes/tests/queries/query.seq.spec.ts +++ b/packages/server/src/api/routes/tests/queries/query.seq.spec.ts @@ -1,7 +1,5 @@ import tk from "timekeeper" -const pg = require("pg") - // Mock out postgres for this jest.mock("pg") jest.mock("node-fetch") @@ -24,13 +22,7 @@ import { checkCacheForDynamicVariable } from "../../../../threads/utils" const { basicQuery, basicDatasource } = setup.structures import { events, db as dbCore } from "@budibase/backend-core" -import { - Datasource, - Query, - SourceName, - QueryPreview, - QueryParameter, -} from "@budibase/types" +import { Datasource, Query, SourceName } from "@budibase/types" tk.freeze(Date.now()) @@ -226,26 +218,28 @@ describe("/queries", () => { describe("preview", () => { it("should be able to preview the query", async () => { - const queryPreview: QueryPreview = { + const query = { datasourceId: datasource._id, - queryVerb: "read", + parameters: {}, fields: {}, - parameters: [], - transformer: "return data", - name: datasource.name!, - schema: {}, - readable: true, + queryVerb: "read", + name: datasource.name, } - const responseBody = await config.api.query.previewQuery(queryPreview) + const res = await request + .post(`/api/queries/preview`) + .send(query) + .set(config.defaultHeaders()) + .expect("Content-Type", /json/) + .expect(200) // these responses come from the mock - expect(responseBody.schema).toEqual({ + expect(res.body.schema).toEqual({ a: { type: "string", name: "a" }, b: { type: "number", name: "b" }, }) - expect(responseBody.rows.length).toEqual(1) + expect(res.body.rows.length).toEqual(1) expect(events.query.previewed).toBeCalledTimes(1) delete datasource.config - expect(events.query.previewed).toBeCalledWith(datasource, queryPreview) + expect(events.query.previewed).toBeCalledWith(datasource, query) }) it("should apply authorization to endpoint", async () => { @@ -255,128 +249,6 @@ describe("/queries", () => { url: `/api/queries/preview`, }) }) - - it("should not error when trying to generate a nested schema for an empty array", async () => { - const queryPreview: QueryPreview = { - datasourceId: datasource._id, - parameters: [], - fields: {}, - queryVerb: "read", - name: datasource.name!, - transformer: "return data", - schema: {}, - readable: true, - } - const rows = [ - { - contacts: [], - }, - ] - pg.queryMock.mockImplementation(() => ({ - rows, - })) - - const responseBody = await config.api.query.previewQuery(queryPreview) - expect(responseBody).toEqual({ - nestedSchemaFields: {}, - rows, - schema: { - contacts: { type: "array", name: "contacts" }, - }, - }) - expect(responseBody.rows.length).toEqual(1) - delete datasource.config - }) - - it("should generate a nested schema based on all the nested items", async () => { - const queryPreview: QueryPreview = { - datasourceId: datasource._id, - parameters: [], - fields: {}, - queryVerb: "read", - name: datasource.name!, - transformer: "return data", - schema: {}, - readable: true, - } - const rows = [ - { - contacts: [ - { - address: "123 Lane", - }, - { - address: "456 Drive", - }, - { - postcode: "BT1 12N", - lat: 54.59, - long: -5.92, - }, - { - city: "Belfast", - }, - { - address: "789 Avenue", - phoneNumber: "0800-999-5555", - }, - { - name: "Name", - isActive: false, - }, - ], - }, - ] - pg.queryMock.mockImplementation(() => ({ - rows, - })) - - const responseBody = await config.api.query.previewQuery(queryPreview) - expect(responseBody).toEqual({ - nestedSchemaFields: { - contacts: { - address: { - type: "string", - name: "address", - }, - postcode: { - type: "string", - name: "postcode", - }, - lat: { - type: "number", - name: "lat", - }, - long: { - type: "number", - name: "long", - }, - city: { - type: "string", - name: "city", - }, - phoneNumber: { - type: "string", - name: "phoneNumber", - }, - name: { - type: "string", - name: "name", - }, - isActive: { - type: "boolean", - name: "isActive", - }, - }, - }, - rows, - schema: { - contacts: { type: "json", name: "contacts", subtype: "array" }, - }, - }) - expect(responseBody.rows.length).toEqual(1) - delete datasource.config - }) }) describe("execute", () => { @@ -411,17 +283,7 @@ describe("/queries", () => { describe("variables", () => { async function preview(datasource: Datasource, fields: any) { - const queryPreview: QueryPreview = { - datasourceId: datasource._id!, - parameters: [], - fields, - queryVerb: "read", - name: datasource.name!, - transformer: "return data", - schema: {}, - readable: true, - } - return await config.api.query.previewQuery(queryPreview) + return config.previewQuery(request, config, datasource, fields, undefined) } it("should work with static variables", async () => { @@ -431,31 +293,31 @@ describe("/queries", () => { variable2: "1", }, }) - const responseBody = await preview(datasource, { + const res = await preview(datasource, { path: "www.{{ variable }}.com", queryString: "test={{ variable2 }}", }) // these responses come from the mock - expect(responseBody.schema).toEqual({ + expect(res.body.schema).toEqual({ opts: { type: "json", name: "opts" }, url: { type: "string", name: "url" }, value: { type: "string", name: "value" }, }) - expect(responseBody.rows[0].url).toEqual("http://www.google.com?test=1") + expect(res.body.rows[0].url).toEqual("http://www.google.com?test=1") }) it("should work with dynamic variables", async () => { const { datasource } = await config.dynamicVariableDatasource() - const responseBody = await preview(datasource, { + const res = await preview(datasource, { path: "www.google.com", queryString: "test={{ variable3 }}", }) - expect(responseBody.schema).toEqual({ + expect(res.body.schema).toEqual({ opts: { type: "json", name: "opts" }, url: { type: "string", name: "url" }, value: { type: "string", name: "value" }, }) - expect(responseBody.rows[0].url).toContain("doctype%20html") + expect(res.body.rows[0].url).toContain("doctype%20html") }) it("check that it automatically retries on fail with cached dynamics", async () => { @@ -469,16 +331,16 @@ describe("/queries", () => { // check its in cache const contents = await checkCacheForDynamicVariable(base._id, "variable3") expect(contents.rows.length).toEqual(1) - const responseBody = await preview(datasource, { + const res = await preview(datasource, { path: "www.failonce.com", queryString: "test={{ variable3 }}", }) - expect(responseBody.schema).toEqual({ + expect(res.body.schema).toEqual({ fails: { type: "number", name: "fails" }, opts: { type: "json", name: "opts" }, url: { type: "string", name: "url" }, }) - expect(responseBody.rows[0].fails).toEqual(1) + expect(res.body.rows[0].fails).toEqual(1) }) it("deletes variables when linked query is deleted", async () => { @@ -509,37 +371,24 @@ describe("/queries", () => { async function previewGet( datasource: Datasource, fields: any, - params: QueryParameter[] + params: any ) { - const queryPreview: QueryPreview = { - datasourceId: datasource._id!, - parameters: params, - fields, - queryVerb: "read", - name: datasource.name!, - transformer: "return data", - schema: {}, - readable: true, - } - return await config.api.query.previewQuery(queryPreview) + return config.previewQuery(request, config, datasource, fields, params) } async function previewPost( datasource: Datasource, fields: any, - params: QueryParameter[] + params: any ) { - const queryPreview: QueryPreview = { - datasourceId: datasource._id!, - parameters: params, + return config.previewQuery( + request, + config, + datasource, fields, - queryVerb: "create", - name: datasource.name!, - transformer: null, - schema: {}, - readable: false, - } - return await config.api.query.previewQuery(queryPreview) + params, + "create" + ) } it("should parse global and query level header mappings", async () => { @@ -551,7 +400,7 @@ describe("/queries", () => { emailHdr: "{{[user].[email]}}", }, }) - const responseBody = await previewGet( + const res = await previewGet( datasource, { path: "www.google.com", @@ -561,17 +410,17 @@ describe("/queries", () => { secondHdr: "1234", }, }, - [] + undefined ) - const parsedRequest = JSON.parse(responseBody.extra.raw) + const parsedRequest = JSON.parse(res.body.extra.raw) expect(parsedRequest.opts.headers).toEqual({ test: "headerVal", emailHdr: userDetails.email, queryHdr: userDetails.firstName, secondHdr: "1234", }) - expect(responseBody.rows[0].url).toEqual( + expect(res.body.rows[0].url).toEqual( "http://www.google.com?email=" + userDetails.email.replace("@", "%40") ) }) @@ -581,21 +430,21 @@ describe("/queries", () => { const datasource = await config.restDatasource() - const responseBody = await previewGet( + const res = await previewGet( datasource, { path: "www.google.com", queryString: "test={{myEmail}}&testName={{myName}}&testParam={{testParam}}", }, - [ - { name: "myEmail", default: "{{[user].[email]}}" }, - { name: "myName", default: "{{[user].[firstName]}}" }, - { name: "testParam", default: "1234" }, - ] + { + myEmail: "{{[user].[email]}}", + myName: "{{[user].[firstName]}}", + testParam: "1234", + } ) - expect(responseBody.rows[0].url).toEqual( + expect(res.body.rows[0].url).toEqual( "http://www.google.com?test=" + userDetails.email.replace("@", "%40") + "&testName=" + @@ -608,7 +457,7 @@ describe("/queries", () => { const userDetails = config.getUserDetails() const datasource = await config.restDatasource() - const responseBody = await previewPost( + const res = await previewPost( datasource, { path: "www.google.com", @@ -617,14 +466,16 @@ describe("/queries", () => { "This is plain text and this is my email: {{[user].[email]}}. This is a test param: {{testParam}}", bodyType: "text", }, - [{ name: "testParam", default: "1234" }] + { + testParam: "1234", + } ) - const parsedRequest = JSON.parse(responseBody.extra.raw) + const parsedRequest = JSON.parse(res.body.extra.raw) expect(parsedRequest.opts.body).toEqual( `This is plain text and this is my email: ${userDetails.email}. This is a test param: 1234` ) - expect(responseBody.rows[0].url).toEqual( + expect(res.body.rows[0].url).toEqual( "http://www.google.com?testParam=1234" ) }) @@ -633,7 +484,7 @@ describe("/queries", () => { const userDetails = config.getUserDetails() const datasource = await config.restDatasource() - const responseBody = await previewPost( + const res = await previewPost( datasource, { path: "www.google.com", @@ -642,16 +493,16 @@ describe("/queries", () => { '{"email":"{{[user].[email]}}","queryCode":{{testParam}},"userRef":"{{userRef}}"}', bodyType: "json", }, - [ - { name: "testParam", default: "1234" }, - { name: "userRef", default: "{{[user].[firstName]}}" }, - ] + { + testParam: "1234", + userRef: "{{[user].[firstName]}}", + } ) - const parsedRequest = JSON.parse(responseBody.extra.raw) + const parsedRequest = JSON.parse(res.body.extra.raw) const test = `{"email":"${userDetails.email}","queryCode":1234,"userRef":"${userDetails.firstName}"}` expect(parsedRequest.opts.body).toEqual(test) - expect(responseBody.rows[0].url).toEqual( + expect(res.body.rows[0].url).toEqual( "http://www.google.com?testParam=1234" ) }) @@ -660,7 +511,7 @@ describe("/queries", () => { const userDetails = config.getUserDetails() const datasource = await config.restDatasource() - const responseBody = await previewPost( + const res = await previewPost( datasource, { path: "www.google.com", @@ -670,17 +521,17 @@ describe("/queries", () => { "{{userId}} testing ", bodyType: "xml", }, - [ - { name: "testParam", default: "1234" }, - { name: "userId", default: "{{[user].[firstName]}}" }, - ] + { + testParam: "1234", + userId: "{{[user].[firstName]}}", + } ) - const parsedRequest = JSON.parse(responseBody.extra.raw) + const parsedRequest = JSON.parse(res.body.extra.raw) const test = ` ${userDetails.email} 1234 ${userDetails.firstName} testing ` expect(parsedRequest.opts.body).toEqual(test) - expect(responseBody.rows[0].url).toEqual( + expect(res.body.rows[0].url).toEqual( "http://www.google.com?testParam=1234" ) }) @@ -689,7 +540,7 @@ describe("/queries", () => { const userDetails = config.getUserDetails() const datasource = await config.restDatasource() - const responseBody = await previewPost( + const res = await previewPost( datasource, { path: "www.google.com", @@ -698,13 +549,13 @@ describe("/queries", () => { '{"email":"{{[user].[email]}}","queryCode":{{testParam}},"userRef":"{{userRef}}"}', bodyType: "form", }, - [ - { name: "testParam", default: "1234" }, - { name: "userRef", default: "{{[user].[firstName]}}" }, - ] + { + testParam: "1234", + userRef: "{{[user].[firstName]}}", + } ) - const parsedRequest = JSON.parse(responseBody.extra.raw) + const parsedRequest = JSON.parse(res.body.extra.raw) const emailData = parsedRequest.opts.body._streams[1] expect(emailData).toEqual(userDetails.email) @@ -715,7 +566,7 @@ describe("/queries", () => { const userRef = parsedRequest.opts.body._streams[7] expect(userRef).toEqual(userDetails.firstName) - expect(responseBody.rows[0].url).toEqual( + expect(res.body.rows[0].url).toEqual( "http://www.google.com?testParam=1234" ) }) @@ -724,7 +575,7 @@ describe("/queries", () => { const userDetails = config.getUserDetails() const datasource = await config.restDatasource() - const responseBody = await previewPost( + const res = await previewPost( datasource, { path: "www.google.com", @@ -733,12 +584,12 @@ describe("/queries", () => { '{"email":"{{[user].[email]}}","queryCode":{{testParam}},"userRef":"{{userRef}}"}', bodyType: "encoded", }, - [ - { name: "testParam", default: "1234" }, - { name: "userRef", default: "{{[user].[firstName]}}" }, - ] + { + testParam: "1234", + userRef: "{{[user].[firstName]}}", + } ) - const parsedRequest = JSON.parse(responseBody.extra.raw) + const parsedRequest = JSON.parse(res.body.extra.raw) expect(parsedRequest.opts.body.email).toEqual(userDetails.email) expect(parsedRequest.opts.body.queryCode).toEqual("1234") diff --git a/packages/server/src/tests/utilities/TestConfiguration.ts b/packages/server/src/tests/utilities/TestConfiguration.ts index 22bb66b130..8e6ecdfeb1 100644 --- a/packages/server/src/tests/utilities/TestConfiguration.ts +++ b/packages/server/src/tests/utilities/TestConfiguration.ts @@ -866,6 +866,28 @@ export default class TestConfiguration { // QUERY + async previewQuery( + request: any, + config: any, + datasource: any, + fields: any, + params: any, + verb?: string + ) { + return request + .post(`/api/queries/preview`) + .send({ + datasourceId: datasource._id, + parameters: params || {}, + fields, + queryVerb: verb || "read", + name: datasource.name, + }) + .set(config.defaultHeaders()) + .expect("Content-Type", /json/) + .expect(200) + } + async createQuery(config?: any) { if (!this.datasource && !config) { throw "No datasource created for query." diff --git a/packages/server/src/tests/utilities/api/query.ts b/packages/server/src/tests/utilities/api/query.ts index b0eac5c8b7..350fe03c74 100644 --- a/packages/server/src/tests/utilities/api/query.ts +++ b/packages/server/src/tests/utilities/api/query.ts @@ -1,7 +1,6 @@ import TestConfiguration from "../TestConfiguration" import { Query, - QueryPreview, type ExecuteQueryRequest, type ExecuteQueryResponse, } from "@budibase/types" @@ -42,19 +41,4 @@ export class QueryAPI extends TestAPI { return res.body } - - previewQuery = async (queryPreview: QueryPreview) => { - const res = await this.request - .post(`/api/queries/preview`) - .send(queryPreview) - .set(this.config.defaultHeaders()) - .expect("Content-Type", /json/) - .expect(200) - - if (res.status !== 200) { - throw new Error(JSON.stringify(res.body)) - } - - return res.body - } } diff --git a/packages/server/src/tests/utilities/structures.ts b/packages/server/src/tests/utilities/structures.ts index 2fecf15fd6..fe82311810 100644 --- a/packages/server/src/tests/utilities/structures.ts +++ b/packages/server/src/tests/utilities/structures.ts @@ -366,7 +366,7 @@ export function basicDatasource(): { datasource: Datasource } { export function basicQuery(datasourceId: string): Query { return { - datasourceId, + datasourceId: datasourceId, name: "New Query", parameters: [], fields: {}, diff --git a/packages/server/src/threads/definitions.ts b/packages/server/src/threads/definitions.ts index 14b97c57b1..52f5576d9d 100644 --- a/packages/server/src/threads/definitions.ts +++ b/packages/server/src/threads/definitions.ts @@ -7,10 +7,10 @@ export interface QueryEvent { datasource: Datasource queryVerb: string fields: { [key: string]: any } - parameters: { [key: string]: unknown } + parameters: { [key: string]: any } pagination?: any transformer: any - queryId?: string + queryId: string environmentVariables?: Record ctx?: any schema?: Record diff --git a/packages/server/src/threads/query.ts b/packages/server/src/threads/query.ts index 6cdccc7868..9366f2b12c 100644 --- a/packages/server/src/threads/query.ts +++ b/packages/server/src/threads/query.ts @@ -43,7 +43,7 @@ class QueryRunner { this.parameters = input.parameters this.pagination = input.pagination this.transformer = input.transformer - this.queryId = input.queryId! + this.queryId = input.queryId this.schema = input.schema this.noRecursiveQuery = flags.noRecursiveQuery this.cachedVariables = [] diff --git a/packages/types/src/documents/app/query.ts b/packages/types/src/documents/app/query.ts index b1b0a1d780..f4547b9774 100644 --- a/packages/types/src/documents/app/query.ts +++ b/packages/types/src/documents/app/query.ts @@ -19,7 +19,7 @@ export interface Query extends Document { } export interface QueryPreview extends Omit { - queryId?: string + queryId: string } export interface QueryParameter { From 6aa577106699f8425033c1f5d8703833c20bcbd0 Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Wed, 21 Feb 2024 18:35:27 -0300 Subject: [PATCH 14/34] acct-portal-sub --- packages/account-portal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/account-portal b/packages/account-portal index 4384bc742c..92129b0d62 160000 --- a/packages/account-portal +++ b/packages/account-portal @@ -1 +1 @@ -Subproject commit 4384bc742ca22fb1e9bf91843e65ae929daf17e2 +Subproject commit 92129b0d6251a7b4dabb367c15b09c079399b763 From fcd2ed14e3f77d1270c823c0d6f16384fd3771c5 Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Wed, 21 Feb 2024 18:46:40 -0300 Subject: [PATCH 15/34] revert acct portal submodule --- packages/account-portal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/account-portal b/packages/account-portal index 92129b0d62..97329c0318 160000 --- a/packages/account-portal +++ b/packages/account-portal @@ -1 +1 @@ -Subproject commit 92129b0d6251a7b4dabb367c15b09c079399b763 +Subproject commit 97329c0318ef0f4bbbd2b9ce30d6976bc6505272 From 1096b8c7f0b32f2f0845e57a95c06adca7d0caac Mon Sep 17 00:00:00 2001 From: Budibase Staging Release Bot <> Date: Wed, 21 Feb 2024 22:14:42 +0000 Subject: [PATCH 16/34] Bump version to 2.20.8 --- lerna.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lerna.json b/lerna.json index 67dd274074..c3df3cef5c 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "2.20.7", + "version": "2.20.8", "npmClient": "yarn", "packages": [ "packages/*", From 68719f1dda7126f391876b2a4f695e173310b565 Mon Sep 17 00:00:00 2001 From: Conor Webb Date: Thu, 22 Feb 2024 11:00:02 +0000 Subject: [PATCH 17/34] Remove Airtable from data source ui --- packages/server/src/api/controllers/integration.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/packages/server/src/api/controllers/integration.ts b/packages/server/src/api/controllers/integration.ts index 23defac831..5ab29f0329 100644 --- a/packages/server/src/api/controllers/integration.ts +++ b/packages/server/src/api/controllers/integration.ts @@ -2,8 +2,16 @@ import { getDefinition, getDefinitions } from "../../integrations" import { BBContext } from "@budibase/types" export async function fetch(ctx: BBContext) { - ctx.status = 200 - ctx.body = await getDefinitions() + try { + const definitions = await getDefinitions() + delete definitions.AIRTABLE + ctx.status = 200 + ctx.body = definitions + } catch (error) { + console.error('Error fetching definitions:', error) + ctx.status = 500 + ctx.body = { error: 'Internal server error' } + } } export async function find(ctx: BBContext) { From b5d44dfcfd66f1a230b9b5473971fd33175cae82 Mon Sep 17 00:00:00 2001 From: Conor Webb Date: Thu, 22 Feb 2024 11:16:38 +0000 Subject: [PATCH 18/34] Fixed linting issue --- packages/server/src/api/controllers/integration.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/server/src/api/controllers/integration.ts b/packages/server/src/api/controllers/integration.ts index 5ab29f0329..935148ff2f 100644 --- a/packages/server/src/api/controllers/integration.ts +++ b/packages/server/src/api/controllers/integration.ts @@ -8,9 +8,9 @@ export async function fetch(ctx: BBContext) { ctx.status = 200 ctx.body = definitions } catch (error) { - console.error('Error fetching definitions:', error) + console.error("Error fetching definitions:", error) ctx.status = 500 - ctx.body = { error: 'Internal server error' } + ctx.body = { error: "Internal server error" } } } From fb1bfbdae8b9593bbb0db30bb01577e23aa4bd85 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Thu, 22 Feb 2024 13:55:26 +0100 Subject: [PATCH 19/34] Clean code --- packages/backend-core/src/middleware/joi-validator.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/backend-core/src/middleware/joi-validator.ts b/packages/backend-core/src/middleware/joi-validator.ts index fcc8316886..ac8064a512 100644 --- a/packages/backend-core/src/middleware/joi-validator.ts +++ b/packages/backend-core/src/middleware/joi-validator.ts @@ -1,12 +1,12 @@ -import Joi, { ObjectSchema } from "joi" -import { BBContext } from "@budibase/types" +import Joi from "joi" +import { Ctx } from "@budibase/types" function validate( schema: Joi.ObjectSchema | Joi.ArraySchema, property: string ) { // Return a Koa middleware function - return (ctx: BBContext, next: any) => { + return (ctx: Ctx, next: any) => { if (!schema) { return next() } @@ -30,7 +30,6 @@ function validate( const { error } = schema.validate(params) if (error) { ctx.throw(400, `Invalid ${property} - ${error.message}`) - return } return next() } From 8e77365c1952a353328e87be13f6d63ac66f9c6c Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Thu, 22 Feb 2024 13:55:38 +0100 Subject: [PATCH 20/34] Allow mocking multiple features --- packages/backend-core/tests/core/utilities/mocks/licenses.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend-core/tests/core/utilities/mocks/licenses.ts b/packages/backend-core/tests/core/utilities/mocks/licenses.ts index 758fd6bf9a..1cbc282575 100644 --- a/packages/backend-core/tests/core/utilities/mocks/licenses.ts +++ b/packages/backend-core/tests/core/utilities/mocks/licenses.ts @@ -58,7 +58,7 @@ export const useCloudFree = () => { // FEATURES const useFeature = (feature: Feature) => { - const license = cloneDeep(UNLIMITED_LICENSE) + const license = cloneDeep(getCachedLicense() || UNLIMITED_LICENSE) const opts: UseLicenseOpts = { features: [feature], } From ea615f420ce5f2dc466b0a3a512eb25b4ea7071f Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Thu, 22 Feb 2024 13:55:59 +0100 Subject: [PATCH 21/34] Add extra test --- .../src/api/routes/global/tests/scim.spec.ts | 20 +++++++++++++++++++ packages/worker/src/tests/api/groups.ts | 8 ++++++++ 2 files changed, 28 insertions(+) diff --git a/packages/worker/src/api/routes/global/tests/scim.spec.ts b/packages/worker/src/api/routes/global/tests/scim.spec.ts index 56b7ca9f40..ae0abadc5d 100644 --- a/packages/worker/src/api/routes/global/tests/scim.spec.ts +++ b/packages/worker/src/api/routes/global/tests/scim.spec.ts @@ -654,6 +654,26 @@ describe("scim", () => { totalResults: groupCount, }) }) + + it("can fetch groups even if internal groups exist", async () => { + mocks.licenses.useGroups() + await config.api.groups.saveGroup(structures.userGroups.userGroup()) + await config.api.groups.saveGroup(structures.userGroups.userGroup()) + + const response = await getScimGroups() + + expect(response).toEqual({ + Resources: expect.arrayContaining(groups), + itemsPerPage: 25, + schemas: ["urn:ietf:params:scim:api:messages:2.0:ListResponse"], + startIndex: 1, + totalResults: groupCount, + }) + + expect((await config.api.groups.fetch()).body.data).toHaveLength( + 25 + 2 // scim groups + internal groups + ) + }) }) }) diff --git a/packages/worker/src/tests/api/groups.ts b/packages/worker/src/tests/api/groups.ts index 91f7c92c7d..0b9081cc92 100644 --- a/packages/worker/src/tests/api/groups.ts +++ b/packages/worker/src/tests/api/groups.ts @@ -53,4 +53,12 @@ export class GroupsAPI extends TestAPI { .expect("Content-Type", /json/) .expect(200) } + + fetch = ({ expect } = { expect: 200 }) => { + return this.request + .get(`/api/global/groups`) + .set(this.config.defaultHeaders()) + .expect("Content-Type", /json/) + .expect(expect) + } } From 302c6d9e0b0438b610a13f54ad979c3f2571d3fe Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Thu, 22 Feb 2024 14:07:46 +0100 Subject: [PATCH 22/34] Update ref --- packages/pro | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/pro b/packages/pro index 60e47a8249..bd296586af 160000 --- a/packages/pro +++ b/packages/pro @@ -1 +1 @@ -Subproject commit 60e47a8249fd6291a6bc20fe3fe6776b11938fa1 +Subproject commit bd296586af0dc17081c613e73539bb34820ddf27 From de0414afbe6ff19dc27bc1bb0d5182ec51060d3e Mon Sep 17 00:00:00 2001 From: Gerard Burns Date: Thu, 22 Feb 2024 13:19:29 +0000 Subject: [PATCH 23/34] Update Svelte Major Version to 4 (#13036) * update svelte versions * temporarilly disable svelte warnings * change bbap version pointed to * remove sub module specifying svelte version * point at updated account-portal * add type declaration for svelte in the server * add svelte transformer for server jest tests * remove svelte from bbui package.json * get builder tests working * linting fixes * prettier fixes * fix server test * fix another server test * fix server serverlog test * account-portal-version * pin master version of pro * revert hbs doc file in server to use triple braces, ensures nothing gets escaped --- package.json | 2 +- packages/account-portal | 2 +- packages/bbui/package.json | 3 +- .../bbui/src/ActionButton/ActionButton.svelte | 1 + .../bbui/src/ActionMenu/ActionMenu.svelte | 2 + packages/bbui/src/Badge/Badge.svelte | 2 + .../bbui/src/ColorPicker/ColorPicker.svelte | 4 + .../src/DetailSummary/DetailSummary.svelte | 2 + packages/bbui/src/FancyForm/FancyField.svelte | 2 + packages/bbui/src/Form/Core/Combobox.svelte | 1 + packages/bbui/src/Form/Core/DatePicker.svelte | 3 + packages/bbui/src/Form/Core/Dropzone.svelte | 3 + .../bbui/src/Form/Core/EnvDropdown.svelte | 2 + packages/bbui/src/Form/Core/File.svelte | 2 + .../bbui/src/Form/Core/InputDropdown.svelte | 1 + packages/bbui/src/Form/Core/Picker.svelte | 1 + .../bbui/src/Form/Core/PickerDropdown.svelte | 1 + packages/bbui/src/Icon/Icon.svelte | 2 + .../bbui/src/IconPicker/IconPicker.svelte | 2 + .../src/IconSideNav/IconSideNavItem.svelte | 2 + packages/bbui/src/Input/CopyInput.svelte | 2 + packages/bbui/src/List/ListItem.svelte | 2 + packages/bbui/src/Menu/Item.svelte | 1 + packages/bbui/src/Modal/CustomContent.svelte | 2 + packages/bbui/src/Modal/Modal.svelte | 1 + .../OptionSelectDnD/OptionSelectDnD.svelte | 2 + .../bbui/src/Pagination/Pagination.svelte | 2 + packages/bbui/src/Popover/Popover.svelte | 1 + .../src/ProgressCircle/ProgressCircle.svelte | 2 + packages/bbui/src/SideNavigation/Item.svelte | 2 + .../bbui/src/StatusLight/StatusLight.svelte | 2 + .../bbui/src/Table/InternalRenderer.svelte | 2 + packages/bbui/src/Table/Table.svelte | 2 + packages/bbui/src/Tabs/Tab.svelte | 3 + packages/bbui/src/Tooltip/AbsTooltip.svelte | 1 + .../bbui/src/Tooltip/TooltipWrapper.svelte | 1 + packages/builder/package.json | 5 +- .../FlowChart/ActionModal.svelte | 2 + .../FlowChart/FlowChart.svelte | 2 + .../FlowChart/FlowItem.svelte | 2 + .../FlowChart/FlowItemHeader.svelte | 2 + .../CreateAutomationModal.svelte | 2 + .../automation/SetupPanel/SchemaSetup.svelte | 2 + .../DataTable/modals/CreateEditColumn.svelte | 2 + .../DataTable/modals/ExportModal.test.js | 1 + .../backend/TableNavigator/ListItem.svelte | 2 + .../commandPalette/CommandPalette.svelte | 2 + .../src/components/common/DashCard.svelte | 2 + .../common/Dropdowns/DropdownContainer.svelte | 2 + .../common/Dropdowns/DropdownItem.svelte | 1 + .../src/components/common/EditableIcon.svelte | 2 + .../src/components/common/Icons/Pencil.svelte | 2 + .../src/components/common/NavHeader.svelte | 1 + .../src/components/common/NavItem.svelte | 2 + .../common/bindings/BindingPicker.svelte | 3 + .../bindings/DrawerBindableCombobox.svelte | 2 + .../bindings/DrawerBindableInput.svelte | 2 + .../common/bindings/DrawerBindableSlot.svelte | 2 + .../common/bindings/ModalBindableInput.svelte | 2 + .../src/components/deploy/AppActions.svelte | 2 + .../src/components/design/Panel.svelte | 2 + .../ButtonActionDrawer.svelte | 3 + .../ButtonConfiguration.svelte | 2 + .../controls/ColumnEditor/ColumnDrawer.svelte | 1 + .../DataSourceCategory.svelte | 1 + .../DraggableList/DraggableList.svelte | 3 + .../FlatButtonGroup/FlatButton.svelte | 2 + .../controls/IconSelect/IconSelect.svelte | 3 + .../components/portal/page/SideNavItem.svelte | 2 + .../settings/UserGroupPicker.svelte | 2 + .../src/components/start/AppRow.svelte | 2 + .../components/start/ChooseIconModal.svelte | 2 + .../_components/BuilderSidePanel.svelte | 2 + .../_components/PreviewOverlay.svelte | 2 + .../data/_components/DatasourceOption.svelte | 2 + .../EditDatasourceConfigButton.svelte | 2 + .../data/datasource/bb_internal/index.svelte | 2 + .../index.svelte | 2 + .../Component/ConditionalUIDrawer.svelte | 1 + .../_components/Navigation/LinksDrawer.svelte | 1 + .../_components/Screen/AppThemeSelect.svelte | 2 + .../new/_components/NewComponentPanel.svelte | 2 + .../[screenId]/_components/AppPreview.svelte | 2 + .../ComponentScrollWrapper.svelte | 1 + .../ComponentList/ComponentTree.svelte | 2 + .../_components/ComponentList/index.svelte | 2 + .../ScreenList/RoleIndicator.svelte | 1 + .../NewScreen/DatasourceTemplateRow.svelte | 2 + .../design/_components/NewScreen/index.svelte | 2 + .../src/pages/builder/auth/forgot.svelte | 2 + .../builder/portal/_components/Logo.svelte | 2 + .../portal/_components/MobileMenu.svelte | 4 + .../portal/account/auditLogs/index.svelte | 2 + .../plugins/_components/PluginRow.svelte | 2 + .../portal/settings/auth/google.svelte | 2 + .../builder/portal/settings/auth/index.svelte | 2 + .../builder/portal/settings/auth/scim.svelte | 2 + .../portal/settings/diagnostics.svelte | 2 + .../email/_components/TemplateBindings.svelte | 2 + .../_components/OnboardingTypeModal.svelte | 2 + .../users/_components/PasswordModal.svelte | 2 + .../_components/RoleTableRenderer.svelte | 2 + .../builder/src/stores/builder/datasources.js | 3 +- .../builder/src/stores/builder/queries.js | 3 +- packages/builder/src/stores/builder/views.js | 2 +- .../builder/src/stores/builder/viewsV2.js | 2 +- packages/client/package.json | 25 +- .../src/components/app/Container.svelte | 2 + .../client/src/components/app/Icon.svelte | 2 + .../client/src/components/app/Layout.svelte | 2 + .../src/components/app/SpectrumCard.svelte | 4 + .../components/devtools/DevToolsStat.svelte | 2 + .../MissingRequiredAncestor.svelte | 2 + .../MissingRequiredSetting.svelte | 2 + .../components/preview/SettingsButton.svelte | 2 + packages/frontend-core/package.json | 3 +- .../grid/cells/AttachmentCell.svelte | 2 + .../src/components/grid/cells/GridCell.svelte | 2 + .../components/grid/cells/GutterCell.svelte | 2 + .../components/grid/cells/HeaderCell.svelte | 2 + .../components/grid/cells/LongFormCell.svelte | 2 + .../components/grid/cells/OptionsCell.svelte | 2 + .../grid/cells/RelationshipCell.svelte | 2 + .../grid/layout/ButtonColumn.svelte | 1 + .../src/components/grid/layout/Grid.svelte | 1 + .../components/grid/layout/GridBody.svelte | 2 + .../src/components/grid/layout/GridRow.svelte | 2 + .../grid/layout/GridScrollWrapper.svelte | 2 + .../grid/layout/NewColumnButton.svelte | 2 + .../src/components/grid/layout/NewRow.svelte | 2 + .../grid/layout/StickyColumn.svelte | 2 + .../grid/overlays/ResizeOverlay.svelte | 1 + .../grid/overlays/ScrollOverlay.svelte | 2 + packages/server/jest.config.ts | 14 + packages/server/package.json | 1 - packages/server/scripts/svelteTransformer.js | 11 + .../src/api/controllers/static/index.ts | 9 +- .../static/templates/BudibaseApp.d.svelte.ts | 11 + .../api/controllers/static/templates/app.hbs | 3 +- .../server/src/automations/tests/bash.spec.js | 18 +- .../automations/tests/executeQuery.spec.js | 28 +- .../src/automations/tests/serverLog.spec.js | 1 + packages/server/tsconfig.build.json | 3 +- scripts/build.js | 81 ++- yarn.lock | 506 ++++++++++++++---- 145 files changed, 777 insertions(+), 196 deletions(-) create mode 100644 packages/server/scripts/svelteTransformer.js create mode 100644 packages/server/src/api/controllers/static/templates/BudibaseApp.d.svelte.ts diff --git a/package.json b/package.json index 4407fd33f3..0a20f01d52 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "nx-cloud": "16.0.5", "prettier": "2.8.8", "prettier-plugin-svelte": "^2.3.0", - "svelte": "3.49.0", + "svelte": "^4.2.10", "svelte-eslint-parser": "^0.33.1", "typescript": "5.2.2", "yargs": "^17.7.2" diff --git a/packages/account-portal b/packages/account-portal index 97329c0318..a851eeacab 160000 --- a/packages/account-portal +++ b/packages/account-portal @@ -1 +1 @@ -Subproject commit 97329c0318ef0f4bbbd2b9ce30d6976bc6505272 +Subproject commit a851eeacabfaad8bff6e781f5e5a62063cbc31f3 diff --git a/packages/bbui/package.json b/packages/bbui/package.json index 78eed2b608..a1baa2a38b 100644 --- a/packages/bbui/package.json +++ b/packages/bbui/package.json @@ -24,8 +24,7 @@ "rollup": "^2.45.2", "rollup-plugin-postcss": "^4.0.0", "rollup-plugin-svelte": "^7.1.0", - "rollup-plugin-terser": "^7.0.2", - "svelte": "3.49.0" + "rollup-plugin-terser": "^7.0.2" }, "keywords": [ "svelte" diff --git a/packages/bbui/src/ActionButton/ActionButton.svelte b/packages/bbui/src/ActionButton/ActionButton.svelte index 0e6ec3d155..c346e34d54 100644 --- a/packages/bbui/src/ActionButton/ActionButton.svelte +++ b/packages/bbui/src/ActionButton/ActionButton.svelte @@ -41,6 +41,7 @@ } + (showTooltip = true)} diff --git a/packages/bbui/src/ActionMenu/ActionMenu.svelte b/packages/bbui/src/ActionMenu/ActionMenu.svelte index 08425e8f59..642ec4932a 100644 --- a/packages/bbui/src/ActionMenu/ActionMenu.svelte +++ b/packages/bbui/src/ActionMenu/ActionMenu.svelte @@ -33,6 +33,8 @@ setContext("actionMenu", { show, hide }) + +
diff --git a/packages/bbui/src/Badge/Badge.svelte b/packages/bbui/src/Badge/Badge.svelte index 8b54045297..e4ec7d4f33 100644 --- a/packages/bbui/src/Badge/Badge.svelte +++ b/packages/bbui/src/Badge/Badge.svelte @@ -13,6 +13,8 @@ export let hoverable = false + + + +
+ +
diff --git a/packages/bbui/src/DetailSummary/DetailSummary.svelte b/packages/bbui/src/DetailSummary/DetailSummary.svelte index 2cbb6796f3..cbfdcbec9b 100644 --- a/packages/bbui/src/DetailSummary/DetailSummary.svelte +++ b/packages/bbui/src/DetailSummary/DetailSummary.svelte @@ -15,6 +15,8 @@ } + +
{#if name}
diff --git a/packages/bbui/src/FancyForm/FancyField.svelte b/packages/bbui/src/FancyForm/FancyField.svelte index 455f4b38fb..798f486187 100644 --- a/packages/bbui/src/FancyForm/FancyField.svelte +++ b/packages/bbui/src/FancyForm/FancyField.svelte @@ -36,6 +36,8 @@ }) + +
+
+ +
{/key} {#if open} +
{/if} diff --git a/packages/bbui/src/Form/Core/Dropzone.svelte b/packages/bbui/src/Form/Core/Dropzone.svelte index fa0be630ba..2bd95df516 100644 --- a/packages/bbui/src/Form/Core/Dropzone.svelte +++ b/packages/bbui/src/Form/Core/Dropzone.svelte @@ -137,6 +137,9 @@ } + + +
{#if selectedImage} {#if gallery} diff --git a/packages/bbui/src/Form/Core/EnvDropdown.svelte b/packages/bbui/src/Form/Core/EnvDropdown.svelte index c690ffbc6b..ed5878d6b2 100644 --- a/packages/bbui/src/Form/Core/EnvDropdown.svelte +++ b/packages/bbui/src/Form/Core/EnvDropdown.svelte @@ -96,6 +96,8 @@ } + +
+ +
{#if value}
diff --git a/packages/bbui/src/Form/Core/InputDropdown.svelte b/packages/bbui/src/Form/Core/InputDropdown.svelte index 128353b7b9..c1bc2ac7e5 100644 --- a/packages/bbui/src/Form/Core/InputDropdown.svelte +++ b/packages/bbui/src/Form/Core/InputDropdown.svelte @@ -110,6 +110,7 @@ } +
+ +
+ +
(showTooltip = true)} diff --git a/packages/bbui/src/IconPicker/IconPicker.svelte b/packages/bbui/src/IconPicker/IconPicker.svelte index b3cc72daa3..3cd7a16eb0 100644 --- a/packages/bbui/src/IconPicker/IconPicker.svelte +++ b/packages/bbui/src/IconPicker/IconPicker.svelte @@ -58,6 +58,8 @@ } + +
(open = true)}>
+ +
+ +
copyToClipboard(value)}> diff --git a/packages/bbui/src/List/ListItem.svelte b/packages/bbui/src/List/ListItem.svelte index 28015c4c57..76b242cf9c 100644 --- a/packages/bbui/src/List/ListItem.svelte +++ b/packages/bbui/src/List/ListItem.svelte @@ -15,6 +15,8 @@ $: initials = avatar ? title?.[0] : null + +
{#if icon} diff --git a/packages/bbui/src/Menu/Item.svelte b/packages/bbui/src/Menu/Item.svelte index ed759f5b10..05a33adda9 100644 --- a/packages/bbui/src/Menu/Item.svelte +++ b/packages/bbui/src/Menu/Item.svelte @@ -33,6 +33,7 @@ } +
  • + +
    Click me {remaining} diff --git a/packages/bbui/src/Modal/Modal.svelte b/packages/bbui/src/Modal/Modal.svelte index da97bf332e..f891d0584d 100644 --- a/packages/bbui/src/Modal/Modal.svelte +++ b/packages/bbui/src/Modal/Modal.svelte @@ -100,6 +100,7 @@ --> {#if visible} +
    + +
    + +