From 343a19250bebc2cce70a7267753de16f7279ead7 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Mon, 16 Jan 2023 15:35:41 +0000 Subject: [PATCH 01/56] Typing supertest parameters --- package.json | 1 + .../src/api/routes/public/tests/utils.ts | 7 ++++++ .../src/api/routes/tests/utilities/index.ts | 3 +-- .../src/tests/utilities/TestConfiguration.ts | 4 +-- yarn.lock | 25 +++++++++++++++++++ 5 files changed, 36 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 7da8db506d..a54f9e037b 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,7 @@ "private": true, "devDependencies": { "@rollup/plugin-json": "^4.0.2", + "@types/supertest": "^2.0.12", "@typescript-eslint/parser": "5.45.0", "babel-eslint": "^10.0.3", "eslint": "^7.28.0", diff --git a/packages/server/src/api/routes/public/tests/utils.ts b/packages/server/src/api/routes/public/tests/utils.ts index ad468332e6..35770aaab5 100644 --- a/packages/server/src/api/routes/public/tests/utils.ts +++ b/packages/server/src/api/routes/public/tests/utils.ts @@ -1,5 +1,12 @@ import { checkSlashesInUrl } from "../../../../utilities" +export type MakeRequestResponse = ( + method: string, + endpoint: string, + body?: any, + intAppId?: string +) => Promise + export function generateMakeRequest(apiKey: string, setup: any) { const request = setup.getRequest() const config = setup.getConfig() diff --git a/packages/server/src/api/routes/tests/utilities/index.ts b/packages/server/src/api/routes/tests/utilities/index.ts index 519e8a1459..2786d2ba12 100644 --- a/packages/server/src/api/routes/tests/utilities/index.ts +++ b/packages/server/src/api/routes/tests/utilities/index.ts @@ -44,7 +44,7 @@ export function delay(ms: number) { return new Promise(resolve => setTimeout(resolve, ms)) } -let request: any, config: any +let request: any, config: TestConfig export function beforeAll() { config = new TestConfig() @@ -58,7 +58,6 @@ export function afterAll() { // clear app files request = null - config = null } export function getRequest() { diff --git a/packages/server/src/tests/utilities/TestConfiguration.ts b/packages/server/src/tests/utilities/TestConfiguration.ts index 7ac20e14ef..fe463fea36 100644 --- a/packages/server/src/tests/utilities/TestConfiguration.ts +++ b/packages/server/src/tests/utilities/TestConfiguration.ts @@ -38,7 +38,7 @@ import { cleanup } from "../../utilities/fileSystem" import newid from "../../db/newid" import { generateUserMetadataID } from "../../db/utils" import { startup } from "../../startup" -const supertest = require("supertest") +import supertest from "supertest" const GLOBAL_USER_ID = "us_uuid1" const EMAIL = "babs@babs.com" @@ -48,7 +48,7 @@ const CSRF_TOKEN = "e3727778-7af0-4226-b5eb-f43cbe60a306" class TestConfiguration { server: any - request: any + request: supertest.SuperTest | undefined started: boolean appId: string | null allApps: any[] diff --git a/yarn.lock b/yarn.lock index 60cb598a60..f80cde1b6f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -986,6 +986,11 @@ estree-walker "^1.0.1" picomatch "^2.2.2" +"@types/cookiejar@*": + version "2.1.2" + resolved "https://registry.yarnpkg.com/@types/cookiejar/-/cookiejar-2.1.2.tgz#66ad9331f63fe8a3d3d9d8c6e3906dd10f6446e8" + integrity sha512-t73xJJrvdTjXrn4jLS9VSGRbz0nUY3cl2DMGDU48lKl+HR9dbbjW2A9r3g40VA++mQpy6uuHg33gy7du2BKpog== + "@types/estree@0.0.39": version "0.0.39" resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f" @@ -1001,6 +1006,11 @@ resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.2.tgz#ee771e2ba4b3dc5b372935d549fd9617bf345b8c" integrity sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ== +"@types/node@*": + version "18.11.18" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.11.18.tgz#8dfb97f0da23c2293e554c5a50d61ef134d7697f" + integrity sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA== + "@types/node@>= 8": version "18.0.0" resolved "https://registry.yarnpkg.com/@types/node/-/node-18.0.0.tgz#67c7b724e1bcdd7a8821ce0d5ee184d3b4dd525a" @@ -1011,6 +1021,21 @@ resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz#d3357479a0fdfdd5907fe67e17e0a85c906e1301" integrity sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw== +"@types/superagent@*": + version "4.1.16" + resolved "https://registry.yarnpkg.com/@types/superagent/-/superagent-4.1.16.tgz#12c9c16f232f9d89beab91d69368f96ce8e2d881" + integrity sha512-tLfnlJf6A5mB6ddqF159GqcDizfzbMUB1/DeT59/wBNqzRTNNKsaw79A/1TZ84X+f/EwWH8FeuSkjlCLyqS/zQ== + dependencies: + "@types/cookiejar" "*" + "@types/node" "*" + +"@types/supertest@^2.0.12": + version "2.0.12" + resolved "https://registry.yarnpkg.com/@types/supertest/-/supertest-2.0.12.tgz#ddb4a0568597c9aadff8dbec5b2e8fddbe8692fc" + integrity sha512-X3HPWTwXRerBZS7Mo1k6vMVR1Z6zmJcDVn5O/31whe0tnjE4te6ZJSJGq1RiqHPjzPdMTfjCFogDJmwng9xHaQ== + dependencies: + "@types/superagent" "*" + "@typescript-eslint/parser@5.45.0": version "5.45.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.45.0.tgz#b18a5f6b3cf1c2b3e399e9d2df4be40d6b0ddd0e" From eedc49de14a43ce3e02ac4c3867ec3495ce0fda5 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 17 Jan 2023 10:27:45 +0000 Subject: [PATCH 02/56] Type s3 responses --- packages/backend-core/src/objectStore/objectStore.ts | 10 +++++----- packages/server/__mocks__/aws-sdk.ts | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/backend-core/src/objectStore/objectStore.ts b/packages/backend-core/src/objectStore/objectStore.ts index f601d40a37..059e1b228d 100644 --- a/packages/backend-core/src/objectStore/objectStore.ts +++ b/packages/backend-core/src/objectStore/objectStore.ts @@ -361,8 +361,8 @@ export const deleteFolder = async ( Prefix: folder, } - let response: any = await client.listObjects(listParams).promise() - if (response.Contents.length === 0) { + const existingObjectsResponse = await client.listObjects(listParams).promise() + if (existingObjectsResponse.Contents?.length === 0) { return } const deleteParams: any = { @@ -372,13 +372,13 @@ export const deleteFolder = async ( }, } - response.Contents.forEach((content: any) => { + existingObjectsResponse.Contents?.forEach((content: any) => { deleteParams.Delete.Objects.push({ Key: content.Key }) }) - response = await client.deleteObjects(deleteParams).promise() + const deleteResponse = await client.deleteObjects(deleteParams).promise() // can only empty 1000 items at once - if (response.Deleted.length === 1000) { + if (deleteResponse.Deleted?.length === 1000) { return deleteFolder(bucketName, folder) } } diff --git a/packages/server/__mocks__/aws-sdk.ts b/packages/server/__mocks__/aws-sdk.ts index 2efbd303f2..8a66f0e213 100644 --- a/packages/server/__mocks__/aws-sdk.ts +++ b/packages/server/__mocks__/aws-sdk.ts @@ -34,7 +34,7 @@ module AwsMock { // @ts-ignore this.listObjects = jest.fn( response({ - Contents: {}, + Contents: [], }) ) From da8f7eff6ce4f7e94be4f0e12d01ba868bdc2bc7 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 17 Jan 2023 10:28:51 +0000 Subject: [PATCH 03/56] Delete apps on end --- .../src/tests/utilities/TestConfiguration.ts | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/packages/server/src/tests/utilities/TestConfiguration.ts b/packages/server/src/tests/utilities/TestConfiguration.ts index fe463fea36..5753538eb7 100644 --- a/packages/server/src/tests/utilities/TestConfiguration.ts +++ b/packages/server/src/tests/utilities/TestConfiguration.ts @@ -137,21 +137,28 @@ class TestConfiguration { return this.createApp(appName) } - end() { + async end() { if (!this) { return } - if (this.server) { - this.server.close() - } if (this.allApps) { cleanup(this.allApps.map(app => app.appId)) + + await this._req( + null, + { appId: this.prodApp.appId }, + controllers.app.destroy + ) + } + + if (this.server) { + this.server.close() } } // UTILS - async _req(body: any, params: any, controlFunc: any) { + _req(body: any, params: any, controlFunc: any) { // create a fake request ctx const request: any = {} const appId = this.appId From 6c61a829702f4cbb321830497e05b35c30190346 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 17 Jan 2023 14:29:24 +0000 Subject: [PATCH 04/56] Delete apps on end From 9fc878590058898336f6615836328457803b1854 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 17 Jan 2023 15:49:56 +0000 Subject: [PATCH 05/56] Remove moduleNameMapper in jest --- packages/backend-core/jest.config.ts | 10 ---------- packages/server/jest.config.ts | 17 ----------------- packages/worker/jest.config.ts | 19 ------------------- 3 files changed, 46 deletions(-) diff --git a/packages/backend-core/jest.config.ts b/packages/backend-core/jest.config.ts index e5993f6596..6240675ade 100644 --- a/packages/backend-core/jest.config.ts +++ b/packages/backend-core/jest.config.ts @@ -11,14 +11,4 @@ const config: Config.InitialOptions = { }, } -if (!process.env.CI) { - // use sources when not in CI - config.moduleNameMapper = { - "@budibase/types": "/../types/src", - "^axios.*$": "/node_modules/axios/lib/axios.js", - } -} else { - console.log("Running tests with compiled dependency sources") -} - export default config diff --git a/packages/server/jest.config.ts b/packages/server/jest.config.ts index e9337001a4..f5f7e5ff88 100644 --- a/packages/server/jest.config.ts +++ b/packages/server/jest.config.ts @@ -16,21 +16,4 @@ const config: Config.InitialOptions = { }, } -if (!process.env.CI) { - // use sources when not in CI - config.moduleNameMapper = { - "@budibase/backend-core/(.*)": "/../backend-core/$1", - "@budibase/backend-core": "/../backend-core/src", - "@budibase/types": "/../types/src", - "^axios.*$": "/node_modules/axios/lib/axios.js", - } - // add pro sources if they exist - if (fs.existsSync("../../../budibase-pro")) { - config.moduleNameMapper["@budibase/pro"] = - "/../../../budibase-pro/packages/pro/src" - } -} else { - console.log("Running tests with compiled dependency sources") -} - export default config diff --git a/packages/worker/jest.config.ts b/packages/worker/jest.config.ts index 21b7ee6763..f526ad7e47 100644 --- a/packages/worker/jest.config.ts +++ b/packages/worker/jest.config.ts @@ -12,23 +12,4 @@ const config: Config.InitialOptions = { }, } -if (!process.env.CI) { - // use sources when not in CI - config.moduleNameMapper = { - "@budibase/backend-core/(.*)": "/../backend-core/$1", - "@budibase/backend-core": "/../backend-core/src", - "@budibase/types": "/../types/src", - "^axios.*$": "/node_modules/axios/lib/axios.js", - } - // add pro sources if they exist - if (fs.existsSync("../../../budibase-pro")) { - config.moduleNameMapper["@budibase/pro/(.*)"] = - "/../../../budibase-pro/packages/pro/$1" - config.moduleNameMapper["@budibase/pro"] = - "/../../../budibase-pro/packages/pro/src" - } -} else { - console.log("Running tests with compiled dependency sources") -} - export default config From ffc322da88659f728d24c345b0654873787d02f8 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 17 Jan 2023 16:05:02 +0000 Subject: [PATCH 06/56] Treat new tests are "no tests" --- packages/backend-core/src/environment.ts | 11 +++++++++++ packages/server/src/environment.ts | 11 +++++++++++ packages/worker/src/environment.ts | 10 ++++++++++ 3 files changed, 32 insertions(+) diff --git a/packages/backend-core/src/environment.ts b/packages/backend-core/src/environment.ts index 91556ddcd6..77328eedaf 100644 --- a/packages/backend-core/src/environment.ts +++ b/packages/backend-core/src/environment.ts @@ -1,4 +1,15 @@ +function isDockerisedTest() { + return process.env.DOCKERISED_TEST === "true" +} + function isTest() { + if (isDockerisedTest()) { + // While we are migrating all the tests to use docker instead of mocked in memory, + // we want to keep treating the old tests as tests, + // but the new tests should not make a difference + return false + } + return isCypress() || isJest() } diff --git a/packages/server/src/environment.ts b/packages/server/src/environment.ts index 6272e0e462..d11661ce27 100644 --- a/packages/server/src/environment.ts +++ b/packages/server/src/environment.ts @@ -1,6 +1,17 @@ import { join } from "path" +function isDockerisedTest() { + return process.env.DOCKERISED_TEST === "true" +} + function isTest() { + if (isDockerisedTest()) { + // While we are migrating all the tests to use docker instead of mocked in memory, + // we want to keep treating the old tests as tests, + // but the new tests should not make a difference + return false + } + return isCypress() || isJest() } diff --git a/packages/worker/src/environment.ts b/packages/worker/src/environment.ts index 52fec210bc..aab30883ea 100644 --- a/packages/worker/src/environment.ts +++ b/packages/worker/src/environment.ts @@ -4,7 +4,17 @@ function isDev() { return process.env.NODE_ENV !== "production" } +function isDockerisedTest() { + return process.env.DOCKERISED_TEST === "true" +} + function isTest() { + if (isDockerisedTest()) { + // While we are migrating all the tests to use docker instead of mocked in memory, + // we want to keep treating the old tests as tests, + // but the new tests should not make a difference + return false + } return ( process.env.NODE_ENV === "jest" || process.env.NODE_ENV === "cypress" || From 70168a20fa1e7adbae17052931252a949cb28045 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 17 Jan 2023 16:07:26 +0000 Subject: [PATCH 07/56] Prevent killing tests when stopping the server --- packages/server/src/app.ts | 2 +- packages/server/src/environment.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/server/src/app.ts b/packages/server/src/app.ts index 177393dee7..50a1e187ba 100644 --- a/packages/server/src/app.ts +++ b/packages/server/src/app.ts @@ -74,7 +74,7 @@ server.on("close", async () => { await events.shutdown() await Thread.shutdown() api.shutdown() - if (!env.isTest()) { + if (!env.isTest() && !env.isDockerisedTest()) { process.exit(errCode) } }) diff --git a/packages/server/src/environment.ts b/packages/server/src/environment.ts index d11661ce27..99949feb41 100644 --- a/packages/server/src/environment.ts +++ b/packages/server/src/environment.ts @@ -115,6 +115,7 @@ const environment = { isInThread: () => { return inThread }, + isDockerisedTest, } // threading can cause memory issues with node-ts in development From 6412da730c9c26d04f937da5bd39a86efee1358d Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 17 Jan 2023 17:19:39 +0000 Subject: [PATCH 08/56] Type tests utils --- .../server/src/api/routes/public/tests/utils.ts | 16 ++++++++++------ .../src/api/routes/tests/utilities/index.ts | 4 +++- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/packages/server/src/api/routes/public/tests/utils.ts b/packages/server/src/api/routes/public/tests/utils.ts index 35770aaab5..8e7c6e187c 100644 --- a/packages/server/src/api/routes/public/tests/utils.ts +++ b/packages/server/src/api/routes/public/tests/utils.ts @@ -1,20 +1,24 @@ +import * as setup from "../../tests/utilities" import { checkSlashesInUrl } from "../../../../utilities" +import supertest from "supertest" + +export type HttpMethod = "post" | "get" export type MakeRequestResponse = ( - method: string, + method: HttpMethod, endpoint: string, body?: any, intAppId?: string -) => Promise +) => Promise -export function generateMakeRequest(apiKey: string, setup: any) { - const request = setup.getRequest() +export function generateMakeRequest(apiKey: string): MakeRequestResponse { + const request = setup.getRequest()! const config = setup.getConfig() return async ( - method: string, + method: HttpMethod, endpoint: string, body?: any, - intAppId: string = config.getAppId() + intAppId: string | null = config.getAppId() ) => { const extraHeaders: any = { "x-budibase-api-key": apiKey, diff --git a/packages/server/src/api/routes/tests/utilities/index.ts b/packages/server/src/api/routes/tests/utilities/index.ts index 2786d2ba12..0997474e5a 100644 --- a/packages/server/src/api/routes/tests/utilities/index.ts +++ b/packages/server/src/api/routes/tests/utilities/index.ts @@ -1,5 +1,6 @@ import TestConfig from "../../../../tests/utilities/TestConfiguration" import env from "../../../../environment" +import supertest from "supertest" export * as structures from "../../../../tests/utilities/structures" function user() { @@ -44,7 +45,8 @@ export function delay(ms: number) { return new Promise(resolve => setTimeout(resolve, ms)) } -let request: any, config: TestConfig +let request: supertest.SuperTest | undefined | null, + config: TestConfig export function beforeAll() { config = new TestConfig() From 89e061060005e77376a5411c269e5e192ffc13f6 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 17 Jan 2023 17:22:31 +0000 Subject: [PATCH 09/56] Add basic test --- packages/server/package.json | 1 + .../server/src/integration-test/row.spec.ts | 73 ++ packages/server/src/tests/jestEnv.ts | 24 +- .../src/tests/utilities/TestConfiguration.ts | 3 +- packages/server/yarn.lock | 790 +++++++++++++++++- packages/types/src/documents/app/table.ts | 4 +- 6 files changed, 871 insertions(+), 24 deletions(-) create mode 100644 packages/server/src/integration-test/row.spec.ts diff --git a/packages/server/package.json b/packages/server/package.json index 8210cf4cba..7894239099 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -122,6 +122,7 @@ "@babel/core": "7.17.4", "@babel/preset-env": "7.16.11", "@budibase/standard-components": "^0.9.139", + "@faker-js/faker": "^7.6.0", "@jest/test-sequencer": "24.9.0", "@swc/core": "^1.3.25", "@swc/jest": "^0.2.24", diff --git a/packages/server/src/integration-test/row.spec.ts b/packages/server/src/integration-test/row.spec.ts new file mode 100644 index 0000000000..3c65152871 --- /dev/null +++ b/packages/server/src/integration-test/row.spec.ts @@ -0,0 +1,73 @@ +import { faker } from "@faker-js/faker" +import { + generateMakeRequest, + MakeRequestResponse, +} from "../api/routes/public/tests/utils" + +import * as setup from "../api/routes/tests/utilities" +import supertest from "supertest" +import { FieldType } from "@budibase/types" + +const config = setup.getConfig() +let apiKey, table, app, makeRequest: MakeRequestResponse + +beforeAll(async () => { + app = await config.init() + table = await config.updateTable() + apiKey = await config.generateApiKey() + makeRequest = generateMakeRequest(apiKey) +}) + +afterAll(async () => { + require("../app").default + await config.end() +}) + +describe("row api", () => { + let request: supertest.SuperTest + let server: any + + beforeAll(() => { + server = require("../app").default + }) + afterAll(() => { + server.close() + }) + + beforeEach(async () => { + request = supertest(server) + }) + + describe("create a row", () => { + test("Given than no row exists, adding a new rows persists it", async () => { + const tableName = faker.lorem.word() + let table = await config.createTable({ + name: tableName, + schema: { + name: { + name: "name", + type: FieldType.STRING, + }, + description: { + name: "description", + type: FieldType.STRING, + }, + value: { + name: "value", + type: FieldType.NUMBER, + }, + }, + }) + + const newRow = { + name: faker.name.fullName(), + description: faker.lorem.paragraphs(), + value: faker.random.numeric(), + } + + const res = await makeRequest("post", `/tables/${table._id}/rows`, newRow) + + expect(res.status).toBe(200) + }) + }) +}) diff --git a/packages/server/src/tests/jestEnv.ts b/packages/server/src/tests/jestEnv.ts index 913c1f0f5e..a91f4ffdb2 100644 --- a/packages/server/src/tests/jestEnv.ts +++ b/packages/server/src/tests/jestEnv.ts @@ -1,13 +1,17 @@ import env from "../environment" import { tmpdir } from "os" -env._set("SELF_HOSTED", "1") -env._set("NODE_ENV", "jest") -env._set("JWT_SECRET", "test-jwtsecret") -env._set("CLIENT_ID", "test-client-id") -env._set("BUDIBASE_DIR", tmpdir("budibase-unittests")) -env._set("LOG_LEVEL", "silent") -env._set("PORT", 0) -env._set("MINIO_URL", "http://localhost") -env._set("MINIO_ACCESS_KEY", "test") -env._set("MINIO_SECRET_KEY", "test") +if (!env.isDockerisedTest()) { + env._set("SELF_HOSTED", "1") + env._set("NODE_ENV", "jest") + env._set("JWT_SECRET", "test-jwtsecret") + env._set("CLIENT_ID", "test-client-id") + env._set("BUDIBASE_DIR", tmpdir("budibase-unittests")) + env._set("LOG_LEVEL", "silent") + env._set("PORT") + env._set("MINIO_URL", "http://localhost") + env._set("MINIO_ACCESS_KEY", "test") + env._set("MINIO_SECRET_KEY", "test") +} else { + env._set("DISABLE_AUTO_PROD_APP_SYNC", "TRUE") +} diff --git a/packages/server/src/tests/utilities/TestConfiguration.ts b/packages/server/src/tests/utilities/TestConfiguration.ts index 5753538eb7..b9d4470c88 100644 --- a/packages/server/src/tests/utilities/TestConfiguration.ts +++ b/packages/server/src/tests/utilities/TestConfiguration.ts @@ -39,6 +39,7 @@ import newid from "../../db/newid" import { generateUserMetadataID } from "../../db/utils" import { startup } from "../../startup" import supertest from "supertest" +import { Table } from "@budibase/types" const GLOBAL_USER_ID = "us_uuid1" const EMAIL = "babs@babs.com" @@ -418,7 +419,7 @@ class TestConfiguration { return this.table } - async createTable(config?: any) { + async createTable(config?: Table) { if (config != null && config._id) { delete config._id } diff --git a/packages/server/yarn.lock b/packages/server/yarn.lock index 2b670f6f90..e3640460e3 100644 --- a/packages/server/yarn.lock +++ b/packages/server/yarn.lock @@ -2,6 +2,11 @@ # yarn lockfile v1 +"@adobe/spectrum-css-workflow-icons@1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@adobe/spectrum-css-workflow-icons/-/spectrum-css-workflow-icons-1.2.1.tgz#7e2cb3fcfb5c8b12d7275afafbb6ec44913551b4" + integrity sha512-uVgekyBXnOVkxp+CUssjN/gefARtudZC8duEn1vm0lBQFwGRZFlDEzU1QC+aIRWCrD1Z8OgRpmBYlSZ7QS003w== + "@adobe/spectrum-css-workflow-icons@^1.2.1": version "1.5.3" resolved "https://registry.yarnpkg.com/@adobe/spectrum-css-workflow-icons/-/spectrum-css-workflow-icons-1.5.3.tgz#5e31ce842b7626f4b99f9d5cd0b17599d287b0bf" @@ -1312,6 +1317,59 @@ uuid "8.3.2" zlib "1.0.5" +"@budibase/bbui@2.2.12-alpha.21": + version "2.2.12-alpha.21" + resolved "https://registry.yarnpkg.com/@budibase/bbui/-/bbui-2.2.12-alpha.21.tgz#91e7d7268e1b52b5262c102944e85363b5928a91" + integrity sha512-4fOv7Y+2a8JoxBl3zo7YoPhK67y08l4YttgApWU+dTpqdPgUQ0T1JobL0gfaKjdwKSmI+2fX6pl8MUUiWw/3/A== + dependencies: + "@adobe/spectrum-css-workflow-icons" "1.2.1" + "@budibase/string-templates" "2.2.12-alpha.21" + "@spectrum-css/actionbutton" "1.0.1" + "@spectrum-css/actiongroup" "1.0.1" + "@spectrum-css/avatar" "3.0.2" + "@spectrum-css/button" "3.0.1" + "@spectrum-css/buttongroup" "3.0.2" + "@spectrum-css/checkbox" "3.0.2" + "@spectrum-css/dialog" "3.0.1" + "@spectrum-css/divider" "1.0.3" + "@spectrum-css/dropzone" "3.0.2" + "@spectrum-css/fieldgroup" "3.0.2" + "@spectrum-css/fieldlabel" "3.0.1" + "@spectrum-css/icon" "3.0.1" + "@spectrum-css/illustratedmessage" "3.0.2" + "@spectrum-css/inlinealert" "2.0.1" + "@spectrum-css/inputgroup" "3.0.2" + "@spectrum-css/label" "2.0.10" + "@spectrum-css/link" "3.1.1" + "@spectrum-css/menu" "3.0.1" + "@spectrum-css/modal" "3.0.1" + "@spectrum-css/pagination" "3.0.3" + "@spectrum-css/picker" "1.0.1" + "@spectrum-css/popover" "3.0.1" + "@spectrum-css/progressbar" "1.0.2" + "@spectrum-css/progresscircle" "1.0.2" + "@spectrum-css/radio" "3.0.2" + "@spectrum-css/search" "3.0.2" + "@spectrum-css/sidenav" "3.0.2" + "@spectrum-css/slider" "3.0.1" + "@spectrum-css/statuslight" "3.0.2" + "@spectrum-css/stepper" "3.0.3" + "@spectrum-css/switch" "1.0.2" + "@spectrum-css/table" "3.0.1" + "@spectrum-css/tabs" "3.2.12" + "@spectrum-css/tags" "3.0.2" + "@spectrum-css/textfield" "3.0.1" + "@spectrum-css/toast" "3.0.1" + "@spectrum-css/tooltip" "3.0.3" + "@spectrum-css/treeview" "3.0.2" + "@spectrum-css/typography" "3.0.1" + "@spectrum-css/underlay" "2.0.9" + "@spectrum-css/vars" "3.0.1" + dayjs "^1.10.4" + easymde "^2.16.1" + svelte-flatpickr "^3.2.3" + svelte-portal "^1.0.0" + "@budibase/bbui@^0.9.139": version "0.9.190" resolved "https://registry.yarnpkg.com/@budibase/bbui/-/bbui-0.9.190.tgz#e1ec400ac90f556bfbc80fc23a04506f1585ea81" @@ -1362,6 +1420,71 @@ svelte-flatpickr "^3.2.3" svelte-portal "^1.0.0" +"@budibase/client@2.2.12-alpha.21": + version "2.2.12-alpha.21" + resolved "https://registry.yarnpkg.com/@budibase/client/-/client-2.2.12-alpha.21.tgz#29f9a1839699f4cd5935120ddda4b7231fa4db21" + integrity sha512-tHOg0o4cWdqajgLI1iSaWjX+wMGJB98zjwl0JpWW06cy5DOnIe4Tp+THLYWeHmWv+wPlMDFLCr6fsnZhZUXz7Q== + dependencies: + "@budibase/bbui" "2.2.12-alpha.21" + "@budibase/frontend-core" "2.2.12-alpha.21" + "@budibase/string-templates" "2.2.12-alpha.21" + "@spectrum-css/button" "^3.0.3" + "@spectrum-css/card" "^3.0.3" + "@spectrum-css/divider" "^1.0.3" + "@spectrum-css/link" "^3.1.3" + "@spectrum-css/page" "^3.0.1" + "@spectrum-css/tag" "^3.1.4" + "@spectrum-css/typography" "^3.0.2" + "@spectrum-css/vars" "^3.0.1" + apexcharts "^3.22.1" + dayjs "^1.10.5" + downloadjs "1.4.7" + html5-qrcode "^2.2.1" + leaflet "^1.7.1" + regexparam "^1.3.0" + sanitize-html "^2.7.0" + screenfull "^6.0.1" + shortid "^2.2.15" + socket.io-client "^4.5.1" + svelte "^3.49.0" + svelte-apexcharts "^1.0.2" + svelte-flatpickr "^3.1.0" + svelte-spa-router "^3.0.5" + +"@budibase/frontend-core@2.2.12-alpha.21": + version "2.2.12-alpha.21" + resolved "https://registry.yarnpkg.com/@budibase/frontend-core/-/frontend-core-2.2.12-alpha.21.tgz#ad9cc2f8ecdddd2f6c5ac07cda44cf2a5c72de96" + integrity sha512-ZPH/U8BnCHSjT+3ySBWniex914tGH+vrNbRjQZlRS4JabSiYDaNr0ya7zWYL39xryazd0NPacxP3xV8UY4+B8A== + dependencies: + "@budibase/bbui" "2.2.12-alpha.21" + lodash "^4.17.21" + svelte "^3.46.2" + +"@budibase/handlebars-helpers@^0.11.8": + version "0.11.8" + resolved "https://registry.yarnpkg.com/@budibase/handlebars-helpers/-/handlebars-helpers-0.11.8.tgz#6953d29673a8c5c407e096c0a84890465c7ce841" + integrity sha512-ggWJUt0GqsHFAEup5tlWlcrmYML57nKhpNGGLzVsqXVYN8eVmf3xluYmmMe7fDYhQH0leSprrdEXmsdFQF3HAQ== + dependencies: + array-sort "^1.0.0" + define-property "^2.0.2" + extend-shallow "^3.0.2" + for-in "^1.0.2" + get-object "^0.2.0" + get-value "^3.0.1" + handlebars "^4.7.7" + handlebars-utils "^1.0.6" + has-value "^2.0.2" + helper-md "^0.2.2" + html-tag "^2.0.0" + is-even "^1.0.0" + is-glob "^4.0.1" + kind-of "^6.0.3" + micromatch "^3.1.5" + relative "^3.0.2" + striptags "^3.1.1" + to-gfm-code-block "^0.1.1" + year "^0.2.1" + "@budibase/nano@10.1.1": version "10.1.1" resolved "https://registry.yarnpkg.com/@budibase/nano/-/nano-10.1.1.tgz#36ccda4d9bb64b5ee14dd2b27a295b40739b1038" @@ -1405,6 +1528,18 @@ svelte-apexcharts "^1.0.2" svelte-flatpickr "^3.1.0" +"@budibase/string-templates@2.2.12-alpha.21": + version "2.2.12-alpha.21" + resolved "https://registry.yarnpkg.com/@budibase/string-templates/-/string-templates-2.2.12-alpha.21.tgz#9238397f25e56ca0779aaf532898cee947a1f6f6" + integrity sha512-Ha469PUP/njc/yY1nkeVfWs3cwqZL3ig/oWK+tprZ3X7WTjA72D4UtiohsAnMZtvsRcd1PAkPhUXH5UgAdNGeg== + dependencies: + "@budibase/handlebars-helpers" "^0.11.8" + dayjs "^1.10.4" + handlebars "^4.7.6" + handlebars-utils "^1.0.6" + lodash "^4.17.20" + vm2 "^3.9.4" + "@budibase/types@2.2.12-alpha.21": version "2.2.12-alpha.21" resolved "https://registry.yarnpkg.com/@budibase/types/-/types-2.2.12-alpha.21.tgz#9344fae4504cf5d9a3a92ffb94434f988389c9a9" @@ -1522,6 +1657,11 @@ pump "^3.0.0" secure-json-parse "^2.1.0" +"@faker-js/faker@^7.6.0": + version "7.6.0" + resolved "https://registry.yarnpkg.com/@faker-js/faker/-/faker-7.6.0.tgz#9ea331766084288634a9247fcd8b84f16ff4ba07" + integrity sha512-XK6BTq1NDMo9Xqw/YkYyGjSsg44fbNwYRx7QK2CuoQgyy+f1rrTDHoExVM5PsyXCtfl2vs2vVJ0MN0yN6LppRw== + "@google-cloud/firestore@5.0.2": version "5.0.2" resolved "https://registry.yarnpkg.com/@google-cloud/firestore/-/firestore-5.0.2.tgz#36923fde45987f928a220d347f341c5602f9e340" @@ -2617,26 +2757,46 @@ resolved "https://registry.yarnpkg.com/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz#96116f2a912e0c02817345b3c10751069920d553" integrity sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg== +"@spectrum-css/actionbutton@1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@spectrum-css/actionbutton/-/actionbutton-1.0.1.tgz#9c75da37ea6915919fb574c74bd60dacc03b6577" + integrity sha512-AUqtyNabHF451Aj9i3xz82TxS5Z6k1dttA68/1hMeU9kbPCSS4P6Viw3vaRGs9CSspuR8xnnhDgrq+F+zMy2Hw== + "@spectrum-css/actionbutton@^1.0.1": version "1.1.14" resolved "https://registry.yarnpkg.com/@spectrum-css/actionbutton/-/actionbutton-1.1.14.tgz#4e12eb7f482fb5944c3d97547591964baebeb1d4" integrity sha512-ViBjdWi23J6vIR4t8JTRQ6jY/+KgpZgCALj3otgy495zMNG7jPeN7sKoy6i6JZJcdIRJA4MjOTVvcDOGkYWUZg== +"@spectrum-css/actiongroup@1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@spectrum-css/actiongroup/-/actiongroup-1.0.1.tgz#b95b86e7af229e90fe1e70399d8d4b547b4bd31c" + integrity sha512-5Q6uMjzv5BFA2TwGASr/jAtJpTWl26fhWvgGY8kOA0RCSij35l+YJg/FPXf6Nnj2qCOl8DkNycjT9YXJ+bhyVA== + "@spectrum-css/actiongroup@^1.0.1": version "1.0.26" resolved "https://registry.yarnpkg.com/@spectrum-css/actiongroup/-/actiongroup-1.0.26.tgz#181ee059f28b1342389a128c39d20d2e10566aae" integrity sha512-T1IK9a2Gxix9givm+chGvFtZh5oGBZQc/S2UA9F76JZKu45eCkLkvUH6F670XOrBhDGkVfzvN21QnFymSY43ow== -"@spectrum-css/avatar@^3.0.2": +"@spectrum-css/avatar@3.0.2", "@spectrum-css/avatar@^3.0.2": version "3.0.2" resolved "https://registry.yarnpkg.com/@spectrum-css/avatar/-/avatar-3.0.2.tgz#4f1826223eae330e64b6d3cc899e9bc2e98dac95" integrity sha512-wEczvSqxttTWSiL3cOvXV/RmGRwSkw2w6+slcHhnf0kb7ovymMM+9oz8vvEpEsSeo5u598bc+7ktrKFpAd6soQ== +"@spectrum-css/button@3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@spectrum-css/button/-/button-3.0.1.tgz#6db8c3e851baecd0f1c2d88fef37d49d01c6e643" + integrity sha512-YXrBtjIYisk4Vaxnp0RiE4gdElQX04P2mc4Pi2GlQ27dJKlHmufYcF+kAqGdtiyK5yjdN/vKRcC8y13aA4rusA== + "@spectrum-css/button@^3.0.1", "@spectrum-css/button@^3.0.3": version "3.0.3" resolved "https://registry.yarnpkg.com/@spectrum-css/button/-/button-3.0.3.tgz#2df1efaab6c7e0b3b06cb4b59e1eae59c7f1fc84" integrity sha512-6CnLPqqtaU/PcSSIGeGRi0iFIIxIUByYLKFO6zn5NEUc12KQ28dJ4PLwB6WBa0L8vRoAGlnWWH2ZZweTijbXgg== +"@spectrum-css/buttongroup@3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@spectrum-css/buttongroup/-/buttongroup-3.0.2.tgz#fd3387973ca3131609e32112de42a1c0400a48d8" + integrity sha512-Wu7B4GJ/SAeVHz9SUGAkeIH8pLaZh4t+w2ykSKOPQIRuK2jCBoudkEClVxviNVwqekccf5XLFXg9GpYF1a3Uaw== + "@spectrum-css/buttongroup@^3.0.2": version "3.0.10" resolved "https://registry.yarnpkg.com/@spectrum-css/buttongroup/-/buttongroup-3.0.10.tgz#897ea04b3ffea389fc7fe5bf67a6d1f3454b774d" @@ -2647,16 +2807,33 @@ resolved "https://registry.yarnpkg.com/@spectrum-css/card/-/card-3.0.3.tgz#56b2e2da6b80c1583228baa279de7407383bfb6b" integrity sha512-+oKLUI2a0QmQP9EzySeq/G4FpUkkdaDNbuEbqCj2IkPMc/2v/nwzsPhh1fj2UIghGAiiUwXfPpzax1e8fyhQUg== +"@spectrum-css/checkbox@3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@spectrum-css/checkbox/-/checkbox-3.0.2.tgz#53ca2fba0d9faa1fead10e7206eb1f6cdcfd6ddd" + integrity sha512-hPbGcnm7kJvJS4jp/P/bdaZvbyR1eIE9mteuZqcBgdmyp9m/k6+mW5jmsbtqb3Y4mMPWvOJFfz/sIvWJP0F0Zg== + "@spectrum-css/checkbox@^3.0.2": version "3.1.2" resolved "https://registry.yarnpkg.com/@spectrum-css/checkbox/-/checkbox-3.1.2.tgz#88698969091da9b50de781d25839446084b4a5f4" integrity sha512-vIuknIhRF/Xtq6OHjOtlhYt722FPcTLBb7Y7tY0Ho8VEpynj3JrVLP/1YYp/YIrYMpsTugxPmbCrEkikkdL6Mg== +"@spectrum-css/dialog@3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@spectrum-css/dialog/-/dialog-3.0.1.tgz#33aae036282159f6aa998848b8c0828640a9620a" + integrity sha512-hUFbRR6RGT63MNuP7wP+k9KU+uRuICsduMihskh700e+jiQ+Gsv53fBFDlB843FoZYlIXzFQXgtjMUC5a4Qibw== + "@spectrum-css/dialog@^3.0.1": version "3.0.12" resolved "https://registry.yarnpkg.com/@spectrum-css/dialog/-/dialog-3.0.12.tgz#fc97e002ca768a3d99dd10cb6a135c2b06052004" integrity sha512-50rbFa+9eUKT+3uYBX7CkmI7SbQ0Z3CAFwjyjai+itYZ8kf/FcHVFwcLjgrry9scUnKhexMs94kkr0gfQpPe8Q== +"@spectrum-css/divider@1.0.3": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@spectrum-css/divider/-/divider-1.0.3.tgz#639e2ebaa0834efa40f42397668bbd5c153ea385" + integrity sha512-Zy4Rn40w8UtzMh3wx/U9+CepSCpm1aOCGftHgWDub0XZuVTzh0c1WwyzTuLCx2Hf21z5VRGNiDh8bGEEzSbtNA== + dependencies: + "@spectrum-css/vars" "^3.0.2" + "@spectrum-css/divider@^1.0.3": version "1.0.26" resolved "https://registry.yarnpkg.com/@spectrum-css/divider/-/divider-1.0.26.tgz#44b610b1b6c747536fca08b3f09286341e18ab29" @@ -2664,56 +2841,106 @@ dependencies: "@spectrum-css/vars" "^8.0.0" +"@spectrum-css/dropzone@3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@spectrum-css/dropzone/-/dropzone-3.0.2.tgz#34f137851054442b219fed7f32006b93fc5e0bcf" + integrity sha512-BuBBzm5re6lM0AWgd6V+mI5eEGnnmFEtcFiJBEn9jYNEQYgflFhvnERUt89jMX5WmspiecwI2JBWJFrtFsOzug== + "@spectrum-css/dropzone@^3.0.2": version "3.0.24" resolved "https://registry.yarnpkg.com/@spectrum-css/dropzone/-/dropzone-3.0.24.tgz#edefb3ca5a01705a64d0161a599c59199bab6299" integrity sha512-JY60hUZAAuzS+o2xFOKv0o31cc+5/cjLpTyKEy73oGKsdUXEEMiQtW2PQBCuxh7PNyw29wCULeZ1EW1QdNPyxg== +"@spectrum-css/fieldgroup@3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@spectrum-css/fieldgroup/-/fieldgroup-3.0.2.tgz#1c1afd3c444d8650fefac275dc66a7a913933846" + integrity sha512-Vyw0kQJdLW18J6w4H+YAsoLntvkw5rXmW3CH5H3SDTXkBztxtHSSe3e106Nw5MoZxTfHlom6CxbYXYCTjQfqGw== + "@spectrum-css/fieldgroup@^3.0.2": version "3.1.3" resolved "https://registry.yarnpkg.com/@spectrum-css/fieldgroup/-/fieldgroup-3.1.3.tgz#945123da56534f1ff6118a9defd18b8a883e34a8" integrity sha512-HIbB3jweNviWXcADoYQW3hanww9RTUIsBUhe0YxSMXUXnQJc/7nlyeLoTRMr2eEVSCREfRnMot/8bZloW7ctnA== +"@spectrum-css/fieldlabel@3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@spectrum-css/fieldlabel/-/fieldlabel-3.0.1.tgz#39f7c0f25cc2ff402afeff005341b0832f7c588c" + integrity sha512-LMfwrwIq8wEEvxFLobdLvXRwKrp8o9Fty4iJ9aYl2Rj1uXkfRd8qLz9HGZjLEE1OuJgoTBgamYABl7EvoA5PLw== + "@spectrum-css/fieldlabel@^3.0.1": version "3.0.3" resolved "https://registry.yarnpkg.com/@spectrum-css/fieldlabel/-/fieldlabel-3.0.3.tgz#f73c04d20734d4718ffb620dc46458904685b449" integrity sha512-nEvIkEXCD5n4fW67Unq6Iu7VXoauEd/JGpfTY02VsC5p4FJLnwKfPDbJUuUsqClAxqw7nAsmXVKtn4zQFf5yPQ== +"@spectrum-css/icon@3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@spectrum-css/icon/-/icon-3.0.1.tgz#e300a6fc353c85c6b5d6e7a364408a940c31b177" + integrity sha512-cGFtIrcQ/7tthdkHK1npuEFiCdYVHLqwmLxghUYQw8Tb8KgJaw3OBO1tpjgsUizexNgu26BjVRIbGxNWuBXIHQ== + "@spectrum-css/icon@^3.0.1": version "3.0.22" resolved "https://registry.yarnpkg.com/@spectrum-css/icon/-/icon-3.0.22.tgz#1dd77e2460121951c60c583edb470d0ba52e6822" integrity sha512-ilrPlHDRGzn7kXVVAwUhoSaMfS6sGlb21ix2gn8IRLBAjDOV8BBV1wJJtjGNw+kzCXMhnVnVOekTdht17Oe9bw== +"@spectrum-css/illustratedmessage@3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@spectrum-css/illustratedmessage/-/illustratedmessage-3.0.2.tgz#6a480be98b027e050b086e7899e40d87adb0a8c0" + integrity sha512-dqnE8X27bGcO0HN8+dYx8O4o0dNNIAqeivOzDHhe2El+V4dTzMrNIerF6G0NLm3GjVf6XliwmitsZK+K6FmbtA== + "@spectrum-css/illustratedmessage@^3.0.2": version "3.0.17" resolved "https://registry.yarnpkg.com/@spectrum-css/illustratedmessage/-/illustratedmessage-3.0.17.tgz#49cb2549fda97a6812156bfba6ccdd3a18bebd11" integrity sha512-kpDqeq1U+rEjG1XuiXkbGvS71vn6mpFF/hiwCgFJWudVOfypDPQ4KLfYw1ditFSUzMCm5H6U/RqAShAJn8oMWA== +"@spectrum-css/inlinealert@2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@spectrum-css/inlinealert/-/inlinealert-2.0.1.tgz#7521f88f6c845806403cc7d925773c7414e204a2" + integrity sha512-Xy5RCOwgurqUXuGQCsEDUduDd5408bmEpmFg+feynG7VFUgLFZWBeylSENB/OqjlFtO76PHXNVdHkhDscPIHTA== + "@spectrum-css/inlinealert@^2.0.1": version "2.0.6" resolved "https://registry.yarnpkg.com/@spectrum-css/inlinealert/-/inlinealert-2.0.6.tgz#4c5e923a1f56a96cc1adb30ef1f06ae04f2c6376" integrity sha512-OpvvoWP02wWyCnF4IgG8SOPkXymovkC9cGtgMS1FdDubnG3tJZB/JeKTsRR9C9Vt3WBaOmISRdSKlZ4lC9CFzA== +"@spectrum-css/inputgroup@3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@spectrum-css/inputgroup/-/inputgroup-3.0.2.tgz#f1b13603832cbd22394f3d898af13203961f8691" + integrity sha512-O0G3Lw9gxsh8gTLQWIAKkN1O8cWhjpEUl+oR1PguIKFni72uNr2ikU9piOwy/r0gJG2Q/TVs6hAshoAAkmsSzw== + "@spectrum-css/inputgroup@^3.0.2": version "3.0.8" resolved "https://registry.yarnpkg.com/@spectrum-css/inputgroup/-/inputgroup-3.0.8.tgz#fc23afc8a73c24d17249c9d2337e8b42085b298b" integrity sha512-cmQWzFp0GU+4IMc8SSeVFdmQDlRUdPelXaQdKUR9mZuO2iYettg37s0lfBCeJyYkUNTagz0zP8O7A0iXfmeE6g== -"@spectrum-css/label@^2.0.10": +"@spectrum-css/label@2.0.10", "@spectrum-css/label@^2.0.10": version "2.0.10" resolved "https://registry.yarnpkg.com/@spectrum-css/label/-/label-2.0.10.tgz#2368651d7636a19385b5d300cdf6272db1916001" integrity sha512-xCbtEiQkZIlLdWFikuw7ifDCC21DOC/KMgVrrVJHXFc4KRQe9LTZSqmGF3tovm+CSq1adE59mYoTbojVQ9YuEQ== +"@spectrum-css/link@3.1.1": + version "3.1.1" + resolved "https://registry.yarnpkg.com/@spectrum-css/link/-/link-3.1.1.tgz#cb526a2e10b50ef5a7ae29cca7272e2610d597eb" + integrity sha512-Bi88lRhTY7g6nM/ryW1yY4Cji211ZYNtRxkxbV7n2lPvwMAAQtyx0qVD3ru4kTGj/FFVvmPR3XiOE10K13HSNA== + "@spectrum-css/link@^3.1.1", "@spectrum-css/link@^3.1.3": version "3.1.22" resolved "https://registry.yarnpkg.com/@spectrum-css/link/-/link-3.1.22.tgz#1e061d674789c5b3be5b0680f9f6eae3e695e1c1" integrity sha512-Zf8bfy+rtq07l4qoR6chNxefmatLZQZjudIm96v+lsCXBkjVbiMpjkW9oOcNwTqKB08koMONHHhOf1wk2Faqiw== +"@spectrum-css/menu@3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@spectrum-css/menu/-/menu-3.0.1.tgz#2a376f991acc24e12ec892bb6b9db2650fc41fbe" + integrity sha512-Qjg0+1O0eC89sb/bRFq2AGnQ8XqhVy23TUXHyffNM8qdcMssnlny3QmhzjURCZKvx/Y5UytCpzhedPQqSpQwZg== + "@spectrum-css/menu@^3.0.1": version "3.0.21" resolved "https://registry.yarnpkg.com/@spectrum-css/menu/-/menu-3.0.21.tgz#d1f7e6e69d30b5e1edd7ed2c86ea4e08dfd670ab" integrity sha512-G5AIUO26O6IAc6HUGZu4AZgyw0QRyLfSbcKlFGu4oJHzP36cQc1S1uCh8Xp4g5d+n6mU62LxNDLSMpVbwnA00A== +"@spectrum-css/modal@3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@spectrum-css/modal/-/modal-3.0.1.tgz#613a6b83d0330a4d38db41a98090800751c56d8d" + integrity sha512-F7D99F3cjDGT9DM9sogx/p49jrNYT7a1J6TUoqV73wUf+0gP+dTsskBOo9jB8VbUE+POQPjiDLB+SWLp6iBB+w== + "@spectrum-css/modal@^3.0.1": version "3.0.22" resolved "https://registry.yarnpkg.com/@spectrum-css/modal/-/modal-3.0.22.tgz#05593a613e246a7cbef85d08a6945219e1207209" @@ -2726,107 +2953,222 @@ dependencies: "@spectrum-css/vars" "^4.3.1" +"@spectrum-css/pagination@3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@spectrum-css/pagination/-/pagination-3.0.3.tgz#b204c3ada384c4af751a354bc428346d82eeea65" + integrity sha512-OJ/v9GeNXJOZ9Yr9LDBYPrR2NCiLOWP9wANT/a5sqFuugRnQbn/HYMnRp9TBxwpDY6ihaPo0T/wi7kLiAJFdDw== + "@spectrum-css/pagination@^3.0.3": version "3.0.11" resolved "https://registry.yarnpkg.com/@spectrum-css/pagination/-/pagination-3.0.11.tgz#68d9f34fe8eb36bf922e41b11f49eac62ac2fc41" integrity sha512-wjZr7NAcqHK6fxNIGKTYEVtAOJugJTbcz4d8K7DZuUDgBVwLJJHJBi4uJ4KrIRYliMWOvqWTZzCJLmmTfx4cyw== +"@spectrum-css/picker@1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@spectrum-css/picker/-/picker-1.0.1.tgz#98991198576d26bd14160824e7b6f3c278ff930b" + integrity sha512-Rv4/UBOdNW1gs7WVBCJnPD5VFly8MqP++psDX6kcugUIcfJy0GC3acvElotmKRlCDk8Qxks2W2A0jKeSgphTmA== + "@spectrum-css/picker@^1.0.1": version "1.2.9" resolved "https://registry.yarnpkg.com/@spectrum-css/picker/-/picker-1.2.9.tgz#854cdca407daaf8e1f821777978690f0804b3c08" integrity sha512-HDUDiqHwM84xfbHJWm4wR67Km3NXcDluhDrkVn8uqOEZrm8y4YiW+esL6FzPgzqLdPIHboQjrdpRq4LiDzGjjA== +"@spectrum-css/popover@3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@spectrum-css/popover/-/popover-3.0.1.tgz#5863c1efc53f98f9aba2de9186666780041303fc" + integrity sha512-LmOSj/yCwQQ9iGmCYnHiJsJR/HfPiGqI1Jl7pkKxBOCxYBMS/5+ans9vfCN2Qnd0eK7WSbfPg72S6mjye7db2Q== + "@spectrum-css/popover@^3.0.1": version "3.0.11" resolved "https://registry.yarnpkg.com/@spectrum-css/popover/-/popover-3.0.11.tgz#a7450c01bcf1609264b4a9df58821368b9e224d1" integrity sha512-bzyNQJVw6Mn1EBelTaRlXCdd0ZfykNX9O6SHx3a+jXPYu8VBrRpHm0gsfWzPAz1etd1vj1CxwG/teQt4qvyZ/Q== +"@spectrum-css/progressbar@1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@spectrum-css/progressbar/-/progressbar-1.0.2.tgz#b5a59432517f9ae6dad49d9504691bc5ac42b424" + integrity sha512-+jExeBLtVCqo3BqtFq5WCtZ028Dzk+oUnX6y4z6ZamKPqOyOELOtFnhYnyhyRndQOqYwKUTXx9zsaWA/lpJOHw== + "@spectrum-css/progressbar@^1.0.2": version "1.0.30" resolved "https://registry.yarnpkg.com/@spectrum-css/progressbar/-/progressbar-1.0.30.tgz#1f1e81ab6080fb843831421f736ed2bccc9b18ed" integrity sha512-tUquDN33RQG8gyrWmwPaCu6I2rxRyv5BBGBPii+1sK7L/DTCJrKXe7TAqoxjNEYzdCvTF/HI1NvnSColWNq0Rw== +"@spectrum-css/progresscircle@1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@spectrum-css/progresscircle/-/progresscircle-1.0.2.tgz#258ea9170fb70f795edda03e38a61d93bef4487c" + integrity sha512-JLULpyzjIY95lzlWR1yE1gv4l1K6p+scQ+edmuZZUHBzwM3pUtkvHJmUlA9TYdResUYW6Uka60VRdY6lZ8gnFQ== + "@spectrum-css/progresscircle@^1.0.2": version "1.0.22" resolved "https://registry.yarnpkg.com/@spectrum-css/progresscircle/-/progresscircle-1.0.22.tgz#80c8fd2ac4ee6855297d98c60c3b36082020a32a" integrity sha512-EGb+q+7RxbbsrEPFpJ1P4XBQ4s6Ra0okjQCDDTTKTp/sUY2WIT2BjPzwxlZTxVmSXWiiuRyzyuSYUrgBw9UgWg== +"@spectrum-css/radio@3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@spectrum-css/radio/-/radio-3.0.2.tgz#9c1386894920bbed604e4e174fbbd45d9d762152" + integrity sha512-0TDdzC9omNXnpKHEXNuuGeXdNh4x8jvTKVUqMRLb7vY4hY94hAdt6X01NBqka+jzK35HxGzpDdPADAz62yZLPQ== + "@spectrum-css/radio@^3.0.2": version "3.0.23" resolved "https://registry.yarnpkg.com/@spectrum-css/radio/-/radio-3.0.23.tgz#118a28c407e7b58bec139483d7e23074d840ae77" integrity sha512-x+08GSufmsyrUU4iBOOMRXZrcHxabXMMm/q2vazDJE8CShztvmdjghCxcwtyM74sjiYmXnCW1V3ztr6zaG5xig== +"@spectrum-css/search@3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@spectrum-css/search/-/search-3.0.2.tgz#70e93e321032d40b399498b2324e3b70e050551e" + integrity sha512-3UbT8yZmNOwrZxq+CUmumE+26ZySZ8OoKNM6U20SLMPLgdx6MrRugVE88r3Bl0sJ0RZX/5bU8nausdiHeX+Jlw== + "@spectrum-css/search@^3.0.2": version "3.1.2" resolved "https://registry.yarnpkg.com/@spectrum-css/search/-/search-3.1.2.tgz#8d43f35f884f7c190e7694c8d26a3f2cfed01ef0" integrity sha512-8cMK1QB07dbReZ/ECyTyoT2dELZ7hK1b3jEDiWSeLBbXcKirR1OI24sZEnewQY/XWFd/62Z1YdNaaA8S6UuXWQ== +"@spectrum-css/sidenav@3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@spectrum-css/sidenav/-/sidenav-3.0.2.tgz#9d70f408d588ee79c69857751010333671f32713" + integrity sha512-YpIdH/F0jEICYmoduGrnkTmxwJq1kfKxEp0wOs+ZkQOsvKMv1an7nyhsfOKCQqcGNfYzJ9mJAk7/u5+vsxHa8g== + "@spectrum-css/sidenav@^3.0.2": version "3.0.23" resolved "https://registry.yarnpkg.com/@spectrum-css/sidenav/-/sidenav-3.0.23.tgz#c218560d472e13a3e0d1499b762df1206dcffbfd" integrity sha512-4IFw2/HMQJRzM0M2c5na/HeY7y5vJoGpMFBkXNpQyhW4TRo7N1rGwYQ5dRD3s4OVEWV4/rjfGV0d/qhfwKUTog== +"@spectrum-css/slider@3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@spectrum-css/slider/-/slider-3.0.1.tgz#5281e6f47eb5a4fd3d1816c138bf66d01d7f2e49" + integrity sha512-DI2dtMRnQuDM1miVzl3SGyR1khUEKnwdXfO5EHDFwkC3yav43F5QogkfjmjFmWWobMVovdJlAuiaaJ/IHejD0Q== + +"@spectrum-css/statuslight@3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@spectrum-css/statuslight/-/statuslight-3.0.2.tgz#dc54b6cd113413dcdb909c486b5d7bae60db65c5" + integrity sha512-xodB8g8vGJH20XmUj9ZsPlM1jHrGeRbvmVXkz0q7YvQrYAhim8pP3W+XKKZAletPFAuu8cmUOc6SWn6i4X4z6w== + "@spectrum-css/statuslight@^3.0.2": version "3.0.8" resolved "https://registry.yarnpkg.com/@spectrum-css/statuslight/-/statuslight-3.0.8.tgz#3b0ea80712573679870a85d469850230e794a0f7" integrity sha512-zMTHs8lk+I7fLdi9waEEbsCmJ1FxeHcjQ0yltWxuRmGk2vl4MQdQIuHIMI63iblqEaiwnJRjXJoKnWlNvndTJQ== +"@spectrum-css/stepper@3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@spectrum-css/stepper/-/stepper-3.0.3.tgz#ae89846886431e3edeee060207b8f81540f73a34" + integrity sha512-prAD61ImlOTs9b6PfB3cB08x4lAfxtvnW+RZiTYky0E8GgZdrc/MfCkL5/oqQaIQUtyQv/3Lb7ELAf/0K8QTXw== + "@spectrum-css/stepper@^3.0.3": version "3.0.25" resolved "https://registry.yarnpkg.com/@spectrum-css/stepper/-/stepper-3.0.25.tgz#a6e77d501a9671c083b6dd9a37c6a3f224ffc961" integrity sha512-nlAZKY4KCYQ4IFuFj/P0LXPsB4Ze36ziuaa3k3iy3+1pBDD4gDcGmNpNcTG1LENu0Bt87KhSj8Ba2NV3wBSY8w== +"@spectrum-css/switch@1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@spectrum-css/switch/-/switch-1.0.2.tgz#f0b4c69271964573e02b08e90998096e49e1de44" + integrity sha512-zqmHpgWPNg1gEwdUNFYV3CBX5JaeALfIqcJIxE0FLZqr9d1C4+oLE0ItIFzt1bwr4bFAOmkEpvtiY+amluzGxQ== + "@spectrum-css/switch@^1.0.2": version "1.0.22" resolved "https://registry.yarnpkg.com/@spectrum-css/switch/-/switch-1.0.22.tgz#8f2fe25a52b4511b9cfde7af45f9bd824b9774d7" integrity sha512-/Q8IxnkSQYo+i3G3BObslSvoKgM0Mm1mS7kmssULOtaQPbaRlRsUNQVaHzcNEX33+fiF/9zKSvs7ypgIvbWp+Q== +"@spectrum-css/table@3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@spectrum-css/table/-/table-3.0.1.tgz#753e0e2498082c0c36b9600828516aff3ac338cd" + integrity sha512-XQ+srMTv9hK1H0nctWUtqyzitmvyb5TNR+7mjAmKRdkBRSTQQSipDhenxZp72ekzMtMoSYZVZ77kgo0Iw3Fpug== + "@spectrum-css/table@^3.0.1": version "3.0.3" resolved "https://registry.yarnpkg.com/@spectrum-css/table/-/table-3.0.3.tgz#7f7f19905ef3275cbf907ce3a5818e63c30b2caf" integrity sha512-nxwzVjLPsXoY/v4sdxOVYLcC+cEbGgJyLcLclT5LT9MGSbngFeUMJzzVR4EvehzuN4dH7hrATG7Mbuq29Mf0Hg== +"@spectrum-css/tabs@3.2.12": + version "3.2.12" + resolved "https://registry.yarnpkg.com/@spectrum-css/tabs/-/tabs-3.2.12.tgz#9b08f23d5aa881b3441af7757800c7173e5685ff" + integrity sha512-rPFUW9SSW4+3/UJ3UrtY2/l3sQvlqB1fqxHLPDjgykvbfrnMejcCTNV4ZrFNHXpE/6+kGnk+yVViSPtWGwJzkA== + "@spectrum-css/tabs@^3.0.1": version "3.2.16" resolved "https://registry.yarnpkg.com/@spectrum-css/tabs/-/tabs-3.2.16.tgz#c3f7800d8d6f7c9930c28cd01354816328bf72b1" integrity sha512-JUcMB/fiDG/KoyrVstlUMacFJUY4OHKqhMRuPtu9ggUXWCRbSkY8he92v6u0HwY3DuhDoOxNTK8d/PLjk/fsbg== +"@spectrum-css/tag@^3.1.4": + version "3.3.15" + resolved "https://registry.yarnpkg.com/@spectrum-css/tag/-/tag-3.3.15.tgz#971184fd8cb977b85a529f808313851863123278" + integrity sha512-pF6Wh61Z7hmAy20twIlpjdDuivYj6UPtWIzK7giyJKr/qcn20BjVN2ChIeFB1N+vBamJdLsuQOewv4AJ3+LZ2Q== + +"@spectrum-css/tags@3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@spectrum-css/tags/-/tags-3.0.2.tgz#5bf35fb79c97cd9344de485bd4626ad5b9f07757" + integrity sha512-HbvMk+QHvCDD1/ScvSErpKROcpAbXuMD4Hl/Gz/1A1lQ0fJ/CJeCq/MMsL7zjK1nlItU/ySu8r8KIuRF+6F8SQ== + "@spectrum-css/tags@^3.0.2": version "3.0.3" resolved "https://registry.yarnpkg.com/@spectrum-css/tags/-/tags-3.0.3.tgz#fc76d2735cdc442de91b7eb3bee49a928c0767ac" integrity sha512-SL8vPxVDfWcY5VdIuyl0TImEXcOU1I7yCyXkk7MudMwfnYs81FaIyY32hFV9OHj0Tz/36UzRzc7AVMSuRQ53pw== +"@spectrum-css/textfield@3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@spectrum-css/textfield/-/textfield-3.0.1.tgz#e875b8e37817378ad08fc4af7d53026df38911e5" + integrity sha512-MUV5q87CVxbkNdSNoxGrFbgyKc51ft/WWf3aVEoPdPw5yBnXqFe1w1YmAit5zYDOOhhs58sCLAlUcCMlOpkgrA== + "@spectrum-css/textfield@^3.0.1": version "3.2.3" resolved "https://registry.yarnpkg.com/@spectrum-css/textfield/-/textfield-3.2.3.tgz#52830498fb3b8957f84bb9bf2cafec7edc55e490" integrity sha512-mtxSQe8VZjQ8PHKlUE03dATAjjxp2Y8XfYmWWFBWWZLeqaojSLv9Q8C/ouK5AenhzCaYpJxTotMjAoivwtmUSw== +"@spectrum-css/toast@3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@spectrum-css/toast/-/toast-3.0.1.tgz#36f62ea05302761e59b9d53e05f6c04423861796" + integrity sha512-jov++S358BrN2tmMfaoYk1N6u9HojgeuQk61keXrK2m3VE5/n94x7Lg3kIPeSWO0odyDfBlMqT9jacbRey3QTg== + "@spectrum-css/toast@^3.0.1": version "3.0.3" resolved "https://registry.yarnpkg.com/@spectrum-css/toast/-/toast-3.0.3.tgz#97c1527384707600832ecda35643ed304615250f" integrity sha512-CjLeaMs+cjUXojCCRtbj0YkD2BoZW16kjj2o5omkEpUTjA34IJ8xJ1a+CCtDILWekhXvN0MBN4sbumcnwcnx8w== +"@spectrum-css/tooltip@3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@spectrum-css/tooltip/-/tooltip-3.0.3.tgz#26b8ca3b3d30e29630244d85eb4fc11d0c841281" + integrity sha512-ztRF7WW1FzyNavXBRc+80z67UoOrY9wl3cMYsVD3MpDnyxdzP8cjza1pCcolKBaFqRTcQKkxKw3GWtGICRKR5A== + "@spectrum-css/tooltip@^3.0.3": version "3.1.17" resolved "https://registry.yarnpkg.com/@spectrum-css/tooltip/-/tooltip-3.1.17.tgz#1f0822c8b69d16d5f940a2b7eb6514d719e6a0fd" integrity sha512-YDuC+Cc6B8DExjL/7fkPnWb8QwlCkjuMHyuttwP/tq/lryWnrdntojgwK5KvgFRjnZ2WfepZVryIt5LOD3tMdg== +"@spectrum-css/treeview@3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@spectrum-css/treeview/-/treeview-3.0.2.tgz#d54d8f17290babb1c885f5d9355e225421beb0d2" + integrity sha512-foO7UBJv1JMFaKgDPVt8jBghZSVbqhXR8TaGaxHSnMubv7ygmKkc1AITrWC2STILCn84ju2vchOohMZfW6sYwg== + "@spectrum-css/treeview@^3.0.2": version "3.0.3" resolved "https://registry.yarnpkg.com/@spectrum-css/treeview/-/treeview-3.0.3.tgz#aeda5175158b9f8d7529cb2b394428eb2a428046" integrity sha512-D5gGzZC/KtRArdx86Mesc9+99W9nTbUOeyYGqoJoAfJSOttoT6Tk5CrDvlCmAqjKf5rajemAkGri1ChqvUIwkw== +"@spectrum-css/typography@3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@spectrum-css/typography/-/typography-3.0.1.tgz#957dafd9b18c314fa37a88b549042ba2175f5b3f" + integrity sha512-XyR68K2rIZX3u4j7HhMLOqLVHDJZcapp3XUqgYMzMWccBFleA0qPxKpfRWqVIA5DzTMSIw0wEcZPYKWFZ2e6dA== + "@spectrum-css/typography@^3.0.1", "@spectrum-css/typography@^3.0.2": version "3.0.2" resolved "https://registry.yarnpkg.com/@spectrum-css/typography/-/typography-3.0.2.tgz#ea3ca0a60e18064527819d48c8c4364cab4fcd38" integrity sha512-5ZOLmQe0edzsDMyhghUd4hBb5uxGsFrxzf+WasfcUw9klSfTsRZ09n1BsaaWbgrLjlMQ+EEHS46v5VNo0Ms2CA== +"@spectrum-css/underlay@2.0.9": + version "2.0.9" + resolved "https://registry.yarnpkg.com/@spectrum-css/underlay/-/underlay-2.0.9.tgz#fc10f971d1325cc844b727e6260f7217844060e8" + integrity sha512-X86xd0PG4QobmUyXA90BFGnyygaI8kW64dA4ysf4z0cOvUWjNbAAl3a/DB/WRyrnp63Zqv83T/cgNbetagTbWg== + "@spectrum-css/underlay@^2.0.9": version "2.0.30" resolved "https://registry.yarnpkg.com/@spectrum-css/underlay/-/underlay-2.0.30.tgz#401cfd68df945692bd6bbe281fee98889c930dd1" integrity sha512-Ssq/KERbDuJu3PUWPkBv9+ZIbKooke3oncRoYMXeyP/Gcw5bmQSXOvnlddU5DIK4PJR+pPGVZ9CUUFaYZot4YQ== -"@spectrum-css/vars@^3.0.1": +"@spectrum-css/vars@3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@spectrum-css/vars/-/vars-3.0.1.tgz#561fd69098f896a647242dd8d6108af603bfa31e" + integrity sha512-l4oRcCOqInChYXZN6OQhpe3isk6l4OE6Ys8cgdlsiKp53suNoQxyyd9p/eGRbCjZgH3xQ8nK0t4DHa7QYC0S6w== + +"@spectrum-css/vars@^3.0.1", "@spectrum-css/vars@^3.0.2": version "3.0.2" resolved "https://registry.yarnpkg.com/@spectrum-css/vars/-/vars-3.0.2.tgz#ea9062c3c98dfc6ba59e5df14a03025ad8969999" integrity sha512-vzS9KqYXot4J3AEER/u618MXWAS+IoMvYMNrOoscKiLLKYQWenaueakUWulFonToPd/9vIpqtdbwxznqrK5qDw== @@ -3029,6 +3371,13 @@ resolved "https://registry.yarnpkg.com/@types/caseless/-/caseless-0.12.2.tgz#f65d3d6389e01eeb458bd54dc8f52b95a9463bc8" integrity sha512-6ckxMjBBD8URvjB6J3NcnuAn5Pkl7t3TizAg+xdlzzQGSPSmBcXf8KoIH0ua/i+tio+ZRUHEXp0HEmvaR4kt0w== +"@types/codemirror@^5.60.4": + version "5.60.6" + resolved "https://registry.yarnpkg.com/@types/codemirror/-/codemirror-5.60.6.tgz#0511ed51d087ad2a2eedb6111fc74e739c4549f6" + integrity sha512-JIDPSvkYRlcv/2F0erqD+de2ni/Mz6FJMEGb0vwF6ByQOcHIKfiEfwrO4d6dSRwYeHyNUMpGjev0PyjX2M0XWw== + dependencies: + "@types/tern" "*" + "@types/connect@*": version "3.4.35" resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.35.tgz#5fcf6ae445e4021d1fc2219a4873cc73a3bb2ad1" @@ -3237,6 +3586,11 @@ resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.2.tgz#b74129719fc8d11c01868010082d483b7545591a" integrity sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA== +"@types/marked@^4.0.7": + version "4.0.8" + resolved "https://registry.yarnpkg.com/@types/marked/-/marked-4.0.8.tgz#b316887ab3499d0a8f4c70b7bd8508f92d477955" + integrity sha512-HVNzMT5QlWCOdeuBsgXP8EZzKUf0+AXzN+sLmjvaB3ZlLqO+e4u0uXrdw9ub69wBKFs+c6/pA4r9sy6cCDvImw== + "@types/mime@^1": version "1.3.2" resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.2.tgz#93e25bf9ee75fe0fd80b594bc4feb0e862111b5a" @@ -3528,6 +3882,13 @@ "@types/node" "*" minipass "^3.3.5" +"@types/tern@*": + version "0.23.4" + resolved "https://registry.yarnpkg.com/@types/tern/-/tern-0.23.4.tgz#03926eb13dbeaf3ae0d390caf706b2643a0127fb" + integrity sha512-JAUw1iXGO1qaWwEOzxTKJZ/5JxVeON9kvGZ/osgZaJImBnyjyn0cjovPsf6FNLmyGY8Vw9DoXZCMlfMkMwHRWg== + dependencies: + "@types/estree" "*" + "@types/tough-cookie@*", "@types/tough-cookie@^4.0.2": version "4.0.2" resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.2.tgz#6286b4c7228d58ab7866d19716f3696e03a09397" @@ -4133,7 +4494,7 @@ arg@^4.1.0: resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== -argparse@^1.0.7: +argparse@^1.0.10, argparse@^1.0.7: version "1.0.10" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== @@ -4180,6 +4541,15 @@ array-equal@^1.0.0: resolved "https://registry.yarnpkg.com/array-equal/-/array-equal-1.0.0.tgz#8c2a5ef2472fd9ea742b04c77a75093ba2757c93" integrity sha512-H3LU5RLiSsGXPhN+Nipar0iR0IofH+8r89G2y1tBKxQ/agagKyAjhkAFDRBfodP2caPrNKHpAWNIM/c9yeL7uA== +array-sort@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/array-sort/-/array-sort-1.0.0.tgz#e4c05356453f56f53512a7d1d6123f2c54c0a88a" + integrity sha512-ihLeJkonmdiAsD7vpgN3CRcx2J2S0TiYW+IS/5zHBI7mKUq3ySvBdzzBfD236ubDBQFiiyG3SWCPc+msQ9KoYg== + dependencies: + default-compare "^1.0.0" + get-value "^2.0.6" + kind-of "^5.0.2" + array-union@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" @@ -4323,6 +4693,13 @@ atomic-sleep@^1.0.0: resolved "https://registry.yarnpkg.com/atomic-sleep/-/atomic-sleep-1.0.0.tgz#eb85b77a601fc932cfe432c5acd364a9e2c9075b" integrity sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ== +autolinker@~0.28.0: + version "0.28.1" + resolved "https://registry.yarnpkg.com/autolinker/-/autolinker-0.28.1.tgz#0652b491881879f0775dace0cdca3233942a4e47" + integrity sha512-zQAFO1Dlsn69eXaO6+7YZc+v84aquQKbwpzCE3L0stj56ERn9hutFxPopViLjo9G+rWwjozRhgS5KJ25Xy19cQ== + dependencies: + gulp-header "^1.7.1" + available-typed-arrays@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7" @@ -5243,6 +5620,18 @@ co@^4.6.0: resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" integrity sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ== +codemirror-spell-checker@1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/codemirror-spell-checker/-/codemirror-spell-checker-1.1.2.tgz#1c660f9089483ccb5113b9ba9ca19c3f4993371e" + integrity sha512-2Tl6n0v+GJRsC9K3MLCdLaMOmvWL0uukajNJseorZJsslaxZyZMgENocPU8R0DyoTAiKsyqiemSOZo7kjGV0LQ== + dependencies: + typo-js "*" + +codemirror@^5.63.1: + version "5.65.11" + resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-5.65.11.tgz#c818edc3274788c008f636520c5490a1f7009b4f" + integrity sha512-Gp62g2eKSCHYt10axmGhKq3WoJSvVpvhXmowNq7pZdRVowwtvBR/hi2LSP5srtctKkRT33T6/n8Kv1UGp7JW4A== + collect-v8-coverage@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz#cc2c8e94fc18bbdffe64d6534570c8a673b27f59" @@ -5393,6 +5782,13 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== +concat-with-sourcemaps@*: + version "1.1.0" + resolved "https://registry.yarnpkg.com/concat-with-sourcemaps/-/concat-with-sourcemaps-1.1.0.tgz#d4ea93f05ae25790951b99e7b3b09e3908a4082e" + integrity sha512-4gEjHJFT9e+2W/77h/DS5SGUgwDaOwprX8L/gl5+3ixnzkVJJsZWDSelmN3Oilw3LNDZjZV0yqH1hLG3k6nghg== + dependencies: + source-map "^0.6.1" + condense-newlines@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/condense-newlines/-/condense-newlines-0.2.1.tgz#3de985553139475d32502c83b02f60684d24c55f" @@ -5787,6 +6183,13 @@ deepmerge@^4.2.2: resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== +default-compare@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/default-compare/-/default-compare-1.0.0.tgz#cb61131844ad84d84788fb68fd01681ca7781a2f" + integrity sha512-QWfXlM0EkAbqOCbD/6HjdwT19j7WCkMyiRhWilc4H9/5h/RzTF9gv5LYh1+CmDV5d1rki6KAWLtQale0xt20eQ== + dependencies: + kind-of "^5.0.2" + default-shell@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/default-shell/-/default-shell-1.0.1.tgz#752304bddc6174f49eb29cb988feea0b8813c8bc" @@ -5964,11 +6367,25 @@ doctrine@3.0.0, doctrine@^3.0.0: dependencies: esutils "^2.0.2" +dom-serializer@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-2.0.0.tgz#e41b802e1eedf9f6cae183ce5e622d789d7d8e53" + integrity sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg== + dependencies: + domelementtype "^2.3.0" + domhandler "^5.0.2" + entities "^4.2.0" + dom-walk@^0.1.0: version "0.1.2" resolved "https://registry.yarnpkg.com/dom-walk/-/dom-walk-0.1.2.tgz#0c548bef048f4d1f2a97249002236060daa3fd84" integrity sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w== +domelementtype@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d" + integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw== + domexception@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/domexception/-/domexception-1.0.1.tgz#937442644ca6a31261ef36e3ec677fe805582c90" @@ -5976,6 +6393,22 @@ domexception@^1.0.1: dependencies: webidl-conversions "^4.0.2" +domhandler@^5.0.1, domhandler@^5.0.2: + version "5.0.3" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-5.0.3.tgz#cc385f7f751f1d1fc650c21374804254538c7d31" + integrity sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w== + dependencies: + domelementtype "^2.3.0" + +domutils@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-3.0.1.tgz#696b3875238338cb186b6c0612bd4901c89a4f1c" + integrity sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q== + dependencies: + dom-serializer "^2.0.0" + domelementtype "^2.3.0" + domhandler "^5.0.1" + dot-prop@^5.2.0: version "5.3.0" resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.3.0.tgz#90ccce708cd9cd82cc4dc8c3ddd9abdd55b20e88" @@ -6020,6 +6453,11 @@ download@8.0.0: p-event "^2.1.0" pify "^4.0.1" +downloadjs@1.4.7: + version "1.4.7" + resolved "https://registry.yarnpkg.com/downloadjs/-/downloadjs-1.4.7.tgz#f69f96f940e0d0553dac291139865a3cd0101e3c" + integrity sha512-LN1gO7+u9xjU5oEScGFKvXhYf7Y/empUIIEAGBs1LzUq/rg5duiDrkuH5A2lQGd5jfMOb9X9usDa2oVXwJ0U/Q== + duplexer3@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" @@ -6035,6 +6473,17 @@ duplexify@^4.0.0: readable-stream "^3.1.1" stream-shift "^1.0.0" +easymde@^2.16.1: + version "2.18.0" + resolved "https://registry.yarnpkg.com/easymde/-/easymde-2.18.0.tgz#ff1397d07329b1a7b9187d2d0c20766fa16b3b1b" + integrity sha512-IxVVUxNWIoXLeqtBU4BLc+eS/ScYhT1Dcb6yF5Wchoj1iXAV+TIIDWx+NCaZhY7RcSHqDPKllbYq7nwGKILnoA== + dependencies: + "@types/codemirror" "^5.60.4" + "@types/marked" "^4.0.7" + codemirror "^5.63.1" + codemirror-spell-checker "1.1.2" + marked "^4.1.0" + ecc-jsbn@~0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" @@ -6197,6 +6646,17 @@ end-stream@~0.1.0: dependencies: write-stream "~0.4.3" +engine.io-client@~6.2.3: + version "6.2.3" + resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-6.2.3.tgz#a8cbdab003162529db85e9de31575097f6d29458" + integrity sha512-aXPtgF1JS3RuuKcpSrBtimSjYvrbhKW9froICH4s0F3XQWLxsKNxqzG39nnvQZQnva4CMvUK63T7shevxRyYHw== + dependencies: + "@socket.io/component-emitter" "~3.1.0" + debug "~4.3.1" + engine.io-parser "~5.0.3" + ws "~8.2.3" + xmlhttprequest-ssl "~2.0.0" + engine.io-parser@~5.0.3: version "5.0.4" resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-5.0.4.tgz#0b13f704fa9271b3ec4f33112410d8f3f41d0fc0" @@ -6226,6 +6686,16 @@ enhanced-resolve@^5.9.3: graceful-fs "^4.2.4" tapable "^2.2.0" +ent@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/ent/-/ent-2.2.0.tgz#e964219325a21d05f44466a2f686ed6ce5f5dd1d" + integrity sha512-GHrMyVZQWvTIdDtpiEXdHZnFQKzeO09apj8Cbl4pKWy4i0Oprcq17usfDt5aO63swf0JOeMWjWQE/LzgSRuWpA== + +entities@^4.2.0, entities@^4.3.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-4.4.0.tgz#97bdaba170339446495e653cfd2db78962900174" + integrity sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA== + entities@~2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/entities/-/entities-2.1.0.tgz#992d3129cf7df6870b96c57858c249a120f8b8b5" @@ -7231,6 +7701,11 @@ fs-constants@^1.0.0: resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== +fs-exists-sync@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/fs-exists-sync/-/fs-exists-sync-0.1.0.tgz#982d6893af918e72d08dec9e8673ff2b5a8d6add" + integrity sha512-cR/vflFyPZtrN6b38ZyWxpWdhlXrzZEBawlpBQMq7033xVY7/kg0GDMBK5jg8lDYQckdJ5x/YC88lM3C7VMsLg== + fs-extra@8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" @@ -7379,6 +7854,14 @@ get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1: has "^1.0.3" has-symbols "^1.0.3" +get-object@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/get-object/-/get-object-0.2.0.tgz#d92ff7d5190c64530cda0543dac63a3d47fe8c0c" + integrity sha512-7P6y6k6EzEFmO/XyUyFlXm1YLJy9xeA1x/grNV8276abX5GuwUtYgKFkRFkLixw4hf4Pz9q2vgv/8Ar42R0HuQ== + dependencies: + is-number "^2.0.2" + isobject "^0.2.0" + get-package-type@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" @@ -7441,6 +7924,13 @@ get-value@^2.0.3, get-value@^2.0.6: resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" integrity sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA== +get-value@^3.0.0, get-value@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/get-value/-/get-value-3.0.1.tgz#5efd2a157f1d6a516d7524e124ac52d0a39ef5a8" + integrity sha512-mKZj9JLQrwMBtj5wxi6MH8Z5eSKaERpAwjg43dPtlGI1ZVEgH/qC7T8/6R2OBSUA+zzHBZgICsVJaEIV2tKTDA== + dependencies: + isobject "^3.0.1" + getopts@2.2.5: version "2.2.5" resolved "https://registry.yarnpkg.com/getopts/-/getopts-2.2.5.tgz#67a0fe471cacb9c687d817cab6450b96dde8313b" @@ -7710,7 +8200,24 @@ gtoken@^5.0.4: google-p12-pem "^3.1.3" jws "^4.0.0" -handlebars@^4.7.7: +gulp-header@^1.7.1: + version "1.8.12" + resolved "https://registry.yarnpkg.com/gulp-header/-/gulp-header-1.8.12.tgz#ad306be0066599127281c4f8786660e705080a84" + integrity sha512-lh9HLdb53sC7XIZOYzTXM4lFuXElv3EVkSDhsd7DoJBj7hm+Ni7D3qYbb+Rr8DuM8nRanBvkVO9d7askreXGnQ== + dependencies: + concat-with-sourcemaps "*" + lodash.template "^4.4.0" + through2 "^2.0.0" + +handlebars-utils@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/handlebars-utils/-/handlebars-utils-1.0.6.tgz#cb9db43362479054782d86ffe10f47abc76357f9" + integrity sha512-d5mmoQXdeEqSKMtQQZ9WkiUcO1E3tPbWxluCK9hVgIDPzQa9WsKo3Lbe/sGflTe7TomHEeZaOgwIkyIr1kfzkw== + dependencies: + kind-of "^6.0.0" + typeof-article "^0.1.1" + +handlebars@^4.7.6, handlebars@^4.7.7: version "4.7.7" resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.7.tgz#9ce33416aad02dbd6c8fafa8240d5d98004945a1" integrity sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA== @@ -7804,6 +8311,14 @@ has-value@^1.0.0: has-values "^1.0.0" isobject "^3.0.0" +has-value@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-2.0.2.tgz#d0f12e8780ba8e90e66ad1a21c707fdb67c25658" + integrity sha512-ybKOlcRsK2MqrM3Hmz/lQxXHZ6ejzSPzpNabKB45jb5qDgJvKPa3SdapTsTLwEb9WltgWpOmNax7i+DzNOk4TA== + dependencies: + get-value "^3.0.0" + has-values "^2.0.1" + has-values@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" @@ -7817,6 +8332,13 @@ has-values@^1.0.0: is-number "^3.0.0" kind-of "^4.0.0" +has-values@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-2.0.1.tgz#3876200ff86d8a8546a9264a952c17d5fc17579d" + integrity sha512-+QdH3jOmq9P8GfdjFg0eJudqx1FqU62NQJ4P16rOEHeRdl7ckgwn6uqQjzYE0ZoHVV/e5E2esuJ5Gl5+HUW19w== + dependencies: + kind-of "^6.0.2" + has-yarn@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/has-yarn/-/has-yarn-2.1.0.tgz#137e11354a7b5bf11aa5cb649cf0c6f3ff2b2e77" @@ -7829,6 +8351,16 @@ has@^1.0.3: dependencies: function-bind "^1.1.1" +helper-md@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/helper-md/-/helper-md-0.2.2.tgz#c1f59d7e55bbae23362fd8a0e971607aec69d41f" + integrity sha512-49TaQzK+Ic7ZVTq4i1UZxRUJEmAilTk8hz7q4I0WNUaTclLR8ArJV5B3A1fe1xF2HtsDTr2gYKLaVTof/Lt84Q== + dependencies: + ent "^2.2.0" + extend-shallow "^2.0.1" + fs-exists-sync "^0.1.0" + remarkable "^1.6.2" + homedir-polyfill@^1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz#743298cef4e5af3e194161fbadcc2151d3a058e8" @@ -7858,6 +8390,29 @@ html-escaper@^2.0.0: resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== +html-tag@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/html-tag/-/html-tag-2.0.0.tgz#36c3bc8d816fd30b570d5764a497a641640c2fed" + integrity sha512-XxzooSo6oBoxBEUazgjdXj7VwTn/iSTSZzTYKzYY6I916tkaYzypHxy+pbVU1h+0UQ9JlVf5XkNQyxOAiiQO1g== + dependencies: + is-self-closing "^1.0.1" + kind-of "^6.0.0" + +html5-qrcode@^2.2.1: + version "2.3.4" + resolved "https://registry.yarnpkg.com/html5-qrcode/-/html5-qrcode-2.3.4.tgz#7e2b4575a23b10ff5e26d2bf147c8027c1ece389" + integrity sha512-VPZrOTG8XR9HmIAhSSiGtJVPErZxKy/DuGc9cPQLburCWZEbvxQGJP9y4K4P+8vdalLtYB/vM5YP1BdWQKZ8jQ== + +htmlparser2@^8.0.0: + version "8.0.1" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-8.0.1.tgz#abaa985474fcefe269bc761a779b544d7196d010" + integrity sha512-4lVbmc1diZC7GUJQtRQ5yBAeUCL1exyMwmForWkRLnwyzWBFxN633SALPMGYaWZvKe9j1pRZJpauvmxENSp/EA== + dependencies: + domelementtype "^2.3.0" + domhandler "^5.0.2" + domutils "^3.0.1" + entities "^4.3.0" + http-assert@^1.3.0: version "1.5.0" resolved "https://registry.yarnpkg.com/http-assert/-/http-assert-1.5.0.tgz#c389ccd87ac16ed2dfa6246fd73b926aa00e6b8f" @@ -8322,6 +8877,13 @@ is-docker@^2.0.0, is-docker@^2.1.1: resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== +is-even@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-even/-/is-even-1.0.0.tgz#76b5055fbad8d294a86b6a949015e1c97b717c06" + integrity sha512-LEhnkAdJqic4Dbqn58A0y52IXoHWlsueqQkKfMfdEnIYG8A1sm/GHidKkS6yvXlMoRrkM34csHnXQtOqcb+Jzg== + dependencies: + is-odd "^0.1.2" + is-extendable@^0.1.0, is-extendable@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" @@ -8428,6 +8990,13 @@ is-number-object@^1.0.4: dependencies: has-tostringtag "^1.0.0" +is-number@^2.0.2: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f" + integrity sha512-QUzH43Gfb9+5yckcrSA0VBDwEtDUchrk4F6tfJZQuNzDJbEDB9cZNzSfXGQ1jqmdDY/kl41lUOWM9syA8z8jlg== + dependencies: + kind-of "^3.0.2" + is-number@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" @@ -8450,6 +9019,13 @@ is-object@^1.0.1: resolved "https://registry.yarnpkg.com/is-object/-/is-object-1.0.2.tgz#a56552e1c665c9e950b4a025461da87e72f86fcf" integrity sha512-2rRIahhZr2UWb45fIOuvZGpFtz0TyOZLf32KxBbSoUCeZR495zCKlWUKKUByk3geS2eAs7ZAABt0Y/Rx0GiQGA== +is-odd@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/is-odd/-/is-odd-0.1.2.tgz#bc573b5ce371ef2aad6e6f49799b72bef13978a7" + integrity sha512-Ri7C2K7o5IrUU9UEI8losXJCCD/UtsaIrkR5sxIcFg4xQ9cRJXlWA5DQvTE0yDc0krvSNLsRGXN11UPS6KyfBw== + dependencies: + is-number "^3.0.0" + is-path-inside@^3.0.2: version "3.0.3" resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" @@ -8467,6 +9043,11 @@ is-plain-object@^2.0.3, is-plain-object@^2.0.4: dependencies: isobject "^3.0.1" +is-plain-object@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344" + integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q== + is-property@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84" @@ -8490,6 +9071,13 @@ is-retry-allowed@^2.2.0: resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-2.2.0.tgz#88f34cbd236e043e71b6932d09b0c65fb7b4d71d" integrity sha512-XVm7LOeLpTW4jV19QSH38vkswxoLud8sQ57YwJVTPWdiaI9I8keEhGFpBlslyVsgdQy4Opg8QOLb8YRgsyZiQg== +is-self-closing@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-self-closing/-/is-self-closing-1.0.1.tgz#5f406b527c7b12610176320338af0fa3896416e4" + integrity sha512-E+60FomW7Blv5GXTlYee2KDrnG6srxF7Xt1SjrhWUGUEsTFIqY/nq2y3DaftCsgUMdh89V07IVfhY9KIJhLezg== + dependencies: + self-closing-tags "^1.0.1" + is-shared-array-buffer@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz#8f259c573b60b6a32d4058a1a07430c0a7344c79" @@ -8600,6 +9188,11 @@ isexe@^2.0.0: resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== +isobject@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-0.2.0.tgz#a3432192f39b910b5f02cc989487836ec70aa85e" + integrity sha512-VaWq6XYAsbvM0wf4dyBO7WH9D7GosB7ZZlqrawI9BBiTMINBeCyqSKBa35m870MY3O4aM31pYyZi9DfGrYMJrQ== + isobject@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" @@ -9789,7 +10382,7 @@ keyv@^3.0.0: dependencies: json-buffer "3.0.0" -kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: +kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.1.0, kind-of@^3.2.0: version "3.2.2" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" integrity sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ== @@ -9803,12 +10396,12 @@ kind-of@^4.0.0: dependencies: is-buffer "^1.1.5" -kind-of@^5.0.0: +kind-of@^5.0.0, kind-of@^5.0.2: version "5.1.0" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw== -kind-of@^6.0.0, kind-of@^6.0.2: +kind-of@^6.0.0, kind-of@^6.0.2, kind-of@^6.0.3: version "6.0.3" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== @@ -10023,6 +10616,11 @@ lcid@^2.0.0: dependencies: invert-kv "^2.0.0" +leaflet@^1.7.1: + version "1.9.3" + resolved "https://registry.yarnpkg.com/leaflet/-/leaflet-1.9.3.tgz#52ec436954964e2d3d39e0d433da4b2500d74414" + integrity sha512-iB2cR9vAkDOu5l3HAay2obcUHZ7xwUBBjph8+PGtmW/2lYhbLizWtG7nTeYht36WfOslixQF9D/uSIzhZgGMfQ== + left-pad@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/left-pad/-/left-pad-1.3.0.tgz#5b8a3a7765dfe001261dde915589e782f8c94d1e" @@ -10217,6 +10815,11 @@ locate-path@^5.0.0: dependencies: p-locate "^4.1.0" +lodash._reinterpolate@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" + integrity sha512-xYHt68QRoYGjeeM/XOE1uJtvXQAgvszfBhjV4yvsQH0u2i9I6cI6c6/eG4Hh3UAOVn0y/xAXwmTzEay49Q//HA== + lodash.camelcase@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" @@ -10322,6 +10925,21 @@ lodash.sortby@^4.7.0: resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" integrity sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA== +lodash.template@^4.4.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-4.5.0.tgz#f976195cf3f347d0d5f52483569fe8031ccce8ab" + integrity sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A== + dependencies: + lodash._reinterpolate "^3.0.0" + lodash.templatesettings "^4.0.0" + +lodash.templatesettings@^4.0.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz#e481310f049d3cf6d47e912ad09313b154f0fb33" + integrity sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ== + dependencies: + lodash._reinterpolate "^3.0.0" + lodash.without@^4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/lodash.without/-/lodash.without-4.4.0.tgz#3cd4574a00b67bae373a94b748772640507b7aac" @@ -10332,7 +10950,7 @@ lodash.xor@^4.5.0: resolved "https://registry.yarnpkg.com/lodash.xor/-/lodash.xor-4.5.0.tgz#4d48ed7e98095b0632582ba714d3ff8ae8fb1db6" integrity sha512-sVN2zimthq7aZ5sPGXnSz32rZPuqcparVW50chJQe+mzTYV+IsxSsl/2gnkWWE2Of7K3myBQBqtLKOUEHJKRsQ== -lodash@4.17.21, lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.21, lodash@^4.17.3: +lodash@4.17.21, lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.3: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -10479,6 +11097,11 @@ markdown-it@^12.2.0: mdurl "^1.0.1" uc.micro "^1.0.5" +marked@^4.1.0: + version "4.2.12" + resolved "https://registry.yarnpkg.com/marked/-/marked-4.2.12.tgz#d69a64e21d71b06250da995dcd065c11083bebb5" + integrity sha512-yr8hSKa3Fv4D3jdZmtMMPghgVt6TWbk86WQaWhDloQjRSQhMMYCAro7jP7VDJrjjdV8pxVxMssXS8B8Y5DZ5aw== + matcher@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/matcher/-/matcher-3.0.0.tgz#bd9060f4c5b70aa8041ccc6f80368760994f30ca" @@ -10574,7 +11197,7 @@ methods@^1.1.1, methods@^1.1.2: resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== -micromatch@^3.1.10, micromatch@^3.1.4: +micromatch@^3.1.10, micromatch@^3.1.4, micromatch@^3.1.5: version "3.1.10" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== @@ -10872,6 +11495,16 @@ nan@^2.12.1: resolved "https://registry.yarnpkg.com/nan/-/nan-2.16.0.tgz#664f43e45460fb98faf00edca0bb0d7b8dce7916" integrity sha512-UdAqHyFngu7TfQKsCBgAA6pWDkT8MAO7d0jyOecVhN5354xbLqdn8mV9Tat9gepAupm0bt2DbeaSC8vS52MuFA== +nanoid@^2.1.0: + version "2.1.11" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-2.1.11.tgz#ec24b8a758d591561531b4176a01e3ab4f0f0280" + integrity sha512-s/snB+WGm6uwi0WjsZdaVcuf3KJXlfGl2LcxgwkEwJF0D/BWzVWAZW/XY4bFaiR7s0Jk3FPvlnepg1H1b1UwlA== + +nanoid@^3.3.4: + version "3.3.4" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab" + integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw== + nanomatch@^1.2.9: version "1.2.13" resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" @@ -11540,6 +12173,11 @@ parse-passwd@^1.0.0: resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6" integrity sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q== +parse-srcset@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/parse-srcset/-/parse-srcset-1.0.2.tgz#f2bd221f6cc970a938d88556abc589caaaa2bde1" + integrity sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q== + parse5@4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/parse5/-/parse5-4.0.0.tgz#6d78656e3da8d78b4ec0b906f7c08ef1dfe3f608" @@ -11888,6 +12526,15 @@ posix-character-classes@^0.1.0: resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" integrity sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg== +postcss@^8.3.11: + version "8.4.21" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.21.tgz#c639b719a57efc3187b13a1d765675485f4134f4" + integrity sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg== + dependencies: + nanoid "^3.3.4" + picocolors "^1.0.0" + source-map-js "^1.0.2" + postgres-array@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/postgres-array/-/postgres-array-2.0.0.tgz#48f8fce054fbc69671999329b8834b772652d82e" @@ -12659,6 +13306,16 @@ regexp.prototype.flags@^1.4.3: define-properties "^1.1.3" functions-have-names "^1.2.2" +regexparam@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/regexparam/-/regexparam-2.0.1.tgz#c912f5dae371e3798100b3c9ce22b7414d0889fa" + integrity sha512-zRgSaYemnNYxUv+/5SeoHI0eJIgTL/A2pUtXUPLHQxUldagouJ9p+K6IbIZ/JiQuCEv2E2B1O11SjVQy3aMCkw== + +regexparam@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/regexparam/-/regexparam-1.3.0.tgz#2fe42c93e32a40eff6235d635e0ffa344b92965f" + integrity sha512-6IQpFBv6e5vz1QAqI+V4k8P2e/3gRrqfCJ9FI+O1FLQTO+Uz6RXZEZOPmTJ6hlGj7gkERzY5BRCv09whKP96/g== + regexpp@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f" @@ -12707,6 +13364,21 @@ relative-microtime@^2.0.0: resolved "https://registry.yarnpkg.com/relative-microtime/-/relative-microtime-2.0.0.tgz#cceed2af095ecd72ea32011279c79e5fcc7de29b" integrity sha512-l18ha6HEZc+No/uK4GyAnNxgKW7nvEe35IaeN54sShMojtqik2a6GbTyuiezkjpPaqP874Z3lW5ysBo5irz4NA== +relative@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/relative/-/relative-3.0.2.tgz#0dcd8ec54a5d35a3c15e104503d65375b5a5367f" + integrity sha512-Q5W2qeYtY9GbiR8z1yHNZ1DGhyjb4AnLEjt8iE6XfcC1QIu+FAtj3HQaO0wH28H1mX6cqNLvAqWhP402dxJGyA== + dependencies: + isobject "^2.0.0" + +remarkable@^1.6.2: + version "1.7.4" + resolved "https://registry.yarnpkg.com/remarkable/-/remarkable-1.7.4.tgz#19073cb960398c87a7d6546eaa5e50d2022fcd00" + integrity sha512-e6NKUXgX95whv7IgddywbeN/ItCkWbISmc2DiqHJb0wTrqZIexqdco5b8Z3XZoo/48IdNVKM9ZCvTPJ4F5uvhg== + dependencies: + argparse "^1.0.10" + autolinker "~0.28.0" + remove-trailing-separator@^1.0.1: version "1.1.0" resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" @@ -12994,6 +13666,18 @@ sane@^4.0.3: minimist "^1.1.1" walker "~1.0.5" +sanitize-html@^2.7.0: + version "2.8.1" + resolved "https://registry.yarnpkg.com/sanitize-html/-/sanitize-html-2.8.1.tgz#319c4fdba67e1edf35b1fd6d9362210044826d47" + integrity sha512-qK5neD0SaMxGwVv5txOYv05huC3o6ZAA4h5+7nJJgWMNFUNRjcjLO6FpwAtKzfKCZ0jrG6xTk6eVFskbvOGblg== + dependencies: + deepmerge "^4.2.2" + escape-string-regexp "^4.0.0" + htmlparser2 "^8.0.0" + is-plain-object "^5.0.0" + parse-srcset "^1.0.2" + postcss "^8.3.11" + sanitize-s3-objectkey@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/sanitize-s3-objectkey/-/sanitize-s3-objectkey-0.0.1.tgz#efa9887cd45275b40234fb4bb12fc5754fe64e7e" @@ -13025,6 +13709,11 @@ schema-utils@^3.1.0, schema-utils@^3.1.1: ajv "^6.12.5" ajv-keywords "^3.5.2" +screenfull@^6.0.1: + version "6.0.2" + resolved "https://registry.yarnpkg.com/screenfull/-/screenfull-6.0.2.tgz#3dbe4b8c4f8f49fb8e33caa8f69d0bca730ab238" + integrity sha512-AQdy8s4WhNvUZ6P8F6PB21tSPIYKniic+Ogx0AacBMjKP1GUHN2E9URxQHtCusiwxudnCKkdy4GrHXPPJSkCCw== + search-params@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/search-params/-/search-params-3.0.0.tgz#dbc7c243058e5a33ae1e9870be91f5aced4100d8" @@ -13042,6 +13731,11 @@ seek-bzip@^1.0.5: dependencies: commander "^2.8.1" +self-closing-tags@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/self-closing-tags/-/self-closing-tags-1.0.1.tgz#6c5fa497994bb826b484216916371accee490a5d" + integrity sha512-7t6hNbYMxM+VHXTgJmxwgZgLGktuXtVVD5AivWzNTdJBM4DBjnDKDzkf2SrNjihaArpeJYNjxkELBu1evI4lQA== + semver-compare@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc" @@ -13194,6 +13888,13 @@ shimmer@^1.2.0: resolved "https://registry.yarnpkg.com/shimmer/-/shimmer-1.2.1.tgz#610859f7de327b587efebf501fb43117f9aff337" integrity sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw== +shortid@^2.2.15: + version "2.2.16" + resolved "https://registry.yarnpkg.com/shortid/-/shortid-2.2.16.tgz#b742b8f0cb96406fd391c76bfc18a67a57fe5608" + integrity sha512-Ugt+GIZqvGXCIItnsL+lvFJOiN7RYqlGy7QE41O3YC1xbNSeDGIRO7xg2JJXIAj1cAGnOeC1r7/T9pgrtQbv4g== + dependencies: + nanoid "^2.1.0" + side-channel@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" @@ -13334,7 +14035,17 @@ socket.io-adapter@~2.4.0: resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-2.4.0.tgz#b50a4a9ecdd00c34d4c8c808224daa1a786152a6" integrity sha512-W4N+o69rkMEGVuk2D/cvca3uYsvGlMwsySWV447y99gUPghxq42BxqLNMndb+a1mm/5/7NeXVQS7RLa2XyXvYg== -socket.io-parser@~4.2.0: +socket.io-client@^4.5.1: + version "4.5.4" + resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-4.5.4.tgz#d3cde8a06a6250041ba7390f08d2468ccebc5ac9" + integrity sha512-ZpKteoA06RzkD32IbqILZ+Cnst4xewU7ZYK12aS1mzHftFFjpoMz69IuhP/nL25pJfao/amoPI527KnuhFm01g== + dependencies: + "@socket.io/component-emitter" "~3.1.0" + debug "~4.3.2" + engine.io-client "~6.2.3" + socket.io-parser "~4.2.1" + +socket.io-parser@~4.2.0, socket.io-parser@~4.2.1: version "4.2.1" resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-4.2.1.tgz#01c96efa11ded938dcb21cbe590c26af5eff65e5" integrity sha512-V4GrkLy+HeF1F/en3SpUaM+7XxYXpuMUWLGde1kSSh5nQMN4hLrbPIkD+otwh6q9R6NOQBN4AMaOZ2zVjui82g== @@ -13396,6 +14107,11 @@ source-list-map@^2.0.1: resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== +source-map-js@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" + integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== + source-map-resolve@^0.5.0: version "0.5.3" resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" @@ -13795,6 +14511,11 @@ strip-outer@^1.0.0: dependencies: escape-string-regexp "^1.0.2" +striptags@^3.1.1: + version "3.2.0" + resolved "https://registry.yarnpkg.com/striptags/-/striptags-3.2.0.tgz#cc74a137db2de8b0b9a370006334161f7dd67052" + integrity sha512-g45ZOGzHDMe2bdYMdIvdAfCQkCTDMGBazSw1ypMowwGIee7ZQ5dU0rBJ8Jqgl+jAKIv4dbeE1jscZq9wid1Tkw== + style-loader@^3.3.1: version "3.3.1" resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-3.3.1.tgz#057dfa6b3d4d7c7064462830f9113ed417d38575" @@ -13894,11 +14615,23 @@ svelte-portal@^1.0.0: resolved "https://registry.yarnpkg.com/svelte-portal/-/svelte-portal-1.0.0.tgz#36a47c5578b1a4d9b4dc60fa32a904640ec4cdd3" integrity sha512-nHf+DS/jZ6jjnZSleBMSaZua9JlG5rZv9lOGKgJuaZStfevtjIlUJrkLc3vbV8QdBvPPVmvcjTlazAzfKu0v3Q== +svelte-spa-router@^3.0.5: + version "3.3.0" + resolved "https://registry.yarnpkg.com/svelte-spa-router/-/svelte-spa-router-3.3.0.tgz#2fc0967a49dc361dfe4d38dddad6e662eed5b42c" + integrity sha512-cwRNe7cxD43sCvSfEeaKiNZg3FCizGxeMcf7CPiWRP3jKXjEma3vxyyuDtPOam6nWbVxl9TNM3hlE/i87ZlqcQ== + dependencies: + regexparam "2.0.1" + svelte@3.49.0: version "3.49.0" resolved "https://registry.yarnpkg.com/svelte/-/svelte-3.49.0.tgz#5baee3c672306de1070c3b7888fc2204e36a4029" integrity sha512-+lmjic1pApJWDfPCpUUTc1m8azDqYCG1JN9YEngrx/hUyIcFJo6VZhj0A1Ai0wqoHcEIuQy+e9tk+4uDgdtsFA== +svelte@^3.46.2, svelte@^3.49.0: + version "3.55.1" + resolved "https://registry.yarnpkg.com/svelte/-/svelte-3.55.1.tgz#6f93b153e5248039906ce5fe196efdb9e05dfce8" + integrity sha512-S+87/P0Ve67HxKkEV23iCdAh/SX1xiSfjF1HOglno/YTbSTW7RniICMCofWGdJJbdjw3S+0PfFb1JtGfTXE0oQ== + svg.draggable.js@^2.2.2: version "2.2.2" resolved "https://registry.yarnpkg.com/svg.draggable.js/-/svg.draggable.js-2.2.2.tgz#c514a2f1405efb6f0263e7958f5b68fce50603ba" @@ -14251,6 +14984,11 @@ to-fast-properties@^2.0.0: resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4= +to-gfm-code-block@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/to-gfm-code-block/-/to-gfm-code-block-0.1.1.tgz#25d045a5fae553189e9637b590900da732d8aa82" + integrity sha512-LQRZWyn8d5amUKnfR9A9Uu7x9ss7Re8peuWR2gkh1E+ildOfv2aF26JpuDg8JtvCduu5+hOrMIH+XstZtnagqg== + to-json-schema@0.2.5: version "0.2.5" resolved "https://registry.yarnpkg.com/to-json-schema/-/to-json-schema-0.2.5.tgz#ef3c3f11ad64460dcfbdbafd0fd525d69d62a98f" @@ -14519,6 +15257,13 @@ typedarray-to-buffer@^3.1.5: dependencies: is-typedarray "^1.0.0" +typeof-article@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/typeof-article/-/typeof-article-0.1.1.tgz#9f07e733c3fbb646ffa9e61c08debacd460e06af" + integrity sha512-Vn42zdX3FhmUrzEmitX3iYyLb+Umwpmv8fkZRIknYh84lmdrwqZA5xYaoKiIj2Rc5i/5wcDrpUmZcbk1U51vTw== + dependencies: + kind-of "^3.1.0" + typeof@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/typeof/-/typeof-1.0.0.tgz#9c84403f2323ae5399167275497638ea1d2f2440" @@ -14529,6 +15274,11 @@ typescript@4.7.3: resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.7.3.tgz#8364b502d5257b540f9de4c40be84c98e23a129d" integrity sha512-WOkT3XYvrpXx4vMMqlD+8R8R37fZkjyLGlxavMc4iB8lrl8L0DeTcHbYgw/v0N/z9wAFsgBhcsF0ruoySS22mA== +typo-js@*: + version "1.2.2" + resolved "https://registry.yarnpkg.com/typo-js/-/typo-js-1.2.2.tgz#340484d81fe518e77c81a5a770162b14492f183b" + integrity sha512-C7pYBQK17EjSg8tVNY91KHdUt5Nf6FMJ+c3js076quPmBML57PmNMzAcIq/2kf/hSYtFABNDIYNYlJRl5BJhGw== + uc.micro@^1.0.1, uc.micro@^1.0.5: version "1.0.6" resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac" @@ -14879,6 +15629,14 @@ vm2@3.9.11: acorn "^8.7.0" acorn-walk "^8.2.0" +vm2@^3.9.4: + version "3.9.13" + resolved "https://registry.yarnpkg.com/vm2/-/vm2-3.9.13.tgz#774a1a3d73b9b90b1aa45bcc5f25e349f2eef649" + integrity sha512-0rvxpB8P8Shm4wX2EKOiMp7H2zq+HUE/UwodY0pCZXs9IffIKZq6vUti5OgkVCTakKo9e/fgO4X1fkwfjWxE3Q== + dependencies: + acorn "^8.7.0" + acorn-walk "^8.2.0" + vuvuzela@1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/vuvuzela/-/vuvuzela-1.0.3.tgz#3be145e58271c73ca55279dd851f12a682114b0b" @@ -15302,6 +16060,11 @@ xmlbuilder@~9.0.1: resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-9.0.7.tgz#132ee63d2ec5565c557e20f4c22df9aca686b10d" integrity sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0= +xmlhttprequest-ssl@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz#91360c86b914e67f44dce769180027c0da618c67" + integrity sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A== + xpath.js@~1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/xpath.js/-/xpath.js-1.1.0.tgz#3816a44ed4bb352091083d002a383dd5104a5ff1" @@ -15440,6 +16203,11 @@ yauzl@^2.4.2: buffer-crc32 "~0.2.3" fd-slicer "~1.1.0" +year@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/year/-/year-0.2.1.tgz#4083ae520a318b23ec86037f3000cb892bdf9bb0" + integrity sha512-9GnJUZ0QM4OgXuOzsKNzTJ5EOkums1Xc+3YQXp+Q+UxFjf7zLucp9dQ8QMIft0Szs1E1hUiXFim1OYfEKFq97w== + ylru@^1.2.0: version "1.3.2" resolved "https://registry.yarnpkg.com/ylru/-/ylru-1.3.2.tgz#0de48017473275a4cbdfc83a1eaf67c01af8a785" diff --git a/packages/types/src/documents/app/table.ts b/packages/types/src/documents/app/table.ts index 8223ce29c6..0f85e895ac 100644 --- a/packages/types/src/documents/app/table.ts +++ b/packages/types/src/documents/app/table.ts @@ -1,10 +1,10 @@ import { Document } from "../document" import { View } from "./view" import { RenameColumn } from "../../sdk" +import { FieldType } from "./row" export interface FieldSchema { - // TODO: replace with field types enum when done - type: string + type: FieldType externalType?: string fieldName?: string name: string From cc930097a83183d1da4c1777ebf0adc3e64996e0 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 17 Jan 2023 17:39:59 +0000 Subject: [PATCH 10/56] Implement test --- packages/server/src/integration-test/row.spec.ts | 13 +++++++++++-- .../server/src/tests/utilities/TestConfiguration.ts | 2 +- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/packages/server/src/integration-test/row.spec.ts b/packages/server/src/integration-test/row.spec.ts index 3c65152871..ece0090a5a 100644 --- a/packages/server/src/integration-test/row.spec.ts +++ b/packages/server/src/integration-test/row.spec.ts @@ -41,7 +41,7 @@ describe("row api", () => { describe("create a row", () => { test("Given than no row exists, adding a new rows persists it", async () => { const tableName = faker.lorem.word() - let table = await config.createTable({ + const table = await config.createTable({ name: tableName, schema: { name: { @@ -62,12 +62,21 @@ describe("row api", () => { const newRow = { name: faker.name.fullName(), description: faker.lorem.paragraphs(), - value: faker.random.numeric(), + value: +faker.random.numeric(), } const res = await makeRequest("post", `/tables/${table._id}/rows`, newRow) expect(res.status).toBe(200) + + const persistedRows = await config.getRows(table._id!) + expect(persistedRows).toHaveLength(1) + expect(persistedRows).toEqual([ + expect.objectContaining({ + ...res.body.data, + ...newRow, + }), + ]) }) }) }) diff --git a/packages/server/src/tests/utilities/TestConfiguration.ts b/packages/server/src/tests/utilities/TestConfiguration.ts index b9d4470c88..ce4fa287a0 100644 --- a/packages/server/src/tests/utilities/TestConfiguration.ts +++ b/packages/server/src/tests/utilities/TestConfiguration.ts @@ -413,7 +413,7 @@ class TestConfiguration { // TABLE - async updateTable(config?: any) { + async updateTable(config?: any): Promise { config = config || basicTable() this.table = await this._req(config, null, controllers.table.save) return this.table From 1d66110d0161c6df3e94da755548e61cb1062880 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 18 Jan 2023 10:45:42 +0000 Subject: [PATCH 11/56] More typings --- .../src/tests/utilities/TestConfiguration.ts | 20 ++++++++++--------- .../server/src/tests/utilities/structures.ts | 13 ++++++------ 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/packages/server/src/tests/utilities/TestConfiguration.ts b/packages/server/src/tests/utilities/TestConfiguration.ts index ce4fa287a0..03ed5ccee2 100644 --- a/packages/server/src/tests/utilities/TestConfiguration.ts +++ b/packages/server/src/tests/utilities/TestConfiguration.ts @@ -39,7 +39,7 @@ import newid from "../../db/newid" import { generateUserMetadataID } from "../../db/utils" import { startup } from "../../startup" import supertest from "supertest" -import { Table } from "@budibase/types" +import { Datasource, SourceName, Table } from "@budibase/types" const GLOBAL_USER_ID = "us_uuid1" const EMAIL = "babs@babs.com" @@ -554,9 +554,13 @@ class TestConfiguration { // DATASOURCE - async createDatasource(config?: any) { + async createDatasource(config?: Datasource): Promise { config = config || basicDatasource() - const response = await this._req(config, null, controllers.datasource.save) + const response = await this._req( + { datasource: config }, + null, + controllers.datasource.save + ) this.datasource = response.datasource return this.datasource } @@ -573,18 +577,16 @@ class TestConfiguration { async restDatasource(cfg?: any) { return this.createDatasource({ - datasource: { - ...basicDatasource().datasource, - source: "REST", - config: cfg || {}, - }, + ...basicDatasource(), + source: SourceName.REST, + config: cfg || {}, }) } async dynamicVariableDatasource() { let datasource = await this.restDatasource() const basedOnQuery = await this.createQuery({ - ...basicQuery(datasource._id), + ...basicQuery(datasource._id!), fields: { path: "www.google.com", }, diff --git a/packages/server/src/tests/utilities/structures.ts b/packages/server/src/tests/utilities/structures.ts index a412be4931..2e453e86e5 100644 --- a/packages/server/src/tests/utilities/structures.ts +++ b/packages/server/src/tests/utilities/structures.ts @@ -3,6 +3,7 @@ import { createHomeScreen } from "../../constants/screens" import { EMPTY_LAYOUT } from "../../constants/layouts" import { cloneDeep } from "lodash/fp" import { TRIGGER_DEFINITIONS, ACTION_DEFINITIONS } from "../../automations" +import { Datasource, SourceName } from "@budibase/types" const { v4: uuidv4 } = require("uuid") export const TENANT_ID = "default" @@ -144,14 +145,12 @@ export function basicRole() { } } -export function basicDatasource() { +export function basicDatasource(): Datasource { return { - datasource: { - type: "datasource", - name: "Test", - source: "POSTGRES", - config: {}, - }, + type: "datasource", + name: "Test", + source: SourceName.POSTGRES, + config: {}, } } From 6bc658c1ff0e74094299b59f72c63c8e4a28fe4b Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 18 Jan 2023 11:50:00 +0000 Subject: [PATCH 12/56] Fix types --- packages/server/src/constants/index.ts | 18 +----------------- .../db/defaultData/datasource_bb_default.ts | 8 ++++---- packages/server/src/integrations/oracle.ts | 2 +- .../src/tests/utilities/TestConfiguration.ts | 4 ++-- packages/types/src/documents/app/row.ts | 1 + 5 files changed, 9 insertions(+), 24 deletions(-) diff --git a/packages/server/src/constants/index.ts b/packages/server/src/constants/index.ts index eb4c6211c6..ef933769b1 100644 --- a/packages/server/src/constants/index.ts +++ b/packages/server/src/constants/index.ts @@ -1,4 +1,5 @@ import { objectStore, roles, constants } from "@budibase/backend-core" +export { FieldType as FieldTypes } from "@budibase/types" export enum FilterTypes { STRING = "string", @@ -22,23 +23,6 @@ export const NoEmptyFilterStrings = [ FilterTypes.NOT_CONTAINS, ] -export enum FieldTypes { - STRING = "string", - BARCODEQR = "barcodeqr", - LONGFORM = "longform", - OPTIONS = "options", - NUMBER = "number", - BOOLEAN = "boolean", - ARRAY = "array", - DATETIME = "datetime", - ATTACHMENT = "attachment", - LINK = "link", - FORMULA = "formula", - AUTO = "auto", - JSON = "json", - INTERNAL = "internal", -} - export const CanSwitchTypes = [ [exports.FieldTypes.JSON, exports.FieldTypes.ARRAY], [ diff --git a/packages/server/src/db/defaultData/datasource_bb_default.ts b/packages/server/src/db/defaultData/datasource_bb_default.ts index 027351139a..81d0488cda 100644 --- a/packages/server/src/db/defaultData/datasource_bb_default.ts +++ b/packages/server/src/db/defaultData/datasource_bb_default.ts @@ -190,7 +190,7 @@ export const DEFAULT_INVENTORY_TABLE_SCHEMA: Table = { }, } -export const DEFAULT_EMPLOYEE_TABLE_SCHEMA = { +export const DEFAULT_EMPLOYEE_TABLE_SCHEMA: Table = { _id: DEFAULT_EMPLOYEE_TABLE_ID, type: "internal", views: {}, @@ -287,7 +287,7 @@ export const DEFAULT_EMPLOYEE_TABLE_SCHEMA = { sortable: false, }, "Badge Photo": { - type: "attachment", + type: FieldTypes.ATTACHMENT, constraints: { type: FieldTypes.ARRAY, presence: false, @@ -466,7 +466,7 @@ export const DEFAULT_JOBS_TABLE_SCHEMA: Table = { // sortable: true, }, "Works End": { - type: "datetime", + type: FieldTypes.DATETIME, constraints: { type: "string", length: {}, @@ -480,7 +480,7 @@ export const DEFAULT_JOBS_TABLE_SCHEMA: Table = { ignoreTimezones: true, }, "Updated Price": { - type: "number", + type: FieldTypes.NUMBER, constraints: { type: "number", presence: false, diff --git a/packages/server/src/integrations/oracle.ts b/packages/server/src/integrations/oracle.ts index 9ec9c3f858..abf9bcbbe3 100644 --- a/packages/server/src/integrations/oracle.ts +++ b/packages/server/src/integrations/oracle.ts @@ -247,7 +247,7 @@ class OracleIntegration extends Sql implements DatasourcePlus { ) } - private internalConvertType(column: OracleColumn): { type: string } { + private internalConvertType(column: OracleColumn): { type: FieldTypes } { if (this.isBooleanType(column)) { return { type: FieldTypes.BOOLEAN } } diff --git a/packages/server/src/tests/utilities/TestConfiguration.ts b/packages/server/src/tests/utilities/TestConfiguration.ts index 03ed5ccee2..5fa7629d2a 100644 --- a/packages/server/src/tests/utilities/TestConfiguration.ts +++ b/packages/server/src/tests/utilities/TestConfiguration.ts @@ -373,11 +373,11 @@ class TestConfiguration { // clear any old app this.appId = null // @ts-ignore - await context.updateAppId(null) + context.updateAppId(null) this.app = await this._req({ name: appName }, null, controllers.app.create) this.appId = this.app.appId // @ts-ignore - await context.updateAppId(this.appId) + context.updateAppId(this.appId) // create production app this.prodApp = await this.publish() diff --git a/packages/types/src/documents/app/row.ts b/packages/types/src/documents/app/row.ts index b18c6ebee7..90c1e3fca8 100644 --- a/packages/types/src/documents/app/row.ts +++ b/packages/types/src/documents/app/row.ts @@ -14,6 +14,7 @@ export enum FieldType { AUTO = "auto", JSON = "json", INTERNAL = "internal", + BARCODEQR = "barcodeqr", } export interface RowAttachment { From e9361a58194e4bcb609e90ea8b2665ec5644ce49 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 18 Jan 2023 12:19:40 +0000 Subject: [PATCH 13/56] Use postgres as datasource --- .../server/src/integration-test/row.spec.ts | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/packages/server/src/integration-test/row.spec.ts b/packages/server/src/integration-test/row.spec.ts index ece0090a5a..44ca5a3d4d 100644 --- a/packages/server/src/integration-test/row.spec.ts +++ b/packages/server/src/integration-test/row.spec.ts @@ -6,15 +6,35 @@ import { import * as setup from "../api/routes/tests/utilities" import supertest from "supertest" -import { FieldType } from "@budibase/types" +import { Datasource, FieldType, SourceName } from "@budibase/types" const config = setup.getConfig() -let apiKey, table, app, makeRequest: MakeRequestResponse +let apiKey, + table, + app, + makeRequest: MakeRequestResponse, + postgresDatasource: Datasource beforeAll(async () => { app = await config.init() table = await config.updateTable() apiKey = await config.generateApiKey() + postgresDatasource = await config.createDatasource({ + type: "datasource", + source: SourceName.POSTGRES, + plus: true, + config: { + host: "192.168.1.98", + port: 54321, + database: "postgres", + user: "root", + password: "root", + schema: "public", + ssl: false, + rejectUnauthorized: false, + ca: false, + }, + }) makeRequest = generateMakeRequest(apiKey) }) @@ -41,6 +61,7 @@ describe("row api", () => { describe("create a row", () => { test("Given than no row exists, adding a new rows persists it", async () => { const tableName = faker.lorem.word() + const table = await config.createTable({ name: tableName, schema: { @@ -57,6 +78,7 @@ describe("row api", () => { type: FieldType.NUMBER, }, }, + sourceId: postgresDatasource._id, }) const newRow = { From 8e73814675c1cb26e7ae94cee66e5927560ddc84 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 18 Jan 2023 12:26:26 +0000 Subject: [PATCH 14/56] Add test for multiple rows --- .../server/src/integration-test/row.spec.ts | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/packages/server/src/integration-test/row.spec.ts b/packages/server/src/integration-test/row.spec.ts index 44ca5a3d4d..4863047265 100644 --- a/packages/server/src/integration-test/row.spec.ts +++ b/packages/server/src/integration-test/row.spec.ts @@ -100,5 +100,48 @@ describe("row api", () => { }), ]) }) + + test("Given than no row exists, multiple rows can be persisted", async () => { + const tableName = faker.lorem.word() + + const table = await config.createTable({ + name: tableName, + schema: { + name: { + name: "name", + type: FieldType.STRING, + }, + description: { + name: "description", + type: FieldType.STRING, + }, + value: { + name: "value", + type: FieldType.NUMBER, + }, + }, + sourceId: postgresDatasource._id, + }) + + const numberOfRows = 10 + const newRows = Array(numberOfRows).map(() => ({ + name: faker.name.fullName(), + description: faker.lorem.paragraphs(), + value: +faker.random.numeric(), + })) + + for (const newRow of newRows) { + const res = await makeRequest( + "post", + `/tables/${table._id}/rows`, + newRow + ) + expect(res.status).toBe(200) + } + + const persistedRows = await config.getRows(table._id!) + expect(persistedRows).toHaveLength(numberOfRows) + expect(persistedRows).toEqual(expect.arrayContaining(newRows)) + }) }) }) From 3bb85f6e467f527a19bf8570fde253aafd5fc520 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 18 Jan 2023 13:55:24 +0000 Subject: [PATCH 15/56] Clean code --- packages/server/src/integration-test/row.spec.ts | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/packages/server/src/integration-test/row.spec.ts b/packages/server/src/integration-test/row.spec.ts index 4863047265..3027c796bb 100644 --- a/packages/server/src/integration-test/row.spec.ts +++ b/packages/server/src/integration-test/row.spec.ts @@ -9,15 +9,10 @@ import supertest from "supertest" import { Datasource, FieldType, SourceName } from "@budibase/types" const config = setup.getConfig() -let apiKey, - table, - app, - makeRequest: MakeRequestResponse, - postgresDatasource: Datasource +let apiKey, makeRequest: MakeRequestResponse, postgresDatasource: Datasource beforeAll(async () => { - app = await config.init() - table = await config.updateTable() + await config.init() apiKey = await config.generateApiKey() postgresDatasource = await config.createDatasource({ type: "datasource", @@ -35,6 +30,7 @@ beforeAll(async () => { ca: false, }, }) + makeRequest = generateMakeRequest(apiKey) }) From 5313c51e0f3fb1a2ee0d9717508ffc559d4c5785 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 18 Jan 2023 16:06:45 +0000 Subject: [PATCH 16/56] Create an app before each test --- .../server/src/integration-test/row.spec.ts | 32 +++++++------------ 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/packages/server/src/integration-test/row.spec.ts b/packages/server/src/integration-test/row.spec.ts index 3027c796bb..c9686f4ae3 100644 --- a/packages/server/src/integration-test/row.spec.ts +++ b/packages/server/src/integration-test/row.spec.ts @@ -5,13 +5,12 @@ import { } from "../api/routes/public/tests/utils" import * as setup from "../api/routes/tests/utilities" -import supertest from "supertest" import { Datasource, FieldType, SourceName } from "@budibase/types" const config = setup.getConfig() let apiKey, makeRequest: MakeRequestResponse, postgresDatasource: Datasource -beforeAll(async () => { +beforeEach(async () => { await config.init() apiKey = await config.generateApiKey() postgresDatasource = await config.createDatasource({ @@ -35,25 +34,10 @@ beforeAll(async () => { }) afterAll(async () => { - require("../app").default await config.end() }) describe("row api", () => { - let request: supertest.SuperTest - let server: any - - beforeAll(() => { - server = require("../app").default - }) - afterAll(() => { - server.close() - }) - - beforeEach(async () => { - request = supertest(server) - }) - describe("create a row", () => { test("Given than no row exists, adding a new rows persists it", async () => { const tableName = faker.lorem.word() @@ -64,6 +48,9 @@ describe("row api", () => { name: { name: "name", type: FieldType.STRING, + constraints: { + presence: true, + }, }, description: { name: "description", @@ -106,6 +93,9 @@ describe("row api", () => { name: { name: "name", type: FieldType.STRING, + constraints: { + presence: true, + }, }, description: { name: "description", @@ -120,11 +110,11 @@ describe("row api", () => { }) const numberOfRows = 10 - const newRows = Array(numberOfRows).map(() => ({ + const newRows = Array(numberOfRows).fill({ name: faker.name.fullName(), description: faker.lorem.paragraphs(), value: +faker.random.numeric(), - })) + }) for (const newRow of newRows) { const res = await makeRequest( @@ -137,7 +127,9 @@ describe("row api", () => { const persistedRows = await config.getRows(table._id!) expect(persistedRows).toHaveLength(numberOfRows) - expect(persistedRows).toEqual(expect.arrayContaining(newRows)) + expect(persistedRows).toEqual( + expect.arrayContaining(newRows.map(expect.objectContaining)) + ) }) }) }) From 4ae43f1a96d4c650dcd49b89cad3d8ce669a52a5 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 18 Jan 2023 16:46:40 +0000 Subject: [PATCH 17/56] Retrieve row test --- .../server/src/integration-test/row.spec.ts | 125 +++++++++--------- .../src/tests/utilities/TestConfiguration.ts | 4 +- 2 files changed, 63 insertions(+), 66 deletions(-) diff --git a/packages/server/src/integration-test/row.spec.ts b/packages/server/src/integration-test/row.spec.ts index c9686f4ae3..7b176549d1 100644 --- a/packages/server/src/integration-test/row.spec.ts +++ b/packages/server/src/integration-test/row.spec.ts @@ -5,10 +5,13 @@ import { } from "../api/routes/public/tests/utils" import * as setup from "../api/routes/tests/utilities" -import { Datasource, FieldType, SourceName } from "@budibase/types" +import { Datasource, FieldType, SourceName, Table } from "@budibase/types" const config = setup.getConfig() -let apiKey, makeRequest: MakeRequestResponse, postgresDatasource: Datasource +let apiKey, + makeRequest: MakeRequestResponse, + postgresDatasource: Datasource, + postgresTable: Table beforeEach(async () => { await config.init() @@ -31,50 +34,56 @@ beforeEach(async () => { }) makeRequest = generateMakeRequest(apiKey) + + postgresTable = await config.createTable({ + name: faker.lorem.word(), + schema: { + name: { + name: "name", + type: FieldType.STRING, + constraints: { + presence: true, + }, + }, + description: { + name: "description", + type: FieldType.STRING, + }, + value: { + name: "value", + type: FieldType.NUMBER, + }, + }, + sourceId: postgresDatasource._id, + }) }) afterAll(async () => { await config.end() }) +function createRandomRow() { + return { + name: faker.name.fullName(), + description: faker.lorem.paragraphs(), + value: +faker.random.numeric(), + } +} + describe("row api", () => { describe("create a row", () => { test("Given than no row exists, adding a new rows persists it", async () => { - const tableName = faker.lorem.word() + const newRow = createRandomRow() - const table = await config.createTable({ - name: tableName, - schema: { - name: { - name: "name", - type: FieldType.STRING, - constraints: { - presence: true, - }, - }, - description: { - name: "description", - type: FieldType.STRING, - }, - value: { - name: "value", - type: FieldType.NUMBER, - }, - }, - sourceId: postgresDatasource._id, - }) - - const newRow = { - name: faker.name.fullName(), - description: faker.lorem.paragraphs(), - value: +faker.random.numeric(), - } - - const res = await makeRequest("post", `/tables/${table._id}/rows`, newRow) + const res = await makeRequest( + "post", + `/tables/${postgresTable._id}/rows`, + newRow + ) expect(res.status).toBe(200) - const persistedRows = await config.getRows(table._id!) + const persistedRows = await config.getRows(postgresTable._id!) expect(persistedRows).toHaveLength(1) expect(persistedRows).toEqual([ expect.objectContaining({ @@ -85,51 +94,39 @@ describe("row api", () => { }) test("Given than no row exists, multiple rows can be persisted", async () => { - const tableName = faker.lorem.word() - - const table = await config.createTable({ - name: tableName, - schema: { - name: { - name: "name", - type: FieldType.STRING, - constraints: { - presence: true, - }, - }, - description: { - name: "description", - type: FieldType.STRING, - }, - value: { - name: "value", - type: FieldType.NUMBER, - }, - }, - sourceId: postgresDatasource._id, - }) - const numberOfRows = 10 - const newRows = Array(numberOfRows).fill({ - name: faker.name.fullName(), - description: faker.lorem.paragraphs(), - value: +faker.random.numeric(), - }) + const newRows = Array(numberOfRows).fill(createRandomRow()) for (const newRow of newRows) { const res = await makeRequest( "post", - `/tables/${table._id}/rows`, + `/tables/${postgresTable._id}/rows`, newRow ) expect(res.status).toBe(200) } - const persistedRows = await config.getRows(table._id!) + const persistedRows = await config.getRows(postgresTable._id!) expect(persistedRows).toHaveLength(numberOfRows) expect(persistedRows).toEqual( expect.arrayContaining(newRows.map(expect.objectContaining)) ) }) }) + + describe("Retrieve a row", () => { + test("Given than a table have a single row, the row can be retrieved successfully", async () => { + const rowData = createRandomRow() + const row = await config.createRow(rowData) + + const res = await makeRequest( + "get", + `/tables/${postgresTable._id}/rows/${row._id}` + ) + + expect(res.status).toBe(200) + + expect(res.body.data).toEqual(expect.objectContaining(rowData)) + }) + }) }) diff --git a/packages/server/src/tests/utilities/TestConfiguration.ts b/packages/server/src/tests/utilities/TestConfiguration.ts index 5fa7629d2a..993d5a788f 100644 --- a/packages/server/src/tests/utilities/TestConfiguration.ts +++ b/packages/server/src/tests/utilities/TestConfiguration.ts @@ -39,7 +39,7 @@ import newid from "../../db/newid" import { generateUserMetadataID } from "../../db/utils" import { startup } from "../../startup" import supertest from "supertest" -import { Datasource, SourceName, Table } from "@budibase/types" +import { Datasource, Row, SourceName, Table } from "@budibase/types" const GLOBAL_USER_ID = "us_uuid1" const EMAIL = "babs@babs.com" @@ -463,7 +463,7 @@ class TestConfiguration { // ROW - async createRow(config: any = null) { + async createRow(config: any = null): Promise { if (!this.table) { throw "Test requires table to be configured." } From 6a1589ba649ce323f91e438baec6fa7db21378d5 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 18 Jan 2023 16:48:18 +0000 Subject: [PATCH 18/56] Adding more tests --- packages/server/src/integration-test/row.spec.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/packages/server/src/integration-test/row.spec.ts b/packages/server/src/integration-test/row.spec.ts index 7b176549d1..e7203cff45 100644 --- a/packages/server/src/integration-test/row.spec.ts +++ b/packages/server/src/integration-test/row.spec.ts @@ -128,5 +128,21 @@ describe("row api", () => { expect(res.body.data).toEqual(expect.objectContaining(rowData)) }) + + test("Given than a table have a multiple rows, a single row can be retrieved successfully", async () => { + await Promise.all(Array(5).map(() => config.createRow(createRandomRow()))) + const rowData = createRandomRow() + const row = await config.createRow(rowData) + await Promise.all(Array(2).map(() => config.createRow(createRandomRow()))) + + const res = await makeRequest( + "get", + `/tables/${postgresTable._id}/rows/${row._id}` + ) + + expect(res.status).toBe(200) + + expect(res.body.data).toEqual(expect.objectContaining(rowData)) + }) }) }) From ac9ad71a11ae03927d3497382ffdabbc5f43fbfc Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 18 Jan 2023 17:07:09 +0000 Subject: [PATCH 19/56] Dry tests --- .../server/src/integration-test/row.spec.ts | 135 ++++++++++-------- 1 file changed, 74 insertions(+), 61 deletions(-) diff --git a/packages/server/src/integration-test/row.spec.ts b/packages/server/src/integration-test/row.spec.ts index e7203cff45..d620b24548 100644 --- a/packages/server/src/integration-test/row.spec.ts +++ b/packages/server/src/integration-test/row.spec.ts @@ -6,73 +6,89 @@ import { import * as setup from "../api/routes/tests/utilities" import { Datasource, FieldType, SourceName, Table } from "@budibase/types" +import _ from "lodash" const config = setup.getConfig() -let apiKey, - makeRequest: MakeRequestResponse, - postgresDatasource: Datasource, - postgresTable: Table -beforeEach(async () => { - await config.init() - apiKey = await config.generateApiKey() - postgresDatasource = await config.createDatasource({ - type: "datasource", - source: SourceName.POSTGRES, - plus: true, - config: { - host: "192.168.1.98", - port: 54321, - database: "postgres", - user: "root", - password: "root", - schema: "public", - ssl: false, - rejectUnauthorized: false, - ca: false, - }, - }) +describe("row api - postgres", () => { + let apiKey, + makeRequest: MakeRequestResponse, + postgresDatasource: Datasource, + postgresTable: Table - makeRequest = generateMakeRequest(apiKey) + beforeEach(async () => { + await config.init() + apiKey = await config.generateApiKey() + postgresDatasource = await config.createDatasource({ + type: "datasource", + source: SourceName.POSTGRES, + plus: true, + config: { + host: "192.168.1.98", + port: 54321, + database: "postgres", + user: "root", + password: "root", + schema: "public", + ssl: false, + rejectUnauthorized: false, + ca: false, + }, + }) - postgresTable = await config.createTable({ - name: faker.lorem.word(), - schema: { - name: { - name: "name", - type: FieldType.STRING, - constraints: { - presence: true, + makeRequest = generateMakeRequest(apiKey) + + postgresTable = await config.createTable({ + name: faker.lorem.word(), + schema: { + name: { + name: "name", + type: FieldType.STRING, + constraints: { + presence: true, + }, + }, + description: { + name: "description", + type: FieldType.STRING, + }, + value: { + name: "value", + type: FieldType.NUMBER, }, }, - description: { - name: "description", - type: FieldType.STRING, - }, - value: { - name: "value", - type: FieldType.NUMBER, - }, - }, - sourceId: postgresDatasource._id, + sourceId: postgresDatasource._id, + }) }) -}) -afterAll(async () => { - await config.end() -}) + afterAll(async () => { + await config.end() + }) -function createRandomRow() { - return { - name: faker.name.fullName(), - description: faker.lorem.paragraphs(), - value: +faker.random.numeric(), + function createRandomRow() { + return { + name: faker.name.fullName(), + description: faker.lorem.paragraphs(), + value: +faker.random.numeric(), + } + } + + async function populateRows(count: number) { + return await Promise.all( + Array(count) + .fill({}) + .map(async () => { + const rowData = createRandomRow() + return { + rowData, + row: await config.createRow(rowData), + } + }) + ) } -} -describe("row api", () => { describe("create a row", () => { - test("Given than no row exists, adding a new rows persists it", async () => { + test("Given than no row exists, adding a new row persists it", async () => { const newRow = createRandomRow() const res = await makeRequest( @@ -114,10 +130,9 @@ describe("row api", () => { }) }) - describe("Retrieve a row", () => { + describe("retrieve a row", () => { test("Given than a table have a single row, the row can be retrieved successfully", async () => { - const rowData = createRandomRow() - const row = await config.createRow(rowData) + const [{ rowData, row }] = await populateRows(1) const res = await makeRequest( "get", @@ -130,10 +145,8 @@ describe("row api", () => { }) test("Given than a table have a multiple rows, a single row can be retrieved successfully", async () => { - await Promise.all(Array(5).map(() => config.createRow(createRandomRow()))) - const rowData = createRandomRow() - const row = await config.createRow(rowData) - await Promise.all(Array(2).map(() => config.createRow(createRandomRow()))) + const rows = await populateRows(10) + const { rowData, row } = _.sample(rows)! const res = await makeRequest( "get", From 233f54f0368c97c3bcfab745293376c166689108 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 18 Jan 2023 17:11:52 +0000 Subject: [PATCH 20/56] Add update test --- .../src/api/routes/public/tests/utils.ts | 2 +- .../server/src/integration-test/row.spec.ts | 31 +++++++++++++++++++ .../src/tests/utilities/TestConfiguration.ts | 2 +- 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/packages/server/src/api/routes/public/tests/utils.ts b/packages/server/src/api/routes/public/tests/utils.ts index 8e7c6e187c..8f798cb587 100644 --- a/packages/server/src/api/routes/public/tests/utils.ts +++ b/packages/server/src/api/routes/public/tests/utils.ts @@ -2,7 +2,7 @@ import * as setup from "../../tests/utilities" import { checkSlashesInUrl } from "../../../../utilities" import supertest from "supertest" -export type HttpMethod = "post" | "get" +export type HttpMethod = "post" | "get" | "put" export type MakeRequestResponse = ( method: HttpMethod, diff --git a/packages/server/src/integration-test/row.spec.ts b/packages/server/src/integration-test/row.spec.ts index d620b24548..27e413ea35 100644 --- a/packages/server/src/integration-test/row.spec.ts +++ b/packages/server/src/integration-test/row.spec.ts @@ -130,6 +130,37 @@ describe("row api - postgres", () => { }) }) + describe("update a row", () => { + test("Given than a row exists, updating it persists it", async () => { + let { rowData, row } = _.sample(await populateRows(10))! + + const newName = faker.random.words(3) + const newValue = +faker.random.numeric() + const updateRow = { + name: newName, + value: newValue, + } + + const res = await makeRequest( + "put", + `/tables/${postgresTable._id}/rows/${row._id}`, + updateRow + ) + + expect(res.status).toBe(200) + + const persistedRows = await config.getRow(postgresTable._id!, row._id!) + + expect(persistedRows).toEqual( + expect.objectContaining({ + ...res.body.data, + ...rowData, + ...updateRow, + }) + ) + }) + }) + describe("retrieve a row", () => { test("Given than a table have a single row, the row can be retrieved successfully", async () => { const [{ rowData, row }] = await populateRows(1) diff --git a/packages/server/src/tests/utilities/TestConfiguration.ts b/packages/server/src/tests/utilities/TestConfiguration.ts index 993d5a788f..74e580d8df 100644 --- a/packages/server/src/tests/utilities/TestConfiguration.ts +++ b/packages/server/src/tests/utilities/TestConfiguration.ts @@ -472,7 +472,7 @@ class TestConfiguration { return this._req(config, { tableId }, controllers.row.save) } - async getRow(tableId: string, rowId: string) { + async getRow(tableId: string, rowId: string): Promise { return this._req(null, { tableId, rowId }, controllers.row.find) } From 38e718b6f102561e5aa91a409defe33a5e59d1f0 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 18 Jan 2023 17:21:17 +0000 Subject: [PATCH 21/56] Test delete --- .../src/api/routes/public/tests/utils.ts | 2 +- .../server/src/integration-test/row.spec.ts | 26 +++++++++++++++++-- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/packages/server/src/api/routes/public/tests/utils.ts b/packages/server/src/api/routes/public/tests/utils.ts index 8f798cb587..5850fd9e2e 100644 --- a/packages/server/src/api/routes/public/tests/utils.ts +++ b/packages/server/src/api/routes/public/tests/utils.ts @@ -2,7 +2,7 @@ import * as setup from "../../tests/utilities" import { checkSlashesInUrl } from "../../../../utilities" import supertest from "supertest" -export type HttpMethod = "post" | "get" | "put" +export type HttpMethod = "post" | "get" | "put" | "delete" export type MakeRequestResponse = ( method: HttpMethod, diff --git a/packages/server/src/integration-test/row.spec.ts b/packages/server/src/integration-test/row.spec.ts index 27e413ea35..31f3a138f2 100644 --- a/packages/server/src/integration-test/row.spec.ts +++ b/packages/server/src/integration-test/row.spec.ts @@ -149,9 +149,9 @@ describe("row api - postgres", () => { expect(res.status).toBe(200) - const persistedRows = await config.getRow(postgresTable._id!, row._id!) + const persistedRow = await config.getRow(postgresTable._id!, row._id!) - expect(persistedRows).toEqual( + expect(persistedRow).toEqual( expect.objectContaining({ ...res.body.data, ...rowData, @@ -161,6 +161,28 @@ describe("row api - postgres", () => { }) }) + describe("delete a row", () => { + test("Given than a row exists, delete request removes it", async () => { + const numberOfInitialRows = 5 + let { row } = _.sample(await populateRows(numberOfInitialRows))! + + const res = await makeRequest( + "delete", + `/tables/${postgresTable._id}/rows/${row._id}` + ) + + expect(res.status).toBe(200) + + const persistedRows = await config.getRows(postgresTable._id!) + expect(persistedRows).toHaveLength(numberOfInitialRows - 1) + + expect(row._id).toBeDefined() + expect(persistedRows).not.toContain( + expect.objectContaining({ _id: row._id }) + ) + }) + }) + describe("retrieve a row", () => { test("Given than a table have a single row, the row can be retrieved successfully", async () => { const [{ rowData, row }] = await populateRows(1) From e13433557ad59c040e7db51ec3640fcd49aa31f2 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Thu, 19 Jan 2023 11:00:51 +0000 Subject: [PATCH 22/56] Add search test --- .../server/src/integration-test/row.spec.ts | 28 ++++++++++++++++++- packages/server/src/tests/jestSetup.ts | 11 +++++--- .../src/tests/utilities/TestConfiguration.ts | 2 +- 3 files changed, 35 insertions(+), 6 deletions(-) diff --git a/packages/server/src/integration-test/row.spec.ts b/packages/server/src/integration-test/row.spec.ts index 31f3a138f2..d71e2b2478 100644 --- a/packages/server/src/integration-test/row.spec.ts +++ b/packages/server/src/integration-test/row.spec.ts @@ -10,6 +10,8 @@ import _ from "lodash" const config = setup.getConfig() +jest.unmock("node-fetch") + describe("row api - postgres", () => { let apiKey, makeRequest: MakeRequestResponse, @@ -81,7 +83,10 @@ describe("row api - postgres", () => { const rowData = createRandomRow() return { rowData, - row: await config.createRow(rowData), + row: await config.createRow({ + tableId: postgresTable._id, + ...rowData, + }), } }) ) @@ -211,4 +216,25 @@ describe("row api - postgres", () => { expect(res.body.data).toEqual(expect.objectContaining(rowData)) }) }) + + describe("search for rows", () => { + test("Given than a table multiple rows, search without query returns all of them", async () => { + const rowsCount = 6 + const rows = await populateRows(rowsCount) + + const res = await makeRequest( + "post", + `/tables/${postgresTable._id}/rows/search` + ) + + expect(res.status).toBe(200) + + expect(res.body.data).toHaveLength(rowsCount) + expect(res.body.data).toEqual( + expect.arrayContaining( + rows.map(r => expect.objectContaining(r.rowData)) + ) + ) + }) + }) }) diff --git a/packages/server/src/tests/jestSetup.ts b/packages/server/src/tests/jestSetup.ts index fc1477a500..72ea3282c8 100644 --- a/packages/server/src/tests/jestSetup.ts +++ b/packages/server/src/tests/jestSetup.ts @@ -1,9 +1,12 @@ import { mocks } from "@budibase/backend-core/tests" +import env from "../environment" -// mock all dates to 2020-01-01T00:00:00.000Z -// use tk.reset() to use real dates in individual tests -const tk = require("timekeeper") -tk.freeze(mocks.date.MOCK_DATE) +if (!env.isDockerisedTest()) { + // mock all dates to 2020-01-01T00:00:00.000Z + // use tk.reset() to use real dates in individual tests + const tk = require("timekeeper") + tk.freeze(mocks.date.MOCK_DATE) +} if (!process.env.DEBUG) { global.console.log = jest.fn() // console.log are ignored in tests diff --git a/packages/server/src/tests/utilities/TestConfiguration.ts b/packages/server/src/tests/utilities/TestConfiguration.ts index 74e580d8df..2934bafbf2 100644 --- a/packages/server/src/tests/utilities/TestConfiguration.ts +++ b/packages/server/src/tests/utilities/TestConfiguration.ts @@ -463,7 +463,7 @@ class TestConfiguration { // ROW - async createRow(config: any = null): Promise { + async createRow(config?: Row): Promise { if (!this.table) { throw "Test requires table to be configured." } From e6dcc47240bb83920db142736214cc20f4a6f148 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Thu, 19 Jan 2023 11:06:41 +0000 Subject: [PATCH 23/56] Improve tests --- .../server/src/integration-test/row.spec.ts | 33 +++++++++++++++++-- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/packages/server/src/integration-test/row.spec.ts b/packages/server/src/integration-test/row.spec.ts index d71e2b2478..afe55c0741 100644 --- a/packages/server/src/integration-test/row.spec.ts +++ b/packages/server/src/integration-test/row.spec.ts @@ -75,7 +75,7 @@ describe("row api - postgres", () => { } } - async function populateRows(count: number) { + async function populateRows(count: number, tableId?: string) { return await Promise.all( Array(count) .fill({}) @@ -84,7 +84,7 @@ describe("row api - postgres", () => { return { rowData, row: await config.createRow({ - tableId: postgresTable._id, + tableId: tableId || postgresTable._id, ...rowData, }), } @@ -218,7 +218,18 @@ describe("row api - postgres", () => { }) describe("search for rows", () => { - test("Given than a table multiple rows, search without query returns all of them", async () => { + test("Given than a table has no rows, search without query returns empty", async () => { + const res = await makeRequest( + "post", + `/tables/${postgresTable._id}/rows/search` + ) + + expect(res.status).toBe(200) + + expect(res.body.data).toHaveLength(0) + }) + + test("Given than a table has multiple rows, search without query returns all of them", async () => { const rowsCount = 6 const rows = await populateRows(rowsCount) @@ -236,5 +247,21 @@ describe("row api - postgres", () => { ) ) }) + + test("Given than multiple tables have multiple rows, search only return the requested ones", async () => { + await populateRows(2, (await config.createTable())._id) + const rowsCount = 6 + await populateRows(rowsCount) + await populateRows(2, (await config.createTable())._id) + + const res = await makeRequest( + "post", + `/tables/${postgresTable._id}/rows/search` + ) + + expect(res.status).toBe(200) + + expect(res.body.data).toHaveLength(rowsCount) + }) }) }) From 2a6d9215212e1e0064af2ac080f274489b8f546b Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Thu, 19 Jan 2023 11:20:20 +0000 Subject: [PATCH 24/56] Add search query tests --- .../server/src/integration-test/row.spec.ts | 114 ++++++++++++------ 1 file changed, 80 insertions(+), 34 deletions(-) diff --git a/packages/server/src/integration-test/row.spec.ts b/packages/server/src/integration-test/row.spec.ts index afe55c0741..5b179bf43c 100644 --- a/packages/server/src/integration-test/row.spec.ts +++ b/packages/server/src/integration-test/row.spec.ts @@ -218,50 +218,96 @@ describe("row api - postgres", () => { }) describe("search for rows", () => { - test("Given than a table has no rows, search without query returns empty", async () => { - const res = await makeRequest( - "post", - `/tables/${postgresTable._id}/rows/search` - ) - - expect(res.status).toBe(200) - - expect(res.body.data).toHaveLength(0) - }) - - test("Given than a table has multiple rows, search without query returns all of them", async () => { - const rowsCount = 6 - const rows = await populateRows(rowsCount) - - const res = await makeRequest( - "post", - `/tables/${postgresTable._id}/rows/search` - ) - - expect(res.status).toBe(200) - - expect(res.body.data).toHaveLength(rowsCount) - expect(res.body.data).toEqual( - expect.arrayContaining( - rows.map(r => expect.objectContaining(r.rowData)) + describe("empty search", () => { + test("Given than a table has no rows, search without query returns empty", async () => { + const res = await makeRequest( + "post", + `/tables/${postgresTable._id}/rows/search` ) - ) + + expect(res.status).toBe(200) + + expect(res.body.data).toHaveLength(0) + }) + + test("Given than a table has multiple rows, search without query returns all of them", async () => { + const rowsCount = 6 + const rows = await populateRows(rowsCount) + + const res = await makeRequest( + "post", + `/tables/${postgresTable._id}/rows/search` + ) + + expect(res.status).toBe(200) + + expect(res.body.data).toHaveLength(rowsCount) + expect(res.body.data).toEqual( + expect.arrayContaining( + rows.map(r => expect.objectContaining(r.rowData)) + ) + ) + }) + + test("Given than multiple tables have multiple rows, search only return the requested ones", async () => { + await populateRows(2, (await config.createTable())._id) + const rowsCount = 6 + await populateRows(rowsCount) + await populateRows(2, (await config.createTable())._id) + + const res = await makeRequest( + "post", + `/tables/${postgresTable._id}/rows/search` + ) + + expect(res.status).toBe(200) + + expect(res.body.data).toHaveLength(rowsCount) + }) }) - test("Given than multiple tables have multiple rows, search only return the requested ones", async () => { - await populateRows(2, (await config.createTable())._id) - const rowsCount = 6 - await populateRows(rowsCount) - await populateRows(2, (await config.createTable())._id) + test("Given than a table has multiple rows, querying by a string field returns the rows with field containing or starting by that value", async () => { + const name = faker.name.fullName() + const rowsToFilter = [ + ...Array(2).fill({ + name, + description: faker.lorem.paragraphs(), + value: +faker.random.numeric(), + }), + ...Array(2).fill({ + name: `${name}${faker.random.alphaNumeric(5)}`, + description: faker.lorem.paragraphs(), + value: +faker.random.numeric(), + }), + ] + + await populateRows(3) + for (const row of rowsToFilter) { + await config.createRow({ + tableId: postgresTable._id, + ...row, + }) + } + await populateRows(1) const res = await makeRequest( "post", - `/tables/${postgresTable._id}/rows/search` + `/tables/${postgresTable._id}/rows/search`, + { + query: { + string: { + name, + }, + }, + } ) expect(res.status).toBe(200) - expect(res.body.data).toHaveLength(rowsCount) + expect(res.body.data).toHaveLength(4) + expect(res.body.data).toEqual( + expect.arrayContaining(rowsToFilter.map(expect.objectContaining)) + ) }) }) }) From 86c2f6dce786248772d1aeec61cf0bec90c77326 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Thu, 19 Jan 2023 11:35:00 +0000 Subject: [PATCH 25/56] Add pagination and sort tests --- .../server/src/integration-test/row.spec.ts | 157 +++++++++++++++++- 1 file changed, 148 insertions(+), 9 deletions(-) diff --git a/packages/server/src/integration-test/row.spec.ts b/packages/server/src/integration-test/row.spec.ts index 5b179bf43c..a6860da646 100644 --- a/packages/server/src/integration-test/row.spec.ts +++ b/packages/server/src/integration-test/row.spec.ts @@ -75,6 +75,20 @@ describe("row api - postgres", () => { } } + function createRow( + row: { + name: string + description: string + value: number + }, + tableId?: string + ) { + return config.createRow({ + tableId: tableId || postgresTable._id, + ...row, + }) + } + async function populateRows(count: number, tableId?: string) { return await Promise.all( Array(count) @@ -83,10 +97,7 @@ describe("row api - postgres", () => { const rowData = createRandomRow() return { rowData, - row: await config.createRow({ - tableId: tableId || postgresTable._id, - ...rowData, - }), + row: await createRow(rowData, tableId || postgresTable._id), } }) ) @@ -266,7 +277,7 @@ describe("row api - postgres", () => { }) }) - test("Given than a table has multiple rows, querying by a string field returns the rows with field containing or starting by that value", async () => { + test("Querying by a string field returns the rows with field containing or starting by that value", async () => { const name = faker.name.fullName() const rowsToFilter = [ ...Array(2).fill({ @@ -283,10 +294,7 @@ describe("row api - postgres", () => { await populateRows(3) for (const row of rowsToFilter) { - await config.createRow({ - tableId: postgresTable._id, - ...row, - }) + await createRow(row, postgresTable._id) } await populateRows(1) @@ -309,5 +317,136 @@ describe("row api - postgres", () => { expect.arrayContaining(rowsToFilter.map(expect.objectContaining)) ) }) + + test("Querying respects the limit fields", async () => { + await populateRows(6) + + const res = await makeRequest( + "post", + `/tables/${postgresTable._id}/rows/search`, + { + limit: 2, + } + ) + + expect(res.status).toBe(200) + + expect(res.body.data).toHaveLength(2) + }) + + describe("sort", () => { + beforeEach(async () => { + const defaultValue = createRandomRow() + + await createRow( + { + ...defaultValue, + name: "d", + value: 3, + }, + postgresTable._id + ) + await createRow( + { ...defaultValue, name: "aaa", value: 40 }, + postgresTable._id + ) + await createRow( + { ...defaultValue, name: "ccccc", value: -5 }, + postgresTable._id + ) + await createRow( + { ...defaultValue, name: "bb", value: 0 }, + postgresTable._id + ) + }) + + test("Querying respects the sort order when sorting ascending by a string value", async () => { + const res = await makeRequest( + "post", + `/tables/${postgresTable._id}/rows/search`, + { + sort: { + order: "ascending", + column: "name", + type: "string", + }, + } + ) + + expect(res.status).toBe(200) + expect(res.body.data).toEqual([ + expect.objectContaining({ name: "aaa" }), + expect.objectContaining({ name: "bb" }), + expect.objectContaining({ name: "ccccc" }), + expect.objectContaining({ name: "d" }), + ]) + }) + + test("Querying respects the sort order when sorting descending by a string value", async () => { + const res = await makeRequest( + "post", + `/tables/${postgresTable._id}/rows/search`, + { + sort: { + order: "descending", + column: "name", + type: "string", + }, + } + ) + + expect(res.status).toBe(200) + expect(res.body.data).toEqual([ + expect.objectContaining({ name: "d" }), + expect.objectContaining({ name: "ccccc" }), + expect.objectContaining({ name: "bb" }), + expect.objectContaining({ name: "aaa" }), + ]) + }) + + test("Querying respects the sort order when sorting ascending by a numeric value", async () => { + const res = await makeRequest( + "post", + `/tables/${postgresTable._id}/rows/search`, + { + sort: { + order: "ascending", + column: "value", + type: "number", + }, + } + ) + + expect(res.status).toBe(200) + expect(res.body.data).toEqual([ + expect.objectContaining({ value: -5 }), + expect.objectContaining({ value: 0 }), + expect.objectContaining({ value: 3 }), + expect.objectContaining({ value: 40 }), + ]) + }) + + test("Querying respects the sort order when sorting descending by a numeric value", async () => { + const res = await makeRequest( + "post", + `/tables/${postgresTable._id}/rows/search`, + { + sort: { + order: "descending", + column: "value", + type: "number", + }, + } + ) + + expect(res.status).toBe(200) + expect(res.body.data).toEqual([ + expect.objectContaining({ value: 40 }), + expect.objectContaining({ value: 3 }), + expect.objectContaining({ value: 0 }), + expect.objectContaining({ value: -5 }), + ]) + }) + }) }) }) From f4553bca7c717ee37fdc63f936e581d210a8420a Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Thu, 19 Jan 2023 11:44:48 +0000 Subject: [PATCH 26/56] Add "get" tests --- .../server/src/integration-test/row.spec.ts | 158 ++++++++++-------- 1 file changed, 85 insertions(+), 73 deletions(-) diff --git a/packages/server/src/integration-test/row.spec.ts b/packages/server/src/integration-test/row.spec.ts index a6860da646..189e3fee8f 100644 --- a/packages/server/src/integration-test/row.spec.ts +++ b/packages/server/src/integration-test/row.spec.ts @@ -229,12 +229,12 @@ describe("row api - postgres", () => { }) describe("search for rows", () => { + const search = (tableId: string | undefined, body?: object) => + makeRequest("post", `/tables/${postgresTable._id}/rows/search`, body) + describe("empty search", () => { test("Given than a table has no rows, search without query returns empty", async () => { - const res = await makeRequest( - "post", - `/tables/${postgresTable._id}/rows/search` - ) + const res = await search(postgresTable._id) expect(res.status).toBe(200) @@ -245,10 +245,7 @@ describe("row api - postgres", () => { const rowsCount = 6 const rows = await populateRows(rowsCount) - const res = await makeRequest( - "post", - `/tables/${postgresTable._id}/rows/search` - ) + const res = await search(postgresTable._id) expect(res.status).toBe(200) @@ -266,10 +263,7 @@ describe("row api - postgres", () => { await populateRows(rowsCount) await populateRows(2, (await config.createTable())._id) - const res = await makeRequest( - "post", - `/tables/${postgresTable._id}/rows/search` - ) + const res = await search(postgresTable._id) expect(res.status).toBe(200) @@ -298,17 +292,13 @@ describe("row api - postgres", () => { } await populateRows(1) - const res = await makeRequest( - "post", - `/tables/${postgresTable._id}/rows/search`, - { - query: { - string: { - name, - }, + const res = await search(postgresTable._id, { + query: { + string: { + name, }, - } - ) + }, + }) expect(res.status).toBe(200) @@ -321,13 +311,9 @@ describe("row api - postgres", () => { test("Querying respects the limit fields", async () => { await populateRows(6) - const res = await makeRequest( - "post", - `/tables/${postgresTable._id}/rows/search`, - { - limit: 2, - } - ) + const res = await search(postgresTable._id, { + limit: 2, + }) expect(res.status).toBe(200) @@ -361,17 +347,13 @@ describe("row api - postgres", () => { }) test("Querying respects the sort order when sorting ascending by a string value", async () => { - const res = await makeRequest( - "post", - `/tables/${postgresTable._id}/rows/search`, - { - sort: { - order: "ascending", - column: "name", - type: "string", - }, - } - ) + const res = await search(postgresTable._id, { + sort: { + order: "ascending", + column: "name", + type: "string", + }, + }) expect(res.status).toBe(200) expect(res.body.data).toEqual([ @@ -383,17 +365,13 @@ describe("row api - postgres", () => { }) test("Querying respects the sort order when sorting descending by a string value", async () => { - const res = await makeRequest( - "post", - `/tables/${postgresTable._id}/rows/search`, - { - sort: { - order: "descending", - column: "name", - type: "string", - }, - } - ) + const res = await search(postgresTable._id, { + sort: { + order: "descending", + column: "name", + type: "string", + }, + }) expect(res.status).toBe(200) expect(res.body.data).toEqual([ @@ -405,17 +383,13 @@ describe("row api - postgres", () => { }) test("Querying respects the sort order when sorting ascending by a numeric value", async () => { - const res = await makeRequest( - "post", - `/tables/${postgresTable._id}/rows/search`, - { - sort: { - order: "ascending", - column: "value", - type: "number", - }, - } - ) + const res = await search(postgresTable._id, { + sort: { + order: "ascending", + column: "value", + type: "number", + }, + }) expect(res.status).toBe(200) expect(res.body.data).toEqual([ @@ -427,17 +401,13 @@ describe("row api - postgres", () => { }) test("Querying respects the sort order when sorting descending by a numeric value", async () => { - const res = await makeRequest( - "post", - `/tables/${postgresTable._id}/rows/search`, - { - sort: { - order: "descending", - column: "value", - type: "number", - }, - } - ) + const res = await search(postgresTable._id, { + sort: { + order: "descending", + column: "value", + type: "number", + }, + }) expect(res.status).toBe(200) expect(res.body.data).toEqual([ @@ -449,4 +419,46 @@ describe("row api - postgres", () => { }) }) }) + + describe("get all rows", () => { + const getAll = (tableId: string | undefined) => + makeRequest("get", `/tables/${postgresTable._id}/rows`) + + test("Given than a table has no rows, get returns empty", async () => { + const res = await getAll(postgresTable._id) + + expect(res.status).toBe(200) + + expect(res.body.data).toHaveLength(0) + }) + + test("Given than a table has multiple rows, get returns all of them", async () => { + const rowsCount = 6 + const rows = await populateRows(rowsCount) + + const res = await getAll(postgresTable._id) + + expect(res.status).toBe(200) + + expect(res.body.data).toHaveLength(rowsCount) + expect(res.body.data).toEqual( + expect.arrayContaining( + rows.map(r => expect.objectContaining(r.rowData)) + ) + ) + }) + + test("Given than multiple tables have multiple rows, get returns the requested ones", async () => { + await populateRows(2, (await config.createTable())._id) + const rowsCount = 6 + await populateRows(rowsCount) + await populateRows(2, (await config.createTable())._id) + + const res = await getAll(postgresTable._id) + + expect(res.status).toBe(200) + + expect(res.body.data).toHaveLength(rowsCount) + }) + }) }) From 2bc45336fc1b526e6f1d1e1f97226c89ea7c24ea Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Thu, 19 Jan 2023 16:10:29 +0000 Subject: [PATCH 27/56] Use right tableid --- packages/server/src/integration-test/row.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/src/integration-test/row.spec.ts b/packages/server/src/integration-test/row.spec.ts index 189e3fee8f..f4c2d23eb8 100644 --- a/packages/server/src/integration-test/row.spec.ts +++ b/packages/server/src/integration-test/row.spec.ts @@ -230,7 +230,7 @@ describe("row api - postgres", () => { describe("search for rows", () => { const search = (tableId: string | undefined, body?: object) => - makeRequest("post", `/tables/${postgresTable._id}/rows/search`, body) + makeRequest("post", `/tables/${tableId}/rows/search`, body) describe("empty search", () => { test("Given than a table has no rows, search without query returns empty", async () => { From 959b4fb2b395d7768c6da52055f0660906abc945 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Thu, 19 Jan 2023 16:21:37 +0000 Subject: [PATCH 28/56] Rename file --- .../{row.spec.ts => postgres.spec.ts} | 43 ++++++------------- 1 file changed, 13 insertions(+), 30 deletions(-) rename packages/server/src/integration-test/{row.spec.ts => postgres.spec.ts} (90%) diff --git a/packages/server/src/integration-test/row.spec.ts b/packages/server/src/integration-test/postgres.spec.ts similarity index 90% rename from packages/server/src/integration-test/row.spec.ts rename to packages/server/src/integration-test/postgres.spec.ts index f4c2d23eb8..efc1ca33a5 100644 --- a/packages/server/src/integration-test/row.spec.ts +++ b/packages/server/src/integration-test/postgres.spec.ts @@ -103,6 +103,19 @@ describe("row api - postgres", () => { ) } + test.only("validate schema", async () => { + const res = await makeRequest( + "get", + `api/datasources/${postgresDatasource._id}`, + undefined, + undefined, + true + ) + + expect(res.status).toBe(200) + expect(res.body).toEqual({}) + }) + describe("create a row", () => { test("Given than no row exists, adding a new row persists it", async () => { const newRow = createRandomRow() @@ -420,45 +433,15 @@ describe("row api - postgres", () => { }) }) - describe("get all rows", () => { - const getAll = (tableId: string | undefined) => - makeRequest("get", `/tables/${postgresTable._id}/rows`) - test("Given than a table has no rows, get returns empty", async () => { - const res = await getAll(postgresTable._id) - expect(res.status).toBe(200) - expect(res.body.data).toHaveLength(0) - }) - test("Given than a table has multiple rows, get returns all of them", async () => { - const rowsCount = 6 - const rows = await populateRows(rowsCount) - const res = await getAll(postgresTable._id) - expect(res.status).toBe(200) - expect(res.body.data).toHaveLength(rowsCount) - expect(res.body.data).toEqual( - expect.arrayContaining( - rows.map(r => expect.objectContaining(r.rowData)) - ) - ) - }) - test("Given than multiple tables have multiple rows, get returns the requested ones", async () => { - await populateRows(2, (await config.createTable())._id) - const rowsCount = 6 - await populateRows(rowsCount) - await populateRows(2, (await config.createTable())._id) - const res = await getAll(postgresTable._id) - expect(res.status).toBe(200) - expect(res.body.data).toHaveLength(rowsCount) - }) - }) }) From bf8e65fa3e3ec30cf1605b63a0fa5bc2154af20e Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Thu, 19 Jan 2023 16:43:39 +0000 Subject: [PATCH 29/56] Fix validate schema --- .../src/integration-test/postgres.spec.ts | 30 ++++++++++++++----- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/packages/server/src/integration-test/postgres.spec.ts b/packages/server/src/integration-test/postgres.spec.ts index efc1ca33a5..f6b3ad67a5 100644 --- a/packages/server/src/integration-test/postgres.spec.ts +++ b/packages/server/src/integration-test/postgres.spec.ts @@ -38,7 +38,7 @@ describe("row api - postgres", () => { }, }) - makeRequest = generateMakeRequest(apiKey) + makeRequest = generateMakeRequest(apiKey, true) postgresTable = await config.createTable({ name: faker.lorem.word(), @@ -103,17 +103,33 @@ describe("row api - postgres", () => { ) } - test.only("validate schema", async () => { + test("validate table schema", async () => { const res = await makeRequest( "get", - `api/datasources/${postgresDatasource._id}`, - undefined, - undefined, - true + `/api/datasources/${postgresDatasource._id}` ) expect(res.status).toBe(200) - expect(res.body).toEqual({}) + expect(res.body).toEqual({ + config: { + ca: false, + database: "postgres", + host: "192.168.1.98", + password: "root", + port: 54321, + rejectUnauthorized: false, + schema: "public", + ssl: false, + user: "root", + }, + plus: true, + source: "POSTGRES", + type: "datasource", + _id: expect.any(String), + _rev: expect.any(String), + createdAt: expect.any(String), + updatedAt: expect.any(String), + }) }) describe("create a row", () => { From f8a3c1260896c979db82d7662fd2e461c2c30da5 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Thu, 19 Jan 2023 16:46:05 +0000 Subject: [PATCH 30/56] Add get all rows tests --- .../src/integration-test/postgres.spec.ts | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/packages/server/src/integration-test/postgres.spec.ts b/packages/server/src/integration-test/postgres.spec.ts index f6b3ad67a5..f9ede157ef 100644 --- a/packages/server/src/integration-test/postgres.spec.ts +++ b/packages/server/src/integration-test/postgres.spec.ts @@ -449,15 +449,45 @@ describe("row api - postgres", () => { }) }) + describe("get all rows", () => { + const getAll = (tableId: string | undefined) => + makeRequest("get", `/api/${tableId}/rows`) + test("Given than a table has no rows, get returns empty", async () => { + const res = await getAll(postgresTable._id) + expect(res.status).toBe(200) + expect(res.body).toHaveLength(0) + }) + test("Given than a table has multiple rows, get returns all of them", async () => { + const rowsCount = 6 + const rows = await populateRows(rowsCount) + const res = await getAll(postgresTable._id) + expect(res.status).toBe(200) + expect(res.body).toHaveLength(rowsCount) + expect(res.body).toEqual( + expect.arrayContaining( + rows.map(r => expect.objectContaining(r.rowData)) + ) + ) + }) + test("Given than multiple tables have multiple rows, get returns the requested ones", async () => { + await populateRows(2, (await config.createTable())._id) + const rowsCount = 6 + await populateRows(rowsCount, postgresTable._id) + await populateRows(2, (await config.createTable())._id) + const res = await getAll(postgresTable._id) + expect(res.status).toBe(200) + expect(res.body).toHaveLength(rowsCount) + }) + }) }) From bcfb0f372707688e5e0a4dcc8c2f01209e58aa79 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Thu, 19 Jan 2023 17:23:48 +0000 Subject: [PATCH 31/56] Change tests to use internal apis --- .../src/api/routes/public/tests/utils.ts | 18 ++- .../src/integration-test/postgres.spec.ts | 134 ++++++++---------- .../src/tests/utilities/TestConfiguration.ts | 14 +- 3 files changed, 83 insertions(+), 83 deletions(-) diff --git a/packages/server/src/api/routes/public/tests/utils.ts b/packages/server/src/api/routes/public/tests/utils.ts index 5850fd9e2e..06601896bc 100644 --- a/packages/server/src/api/routes/public/tests/utils.ts +++ b/packages/server/src/api/routes/public/tests/utils.ts @@ -2,7 +2,7 @@ import * as setup from "../../tests/utilities" import { checkSlashesInUrl } from "../../../../utilities" import supertest from "supertest" -export type HttpMethod = "post" | "get" | "put" | "delete" +export type HttpMethod = "post" | "get" | "put" | "delete" | "patch" export type MakeRequestResponse = ( method: HttpMethod, @@ -11,7 +11,10 @@ export type MakeRequestResponse = ( intAppId?: string ) => Promise -export function generateMakeRequest(apiKey: string): MakeRequestResponse { +export function generateMakeRequest( + apiKey: string, + isInternal = false +): MakeRequestResponse { const request = setup.getRequest()! const config = setup.getConfig() return async ( @@ -26,9 +29,14 @@ export function generateMakeRequest(apiKey: string): MakeRequestResponse { if (intAppId) { extraHeaders["x-budibase-app-id"] = intAppId } - const req = request[method]( - checkSlashesInUrl(`/api/public/v1/${endpoint}`) - ).set(config.defaultHeaders(extraHeaders)) + + const url = isInternal + ? endpoint + : checkSlashesInUrl(`/api/public/v1/${endpoint}`) + + const req = request[method](url).set( + config.defaultHeaders(extraHeaders, isInternal) + ) if (body) { req.send(body) } diff --git a/packages/server/src/integration-test/postgres.spec.ts b/packages/server/src/integration-test/postgres.spec.ts index f9ede157ef..bb5d31bd90 100644 --- a/packages/server/src/integration-test/postgres.spec.ts +++ b/packages/server/src/integration-test/postgres.spec.ts @@ -5,7 +5,7 @@ import { } from "../api/routes/public/tests/utils" import * as setup from "../api/routes/tests/utilities" -import { Datasource, FieldType, SourceName, Table } from "@budibase/types" +import { Datasource, FieldType, Row, SourceName, Table } from "@budibase/types" import _ from "lodash" const config = setup.getConfig() @@ -133,14 +133,13 @@ describe("row api - postgres", () => { }) describe("create a row", () => { + const createRow = (tableId: string | undefined, body: object) => + makeRequest("post", `/api/${tableId}/rows`, body) + test("Given than no row exists, adding a new row persists it", async () => { const newRow = createRandomRow() - const res = await makeRequest( - "post", - `/tables/${postgresTable._id}/rows`, - newRow - ) + const res = await createRow(postgresTable._id, newRow) expect(res.status).toBe(200) @@ -148,7 +147,7 @@ describe("row api - postgres", () => { expect(persistedRows).toHaveLength(1) expect(persistedRows).toEqual([ expect.objectContaining({ - ...res.body.data, + ...res.body, ...newRow, }), ]) @@ -159,11 +158,7 @@ describe("row api - postgres", () => { const newRows = Array(numberOfRows).fill(createRandomRow()) for (const newRow of newRows) { - const res = await makeRequest( - "post", - `/tables/${postgresTable._id}/rows`, - newRow - ) + const res = await createRow(postgresTable._id, newRow) expect(res.status).toBe(200) } @@ -176,21 +171,21 @@ describe("row api - postgres", () => { }) describe("update a row", () => { + const updateRow = (tableId: string | undefined, body: Row) => + makeRequest("patch", `/api/${tableId}/rows`, body) + test("Given than a row exists, updating it persists it", async () => { - let { rowData, row } = _.sample(await populateRows(10))! + let { row } = _.sample(await populateRows(10))! const newName = faker.random.words(3) const newValue = +faker.random.numeric() - const updateRow = { + const updatedRow = { + ...row, name: newName, value: newValue, } - const res = await makeRequest( - "put", - `/tables/${postgresTable._id}/rows/${row._id}`, - updateRow - ) + const res = await updateRow(postgresTable._id, updatedRow) expect(res.status).toBe(200) @@ -198,23 +193,25 @@ describe("row api - postgres", () => { expect(persistedRow).toEqual( expect.objectContaining({ - ...res.body.data, - ...rowData, - ...updateRow, + _id: row._id, + name: newName, + value: newValue, }) ) }) }) describe("delete a row", () => { + const deleteRow = ( + tableId: string | undefined, + body: Row | { rows: Row[] } + ) => makeRequest("delete", `/api/${tableId}/rows`, body) + test("Given than a row exists, delete request removes it", async () => { const numberOfInitialRows = 5 let { row } = _.sample(await populateRows(numberOfInitialRows))! - const res = await makeRequest( - "delete", - `/tables/${postgresTable._id}/rows/${row._id}` - ) + const res = await deleteRow(postgresTable._id, row) expect(res.status).toBe(200) @@ -226,40 +223,39 @@ describe("row api - postgres", () => { expect.objectContaining({ _id: row._id }) ) }) + + // TODO: delete multiple rows }) describe("retrieve a row", () => { + const getRow = (tableId: string | undefined, rowId?: string | undefined) => + makeRequest("get", `/api/${tableId}/rows/${rowId}`) + test("Given than a table have a single row, the row can be retrieved successfully", async () => { const [{ rowData, row }] = await populateRows(1) - const res = await makeRequest( - "get", - `/tables/${postgresTable._id}/rows/${row._id}` - ) + const res = await getRow(postgresTable._id, row._id) expect(res.status).toBe(200) - expect(res.body.data).toEqual(expect.objectContaining(rowData)) + expect(res.body).toEqual(expect.objectContaining(rowData)) }) test("Given than a table have a multiple rows, a single row can be retrieved successfully", async () => { const rows = await populateRows(10) const { rowData, row } = _.sample(rows)! - const res = await makeRequest( - "get", - `/tables/${postgresTable._id}/rows/${row._id}` - ) + const res = await getRow(postgresTable._id, row._id) expect(res.status).toBe(200) - expect(res.body.data).toEqual(expect.objectContaining(rowData)) + expect(res.body).toEqual(expect.objectContaining(rowData)) }) }) describe("search for rows", () => { const search = (tableId: string | undefined, body?: object) => - makeRequest("post", `/tables/${tableId}/rows/search`, body) + makeRequest("post", `/api/${tableId}/search`, body) describe("empty search", () => { test("Given than a table has no rows, search without query returns empty", async () => { @@ -267,7 +263,7 @@ describe("row api - postgres", () => { expect(res.status).toBe(200) - expect(res.body.data).toHaveLength(0) + expect(res.body).toEqual({ rows: [] }) }) test("Given than a table has multiple rows, search without query returns all of them", async () => { @@ -278,12 +274,12 @@ describe("row api - postgres", () => { expect(res.status).toBe(200) - expect(res.body.data).toHaveLength(rowsCount) - expect(res.body.data).toEqual( - expect.arrayContaining( + expect(res.body).toEqual({ + rows: expect.arrayContaining( rows.map(r => expect.objectContaining(r.rowData)) - ) - ) + ), + }) + expect(res.body.rows).toHaveLength(rowsCount) }) test("Given than multiple tables have multiple rows, search only return the requested ones", async () => { @@ -296,7 +292,7 @@ describe("row api - postgres", () => { expect(res.status).toBe(200) - expect(res.body.data).toHaveLength(rowsCount) + expect(res.body.rows).toHaveLength(rowsCount) }) }) @@ -331,10 +327,10 @@ describe("row api - postgres", () => { expect(res.status).toBe(200) - expect(res.body.data).toHaveLength(4) - expect(res.body.data).toEqual( - expect.arrayContaining(rowsToFilter.map(expect.objectContaining)) - ) + expect(res.body).toEqual({ + rows: expect.arrayContaining(rowsToFilter.map(expect.objectContaining)), + }) + expect(res.body.rows).toHaveLength(4) }) test("Querying respects the limit fields", async () => { @@ -346,7 +342,7 @@ describe("row api - postgres", () => { expect(res.status).toBe(200) - expect(res.body.data).toHaveLength(2) + expect(res.body.rows).toHaveLength(2) }) describe("sort", () => { @@ -377,15 +373,13 @@ describe("row api - postgres", () => { test("Querying respects the sort order when sorting ascending by a string value", async () => { const res = await search(postgresTable._id, { - sort: { - order: "ascending", - column: "name", - type: "string", - }, + sort: "name", + sortOrder: "ascending", + sortType: "string", }) expect(res.status).toBe(200) - expect(res.body.data).toEqual([ + expect(res.body.rows).toEqual([ expect.objectContaining({ name: "aaa" }), expect.objectContaining({ name: "bb" }), expect.objectContaining({ name: "ccccc" }), @@ -395,15 +389,13 @@ describe("row api - postgres", () => { test("Querying respects the sort order when sorting descending by a string value", async () => { const res = await search(postgresTable._id, { - sort: { - order: "descending", - column: "name", - type: "string", - }, + sort: "name", + sortOrder: "descending", + sortType: "string", }) expect(res.status).toBe(200) - expect(res.body.data).toEqual([ + expect(res.body.rows).toEqual([ expect.objectContaining({ name: "d" }), expect.objectContaining({ name: "ccccc" }), expect.objectContaining({ name: "bb" }), @@ -413,15 +405,13 @@ describe("row api - postgres", () => { test("Querying respects the sort order when sorting ascending by a numeric value", async () => { const res = await search(postgresTable._id, { - sort: { - order: "ascending", - column: "value", - type: "number", - }, + sort: "value", + sortOrder: "ascending", + sortType: "number", }) expect(res.status).toBe(200) - expect(res.body.data).toEqual([ + expect(res.body.rows).toEqual([ expect.objectContaining({ value: -5 }), expect.objectContaining({ value: 0 }), expect.objectContaining({ value: 3 }), @@ -431,15 +421,13 @@ describe("row api - postgres", () => { test("Querying respects the sort order when sorting descending by a numeric value", async () => { const res = await search(postgresTable._id, { - sort: { - order: "descending", - column: "value", - type: "number", - }, + sort: "value", + sortOrder: "descending", + sortType: "number", }) expect(res.status).toBe(200) - expect(res.body.data).toEqual([ + expect(res.body.rows).toEqual([ expect.objectContaining({ value: 40 }), expect.objectContaining({ value: 3 }), expect.objectContaining({ value: 0 }), diff --git a/packages/server/src/tests/utilities/TestConfiguration.ts b/packages/server/src/tests/utilities/TestConfiguration.ts index 2934bafbf2..a0208993e0 100644 --- a/packages/server/src/tests/utilities/TestConfiguration.ts +++ b/packages/server/src/tests/utilities/TestConfiguration.ts @@ -300,7 +300,7 @@ class TestConfiguration { }) } - defaultHeaders(extras = {}) { + defaultHeaders(extras = {}, isInternal: boolean) { const authObj = { userId: GLOBAL_USER_ID, sessionId: "sessionid", @@ -314,13 +314,17 @@ class TestConfiguration { const appToken = auth.jwt.sign(app, env.JWT_SECRET) const headers: any = { Accept: "application/json", - Cookie: [ - `${constants.Cookie.Auth}=${authToken}`, - `${constants.Cookie.CurrentApp}=${appToken}`, - ], [constants.Header.CSRF_TOKEN]: CSRF_TOKEN, ...extras, } + + if (!isInternal) { + headers.Cookie = [ + `${constants.Cookie.Auth}=${authToken}`, + `${constants.Cookie.CurrentApp}=${appToken}`, + ] + } + if (this.appId) { headers[constants.Header.APP_ID] = this.appId } From af42e789ff4cbbae37f63b2991a595bc2a947317 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Thu, 19 Jan 2023 17:28:42 +0000 Subject: [PATCH 32/56] Test delete multiple --- .../src/integration-test/postgres.spec.ts | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/packages/server/src/integration-test/postgres.spec.ts b/packages/server/src/integration-test/postgres.spec.ts index bb5d31bd90..2b49d9a746 100644 --- a/packages/server/src/integration-test/postgres.spec.ts +++ b/packages/server/src/integration-test/postgres.spec.ts @@ -224,7 +224,25 @@ describe("row api - postgres", () => { ) }) - // TODO: delete multiple rows + test("Given than multiple rows exist, multiple rows can be removed", async () => { + const numberOfInitialRows = 5 + let rows = _.sampleSize(await populateRows(numberOfInitialRows), 3)!.map( + x => x.row + ) + + const res = await deleteRow(postgresTable._id, { rows }) + + expect(res.status).toBe(200) + + const persistedRows = await config.getRows(postgresTable._id!) + expect(persistedRows).toHaveLength(numberOfInitialRows - 3) + + for (const row of rows) { + expect(persistedRows).not.toContain( + expect.objectContaining({ _id: row._id }) + ) + } + }) }) describe("retrieve a row", () => { From 5fb20abcfd5ad00f9d0e1df27a9f9e7548d2e50a Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Thu, 19 Jan 2023 20:09:39 +0000 Subject: [PATCH 33/56] Add enrich test --- packages/server/src/api/routes/row.ts | 2 +- .../src/integration-test/postgres.spec.ts | 95 ++++++++++++++++++- packages/types/src/documents/app/table.ts | 8 +- 3 files changed, 101 insertions(+), 4 deletions(-) diff --git a/packages/server/src/api/routes/row.ts b/packages/server/src/api/routes/row.ts index f4462b3595..6d1cd206c6 100644 --- a/packages/server/src/api/routes/row.ts +++ b/packages/server/src/api/routes/row.ts @@ -3,7 +3,7 @@ import * as rowController from "../controllers/row" import authorized from "../../middleware/authorized" import { paramResource, paramSubResource } from "../../middleware/resourceId" import { permissions } from "@budibase/backend-core" -const { internalSearchValidator } = require("./utils/validators") +import { internalSearchValidator } from "./utils/validators" const { PermissionType, PermissionLevel } = permissions const router: Router = new Router() diff --git a/packages/server/src/integration-test/postgres.spec.ts b/packages/server/src/integration-test/postgres.spec.ts index 2b49d9a746..b194aa53db 100644 --- a/packages/server/src/integration-test/postgres.spec.ts +++ b/packages/server/src/integration-test/postgres.spec.ts @@ -5,7 +5,14 @@ import { } from "../api/routes/public/tests/utils" import * as setup from "../api/routes/tests/utilities" -import { Datasource, FieldType, Row, SourceName, Table } from "@budibase/types" +import { + Datasource, + FieldType, + RelationshipTypes, + Row, + SourceName, + Table, +} from "@budibase/types" import _ from "lodash" const config = setup.getConfig() @@ -16,7 +23,8 @@ describe("row api - postgres", () => { let apiKey, makeRequest: MakeRequestResponse, postgresDatasource: Datasource, - postgresTable: Table + postgresTable: Table, + auxPostgresTable: Table beforeEach(async () => { await config.init() @@ -61,6 +69,31 @@ describe("row api - postgres", () => { }, sourceId: postgresDatasource._id, }) + + auxPostgresTable = await config.createTable({ + name: faker.lorem.word(), + schema: { + title: { + name: "title", + type: FieldType.STRING, + constraints: { + presence: true, + }, + }, + linkedField: { + type: FieldType.LINK, + constraints: { + type: "array", + presence: true, + }, + fieldName: "foreignField", + name: "linkedField", + relationshipType: RelationshipTypes.MANY_TO_MANY, + tableId: postgresTable._id, + }, + }, + sourceId: postgresDatasource._id, + }) }) afterAll(async () => { @@ -269,6 +302,31 @@ describe("row api - postgres", () => { expect(res.body).toEqual(expect.objectContaining(rowData)) }) + + test("given having rows with relation data, only the ids are retrieved", async () => { + let [{ row }] = await populateRows(1) + + const foreignRow = await config.createRow({ + tableId: auxPostgresTable._id, + title: faker.random.alphaNumeric(10), + linkedField: row._id, + }) + + const res = await getRow(postgresTable._id, row._id) + + expect(res.status).toBe(200) + + expect(res.body).toEqual({ + ...row, + foreignField: [ + { + _id: foreignRow._id, + }, + ], + createdAt: expect.any(String), + updatedAt: expect.any(String), + }) + }) }) describe("search for rows", () => { @@ -455,6 +513,39 @@ describe("row api - postgres", () => { }) }) + describe("enrich a row", () => { + const getAll = (tableId: string | undefined, rowId: string | undefined) => + makeRequest("get", `/api/${tableId}/${rowId}/enrich`) + + test("given having rows with relation data, enrich populates the", async () => { + let [{ row }] = await populateRows(1) + + const foreignRow = await config.createRow({ + tableId: auxPostgresTable._id, + title: faker.random.alphaNumeric(10), + linkedField: row._id, + }) + + const res = await getAll(postgresTable._id, row._id) + + expect(res.status).toBe(200) + + expect(res.body).toEqual({ + ...row, + foreignField: [ + { + ...foreignRow, + linkedField: [{ _id: row._id }], + createdAt: expect.any(String), + updatedAt: expect.any(String), + }, + ], + createdAt: expect.any(String), + updatedAt: expect.any(String), + }) + }) + }) + describe("get all rows", () => { const getAll = (tableId: string | undefined) => makeRequest("get", `/api/${tableId}/rows`) diff --git a/packages/types/src/documents/app/table.ts b/packages/types/src/documents/app/table.ts index 0f85e895ac..01d2486dcb 100644 --- a/packages/types/src/documents/app/table.ts +++ b/packages/types/src/documents/app/table.ts @@ -3,6 +3,12 @@ import { View } from "./view" import { RenameColumn } from "../../sdk" import { FieldType } from "./row" +export enum RelationshipTypes { + ONE_TO_MANY = "one-to-many", + MANY_TO_ONE = "many-to-one", + MANY_TO_MANY = "many-to-many", +} + export interface FieldSchema { type: FieldType externalType?: string @@ -10,7 +16,7 @@ export interface FieldSchema { name: string sortable?: boolean tableId?: string - relationshipType?: string + relationshipType?: RelationshipTypes through?: string foreignKey?: string icon?: string From 7f53cbeca711af1b99bc3d6851ecdbb05eda32f3 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Fri, 20 Jan 2023 09:58:59 +0000 Subject: [PATCH 34/56] Fix types --- .../server/src/api/controllers/row/ExternalRequest.ts | 3 ++- packages/server/src/api/controllers/table/external.ts | 5 +++-- packages/server/src/constants/index.ts | 6 ------ .../server/src/db/defaultData/datasource_bb_default.ts | 8 ++------ packages/server/src/db/linkedRows/LinkController.ts | 3 ++- 5 files changed, 9 insertions(+), 16 deletions(-) diff --git a/packages/server/src/api/controllers/row/ExternalRequest.ts b/packages/server/src/api/controllers/row/ExternalRequest.ts index 6085921423..4581724910 100644 --- a/packages/server/src/api/controllers/row/ExternalRequest.ts +++ b/packages/server/src/api/controllers/row/ExternalRequest.ts @@ -10,6 +10,7 @@ import { FieldSchema, Row, Table, + RelationshipTypes, } from "@budibase/types" import { breakRowIdField, @@ -18,7 +19,7 @@ import { convertRowId, } from "../../../integrations/utils" import { getDatasourceAndQuery } from "./utils" -import { FieldTypes, RelationshipTypes } from "../../../constants" +import { FieldTypes } from "../../../constants" import { breakExternalTableId, isSQL } from "../../../integrations/utils" import { processObjectSync } from "@budibase/string-templates" import { cloneDeep } from "lodash/fp" diff --git a/packages/server/src/api/controllers/table/external.ts b/packages/server/src/api/controllers/table/external.ts index dbe09f59c1..835aba0450 100644 --- a/packages/server/src/api/controllers/table/external.ts +++ b/packages/server/src/api/controllers/table/external.ts @@ -8,7 +8,7 @@ import { foreignKeyStructure, hasTypeChanged, } from "./utils" -import { FieldTypes, RelationshipTypes } from "../../../constants" +import { FieldTypes } from "../../../constants" import { makeExternalQuery } from "../../../integrations/base/query" import { handleRequest } from "../row/external" import { events, context } from "@budibase/backend-core" @@ -22,6 +22,7 @@ import { FieldSchema, BBContext, TableRequest, + RelationshipTypes, } from "@budibase/types" import sdk from "../../../sdk" const { cloneDeep } = require("lodash/fp") @@ -146,7 +147,7 @@ function generateLinkSchema( column: FieldSchema, table: Table, relatedTable: Table, - type: string + type: RelationshipTypes ) { if (!table.primary || !relatedTable.primary) { throw new Error("Unable to generate link schema, no primary keys") diff --git a/packages/server/src/constants/index.ts b/packages/server/src/constants/index.ts index ef933769b1..ded3a91b19 100644 --- a/packages/server/src/constants/index.ts +++ b/packages/server/src/constants/index.ts @@ -38,12 +38,6 @@ export const SwitchableTypes = CanSwitchTypes.reduce((prev, current) => prev ? prev.concat(current) : current ) -export enum RelationshipTypes { - ONE_TO_MANY = "one-to-many", - MANY_TO_ONE = "many-to-one", - MANY_TO_MANY = "many-to-many", -} - export enum FormulaTypes { STATIC = "static", DYNAMIC = "dynamic", diff --git a/packages/server/src/db/defaultData/datasource_bb_default.ts b/packages/server/src/db/defaultData/datasource_bb_default.ts index 81d0488cda..516783ed31 100644 --- a/packages/server/src/db/defaultData/datasource_bb_default.ts +++ b/packages/server/src/db/defaultData/datasource_bb_default.ts @@ -1,8 +1,4 @@ -import { - FieldTypes, - AutoFieldSubTypes, - RelationshipTypes, -} from "../../constants" +import { FieldTypes, AutoFieldSubTypes } from "../../constants" import { importToRows } from "../../api/controllers/table/utils" import { cloneDeep } from "lodash/fp" import LinkDocument from "../linkedRows/LinkDocument" @@ -11,7 +7,7 @@ import { employeeImport } from "./employeeImport" import { jobsImport } from "./jobsImport" import { expensesImport } from "./expensesImport" import { db as dbCore } from "@budibase/backend-core" -import { Table, Row } from "@budibase/types" +import { Table, Row, RelationshipTypes } from "@budibase/types" export const DEFAULT_JOBS_TABLE_ID = "ta_bb_jobs" export const DEFAULT_INVENTORY_TABLE_ID = "ta_bb_inventory" diff --git a/packages/server/src/db/linkedRows/LinkController.ts b/packages/server/src/db/linkedRows/LinkController.ts index 765efb9c8f..4a154b8f19 100644 --- a/packages/server/src/db/linkedRows/LinkController.ts +++ b/packages/server/src/db/linkedRows/LinkController.ts @@ -1,13 +1,14 @@ import { IncludeDocs, getLinkDocuments } from "./linkUtils" import { InternalTables, getUserMetadataParams } from "../utils" import Sentry from "@sentry/node" -import { FieldTypes, RelationshipTypes } from "../../constants" +import { FieldTypes } from "../../constants" import { context } from "@budibase/backend-core" import LinkDocument from "./LinkDocument" import { Database, FieldSchema, LinkDocumentValue, + RelationshipTypes, Row, Table, } from "@budibase/types" From 12118fd028c322c65041a99ef26d5df65b91da13 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Fri, 20 Jan 2023 10:12:52 +0000 Subject: [PATCH 35/56] Fix old tests --- packages/server/src/api/routes/public/tests/compare.spec.js | 2 +- packages/server/src/api/routes/public/tests/users.spec.js | 2 +- packages/server/src/constants/index.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/server/src/api/routes/public/tests/compare.spec.js b/packages/server/src/api/routes/public/tests/compare.spec.js index eaf0fb2049..36693a1098 100644 --- a/packages/server/src/api/routes/public/tests/compare.spec.js +++ b/packages/server/src/api/routes/public/tests/compare.spec.js @@ -13,7 +13,7 @@ beforeAll(async () => { app = await config.init() table = await config.updateTable() apiKey = await config.generateApiKey() - makeRequest = generateMakeRequest(apiKey, setup) + makeRequest = generateMakeRequest(apiKey) }) afterAll(setup.afterAll) diff --git a/packages/server/src/api/routes/public/tests/users.spec.js b/packages/server/src/api/routes/public/tests/users.spec.js index 82f63cb847..1daa611df8 100644 --- a/packages/server/src/api/routes/public/tests/users.spec.js +++ b/packages/server/src/api/routes/public/tests/users.spec.js @@ -10,7 +10,7 @@ beforeAll(async () => { await config.init() globalUser = await config.globalUser() apiKey = await config.generateApiKey(globalUser._id) - makeRequest = generateMakeRequest(apiKey, setup) + makeRequest = generateMakeRequest(apiKey) workerRequests.readGlobalUser.mockReturnValue(globalUser) }) diff --git a/packages/server/src/constants/index.ts b/packages/server/src/constants/index.ts index ded3a91b19..e55ad09add 100644 --- a/packages/server/src/constants/index.ts +++ b/packages/server/src/constants/index.ts @@ -1,5 +1,5 @@ import { objectStore, roles, constants } from "@budibase/backend-core" -export { FieldType as FieldTypes } from "@budibase/types" +export { FieldType as FieldTypes, RelationshipTypes } from "@budibase/types" export enum FilterTypes { STRING = "string", From 62b372ce592691e51aa64dde930beb80b04cbecf Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Fri, 20 Jan 2023 10:29:11 +0000 Subject: [PATCH 36/56] Fix broken tests --- .../src/tests/utilities/TestConfiguration.ts | 18 +++++++++--------- .../server/src/tests/utilities/structures.ts | 12 +++++++----- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/packages/server/src/tests/utilities/TestConfiguration.ts b/packages/server/src/tests/utilities/TestConfiguration.ts index a0208993e0..25123cac12 100644 --- a/packages/server/src/tests/utilities/TestConfiguration.ts +++ b/packages/server/src/tests/utilities/TestConfiguration.ts @@ -558,13 +558,11 @@ class TestConfiguration { // DATASOURCE - async createDatasource(config?: Datasource): Promise { + async createDatasource(config?: { + datasource: Datasource + }): Promise { config = config || basicDatasource() - const response = await this._req( - { datasource: config }, - null, - controllers.datasource.save - ) + const response = await this._req(config, null, controllers.datasource.save) this.datasource = response.datasource return this.datasource } @@ -581,9 +579,11 @@ class TestConfiguration { async restDatasource(cfg?: any) { return this.createDatasource({ - ...basicDatasource(), - source: SourceName.REST, - config: cfg || {}, + datasource: { + ...basicDatasource().datasource, + source: SourceName.REST, + config: cfg || {}, + }, }) } diff --git a/packages/server/src/tests/utilities/structures.ts b/packages/server/src/tests/utilities/structures.ts index 2e453e86e5..24ec6eeca6 100644 --- a/packages/server/src/tests/utilities/structures.ts +++ b/packages/server/src/tests/utilities/structures.ts @@ -145,12 +145,14 @@ export function basicRole() { } } -export function basicDatasource(): Datasource { +export function basicDatasource(): { datasource: Datasource } { return { - type: "datasource", - name: "Test", - source: SourceName.POSTGRES, - config: {}, + datasource: { + type: "datasource", + name: "Test", + source: SourceName.POSTGRES, + config: {}, + }, } } From 27171e768002b52dc954166c0309f0f0ae2c1fd7 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Fri, 20 Jan 2023 11:48:11 +0000 Subject: [PATCH 37/56] Extra typings --- .../server/src/api/routes/tests/cloud.spec.ts | 4 +-- .../src/api/routes/tests/utilities/index.ts | 3 +- .../src/integration-test/postgres.spec.ts | 33 ++++++++++--------- .../src/tests/utilities/TestConfiguration.ts | 14 ++++---- .../types/src/documents/app/datasource.ts | 2 +- 5 files changed, 31 insertions(+), 25 deletions(-) diff --git a/packages/server/src/api/routes/tests/cloud.spec.ts b/packages/server/src/api/routes/tests/cloud.spec.ts index f52bf838ff..1e6aaaea35 100644 --- a/packages/server/src/api/routes/tests/cloud.spec.ts +++ b/packages/server/src/api/routes/tests/cloud.spec.ts @@ -4,7 +4,7 @@ import { AppStatus } from "../../../db/utils" import * as setup from "./utilities" describe("/cloud", () => { - let request = setup.getRequest() + let request = setup.getRequest()! let config = setup.getConfig() afterAll(setup.afterAll) @@ -25,7 +25,7 @@ describe("/cloud", () => { await request .post( `/api/applications/${dbCore.getProdAppID( - config.getAppId() + config.getAppId()! )}/unpublish` ) .set(config.defaultHeaders()) diff --git a/packages/server/src/api/routes/tests/utilities/index.ts b/packages/server/src/api/routes/tests/utilities/index.ts index 0997474e5a..b0c73e07f3 100644 --- a/packages/server/src/api/routes/tests/utilities/index.ts +++ b/packages/server/src/api/routes/tests/utilities/index.ts @@ -46,7 +46,7 @@ export function delay(ms: number) { } let request: supertest.SuperTest | undefined | null, - config: TestConfig + config: TestConfig | null export function beforeAll() { config = new TestConfig() @@ -60,6 +60,7 @@ export function afterAll() { // clear app files request = null + config = null } export function getRequest() { diff --git a/packages/server/src/integration-test/postgres.spec.ts b/packages/server/src/integration-test/postgres.spec.ts index b194aa53db..4d9c3c0988 100644 --- a/packages/server/src/integration-test/postgres.spec.ts +++ b/packages/server/src/integration-test/postgres.spec.ts @@ -15,11 +15,12 @@ import { } from "@budibase/types" import _ from "lodash" -const config = setup.getConfig() +const config = setup.getConfig()! jest.unmock("node-fetch") -describe("row api - postgres", () => { +// TODO: Waiting for the test image to exist +describe.skip("row api - postgres", () => { let apiKey, makeRequest: MakeRequestResponse, postgresDatasource: Datasource, @@ -30,19 +31,21 @@ describe("row api - postgres", () => { await config.init() apiKey = await config.generateApiKey() postgresDatasource = await config.createDatasource({ - type: "datasource", - source: SourceName.POSTGRES, - plus: true, - config: { - host: "192.168.1.98", - port: 54321, - database: "postgres", - user: "root", - password: "root", - schema: "public", - ssl: false, - rejectUnauthorized: false, - ca: false, + datasource: { + type: "datasource", + source: SourceName.POSTGRES, + plus: true, + config: { + host: "192.168.1.98", + port: 54321, + database: "postgres", + user: "root", + password: "root", + schema: "public", + ssl: false, + rejectUnauthorized: false, + ca: false, + }, }, }) diff --git a/packages/server/src/tests/utilities/TestConfiguration.ts b/packages/server/src/tests/utilities/TestConfiguration.ts index 25123cac12..5a5b5e58bb 100644 --- a/packages/server/src/tests/utilities/TestConfiguration.ts +++ b/packages/server/src/tests/utilities/TestConfiguration.ts @@ -145,11 +145,13 @@ class TestConfiguration { if (this.allApps) { cleanup(this.allApps.map(app => app.appId)) - await this._req( - null, - { appId: this.prodApp.appId }, - controllers.app.destroy - ) + if (env.isDockerisedTest()) { + await this._req( + null, + { appId: this.prodApp.appId }, + controllers.app.destroy + ) + } } if (this.server) { @@ -300,7 +302,7 @@ class TestConfiguration { }) } - defaultHeaders(extras = {}, isInternal: boolean) { + defaultHeaders(extras = {}, isInternal: boolean = false) { const authObj = { userId: GLOBAL_USER_ID, sessionId: "sessionid", diff --git a/packages/types/src/documents/app/datasource.ts b/packages/types/src/documents/app/datasource.ts index efdc2ca1bd..a37e3d3ddc 100644 --- a/packages/types/src/documents/app/datasource.ts +++ b/packages/types/src/documents/app/datasource.ts @@ -8,7 +8,7 @@ export interface Datasource extends Document { source: SourceName // the config is defined by the schema config?: { - [key: string]: string | number | boolean + [key: string]: string | number | boolean | any[] } plus?: boolean entities?: { From dec7e69391232a7fafbcb287899bb08b5c735177 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Fri, 20 Jan 2023 12:03:00 +0000 Subject: [PATCH 38/56] Fix types --- packages/server/src/api/routes/public/tests/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/src/api/routes/public/tests/utils.ts b/packages/server/src/api/routes/public/tests/utils.ts index 06601896bc..d861fa3660 100644 --- a/packages/server/src/api/routes/public/tests/utils.ts +++ b/packages/server/src/api/routes/public/tests/utils.ts @@ -16,7 +16,7 @@ export function generateMakeRequest( isInternal = false ): MakeRequestResponse { const request = setup.getRequest()! - const config = setup.getConfig() + const config = setup.getConfig()! return async ( method: HttpMethod, endpoint: string, From 46994a45f99a3a086d238a6cf4641c97dd4e398f Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Fri, 20 Jan 2023 12:12:59 +0000 Subject: [PATCH 39/56] Update openapi specs --- packages/server/specs/openapi.json | 12 ++++++------ packages/server/specs/openapi.yaml | 6 +++--- .../server/src/tests/utilities/TestConfiguration.ts | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/server/specs/openapi.json b/packages/server/specs/openapi.json index 9a0d69e352..cf4eef75a9 100644 --- a/packages/server/specs/openapi.json +++ b/packages/server/specs/openapi.json @@ -817,7 +817,6 @@ "type": "string", "enum": [ "string", - "barcodeqr", "longform", "options", "number", @@ -829,7 +828,8 @@ "formula", "auto", "json", - "internal" + "internal", + "barcodeqr" ], "description": "Defines the type of the column, most explain themselves, a link column is a relationship." }, @@ -1021,7 +1021,6 @@ "type": "string", "enum": [ "string", - "barcodeqr", "longform", "options", "number", @@ -1033,7 +1032,8 @@ "formula", "auto", "json", - "internal" + "internal", + "barcodeqr" ], "description": "Defines the type of the column, most explain themselves, a link column is a relationship." }, @@ -1236,7 +1236,6 @@ "type": "string", "enum": [ "string", - "barcodeqr", "longform", "options", "number", @@ -1248,7 +1247,8 @@ "formula", "auto", "json", - "internal" + "internal", + "barcodeqr" ], "description": "Defines the type of the column, most explain themselves, a link column is a relationship." }, diff --git a/packages/server/specs/openapi.yaml b/packages/server/specs/openapi.yaml index 69e44d881c..414efe7adb 100644 --- a/packages/server/specs/openapi.yaml +++ b/packages/server/specs/openapi.yaml @@ -603,7 +603,6 @@ components: type: string enum: - string - - barcodeqr - longform - options - number @@ -616,6 +615,7 @@ components: - auto - json - internal + - barcodeqr description: Defines the type of the column, most explain themselves, a link column is a relationship. constraints: @@ -766,7 +766,6 @@ components: type: string enum: - string - - barcodeqr - longform - options - number @@ -779,6 +778,7 @@ components: - auto - json - internal + - barcodeqr description: Defines the type of the column, most explain themselves, a link column is a relationship. constraints: @@ -936,7 +936,6 @@ components: type: string enum: - string - - barcodeqr - longform - options - number @@ -949,6 +948,7 @@ components: - auto - json - internal + - barcodeqr description: Defines the type of the column, most explain themselves, a link column is a relationship. constraints: diff --git a/packages/server/src/tests/utilities/TestConfiguration.ts b/packages/server/src/tests/utilities/TestConfiguration.ts index 5a5b5e58bb..1867747dad 100644 --- a/packages/server/src/tests/utilities/TestConfiguration.ts +++ b/packages/server/src/tests/utilities/TestConfiguration.ts @@ -620,7 +620,7 @@ class TestConfiguration { datasource: any, fields: any, params: any, - verb: string + verb?: string ) { return request .post(`/api/queries/preview`) From 3328c908ff1dd24e67bee3ad208adfd42c6d09ef Mon Sep 17 00:00:00 2001 From: adrinr Date: Thu, 2 Feb 2023 10:43:18 +0000 Subject: [PATCH 40/56] Fix tests after merge --- packages/server/specs/openapi.json | 12 ++--- packages/server/specs/openapi.yaml | 6 +-- .../src/integration-test/postgres.spec.ts | 48 ++++++++++--------- 3 files changed, 35 insertions(+), 31 deletions(-) diff --git a/packages/server/specs/openapi.json b/packages/server/specs/openapi.json index cf4eef75a9..9a0d69e352 100644 --- a/packages/server/specs/openapi.json +++ b/packages/server/specs/openapi.json @@ -817,6 +817,7 @@ "type": "string", "enum": [ "string", + "barcodeqr", "longform", "options", "number", @@ -828,8 +829,7 @@ "formula", "auto", "json", - "internal", - "barcodeqr" + "internal" ], "description": "Defines the type of the column, most explain themselves, a link column is a relationship." }, @@ -1021,6 +1021,7 @@ "type": "string", "enum": [ "string", + "barcodeqr", "longform", "options", "number", @@ -1032,8 +1033,7 @@ "formula", "auto", "json", - "internal", - "barcodeqr" + "internal" ], "description": "Defines the type of the column, most explain themselves, a link column is a relationship." }, @@ -1236,6 +1236,7 @@ "type": "string", "enum": [ "string", + "barcodeqr", "longform", "options", "number", @@ -1247,8 +1248,7 @@ "formula", "auto", "json", - "internal", - "barcodeqr" + "internal" ], "description": "Defines the type of the column, most explain themselves, a link column is a relationship." }, diff --git a/packages/server/specs/openapi.yaml b/packages/server/specs/openapi.yaml index 414efe7adb..69e44d881c 100644 --- a/packages/server/specs/openapi.yaml +++ b/packages/server/specs/openapi.yaml @@ -603,6 +603,7 @@ components: type: string enum: - string + - barcodeqr - longform - options - number @@ -615,7 +616,6 @@ components: - auto - json - internal - - barcodeqr description: Defines the type of the column, most explain themselves, a link column is a relationship. constraints: @@ -766,6 +766,7 @@ components: type: string enum: - string + - barcodeqr - longform - options - number @@ -778,7 +779,6 @@ components: - auto - json - internal - - barcodeqr description: Defines the type of the column, most explain themselves, a link column is a relationship. constraints: @@ -936,6 +936,7 @@ components: type: string enum: - string + - barcodeqr - longform - options - number @@ -948,7 +949,6 @@ components: - auto - json - internal - - barcodeqr description: Defines the type of the column, most explain themselves, a link column is a relationship. constraints: diff --git a/packages/server/src/integration-test/postgres.spec.ts b/packages/server/src/integration-test/postgres.spec.ts index 4d9c3c0988..ab33a932a2 100644 --- a/packages/server/src/integration-test/postgres.spec.ts +++ b/packages/server/src/integration-test/postgres.spec.ts @@ -1,4 +1,3 @@ -import { faker } from "@faker-js/faker" import { generateMakeRequest, MakeRequestResponse, @@ -14,19 +13,24 @@ import { Table, } from "@budibase/types" import _ from "lodash" +import { generator } from "@budibase/backend-core/tests" +import { utils } from "@budibase/backend-core" const config = setup.getConfig()! jest.unmock("node-fetch") // TODO: Waiting for the test image to exist -describe.skip("row api - postgres", () => { +describe("row api - postgres", () => { let apiKey, makeRequest: MakeRequestResponse, postgresDatasource: Datasource, postgresTable: Table, auxPostgresTable: Table + const host = generator.ip() + const port = generator.natural() + beforeEach(async () => { await config.init() apiKey = await config.generateApiKey() @@ -36,8 +40,8 @@ describe.skip("row api - postgres", () => { source: SourceName.POSTGRES, plus: true, config: { - host: "192.168.1.98", - port: 54321, + host, + port, database: "postgres", user: "root", password: "root", @@ -52,7 +56,7 @@ describe.skip("row api - postgres", () => { makeRequest = generateMakeRequest(apiKey, true) postgresTable = await config.createTable({ - name: faker.lorem.word(), + name: generator.word(), schema: { name: { name: "name", @@ -74,7 +78,7 @@ describe.skip("row api - postgres", () => { }) auxPostgresTable = await config.createTable({ - name: faker.lorem.word(), + name: generator.word(), schema: { title: { name: "title", @@ -105,9 +109,9 @@ describe.skip("row api - postgres", () => { function createRandomRow() { return { - name: faker.name.fullName(), - description: faker.lorem.paragraphs(), - value: +faker.random.numeric(), + name: generator.name(), + description: generator.paragraph(), + value: generator.integer(), } } @@ -150,9 +154,9 @@ describe.skip("row api - postgres", () => { config: { ca: false, database: "postgres", - host: "192.168.1.98", - password: "root", - port: 54321, + host, + password: "--secret-value--", + port, rejectUnauthorized: false, schema: "public", ssl: false, @@ -213,8 +217,8 @@ describe.skip("row api - postgres", () => { test("Given than a row exists, updating it persists it", async () => { let { row } = _.sample(await populateRows(10))! - const newName = faker.random.words(3) - const newValue = +faker.random.numeric() + const newName = generator.name() + const newValue = generator.integer() const updatedRow = { ...row, name: newName, @@ -311,7 +315,7 @@ describe.skip("row api - postgres", () => { const foreignRow = await config.createRow({ tableId: auxPostgresTable._id, - title: faker.random.alphaNumeric(10), + title: generator.sentence(), linkedField: row._id, }) @@ -376,17 +380,17 @@ describe.skip("row api - postgres", () => { }) test("Querying by a string field returns the rows with field containing or starting by that value", async () => { - const name = faker.name.fullName() + const name = generator.name() const rowsToFilter = [ ...Array(2).fill({ name, - description: faker.lorem.paragraphs(), - value: +faker.random.numeric(), + description: generator.paragraph(), + value: generator.integer(), }), ...Array(2).fill({ - name: `${name}${faker.random.alphaNumeric(5)}`, - description: faker.lorem.paragraphs(), - value: +faker.random.numeric(), + name: `${name}${utils.newid()}`, + description: generator.paragraph(), + value: generator.integer(), }), ] @@ -525,7 +529,7 @@ describe.skip("row api - postgres", () => { const foreignRow = await config.createRow({ tableId: auxPostgresTable._id, - title: faker.random.alphaNumeric(10), + title: generator.name(), linkedField: row._id, }) From a80fc4aaa807f0f21f4611f8b41c83da19bbb5d9 Mon Sep 17 00:00:00 2001 From: adrinr Date: Thu, 2 Feb 2023 10:50:20 +0000 Subject: [PATCH 41/56] Remove isDockerisedTest --- packages/backend-core/src/environment.ts | 11 ----------- packages/server/src/app.ts | 2 +- packages/server/src/environment.ts | 12 ------------ .../server/src/tests/utilities/TestConfiguration.ts | 8 -------- packages/worker/src/environment.ts | 10 ---------- 5 files changed, 1 insertion(+), 42 deletions(-) diff --git a/packages/backend-core/src/environment.ts b/packages/backend-core/src/environment.ts index f78391d72f..d742ca1cc9 100644 --- a/packages/backend-core/src/environment.ts +++ b/packages/backend-core/src/environment.ts @@ -1,15 +1,4 @@ -function isDockerisedTest() { - return process.env.DOCKERISED_TEST === "true" -} - function isTest() { - if (isDockerisedTest()) { - // While we are migrating all the tests to use docker instead of mocked in memory, - // we want to keep treating the old tests as tests, - // but the new tests should not make a difference - return false - } - return isCypress() || isJest() } diff --git a/packages/server/src/app.ts b/packages/server/src/app.ts index 50a1e187ba..177393dee7 100644 --- a/packages/server/src/app.ts +++ b/packages/server/src/app.ts @@ -74,7 +74,7 @@ server.on("close", async () => { await events.shutdown() await Thread.shutdown() api.shutdown() - if (!env.isTest() && !env.isDockerisedTest()) { + if (!env.isTest()) { process.exit(errCode) } }) diff --git a/packages/server/src/environment.ts b/packages/server/src/environment.ts index 99949feb41..6272e0e462 100644 --- a/packages/server/src/environment.ts +++ b/packages/server/src/environment.ts @@ -1,17 +1,6 @@ import { join } from "path" -function isDockerisedTest() { - return process.env.DOCKERISED_TEST === "true" -} - function isTest() { - if (isDockerisedTest()) { - // While we are migrating all the tests to use docker instead of mocked in memory, - // we want to keep treating the old tests as tests, - // but the new tests should not make a difference - return false - } - return isCypress() || isJest() } @@ -115,7 +104,6 @@ const environment = { isInThread: () => { return inThread }, - isDockerisedTest, } // threading can cause memory issues with node-ts in development diff --git a/packages/server/src/tests/utilities/TestConfiguration.ts b/packages/server/src/tests/utilities/TestConfiguration.ts index f12669f5e7..f98efb661f 100644 --- a/packages/server/src/tests/utilities/TestConfiguration.ts +++ b/packages/server/src/tests/utilities/TestConfiguration.ts @@ -168,14 +168,6 @@ class TestConfiguration { } if (this.allApps) { cleanup(this.allApps.map(app => app.appId)) - - if (env.isDockerisedTest()) { - await this._req( - null, - { appId: this.prodApp.appId }, - controllers.app.destroy - ) - } } if (this.server) { diff --git a/packages/worker/src/environment.ts b/packages/worker/src/environment.ts index aab30883ea..52fec210bc 100644 --- a/packages/worker/src/environment.ts +++ b/packages/worker/src/environment.ts @@ -4,17 +4,7 @@ function isDev() { return process.env.NODE_ENV !== "production" } -function isDockerisedTest() { - return process.env.DOCKERISED_TEST === "true" -} - function isTest() { - if (isDockerisedTest()) { - // While we are migrating all the tests to use docker instead of mocked in memory, - // we want to keep treating the old tests as tests, - // but the new tests should not make a difference - return false - } return ( process.env.NODE_ENV === "jest" || process.env.NODE_ENV === "cypress" || From 9a12fd432aebe31c8c4985e31ee03ef3c45a8ff4 Mon Sep 17 00:00:00 2001 From: adrinr Date: Thu, 2 Feb 2023 10:53:19 +0000 Subject: [PATCH 42/56] Remove faker --- packages/server/package.json | 1 - packages/server/yarn.lock | 5 ----- 2 files changed, 6 deletions(-) diff --git a/packages/server/package.json b/packages/server/package.json index fcd8f71e5e..6017be72fe 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -122,7 +122,6 @@ "@babel/core": "7.17.4", "@babel/preset-env": "7.16.11", "@budibase/standard-components": "^0.9.139", - "@faker-js/faker": "^7.6.0", "@jest/test-sequencer": "24.9.0", "@swc/core": "^1.3.25", "@swc/jest": "^0.2.24", diff --git a/packages/server/yarn.lock b/packages/server/yarn.lock index 8ffb9586d9..18017a2ac1 100644 --- a/packages/server/yarn.lock +++ b/packages/server/yarn.lock @@ -1528,11 +1528,6 @@ pump "^3.0.0" secure-json-parse "^2.1.0" -"@faker-js/faker@^7.6.0": - version "7.6.0" - resolved "https://registry.yarnpkg.com/@faker-js/faker/-/faker-7.6.0.tgz#9ea331766084288634a9247fcd8b84f16ff4ba07" - integrity sha512-XK6BTq1NDMo9Xqw/YkYyGjSsg44fbNwYRx7QK2CuoQgyy+f1rrTDHoExVM5PsyXCtfl2vs2vVJ0MN0yN6LppRw== - "@google-cloud/firestore@5.0.2": version "5.0.2" resolved "https://registry.yarnpkg.com/@google-cloud/firestore/-/firestore-5.0.2.tgz#36923fde45987f928a220d347f341c5602f9e340" From 7d3c24d25755786dd1f34899a5280885665d64da Mon Sep 17 00:00:00 2001 From: adrinr Date: Mon, 6 Feb 2023 13:07:21 +0000 Subject: [PATCH 43/56] Add pg test container --- hosting/docker-compose.test.yaml | 8 +++++ .../tests/utilities/testContainerUtils.ts | 29 +++++++++++++------ packages/server/jest-testcontainers-config.js | 2 ++ 3 files changed, 30 insertions(+), 9 deletions(-) diff --git a/hosting/docker-compose.test.yaml b/hosting/docker-compose.test.yaml index dfd78621c5..58936d233a 100644 --- a/hosting/docker-compose.test.yaml +++ b/hosting/docker-compose.test.yaml @@ -45,3 +45,11 @@ services: - 6379 healthcheck: test: ["CMD", "redis-cli", "ping"] + + postgres: + image: postgres + restart: on-failure + ports: + - 5432 + environment: + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} diff --git a/packages/backend-core/tests/utilities/testContainerUtils.ts b/packages/backend-core/tests/utilities/testContainerUtils.ts index a5a779a00b..f7f1b34b73 100644 --- a/packages/backend-core/tests/utilities/testContainerUtils.ts +++ b/packages/backend-core/tests/utilities/testContainerUtils.ts @@ -10,20 +10,29 @@ function getTestContainerSettings(serverName: string, key: string) { return entry[1] } -function getCouchConfig() { - const port = getTestContainerSettings("COUCHDB-SERVICE", "PORT_5984") +function getContainerInfo(containerName: string, port: number) { + const assignedPort = getTestContainerSettings( + containerName.toUpperCase(), + `PORT_${port}` + ) + const host = getTestContainerSettings(containerName.toUpperCase(), "IP") return { - port, - url: `http://${getTestContainerSettings("COUCHDB-SERVICE", "IP")}:${port}`, + port: assignedPort, + host, + url: `http://${host}:${assignedPort}`, } } +function getCouchConfig() { + return getContainerInfo("couchdb-service", 5984) +} + function getMinioConfig() { - const port = getTestContainerSettings("MINIO-SERVICE", "PORT_9000") - return { - port, - url: `http://${getTestContainerSettings("MINIO-SERVICE", "IP")}:${port}`, - } + return getContainerInfo("minio-service", 9000) +} + +function getPostgresConfig() { + return getContainerInfo("postgres", 5432) } export function setupEnv(...envs: any[]) { @@ -32,6 +41,8 @@ export function setupEnv(...envs: any[]) { { key: "COUCH_DB_URL", value: getCouchConfig().url }, { key: "MINIO_PORT", value: getMinioConfig().port }, { key: "MINIO_URL", value: getMinioConfig().url }, + { key: "POSTGRES_HOST", value: getPostgresConfig().host }, + { key: "POSTGRES_PORT", value: getPostgresConfig().port }, ] for (const config of configs.filter(x => x.value !== null)) { diff --git a/packages/server/jest-testcontainers-config.js b/packages/server/jest-testcontainers-config.js index 8ac0f0cd9d..48f9a9b21b 100644 --- a/packages/server/jest-testcontainers-config.js +++ b/packages/server/jest-testcontainers-config.js @@ -3,6 +3,8 @@ require("dotenv").config({ path: join(__dirname, "..", "..", "hosting", ".env"), }) +process.env.POSTGRES_PASSWORD = "password" + const jestTestcontainersConfigGenerator = require("../../jestTestcontainersConfigGenerator") module.exports = jestTestcontainersConfigGenerator() From 3d9a208bfb47dfda5ef948ee71a1d873fbc6e5a5 Mon Sep 17 00:00:00 2001 From: adrinr Date: Mon, 6 Feb 2023 14:54:49 +0000 Subject: [PATCH 44/56] Fix tests --- .../src/integration-test/postgres.spec.ts | 231 +++++++++++------- 1 file changed, 148 insertions(+), 83 deletions(-) diff --git a/packages/server/src/integration-test/postgres.spec.ts b/packages/server/src/integration-test/postgres.spec.ts index ab33a932a2..4c69d790e3 100644 --- a/packages/server/src/integration-test/postgres.spec.ts +++ b/packages/server/src/integration-test/postgres.spec.ts @@ -13,27 +13,31 @@ import { Table, } from "@budibase/types" import _ from "lodash" -import { generator } from "@budibase/backend-core/tests" +import { generator, structures } from "@budibase/backend-core/tests" import { utils } from "@budibase/backend-core" const config = setup.getConfig()! jest.unmock("node-fetch") +jest.unmock("pg") -// TODO: Waiting for the test image to exist describe("row api - postgres", () => { - let apiKey, - makeRequest: MakeRequestResponse, + let makeRequest: MakeRequestResponse, postgresDatasource: Datasource, postgresTable: Table, auxPostgresTable: Table - const host = generator.ip() - const port = generator.natural() + const host = process.env.POSTGRES_HOST! + const port = process.env.POSTGRES_PORT! + + beforeAll(async () => { + await config.init() + const apiKey = await config.generateApiKey() + + makeRequest = generateMakeRequest(apiKey, true) + }) beforeEach(async () => { - await config.init() - apiKey = await config.generateApiKey() postgresDatasource = await config.createDatasource({ datasource: { type: "datasource", @@ -43,8 +47,8 @@ describe("row api - postgres", () => { host, port, database: "postgres", - user: "root", - password: "root", + user: "postgres", + password: process.env.POSTGRES_PASSWORD!, schema: "public", ssl: false, rejectUnauthorized: false, @@ -53,11 +57,41 @@ describe("row api - postgres", () => { }, }) - makeRequest = generateMakeRequest(apiKey, true) + auxPostgresTable = await config.createTable({ + name: generator.word({ length: 10 }), + type: "external", + primary: ["id"], + schema: { + id: { + name: "id", + type: FieldType.AUTO, + constraints: { + presence: true, + }, + }, + title: { + name: "title", + type: FieldType.STRING, + constraints: { + presence: true, + }, + }, + }, + sourceId: postgresDatasource._id, + }) postgresTable = await config.createTable({ - name: generator.word(), + name: generator.word({ length: 10 }), + type: "external", + primary: ["id"], schema: { + id: { + name: "id", + type: FieldType.AUTO, + constraints: { + presence: true, + }, + }, name: { name: "name", type: FieldType.STRING, @@ -73,30 +107,16 @@ describe("row api - postgres", () => { name: "value", type: FieldType.NUMBER, }, - }, - sourceId: postgresDatasource._id, - }) - - auxPostgresTable = await config.createTable({ - name: generator.word(), - schema: { - title: { - name: "title", - type: FieldType.STRING, - constraints: { - presence: true, - }, - }, linkedField: { type: FieldType.LINK, constraints: { type: "array", - presence: true, + presence: false, }, fieldName: "foreignField", name: "linkedField", - relationshipType: RelationshipTypes.MANY_TO_MANY, - tableId: postgresTable._id, + relationshipType: RelationshipTypes.ONE_TO_MANY, + tableId: auxPostgresTable._id, }, }, sourceId: postgresDatasource._id, @@ -107,34 +127,67 @@ describe("row api - postgres", () => { await config.end() }) - function createRandomRow() { + const randomInteger = () => generator.integer({ min: 0, max: 10000 }) + + function makeRandomRow() { return { name: generator.name(), description: generator.paragraph(), - value: generator.integer(), + value: randomInteger(), } } - function createRow( + async function createRow( row: { name: string description: string value: number }, - tableId?: string + tableId?: string, + createForeignRow?: boolean ) { - return config.createRow({ + if (createForeignRow) { + const foreignRow = await config.createRow({ + tableId: auxPostgresTable._id, + title: generator.name(), + }) + + row = { + ...row, + [`fk_${auxPostgresTable.name}_foreignField`]: foreignRow.id, + } + } + + return await config.createRow({ tableId: tableId || postgresTable._id, ...row, }) } + async function createDefaultPgTable() { + return await config.createTable({ + name: generator.word({ length: 10 }), + type: "external", + primary: ["id"], + schema: { + id: { + name: "id", + type: FieldType.AUTO, + constraints: { + presence: true, + }, + }, + }, + sourceId: postgresDatasource._id, + }) + } + async function populateRows(count: number, tableId?: string) { return await Promise.all( Array(count) .fill({}) .map(async () => { - const rowData = createRandomRow() + const rowData = makeRandomRow() return { rowData, row: await createRow(rowData, tableId || postgresTable._id), @@ -160,7 +213,7 @@ describe("row api - postgres", () => { rejectUnauthorized: false, schema: "public", ssl: false, - user: "root", + user: "postgres", }, plus: true, source: "POSTGRES", @@ -169,6 +222,7 @@ describe("row api - postgres", () => { _rev: expect.any(String), createdAt: expect.any(String), updatedAt: expect.any(String), + entities: expect.any(Object), }) }) @@ -177,7 +231,7 @@ describe("row api - postgres", () => { makeRequest("post", `/api/${tableId}/rows`, body) test("Given than no row exists, adding a new row persists it", async () => { - const newRow = createRandomRow() + const newRow = makeRandomRow() const res = await createRow(postgresTable._id, newRow) @@ -185,17 +239,20 @@ describe("row api - postgres", () => { const persistedRows = await config.getRows(postgresTable._id!) expect(persistedRows).toHaveLength(1) - expect(persistedRows).toEqual([ - expect.objectContaining({ - ...res.body, - ...newRow, - }), - ]) + + const expected = { + ...res.body, + ...newRow, + } + + // TODO: check why this is being returned from the creation + delete expected.linkedField + expect(persistedRows).toEqual([expect.objectContaining(expected)]) }) test("Given than no row exists, multiple rows can be persisted", async () => { const numberOfRows = 10 - const newRows = Array(numberOfRows).fill(createRandomRow()) + const newRows = Array(numberOfRows).fill(makeRandomRow()) for (const newRow of newRows) { const res = await createRow(postgresTable._id, newRow) @@ -218,22 +275,25 @@ describe("row api - postgres", () => { let { row } = _.sample(await populateRows(10))! const newName = generator.name() - const newValue = generator.integer() + const newValue = randomInteger() const updatedRow = { ...row, name: newName, value: newValue, } + // TODO: check why this is being returned from the creation + delete (updatedRow as any).linkedField + const res = await updateRow(postgresTable._id, updatedRow) expect(res.status).toBe(200) - const persistedRow = await config.getRow(postgresTable._id!, row._id!) + const persistedRow = await config.getRow(postgresTable._id!, row.id) expect(persistedRow).toEqual( expect.objectContaining({ - _id: row._id, + id: row.id, name: newName, value: newValue, }) @@ -258,9 +318,9 @@ describe("row api - postgres", () => { const persistedRows = await config.getRows(postgresTable._id!) expect(persistedRows).toHaveLength(numberOfInitialRows - 1) - expect(row._id).toBeDefined() + expect(row.id).toBeDefined() expect(persistedRows).not.toContain( - expect.objectContaining({ _id: row._id }) + expect.objectContaining({ _id: row.id }) ) }) @@ -279,7 +339,7 @@ describe("row api - postgres", () => { for (const row of rows) { expect(persistedRows).not.toContain( - expect.objectContaining({ _id: row._id }) + expect.objectContaining({ _id: row.id }) ) } }) @@ -292,7 +352,7 @@ describe("row api - postgres", () => { test("Given than a table have a single row, the row can be retrieved successfully", async () => { const [{ rowData, row }] = await populateRows(1) - const res = await getRow(postgresTable._id, row._id) + const res = await getRow(postgresTable._id, row.id) expect(res.status).toBe(200) @@ -303,7 +363,7 @@ describe("row api - postgres", () => { const rows = await populateRows(10) const { rowData, row } = _.sample(rows)! - const res = await getRow(postgresTable._id, row._id) + const res = await getRow(postgresTable._id, row.id) expect(res.status).toBe(200) @@ -313,26 +373,25 @@ describe("row api - postgres", () => { test("given having rows with relation data, only the ids are retrieved", async () => { let [{ row }] = await populateRows(1) - const foreignRow = await config.createRow({ + await config.createRow({ tableId: auxPostgresTable._id, title: generator.sentence(), - linkedField: row._id, + linkedField: row.id, }) - const res = await getRow(postgresTable._id, row._id) + const res = await getRow(postgresTable._id, row.id) expect(res.status).toBe(200) + const expected = { ...row } + // TODO: check why this is being returned from the creation + delete expected.linkedField expect(res.body).toEqual({ - ...row, - foreignField: [ - { - _id: foreignRow._id, - }, - ], - createdAt: expect.any(String), - updatedAt: expect.any(String), + ...expected, + _id: expect.any(String), + _rev: expect.any(String), }) + expect(res.body.foreignField).toBeUndefined() }) }) @@ -346,7 +405,11 @@ describe("row api - postgres", () => { expect(res.status).toBe(200) - expect(res.body).toEqual({ rows: [] }) + expect(res.body).toEqual({ + rows: [], + bookmark: null, + hasNextPage: false, + }) }) test("Given than a table has multiple rows, search without query returns all of them", async () => { @@ -361,15 +424,17 @@ describe("row api - postgres", () => { rows: expect.arrayContaining( rows.map(r => expect.objectContaining(r.rowData)) ), + bookmark: null, + hasNextPage: false, }) expect(res.body.rows).toHaveLength(rowsCount) }) test("Given than multiple tables have multiple rows, search only return the requested ones", async () => { - await populateRows(2, (await config.createTable())._id) + await populateRows(2, (await createDefaultPgTable())._id) const rowsCount = 6 await populateRows(rowsCount) - await populateRows(2, (await config.createTable())._id) + await populateRows(2, (await createDefaultPgTable())._id) const res = await search(postgresTable._id) @@ -385,12 +450,12 @@ describe("row api - postgres", () => { ...Array(2).fill({ name, description: generator.paragraph(), - value: generator.integer(), + value: randomInteger(), }), ...Array(2).fill({ name: `${name}${utils.newid()}`, description: generator.paragraph(), - value: generator.integer(), + value: randomInteger(), }), ] @@ -412,6 +477,8 @@ describe("row api - postgres", () => { expect(res.body).toEqual({ rows: expect.arrayContaining(rowsToFilter.map(expect.objectContaining)), + bookmark: null, + hasNextPage: false, }) expect(res.body.rows).toHaveLength(4) }) @@ -430,7 +497,7 @@ describe("row api - postgres", () => { describe("sort", () => { beforeEach(async () => { - const defaultValue = createRandomRow() + const defaultValue = makeRandomRow() await createRow( { @@ -520,35 +587,33 @@ describe("row api - postgres", () => { }) }) - describe("enrich a row", () => { + describe("get enriched row", () => { const getAll = (tableId: string | undefined, rowId: string | undefined) => makeRequest("get", `/api/${tableId}/${rowId}/enrich`) - test("given having rows with relation data, enrich populates the", async () => { - let [{ row }] = await populateRows(1) - + test("given having rows with relation data, enrich populates the foreign field", async () => { const foreignRow = await config.createRow({ tableId: auxPostgresTable._id, title: generator.name(), - linkedField: row._id, }) - const res = await getAll(postgresTable._id, row._id) + const rowData = { + ...makeRandomRow(), + [`fk_${auxPostgresTable.name}_foreignField`]: foreignRow.id, + } + const row = await createRow(rowData) + + const res = await getAll(postgresTable._id, row.id) expect(res.status).toBe(200) expect(res.body).toEqual({ ...row, - foreignField: [ + linkedField: [ { ...foreignRow, - linkedField: [{ _id: row._id }], - createdAt: expect.any(String), - updatedAt: expect.any(String), }, ], - createdAt: expect.any(String), - updatedAt: expect.any(String), }) }) }) @@ -582,10 +647,10 @@ describe("row api - postgres", () => { }) test("Given than multiple tables have multiple rows, get returns the requested ones", async () => { - await populateRows(2, (await config.createTable())._id) + await populateRows(2, (await createDefaultPgTable())._id) const rowsCount = 6 await populateRows(rowsCount, postgresTable._id) - await populateRows(2, (await config.createTable())._id) + await populateRows(2, (await createDefaultPgTable())._id) const res = await getAll(postgresTable._id) From 29503d1244bece876b6bf2ca2e4b34a82dac2382 Mon Sep 17 00:00:00 2001 From: adrinr Date: Mon, 6 Feb 2023 17:30:33 +0000 Subject: [PATCH 45/56] Fix tests --- .../src/integration-test/postgres.spec.ts | 21 ++++--------- packages/server/src/integrations/base/sql.ts | 30 ++++++++++++++----- 2 files changed, 28 insertions(+), 23 deletions(-) diff --git a/packages/server/src/integration-test/postgres.spec.ts b/packages/server/src/integration-test/postgres.spec.ts index 4c69d790e3..24671498b9 100644 --- a/packages/server/src/integration-test/postgres.spec.ts +++ b/packages/server/src/integration-test/postgres.spec.ts @@ -18,7 +18,6 @@ import { utils } from "@budibase/backend-core" const config = setup.getConfig()! -jest.unmock("node-fetch") jest.unmock("pg") describe("row api - postgres", () => { @@ -127,13 +126,11 @@ describe("row api - postgres", () => { await config.end() }) - const randomInteger = () => generator.integer({ min: 0, max: 10000 }) - function makeRandomRow() { return { name: generator.name(), description: generator.paragraph(), - value: randomInteger(), + value: generator.age(), } } @@ -245,8 +242,6 @@ describe("row api - postgres", () => { ...newRow, } - // TODO: check why this is being returned from the creation - delete expected.linkedField expect(persistedRows).toEqual([expect.objectContaining(expected)]) }) @@ -275,16 +270,13 @@ describe("row api - postgres", () => { let { row } = _.sample(await populateRows(10))! const newName = generator.name() - const newValue = randomInteger() + const newValue = generator.age() const updatedRow = { ...row, name: newName, value: newValue, } - // TODO: check why this is being returned from the creation - delete (updatedRow as any).linkedField - const res = await updateRow(postgresTable._id, updatedRow) expect(res.status).toBe(200) @@ -383,11 +375,8 @@ describe("row api - postgres", () => { expect(res.status).toBe(200) - const expected = { ...row } - // TODO: check why this is being returned from the creation - delete expected.linkedField expect(res.body).toEqual({ - ...expected, + ...row, _id: expect.any(String), _rev: expect.any(String), }) @@ -450,12 +439,12 @@ describe("row api - postgres", () => { ...Array(2).fill({ name, description: generator.paragraph(), - value: randomInteger(), + value: generator.age(), }), ...Array(2).fill({ name: `${name}${utils.newid()}`, description: generator.paragraph(), - value: randomInteger(), + value: generator.age(), }), ] diff --git a/packages/server/src/integrations/base/sql.ts b/packages/server/src/integrations/base/sql.ts index 1d3c57414c..fe96248ce5 100644 --- a/packages/server/src/integrations/base/sql.ts +++ b/packages/server/src/integrations/base/sql.ts @@ -89,13 +89,22 @@ function parseFilters(filters: SearchFilters | undefined): SearchFilters { function generateSelectStatement( json: QueryJson, - knex: Knex + knex: Knex, + excludeJoinColumns = false ): (string | Knex.Raw)[] { const { resource, meta } = json const schema = meta?.table?.schema - return resource!.fields.map(field => { + + return resource!.fields.reduce<(string | Knex.Raw)[]>((p, field) => { const fieldNames = field.split(/\./g) const tableName = fieldNames[0] + if ( + meta?.table?.name && + excludeJoinColumns && + tableName !== meta.table.name + ) { + return p + } const columnName = fieldNames[1] if ( columnName && @@ -104,13 +113,18 @@ function generateSelectStatement( ) { const externalType = schema[columnName].externalType if (externalType?.includes("money")) { - return knex.raw( - `"${tableName}"."${columnName}"::money::numeric as "${field}"` + p.push( + knex.raw( + `"${tableName}"."${columnName}"::money::numeric as "${field}"` + ) ) + return p } } - return `${field} as ${field}` - }) + + p.push(`${field} as ${field}`) + return p + }, []) } class InternalBuilder { @@ -396,7 +410,9 @@ class InternalBuilder { if (opts.disableReturning) { return query.insert(parsedBody) } else { - return query.insert(parsedBody).returning("*") + return query + .insert(parsedBody) + .returning(generateSelectStatement(json, knex, true)) } } From c87efb786684db1aff24ae533c6621b9c0d7315e Mon Sep 17 00:00:00 2001 From: adrinr Date: Mon, 6 Feb 2023 17:37:00 +0000 Subject: [PATCH 46/56] Don't return foreign keys on updates and deletes --- packages/server/src/integration-test/postgres.spec.ts | 1 + packages/server/src/integrations/base/sql.ts | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/server/src/integration-test/postgres.spec.ts b/packages/server/src/integration-test/postgres.spec.ts index 24671498b9..59dc4b1894 100644 --- a/packages/server/src/integration-test/postgres.spec.ts +++ b/packages/server/src/integration-test/postgres.spec.ts @@ -280,6 +280,7 @@ describe("row api - postgres", () => { const res = await updateRow(postgresTable._id, updatedRow) expect(res.status).toBe(200) + expect(res.body).toEqual(updatedRow) const persistedRow = await config.getRow(postgresTable._id!, row.id) diff --git a/packages/server/src/integrations/base/sql.ts b/packages/server/src/integrations/base/sql.ts index fe96248ce5..2a3eaec69f 100644 --- a/packages/server/src/integrations/base/sql.ts +++ b/packages/server/src/integrations/base/sql.ts @@ -497,7 +497,9 @@ class InternalBuilder { if (opts.disableReturning) { return query.update(parsedBody) } else { - return query.update(parsedBody).returning("*") + return query + .update(parsedBody) + .returning(generateSelectStatement(json, knex, true)) } } @@ -512,7 +514,7 @@ class InternalBuilder { if (opts.disableReturning) { return query.delete() } else { - return query.delete().returning("*") + return query.delete().returning(generateSelectStatement(json, knex, true)) } } } From 4908cc538736720834490663fe37a4a7496b4695 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Mon, 6 Feb 2023 19:43:08 +0000 Subject: [PATCH 47/56] Spin up postgres only on the postgres test --- hosting/docker-compose.test.yaml | 8 -------- .../tests/utilities/testContainerUtils.ts | 15 ++++++--------- packages/server/jest-testcontainers-config.js | 2 -- .../server/src/integration-test/postgres.spec.ts | 15 ++++++++++++--- 4 files changed, 18 insertions(+), 22 deletions(-) diff --git a/hosting/docker-compose.test.yaml b/hosting/docker-compose.test.yaml index 58936d233a..dfd78621c5 100644 --- a/hosting/docker-compose.test.yaml +++ b/hosting/docker-compose.test.yaml @@ -45,11 +45,3 @@ services: - 6379 healthcheck: test: ["CMD", "redis-cli", "ping"] - - postgres: - image: postgres - restart: on-failure - ports: - - 5432 - environment: - POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} diff --git a/packages/backend-core/tests/utilities/testContainerUtils.ts b/packages/backend-core/tests/utilities/testContainerUtils.ts index f7f1b34b73..22198bd496 100644 --- a/packages/backend-core/tests/utilities/testContainerUtils.ts +++ b/packages/backend-core/tests/utilities/testContainerUtils.ts @@ -1,4 +1,7 @@ -function getTestContainerSettings(serverName: string, key: string) { +function getTestContainerSettings( + serverName: string, + key: string +): string | null { const entry = Object.entries(global).find( ([k]) => k.includes(`_${serverName.toUpperCase()}`) && @@ -19,7 +22,7 @@ function getContainerInfo(containerName: string, port: number) { return { port: assignedPort, host, - url: `http://${host}:${assignedPort}`, + url: host && assignedPort && `http://${host}:${assignedPort}`, } } @@ -31,21 +34,15 @@ function getMinioConfig() { return getContainerInfo("minio-service", 9000) } -function getPostgresConfig() { - return getContainerInfo("postgres", 5432) -} - export function setupEnv(...envs: any[]) { const configs = [ { key: "COUCH_DB_PORT", value: getCouchConfig().port }, { key: "COUCH_DB_URL", value: getCouchConfig().url }, { key: "MINIO_PORT", value: getMinioConfig().port }, { key: "MINIO_URL", value: getMinioConfig().url }, - { key: "POSTGRES_HOST", value: getPostgresConfig().host }, - { key: "POSTGRES_PORT", value: getPostgresConfig().port }, ] - for (const config of configs.filter(x => x.value !== null)) { + for (const config of configs.filter(x => !!x.value)) { for (const env of envs) { env._set(config.key, config.value) } diff --git a/packages/server/jest-testcontainers-config.js b/packages/server/jest-testcontainers-config.js index 48f9a9b21b..8ac0f0cd9d 100644 --- a/packages/server/jest-testcontainers-config.js +++ b/packages/server/jest-testcontainers-config.js @@ -3,8 +3,6 @@ require("dotenv").config({ path: join(__dirname, "..", "..", "hosting", ".env"), }) -process.env.POSTGRES_PASSWORD = "password" - const jestTestcontainersConfigGenerator = require("../../jestTestcontainersConfigGenerator") module.exports = jestTestcontainersConfigGenerator() diff --git a/packages/server/src/integration-test/postgres.spec.ts b/packages/server/src/integration-test/postgres.spec.ts index 59dc4b1894..75c232987b 100644 --- a/packages/server/src/integration-test/postgres.spec.ts +++ b/packages/server/src/integration-test/postgres.spec.ts @@ -13,8 +13,9 @@ import { Table, } from "@budibase/types" import _ from "lodash" -import { generator, structures } from "@budibase/backend-core/tests" +import { generator } from "@budibase/backend-core/tests" import { utils } from "@budibase/backend-core" +import { GenericContainer } from "testcontainers" const config = setup.getConfig()! @@ -26,10 +27,18 @@ describe("row api - postgres", () => { postgresTable: Table, auxPostgresTable: Table - const host = process.env.POSTGRES_HOST! - const port = process.env.POSTGRES_PORT! + let host: string + let port: number beforeAll(async () => { + const container = await new GenericContainer("postgres") + .withExposedPorts(5432) + .withEnv("POSTGRES_PASSWORD", "password") + .start() + + host = container.getContainerIpAddress() + port = container.getMappedPort(5432) + await config.init() const apiKey = await config.generateApiKey() From 71199c06ec6a6b41e6a25104cb5c40ab218bd168 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Mon, 6 Feb 2023 19:49:30 +0000 Subject: [PATCH 48/56] Stop container after tests --- packages/server/src/integration-test/postgres.spec.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/server/src/integration-test/postgres.spec.ts b/packages/server/src/integration-test/postgres.spec.ts index 75c232987b..ba65f01eb6 100644 --- a/packages/server/src/integration-test/postgres.spec.ts +++ b/packages/server/src/integration-test/postgres.spec.ts @@ -15,7 +15,7 @@ import { import _ from "lodash" import { generator } from "@budibase/backend-core/tests" import { utils } from "@budibase/backend-core" -import { GenericContainer } from "testcontainers" +import { GenericContainer, StartedTestContainer } from "testcontainers" const config = setup.getConfig()! @@ -30,14 +30,16 @@ describe("row api - postgres", () => { let host: string let port: number + let postgresContainer: StartedTestContainer + beforeAll(async () => { - const container = await new GenericContainer("postgres") + postgresContainer = await new GenericContainer("postgres") .withExposedPorts(5432) .withEnv("POSTGRES_PASSWORD", "password") .start() - host = container.getContainerIpAddress() - port = container.getMappedPort(5432) + host = postgresContainer.getContainerIpAddress() + port = postgresContainer.getMappedPort(5432) await config.init() const apiKey = await config.generateApiKey() @@ -132,6 +134,7 @@ describe("row api - postgres", () => { }) afterAll(async () => { + await postgresContainer?.stop() await config.end() }) From 015a91c1833a3168416de5ac7258f654aa58dbd4 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Mon, 6 Feb 2023 20:12:08 +0000 Subject: [PATCH 49/56] Increase timeout --- .../server/src/integration-test/postgres.spec.ts | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/packages/server/src/integration-test/postgres.spec.ts b/packages/server/src/integration-test/postgres.spec.ts index ba65f01eb6..879338dfdb 100644 --- a/packages/server/src/integration-test/postgres.spec.ts +++ b/packages/server/src/integration-test/postgres.spec.ts @@ -15,10 +15,12 @@ import { import _ from "lodash" import { generator } from "@budibase/backend-core/tests" import { utils } from "@budibase/backend-core" -import { GenericContainer, StartedTestContainer } from "testcontainers" +import { GenericContainer } from "testcontainers" const config = setup.getConfig()! +jest.setTimeout(30000) + jest.unmock("pg") describe("row api - postgres", () => { @@ -30,16 +32,14 @@ describe("row api - postgres", () => { let host: string let port: number - let postgresContainer: StartedTestContainer - beforeAll(async () => { - postgresContainer = await new GenericContainer("postgres") + const container = await new GenericContainer("postgres") .withExposedPorts(5432) .withEnv("POSTGRES_PASSWORD", "password") .start() - host = postgresContainer.getContainerIpAddress() - port = postgresContainer.getMappedPort(5432) + host = container.getContainerIpAddress() + port = container.getMappedPort(5432) await config.init() const apiKey = await config.generateApiKey() @@ -134,7 +134,6 @@ describe("row api - postgres", () => { }) afterAll(async () => { - await postgresContainer?.stop() await config.end() }) From afa282ecbf439b2bc08de4de154e94c37ad895dc Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Mon, 6 Feb 2023 20:47:49 +0000 Subject: [PATCH 50/56] Fix sql when no resource --- packages/server/src/integrations/base/sql.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/server/src/integrations/base/sql.ts b/packages/server/src/integrations/base/sql.ts index 2a3eaec69f..3ee5bda673 100644 --- a/packages/server/src/integrations/base/sql.ts +++ b/packages/server/src/integrations/base/sql.ts @@ -91,11 +91,15 @@ function generateSelectStatement( json: QueryJson, knex: Knex, excludeJoinColumns = false -): (string | Knex.Raw)[] { +): (string | Knex.Raw)[] | "*" { const { resource, meta } = json const schema = meta?.table?.schema - return resource!.fields.reduce<(string | Knex.Raw)[]>((p, field) => { + if (!resource) { + return "*" + } + + return resource.fields.reduce<(string | Knex.Raw)[]>((p, field) => { const fieldNames = field.split(/\./g) const tableName = fieldNames[0] if ( @@ -406,6 +410,7 @@ class InternalBuilder { delete parsedBody[key] } } + // mysql can't use returning if (opts.disableReturning) { return query.insert(parsedBody) From 7474099bac7a6de028ebce3f17d374b96996b21f Mon Sep 17 00:00:00 2001 From: adrinr Date: Tue, 7 Feb 2023 10:12:42 +0000 Subject: [PATCH 51/56] Fix pg test --- packages/server/src/integration-test/postgres.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/src/integration-test/postgres.spec.ts b/packages/server/src/integration-test/postgres.spec.ts index 879338dfdb..d1d292db2c 100644 --- a/packages/server/src/integration-test/postgres.spec.ts +++ b/packages/server/src/integration-test/postgres.spec.ts @@ -58,7 +58,7 @@ describe("row api - postgres", () => { port, database: "postgres", user: "postgres", - password: process.env.POSTGRES_PASSWORD!, + password: "password", schema: "public", ssl: false, rejectUnauthorized: false, From 79e42abc1e94b2f085d4b305b2c5463460988dd6 Mon Sep 17 00:00:00 2001 From: adrinr Date: Tue, 7 Feb 2023 10:46:34 +0000 Subject: [PATCH 52/56] Tidy tests --- .../src/integration-test/postgres.spec.ts | 263 ++++++++++-------- packages/server/src/integrations/base/sql.ts | 18 +- 2 files changed, 153 insertions(+), 128 deletions(-) diff --git a/packages/server/src/integration-test/postgres.spec.ts b/packages/server/src/integration-test/postgres.spec.ts index d1d292db2c..bc909a66f0 100644 --- a/packages/server/src/integration-test/postgres.spec.ts +++ b/packages/server/src/integration-test/postgres.spec.ts @@ -26,7 +26,7 @@ jest.unmock("pg") describe("row api - postgres", () => { let makeRequest: MakeRequestResponse, postgresDatasource: Datasource, - postgresTable: Table, + primaryPostgresTable: Table, auxPostgresTable: Table let host: string @@ -90,7 +90,7 @@ describe("row api - postgres", () => { sourceId: postgresDatasource._id, }) - postgresTable = await config.createTable({ + primaryPostgresTable = await config.createTable({ name: generator.word({ length: 10 }), type: "external", primary: ["id"], @@ -137,7 +137,7 @@ describe("row api - postgres", () => { await config.end() }) - function makeRandomRow() { + function generateRandomPrimaryRowData() { return { name: generator.name(), description: generator.paragraph(), @@ -145,31 +145,34 @@ describe("row api - postgres", () => { } } - async function createRow( - row: { + async function createPrimaryRow(opts: { + rowData: { name: string description: string value: number - }, - tableId?: string, + } createForeignRow?: boolean - ) { - if (createForeignRow) { - const foreignRow = await config.createRow({ + }) { + let { rowData } = opts + let foreignRow: Row | undefined + if (opts?.createForeignRow) { + foreignRow = await config.createRow({ tableId: auxPostgresTable._id, title: generator.name(), }) - row = { - ...row, + rowData = { + ...rowData, [`fk_${auxPostgresTable.name}_foreignField`]: foreignRow.id, } } - return await config.createRow({ - tableId: tableId || postgresTable._id, - ...row, + const row = await config.createRow({ + tableId: primaryPostgresTable._id, + ...rowData, }) + + return { row, foreignRow } } async function createDefaultPgTable() { @@ -190,21 +193,23 @@ describe("row api - postgres", () => { }) } - async function populateRows(count: number, tableId?: string) { + async function populatePrimaryRows(count: number) { return await Promise.all( Array(count) .fill({}) .map(async () => { - const rowData = makeRandomRow() + const rowData = generateRandomPrimaryRowData() return { rowData, - row: await createRow(rowData, tableId || postgresTable._id), + ...(await createPrimaryRow({ + rowData, + })), } }) ) } - test("validate table schema", async () => { + it("validate table schema", async () => { const res = await makeRequest( "get", `/api/datasources/${postgresDatasource._id}` @@ -234,18 +239,18 @@ describe("row api - postgres", () => { }) }) - describe("create a row", () => { + describe("POST /api/:tableId/rows", () => { const createRow = (tableId: string | undefined, body: object) => makeRequest("post", `/api/${tableId}/rows`, body) - test("Given than no row exists, adding a new row persists it", async () => { - const newRow = makeRandomRow() + it("Given than no row exists, adding a new one persists it", async () => { + const newRow = generateRandomPrimaryRowData() - const res = await createRow(postgresTable._id, newRow) + const res = await createRow(primaryPostgresTable._id, newRow) expect(res.status).toBe(200) - const persistedRows = await config.getRows(postgresTable._id!) + const persistedRows = await config.getRows(primaryPostgresTable._id!) expect(persistedRows).toHaveLength(1) const expected = { @@ -256,16 +261,16 @@ describe("row api - postgres", () => { expect(persistedRows).toEqual([expect.objectContaining(expected)]) }) - test("Given than no row exists, multiple rows can be persisted", async () => { + it("Given than no row exists, multiple rows can be persisted", async () => { const numberOfRows = 10 - const newRows = Array(numberOfRows).fill(makeRandomRow()) + const newRows = Array(numberOfRows).fill(generateRandomPrimaryRowData()) for (const newRow of newRows) { - const res = await createRow(postgresTable._id, newRow) + const res = await createRow(primaryPostgresTable._id, newRow) expect(res.status).toBe(200) } - const persistedRows = await config.getRows(postgresTable._id!) + const persistedRows = await config.getRows(primaryPostgresTable._id!) expect(persistedRows).toHaveLength(numberOfRows) expect(persistedRows).toEqual( expect.arrayContaining(newRows.map(expect.objectContaining)) @@ -273,12 +278,12 @@ describe("row api - postgres", () => { }) }) - describe("update a row", () => { + describe("PATCH /api/:tableId/rows", () => { const updateRow = (tableId: string | undefined, body: Row) => makeRequest("patch", `/api/${tableId}/rows`, body) - test("Given than a row exists, updating it persists it", async () => { - let { row } = _.sample(await populateRows(10))! + it("Given than a row exists, updating it persists it", async () => { + let { row } = _.sample(await populatePrimaryRows(10))! const newName = generator.name() const newValue = generator.age() @@ -288,12 +293,15 @@ describe("row api - postgres", () => { value: newValue, } - const res = await updateRow(postgresTable._id, updatedRow) + const res = await updateRow(primaryPostgresTable._id, updatedRow) expect(res.status).toBe(200) expect(res.body).toEqual(updatedRow) - const persistedRow = await config.getRow(postgresTable._id!, row.id) + const persistedRow = await config.getRow( + primaryPostgresTable._id!, + row.id + ) expect(persistedRow).toEqual( expect.objectContaining({ @@ -305,21 +313,21 @@ describe("row api - postgres", () => { }) }) - describe("delete a row", () => { + describe("DELETE /api/:tableId/rows", () => { const deleteRow = ( tableId: string | undefined, body: Row | { rows: Row[] } ) => makeRequest("delete", `/api/${tableId}/rows`, body) - test("Given than a row exists, delete request removes it", async () => { + it("Given than a row exists, delete request removes it", async () => { const numberOfInitialRows = 5 - let { row } = _.sample(await populateRows(numberOfInitialRows))! + let { row } = _.sample(await populatePrimaryRows(numberOfInitialRows))! - const res = await deleteRow(postgresTable._id, row) + const res = await deleteRow(primaryPostgresTable._id, row) expect(res.status).toBe(200) - const persistedRows = await config.getRows(postgresTable._id!) + const persistedRows = await config.getRows(primaryPostgresTable._id!) expect(persistedRows).toHaveLength(numberOfInitialRows - 1) expect(row.id).toBeDefined() @@ -328,17 +336,18 @@ describe("row api - postgres", () => { ) }) - test("Given than multiple rows exist, multiple rows can be removed", async () => { + it("Given than multiple rows exist, multiple rows can be removed at once", async () => { const numberOfInitialRows = 5 - let rows = _.sampleSize(await populateRows(numberOfInitialRows), 3)!.map( - x => x.row - ) + let rows = _.sampleSize( + await populatePrimaryRows(numberOfInitialRows), + 3 + )!.map(x => x.row) - const res = await deleteRow(postgresTable._id, { rows }) + const res = await deleteRow(primaryPostgresTable._id, { rows }) expect(res.status).toBe(200) - const persistedRows = await config.getRows(postgresTable._id!) + const persistedRows = await config.getRows(primaryPostgresTable._id!) expect(persistedRows).toHaveLength(numberOfInitialRows - 3) for (const row of rows) { @@ -349,33 +358,33 @@ describe("row api - postgres", () => { }) }) - describe("retrieve a row", () => { + describe("GET /api/:tableId/rows/:rowId", () => { const getRow = (tableId: string | undefined, rowId?: string | undefined) => makeRequest("get", `/api/${tableId}/rows/${rowId}`) - test("Given than a table have a single row, the row can be retrieved successfully", async () => { - const [{ rowData, row }] = await populateRows(1) + it("Given than a table have a single row, that row can be retrieved successfully", async () => { + const [{ rowData, row }] = await populatePrimaryRows(1) - const res = await getRow(postgresTable._id, row.id) + const res = await getRow(primaryPostgresTable._id, row.id) expect(res.status).toBe(200) expect(res.body).toEqual(expect.objectContaining(rowData)) }) - test("Given than a table have a multiple rows, a single row can be retrieved successfully", async () => { - const rows = await populateRows(10) + it("Given than a table have a multiple rows, a single row can be retrieved successfully", async () => { + const rows = await populatePrimaryRows(10) const { rowData, row } = _.sample(rows)! - const res = await getRow(postgresTable._id, row.id) + const res = await getRow(primaryPostgresTable._id, row.id) expect(res.status).toBe(200) expect(res.body).toEqual(expect.objectContaining(rowData)) }) - test("given having rows with relation data, only the ids are retrieved", async () => { - let [{ row }] = await populateRows(1) + it("Given a rows with relation data, foreign key fields are not retrieved", async () => { + let [{ row }] = await populatePrimaryRows(1) await config.createRow({ tableId: auxPostgresTable._id, @@ -383,7 +392,7 @@ describe("row api - postgres", () => { linkedField: row.id, }) - const res = await getRow(postgresTable._id, row.id) + const res = await getRow(primaryPostgresTable._id, row.id) expect(res.status).toBe(200) @@ -396,13 +405,13 @@ describe("row api - postgres", () => { }) }) - describe("search for rows", () => { + describe("POST /api/:tableId/search", () => { const search = (tableId: string | undefined, body?: object) => makeRequest("post", `/api/${tableId}/search`, body) describe("empty search", () => { - test("Given than a table has no rows, search without query returns empty", async () => { - const res = await search(postgresTable._id) + it("Given than a table has no rows, search without query returns empty", async () => { + const res = await search(primaryPostgresTable._id) expect(res.status).toBe(200) @@ -413,11 +422,11 @@ describe("row api - postgres", () => { }) }) - test("Given than a table has multiple rows, search without query returns all of them", async () => { + it("Given than a table has multiple rows, search without query returns all of them", async () => { const rowsCount = 6 - const rows = await populateRows(rowsCount) + const rows = await populatePrimaryRows(rowsCount) - const res = await search(postgresTable._id) + const res = await search(primaryPostgresTable._id) expect(res.status).toBe(200) @@ -431,13 +440,22 @@ describe("row api - postgres", () => { expect(res.body.rows).toHaveLength(rowsCount) }) - test("Given than multiple tables have multiple rows, search only return the requested ones", async () => { - await populateRows(2, (await createDefaultPgTable())._id) - const rowsCount = 6 - await populateRows(rowsCount) - await populateRows(2, (await createDefaultPgTable())._id) + it("Given than multiple tables have multiple rows, search only return the requested ones", async () => { + const createRandomTableWithRows = async () => + await config.createRow({ + tableId: (await createDefaultPgTable())._id, + title: generator.name(), + }) - const res = await search(postgresTable._id) + await createRandomTableWithRows() + await createRandomTableWithRows() + + const rowsCount = 6 + await populatePrimaryRows(rowsCount) + + await createRandomTableWithRows() + + const res = await search(primaryPostgresTable._id) expect(res.status).toBe(200) @@ -445,7 +463,7 @@ describe("row api - postgres", () => { }) }) - test("Querying by a string field returns the rows with field containing or starting by that value", async () => { + it("Querying by a string field returns the rows with field containing or starting by that value", async () => { const name = generator.name() const rowsToFilter = [ ...Array(2).fill({ @@ -460,13 +478,15 @@ describe("row api - postgres", () => { }), ] - await populateRows(3) + await populatePrimaryRows(3) for (const row of rowsToFilter) { - await createRow(row, postgresTable._id) + await createPrimaryRow({ + rowData: row, + }) } - await populateRows(1) + await populatePrimaryRows(1) - const res = await search(postgresTable._id, { + const res = await search(primaryPostgresTable._id, { query: { string: { name, @@ -484,10 +504,10 @@ describe("row api - postgres", () => { expect(res.body.rows).toHaveLength(4) }) - test("Querying respects the limit fields", async () => { - await populateRows(6) + it("Querying respects the limit fields", async () => { + await populatePrimaryRows(6) - const res = await search(postgresTable._id, { + const res = await search(primaryPostgresTable._id, { limit: 2, }) @@ -498,32 +518,28 @@ describe("row api - postgres", () => { describe("sort", () => { beforeEach(async () => { - const defaultValue = makeRandomRow() + const defaultValue = generateRandomPrimaryRowData() - await createRow( - { + await createPrimaryRow({ + rowData: { ...defaultValue, name: "d", value: 3, }, - postgresTable._id - ) - await createRow( - { ...defaultValue, name: "aaa", value: 40 }, - postgresTable._id - ) - await createRow( - { ...defaultValue, name: "ccccc", value: -5 }, - postgresTable._id - ) - await createRow( - { ...defaultValue, name: "bb", value: 0 }, - postgresTable._id - ) + }) + await createPrimaryRow({ + rowData: { ...defaultValue, name: "aaa", value: 40 }, + }) + await createPrimaryRow({ + rowData: { ...defaultValue, name: "ccccc", value: -5 }, + }) + await createPrimaryRow({ + rowData: { ...defaultValue, name: "bb", value: 0 }, + }) }) - test("Querying respects the sort order when sorting ascending by a string value", async () => { - const res = await search(postgresTable._id, { + it("Querying respects the sort order when sorting ascending by a string value", async () => { + const res = await search(primaryPostgresTable._id, { sort: "name", sortOrder: "ascending", sortType: "string", @@ -538,8 +554,8 @@ describe("row api - postgres", () => { ]) }) - test("Querying respects the sort order when sorting descending by a string value", async () => { - const res = await search(postgresTable._id, { + it("Querying respects the sort order when sorting descending by a string value", async () => { + const res = await search(primaryPostgresTable._id, { sort: "name", sortOrder: "descending", sortType: "string", @@ -554,8 +570,8 @@ describe("row api - postgres", () => { ]) }) - test("Querying respects the sort order when sorting ascending by a numeric value", async () => { - const res = await search(postgresTable._id, { + it("Querying respects the sort order when sorting ascending by a numeric value", async () => { + const res = await search(primaryPostgresTable._id, { sort: "value", sortOrder: "ascending", sortType: "number", @@ -570,8 +586,8 @@ describe("row api - postgres", () => { ]) }) - test("Querying respects the sort order when sorting descending by a numeric value", async () => { - const res = await search(postgresTable._id, { + it("Querying respects the sort order when sorting descending by a numeric value", async () => { + const res = await search(primaryPostgresTable._id, { sort: "value", sortOrder: "descending", sortType: "number", @@ -588,26 +604,21 @@ describe("row api - postgres", () => { }) }) - describe("get enriched row", () => { + describe("GET /api/:tableId/:rowId/enrich", () => { const getAll = (tableId: string | undefined, rowId: string | undefined) => makeRequest("get", `/api/${tableId}/${rowId}/enrich`) - test("given having rows with relation data, enrich populates the foreign field", async () => { - const foreignRow = await config.createRow({ - tableId: auxPostgresTable._id, - title: generator.name(), + it("Given a row with relation data, enrich populates the foreign field", async () => { + const { row, foreignRow } = await createPrimaryRow({ + rowData: generateRandomPrimaryRowData(), + createForeignRow: true, }) - const rowData = { - ...makeRandomRow(), - [`fk_${auxPostgresTable.name}_foreignField`]: foreignRow.id, - } - const row = await createRow(rowData) - - const res = await getAll(postgresTable._id, row.id) + const res = await getAll(primaryPostgresTable._id, row.id) expect(res.status).toBe(200) + expect(foreignRow).toBeDefined() expect(res.body).toEqual({ ...row, linkedField: [ @@ -619,23 +630,23 @@ describe("row api - postgres", () => { }) }) - describe("get all rows", () => { + describe("GET /api/:tableId/rows", () => { const getAll = (tableId: string | undefined) => makeRequest("get", `/api/${tableId}/rows`) - test("Given than a table has no rows, get returns empty", async () => { - const res = await getAll(postgresTable._id) + it("Given than a table has no rows, get returns empty", async () => { + const res = await getAll(primaryPostgresTable._id) expect(res.status).toBe(200) expect(res.body).toHaveLength(0) }) - test("Given than a table has multiple rows, get returns all of them", async () => { + it("Given than a table has multiple rows, get returns all of them", async () => { const rowsCount = 6 - const rows = await populateRows(rowsCount) + const rows = await populatePrimaryRows(rowsCount) - const res = await getAll(postgresTable._id) + const res = await getAll(primaryPostgresTable._id) expect(res.status).toBe(200) @@ -647,13 +658,19 @@ describe("row api - postgres", () => { ) }) - test("Given than multiple tables have multiple rows, get returns the requested ones", async () => { - await populateRows(2, (await createDefaultPgTable())._id) - const rowsCount = 6 - await populateRows(rowsCount, postgresTable._id) - await populateRows(2, (await createDefaultPgTable())._id) + it("Given than multiple tables have multiple rows, get returns the requested ones", async () => { + const createRandomTableWithRows = async () => + await config.createRow({ + tableId: (await createDefaultPgTable())._id, + title: generator.name(), + }) - const res = await getAll(postgresTable._id) + await createRandomTableWithRows() + const rowsCount = 6 + await populatePrimaryRows(rowsCount) + await createRandomTableWithRows() + + const res = await getAll(primaryPostgresTable._id) expect(res.status).toBe(200) diff --git a/packages/server/src/integrations/base/sql.ts b/packages/server/src/integrations/base/sql.ts index 3ee5bda673..cd7f3ea12d 100644 --- a/packages/server/src/integrations/base/sql.ts +++ b/packages/server/src/integrations/base/sql.ts @@ -90,7 +90,7 @@ function parseFilters(filters: SearchFilters | undefined): SearchFilters { function generateSelectStatement( json: QueryJson, knex: Knex, - excludeJoinColumns = false + opts?: { excludeJoinColumns: boolean } ): (string | Knex.Raw)[] | "*" { const { resource, meta } = json const schema = meta?.table?.schema @@ -104,7 +104,7 @@ function generateSelectStatement( const tableName = fieldNames[0] if ( meta?.table?.name && - excludeJoinColumns && + opts?.excludeJoinColumns && tableName !== meta.table.name ) { return p @@ -417,7 +417,9 @@ class InternalBuilder { } else { return query .insert(parsedBody) - .returning(generateSelectStatement(json, knex, true)) + .returning( + generateSelectStatement(json, knex, { excludeJoinColumns: true }) + ) } } @@ -504,7 +506,9 @@ class InternalBuilder { } else { return query .update(parsedBody) - .returning(generateSelectStatement(json, knex, true)) + .returning( + generateSelectStatement(json, knex, { excludeJoinColumns: true }) + ) } } @@ -519,7 +523,11 @@ class InternalBuilder { if (opts.disableReturning) { return query.delete() } else { - return query.delete().returning(generateSelectStatement(json, knex, true)) + return query + .delete() + .returning( + generateSelectStatement(json, knex, { excludeJoinColumns: true }) + ) } } } From bc0208bc83cdfbefc89c18472f593d478a53b2eb Mon Sep 17 00:00:00 2001 From: adrinr Date: Tue, 7 Feb 2023 11:27:46 +0000 Subject: [PATCH 53/56] Clean describes --- .../src/integration-test/postgres.spec.ts | 484 ++++++++++-------- packages/server/src/integrations/base/sql.ts | 4 +- 2 files changed, 281 insertions(+), 207 deletions(-) diff --git a/packages/server/src/integration-test/postgres.spec.ts b/packages/server/src/integration-test/postgres.spec.ts index bc909a66f0..ecfb532e7f 100644 --- a/packages/server/src/integration-test/postgres.spec.ts +++ b/packages/server/src/integration-test/postgres.spec.ts @@ -145,12 +145,14 @@ describe("row api - postgres", () => { } } + type PrimaryRowData = { + name: string + description: string + value: number + } + async function createPrimaryRow(opts: { - rowData: { - name: string - description: string - value: number - } + rowData: PrimaryRowData createForeignRow?: boolean }) { let { rowData } = opts @@ -193,7 +195,12 @@ describe("row api - postgres", () => { }) } - async function populatePrimaryRows(count: number) { + async function populatePrimaryRows( + count: number, + opts?: { + createForeignRow?: boolean + } + ) { return await Promise.all( Array(count) .fill({}) @@ -203,6 +210,7 @@ describe("row api - postgres", () => { rowData, ...(await createPrimaryRow({ rowData, + createForeignRow: opts?.createForeignRow, })), } }) @@ -243,38 +251,40 @@ describe("row api - postgres", () => { const createRow = (tableId: string | undefined, body: object) => makeRequest("post", `/api/${tableId}/rows`, body) - it("Given than no row exists, adding a new one persists it", async () => { - const newRow = generateRandomPrimaryRowData() + describe("given than no row exists", () => { + it("adding a new one persists it", async () => { + const newRow = generateRandomPrimaryRowData() - const res = await createRow(primaryPostgresTable._id, newRow) - - expect(res.status).toBe(200) - - const persistedRows = await config.getRows(primaryPostgresTable._id!) - expect(persistedRows).toHaveLength(1) - - const expected = { - ...res.body, - ...newRow, - } - - expect(persistedRows).toEqual([expect.objectContaining(expected)]) - }) - - it("Given than no row exists, multiple rows can be persisted", async () => { - const numberOfRows = 10 - const newRows = Array(numberOfRows).fill(generateRandomPrimaryRowData()) - - for (const newRow of newRows) { const res = await createRow(primaryPostgresTable._id, newRow) - expect(res.status).toBe(200) - } - const persistedRows = await config.getRows(primaryPostgresTable._id!) - expect(persistedRows).toHaveLength(numberOfRows) - expect(persistedRows).toEqual( - expect.arrayContaining(newRows.map(expect.objectContaining)) - ) + expect(res.status).toBe(200) + + const persistedRows = await config.getRows(primaryPostgresTable._id!) + expect(persistedRows).toHaveLength(1) + + const expected = { + ...res.body, + ...newRow, + } + + expect(persistedRows).toEqual([expect.objectContaining(expected)]) + }) + + it("multiple rows can be persisted", async () => { + const numberOfRows = 10 + const newRows = Array(numberOfRows).fill(generateRandomPrimaryRowData()) + + for (const newRow of newRows) { + const res = await createRow(primaryPostgresTable._id, newRow) + expect(res.status).toBe(200) + } + + const persistedRows = await config.getRows(primaryPostgresTable._id!) + expect(persistedRows).toHaveLength(numberOfRows) + expect(persistedRows).toEqual( + expect.arrayContaining(newRows.map(expect.objectContaining)) + ) + }) }) }) @@ -282,34 +292,40 @@ describe("row api - postgres", () => { const updateRow = (tableId: string | undefined, body: Row) => makeRequest("patch", `/api/${tableId}/rows`, body) - it("Given than a row exists, updating it persists it", async () => { - let { row } = _.sample(await populatePrimaryRows(10))! + describe("given than a row exists", () => { + let row: Row + beforeEach(async () => { + let rowResponse = _.sample(await populatePrimaryRows(10))! + row = rowResponse.row + }) - const newName = generator.name() - const newValue = generator.age() - const updatedRow = { - ...row, - name: newName, - value: newValue, - } - - const res = await updateRow(primaryPostgresTable._id, updatedRow) - - expect(res.status).toBe(200) - expect(res.body).toEqual(updatedRow) - - const persistedRow = await config.getRow( - primaryPostgresTable._id!, - row.id - ) - - expect(persistedRow).toEqual( - expect.objectContaining({ - id: row.id, + it("updating it persists it", async () => { + const newName = generator.name() + const newValue = generator.age() + const updatedRow = { + ...row, name: newName, value: newValue, - }) - ) + } + + const res = await updateRow(primaryPostgresTable._id, updatedRow) + + expect(res.status).toBe(200) + expect(res.body).toEqual(updatedRow) + + const persistedRow = await config.getRow( + primaryPostgresTable._id!, + row.id + ) + + expect(persistedRow).toEqual( + expect.objectContaining({ + id: row.id, + name: newName, + value: newValue, + }) + ) + }) }) }) @@ -319,42 +335,46 @@ describe("row api - postgres", () => { body: Row | { rows: Row[] } ) => makeRequest("delete", `/api/${tableId}/rows`, body) - it("Given than a row exists, delete request removes it", async () => { + describe("given than multiple row exist", () => { const numberOfInitialRows = 5 - let { row } = _.sample(await populatePrimaryRows(numberOfInitialRows))! + let rows: Row[] + beforeEach(async () => { + rows = (await populatePrimaryRows(numberOfInitialRows)).map(x => x.row) + }) - const res = await deleteRow(primaryPostgresTable._id, row) + it("delete request removes it", async () => { + const row = _.sample(rows)! + const res = await deleteRow(primaryPostgresTable._id, row) - expect(res.status).toBe(200) + expect(res.status).toBe(200) - const persistedRows = await config.getRows(primaryPostgresTable._id!) - expect(persistedRows).toHaveLength(numberOfInitialRows - 1) + const persistedRows = await config.getRows(primaryPostgresTable._id!) + expect(persistedRows).toHaveLength(numberOfInitialRows - 1) - expect(row.id).toBeDefined() - expect(persistedRows).not.toContain( - expect.objectContaining({ _id: row.id }) - ) - }) - - it("Given than multiple rows exist, multiple rows can be removed at once", async () => { - const numberOfInitialRows = 5 - let rows = _.sampleSize( - await populatePrimaryRows(numberOfInitialRows), - 3 - )!.map(x => x.row) - - const res = await deleteRow(primaryPostgresTable._id, { rows }) - - expect(res.status).toBe(200) - - const persistedRows = await config.getRows(primaryPostgresTable._id!) - expect(persistedRows).toHaveLength(numberOfInitialRows - 3) - - for (const row of rows) { + expect(row.id).toBeDefined() expect(persistedRows).not.toContain( expect.objectContaining({ _id: row.id }) ) - } + }) + + it("multiple rows can be removed at once", async () => { + let rowsToDelete = _.sampleSize(rows, 3)! + + const res = await deleteRow(primaryPostgresTable._id, { + rows: rowsToDelete, + }) + + expect(res.status).toBe(200) + + const persistedRows = await config.getRows(primaryPostgresTable._id!) + expect(persistedRows).toHaveLength(numberOfInitialRows - 3) + + for (const row of rowsToDelete) { + expect(persistedRows).not.toContain( + expect.objectContaining({ _id: row.id }) + ) + } + }) }) }) @@ -362,46 +382,62 @@ describe("row api - postgres", () => { const getRow = (tableId: string | undefined, rowId?: string | undefined) => makeRequest("get", `/api/${tableId}/rows/${rowId}`) - it("Given than a table have a single row, that row can be retrieved successfully", async () => { - const [{ rowData, row }] = await populatePrimaryRows(1) - - const res = await getRow(primaryPostgresTable._id, row.id) - - expect(res.status).toBe(200) - - expect(res.body).toEqual(expect.objectContaining(rowData)) - }) - - it("Given than a table have a multiple rows, a single row can be retrieved successfully", async () => { - const rows = await populatePrimaryRows(10) - const { rowData, row } = _.sample(rows)! - - const res = await getRow(primaryPostgresTable._id, row.id) - - expect(res.status).toBe(200) - - expect(res.body).toEqual(expect.objectContaining(rowData)) - }) - - it("Given a rows with relation data, foreign key fields are not retrieved", async () => { - let [{ row }] = await populatePrimaryRows(1) - - await config.createRow({ - tableId: auxPostgresTable._id, - title: generator.sentence(), - linkedField: row.id, + describe("given than a table have a single row", () => { + let rowData: PrimaryRowData, row: Row + beforeEach(async () => { + const [createdRow] = await populatePrimaryRows(1) + rowData = createdRow.rowData + row = createdRow.row }) - const res = await getRow(primaryPostgresTable._id, row.id) + it("the row can be retrieved successfully", async () => { + const res = await getRow(primaryPostgresTable._id, row.id) - expect(res.status).toBe(200) + expect(res.status).toBe(200) - expect(res.body).toEqual({ - ...row, - _id: expect.any(String), - _rev: expect.any(String), + expect(res.body).toEqual(expect.objectContaining(rowData)) + }) + }) + + describe("given than a table have a multiple rows", () => { + let rows: { row: Row; rowData: PrimaryRowData }[] + + beforeEach(async () => { + rows = await populatePrimaryRows(10) + }) + + it("a single row can be retrieved successfully", async () => { + const { rowData, row } = _.sample(rows)! + + const res = await getRow(primaryPostgresTable._id, row.id) + + expect(res.status).toBe(200) + + expect(res.body).toEqual(expect.objectContaining(rowData)) + }) + }) + + describe("given a row with relation data", () => { + let row: Row + beforeEach(async () => { + let [createdRow] = await populatePrimaryRows(1, { + createForeignRow: true, + }) + row = createdRow.row + }) + + it("foreign key fields are not retrieved", async () => { + const res = await getRow(primaryPostgresTable._id, row.id) + + expect(res.status).toBe(200) + + expect(res.body).toEqual({ + ...row, + _id: expect.any(String), + _rev: expect.any(String), + }) + expect(res.body.foreignField).toBeUndefined() }) - expect(res.body.foreignField).toBeUndefined() }) }) @@ -409,57 +445,70 @@ describe("row api - postgres", () => { const search = (tableId: string | undefined, body?: object) => makeRequest("post", `/api/${tableId}/search`, body) - describe("empty search", () => { - it("Given than a table has no rows, search without query returns empty", async () => { - const res = await search(primaryPostgresTable._id) + describe("search without parameters", () => { + describe("given than a table has no rows", () => { + it("search without query returns empty", async () => { + const res = await search(primaryPostgresTable._id) - expect(res.status).toBe(200) + expect(res.status).toBe(200) - expect(res.body).toEqual({ - rows: [], - bookmark: null, - hasNextPage: false, - }) - }) - - it("Given than a table has multiple rows, search without query returns all of them", async () => { - const rowsCount = 6 - const rows = await populatePrimaryRows(rowsCount) - - const res = await search(primaryPostgresTable._id) - - expect(res.status).toBe(200) - - expect(res.body).toEqual({ - rows: expect.arrayContaining( - rows.map(r => expect.objectContaining(r.rowData)) - ), - bookmark: null, - hasNextPage: false, - }) - expect(res.body.rows).toHaveLength(rowsCount) - }) - - it("Given than multiple tables have multiple rows, search only return the requested ones", async () => { - const createRandomTableWithRows = async () => - await config.createRow({ - tableId: (await createDefaultPgTable())._id, - title: generator.name(), + expect(res.body).toEqual({ + rows: [], + bookmark: null, + hasNextPage: false, }) + }) + }) - await createRandomTableWithRows() - await createRandomTableWithRows() - + describe("given than a table has multiple rows", () => { const rowsCount = 6 - await populatePrimaryRows(rowsCount) + let rows: { + row: Row + rowData: PrimaryRowData + }[] + beforeEach(async () => { + rows = await populatePrimaryRows(rowsCount) + }) - await createRandomTableWithRows() + it("search without query returns all of them", async () => { + const res = await search(primaryPostgresTable._id) - const res = await search(primaryPostgresTable._id) + expect(res.status).toBe(200) - expect(res.status).toBe(200) + expect(res.body).toEqual({ + rows: expect.arrayContaining( + rows.map(r => expect.objectContaining(r.rowData)) + ), + bookmark: null, + hasNextPage: false, + }) + expect(res.body.rows).toHaveLength(rowsCount) + }) + }) - expect(res.body.rows).toHaveLength(rowsCount) + describe("given than multiple tables have multiple rows", () => { + const rowsCount = 6 + beforeEach(async () => { + const createRandomTableWithRows = async () => + await config.createRow({ + tableId: (await createDefaultPgTable())._id, + title: generator.name(), + }) + + await createRandomTableWithRows() + await createRandomTableWithRows() + + await populatePrimaryRows(rowsCount) + + await createRandomTableWithRows() + }) + it("search only return the requested ones", async () => { + const res = await search(primaryPostgresTable._id) + + expect(res.status).toBe(200) + + expect(res.body.rows).toHaveLength(rowsCount) + }) }) }) @@ -607,25 +656,33 @@ describe("row api - postgres", () => { describe("GET /api/:tableId/:rowId/enrich", () => { const getAll = (tableId: string | undefined, rowId: string | undefined) => makeRequest("get", `/api/${tableId}/${rowId}/enrich`) + describe("given a row with relation data", () => { + let row: Row, foreignRow: Row | undefined - it("Given a row with relation data, enrich populates the foreign field", async () => { - const { row, foreignRow } = await createPrimaryRow({ - rowData: generateRandomPrimaryRowData(), - createForeignRow: true, + beforeEach(async () => { + const rowsInfo = await createPrimaryRow({ + rowData: generateRandomPrimaryRowData(), + createForeignRow: true, + }) + + row = rowsInfo.row + foreignRow = rowsInfo.foreignRow }) - const res = await getAll(primaryPostgresTable._id, row.id) + it("enrich populates the foreign field", async () => { + const res = await getAll(primaryPostgresTable._id, row.id) - expect(res.status).toBe(200) + expect(res.status).toBe(200) - expect(foreignRow).toBeDefined() - expect(res.body).toEqual({ - ...row, - linkedField: [ - { - ...foreignRow, - }, - ], + expect(foreignRow).toBeDefined() + expect(res.body).toEqual({ + ...row, + linkedField: [ + { + ...foreignRow, + }, + ], + }) }) }) }) @@ -634,47 +691,62 @@ describe("row api - postgres", () => { const getAll = (tableId: string | undefined) => makeRequest("get", `/api/${tableId}/rows`) - it("Given than a table has no rows, get returns empty", async () => { - const res = await getAll(primaryPostgresTable._id) + describe("given a table with no rows", () => { + it("get request returns empty", async () => { + const res = await getAll(primaryPostgresTable._id) - expect(res.status).toBe(200) + expect(res.status).toBe(200) - expect(res.body).toHaveLength(0) + expect(res.body).toHaveLength(0) + }) }) - - it("Given than a table has multiple rows, get returns all of them", async () => { + describe("given a table with multiple rows", () => { const rowsCount = 6 - const rows = await populatePrimaryRows(rowsCount) + let rows: { + row: Row + foreignRow: Row | undefined + rowData: PrimaryRowData + }[] + beforeEach(async () => { + rows = await populatePrimaryRows(rowsCount) + }) - const res = await getAll(primaryPostgresTable._id) + it("get request returns all of them", async () => { + const res = await getAll(primaryPostgresTable._id) - expect(res.status).toBe(200) + expect(res.status).toBe(200) - expect(res.body).toHaveLength(rowsCount) - expect(res.body).toEqual( - expect.arrayContaining( - rows.map(r => expect.objectContaining(r.rowData)) + expect(res.body).toHaveLength(rowsCount) + expect(res.body).toEqual( + expect.arrayContaining( + rows.map(r => expect.objectContaining(r.rowData)) + ) ) - ) + }) }) - it("Given than multiple tables have multiple rows, get returns the requested ones", async () => { - const createRandomTableWithRows = async () => - await config.createRow({ - tableId: (await createDefaultPgTable())._id, - title: generator.name(), - }) - - await createRandomTableWithRows() + describe("given multiple tables with multiple rows", () => { const rowsCount = 6 - await populatePrimaryRows(rowsCount) - await createRandomTableWithRows() - const res = await getAll(primaryPostgresTable._id) + beforeEach(async () => { + const createRandomTableWithRows = async () => + await config.createRow({ + tableId: (await createDefaultPgTable())._id, + title: generator.name(), + }) - expect(res.status).toBe(200) + await createRandomTableWithRows() + await populatePrimaryRows(rowsCount) + await createRandomTableWithRows() + }) - expect(res.body).toHaveLength(rowsCount) + it("get returns the requested ones", async () => { + const res = await getAll(primaryPostgresTable._id) + + expect(res.status).toBe(200) + + expect(res.body).toHaveLength(rowsCount) + }) }) }) }) diff --git a/packages/server/src/integrations/base/sql.ts b/packages/server/src/integrations/base/sql.ts index cd7f3ea12d..2bc1061fd2 100644 --- a/packages/server/src/integrations/base/sql.ts +++ b/packages/server/src/integrations/base/sql.ts @@ -448,7 +448,9 @@ class InternalBuilder { if (resource.fields && resource.fields.length > 0) { // select the resources as the format "table.columnName" - this is what is provided // by the resource builder further up - selectStatement = generateSelectStatement(json, knex) + selectStatement = generateSelectStatement(json, knex, { + excludeJoinColumns: false, + }) } let foundLimit = limit || BASE_LIMIT // handle pagination From 3d917bfe22d76d5feb7b6f4893f535643d5aa18a Mon Sep 17 00:00:00 2001 From: adrinr Date: Tue, 7 Feb 2023 12:25:02 +0000 Subject: [PATCH 54/56] Fix conflicting columns issue --- .../api/controllers/row/ExternalRequest.ts | 5 +- .../src/api/controllers/row/external.ts | 14 +++++- packages/server/src/integrations/base/sql.ts | 50 ++++--------------- 3 files changed, 28 insertions(+), 41 deletions(-) diff --git a/packages/server/src/api/controllers/row/ExternalRequest.ts b/packages/server/src/api/controllers/row/ExternalRequest.ts index 83f3004c14..2faff95595 100644 --- a/packages/server/src/api/controllers/row/ExternalRequest.ts +++ b/packages/server/src/api/controllers/row/ExternalRequest.ts @@ -45,6 +45,7 @@ export interface RunConfig { row?: Row rows?: Row[] tables?: Record + includeSqlRelationships?: IncludeRelationship } function buildFilters( @@ -707,7 +708,9 @@ export class ExternalRequest { }, resource: { // have to specify the fields to avoid column overlap (for SQL) - fields: isSql ? this.buildFields(table) : [], + fields: isSql + ? this.buildFields(table, config.includeSqlRelationships) + : [], }, filters, sort, diff --git a/packages/server/src/api/controllers/row/external.ts b/packages/server/src/api/controllers/row/external.ts index 65fcbfbc41..2a43c3665c 100644 --- a/packages/server/src/api/controllers/row/external.ts +++ b/packages/server/src/api/controllers/row/external.ts @@ -18,6 +18,7 @@ import { PaginationJson, Table, Datasource, + IncludeRelationship, } from "@budibase/types" import sdk from "../../../sdk" @@ -57,6 +58,7 @@ export async function patch(ctx: BBContext) { return handleRequest(Operation.UPDATE, tableId, { id: breakRowIdField(id), row: inputs, + includeSqlRelationships: IncludeRelationship.EXCLUDE, }) } @@ -65,6 +67,7 @@ export async function save(ctx: BBContext) { const tableId = ctx.params.tableId return handleRequest(Operation.CREATE, tableId, { row: inputs, + includeSqlRelationships: IncludeRelationship.EXCLUDE, }) } @@ -78,7 +81,9 @@ export async function fetchView(ctx: BBContext) { export async function fetch(ctx: BBContext) { const tableId = ctx.params.tableId - return handleRequest(Operation.READ, tableId) + return handleRequest(Operation.READ, tableId, { + includeSqlRelationships: IncludeRelationship.INCLUDE, + }) } export async function find(ctx: BBContext) { @@ -86,6 +91,7 @@ export async function find(ctx: BBContext) { const tableId = ctx.params.tableId const response = (await handleRequest(Operation.READ, tableId, { id: breakRowIdField(id), + includeSqlRelationships: IncludeRelationship.EXCLUDE, })) as Row[] return response ? response[0] : response } @@ -95,6 +101,7 @@ export async function destroy(ctx: BBContext) { const id = ctx.request.body._id const { row } = (await handleRequest(Operation.DELETE, tableId, { id: breakRowIdField(id), + includeSqlRelationships: IncludeRelationship.EXCLUDE, })) as { row: Row } return { response: { ok: true }, row } } @@ -107,6 +114,7 @@ export async function bulkDestroy(ctx: BBContext) { promises.push( handleRequest(Operation.DELETE, tableId, { id: breakRowIdField(row._id), + includeSqlRelationships: IncludeRelationship.EXCLUDE, }) ) } @@ -149,6 +157,7 @@ export async function search(ctx: BBContext) { filters: query, sort, paginate: paginateObj as PaginationJson, + includeSqlRelationships: IncludeRelationship.INCLUDE, })) as Row[] let hasNextPage = false if (paginate && rows.length === limit) { @@ -159,6 +168,7 @@ export async function search(ctx: BBContext) { limit: 1, page: bookmark * limit + 1, }, + includeSqlRelationships: IncludeRelationship.INCLUDE, })) as Row[] hasNextPage = nextRows.length > 0 } @@ -247,6 +257,7 @@ export async function fetchEnrichedRow(ctx: BBContext) { const response = (await handleRequest(Operation.READ, tableId, { id, datasource, + includeSqlRelationships: IncludeRelationship.INCLUDE, })) as Row[] const table: Table = tables[tableName] const row = response[0] @@ -274,6 +285,7 @@ export async function fetchEnrichedRow(ctx: BBContext) { [primaryLink]: linkedIds, }, }, + includeSqlRelationships: IncludeRelationship.INCLUDE, }) } return row diff --git a/packages/server/src/integrations/base/sql.ts b/packages/server/src/integrations/base/sql.ts index 2bc1061fd2..2599f1ca0d 100644 --- a/packages/server/src/integrations/base/sql.ts +++ b/packages/server/src/integrations/base/sql.ts @@ -89,26 +89,13 @@ function parseFilters(filters: SearchFilters | undefined): SearchFilters { function generateSelectStatement( json: QueryJson, - knex: Knex, - opts?: { excludeJoinColumns: boolean } -): (string | Knex.Raw)[] | "*" { + knex: Knex +): (string | Knex.Raw)[] { const { resource, meta } = json const schema = meta?.table?.schema - - if (!resource) { - return "*" - } - - return resource.fields.reduce<(string | Knex.Raw)[]>((p, field) => { + return resource!.fields.map(field => { const fieldNames = field.split(/\./g) const tableName = fieldNames[0] - if ( - meta?.table?.name && - opts?.excludeJoinColumns && - tableName !== meta.table.name - ) { - return p - } const columnName = fieldNames[1] if ( columnName && @@ -117,18 +104,13 @@ function generateSelectStatement( ) { const externalType = schema[columnName].externalType if (externalType?.includes("money")) { - p.push( - knex.raw( - `"${tableName}"."${columnName}"::money::numeric as "${field}"` - ) + return knex.raw( + `"${tableName}"."${columnName}"::money::numeric as "${field}"` ) - return p } } - - p.push(`${field} as ${field}`) - return p - }, []) + return `${field} as ${field}` + }) } class InternalBuilder { @@ -417,9 +399,7 @@ class InternalBuilder { } else { return query .insert(parsedBody) - .returning( - generateSelectStatement(json, knex, { excludeJoinColumns: true }) - ) + .returning(generateSelectStatement(json, knex)) } } @@ -448,9 +428,7 @@ class InternalBuilder { if (resource.fields && resource.fields.length > 0) { // select the resources as the format "table.columnName" - this is what is provided // by the resource builder further up - selectStatement = generateSelectStatement(json, knex, { - excludeJoinColumns: false, - }) + selectStatement = generateSelectStatement(json, knex) } let foundLimit = limit || BASE_LIMIT // handle pagination @@ -508,9 +486,7 @@ class InternalBuilder { } else { return query .update(parsedBody) - .returning( - generateSelectStatement(json, knex, { excludeJoinColumns: true }) - ) + .returning(generateSelectStatement(json, knex)) } } @@ -525,11 +501,7 @@ class InternalBuilder { if (opts.disableReturning) { return query.delete() } else { - return query - .delete() - .returning( - generateSelectStatement(json, knex, { excludeJoinColumns: true }) - ) + return query.delete().returning(generateSelectStatement(json, knex)) } } } From 2cdc2f3fec786e308fbe4049cecff0ef0a518f9a Mon Sep 17 00:00:00 2001 From: adrinr Date: Tue, 7 Feb 2023 12:29:58 +0000 Subject: [PATCH 55/56] Fix select statement when no resource --- packages/server/src/integrations/base/sql.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/server/src/integrations/base/sql.ts b/packages/server/src/integrations/base/sql.ts index 2599f1ca0d..e42350091b 100644 --- a/packages/server/src/integrations/base/sql.ts +++ b/packages/server/src/integrations/base/sql.ts @@ -90,10 +90,15 @@ function parseFilters(filters: SearchFilters | undefined): SearchFilters { function generateSelectStatement( json: QueryJson, knex: Knex -): (string | Knex.Raw)[] { +): (string | Knex.Raw)[] | "*" { const { resource, meta } = json + + if (!resource) { + return "*" + } + const schema = meta?.table?.schema - return resource!.fields.map(field => { + return resource.fields.map(field => { const fieldNames = field.split(/\./g) const tableName = fieldNames[0] const columnName = fieldNames[1] From 452147c308120179bd1f4e82d7219f0648df42a2 Mon Sep 17 00:00:00 2001 From: adrinr Date: Tue, 7 Feb 2023 12:45:41 +0000 Subject: [PATCH 56/56] Cleanup tests --- .../src/api/routes/public/tests/utils.ts | 4 +--- .../src/tests/utilities/TestConfiguration.ts | 18 +++++------------- 2 files changed, 6 insertions(+), 16 deletions(-) diff --git a/packages/server/src/api/routes/public/tests/utils.ts b/packages/server/src/api/routes/public/tests/utils.ts index d861fa3660..755e2d659f 100644 --- a/packages/server/src/api/routes/public/tests/utils.ts +++ b/packages/server/src/api/routes/public/tests/utils.ts @@ -34,9 +34,7 @@ export function generateMakeRequest( ? endpoint : checkSlashesInUrl(`/api/public/v1/${endpoint}`) - const req = request[method](url).set( - config.defaultHeaders(extraHeaders, isInternal) - ) + const req = request[method](url).set(config.defaultHeaders(extraHeaders)) if (body) { req.send(body) } diff --git a/packages/server/src/tests/utilities/TestConfiguration.ts b/packages/server/src/tests/utilities/TestConfiguration.ts index 7b5673a457..5c45f89a2b 100644 --- a/packages/server/src/tests/utilities/TestConfiguration.ts +++ b/packages/server/src/tests/utilities/TestConfiguration.ts @@ -169,17 +169,16 @@ class TestConfiguration { return this.createApp(appName) } - async end() { + end() { if (!this) { return } - if (this.allApps) { - cleanup(this.allApps.map(app => app.appId)) - } - if (this.server) { this.server.close() } + if (this.allApps) { + cleanup(this.allApps.map(app => app.appId)) + } } // MODES @@ -346,7 +345,7 @@ class TestConfiguration { }) } - defaultHeaders(extras = {}, isInternal: boolean = false) { + defaultHeaders(extras = {}) { const tenantId = this.getTenantId() const authObj: AuthToken = { userId: this.defaultUserValues.globalUserId, @@ -369,13 +368,6 @@ class TestConfiguration { ...extras, } - if (!isInternal) { - headers.Cookie = [ - `${constants.Cookie.Auth}=${authToken}`, - `${constants.Cookie.CurrentApp}=${appToken}`, - ] - } - if (this.appId) { headers[constants.Header.APP_ID] = this.appId }