From 1c135654599584e2f2065c6665d34a4fddf9cafc Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Thu, 21 Mar 2024 18:16:52 +0000 Subject: [PATCH 01/97] Making really good progress removing the pg mocks. More to do, though. --- packages/server/__mocks__/pg.ts | 25 - packages/server/jest.config.ts | 2 + packages/server/scripts/test.sh | 2 +- .../routes/tests/queries/generic-sql.spec.ts | 818 +++++++++++++----- .../api/routes/tests/queries/mongodb.spec.ts | 709 +++++++++------ .../routes/tests/queries/query.seq.spec.ts | 381 +------- .../src/tests/utilities/api/datasource.ts | 13 + .../server/src/tests/utilities/api/query.ts | 40 +- 8 files changed, 1068 insertions(+), 922 deletions(-) delete mode 100644 packages/server/__mocks__/pg.ts diff --git a/packages/server/__mocks__/pg.ts b/packages/server/__mocks__/pg.ts deleted file mode 100644 index 50a7c7349e..0000000000 --- a/packages/server/__mocks__/pg.ts +++ /dev/null @@ -1,25 +0,0 @@ -const query = jest.fn(() => ({ - rows: [ - { - a: "string", - b: 1, - }, - ], -})) - -class Client { - query = query - end = jest.fn(cb => { - if (cb) cb() - }) - connect = jest.fn() - release = jest.fn() -} - -const on = jest.fn() - -module.exports = { - Client, - queryMock: query, - on, -} diff --git a/packages/server/jest.config.ts b/packages/server/jest.config.ts index 6c6d6a20d3..f3f8ebce02 100644 --- a/packages/server/jest.config.ts +++ b/packages/server/jest.config.ts @@ -60,6 +60,8 @@ const config: Config.InitialOptions = { "!src/db/views/staticViews.*", "!src/**/*.spec.{js,ts}", "!src/tests/**/*.{js,ts}", + // The use of coverage in the JS runner bundles breaks tests + "!src/jsRunner/bundles/**/*.{js,ts}", ], coverageReporters: ["lcov", "json", "clover"], } diff --git a/packages/server/scripts/test.sh b/packages/server/scripts/test.sh index 3ecf8bb794..53677a68e0 100644 --- a/packages/server/scripts/test.sh +++ b/packages/server/scripts/test.sh @@ -10,5 +10,5 @@ else # --maxWorkers performs better in development export NODE_OPTIONS="--no-node-snapshot $NODE_OPTIONS" echo "jest --coverage --maxWorkers=2 --forceExit $@" - jest --coverage --maxWorkers=2 --forceExit $@ + jest --maxWorkers=2 --forceExit $@ fi \ No newline at end of file diff --git a/packages/server/src/api/routes/tests/queries/generic-sql.spec.ts b/packages/server/src/api/routes/tests/queries/generic-sql.spec.ts index 1fc0ecb382..4d97e07ed7 100644 --- a/packages/server/src/api/routes/tests/queries/generic-sql.spec.ts +++ b/packages/server/src/api/routes/tests/queries/generic-sql.spec.ts @@ -1,9 +1,11 @@ -import { Datasource, Query, SourceName } from "@budibase/types" +import { Datasource, Query, QueryPreview, SourceName } from "@budibase/types" import * as setup from "../utilities" import { databaseTestProviders } from "../../../../integrations/tests/utils" import pg from "pg" import mysql from "mysql2/promise" import mssql from "mssql" +import { Expectations } from "src/tests/utilities/api/base" +import { events } from "@budibase/backend-core" jest.unmock("pg") @@ -40,7 +42,10 @@ describe.each([ const config = setup.getConfig() let datasource: Datasource - async function createQuery(query: Partial): Promise { + async function createQuery( + query: Partial, + expectations?: Expectations + ): Promise { const defaultQuery: Query = { datasourceId: datasource._id!, name: "New Query", @@ -51,17 +56,16 @@ describe.each([ transformer: "return data", readable: true, } - return await config.api.query.create({ ...defaultQuery, ...query }) + return await config.api.query.save( + { ...defaultQuery, ...query }, + expectations + ) } async function rawQuery(sql: string): Promise { - // We re-fetch the datasource here because the one returned by - // config.api.datasource.create has the password field blanked out, and we - // need the password to connect to the database. - const ds = await dsProvider.datasource() - switch (ds.source) { + switch (datasource.source) { case SourceName.POSTGRES: { - const client = new pg.Client(ds.config!) + const client = new pg.Client(datasource.config!) await client.connect() try { const { rows } = await client.query(sql) @@ -71,7 +75,7 @@ describe.each([ } } case SourceName.MYSQL: { - const con = await mysql.createConnection(ds.config!) + const con = await mysql.createConnection(datasource.config!) try { const [rows] = await con.query(sql) return rows @@ -80,7 +84,9 @@ describe.each([ } } case SourceName.SQL_SERVER: { - const pool = new mssql.ConnectionPool(ds.config! as mssql.config) + const pool = new mssql.ConnectionPool( + datasource.config! as mssql.config + ) const client = await pool.connect() try { const { recordset } = await client.query(sql) @@ -94,17 +100,26 @@ describe.each([ beforeAll(async () => { await config.init() - datasource = await config.api.datasource.create( - await dsProvider.datasource() - ) }) beforeEach(async () => { + const datasourceRequest = await dsProvider.datasource() + datasource = await config.api.datasource.create(datasourceRequest) + + // The Datasource API does not return the password, but we need + // it later to connect to the underlying database, so we fill it + // back in here. + datasource.config!.password = datasourceRequest.config!.password + await rawQuery(createTableSQL[datasource.source]) await rawQuery(insertSQL) + + jest.clearAllMocks() }) afterEach(async () => { + const ds = await config.api.datasource.get(datasource._id!) + config.api.datasource.delete(ds) await rawQuery(dropTableSQL) }) @@ -113,78 +128,319 @@ describe.each([ setup.afterAll() }) - describe("create", () => { - it("should be able to insert with bindings", async () => { - const query = await createQuery({ - fields: { - sql: "INSERT INTO test_table (name) VALUES ({{ foo }})", - }, - parameters: [ - { - name: "foo", - default: "bar", + describe("query admin", () => { + describe("create", () => { + it("should be able to create a query", async () => { + const query = await createQuery({ + name: "New Query", + fields: { + sql: "SELECT * FROM test_table", }, - ], - queryVerb: "create", + }) + + expect(query).toMatchObject({ + datasourceId: datasource._id!, + name: "New Query", + parameters: [], + fields: { + sql: "SELECT * FROM test_table", + }, + schema: {}, + queryVerb: "read", + transformer: "return data", + readable: true, + createdAt: expect.any(String), + updatedAt: expect.any(String), + }) + + expect(events.query.created).toHaveBeenCalledTimes(1) + expect(events.query.updated).not.toHaveBeenCalled() }) - - const result = await config.api.query.execute(query._id!, { - parameters: { - foo: "baz", - }, - }) - - expect(result.data).toEqual([ - { - created: true, - }, - ]) - - const rows = await rawQuery("SELECT * FROM test_table WHERE name = 'baz'") - expect(rows).toHaveLength(1) }) - it.each(["2021-02-05T12:01:00.000Z", "2021-02-05"])( - "should coerce %s into a date", - async datetimeStr => { - const date = new Date(datetimeStr) + describe("update", () => { + it("should be able to update a query", async () => { const query = await createQuery({ fields: { - sql: `INSERT INTO test_table (name, birthday) VALUES ('foo', {{ birthday }})`, + sql: "SELECT * FROM test_table", }, - parameters: [ + }) + + jest.clearAllMocks() + + const updatedQuery = await config.api.query.save({ + ...query, + name: "Updated Query", + fields: { + sql: "SELECT * FROM test_table WHERE id = 1", + }, + }) + + expect(updatedQuery).toMatchObject({ + datasourceId: datasource._id!, + name: "Updated Query", + parameters: [], + fields: { + sql: "SELECT * FROM test_table WHERE id = 1", + }, + schema: {}, + queryVerb: "read", + transformer: "return data", + readable: true, + }) + + expect(events.query.created).not.toHaveBeenCalled() + expect(events.query.updated).toHaveBeenCalledTimes(1) + }) + }) + + describe("delete", () => { + it("should be able to delete a query", async () => { + const query = await createQuery({ + fields: { + sql: "SELECT * FROM test_table", + }, + }) + + await config.api.query.delete(query) + await config.api.query.get(query._id!, { status: 404 }) + + const queries = await config.api.query.fetch() + expect(queries).not.toContainEqual(query) + + expect(events.query.deleted).toHaveBeenCalledTimes(1) + expect(events.query.deleted).toHaveBeenCalledWith(datasource, query) + }) + }) + + describe("read", () => { + it("should be able to list queries", async () => { + const query = await createQuery({ + fields: { + sql: "SELECT * FROM test_table", + }, + }) + + const queries = await config.api.query.fetch() + expect(queries).toContainEqual(query) + }) + + it("should strip sensitive fields for prod apps", async () => { + const query = await createQuery({ + fields: { + sql: "SELECT * FROM test_table", + }, + }) + + await config.publish() + const prodQuery = await config.api.query.getProd(query._id!) + + expect(prodQuery._id).toEqual(query._id) + expect(prodQuery.fields).toBeUndefined() + expect(prodQuery.parameters).toBeUndefined() + expect(prodQuery.schema).toBeDefined() + }) + }) + }) + + describe("preview", () => { + it("should be able to preview a query", async () => { + const request: QueryPreview = { + datasourceId: datasource._id!, + queryVerb: "read", + fields: { + sql: `SELECT * FROM test_table WHERE id = 1`, + }, + parameters: [], + transformer: "return data", + name: datasource.name!, + schema: {}, + readable: true, + } + const response = await config.api.query.previewQuery(request) + expect(response.schema).toEqual({ + birthday: { + name: "birthday", + type: "string", + }, + id: { + name: "id", + type: "number", + }, + name: { + name: "name", + type: "string", + }, + }) + expect(response.rows).toEqual([ + { + birthday: null, + id: 1, + name: "one", + }, + ]) + expect(events.query.previewed).toHaveBeenCalledTimes(1) + + const dsWithoutConfig = { ...datasource } + delete dsWithoutConfig.config + expect(events.query.previewed).toHaveBeenCalledWith( + dsWithoutConfig, + request + ) + }) + + it("should work with static variables", async () => { + await config.api.datasource.update({ + ...datasource, + config: { + ...datasource.config, + staticVariables: { + foo: "bar", + }, + }, + }) + + const request: QueryPreview = { + datasourceId: datasource._id!, + queryVerb: "read", + fields: { + sql: `SELECT '{{ foo }}' as foo`, + }, + parameters: [], + transformer: "return data", + name: datasource.name!, + schema: {}, + readable: true, + } + + const response = await config.api.query.previewQuery(request) + + expect(response.schema).toEqual({ + foo: { + name: "foo", + type: "string", + }, + }) + + expect(response.rows).toEqual([ + { + foo: "bar", + }, + ]) + }) + + it("should work with dynamic variables", async () => { + const basedOnQuery = await createQuery({ + fields: { + sql: "SELECT name FROM test_table WHERE id = 1", + }, + }) + + await config.api.datasource.update({ + ...datasource, + config: { + ...datasource.config, + dynamicVariables: [ { - name: "birthday", - default: "", + queryId: basedOnQuery._id!, + name: "foo", + value: "{{ data[0].name }}", }, ], - queryVerb: "create", - }) + }, + }) - const result = await config.api.query.execute(query._id!, { - parameters: { birthday: datetimeStr }, - }) + const preview = await config.api.query.previewQuery({ + datasourceId: datasource._id!, + queryVerb: "read", + fields: { + sql: `SELECT '{{ foo }}' as foo`, + }, + parameters: [], + transformer: "return data", + name: datasource.name!, + schema: {}, + readable: true, + }) - expect(result.data).toEqual([{ created: true }]) + expect(preview.schema).toEqual({ + foo: { + name: "foo", + type: "string", + }, + }) - const rows = await rawQuery( - `SELECT * FROM test_table WHERE birthday = '${date.toISOString()}'` - ) - expect(rows).toHaveLength(1) - } - ) + expect(preview.rows).toEqual([ + { + foo: "one", + }, + ]) + }) - it.each(["2021,02,05", "202205-1500"])( - "should not coerce %s as a date", - async notDateStr => { + it("should handle the dynamic base query being deleted", async () => { + const basedOnQuery = await createQuery({ + fields: { + sql: "SELECT name FROM test_table WHERE id = 1", + }, + }) + + await config.api.datasource.update({ + ...datasource, + config: { + ...datasource.config, + dynamicVariables: [ + { + queryId: basedOnQuery._id!, + name: "foo", + value: "{{ data[0].name }}", + }, + ], + }, + }) + + await config.api.query.delete(basedOnQuery) + + const preview = await config.api.query.previewQuery({ + datasourceId: datasource._id!, + queryVerb: "read", + fields: { + sql: `SELECT '{{ foo }}' as foo`, + }, + parameters: [], + transformer: "return data", + name: datasource.name!, + schema: {}, + readable: true, + }) + + expect(preview.schema).toEqual({ + foo: { + name: "foo", + type: "string", + }, + }) + + // TODO: is this the correct behaviour? To return an empty string when the + // underlying query has been deleted? + expect(preview.rows).toEqual([ + { + foo: "", + }, + ]) + }) + }) + + describe("query verbs", () => { + describe("create", () => { + it("should be able to insert with bindings", async () => { const query = await createQuery({ fields: { - sql: "INSERT INTO test_table (name) VALUES ({{ name }})", + sql: "INSERT INTO test_table (name) VALUES ({{ foo }})", }, parameters: [ { - name: "name", - default: "", + name: "foo", + default: "bar", }, ], queryVerb: "create", @@ -192,210 +448,306 @@ describe.each([ const result = await config.api.query.execute(query._id!, { parameters: { - name: notDateStr, + foo: "baz", }, }) - expect(result.data).toEqual([{ created: true }]) + expect(result.data).toEqual([ + { + created: true, + }, + ]) const rows = await rawQuery( - `SELECT * FROM test_table WHERE name = '${notDateStr}'` + "SELECT * FROM test_table WHERE name = 'baz'" ) expect(rows).toHaveLength(1) - } - ) - }) - - describe("read", () => { - it("should execute a query", async () => { - const query = await createQuery({ - fields: { - sql: "SELECT * FROM test_table ORDER BY id", - }, }) - const result = await config.api.query.execute(query._id!) + it("should not allow handlebars as parameters", async () => { + const query = await createQuery({ + fields: { + sql: "INSERT INTO test_table (name) VALUES ({{ foo }})", + }, + parameters: [ + { + name: "foo", + default: "bar", + }, + ], + queryVerb: "create", + }) - expect(result.data).toEqual([ - { - id: 1, - name: "one", - birthday: null, - }, - { - id: 2, - name: "two", - birthday: null, - }, - { - id: 3, - name: "three", - birthday: null, - }, - { - id: 4, - name: "four", - birthday: null, - }, - { - id: 5, - name: "five", - birthday: null, - }, - ]) + await config.api.query.execute( + query._id!, + { + parameters: { + foo: "{{ 'test' }}", + }, + }, + { + status: 400, + body: { + message: + "Parameter 'foo' input contains a handlebars binding - this is not allowed.", + }, + } + ) + }) + + it.each(["2021-02-05T12:01:00.000Z", "2021-02-05"])( + "should coerce %s into a date", + async datetimeStr => { + const date = new Date(datetimeStr) + const query = await createQuery({ + fields: { + sql: `INSERT INTO test_table (name, birthday) VALUES ('foo', {{ birthday }})`, + }, + parameters: [ + { + name: "birthday", + default: "", + }, + ], + queryVerb: "create", + }) + + const result = await config.api.query.execute(query._id!, { + parameters: { birthday: datetimeStr }, + }) + + expect(result.data).toEqual([{ created: true }]) + + const rows = await rawQuery( + `SELECT * FROM test_table WHERE birthday = '${date.toISOString()}'` + ) + expect(rows).toHaveLength(1) + } + ) + + it.each(["2021,02,05", "202205-1500"])( + "should not coerce %s as a date", + async notDateStr => { + const query = await createQuery({ + fields: { + sql: "INSERT INTO test_table (name) VALUES ({{ name }})", + }, + parameters: [ + { + name: "name", + default: "", + }, + ], + queryVerb: "create", + }) + + const result = await config.api.query.execute(query._id!, { + parameters: { + name: notDateStr, + }, + }) + + expect(result.data).toEqual([{ created: true }]) + + const rows = await rawQuery( + `SELECT * FROM test_table WHERE name = '${notDateStr}'` + ) + expect(rows).toHaveLength(1) + } + ) }) - it("should be able to transform a query", async () => { - const query = await createQuery({ - fields: { - sql: "SELECT * FROM test_table WHERE id = 1", - }, - transformer: ` + describe("read", () => { + it("should execute a query", async () => { + const query = await createQuery({ + fields: { + sql: "SELECT * FROM test_table ORDER BY id", + }, + }) + + const result = await config.api.query.execute(query._id!) + + expect(result.data).toEqual([ + { + id: 1, + name: "one", + birthday: null, + }, + { + id: 2, + name: "two", + birthday: null, + }, + { + id: 3, + name: "three", + birthday: null, + }, + { + id: 4, + name: "four", + birthday: null, + }, + { + id: 5, + name: "five", + birthday: null, + }, + ]) + }) + + it("should be able to transform a query", async () => { + const query = await createQuery({ + fields: { + sql: "SELECT * FROM test_table WHERE id = 1", + }, + transformer: ` data[0].id = data[0].id + 1; return data; `, - }) + }) - const result = await config.api.query.execute(query._id!) + const result = await config.api.query.execute(query._id!) - expect(result.data).toEqual([ - { - id: 2, - name: "one", - birthday: null, - }, - ]) - }) - - it("should coerce numeric bindings", async () => { - const query = await createQuery({ - fields: { - sql: "SELECT * FROM test_table WHERE id = {{ id }}", - }, - parameters: [ + expect(result.data).toEqual([ { - name: "id", - default: "", + id: 2, + name: "one", + birthday: null, }, - ], + ]) }) - const result = await config.api.query.execute(query._id!, { - parameters: { - id: "1", - }, - }) + it("should coerce numeric bindings", async () => { + const query = await createQuery({ + fields: { + sql: "SELECT * FROM test_table WHERE id = {{ id }}", + }, + parameters: [ + { + name: "id", + default: "", + }, + ], + }) - expect(result.data).toEqual([ - { - id: 1, - name: "one", - birthday: null, - }, - ]) - }) - }) + const result = await config.api.query.execute(query._id!, { + parameters: { + id: "1", + }, + }) - describe("update", () => { - it("should be able to update rows", async () => { - const query = await createQuery({ - fields: { - sql: "UPDATE test_table SET name = {{ name }} WHERE id = {{ id }}", - }, - parameters: [ + expect(result.data).toEqual([ { - name: "id", - default: "", + id: 1, + name: "one", + birthday: null, }, + ]) + }) + }) + + describe("update", () => { + it("should be able to update rows", async () => { + const query = await createQuery({ + fields: { + sql: "UPDATE test_table SET name = {{ name }} WHERE id = {{ id }}", + }, + parameters: [ + { + name: "id", + default: "", + }, + { + name: "name", + default: "updated", + }, + ], + queryVerb: "update", + }) + + const result = await config.api.query.execute(query._id!, { + parameters: { + id: "1", + name: "foo", + }, + }) + + expect(result.data).toEqual([ { - name: "name", - default: "updated", + updated: true, }, - ], - queryVerb: "update", + ]) + + const rows = await rawQuery("SELECT * FROM test_table WHERE id = 1") + expect(rows).toEqual([{ id: 1, name: "foo", birthday: null }]) }) - const result = await config.api.query.execute(query._id!, { - parameters: { - id: "1", - name: "foo", - }, - }) + it("should be able to execute an update that updates no rows", async () => { + const query = await createQuery({ + fields: { + sql: "UPDATE test_table SET name = 'updated' WHERE id = 100", + }, + queryVerb: "update", + }) - expect(result.data).toEqual([ - { - updated: true, - }, - ]) + const result = await config.api.query.execute(query._id!) - const rows = await rawQuery("SELECT * FROM test_table WHERE id = 1") - expect(rows).toEqual([{ id: 1, name: "foo", birthday: null }]) - }) - - it("should be able to execute an update that updates no rows", async () => { - const query = await createQuery({ - fields: { - sql: "UPDATE test_table SET name = 'updated' WHERE id = 100", - }, - queryVerb: "update", - }) - - const result = await config.api.query.execute(query._id!) - - expect(result.data).toEqual([ - { - updated: true, - }, - ]) - }) - - it("should be able to execute a delete that deletes no rows", async () => { - const query = await createQuery({ - fields: { - sql: "DELETE FROM test_table WHERE id = 100", - }, - queryVerb: "delete", - }) - - const result = await config.api.query.execute(query._id!) - - expect(result.data).toEqual([ - { - deleted: true, - }, - ]) - }) - }) - - describe("delete", () => { - it("should be able to delete rows", async () => { - const query = await createQuery({ - fields: { - sql: "DELETE FROM test_table WHERE id = {{ id }}", - }, - parameters: [ + expect(result.data).toEqual([ { - name: "id", - default: "", + updated: true, }, - ], - queryVerb: "delete", + ]) }) - const result = await config.api.query.execute(query._id!, { - parameters: { - id: "1", - }, + it("should be able to execute a delete that deletes no rows", async () => { + const query = await createQuery({ + fields: { + sql: "DELETE FROM test_table WHERE id = 100", + }, + queryVerb: "delete", + }) + + const result = await config.api.query.execute(query._id!) + + expect(result.data).toEqual([ + { + deleted: true, + }, + ]) }) + }) - expect(result.data).toEqual([ - { - deleted: true, - }, - ]) + describe("delete", () => { + it("should be able to delete rows", async () => { + const query = await createQuery({ + fields: { + sql: "DELETE FROM test_table WHERE id = {{ id }}", + }, + parameters: [ + { + name: "id", + default: "", + }, + ], + queryVerb: "delete", + }) - const rows = await rawQuery("SELECT * FROM test_table WHERE id = 1") - expect(rows).toHaveLength(0) + const result = await config.api.query.execute(query._id!, { + parameters: { + id: "1", + }, + }) + + expect(result.data).toEqual([ + { + deleted: true, + }, + ]) + + const rows = await rawQuery("SELECT * FROM test_table WHERE id = 1") + expect(rows).toHaveLength(0) + }) }) }) }) diff --git a/packages/server/src/api/routes/tests/queries/mongodb.spec.ts b/packages/server/src/api/routes/tests/queries/mongodb.spec.ts index b61f905bef..6ab3dec80d 100644 --- a/packages/server/src/api/routes/tests/queries/mongodb.spec.ts +++ b/packages/server/src/api/routes/tests/queries/mongodb.spec.ts @@ -2,6 +2,7 @@ import { Datasource, Query } from "@budibase/types" import * as setup from "../utilities" import { databaseTestProviders } from "../../../../integrations/tests/utils" import { MongoClient, type Collection, BSON } from "mongodb" +import { generator } from "@budibase/backend-core/tests" jest.unmock("mongodb") @@ -33,30 +34,30 @@ describe("/queries", () => { ) { combinedQuery.fields.extra.collection = collection } - return await config.api.query.create(combinedQuery) + return await config.api.query.save(combinedQuery) } - async function withClient( - callback: (client: MongoClient) => Promise - ): Promise { + async function withClient( + callback: (client: MongoClient) => Promise + ): Promise { const ds = await databaseTestProviders.mongodb.datasource() const client = new MongoClient(ds.config!.connectionString) await client.connect() try { - await callback(client) + return await callback(client) } finally { await client.close() } } - async function withCollection( - callback: (collection: Collection) => Promise - ): Promise { - await withClient(async client => { + async function withCollection( + callback: (collection: Collection) => Promise + ): Promise { + return await withClient(async client => { const db = client.db( (await databaseTestProviders.mongodb.datasource()).config!.db ) - await callback(db.collection(collection)) + return await callback(db.collection(collection)) }) } @@ -85,309 +86,453 @@ describe("/queries", () => { }) afterEach(async () => { - await withCollection(async collection => { - await collection.drop() - }) + await withCollection(collection => collection.drop()) }) - it("should execute a count query", async () => { - const query = await createQuery({ - fields: { - json: {}, - extra: { - actionType: "count", + describe.only("preview", () => { + it("should generate a nested schema with an empty array", async () => { + const name = generator.guid() + await withCollection( + async collection => await collection.insertOne({ name, nested: [] }) + ) + + const preview = await config.api.query.previewQuery({ + name: "New Query", + datasourceId: datasource._id!, + fields: { + json: { + name: { $eq: name }, + }, + extra: { + collection, + actionType: "findOne", + }, }, - }, + schema: {}, + queryVerb: "read", + parameters: [], + transformer: "return data", + readable: true, + }) + + expect(preview).toEqual({ + nestedSchemaFields: {}, + rows: [{ _id: expect.any(String), name, nested: [] }], + schema: { + _id: { + type: "string", + name: "_id", + }, + name: { + type: "string", + name: "name", + }, + nested: { + type: "array", + name: "nested", + }, + }, + }) }) - const result = await config.api.query.execute(query._id!) + it("should generate a nested schema based on all of the nested items", async () => { + const name = generator.guid() + const item = { + name, + contacts: [ + { + address: "123 Lane", + }, + { + address: "456 Drive", + }, + { + postcode: "BT1 12N", + lat: 54.59, + long: -5.92, + }, + { + city: "Belfast", + }, + { + address: "789 Avenue", + phoneNumber: "0800-999-5555", + }, + { + name: "Name", + isActive: false, + }, + ], + } - expect(result.data).toEqual([{ value: 5 }]) - }) + await withCollection(collection => collection.insertOne(item)) - it("should execute a count query with a transformer", async () => { - const query = await createQuery({ - fields: { - json: {}, - extra: { - actionType: "count", + const preview = await config.api.query.previewQuery({ + name: "New Query", + datasourceId: datasource._id!, + fields: { + json: { + name: { $eq: name }, + }, + extra: { + collection, + actionType: "findOne", + }, }, - }, - transformer: "return data + 1", - }) + schema: {}, + queryVerb: "read", + parameters: [], + transformer: "return data", + readable: true, + }) - const result = await config.api.query.execute(query._id!) - - expect(result.data).toEqual([{ value: 6 }]) - }) - - it("should execute a find query", async () => { - const query = await createQuery({ - fields: { - json: {}, - extra: { - actionType: "find", + expect(preview).toEqual({ + nestedSchemaFields: { + contacts: { + address: { + type: "string", + name: "address", + }, + postcode: { + type: "string", + name: "postcode", + }, + lat: { + type: "number", + name: "lat", + }, + long: { + type: "number", + name: "long", + }, + city: { + type: "string", + name: "city", + }, + phoneNumber: { + type: "string", + name: "phoneNumber", + }, + name: { + type: "string", + name: "name", + }, + isActive: { + type: "boolean", + name: "isActive", + }, + }, }, - }, - }) - - const result = await config.api.query.execute(query._id!) - - expect(result.data).toEqual([ - { _id: expectValidId, name: "one" }, - { _id: expectValidId, name: "two" }, - { _id: expectValidId, name: "three" }, - { _id: expectValidId, name: "four" }, - { _id: expectValidId, name: "five" }, - ]) - }) - - it("should execute a findOne query", async () => { - const query = await createQuery({ - fields: { - json: {}, - extra: { - actionType: "findOne", + rows: [{ ...item, _id: expect.any(String) }], + schema: { + _id: { type: "string", name: "_id" }, + name: { type: "string", name: "name" }, + contacts: { type: "json", name: "contacts", subtype: "array" }, }, - }, - }) - - const result = await config.api.query.execute(query._id!) - - expect(result.data).toEqual([{ _id: expectValidId, name: "one" }]) - }) - - it("should execute a findOneAndUpdate query", async () => { - const query = await createQuery({ - fields: { - json: { - filter: { name: { $eq: "one" } }, - update: { $set: { name: "newName" } }, - }, - extra: { - actionType: "findOneAndUpdate", - }, - }, - }) - - const result = await config.api.query.execute(query._id!) - - expect(result.data).toEqual([ - { - lastErrorObject: { n: 1, updatedExisting: true }, - ok: 1, - value: { _id: expectValidId, name: "one" }, - }, - ]) - - await withCollection(async collection => { - expect(await collection.countDocuments()).toBe(5) - - const doc = await collection.findOne({ name: { $eq: "newName" } }) - expect(doc).toEqual({ - _id: expectValidBsonObjectId, - name: "newName", }) }) }) - it("should execute a distinct query", async () => { - const query = await createQuery({ - fields: { - json: "name", - extra: { - actionType: "distinct", + describe("execute", () => { + it("a count query", async () => { + const query = await createQuery({ + fields: { + json: {}, + extra: { + actionType: "count", + }, }, - }, - }) - - const result = await config.api.query.execute(query._id!) - const values = result.data.map(o => o.value).sort() - expect(values).toEqual(["five", "four", "one", "three", "two"]) - }) - - it("should execute a create query with parameters", async () => { - const query = await createQuery({ - fields: { - json: { foo: "{{ foo }}" }, - extra: { - actionType: "insertOne", - }, - }, - queryVerb: "create", - parameters: [ - { - name: "foo", - default: "default", - }, - ], - }) - - const result = await config.api.query.execute(query._id!, { - parameters: { foo: "bar" }, - }) - - expect(result.data).toEqual([ - { - acknowledged: true, - insertedId: expectValidId, - }, - ]) - - await withCollection(async collection => { - const doc = await collection.findOne({ foo: { $eq: "bar" } }) - expect(doc).toEqual({ - _id: expectValidBsonObjectId, - foo: "bar", - }) - }) - }) - - it("should execute a delete query with parameters", async () => { - const query = await createQuery({ - fields: { - json: { name: { $eq: "{{ name }}" } }, - extra: { - actionType: "deleteOne", - }, - }, - queryVerb: "delete", - parameters: [ - { - name: "name", - default: "", - }, - ], - }) - - const result = await config.api.query.execute(query._id!, { - parameters: { name: "one" }, - }) - - expect(result.data).toEqual([ - { - acknowledged: true, - deletedCount: 1, - }, - ]) - - await withCollection(async collection => { - const doc = await collection.findOne({ name: { $eq: "one" } }) - expect(doc).toBeNull() - }) - }) - - it("should execute an update query with parameters", async () => { - const query = await createQuery({ - fields: { - json: { - filter: { name: { $eq: "{{ name }}" } }, - update: { $set: { name: "{{ newName }}" } }, - }, - extra: { - actionType: "updateOne", - }, - }, - queryVerb: "update", - parameters: [ - { - name: "name", - default: "", - }, - { - name: "newName", - default: "", - }, - ], - }) - - const result = await config.api.query.execute(query._id!, { - parameters: { name: "one", newName: "newOne" }, - }) - - expect(result.data).toEqual([ - { - acknowledged: true, - matchedCount: 1, - modifiedCount: 1, - upsertedCount: 0, - upsertedId: null, - }, - ]) - - await withCollection(async collection => { - const doc = await collection.findOne({ name: { $eq: "newOne" } }) - expect(doc).toEqual({ - _id: expectValidBsonObjectId, - name: "newOne", }) - const oldDoc = await collection.findOne({ name: { $eq: "one" } }) - expect(oldDoc).toBeNull() - }) - }) + const result = await config.api.query.execute(query._id!) - it("should be able to delete all records", async () => { - const query = await createQuery({ - fields: { - json: {}, - extra: { - actionType: "deleteMany", + expect(result.data).toEqual([{ value: 5 }]) + }) + + it("a count query with a transformer", async () => { + const query = await createQuery({ + fields: { + json: {}, + extra: { + actionType: "count", + }, }, - }, - queryVerb: "delete", + transformer: "return data + 1", + }) + + const result = await config.api.query.execute(query._id!) + + expect(result.data).toEqual([{ value: 6 }]) }) - const result = await config.api.query.execute(query._id!) - - expect(result.data).toEqual([ - { - acknowledged: true, - deletedCount: 5, - }, - ]) - - await withCollection(async collection => { - const docs = await collection.find().toArray() - expect(docs).toHaveLength(0) - }) - }) - - it("should be able to update all documents", async () => { - const query = await createQuery({ - fields: { - json: { - filter: {}, - update: { $set: { name: "newName" } }, + it("a find query", async () => { + const query = await createQuery({ + fields: { + json: {}, + extra: { + actionType: "find", + }, }, - extra: { - actionType: "updateMany", - }, - }, - queryVerb: "update", + }) + + const result = await config.api.query.execute(query._id!) + + expect(result.data).toEqual([ + { _id: expectValidId, name: "one" }, + { _id: expectValidId, name: "two" }, + { _id: expectValidId, name: "three" }, + { _id: expectValidId, name: "four" }, + { _id: expectValidId, name: "five" }, + ]) }) - const result = await config.api.query.execute(query._id!) + it("a findOne query", async () => { + const query = await createQuery({ + fields: { + json: {}, + extra: { + actionType: "findOne", + }, + }, + }) - expect(result.data).toEqual([ - { - acknowledged: true, - matchedCount: 5, - modifiedCount: 5, - upsertedCount: 0, - upsertedId: null, - }, - ]) + const result = await config.api.query.execute(query._id!) - await withCollection(async collection => { - const docs = await collection.find().toArray() - expect(docs).toHaveLength(5) - for (const doc of docs) { + expect(result.data).toEqual([{ _id: expectValidId, name: "one" }]) + }) + + it("a findOneAndUpdate query", async () => { + const query = await createQuery({ + fields: { + json: { + filter: { name: { $eq: "one" } }, + update: { $set: { name: "newName" } }, + }, + extra: { + actionType: "findOneAndUpdate", + }, + }, + }) + + const result = await config.api.query.execute(query._id!) + + expect(result.data).toEqual([ + { + lastErrorObject: { n: 1, updatedExisting: true }, + ok: 1, + value: { _id: expectValidId, name: "one" }, + }, + ]) + + await withCollection(async collection => { + expect(await collection.countDocuments()).toBe(5) + + const doc = await collection.findOne({ name: { $eq: "newName" } }) expect(doc).toEqual({ _id: expectValidBsonObjectId, name: "newName", }) - } + }) + }) + + it("a distinct query", async () => { + const query = await createQuery({ + fields: { + json: "name", + extra: { + actionType: "distinct", + }, + }, + }) + + const result = await config.api.query.execute(query._id!) + const values = result.data.map(o => o.value).sort() + expect(values).toEqual(["five", "four", "one", "three", "two"]) + }) + + it("a create query with parameters", async () => { + const query = await createQuery({ + fields: { + json: { foo: "{{ foo }}" }, + extra: { + actionType: "insertOne", + }, + }, + queryVerb: "create", + parameters: [ + { + name: "foo", + default: "default", + }, + ], + }) + + const result = await config.api.query.execute(query._id!, { + parameters: { foo: "bar" }, + }) + + expect(result.data).toEqual([ + { + acknowledged: true, + insertedId: expectValidId, + }, + ]) + + await withCollection(async collection => { + const doc = await collection.findOne({ foo: { $eq: "bar" } }) + expect(doc).toEqual({ + _id: expectValidBsonObjectId, + foo: "bar", + }) + }) + }) + + it("a delete query with parameters", async () => { + const query = await createQuery({ + fields: { + json: { name: { $eq: "{{ name }}" } }, + extra: { + actionType: "deleteOne", + }, + }, + queryVerb: "delete", + parameters: [ + { + name: "name", + default: "", + }, + ], + }) + + const result = await config.api.query.execute(query._id!, { + parameters: { name: "one" }, + }) + + expect(result.data).toEqual([ + { + acknowledged: true, + deletedCount: 1, + }, + ]) + + await withCollection(async collection => { + const doc = await collection.findOne({ name: { $eq: "one" } }) + expect(doc).toBeNull() + }) + }) + + it("an update query with parameters", async () => { + const query = await createQuery({ + fields: { + json: { + filter: { name: { $eq: "{{ name }}" } }, + update: { $set: { name: "{{ newName }}" } }, + }, + extra: { + actionType: "updateOne", + }, + }, + queryVerb: "update", + parameters: [ + { + name: "name", + default: "", + }, + { + name: "newName", + default: "", + }, + ], + }) + + const result = await config.api.query.execute(query._id!, { + parameters: { name: "one", newName: "newOne" }, + }) + + expect(result.data).toEqual([ + { + acknowledged: true, + matchedCount: 1, + modifiedCount: 1, + upsertedCount: 0, + upsertedId: null, + }, + ]) + + await withCollection(async collection => { + const doc = await collection.findOne({ name: { $eq: "newOne" } }) + expect(doc).toEqual({ + _id: expectValidBsonObjectId, + name: "newOne", + }) + + const oldDoc = await collection.findOne({ name: { $eq: "one" } }) + expect(oldDoc).toBeNull() + }) + }) + + it("should be able to delete all records", async () => { + const query = await createQuery({ + fields: { + json: {}, + extra: { + actionType: "deleteMany", + }, + }, + queryVerb: "delete", + }) + + const result = await config.api.query.execute(query._id!) + + expect(result.data).toEqual([ + { + acknowledged: true, + deletedCount: 5, + }, + ]) + + await withCollection(async collection => { + const docs = await collection.find().toArray() + expect(docs).toHaveLength(0) + }) + }) + + it("should be able to update all documents", async () => { + const query = await createQuery({ + fields: { + json: { + filter: {}, + update: { $set: { name: "newName" } }, + }, + extra: { + actionType: "updateMany", + }, + }, + queryVerb: "update", + }) + + const result = await config.api.query.execute(query._id!) + + expect(result.data).toEqual([ + { + acknowledged: true, + matchedCount: 5, + modifiedCount: 5, + upsertedCount: 0, + upsertedId: null, + }, + ]) + + await withCollection(async collection => { + const docs = await collection.find().toArray() + expect(docs).toHaveLength(5) + for (const doc of docs) { + expect(doc).toEqual({ + _id: expectValidBsonObjectId, + name: "newName", + }) + } + }) }) }) }) diff --git a/packages/server/src/api/routes/tests/queries/query.seq.spec.ts b/packages/server/src/api/routes/tests/queries/query.seq.spec.ts index 10b90eafb1..d8b06c001d 100644 --- a/packages/server/src/api/routes/tests/queries/query.seq.spec.ts +++ b/packages/server/src/api/routes/tests/queries/query.seq.spec.ts @@ -55,165 +55,15 @@ describe("/queries", () => { await setupTest() }) - const createQuery = async (query: Query) => { - return request - .post(`/api/queries`) - .send(query) - .set(config.defaultHeaders()) - .expect("Content-Type", /json/) - .expect(200) - } - - describe("create", () => { - it("should create a new query", async () => { - const { _id } = await config.createDatasource() - const query = basicQuery(_id) - jest.clearAllMocks() - const res = await createQuery(query) - - expect((res as any).res.statusMessage).toEqual( - `Query ${query.name} saved successfully.` - ) - expect(res.body).toEqual({ - _rev: res.body._rev, - _id: res.body._id, - ...query, - createdAt: new Date().toISOString(), - updatedAt: new Date().toISOString(), - }) - expect(events.query.created).toHaveBeenCalledTimes(1) - expect(events.query.updated).not.toHaveBeenCalled() - }) - }) - - describe("update", () => { - it("should update query", async () => { - const { _id } = await config.createDatasource() - const query = basicQuery(_id) - const res = await createQuery(query) - jest.clearAllMocks() - query._id = res.body._id - query._rev = res.body._rev - await createQuery(query) - - expect((res as any).res.statusMessage).toEqual( - `Query ${query.name} saved successfully.` - ) - expect(res.body).toEqual({ - _rev: res.body._rev, - _id: res.body._id, - ...query, - createdAt: new Date().toISOString(), - updatedAt: new Date().toISOString(), - }) - expect(events.query.created).not.toHaveBeenCalled() - expect(events.query.updated).toHaveBeenCalledTimes(1) - }) - }) - - describe("fetch", () => { - beforeEach(async () => { - await setupTest() - }) - - it("returns all the queries from the server", async () => { - const res = await request - .get(`/api/queries`) - .set(config.defaultHeaders()) - .expect("Content-Type", /json/) - .expect(200) - - const queries = res.body - expect(queries).toEqual([ - { - _rev: query._rev, - _id: query._id, - createdAt: new Date().toISOString(), - ...basicQuery(datasource._id), - updatedAt: new Date().toISOString(), - readable: true, - }, - ]) - }) - - it("should apply authorization to endpoint", async () => { - await checkBuilderEndpoint({ - config, - method: "GET", - url: `/api/datasources`, - }) - }) - }) - - describe("find", () => { - it("should find a query in builder", async () => { - const query = await config.createQuery() - const res = await request - .get(`/api/queries/${query._id}`) - .set(config.defaultHeaders()) - .expect("Content-Type", /json/) - .expect(200) - expect(res.body._id).toEqual(query._id) - }) - - it("should find a query in cloud", async () => { - await config.withEnv({ SELF_HOSTED: "true" }, async () => { - const query = await config.createQuery() - const res = await request - .get(`/api/queries/${query._id}`) - .set(await config.defaultHeaders()) - .expect(200) - .expect("Content-Type", /json/) - expect(res.body.fields).toBeDefined() - expect(res.body.parameters).toBeDefined() - expect(res.body.schema).toBeDefined() - }) - }) - - it("should remove sensitive info for prod apps", async () => { - // Mock isProdAppID to pretend we are using a prod app - mockIsProdAppID.mockClear() - mockIsProdAppID.mockImplementation(() => true) - - const query = await config.createQuery() - const res = await request - .get(`/api/queries/${query._id}`) - .set(await config.defaultHeaders()) - .expect("Content-Type", /json/) - .expect(200) - expect(res.body._id).toEqual(query._id) - expect(res.body.fields).toBeUndefined() - expect(res.body.parameters).toBeUndefined() - expect(res.body.schema).toBeDefined() - - // Reset isProdAppID mock - expect(dbCore.isProdAppID).toHaveBeenCalledTimes(1) - mockIsProdAppID.mockImplementation(() => false) + it("should apply authorization to endpoint", async () => { + await checkBuilderEndpoint({ + config, + method: "GET", + url: `/api/datasources`, }) }) describe("destroy", () => { - beforeEach(async () => { - await setupTest() - }) - - it("deletes a query and returns a success message", async () => { - await request - .delete(`/api/queries/${query._id}/${query._rev}`) - .set(config.defaultHeaders()) - .expect(200) - - const res = await request - .get(`/api/queries`) - .set(config.defaultHeaders()) - .expect("Content-Type", /json/) - .expect(200) - - expect(res.body).toEqual([]) - expect(events.query.deleted).toHaveBeenCalledTimes(1) - expect(events.query.deleted).toHaveBeenCalledWith(datasource, query) - }) - it("should apply authorization to endpoint", async () => { const query = await config.createQuery() await checkBuilderEndpoint({ @@ -225,32 +75,6 @@ describe("/queries", () => { }) describe("preview", () => { - it("should be able to preview the query", async () => { - const queryPreview: QueryPreview = { - datasourceId: datasource._id, - queryVerb: "read", - fields: {}, - parameters: [], - transformer: "return data", - name: datasource.name!, - schema: {}, - readable: true, - } - const responseBody = await config.api.query.previewQuery(queryPreview) - // these responses come from the mock - expect(responseBody.schema).toEqual({ - a: { type: "string", name: "a" }, - b: { type: "number", name: "b" }, - }) - expect(responseBody.rows.length).toEqual(1) - expect(events.query.previewed).toHaveBeenCalledTimes(1) - delete datasource.config - expect(events.query.previewed).toHaveBeenCalledWith( - datasource, - queryPreview - ) - }) - it("should apply authorization to endpoint", async () => { await checkBuilderEndpoint({ config, @@ -258,129 +82,6 @@ describe("/queries", () => { url: `/api/queries/preview`, }) }) - - it("should not error when trying to generate a nested schema for an empty array", async () => { - const queryPreview: QueryPreview = { - datasourceId: datasource._id, - parameters: [], - fields: {}, - queryVerb: "read", - name: datasource.name!, - transformer: "return data", - schema: {}, - readable: true, - } - const rows = [ - { - contacts: [], - }, - ] - pg.queryMock.mockImplementation(() => ({ - rows, - })) - - const responseBody = await config.api.query.previewQuery(queryPreview) - expect(responseBody).toEqual({ - nestedSchemaFields: {}, - rows, - schema: { - contacts: { type: "array", name: "contacts" }, - }, - }) - expect(responseBody.rows.length).toEqual(1) - delete datasource.config - }) - - it("should generate a nested schema based on all the nested items", async () => { - const queryPreview: QueryPreview = { - datasourceId: datasource._id, - parameters: [], - fields: {}, - queryVerb: "read", - name: datasource.name!, - transformer: "return data", - schema: {}, - readable: true, - } - const rows = [ - { - contacts: [ - { - address: "123 Lane", - }, - { - address: "456 Drive", - }, - { - postcode: "BT1 12N", - lat: 54.59, - long: -5.92, - }, - { - city: "Belfast", - }, - { - address: "789 Avenue", - phoneNumber: "0800-999-5555", - }, - { - name: "Name", - isActive: false, - }, - ], - }, - ] - - pg.queryMock.mockImplementation(() => ({ - rows, - })) - - const responseBody = await config.api.query.previewQuery(queryPreview) - expect(responseBody).toEqual({ - nestedSchemaFields: { - contacts: { - address: { - type: "string", - name: "address", - }, - postcode: { - type: "string", - name: "postcode", - }, - lat: { - type: "number", - name: "lat", - }, - long: { - type: "number", - name: "long", - }, - city: { - type: "string", - name: "city", - }, - phoneNumber: { - type: "string", - name: "phoneNumber", - }, - name: { - type: "string", - name: "name", - }, - isActive: { - type: "boolean", - name: "isActive", - }, - }, - }, - rows, - schema: { - contacts: { type: "json", name: "contacts", subtype: "array" }, - }, - }) - expect(responseBody.rows.length).toEqual(1) - delete datasource.config - }) }) describe("execute", () => { @@ -412,21 +113,6 @@ describe("/queries", () => { }, }) }) - - it("shouldn't allow handlebars to be passed as parameters", async () => { - const res = await request - .post(`/api/queries/${query._id}`) - .send({ - parameters: { - a: "{{ 'test' }}", - }, - }) - .set(config.defaultHeaders()) - .expect(400) - expect(res.body.message).toEqual( - "Parameter 'a' input contains a handlebars binding - this is not allowed." - ) - }) }) describe("variables", () => { @@ -444,40 +130,6 @@ describe("/queries", () => { return await config.api.query.previewQuery(queryPreview) } - it("should work with static variables", async () => { - const datasource = await config.restDatasource({ - staticVariables: { - variable: "google", - variable2: "1", - }, - }) - const responseBody = await preview(datasource, { - path: "www.{{ variable }}.com", - queryString: "test={{ variable2 }}", - }) - // these responses come from the mock - expect(responseBody.schema).toEqual({ - opts: { type: "json", name: "opts" }, - url: { type: "string", name: "url" }, - value: { type: "string", name: "value" }, - }) - expect(responseBody.rows[0].url).toEqual("http://www.google.com?test=1") - }) - - it("should work with dynamic variables", async () => { - const { datasource } = await config.dynamicVariableDatasource() - const responseBody = await preview(datasource, { - path: "www.google.com", - queryString: "test={{ variable3 }}", - }) - expect(responseBody.schema).toEqual({ - opts: { type: "json", name: "opts" }, - url: { type: "string", name: "url" }, - value: { type: "string", name: "value" }, - }) - expect(responseBody.rows[0].url).toContain("doctype%20html") - }) - it("check that it automatically retries on fail with cached dynamics", async () => { const { datasource, query: base } = await config.dynamicVariableDatasource() @@ -503,29 +155,6 @@ describe("/queries", () => { }) expect(responseBody.rows[0].fails).toEqual(1) }) - - it("deletes variables when linked query is deleted", async () => { - const { datasource, query: base } = - await config.dynamicVariableDatasource() - // preview once to cache - await preview(datasource, { - path: "www.google.com", - queryString: "test={{ variable3 }}", - }) - // check its in cache - let contents = await checkCacheForDynamicVariable(base._id!, "variable3") - expect(contents.rows.length).toEqual(1) - - // delete the query - await request - .delete(`/api/queries/${base._id}/${base._rev}`) - .set(config.defaultHeaders()) - .expect(200) - - // check variables no longer in cache - contents = await checkCacheForDynamicVariable(base._id!, "variable3") - expect(contents).toBe(null) - }) }) describe("Current User Request Mapping", () => { diff --git a/packages/server/src/tests/utilities/api/datasource.ts b/packages/server/src/tests/utilities/api/datasource.ts index 06aa9b4e1e..fd2a74a409 100644 --- a/packages/server/src/tests/utilities/api/datasource.ts +++ b/packages/server/src/tests/utilities/api/datasource.ts @@ -45,4 +45,17 @@ export class DatasourceAPI extends TestAPI { expectations, }) } + + delete = async (datasource: Datasource, expectations?: Expectations) => { + return await this._delete( + `/api/datasources/${datasource._id!}/${datasource._rev!}`, + { expectations } + ) + } + + get = async (id: string, expectations?: Expectations) => { + return await this._get(`/api/datasources/${id}`, { + expectations, + }) + } } diff --git a/packages/server/src/tests/utilities/api/query.ts b/packages/server/src/tests/utilities/api/query.ts index 32866314ff..aba4058832 100644 --- a/packages/server/src/tests/utilities/api/query.ts +++ b/packages/server/src/tests/utilities/api/query.ts @@ -5,28 +5,58 @@ import { PreviewQueryRequest, PreviewQueryResponse, } from "@budibase/types" -import { TestAPI } from "./base" +import { Expectations, TestAPI } from "./base" +import { constants } from "@budibase/backend-core" export class QueryAPI extends TestAPI { - create = async (body: Query): Promise => { - return await this._post(`/api/queries`, { body }) + save = async (body: Query, expectations?: Expectations): Promise => { + return await this._post(`/api/queries`, { body, expectations }) } execute = async ( queryId: string, - body?: ExecuteQueryRequest + body?: ExecuteQueryRequest, + expectations?: Expectations ): Promise => { return await this._post( `/api/v2/queries/${queryId}`, { body, + expectations, } ) } - previewQuery = async (queryPreview: PreviewQueryRequest) => { + previewQuery = async ( + queryPreview: PreviewQueryRequest, + expectations?: Expectations + ) => { return await this._post(`/api/queries/preview`, { body: queryPreview, + expectations, }) } + + delete = async (query: Query, expectations?: Expectations) => { + return await this._delete(`/api/queries/${query._id!}/${query._rev!}`, { + expectations, + }) + } + + get = async (queryId: string, expectations?: Expectations) => { + return await this._get(`/api/queries/${queryId}`, { expectations }) + } + + getProd = async (queryId: string, expectations?: Expectations) => { + return await this._get(`/api/queries/${queryId}`, { + expectations, + headers: { + [constants.Header.APP_ID]: this.config.getProdAppId(), + }, + }) + } + + fetch = async (expectations?: Expectations) => { + return await this._get(`/api/queries`, { expectations }) + } } From 08fecca9f289db9a8f57b0e45f85ebe939cfef5a Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Fri, 22 Mar 2024 12:00:26 +0000 Subject: [PATCH 02/97] Delete query.seq.spec.ts --- packages/server/package.json | 1 + .../server/src/api/controllers/datasource.ts | 4 +- .../server/src/api/controllers/query/index.ts | 4 +- .../src/api/routes/tests/datasource.spec.ts | 61 +-- .../routes/tests/queries/generic-sql.spec.ts | 42 +- .../api/routes/tests/queries/mongodb.spec.ts | 4 +- .../routes/tests/queries/permissions.spec.ts | 47 ++ .../routes/tests/queries/query.seq.spec.ts | 400 ----------------- .../src/api/routes/tests/queries/rest.spec.ts | 406 ++++++++++++++++++ .../src/tests/utilities/api/datasource.ts | 8 + .../server/src/tests/utilities/api/query.ts | 2 +- packages/server/src/threads/utils.ts | 26 +- yarn.lock | 179 ++++++-- 13 files changed, 691 insertions(+), 493 deletions(-) create mode 100644 packages/server/src/api/routes/tests/queries/permissions.spec.ts delete mode 100644 packages/server/src/api/routes/tests/queries/query.seq.spec.ts create mode 100644 packages/server/src/api/routes/tests/queries/rest.spec.ts diff --git a/packages/server/package.json b/packages/server/package.json index 8e66f3d7e0..182b29bb61 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -144,6 +144,7 @@ "jest-openapi": "0.14.2", "jest-runner": "29.7.0", "jest-serial-runner": "1.2.1", + "nock": "^13.5.4", "nodemon": "2.0.15", "openapi-typescript": "5.2.0", "path-to-regexp": "6.2.0", diff --git a/packages/server/src/api/controllers/datasource.ts b/packages/server/src/api/controllers/datasource.ts index 0f17c5a2f5..243d0a17a0 100644 --- a/packages/server/src/api/controllers/datasource.ts +++ b/packages/server/src/api/controllers/datasource.ts @@ -1,6 +1,6 @@ import { getQueryParams, getTableParams } from "../../db/utils" import { getIntegration } from "../../integrations" -import { invalidateDynamicVariables } from "../../threads/utils" +import { invalidateCachedVariable } from "../../threads/utils" import { context, db as dbCore, events } from "@budibase/backend-core" import { BuildSchemaFromSourceRequest, @@ -121,7 +121,7 @@ async function invalidateVariables( } }) } - await invalidateDynamicVariables(toInvalidate) + await invalidateCachedVariable(toInvalidate) } export async function update( diff --git a/packages/server/src/api/controllers/query/index.ts b/packages/server/src/api/controllers/query/index.ts index 0dba20dacd..d4d1711d42 100644 --- a/packages/server/src/api/controllers/query/index.ts +++ b/packages/server/src/api/controllers/query/index.ts @@ -2,7 +2,7 @@ import { generateQueryID } from "../../../db/utils" import { Thread, ThreadType } from "../../../threads" import { save as saveDatasource } from "../datasource" import { RestImporter } from "./import" -import { invalidateDynamicVariables } from "../../../threads/utils" +import { invalidateCachedVariable } from "../../../threads/utils" import env from "../../../environment" import { events, context, utils, constants } from "@budibase/backend-core" import sdk from "../../../sdk" @@ -401,7 +401,7 @@ const removeDynamicVariables = async (queryId: string) => { const variablesToDelete = dynamicVariables!.filter( (dv: any) => dv.queryId === queryId ) - await invalidateDynamicVariables(variablesToDelete) + await invalidateCachedVariable(variablesToDelete) } } diff --git a/packages/server/src/api/routes/tests/datasource.spec.ts b/packages/server/src/api/routes/tests/datasource.spec.ts index cbd830aee5..0066be2a64 100644 --- a/packages/server/src/api/routes/tests/datasource.spec.ts +++ b/packages/server/src/api/routes/tests/datasource.spec.ts @@ -1,18 +1,16 @@ -jest.mock("pg") import * as setup from "./utilities" import { checkBuilderEndpoint } from "./utilities/TestFunctions" -import { checkCacheForDynamicVariable } from "../../../threads/utils" +import { getCachedVariable } from "../../../threads/utils" import { context, events } from "@budibase/backend-core" import sdk from "../../../sdk" import tk from "timekeeper" import { mocks } from "@budibase/backend-core/tests" -import { QueryPreview } from "@budibase/types" +import { QueryPreview, SourceName } from "@budibase/types" tk.freeze(mocks.date.MOCK_DATE) let { basicDatasource } = setup.structures -const pg = require("pg") describe("/datasources", () => { let request = setup.getRequest() @@ -42,6 +40,23 @@ describe("/datasources", () => { expect(res.body.errors).toEqual({}) expect(events.datasource.created).toHaveBeenCalledTimes(1) }) + + it("should fail if the datasource is invalid", async () => { + await config.api.datasource.create( + { + name: "Test", + type: "test", + source: "invalid" as SourceName, + config: {}, + }, + { + status: 500, + body: { + message: "No datasource implementation found.", + }, + } + ) + }) }) describe("update", () => { @@ -74,7 +89,7 @@ describe("/datasources", () => { schema: {}, readable: true, } - return config.api.query.previewQuery(queryPreview) + return config.api.query.preview(queryPreview) } it("should invalidate changed or removed variables", async () => { @@ -85,10 +100,7 @@ describe("/datasources", () => { queryString: "test={{ variable3 }}", }) // check variables in cache - let contents = await checkCacheForDynamicVariable( - query._id!, - "variable3" - ) + let contents = await getCachedVariable(query._id!, "variable3") expect(contents.rows.length).toEqual(1) // update the datasource to remove the variables @@ -102,7 +114,7 @@ describe("/datasources", () => { expect(res.body.errors).toBeUndefined() // check variables no longer in cache - contents = await checkCacheForDynamicVariable(query._id!, "variable3") + contents = await getCachedVariable(query._id!, "variable3") expect(contents).toBe(null) }) }) @@ -149,35 +161,6 @@ describe("/datasources", () => { }) }) - describe("query", () => { - it("should be able to query a pg datasource", async () => { - const res = await request - .post(`/api/datasources/query`) - .send({ - endpoint: { - datasourceId: datasource._id, - operation: "READ", - // table name below - entityId: "users", - }, - resource: { - fields: ["users.name", "users.age"], - }, - filters: { - string: { - name: "John", - }, - }, - }) - .set(config.defaultHeaders()) - .expect(200) - // this is mock data, can't test it - expect(res.body).toBeDefined() - const expSql = `select "users"."name" as "users.name", "users"."age" as "users.age" from (select * from "users" where "users"."name" ilike $1 limit $2) as "users"` - expect(pg.queryMock).toHaveBeenCalledWith(expSql, ["John%", 5000]) - }) - }) - describe("destroy", () => { beforeAll(setupTest) diff --git a/packages/server/src/api/routes/tests/queries/generic-sql.spec.ts b/packages/server/src/api/routes/tests/queries/generic-sql.spec.ts index 4d97e07ed7..0e93fb6c98 100644 --- a/packages/server/src/api/routes/tests/queries/generic-sql.spec.ts +++ b/packages/server/src/api/routes/tests/queries/generic-sql.spec.ts @@ -1,4 +1,10 @@ -import { Datasource, Query, QueryPreview, SourceName } from "@budibase/types" +import { + Datasource, + Operation, + Query, + QueryPreview, + SourceName, +} from "@budibase/types" import * as setup from "../utilities" import { databaseTestProviders } from "../../../../integrations/tests/utils" import pg from "pg" @@ -6,6 +12,7 @@ import mysql from "mysql2/promise" import mssql from "mssql" import { Expectations } from "src/tests/utilities/api/base" import { events } from "@budibase/backend-core" +import { getCachedVariable } from "../../../../../src/threads/utils" jest.unmock("pg") @@ -257,7 +264,7 @@ describe.each([ schema: {}, readable: true, } - const response = await config.api.query.previewQuery(request) + const response = await config.api.query.preview(request) expect(response.schema).toEqual({ birthday: { name: "birthday", @@ -313,7 +320,7 @@ describe.each([ readable: true, } - const response = await config.api.query.previewQuery(request) + const response = await config.api.query.preview(request) expect(response.schema).toEqual({ foo: { @@ -350,7 +357,7 @@ describe.each([ }, }) - const preview = await config.api.query.previewQuery({ + const preview = await config.api.query.preview({ datasourceId: datasource._id!, queryVerb: "read", fields: { @@ -400,7 +407,7 @@ describe.each([ await config.api.query.delete(basedOnQuery) - const preview = await config.api.query.previewQuery({ + const preview = await config.api.query.preview({ datasourceId: datasource._id!, queryVerb: "read", fields: { @@ -750,4 +757,29 @@ describe.each([ }) }) }) + + describe("query through datasource", () => { + it("should be able to query a pg datasource", async () => { + const res = await config.api.datasource.query({ + endpoint: { + datasourceId: datasource._id!, + operation: Operation.READ, + entityId: "test_table", + }, + resource: { + fields: ["id", "name"], + }, + filters: { + string: { + name: "two", + }, + }, + }) + expect(res).toHaveLength(1) + expect(res[0]).toEqual({ + id: 2, + name: "two", + }) + }) + }) }) diff --git a/packages/server/src/api/routes/tests/queries/mongodb.spec.ts b/packages/server/src/api/routes/tests/queries/mongodb.spec.ts index 6ab3dec80d..aaeb93afc2 100644 --- a/packages/server/src/api/routes/tests/queries/mongodb.spec.ts +++ b/packages/server/src/api/routes/tests/queries/mongodb.spec.ts @@ -96,7 +96,7 @@ describe("/queries", () => { async collection => await collection.insertOne({ name, nested: [] }) ) - const preview = await config.api.query.previewQuery({ + const preview = await config.api.query.preview({ name: "New Query", datasourceId: datasource._id!, fields: { @@ -167,7 +167,7 @@ describe("/queries", () => { await withCollection(collection => collection.insertOne(item)) - const preview = await config.api.query.previewQuery({ + const preview = await config.api.query.preview({ name: "New Query", datasourceId: datasource._id!, fields: { diff --git a/packages/server/src/api/routes/tests/queries/permissions.spec.ts b/packages/server/src/api/routes/tests/queries/permissions.spec.ts new file mode 100644 index 0000000000..a0b342e64d --- /dev/null +++ b/packages/server/src/api/routes/tests/queries/permissions.spec.ts @@ -0,0 +1,47 @@ +import * as setup from "../utilities" +import { checkBuilderEndpoint } from "../utilities/TestFunctions" +import TestConfiguration from "../../../../tests/utilities/TestConfiguration" +import { Datasource, Query, SourceName } from "@budibase/types" + +describe("query permissions", () => { + let config: TestConfiguration + let datasource: Datasource + let query: Query + + beforeAll(async () => { + config = setup.getConfig() + await config.init() + datasource = await config.api.datasource.create({ + name: "test datasource", + type: "test", + source: SourceName.REST, + config: {}, + }) + query = await config.api.query.save({ + name: "test query", + datasourceId: datasource._id!, + parameters: [], + fields: {}, + transformer: "", + schema: {}, + readable: true, + queryVerb: "read", + }) + }) + + it("delete should require builder", async () => { + await checkBuilderEndpoint({ + config, + method: "DELETE", + url: `/api/queries/${query._id}/${query._rev}`, + }) + }) + + it("preview should require builder", async () => { + await checkBuilderEndpoint({ + config, + method: "POST", + url: `/api/queries/preview`, + }) + }) +}) diff --git a/packages/server/src/api/routes/tests/queries/query.seq.spec.ts b/packages/server/src/api/routes/tests/queries/query.seq.spec.ts deleted file mode 100644 index d8b06c001d..0000000000 --- a/packages/server/src/api/routes/tests/queries/query.seq.spec.ts +++ /dev/null @@ -1,400 +0,0 @@ -import tk from "timekeeper" - -const pg = require("pg") - -// Mock out postgres for this -jest.mock("pg") -jest.mock("node-fetch") - -// Mock isProdAppID to we can later mock the implementation and pretend we are -// using prod app IDs -jest.mock("@budibase/backend-core", () => { - const core = jest.requireActual("@budibase/backend-core") - return { - ...core, - db: { - ...core.db, - isProdAppID: jest.fn(), - }, - } -}) -import * as setup from "../utilities" -import { checkBuilderEndpoint } from "../utilities/TestFunctions" -import { checkCacheForDynamicVariable } from "../../../../threads/utils" - -const { basicQuery, basicDatasource } = setup.structures -import { events, db as dbCore } from "@budibase/backend-core" -import { - Datasource, - Query, - SourceName, - QueryPreview, - QueryParameter, -} from "@budibase/types" - -tk.freeze(Date.now()) - -const mockIsProdAppID = dbCore.isProdAppID as jest.MockedFunction< - typeof dbCore.isProdAppID -> - -describe("/queries", () => { - let request = setup.getRequest() - let config = setup.getConfig() - let datasource: Datasource & Required>, query: Query - - afterAll(setup.afterAll) - - const setupTest = async () => { - await config.init() - datasource = await config.createDatasource() - query = await config.createQuery() - } - - beforeAll(async () => { - await setupTest() - }) - - it("should apply authorization to endpoint", async () => { - await checkBuilderEndpoint({ - config, - method: "GET", - url: `/api/datasources`, - }) - }) - - describe("destroy", () => { - it("should apply authorization to endpoint", async () => { - const query = await config.createQuery() - await checkBuilderEndpoint({ - config, - method: "DELETE", - url: `/api/queries/${query._id}/${query._rev}`, - }) - }) - }) - - describe("preview", () => { - it("should apply authorization to endpoint", async () => { - await checkBuilderEndpoint({ - config, - method: "POST", - url: `/api/queries/preview`, - }) - }) - }) - - describe("execute", () => { - beforeEach(async () => { - await setupTest() - }) - - it("should be able to execute the query", async () => { - const res = await request - .post(`/api/queries/${query._id}`) - .send({ - parameters: {}, - }) - .set(config.defaultHeaders()) - .expect("Content-Type", /json/) - .expect(200) - expect(res.body.length).toEqual(1) - }) - - it("should fail with invalid integration type", async () => { - const datasource: Datasource = { - ...basicDatasource().datasource, - source: "INVALID_INTEGRATION" as SourceName, - } - await config.api.datasource.create(datasource, { - status: 500, - body: { - message: "No datasource implementation found.", - }, - }) - }) - }) - - describe("variables", () => { - async function preview(datasource: Datasource, fields: any) { - const queryPreview: QueryPreview = { - datasourceId: datasource._id!, - parameters: [], - fields, - queryVerb: "read", - name: datasource.name!, - transformer: "return data", - schema: {}, - readable: true, - } - return await config.api.query.previewQuery(queryPreview) - } - - it("check that it automatically retries on fail with cached dynamics", async () => { - const { datasource, query: base } = - await config.dynamicVariableDatasource() - // preview once to cache - await preview(datasource, { - path: "www.google.com", - queryString: "test={{ variable3 }}", - }) - // check its in cache - const contents = await checkCacheForDynamicVariable( - base._id!, - "variable3" - ) - expect(contents.rows.length).toEqual(1) - const responseBody = await preview(datasource, { - path: "www.failonce.com", - queryString: "test={{ variable3 }}", - }) - expect(responseBody.schema).toEqual({ - fails: { type: "number", name: "fails" }, - opts: { type: "json", name: "opts" }, - url: { type: "string", name: "url" }, - }) - expect(responseBody.rows[0].fails).toEqual(1) - }) - }) - - describe("Current User Request Mapping", () => { - async function previewGet( - datasource: Datasource, - fields: any, - params: QueryParameter[] - ) { - const queryPreview: QueryPreview = { - datasourceId: datasource._id!, - parameters: params, - fields, - queryVerb: "read", - name: datasource.name!, - transformer: "return data", - schema: {}, - readable: true, - } - return await config.api.query.previewQuery(queryPreview) - } - - async function previewPost( - datasource: Datasource, - fields: any, - params: QueryParameter[] - ) { - const queryPreview: QueryPreview = { - datasourceId: datasource._id!, - parameters: params, - fields, - queryVerb: "create", - name: datasource.name!, - transformer: null, - schema: {}, - readable: false, - } - return await config.api.query.previewQuery(queryPreview) - } - - it("should parse global and query level header mappings", async () => { - const userDetails = config.getUserDetails() - - const datasource = await config.restDatasource({ - defaultHeaders: { - test: "headerVal", - emailHdr: "{{[user].[email]}}", - }, - }) - const responseBody = await previewGet( - datasource, - { - path: "www.google.com", - queryString: "email={{[user].[email]}}", - headers: { - queryHdr: "{{[user].[firstName]}}", - secondHdr: "1234", - }, - }, - [] - ) - - const parsedRequest = JSON.parse(responseBody.extra.raw) - expect(parsedRequest.opts.headers).toEqual({ - test: "headerVal", - emailHdr: userDetails.email, - queryHdr: userDetails.firstName, - secondHdr: "1234", - }) - expect(responseBody.rows[0].url).toEqual( - "http://www.google.com?email=" + userDetails.email.replace("@", "%40") - ) - }) - - it("should bind the current user to query parameters", async () => { - const userDetails = config.getUserDetails() - - const datasource = await config.restDatasource() - - const responseBody = await previewGet( - datasource, - { - path: "www.google.com", - queryString: - "test={{myEmail}}&testName={{myName}}&testParam={{testParam}}", - }, - [ - { name: "myEmail", default: "{{[user].[email]}}" }, - { name: "myName", default: "{{[user].[firstName]}}" }, - { name: "testParam", default: "1234" }, - ] - ) - - expect(responseBody.rows[0].url).toEqual( - "http://www.google.com?test=" + - userDetails.email.replace("@", "%40") + - "&testName=" + - userDetails.firstName + - "&testParam=1234" - ) - }) - - it("should bind the current user the request body - plain text", async () => { - const userDetails = config.getUserDetails() - const datasource = await config.restDatasource() - - const responseBody = await previewPost( - datasource, - { - path: "www.google.com", - queryString: "testParam={{testParam}}", - requestBody: - "This is plain text and this is my email: {{[user].[email]}}. This is a test param: {{testParam}}", - bodyType: "text", - }, - [{ name: "testParam", default: "1234" }] - ) - - const parsedRequest = JSON.parse(responseBody.extra.raw) - expect(parsedRequest.opts.body).toEqual( - `This is plain text and this is my email: ${userDetails.email}. This is a test param: 1234` - ) - expect(responseBody.rows[0].url).toEqual( - "http://www.google.com?testParam=1234" - ) - }) - - it("should bind the current user the request body - json", async () => { - const userDetails = config.getUserDetails() - const datasource = await config.restDatasource() - - const responseBody = await previewPost( - datasource, - { - path: "www.google.com", - queryString: "testParam={{testParam}}", - requestBody: - '{"email":"{{[user].[email]}}","queryCode":{{testParam}},"userRef":"{{userRef}}"}', - bodyType: "json", - }, - [ - { name: "testParam", default: "1234" }, - { name: "userRef", default: "{{[user].[firstName]}}" }, - ] - ) - - const parsedRequest = JSON.parse(responseBody.extra.raw) - const test = `{"email":"${userDetails.email}","queryCode":1234,"userRef":"${userDetails.firstName}"}` - expect(parsedRequest.opts.body).toEqual(test) - expect(responseBody.rows[0].url).toEqual( - "http://www.google.com?testParam=1234" - ) - }) - - it("should bind the current user the request body - xml", async () => { - const userDetails = config.getUserDetails() - const datasource = await config.restDatasource() - - const responseBody = await previewPost( - datasource, - { - path: "www.google.com", - queryString: "testParam={{testParam}}", - requestBody: - " {{[user].[email]}} {{testParam}} " + - "{{userId}} testing ", - bodyType: "xml", - }, - [ - { name: "testParam", default: "1234" }, - { name: "userId", default: "{{[user].[firstName]}}" }, - ] - ) - - const parsedRequest = JSON.parse(responseBody.extra.raw) - const test = ` ${userDetails.email} 1234 ${userDetails.firstName} testing ` - - expect(parsedRequest.opts.body).toEqual(test) - expect(responseBody.rows[0].url).toEqual( - "http://www.google.com?testParam=1234" - ) - }) - - it("should bind the current user the request body - form-data", async () => { - const userDetails = config.getUserDetails() - const datasource = await config.restDatasource() - - const responseBody = await previewPost( - datasource, - { - path: "www.google.com", - queryString: "testParam={{testParam}}", - requestBody: - '{"email":"{{[user].[email]}}","queryCode":{{testParam}},"userRef":"{{userRef}}"}', - bodyType: "form", - }, - [ - { name: "testParam", default: "1234" }, - { name: "userRef", default: "{{[user].[firstName]}}" }, - ] - ) - - const parsedRequest = JSON.parse(responseBody.extra.raw) - - const emailData = parsedRequest.opts.body._streams[1] - expect(emailData).toEqual(userDetails.email) - - const queryCodeData = parsedRequest.opts.body._streams[4] - expect(queryCodeData).toEqual("1234") - - const userRef = parsedRequest.opts.body._streams[7] - expect(userRef).toEqual(userDetails.firstName) - - expect(responseBody.rows[0].url).toEqual( - "http://www.google.com?testParam=1234" - ) - }) - - it("should bind the current user the request body - encoded", async () => { - const userDetails = config.getUserDetails() - const datasource = await config.restDatasource() - - const responseBody = await previewPost( - datasource, - { - path: "www.google.com", - queryString: "testParam={{testParam}}", - requestBody: - '{"email":"{{[user].[email]}}","queryCode":{{testParam}},"userRef":"{{userRef}}"}', - bodyType: "encoded", - }, - [ - { name: "testParam", default: "1234" }, - { name: "userRef", default: "{{[user].[firstName]}}" }, - ] - ) - const parsedRequest = JSON.parse(responseBody.extra.raw) - - expect(parsedRequest.opts.body.email).toEqual(userDetails.email) - expect(parsedRequest.opts.body.queryCode).toEqual("1234") - expect(parsedRequest.opts.body.userRef).toEqual(userDetails.firstName) - }) - }) -}) diff --git a/packages/server/src/api/routes/tests/queries/rest.spec.ts b/packages/server/src/api/routes/tests/queries/rest.spec.ts new file mode 100644 index 0000000000..5c41583244 --- /dev/null +++ b/packages/server/src/api/routes/tests/queries/rest.spec.ts @@ -0,0 +1,406 @@ +import * as setup from "../utilities" +import TestConfiguration from "../../../../tests/utilities/TestConfiguration" +import { Datasource, SourceName } from "@budibase/types" +import { getCachedVariable } from "../../../../threads/utils" +import nock from "nock" +import { generator } from "@budibase/backend-core/tests" + +jest.unmock("node-fetch") + +describe("rest", () => { + let config: TestConfiguration + let datasource: Datasource + + async function createQuery(fields: any) { + return await config.api.query.save({ + name: "test query", + datasourceId: datasource._id!, + parameters: [], + fields, + transformer: "", + schema: {}, + readable: true, + queryVerb: "read", + }) + } + + beforeAll(async () => { + config = setup.getConfig() + await config.init() + datasource = await config.api.datasource.create({ + name: generator.guid(), + type: "test", + source: SourceName.REST, + config: {}, + }) + }) + + afterEach(() => { + nock.cleanAll() + }) + + it("should automatically retry on fail with cached dynamics", async () => { + const basedOnQuery = await createQuery({ + path: "one.example.com", + }) + + let cached = await getCachedVariable(basedOnQuery._id!, "foo") + expect(cached).toBeNull() + + await config.api.datasource.update({ + ...datasource, + config: { + ...datasource.config, + dynamicVariables: [ + { + queryId: basedOnQuery._id!, + name: "foo", + value: "{{ data[0].name }}", + }, + ], + }, + }) + + cached = await getCachedVariable(basedOnQuery._id!, "foo") + expect(cached).toBeNull() + + nock("http://one.example.com") + .get("/") + .reply(200, [{ name: "one" }]) + nock("http://two.example.com").get("/?test=one").reply(500) + nock("http://two.example.com") + .get("/?test=one") + .reply(200, [{ name: "two" }]) + + const res = await config.api.query.preview({ + datasourceId: datasource._id!, + name: "test query", + parameters: [], + queryVerb: "read", + transformer: "", + schema: {}, + readable: true, + fields: { + path: "two.example.com", + queryString: "test={{ foo }}", + }, + }) + expect(res.schema).toEqual({ + name: { type: "string", name: "name" }, + }) + + cached = await getCachedVariable(basedOnQuery._id!, "foo") + expect(cached.rows.length).toEqual(1) + expect(cached.rows[0].name).toEqual("one") + }) + + it("should parse global and query level header mappings", async () => { + const datasource = await config.api.datasource.create({ + name: generator.guid(), + type: "test", + source: SourceName.REST, + config: { + defaultHeaders: { + test: "headerVal", + emailHdr: "{{[user].[email]}}", + }, + }, + }) + + const user = config.getUserDetails() + const mock = nock("http://www.example.com", { + reqheaders: { + test: "headerVal", + emailhdr: user.email, + queryhdr: user.firstName!, + secondhdr: "1234", + }, + }) + .get("/?email=" + user.email.replace("@", "%40")) + .reply(200, {}) + + await config.api.query.preview({ + datasourceId: datasource._id!, + name: generator.guid(), + parameters: [], + queryVerb: "read", + transformer: "", + schema: {}, + readable: true, + fields: { + path: "www.example.com", + queryString: "email={{[user].[email]}}", + headers: { + queryHdr: "{{[user].[firstName]}}", + secondHdr: "1234", + }, + }, + }) + + expect(mock.isDone()).toEqual(true) + }) + + it("should bind the current user to query params", async () => { + const user = config.getUserDetails() + const mock = nock("http://www.example.com") + .get( + "/?test=" + + user.email.replace("@", "%40") + + "&testName=" + + user.firstName + + "&testParam=1234" + ) + .reply(200, {}) + + await config.api.query.preview({ + datasourceId: datasource._id!, + name: generator.guid(), + parameters: [ + { name: "myEmail", default: "{{[user].[email]}}" }, + { name: "myName", default: "{{[user].[firstName]}}" }, + { name: "testParam", default: "1234" }, + ], + queryVerb: "read", + transformer: "", + schema: {}, + readable: true, + fields: { + path: "www.example.com", + queryString: + "test={{myEmail}}&testName={{myName}}&testParam={{testParam}}", + }, + }) + + expect(mock.isDone()).toEqual(true) + }) + + it("should bind the current user to the request body - plain text", async () => { + const datasource = await config.api.datasource.create({ + name: generator.guid(), + type: "test", + source: SourceName.REST, + config: { + method: "POST", + defaultHeaders: { + test: "headerVal", + emailHdr: "{{[user].[email]}}", + }, + }, + }) + + const user = config.getUserDetails() + const mock = nock("http://www.example.com") + .post( + "/?testParam=1234", + "This is plain text and this is my email: " + + user.email + + ". This is a test param: 1234" + ) + .reply(200, {}) + + await config.api.query.preview({ + datasourceId: datasource._id!, + name: generator.guid(), + parameters: [{ name: "testParam", default: "1234" }], + queryVerb: "create", + transformer: "", + schema: {}, + readable: true, + fields: { + path: "www.example.com", + bodyType: "text", + queryString: "&testParam={{testParam}}", + requestBody: + "This is plain text and this is my email: {{[user].[email]}}. This is a test param: {{testParam}}", + }, + }) + + expect(mock.isDone()).toEqual(true) + }) + + it("should bind the current user to the request body - json", async () => { + const datasource = await config.api.datasource.create({ + name: generator.guid(), + type: "test", + source: SourceName.REST, + config: { + method: "POST", + defaultHeaders: { + test: "headerVal", + emailHdr: "{{[user].[email]}}", + }, + }, + }) + + const user = config.getUserDetails() + const mock = nock("http://www.example.com") + .post("/?testParam=1234", { + email: user.email, + queryCode: 1234, + userRef: user.firstName, + }) + .reply(200, {}) + + await config.api.query.preview({ + datasourceId: datasource._id!, + name: generator.guid(), + parameters: [ + { name: "testParam", default: "1234" }, + { name: "userRef", default: "{{[user].[firstName]}}" }, + ], + queryVerb: "create", + transformer: "", + schema: {}, + readable: true, + fields: { + path: "www.example.com", + bodyType: "json", + queryString: "&testParam={{testParam}}", + requestBody: + '{"email":"{{[user].[email]}}","queryCode":{{testParam}},"userRef":"{{userRef}}"}', + }, + }) + + expect(mock.isDone()).toEqual(true) + }) + + it("should bind the current user to the request body - xml", async () => { + const datasource = await config.api.datasource.create({ + name: generator.guid(), + type: "test", + source: SourceName.REST, + config: { + method: "POST", + defaultHeaders: { + test: "headerVal", + emailHdr: "{{[user].[email]}}", + }, + }, + }) + + const user = config.getUserDetails() + const mock = nock("http://www.example.com") + .post( + "/?testParam=1234", + ` ${user.email} 1234 ${user.firstName} testing ` + ) + .reply(200, {}) + + await config.api.query.preview({ + datasourceId: datasource._id!, + name: generator.guid(), + parameters: [ + { name: "testParam", default: "1234" }, + { name: "userId", default: "{{[user].[firstName]}}" }, + ], + queryVerb: "create", + transformer: "", + schema: {}, + readable: true, + fields: { + path: "www.example.com", + bodyType: "xml", + queryString: "&testParam={{testParam}}", + requestBody: + " {{[user].[email]}} {{testParam}} " + + "{{userId}} testing ", + }, + }) + + expect(mock.isDone()).toEqual(true) + }) + + it("should bind the current user to the request body - form-data", async () => { + const datasource = await config.api.datasource.create({ + name: generator.guid(), + type: "test", + source: SourceName.REST, + config: { + method: "POST", + defaultHeaders: { + test: "headerVal", + emailHdr: "{{[user].[email]}}", + }, + }, + }) + + const user = config.getUserDetails() + const mock = nock("http://www.example.com") + .post("/?testParam=1234", body => { + return ( + body.includes('name="email"\r\n\r\n' + user.email + "\r\n") && + body.includes('name="queryCode"\r\n\r\n1234\r\n') && + body.includes('name="userRef"\r\n\r\n' + user.firstName + "\r\n") + ) + }) + .reply(200, {}) + + await config.api.query.preview({ + datasourceId: datasource._id!, + name: generator.guid(), + parameters: [ + { name: "testParam", default: "1234" }, + { name: "userRef", default: "{{[user].[firstName]}}" }, + ], + queryVerb: "create", + transformer: "", + schema: {}, + readable: true, + fields: { + path: "www.example.com", + bodyType: "form", + queryString: "&testParam={{testParam}}", + requestBody: + '{"email":"{{[user].[email]}}","queryCode":{{testParam}},"userRef":"{{userRef}}"}', + }, + }) + + expect(mock.isDone()).toEqual(true) + }) + + it("should bind the current user to the request body - encoded", async () => { + const datasource = await config.api.datasource.create({ + name: generator.guid(), + type: "test", + source: SourceName.REST, + config: { + method: "POST", + defaultHeaders: { + test: "headerVal", + emailHdr: "{{[user].[email]}}", + }, + }, + }) + + const user = config.getUserDetails() + const mock = nock("http://www.example.com") + .post("/?testParam=1234", { + email: user.email, + queryCode: 1234, + userRef: user.firstName, + }) + .reply(200, {}) + + await config.api.query.preview({ + datasourceId: datasource._id!, + name: generator.guid(), + parameters: [ + { name: "testParam", default: "1234" }, + { name: "userRef", default: "{{[user].[firstName]}}" }, + ], + queryVerb: "create", + transformer: "", + schema: {}, + readable: true, + fields: { + path: "www.example.com", + bodyType: "encoded", + queryString: "&testParam={{testParam}}", + requestBody: + '{"email":"{{[user].[email]}}","queryCode":{{testParam}},"userRef":"{{userRef}}"}', + }, + }) + + expect(mock.isDone()).toEqual(true) + }) +}) diff --git a/packages/server/src/tests/utilities/api/datasource.ts b/packages/server/src/tests/utilities/api/datasource.ts index fd2a74a409..0296f58f7d 100644 --- a/packages/server/src/tests/utilities/api/datasource.ts +++ b/packages/server/src/tests/utilities/api/datasource.ts @@ -4,6 +4,7 @@ import { CreateDatasourceResponse, UpdateDatasourceResponse, UpdateDatasourceRequest, + QueryJson, } from "@budibase/types" import { Expectations, TestAPI } from "./base" @@ -58,4 +59,11 @@ export class DatasourceAPI extends TestAPI { expectations, }) } + + query = async (query: QueryJson, expectations?: Expectations) => { + return await this._post(`/api/datasources/query`, { + body: query, + expectations, + }) + } } diff --git a/packages/server/src/tests/utilities/api/query.ts b/packages/server/src/tests/utilities/api/query.ts index aba4058832..2d5f7970cd 100644 --- a/packages/server/src/tests/utilities/api/query.ts +++ b/packages/server/src/tests/utilities/api/query.ts @@ -27,7 +27,7 @@ export class QueryAPI extends TestAPI { ) } - previewQuery = async ( + preview = async ( queryPreview: PreviewQueryRequest, expectations?: Expectations ) => { diff --git a/packages/server/src/threads/utils.ts b/packages/server/src/threads/utils.ts index cd547cacae..46752975a2 100644 --- a/packages/server/src/threads/utils.ts +++ b/packages/server/src/threads/utils.ts @@ -5,7 +5,7 @@ import { redis, db as dbCore } from "@budibase/backend-core" import * as jsRunner from "../jsRunner" const VARIABLE_TTL_SECONDS = 3600 -let client: any +let client: redis.Client | null = null async function getClient() { if (!client) { @@ -36,23 +36,15 @@ export function threadSetup() { db.init() } -export async function checkCacheForDynamicVariable( - queryId: string, - variable: string -) { - const cache = await getClient() - return cache.get(makeVariableKey(queryId, variable)) +export async function getCachedVariable(queryId: string, variable: string) { + return (await getClient()).get(makeVariableKey(queryId, variable)) } -export async function invalidateDynamicVariables(cachedVars: QueryVariable[]) { +export async function invalidateCachedVariable(vars: QueryVariable[]) { const cache = await getClient() - let promises = [] - for (let variable of cachedVars) { - promises.push( - cache.delete(makeVariableKey(variable.queryId, variable.name)) - ) - } - await Promise.all(promises) + await Promise.all( + vars.map(v => cache.delete(makeVariableKey(v.queryId, v.name))) + ) } export async function storeDynamicVariable( @@ -93,7 +85,7 @@ export default { hasExtraData, formatResponse, storeDynamicVariable, - invalidateDynamicVariables, - checkCacheForDynamicVariable, + invalidateDynamicVariables: invalidateCachedVariable, + checkCacheForDynamicVariable: getCachedVariable, threadSetup, } diff --git a/yarn.lock b/yarn.lock index d035d65ba1..601bbf4641 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7265,7 +7265,37 @@ axios-retry@^3.1.9: "@babel/runtime" "^7.15.4" is-retry-allowed "^2.2.0" -axios@0.24.0, axios@1.1.3, axios@1.6.3, axios@^0.21.1, axios@^0.21.4, axios@^0.26.0, axios@^1.0.0, axios@^1.1.3, axios@^1.5.0: +axios@0.24.0: + version "0.24.0" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.24.0.tgz#804e6fa1e4b9c5288501dd9dff56a7a0940d20d6" + integrity sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA== + dependencies: + follow-redirects "^1.14.4" + +axios@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.1.3.tgz#8274250dada2edf53814ed7db644b9c2866c1e35" + integrity sha512-00tXVRwKx/FZr/IDVFt4C+f9FYairX517WoGCL6dpOntqLkZofjhu43F/Xl44UOpqa+9sLFDrG/XAnFsUYgkDA== + dependencies: + follow-redirects "^1.15.0" + form-data "^4.0.0" + proxy-from-env "^1.1.0" + +axios@^0.21.1, axios@^0.21.4: + version "0.21.4" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.4.tgz#c67b90dc0568e5c1cf2b0b858c43ba28e2eda575" + integrity sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg== + dependencies: + follow-redirects "^1.14.0" + +axios@^0.26.0: + version "0.26.1" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.26.1.tgz#1ede41c51fcf51bbbd6fd43669caaa4f0495aaa9" + integrity sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA== + dependencies: + follow-redirects "^1.14.8" + +axios@^1.0.0, axios@^1.1.3, axios@^1.5.0: version "1.6.3" resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.3.tgz#7f50f23b3aa246eff43c54834272346c396613f4" integrity sha512-fWyNdeawGam70jXSVlKl+SUNVcL6j6W79CuSIPfi6HnDUmSCH6gyUys/HrqHeA/wU0Az41rRgean494d0Jb+ww== @@ -11255,6 +11285,11 @@ fn.name@1.x.x: resolved "https://registry.yarnpkg.com/fn.name/-/fn.name-1.1.0.tgz#26cad8017967aea8731bc42961d04a3d5988accc" integrity sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw== +follow-redirects@^1.14.0, follow-redirects@^1.14.4, follow-redirects@^1.14.8: + version "1.15.6" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b" + integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA== + follow-redirects@^1.15.0: version "1.15.2" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13" @@ -12396,7 +12431,12 @@ http-assert@^1.3.0: deep-equal "~1.0.1" http-errors "~1.8.0" -http-cache-semantics@3.8.1, http-cache-semantics@4.1.1, http-cache-semantics@^4.0.0, http-cache-semantics@^4.1.0, http-cache-semantics@^4.1.1: +http-cache-semantics@3.8.1: + version "3.8.1" + resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz#39b0e16add9b605bf0a9ef3d9daaf4843b4cacd2" + integrity sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w== + +http-cache-semantics@^4.0.0, http-cache-semantics@^4.1.0, http-cache-semantics@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a" integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ== @@ -13346,11 +13386,6 @@ isobject@^3.0.1: resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== -isobject@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/isobject/-/isobject-4.0.0.tgz#3f1c9155e73b192022a80819bacd0343711697b0" - integrity sha512-S/2fF5wH8SJA/kmwr6HYhK/RI/OkhD84k8ntalo0iJjZikgq1XFvR5M8NPT1x5F7fBwCG3qHfnzeP/Vh/ZxCUA== - isolated-vm@^4.7.2: version "4.7.2" resolved "https://registry.yarnpkg.com/isolated-vm/-/isolated-vm-4.7.2.tgz#5670d5cce1d92004f9b825bec5b0b11fc7501b65" @@ -15945,7 +15980,7 @@ msgpackr-extract@^3.0.2: "@msgpackr-extract/msgpackr-extract-linux-x64" "3.0.2" "@msgpackr-extract/msgpackr-extract-win32-x64" "3.0.2" -msgpackr@1.10.1, msgpackr@^1.5.2: +msgpackr@^1.5.2: version "1.10.1" resolved "https://registry.yarnpkg.com/msgpackr/-/msgpackr-1.10.1.tgz#51953bb4ce4f3494f0c4af3f484f01cfbb306555" integrity sha512-r5VRLv9qouXuLiIBrLpl2d5ZvPt8svdQTl5/vMvE4nzDMyEX4sgW5yWhuBBj5UmgwOTWj8CIdSXn5sAfsHAWIQ== @@ -16113,6 +16148,15 @@ nice-try@^1.0.4: resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== +nock@^13.5.4: + version "13.5.4" + resolved "https://registry.yarnpkg.com/nock/-/nock-13.5.4.tgz#8918f0addc70a63736170fef7106a9721e0dc479" + integrity sha512-yAyTfdeNJGGBFxWdzSKCBYxs5FxLbCg5X5Q4ets974hcQzG1+qCxvIyOo4j2Ry6MUlhWVMX4OoYDefAIIwupjw== + dependencies: + debug "^4.1.0" + json-stringify-safe "^5.0.1" + propagate "^2.0.0" + node-abi@^3.3.0: version "3.54.0" resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-3.54.0.tgz#f6386f7548817acac6434c6cba02999c9aebcc69" @@ -16145,13 +16189,25 @@ node-duration@^1.0.4: resolved "https://registry.yarnpkg.com/node-duration/-/node-duration-1.0.4.tgz#3e94ecc0e473691c89c4560074503362071cecac" integrity sha512-eUXYNSY7DL53vqfTosggWkvyIW3bhAcqBDIlolgNYlZhianXTrCL50rlUJWD1eRqkIxMppXTfiFbp+9SjpPrgA== -node-fetch@2.6.0, node-fetch@2.6.7, node-fetch@^2.6.0, node-fetch@^2.6.1, node-fetch@^2.6.7, node-fetch@^2.6.9, node-fetch@^2.7.0: +node-fetch@2.6.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.0.tgz#e633456386d4aa55863f676a7ab0daa8fdecb0fd" + integrity sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA== + +node-fetch@2.6.7, node-fetch@^2.6.0, node-fetch@^2.6.1, node-fetch@^2.6.7: version "2.6.7" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== dependencies: whatwg-url "^5.0.0" +node-fetch@^2.6.9, node-fetch@^2.7.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d" + integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== + dependencies: + whatwg-url "^5.0.0" + node-forge@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3" @@ -17301,7 +17357,15 @@ passport-strategy@1.x.x, passport-strategy@^1.0.0: resolved "https://registry.yarnpkg.com/passport-strategy/-/passport-strategy-1.0.0.tgz#b5539aa8fc225a3d1ad179476ddf236b440f52e4" integrity sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA== -passport@0.6.0, passport@^0.4.0, passport@^0.6.0: +passport@^0.4.0: + version "0.4.1" + resolved "https://registry.yarnpkg.com/passport/-/passport-0.4.1.tgz#941446a21cb92fc688d97a0861c38ce9f738f270" + integrity sha512-IxXgZZs8d7uFSt3eqNjM9NQ3g3uQCW5avD8mRNoXV99Yig50vjuaez6dQK2qC0kVWPRTujxY0dWgGfT09adjYg== + dependencies: + passport-strategy "1.x.x" + pause "0.0.1" + +passport@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/passport/-/passport-0.6.0.tgz#e869579fab465b5c0b291e841e6cc95c005fac9d" integrity sha512-0fe+p3ZnrWRW74fe8+SvCyf4a3Pb2/h7gFkQ8yTJpAO50gDzlfjZUZTO1k5Eg9kUct22OxHLqDZoKUWRHOh9ug== @@ -18486,6 +18550,11 @@ promzard@^1.0.0: dependencies: read "^2.0.0" +propagate@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/propagate/-/propagate-2.0.1.tgz#40cdedab18085c792334e64f0ac17256d38f9a45" + integrity sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag== + proper-lockfile@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/proper-lockfile/-/proper-lockfile-4.1.2.tgz#c8b9de2af6b2f1601067f98e01ac66baa223141f" @@ -18596,7 +18665,7 @@ pseudomap@^1.0.2: resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" integrity sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ== -psl@^1.1.33: +psl@^1.1.28, psl@^1.1.33: version "1.9.0" resolved "https://registry.yarnpkg.com/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7" integrity sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag== @@ -19621,6 +19690,11 @@ sax@1.2.1: resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.1.tgz#7b8e656190b228e81a66aea748480d828cd2d37a" integrity sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA== +sax@>=0.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.3.0.tgz#a5dbe77db3be05c9d1ee7785dbd3ea9de51593d0" + integrity sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA== + sax@>=0.6.0: version "1.2.4" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" @@ -19702,13 +19776,40 @@ semver-diff@^3.1.1: dependencies: semver "^6.3.0" -"semver@2 || 3 || 4 || 5", semver@7.5.3, semver@^5.5.0, semver@^5.6.0, semver@^5.7.1, semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0, semver@^6.3.1, semver@^7.0.0, semver@^7.1.1, semver@^7.1.2, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.3, semver@^7.5.4, semver@~2.3.1, semver@~7.0.0: +"semver@2 || 3 || 4 || 5", semver@^5.5.0, semver@^5.6.0, semver@^5.7.1: + version "5.7.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" + integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== + +semver@7.5.3, semver@^7.0.0, semver@^7.1.1, semver@^7.1.2, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.3: version "7.5.3" resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.3.tgz#161ce8c2c6b4b3bdca6caadc9fa3317a4c4fe88e" integrity sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ== dependencies: lru-cache "^6.0.0" +semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0, semver@^6.3.1: + version "6.3.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== + +semver@^7.5.4: + version "7.6.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.0.tgz#1a46a4db4bffcccd97b743b5005c8325f23d4e2d" + integrity sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg== + dependencies: + lru-cache "^6.0.0" + +semver@~2.3.1: + version "2.3.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-2.3.2.tgz#b9848f25d6cf36333073ec9ef8856d42f1233e52" + integrity sha512-abLdIKCosKfpnmhS52NCTjO4RiLspDfsn37prjzGrp9im5DPJOgh82Os92vtwGh6XdQryKI/7SREZnV+aqiXrA== + +semver@~7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e" + integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A== + seq-queue@^0.0.5: version "0.0.5" resolved "https://registry.yarnpkg.com/seq-queue/-/seq-queue-0.0.5.tgz#d56812e1c017a6e4e7c3e3a37a1da6d78dd3c93e" @@ -21311,7 +21412,7 @@ touch@^3.1.0: dependencies: nopt "~1.0.10" -tough-cookie@4.1.3, "tough-cookie@^2.3.3 || ^3.0.1 || ^4.0.0", tough-cookie@^4.0.0, tough-cookie@^4.1.2, tough-cookie@~2.5.0: +"tough-cookie@^2.3.3 || ^3.0.1 || ^4.0.0", tough-cookie@^4.0.0, tough-cookie@^4.1.2: version "4.1.3" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.1.3.tgz#97b9adb0728b42280aa3d814b6b999b2ff0318bf" integrity sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw== @@ -21321,6 +21422,14 @@ tough-cookie@4.1.3, "tough-cookie@^2.3.3 || ^3.0.1 || ^4.0.0", tough-cookie@^4.0 universalify "^0.2.0" url-parse "^1.5.3" +tough-cookie@~2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" + integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== + dependencies: + psl "^1.1.28" + punycode "^2.1.1" + tr46@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/tr46/-/tr46-2.1.0.tgz#fa87aa81ca5d5941da8cbf1f9b749dc969a4e240" @@ -21797,14 +21906,6 @@ unpipe@1.0.0: resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== -unset-value@2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-2.0.1.tgz#57bed0c22d26f28d69acde5df9a11b77c74d2df3" - integrity sha512-2hvrBfjUE00PkqN+q0XP6yRAOGrR06uSiUoIQGZkc7GxvQ9H7v8quUPNtZjMg4uux69i8HWpIjLPUKwCuRGyNg== - dependencies: - has-value "^2.0.2" - isobject "^4.0.0" - untildify@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/untildify/-/untildify-4.0.0.tgz#2bc947b953652487e4600949fb091e3ae8cd919b" @@ -22575,10 +22676,33 @@ xml-parse-from-string@^1.0.0: resolved "https://registry.yarnpkg.com/xml-parse-from-string/-/xml-parse-from-string-1.0.1.tgz#a9029e929d3dbcded169f3c6e28238d95a5d5a28" integrity sha512-ErcKwJTF54uRzzNMXq2X5sMIy88zJvfN2DmdoQvy7PAFJ+tPRU6ydWuOKNMyfmOjdyBQTFREi60s0Y0SyI0G0g== -xml2js@0.1.x, xml2js@0.4.19, xml2js@0.5.0, xml2js@0.6.2, xml2js@^0.4.19, xml2js@^0.4.5: - version "0.6.2" - resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.6.2.tgz#dd0b630083aa09c161e25a4d0901e2b2a929b499" - integrity sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA== +xml2js@0.1.x: + version "0.1.14" + resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.1.14.tgz#5274e67f5a64c5f92974cd85139e0332adc6b90c" + integrity sha512-pbdws4PPPNc1HPluSUKamY4GWMk592K7qwcj6BExbVOhhubub8+pMda/ql68b6L3luZs/OGjGSB5goV7SnmgnA== + dependencies: + sax ">=0.1.1" + +xml2js@0.4.19: + version "0.4.19" + resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.19.tgz#686c20f213209e94abf0d1bcf1efaa291c7827a7" + integrity sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q== + dependencies: + sax ">=0.6.0" + xmlbuilder "~9.0.1" + +xml2js@0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.5.0.tgz#d9440631fbb2ed800203fad106f2724f62c493b7" + integrity sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA== + dependencies: + sax ">=0.6.0" + xmlbuilder "~11.0.0" + +xml2js@^0.4.19, xml2js@^0.4.5: + version "0.4.23" + resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.23.tgz#a0c69516752421eb2ac758ee4d4ccf58843eac66" + integrity sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug== dependencies: sax ">=0.6.0" xmlbuilder "~11.0.0" @@ -22588,6 +22712,11 @@ xmlbuilder@~11.0.0: resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz#be9bae1c8a046e76b31127726347d0ad7002beb3" integrity sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA== +xmlbuilder@~9.0.1: + version "9.0.7" + resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-9.0.7.tgz#132ee63d2ec5565c557e20f4c22df9aca686b10d" + integrity sha512-7YXTQc3P2l9+0rjaUbLwMKRhtmwg1M1eDf6nag7urC7pIPYLD9W/jmzQ4ptRSUbodw5S0jfoGTflLemQibSpeQ== + xmlchars@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" From 5e6a9d2176405ea50681384040e602ea6838d33c Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Fri, 22 Mar 2024 14:41:09 +0000 Subject: [PATCH 03/97] Convert executeQuery.spec.js to TypeScript and make it hit real databases. --- .../routes/tests/queries/generic-sql.spec.ts | 1 - .../automations/tests/executeQuery.spec.js | 39 -------- .../automations/tests/executeQuery.spec.ts | 92 +++++++++++++++++++ 3 files changed, 92 insertions(+), 40 deletions(-) delete mode 100644 packages/server/src/automations/tests/executeQuery.spec.js create mode 100644 packages/server/src/automations/tests/executeQuery.spec.ts diff --git a/packages/server/src/api/routes/tests/queries/generic-sql.spec.ts b/packages/server/src/api/routes/tests/queries/generic-sql.spec.ts index 0e93fb6c98..3ce96bd7d7 100644 --- a/packages/server/src/api/routes/tests/queries/generic-sql.spec.ts +++ b/packages/server/src/api/routes/tests/queries/generic-sql.spec.ts @@ -12,7 +12,6 @@ import mysql from "mysql2/promise" import mssql from "mssql" import { Expectations } from "src/tests/utilities/api/base" import { events } from "@budibase/backend-core" -import { getCachedVariable } from "../../../../../src/threads/utils" jest.unmock("pg") diff --git a/packages/server/src/automations/tests/executeQuery.spec.js b/packages/server/src/automations/tests/executeQuery.spec.js deleted file mode 100644 index 3b691f48ea..0000000000 --- a/packages/server/src/automations/tests/executeQuery.spec.js +++ /dev/null @@ -1,39 +0,0 @@ -const setup = require("./utilities") - -describe("test the execute query action", () => { - let query - let config = setup.getConfig() - - beforeAll(async () => { - await config.init() - - await config.createDatasource() - query = await config.createQuery() - }) - - afterAll(setup.afterAll) - - it("should be able to execute a query", async () => { - let res = await setup.runStep(setup.actions.EXECUTE_QUERY.stepId, { - query: { queryId: query._id }, - }) - expect(res.response).toEqual([{ a: "string", b: 1 }]) - expect(res.success).toEqual(true) - }) - - it("should handle a null query value", async () => { - let res = await setup.runStep(setup.actions.EXECUTE_QUERY.stepId, { - query: null, - }) - expect(res.response.message).toEqual("Invalid inputs") - expect(res.success).toEqual(false) - }) - - it("should handle an error executing a query", async () => { - let res = await setup.runStep(setup.actions.EXECUTE_QUERY.stepId, { - query: { queryId: "wrong_id" }, - }) - expect(res.response).toEqual("Error: missing") - expect(res.success).toEqual(false) - }) -}) diff --git a/packages/server/src/automations/tests/executeQuery.spec.ts b/packages/server/src/automations/tests/executeQuery.spec.ts new file mode 100644 index 0000000000..1d25a31b92 --- /dev/null +++ b/packages/server/src/automations/tests/executeQuery.spec.ts @@ -0,0 +1,92 @@ +import { Datasource, Query, SourceName } from "@budibase/types" +import * as setup from "./utilities" +import { databaseTestProviders } from "../../integrations/tests/utils" +import knex, { Knex } from "knex" +import { generator } from "@budibase/backend-core/tests" + +function getKnexClientName(source: SourceName) { + switch (source) { + case SourceName.MYSQL: + return "mysql2" + case SourceName.SQL_SERVER: + return "mssql" + case SourceName.POSTGRES: + return "pg" + } + throw new Error(`Unsupported source: ${source}`) +} + +describe.each([ + ["postgres", databaseTestProviders.postgres], + ["mysql", databaseTestProviders.mysql], + ["mssql", databaseTestProviders.mssql], + ["mariadb", databaseTestProviders.mariadb], +])("execute query action (%s)", (__, dsProvider) => { + let tableName: string + let client: Knex + let datasource: Datasource + let query: Query + let config = setup.getConfig() + + beforeAll(async () => { + await config.init() + + const ds = await dsProvider.datasource() + datasource = await config.api.datasource.create(ds) + client = knex({ + client: getKnexClientName(ds.source), + connection: ds.config, + }) + }) + + beforeEach(async () => { + tableName = generator.guid() + await client.schema.createTable(tableName, table => { + table.string("a") + table.integer("b") + }) + await client(tableName).insert({ a: "string", b: 1 }) + query = await config.api.query.save({ + name: "test query", + datasourceId: datasource._id!, + parameters: [], + fields: { + sql: client(tableName).select("*").toSQL().toNative().sql, + }, + transformer: "", + schema: {}, + readable: true, + queryVerb: "read", + }) + }) + + afterEach(async () => { + await client.schema.dropTable(tableName) + }) + + afterAll(setup.afterAll) + + it("should be able to execute a query", async () => { + let res = await setup.runStep(setup.actions.EXECUTE_QUERY.stepId, { + query: { queryId: query._id }, + }) + expect(res.response).toEqual([{ a: "string", b: 1 }]) + expect(res.success).toEqual(true) + }) + + it("should handle a null query value", async () => { + let res = await setup.runStep(setup.actions.EXECUTE_QUERY.stepId, { + query: null, + }) + expect(res.response.message).toEqual("Invalid inputs") + expect(res.success).toEqual(false) + }) + + it("should handle an error executing a query", async () => { + let res = await setup.runStep(setup.actions.EXECUTE_QUERY.stepId, { + query: { queryId: "wrong_id" }, + }) + expect(res.response).toEqual("Error: missing") + expect(res.success).toEqual(false) + }) +}) From a9aa409d7c976c8b5fd8ec40c5f205f3b3d889b4 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Fri, 22 Mar 2024 15:01:40 +0000 Subject: [PATCH 04/97] Fix up tests. --- packages/server/jest.config.ts | 6 ------ packages/server/package.json | 1 - .../api/routes/tests/queries/generic-sql.spec.ts | 2 -- packages/server/src/api/routes/tests/row.spec.ts | 1 - .../server/src/api/routes/tests/viewV2.spec.ts | 1 - .../server/src/integration-test/postgres.spec.ts | 1 - .../server/src/integrations/tests/utils/index.ts | 2 -- .../server/src/sdk/users/tests/utils.spec.ts | 16 ++++++++++++---- 8 files changed, 12 insertions(+), 18 deletions(-) diff --git a/packages/server/jest.config.ts b/packages/server/jest.config.ts index f3f8ebce02..db0625241d 100644 --- a/packages/server/jest.config.ts +++ b/packages/server/jest.config.ts @@ -42,12 +42,6 @@ if (fs.existsSync("../pro/src")) { const config: Config.InitialOptions = { projects: [ - { - ...baseConfig, - displayName: "sequential test", - testMatch: ["/**/*.seq.spec.[jt]s"], - runner: "jest-serial-runner", - }, { ...baseConfig, testMatch: ["/**/!(*.seq).spec.[jt]s"], diff --git a/packages/server/package.json b/packages/server/package.json index 182b29bb61..27f34e3607 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -143,7 +143,6 @@ "jest": "29.7.0", "jest-openapi": "0.14.2", "jest-runner": "29.7.0", - "jest-serial-runner": "1.2.1", "nock": "^13.5.4", "nodemon": "2.0.15", "openapi-typescript": "5.2.0", diff --git a/packages/server/src/api/routes/tests/queries/generic-sql.spec.ts b/packages/server/src/api/routes/tests/queries/generic-sql.spec.ts index 3ce96bd7d7..060699450c 100644 --- a/packages/server/src/api/routes/tests/queries/generic-sql.spec.ts +++ b/packages/server/src/api/routes/tests/queries/generic-sql.spec.ts @@ -13,8 +13,6 @@ import mssql from "mssql" import { Expectations } from "src/tests/utilities/api/base" import { events } from "@budibase/backend-core" -jest.unmock("pg") - const createTableSQL: Record = { [SourceName.POSTGRES]: ` CREATE TABLE test_table ( diff --git a/packages/server/src/api/routes/tests/row.spec.ts b/packages/server/src/api/routes/tests/row.spec.ts index f638f2c4bf..7f8a28ad99 100644 --- a/packages/server/src/api/routes/tests/row.spec.ts +++ b/packages/server/src/api/routes/tests/row.spec.ts @@ -30,7 +30,6 @@ const timestamp = new Date("2023-01-26T11:48:57.597Z").toISOString() tk.freeze(timestamp) jest.unmock("mssql") -jest.unmock("pg") describe.each([ ["internal", undefined], diff --git a/packages/server/src/api/routes/tests/viewV2.spec.ts b/packages/server/src/api/routes/tests/viewV2.spec.ts index f9d213a26b..b99edc2264 100644 --- a/packages/server/src/api/routes/tests/viewV2.spec.ts +++ b/packages/server/src/api/routes/tests/viewV2.spec.ts @@ -26,7 +26,6 @@ import { quotas } from "@budibase/pro" import { roles } from "@budibase/backend-core" jest.unmock("mssql") -jest.unmock("pg") describe.each([ ["internal", undefined], diff --git a/packages/server/src/integration-test/postgres.spec.ts b/packages/server/src/integration-test/postgres.spec.ts index 107c4ade1e..bb6882e0e1 100644 --- a/packages/server/src/integration-test/postgres.spec.ts +++ b/packages/server/src/integration-test/postgres.spec.ts @@ -23,7 +23,6 @@ fetch.mockSearch() const config = setup.getConfig()! -jest.unmock("pg") jest.mock("../websockets") describe("postgres integrations", () => { diff --git a/packages/server/src/integrations/tests/utils/index.ts b/packages/server/src/integrations/tests/utils/index.ts index b2be3df4e0..7faa84099b 100644 --- a/packages/server/src/integrations/tests/utils/index.ts +++ b/packages/server/src/integrations/tests/utils/index.ts @@ -1,5 +1,3 @@ -jest.unmock("pg") - import { Datasource } from "@budibase/types" import * as postgres from "./postgres" import * as mongodb from "./mongodb" diff --git a/packages/server/src/sdk/users/tests/utils.spec.ts b/packages/server/src/sdk/users/tests/utils.spec.ts index 6f1c5afd3d..0aa112094d 100644 --- a/packages/server/src/sdk/users/tests/utils.spec.ts +++ b/packages/server/src/sdk/users/tests/utils.spec.ts @@ -35,11 +35,20 @@ describe("syncGlobalUsers", () => { builder: { global: true }, }) await config.doInContext(config.appId, async () => { - expect(await rawUserMetadata()).toHaveLength(1) + let metadata = await rawUserMetadata() + expect(metadata).not.toContainEqual( + expect.objectContaining({ + _id: db.generateUserMetadataID(user1._id!), + }) + ) + expect(metadata).not.toContainEqual( + expect.objectContaining({ + _id: db.generateUserMetadataID(user2._id!), + }) + ) await syncGlobalUsers() - const metadata = await rawUserMetadata() - expect(metadata).toHaveLength(3) + metadata = await rawUserMetadata() expect(metadata).toContainEqual( expect.objectContaining({ _id: db.generateUserMetadataID(user1._id!), @@ -62,7 +71,6 @@ describe("syncGlobalUsers", () => { await syncGlobalUsers() const metadata = await rawUserMetadata() - expect(metadata).toHaveLength(1) expect(metadata).not.toContainEqual( expect.objectContaining({ _id: db.generateUserMetadataID(user._id!), From a925132fdff9fc8905cdc8de33f4dba56b751bb3 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Mon, 25 Mar 2024 15:00:16 +0000 Subject: [PATCH 05/97] Fix broken tests. --- .../routes/tests/queries/generic-sql.spec.ts | 26 ++- yarn.lock | 165 +++--------------- 2 files changed, 35 insertions(+), 156 deletions(-) diff --git a/packages/server/src/api/routes/tests/queries/generic-sql.spec.ts b/packages/server/src/api/routes/tests/queries/generic-sql.spec.ts index ecae874913..f8e54e064e 100644 --- a/packages/server/src/api/routes/tests/queries/generic-sql.spec.ts +++ b/packages/server/src/api/routes/tests/queries/generic-sql.spec.ts @@ -278,22 +278,20 @@ describe.each([ name: "name", type: "string", }, + number: { + name: "number", + type: "string", + }, }) expect(response.rows).toEqual([ { birthday: null, id: 1, name: "one", + number: null, }, ]) expect(events.query.previewed).toHaveBeenCalledTimes(1) - - const dsWithoutConfig = { ...datasource } - delete dsWithoutConfig.config - expect(events.query.previewed).toHaveBeenCalledWith( - dsWithoutConfig, - request - ) }) it("should work with static variables", async () => { @@ -427,11 +425,9 @@ describe.each([ }, }) - // TODO: is this the correct behaviour? To return an empty string when the - // underlying query has been deleted? expect(preview.rows).toEqual([ { - foo: "", + foo: datasource.source === SourceName.SQL_SERVER ? "" : null, }, ]) }) @@ -626,6 +622,7 @@ describe.each([ id: 2, name: "one", birthday: null, + number: null, }, ]) }) @@ -693,7 +690,9 @@ describe.each([ ]) const rows = await rawQuery("SELECT * FROM test_table WHERE id = 1") - expect(rows).toEqual([{ id: 1, name: "foo", birthday: null }]) + expect(rows).toEqual([ + { id: 1, name: "foo", birthday: null, number: null }, + ]) }) it("should be able to execute an update that updates no rows", async () => { @@ -786,11 +785,6 @@ describe.each([ id: 2, name: "two", }) - - const rows = await rawQuery("SELECT * FROM test_table WHERE id = 1") - expect(rows).toEqual([ - { id: 1, name: "foo", birthday: null, number: null }, - ]) }) it("should be able to execute an update that updates no rows", async () => { diff --git a/yarn.lock b/yarn.lock index 601bbf4641..9e1dcae301 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7265,37 +7265,7 @@ axios-retry@^3.1.9: "@babel/runtime" "^7.15.4" is-retry-allowed "^2.2.0" -axios@0.24.0: - version "0.24.0" - resolved "https://registry.yarnpkg.com/axios/-/axios-0.24.0.tgz#804e6fa1e4b9c5288501dd9dff56a7a0940d20d6" - integrity sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA== - dependencies: - follow-redirects "^1.14.4" - -axios@1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/axios/-/axios-1.1.3.tgz#8274250dada2edf53814ed7db644b9c2866c1e35" - integrity sha512-00tXVRwKx/FZr/IDVFt4C+f9FYairX517WoGCL6dpOntqLkZofjhu43F/Xl44UOpqa+9sLFDrG/XAnFsUYgkDA== - dependencies: - follow-redirects "^1.15.0" - form-data "^4.0.0" - proxy-from-env "^1.1.0" - -axios@^0.21.1, axios@^0.21.4: - version "0.21.4" - resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.4.tgz#c67b90dc0568e5c1cf2b0b858c43ba28e2eda575" - integrity sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg== - dependencies: - follow-redirects "^1.14.0" - -axios@^0.26.0: - version "0.26.1" - resolved "https://registry.yarnpkg.com/axios/-/axios-0.26.1.tgz#1ede41c51fcf51bbbd6fd43669caaa4f0495aaa9" - integrity sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA== - dependencies: - follow-redirects "^1.14.8" - -axios@^1.0.0, axios@^1.1.3, axios@^1.5.0: +axios@0.24.0, axios@1.1.3, axios@1.6.3, axios@^0.21.1, axios@^0.21.4, axios@^0.26.0, axios@^1.0.0, axios@^1.1.3, axios@^1.5.0: version "1.6.3" resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.3.tgz#7f50f23b3aa246eff43c54834272346c396613f4" integrity sha512-fWyNdeawGam70jXSVlKl+SUNVcL6j6W79CuSIPfi6HnDUmSCH6gyUys/HrqHeA/wU0Az41rRgean494d0Jb+ww== @@ -11285,11 +11255,6 @@ fn.name@1.x.x: resolved "https://registry.yarnpkg.com/fn.name/-/fn.name-1.1.0.tgz#26cad8017967aea8731bc42961d04a3d5988accc" integrity sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw== -follow-redirects@^1.14.0, follow-redirects@^1.14.4, follow-redirects@^1.14.8: - version "1.15.6" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b" - integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA== - follow-redirects@^1.15.0: version "1.15.2" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13" @@ -12431,12 +12396,7 @@ http-assert@^1.3.0: deep-equal "~1.0.1" http-errors "~1.8.0" -http-cache-semantics@3.8.1: - version "3.8.1" - resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz#39b0e16add9b605bf0a9ef3d9daaf4843b4cacd2" - integrity sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w== - -http-cache-semantics@^4.0.0, http-cache-semantics@^4.1.0, http-cache-semantics@^4.1.1: +http-cache-semantics@3.8.1, http-cache-semantics@4.1.1, http-cache-semantics@^4.0.0, http-cache-semantics@^4.1.0, http-cache-semantics@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a" integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ== @@ -13386,6 +13346,11 @@ isobject@^3.0.1: resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== +isobject@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-4.0.0.tgz#3f1c9155e73b192022a80819bacd0343711697b0" + integrity sha512-S/2fF5wH8SJA/kmwr6HYhK/RI/OkhD84k8ntalo0iJjZikgq1XFvR5M8NPT1x5F7fBwCG3qHfnzeP/Vh/ZxCUA== + isolated-vm@^4.7.2: version "4.7.2" resolved "https://registry.yarnpkg.com/isolated-vm/-/isolated-vm-4.7.2.tgz#5670d5cce1d92004f9b825bec5b0b11fc7501b65" @@ -15980,7 +15945,7 @@ msgpackr-extract@^3.0.2: "@msgpackr-extract/msgpackr-extract-linux-x64" "3.0.2" "@msgpackr-extract/msgpackr-extract-win32-x64" "3.0.2" -msgpackr@^1.5.2: +msgpackr@1.10.1, msgpackr@^1.5.2: version "1.10.1" resolved "https://registry.yarnpkg.com/msgpackr/-/msgpackr-1.10.1.tgz#51953bb4ce4f3494f0c4af3f484f01cfbb306555" integrity sha512-r5VRLv9qouXuLiIBrLpl2d5ZvPt8svdQTl5/vMvE4nzDMyEX4sgW5yWhuBBj5UmgwOTWj8CIdSXn5sAfsHAWIQ== @@ -16189,25 +16154,13 @@ node-duration@^1.0.4: resolved "https://registry.yarnpkg.com/node-duration/-/node-duration-1.0.4.tgz#3e94ecc0e473691c89c4560074503362071cecac" integrity sha512-eUXYNSY7DL53vqfTosggWkvyIW3bhAcqBDIlolgNYlZhianXTrCL50rlUJWD1eRqkIxMppXTfiFbp+9SjpPrgA== -node-fetch@2.6.0: - version "2.6.0" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.0.tgz#e633456386d4aa55863f676a7ab0daa8fdecb0fd" - integrity sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA== - -node-fetch@2.6.7, node-fetch@^2.6.0, node-fetch@^2.6.1, node-fetch@^2.6.7: +node-fetch@2.6.0, node-fetch@2.6.7, node-fetch@^2.6.0, node-fetch@^2.6.1, node-fetch@^2.6.7, node-fetch@^2.6.9, node-fetch@^2.7.0: version "2.6.7" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== dependencies: whatwg-url "^5.0.0" -node-fetch@^2.6.9, node-fetch@^2.7.0: - version "2.7.0" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d" - integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== - dependencies: - whatwg-url "^5.0.0" - node-forge@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3" @@ -17357,15 +17310,7 @@ passport-strategy@1.x.x, passport-strategy@^1.0.0: resolved "https://registry.yarnpkg.com/passport-strategy/-/passport-strategy-1.0.0.tgz#b5539aa8fc225a3d1ad179476ddf236b440f52e4" integrity sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA== -passport@^0.4.0: - version "0.4.1" - resolved "https://registry.yarnpkg.com/passport/-/passport-0.4.1.tgz#941446a21cb92fc688d97a0861c38ce9f738f270" - integrity sha512-IxXgZZs8d7uFSt3eqNjM9NQ3g3uQCW5avD8mRNoXV99Yig50vjuaez6dQK2qC0kVWPRTujxY0dWgGfT09adjYg== - dependencies: - passport-strategy "1.x.x" - pause "0.0.1" - -passport@^0.6.0: +passport@0.6.0, passport@^0.4.0, passport@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/passport/-/passport-0.6.0.tgz#e869579fab465b5c0b291e841e6cc95c005fac9d" integrity sha512-0fe+p3ZnrWRW74fe8+SvCyf4a3Pb2/h7gFkQ8yTJpAO50gDzlfjZUZTO1k5Eg9kUct22OxHLqDZoKUWRHOh9ug== @@ -18665,7 +18610,7 @@ pseudomap@^1.0.2: resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" integrity sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ== -psl@^1.1.28, psl@^1.1.33: +psl@^1.1.33: version "1.9.0" resolved "https://registry.yarnpkg.com/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7" integrity sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag== @@ -19690,11 +19635,6 @@ sax@1.2.1: resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.1.tgz#7b8e656190b228e81a66aea748480d828cd2d37a" integrity sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA== -sax@>=0.1.1: - version "1.3.0" - resolved "https://registry.yarnpkg.com/sax/-/sax-1.3.0.tgz#a5dbe77db3be05c9d1ee7785dbd3ea9de51593d0" - integrity sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA== - sax@>=0.6.0: version "1.2.4" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" @@ -19776,40 +19716,13 @@ semver-diff@^3.1.1: dependencies: semver "^6.3.0" -"semver@2 || 3 || 4 || 5", semver@^5.5.0, semver@^5.6.0, semver@^5.7.1: - version "5.7.2" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" - integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== - -semver@7.5.3, semver@^7.0.0, semver@^7.1.1, semver@^7.1.2, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.3: +"semver@2 || 3 || 4 || 5", semver@7.5.3, semver@^5.5.0, semver@^5.6.0, semver@^5.7.1, semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0, semver@^6.3.1, semver@^7.0.0, semver@^7.1.1, semver@^7.1.2, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.3, semver@^7.5.4, semver@~2.3.1, semver@~7.0.0: version "7.5.3" resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.3.tgz#161ce8c2c6b4b3bdca6caadc9fa3317a4c4fe88e" integrity sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ== dependencies: lru-cache "^6.0.0" -semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0, semver@^6.3.1: - version "6.3.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" - integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== - -semver@^7.5.4: - version "7.6.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.0.tgz#1a46a4db4bffcccd97b743b5005c8325f23d4e2d" - integrity sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg== - dependencies: - lru-cache "^6.0.0" - -semver@~2.3.1: - version "2.3.2" - resolved "https://registry.yarnpkg.com/semver/-/semver-2.3.2.tgz#b9848f25d6cf36333073ec9ef8856d42f1233e52" - integrity sha512-abLdIKCosKfpnmhS52NCTjO4RiLspDfsn37prjzGrp9im5DPJOgh82Os92vtwGh6XdQryKI/7SREZnV+aqiXrA== - -semver@~7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e" - integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A== - seq-queue@^0.0.5: version "0.0.5" resolved "https://registry.yarnpkg.com/seq-queue/-/seq-queue-0.0.5.tgz#d56812e1c017a6e4e7c3e3a37a1da6d78dd3c93e" @@ -21412,7 +21325,7 @@ touch@^3.1.0: dependencies: nopt "~1.0.10" -"tough-cookie@^2.3.3 || ^3.0.1 || ^4.0.0", tough-cookie@^4.0.0, tough-cookie@^4.1.2: +tough-cookie@4.1.3, "tough-cookie@^2.3.3 || ^3.0.1 || ^4.0.0", tough-cookie@^4.0.0, tough-cookie@^4.1.2, tough-cookie@~2.5.0: version "4.1.3" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.1.3.tgz#97b9adb0728b42280aa3d814b6b999b2ff0318bf" integrity sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw== @@ -21422,14 +21335,6 @@ touch@^3.1.0: universalify "^0.2.0" url-parse "^1.5.3" -tough-cookie@~2.5.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" - integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== - dependencies: - psl "^1.1.28" - punycode "^2.1.1" - tr46@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/tr46/-/tr46-2.1.0.tgz#fa87aa81ca5d5941da8cbf1f9b749dc969a4e240" @@ -21906,6 +21811,14 @@ unpipe@1.0.0: resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== +unset-value@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-2.0.1.tgz#57bed0c22d26f28d69acde5df9a11b77c74d2df3" + integrity sha512-2hvrBfjUE00PkqN+q0XP6yRAOGrR06uSiUoIQGZkc7GxvQ9H7v8quUPNtZjMg4uux69i8HWpIjLPUKwCuRGyNg== + dependencies: + has-value "^2.0.2" + isobject "^4.0.0" + untildify@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/untildify/-/untildify-4.0.0.tgz#2bc947b953652487e4600949fb091e3ae8cd919b" @@ -22676,33 +22589,10 @@ xml-parse-from-string@^1.0.0: resolved "https://registry.yarnpkg.com/xml-parse-from-string/-/xml-parse-from-string-1.0.1.tgz#a9029e929d3dbcded169f3c6e28238d95a5d5a28" integrity sha512-ErcKwJTF54uRzzNMXq2X5sMIy88zJvfN2DmdoQvy7PAFJ+tPRU6ydWuOKNMyfmOjdyBQTFREi60s0Y0SyI0G0g== -xml2js@0.1.x: - version "0.1.14" - resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.1.14.tgz#5274e67f5a64c5f92974cd85139e0332adc6b90c" - integrity sha512-pbdws4PPPNc1HPluSUKamY4GWMk592K7qwcj6BExbVOhhubub8+pMda/ql68b6L3luZs/OGjGSB5goV7SnmgnA== - dependencies: - sax ">=0.1.1" - -xml2js@0.4.19: - version "0.4.19" - resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.19.tgz#686c20f213209e94abf0d1bcf1efaa291c7827a7" - integrity sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q== - dependencies: - sax ">=0.6.0" - xmlbuilder "~9.0.1" - -xml2js@0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.5.0.tgz#d9440631fbb2ed800203fad106f2724f62c493b7" - integrity sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA== - dependencies: - sax ">=0.6.0" - xmlbuilder "~11.0.0" - -xml2js@^0.4.19, xml2js@^0.4.5: - version "0.4.23" - resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.23.tgz#a0c69516752421eb2ac758ee4d4ccf58843eac66" - integrity sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug== +xml2js@0.1.x, xml2js@0.4.19, xml2js@0.5.0, xml2js@0.6.2, xml2js@^0.4.19, xml2js@^0.4.5: + version "0.6.2" + resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.6.2.tgz#dd0b630083aa09c161e25a4d0901e2b2a929b499" + integrity sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA== dependencies: sax ">=0.6.0" xmlbuilder "~11.0.0" @@ -22712,11 +22602,6 @@ xmlbuilder@~11.0.0: resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz#be9bae1c8a046e76b31127726347d0ad7002beb3" integrity sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA== -xmlbuilder@~9.0.1: - version "9.0.7" - resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-9.0.7.tgz#132ee63d2ec5565c557e20f4c22df9aca686b10d" - integrity sha512-7YXTQc3P2l9+0rjaUbLwMKRhtmwg1M1eDf6nag7urC7pIPYLD9W/jmzQ4ptRSUbodw5S0jfoGTflLemQibSpeQ== - xmlchars@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" From 158964c4d2b4a7f0f6139932d2f7aade56e52f54 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Tue, 26 Mar 2024 11:22:40 +0000 Subject: [PATCH 06/97] Got container reuse strategy in place, need to convert tests now. --- .../src/integration-test/postgres.spec.ts | 55 ++++++++++--------- .../src/integrations/tests/utils/index.ts | 53 ++++++++++++++---- .../src/integrations/tests/utils/mariadb.ts | 22 ++------ .../src/integrations/tests/utils/mongodb.ts | 23 ++------ .../src/integrations/tests/utils/mssql.ts | 22 ++------ .../src/integrations/tests/utils/mysql.ts | 23 ++------ .../src/integrations/tests/utils/postgres.ts | 23 ++------ 7 files changed, 98 insertions(+), 123 deletions(-) diff --git a/packages/server/src/integration-test/postgres.spec.ts b/packages/server/src/integration-test/postgres.spec.ts index 107c4ade1e..57da02b507 100644 --- a/packages/server/src/integration-test/postgres.spec.ts +++ b/packages/server/src/integration-test/postgres.spec.ts @@ -16,7 +16,7 @@ import { import _ from "lodash" import { generator } from "@budibase/backend-core/tests" import { utils } from "@budibase/backend-core" -import { databaseTestProviders } from "../integrations/tests/utils" +import { DatabaseName, getDatasource } from "../integrations/tests/utils" import { Client } from "pg" // @ts-ignore fetch.mockSearch() @@ -41,18 +41,17 @@ describe("postgres integrations", () => { makeRequest = generateMakeRequest(apiKey, true) postgresDatasource = await config.api.datasource.create( - await databaseTestProviders.postgres.datasource() + await getDatasource(DatabaseName.POSTGRES) ) }) - afterAll(async () => { - await databaseTestProviders.postgres.stop() - }) - beforeEach(async () => { async function createAuxTable(prefix: string) { return await config.createTable({ - name: `${prefix}_${generator.word({ length: 6 })}`, + name: `${prefix}_${generator + .guid() + .replaceAll("-", "") + .substring(0, 6)}`, type: "table", primary: ["id"], primaryDisplay: "title", @@ -89,7 +88,7 @@ describe("postgres integrations", () => { } primaryPostgresTable = await config.createTable({ - name: `p_${generator.word({ length: 6 })}`, + name: `p_${generator.guid().replaceAll("-", "").substring(0, 6)}`, type: "table", primary: ["id"], schema: { @@ -251,7 +250,7 @@ describe("postgres integrations", () => { async function createDefaultPgTable() { return await config.createTable({ - name: generator.word({ length: 10 }), + name: generator.guid().replaceAll("-", "").substring(0, 10), type: "table", primary: ["id"], schema: { @@ -1043,7 +1042,7 @@ describe("postgres integrations", () => { it("should be able to verify the connection", async () => { await config.api.datasource.verify( { - datasource: await databaseTestProviders.postgres.datasource(), + datasource: await getDatasource(DatabaseName.POSTGRES), }, { body: { @@ -1054,7 +1053,7 @@ describe("postgres integrations", () => { }) it("should state an invalid datasource cannot connect", async () => { - const dbConfig = await databaseTestProviders.postgres.datasource() + const dbConfig = await getDatasource(DatabaseName.POSTGRES) await config.api.datasource.verify( { datasource: { @@ -1089,21 +1088,21 @@ describe("postgres integrations", () => { describe("POST /api/datasources/:datasourceId/schema", () => { let client: Client + let tableName: string beforeEach(async () => { - client = new Client( - (await databaseTestProviders.postgres.datasource()).config! - ) + tableName = generator.guid().replaceAll("-", "").substring(0, 10) + client = new Client((await getDatasource(DatabaseName.POSTGRES)).config!) await client.connect() }) afterEach(async () => { - await client.query(`DROP TABLE IF EXISTS "table"`) + await client.query(`DROP TABLE IF EXISTS "${tableName}"`) await client.end() }) it("recognises when a table has no primary key", async () => { - await client.query(`CREATE TABLE "table" (id SERIAL)`) + await client.query(`CREATE TABLE "${tableName}" (id SERIAL)`) const response = await makeRequest( "post", @@ -1111,12 +1110,14 @@ describe("postgres integrations", () => { ) expect(response.body.errors).toEqual({ - table: "Table must have a primary key.", + [tableName]: "Table must have a primary key.", }) }) it("recognises when a table is using a reserved column name", async () => { - await client.query(`CREATE TABLE "table" (_id SERIAL PRIMARY KEY) `) + await client.query( + `CREATE TABLE "${tableName}" (_id SERIAL PRIMARY KEY) ` + ) const response = await makeRequest( "post", @@ -1124,18 +1125,22 @@ describe("postgres integrations", () => { ) expect(response.body.errors).toEqual({ - table: "Table contains invalid columns.", + [tableName]: "Table contains invalid columns.", }) }) }) describe("Integration compatibility with postgres search_path", () => { - let client: Client, pathDatasource: Datasource - const schema1 = "test1", - schema2 = "test-2" + let client: Client, + pathDatasource: Datasource, + schema1: string, + schema2: string - beforeAll(async () => { - const dsConfig = await databaseTestProviders.postgres.datasource() + beforeEach(async () => { + schema1 = generator.guid().replaceAll("-", "") + schema2 = generator.guid().replaceAll("-", "") + + const dsConfig = await getDatasource(DatabaseName.POSTGRES) const dbConfig = dsConfig.config! client = new Client(dbConfig) @@ -1153,7 +1158,7 @@ describe("postgres integrations", () => { pathDatasource = await config.api.datasource.create(pathConfig) }) - afterAll(async () => { + afterEach(async () => { await client.query(`DROP SCHEMA "${schema1}" CASCADE;`) await client.query(`DROP SCHEMA "${schema2}" CASCADE;`) await client.end() diff --git a/packages/server/src/integrations/tests/utils/index.ts b/packages/server/src/integrations/tests/utils/index.ts index b2be3df4e0..650a1b414d 100644 --- a/packages/server/src/integrations/tests/utils/index.ts +++ b/packages/server/src/integrations/tests/utils/index.ts @@ -1,19 +1,50 @@ jest.unmock("pg") import { Datasource } from "@budibase/types" -import * as postgres from "./postgres" -import * as mongodb from "./mongodb" -import * as mysql from "./mysql" -import * as mssql from "./mssql" -import * as mariadb from "./mariadb" -import { StartedTestContainer } from "testcontainers" +import { postgres } from "./postgres" +import { mongodb } from "./mongodb" +import { mysql } from "./mysql" +import { mssql } from "./mssql" +import { mariadb } from "./mariadb" -jest.setTimeout(30000) +export type DatasourceProvider = () => Promise -export interface DatabaseProvider { - start(): Promise - stop(): Promise - datasource(): Promise +export enum DatabaseName { + POSTGRES = "postgres", + MONGODB = "mongodb", + MYSQL = "mysql", + SQL_SERVER = "mssql", + MARIADB = "mariadb", +} + +const providers: Record = { + [DatabaseName.POSTGRES]: postgres, + [DatabaseName.MONGODB]: mongodb, + [DatabaseName.MYSQL]: mysql, + [DatabaseName.SQL_SERVER]: mssql, + [DatabaseName.MARIADB]: mariadb, +} + +export function getDatasourceProviders( + ...sourceNames: DatabaseName[] +): Promise[] { + return sourceNames.map(sourceName => providers[sourceName]()) +} + +export function getDatasourceProvider( + sourceName: DatabaseName +): DatasourceProvider { + return providers[sourceName] +} + +export function getDatasource(sourceName: DatabaseName): Promise { + return providers[sourceName]() +} + +export async function getDatasources( + ...sourceNames: DatabaseName[] +): Promise { + return Promise.all(sourceNames.map(sourceName => providers[sourceName]())) } export const databaseTestProviders = { diff --git a/packages/server/src/integrations/tests/utils/mariadb.ts b/packages/server/src/integrations/tests/utils/mariadb.ts index a097e0aaa1..a10c36f9ff 100644 --- a/packages/server/src/integrations/tests/utils/mariadb.ts +++ b/packages/server/src/integrations/tests/utils/mariadb.ts @@ -1,9 +1,7 @@ import { Datasource, SourceName } from "@budibase/types" -import { GenericContainer, Wait, StartedTestContainer } from "testcontainers" +import { GenericContainer, Wait } from "testcontainers" import { AbstractWaitStrategy } from "testcontainers/build/wait-strategies/wait-strategy" -let container: StartedTestContainer | undefined - class MariaDBWaitStrategy extends AbstractWaitStrategy { async waitUntilReady(container: any, boundPorts: any, startTime?: Date) { // Because MariaDB first starts itself up, runs an init script, then restarts, @@ -21,18 +19,15 @@ class MariaDBWaitStrategy extends AbstractWaitStrategy { } } -export async function start(): Promise { - return await new GenericContainer("mariadb:lts") +export async function mariadb(): Promise { + const container = await new GenericContainer("mariadb:lts") + .withName("budibase-test-mariadb") + .withReuse() .withExposedPorts(3306) .withEnvironment({ MARIADB_ROOT_PASSWORD: "password" }) .withWaitStrategy(new MariaDBWaitStrategy()) .start() -} -export async function datasource(): Promise { - if (!container) { - container = await start() - } const host = container.getHost() const port = container.getMappedPort(3306) @@ -49,10 +44,3 @@ export async function datasource(): Promise { }, } } - -export async function stop() { - if (container) { - await container.stop() - container = undefined - } -} diff --git a/packages/server/src/integrations/tests/utils/mongodb.ts b/packages/server/src/integrations/tests/utils/mongodb.ts index 0baafc6276..ff24bbc62e 100644 --- a/packages/server/src/integrations/tests/utils/mongodb.ts +++ b/packages/server/src/integrations/tests/utils/mongodb.ts @@ -1,10 +1,10 @@ import { Datasource, SourceName } from "@budibase/types" -import { GenericContainer, Wait, StartedTestContainer } from "testcontainers" +import { GenericContainer, Wait } from "testcontainers" -let container: StartedTestContainer | undefined - -export async function start(): Promise { - return await new GenericContainer("mongo:7.0-jammy") +export async function mongodb(): Promise { + const container = await new GenericContainer("mongo:7.0-jammy") + .withName("budibase-test-mongodb") + .withReuse() .withExposedPorts(27017) .withEnvironment({ MONGO_INITDB_ROOT_USERNAME: "mongo", @@ -16,14 +16,10 @@ export async function start(): Promise { ).withStartupTimeout(10000) ) .start() -} -export async function datasource(): Promise { - if (!container) { - container = await start() - } const host = container.getHost() const port = container.getMappedPort(27017) + return { type: "datasource", source: SourceName.MONGODB, @@ -34,10 +30,3 @@ export async function datasource(): Promise { }, } } - -export async function stop() { - if (container) { - await container.stop() - container = undefined - } -} diff --git a/packages/server/src/integrations/tests/utils/mssql.ts b/packages/server/src/integrations/tests/utils/mssql.ts index 6bd4290a90..0f4e290526 100644 --- a/packages/server/src/integrations/tests/utils/mssql.ts +++ b/packages/server/src/integrations/tests/utils/mssql.ts @@ -1,12 +1,12 @@ import { Datasource, SourceName } from "@budibase/types" -import { GenericContainer, Wait, StartedTestContainer } from "testcontainers" +import { GenericContainer, Wait } from "testcontainers" -let container: StartedTestContainer | undefined - -export async function start(): Promise { - return await new GenericContainer( +export async function mssql(): Promise { + const container = await new GenericContainer( "mcr.microsoft.com/mssql/server:2022-latest" ) + .withName("budibase-test-mssql") + .withReuse() .withExposedPorts(1433) .withEnvironment({ ACCEPT_EULA: "Y", @@ -23,12 +23,7 @@ export async function start(): Promise { ) ) .start() -} -export async function datasource(): Promise { - if (!container) { - container = await start() - } const host = container.getHost() const port = container.getMappedPort(1433) @@ -47,10 +42,3 @@ export async function datasource(): Promise { }, } } - -export async function stop() { - if (container) { - await container.stop() - container = undefined - } -} diff --git a/packages/server/src/integrations/tests/utils/mysql.ts b/packages/server/src/integrations/tests/utils/mysql.ts index 5e51478998..665d6f0ecf 100644 --- a/packages/server/src/integrations/tests/utils/mysql.ts +++ b/packages/server/src/integrations/tests/utils/mysql.ts @@ -1,9 +1,7 @@ import { Datasource, SourceName } from "@budibase/types" -import { GenericContainer, Wait, StartedTestContainer } from "testcontainers" +import { GenericContainer, Wait } from "testcontainers" import { AbstractWaitStrategy } from "testcontainers/build/wait-strategies/wait-strategy" -let container: StartedTestContainer | undefined - class MySQLWaitStrategy extends AbstractWaitStrategy { async waitUntilReady(container: any, boundPorts: any, startTime?: Date) { // Because MySQL first starts itself up, runs an init script, then restarts, @@ -24,18 +22,14 @@ class MySQLWaitStrategy extends AbstractWaitStrategy { } } -export async function start(): Promise { - return await new GenericContainer("mysql:8.3") +export async function mysql(): Promise { + const container = await new GenericContainer("mysql:8.3") + .withName("budibase-test-mysql") + .withReuse() .withExposedPorts(3306) .withEnvironment({ MYSQL_ROOT_PASSWORD: "password" }) .withWaitStrategy(new MySQLWaitStrategy().withStartupTimeout(10000)) .start() -} - -export async function datasource(): Promise { - if (!container) { - container = await start() - } const host = container.getHost() const port = container.getMappedPort(3306) @@ -52,10 +46,3 @@ export async function datasource(): Promise { }, } } - -export async function stop() { - if (container) { - await container.stop() - container = undefined - } -} diff --git a/packages/server/src/integrations/tests/utils/postgres.ts b/packages/server/src/integrations/tests/utils/postgres.ts index 82a62e3916..896c7ea3e0 100644 --- a/packages/server/src/integrations/tests/utils/postgres.ts +++ b/packages/server/src/integrations/tests/utils/postgres.ts @@ -1,10 +1,10 @@ import { Datasource, SourceName } from "@budibase/types" -import { GenericContainer, Wait, StartedTestContainer } from "testcontainers" +import { GenericContainer, Wait } from "testcontainers" -let container: StartedTestContainer | undefined - -export async function start(): Promise { - return await new GenericContainer("postgres:16.1-bullseye") +export async function postgres(): Promise { + const container = await new GenericContainer("postgres:16.1-bullseye") + .withName("budibase-test-postgres") + .withReuse() .withExposedPorts(5432) .withEnvironment({ POSTGRES_PASSWORD: "password" }) .withWaitStrategy( @@ -13,12 +13,6 @@ export async function start(): Promise { ).withStartupTimeout(10000) ) .start() -} - -export async function datasource(): Promise { - if (!container) { - container = await start() - } const host = container.getHost() const port = container.getMappedPort(5432) @@ -39,10 +33,3 @@ export async function datasource(): Promise { }, } } - -export async function stop() { - if (container) { - await container.stop() - container = undefined - } -} From 5530d7f4b63b717c7879a6bfaf70387d35844aa8 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Tue, 26 Mar 2024 14:05:58 +0000 Subject: [PATCH 07/97] Migrate mongodb.spec.ts to new datasource providers. --- .../routes/tests/queries/generic-sql.spec.ts | 28 ++++++++++--------- .../api/routes/tests/queries/mongodb.spec.ts | 17 ++++++----- 2 files changed, 25 insertions(+), 20 deletions(-) diff --git a/packages/server/src/api/routes/tests/queries/generic-sql.spec.ts b/packages/server/src/api/routes/tests/queries/generic-sql.spec.ts index f9a3ac6e03..d393430060 100644 --- a/packages/server/src/api/routes/tests/queries/generic-sql.spec.ts +++ b/packages/server/src/api/routes/tests/queries/generic-sql.spec.ts @@ -1,6 +1,9 @@ import { Datasource, Query, SourceName } from "@budibase/types" import * as setup from "../utilities" -import { databaseTestProviders } from "../../../../integrations/tests/utils" +import { + DatabaseName, + getDatasource, +} from "../../../../integrations/tests/utils" import pg from "pg" import mysql from "mysql2/promise" import mssql from "mssql" @@ -34,12 +37,14 @@ const createTableSQL: Record = { const insertSQL = `INSERT INTO test_table (name) VALUES ('one'), ('two'), ('three'), ('four'), ('five')` const dropTableSQL = `DROP TABLE test_table;` -describe.each([ - ["postgres", databaseTestProviders.postgres], - ["mysql", databaseTestProviders.mysql], - ["mssql", databaseTestProviders.mssql], - ["mariadb", databaseTestProviders.mariadb], -])("queries (%s)", (dbName, dsProvider) => { +describe.each( + [ + DatabaseName.POSTGRES, + DatabaseName.MYSQL, + DatabaseName.SQL_SERVER, + DatabaseName.MARIADB, + ].map(name => [name, getDatasource(name)]) +)("queries (%s)", (dbName, dsProvider) => { const config = setup.getConfig() let datasource: Datasource @@ -61,7 +66,7 @@ describe.each([ // We re-fetch the datasource here because the one returned by // config.api.datasource.create has the password field blanked out, and we // need the password to connect to the database. - const ds = await dsProvider.datasource() + const ds = await dsProvider switch (ds.source) { case SourceName.POSTGRES: { const client = new pg.Client(ds.config!) @@ -97,9 +102,7 @@ describe.each([ beforeAll(async () => { await config.init() - datasource = await config.api.datasource.create( - await dsProvider.datasource() - ) + datasource = await config.api.datasource.create(await dsProvider) }) beforeEach(async () => { @@ -112,7 +115,6 @@ describe.each([ }) afterAll(async () => { - await dsProvider.stop() setup.afterAll() }) @@ -443,7 +445,7 @@ describe.each([ } catch (err: any) { error = err.message } - if (dbName === "mssql") { + if (dbName === DatabaseName.SQL_SERVER) { expect(error).toBeUndefined() } else { expect(error).toBeDefined() diff --git a/packages/server/src/api/routes/tests/queries/mongodb.spec.ts b/packages/server/src/api/routes/tests/queries/mongodb.spec.ts index 492f24abf9..148f2c15ec 100644 --- a/packages/server/src/api/routes/tests/queries/mongodb.spec.ts +++ b/packages/server/src/api/routes/tests/queries/mongodb.spec.ts @@ -1,14 +1,17 @@ import { Datasource, Query } from "@budibase/types" import * as setup from "../utilities" -import { databaseTestProviders } from "../../../../integrations/tests/utils" +import { + DatabaseName, + getDatasource, +} from "../../../../integrations/tests/utils" import { MongoClient, type Collection, BSON } from "mongodb" - -const collection = "test_collection" +import { generator } from "@budibase/backend-core/tests" const expectValidId = expect.stringMatching(/^\w{24}$/) const expectValidBsonObjectId = expect.any(BSON.ObjectId) describe("/queries", () => { + let collection: string let config = setup.getConfig() let datasource: Datasource @@ -37,7 +40,7 @@ describe("/queries", () => { async function withClient( callback: (client: MongoClient) => Promise ): Promise { - const ds = await databaseTestProviders.mongodb.datasource() + const ds = await getDatasource(DatabaseName.MONGODB) const client = new MongoClient(ds.config!.connectionString) await client.connect() try { @@ -52,25 +55,25 @@ describe("/queries", () => { ): Promise { return await withClient(async client => { const db = client.db( - (await databaseTestProviders.mongodb.datasource()).config!.db + (await getDatasource(DatabaseName.MONGODB)).config!.db ) return await callback(db.collection(collection)) }) } afterAll(async () => { - await databaseTestProviders.mongodb.stop() setup.afterAll() }) beforeAll(async () => { await config.init() datasource = await config.api.datasource.create( - await databaseTestProviders.mongodb.datasource() + await getDatasource(DatabaseName.MONGODB) ) }) beforeEach(async () => { + collection = generator.guid() await withCollection(async collection => { await collection.insertMany([ { name: "one" }, From 2304aeaa719937130fbdd07cee6580414b2f72af Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Tue, 26 Mar 2024 14:36:18 +0000 Subject: [PATCH 08/97] Migrate mysql.spec.ts to new datasource providers. --- .../server/src/api/routes/tests/row.spec.ts | 39 +++++++++---------- .../server/src/integration-test/mysql.spec.ts | 30 +++++++------- 2 files changed, 32 insertions(+), 37 deletions(-) diff --git a/packages/server/src/api/routes/tests/row.spec.ts b/packages/server/src/api/routes/tests/row.spec.ts index f638f2c4bf..8910522565 100644 --- a/packages/server/src/api/routes/tests/row.spec.ts +++ b/packages/server/src/api/routes/tests/row.spec.ts @@ -1,4 +1,4 @@ -import { databaseTestProviders } from "../../../integrations/tests/utils" +import { DatabaseName, getDatasource } from "../../../integrations/tests/utils" import tk from "timekeeper" import { outputProcessing } from "../../../utilities/rowProcessor" @@ -34,10 +34,10 @@ jest.unmock("pg") describe.each([ ["internal", undefined], - ["postgres", databaseTestProviders.postgres], - ["mysql", databaseTestProviders.mysql], - ["mssql", databaseTestProviders.mssql], - ["mariadb", databaseTestProviders.mariadb], + [DatabaseName.POSTGRES, getDatasource(DatabaseName.POSTGRES)], + [DatabaseName.MYSQL, getDatasource(DatabaseName.MYSQL)], + [DatabaseName.SQL_SERVER, getDatasource(DatabaseName.SQL_SERVER)], + [DatabaseName.MARIADB, getDatasource(DatabaseName.MARIADB)], ])("/rows (%s)", (__, dsProvider) => { const isInternal = dsProvider === undefined const config = setup.getConfig() @@ -49,23 +49,23 @@ describe.each([ await config.init() if (dsProvider) { datasource = await config.createDatasource({ - datasource: await dsProvider.datasource(), + datasource: await dsProvider, }) } }) afterAll(async () => { - if (dsProvider) { - await dsProvider.stop() - } setup.afterAll() }) function saveTableRequest( - ...overrides: Partial[] + // We omit the name field here because it's generated in the function with a + // high likelihood to be unique. Tests should not have any reason to control + // the table name they're writing to. + ...overrides: Partial>[] ): SaveTableRequest { const req: SaveTableRequest = { - name: uuid.v4().substring(0, 16), + name: uuid.v4().substring(0, 10), type: "table", sourceType: datasource ? TableSourceType.EXTERNAL @@ -87,7 +87,10 @@ describe.each([ } function defaultTable( - ...overrides: Partial[] + // We omit the name field here because it's generated in the function with a + // high likelihood to be unique. Tests should not have any reason to control + // the table name they're writing to. + ...overrides: Partial>[] ): SaveTableRequest { return saveTableRequest( { @@ -194,7 +197,6 @@ describe.each([ const newTable = await config.api.table.save( saveTableRequest({ - name: "TestTableAuto", schema: { "Row ID": { name: "Row ID", @@ -383,11 +385,9 @@ describe.each([ isInternal && it("doesn't allow creating in user table", async () => { - const userTableId = InternalTable.USER_METADATA const response = await config.api.row.save( - userTableId, + InternalTable.USER_METADATA, { - tableId: userTableId, firstName: "Joe", lastName: "Joe", email: "joe@joe.com", @@ -462,7 +462,6 @@ describe.each([ table = await config.api.table.save(defaultTable()) otherTable = await config.api.table.save( defaultTable({ - name: "a", schema: { relationship: { name: "relationship", @@ -898,8 +897,8 @@ describe.each([ let o2mTable: Table let m2mTable: Table beforeAll(async () => { - o2mTable = await config.api.table.save(defaultTable({ name: "o2m" })) - m2mTable = await config.api.table.save(defaultTable({ name: "m2m" })) + o2mTable = await config.api.table.save(defaultTable()) + m2mTable = await config.api.table.save(defaultTable()) }) describe.each([ @@ -1256,7 +1255,6 @@ describe.each([ otherTable = await config.api.table.save(defaultTable()) table = await config.api.table.save( saveTableRequest({ - name: "b", schema: { links: { name: "links", @@ -1354,7 +1352,6 @@ describe.each([ const table = await config.api.table.save( saveTableRequest({ - name: "table", schema: { text: { name: "text", diff --git a/packages/server/src/integration-test/mysql.spec.ts b/packages/server/src/integration-test/mysql.spec.ts index 92420fb336..65fbe2949d 100644 --- a/packages/server/src/integration-test/mysql.spec.ts +++ b/packages/server/src/integration-test/mysql.spec.ts @@ -3,7 +3,6 @@ import { generateMakeRequest, MakeRequestResponse, } from "../api/routes/public/tests/utils" -import { v4 as uuidv4 } from "uuid" import * as setup from "../api/routes/tests/utilities" import { Datasource, @@ -12,9 +11,10 @@ import { TableRequest, TableSourceType, } from "@budibase/types" -import { databaseTestProviders } from "../integrations/tests/utils" +import { DatabaseName, getDatasource } from "../integrations/tests/utils" import mysql from "mysql2/promise" import { builderSocket } from "../websockets" +import { generator } from "@budibase/backend-core/tests" // @ts-ignore fetch.mockSearch() @@ -47,17 +47,13 @@ describe("mysql integrations", () => { makeRequest = generateMakeRequest(apiKey, true) mysqlDatasource = await config.api.datasource.create( - await databaseTestProviders.mysql.datasource() + await getDatasource(DatabaseName.MYSQL) ) }) - afterAll(async () => { - await databaseTestProviders.mysql.stop() - }) - beforeEach(async () => { primaryMySqlTable = await config.createTable({ - name: uuidv4(), + name: generator.guid().replaceAll("-", "_").substring(0, 10), type: "table", primary: ["id"], schema: { @@ -117,7 +113,7 @@ describe("mysql integrations", () => { it("should be able to verify the connection", async () => { await config.api.datasource.verify( { - datasource: await databaseTestProviders.mysql.datasource(), + datasource: await getDatasource(DatabaseName.MYSQL), }, { body: { @@ -128,7 +124,7 @@ describe("mysql integrations", () => { }) it("should state an invalid datasource cannot connect", async () => { - const dbConfig = await databaseTestProviders.mysql.datasource() + const dbConfig = await getDatasource(DatabaseName.MYSQL) await config.api.datasource.verify( { datasource: { @@ -168,7 +164,7 @@ describe("mysql integrations", () => { const database2 = "test-2" beforeAll(async () => { - const dsConfig = await databaseTestProviders.mysql.datasource() + const dsConfig = await getDatasource(DatabaseName.MYSQL) const dbConfig = dsConfig.config! client = await mysql.createConnection(dbConfig) @@ -237,11 +233,11 @@ describe("mysql integrations", () => { beforeEach(async () => { client = await mysql.createConnection( ( - await databaseTestProviders.mysql.datasource() + await getDatasource(DatabaseName.MYSQL) ).config! ) mysqlDatasource = await config.api.datasource.create( - await databaseTestProviders.mysql.datasource() + await getDatasource(DatabaseName.MYSQL) ) }) @@ -253,7 +249,7 @@ describe("mysql integrations", () => { const addColumnToTable: TableRequest = { type: "table", sourceType: TableSourceType.EXTERNAL, - name: "table", + name: generator.guid().replaceAll("-", "_").substring(0, 10), sourceId: mysqlDatasource._id!, primary: ["id"], schema: { @@ -301,14 +297,16 @@ describe("mysql integrations", () => { }, }, created: true, - _id: `${mysqlDatasource._id}__table`, + _id: `${mysqlDatasource._id}__${addColumnToTable.name}`, } delete expectedTable._add expect(emitDatasourceUpdateMock).toHaveBeenCalledTimes(1) const emittedDatasource: Datasource = emitDatasourceUpdateMock.mock.calls[0][1] - expect(emittedDatasource.entities!["table"]).toEqual(expectedTable) + expect(emittedDatasource.entities![expectedTable.name]).toEqual( + expectedTable + ) }) it("will rename a column", async () => { From b84bbd6003bf1317a5fe4e6430c56c30ad4dfead Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Tue, 26 Mar 2024 14:43:17 +0000 Subject: [PATCH 09/97] Migrate viewV2.spec.ts to new datasource providers. --- .../src/api/routes/tests/viewV2.spec.ts | 24 +++++++------------ .../src/integrations/tests/utils/index.ts | 8 ------- 2 files changed, 9 insertions(+), 23 deletions(-) diff --git a/packages/server/src/api/routes/tests/viewV2.spec.ts b/packages/server/src/api/routes/tests/viewV2.spec.ts index f9d213a26b..ce959ac429 100644 --- a/packages/server/src/api/routes/tests/viewV2.spec.ts +++ b/packages/server/src/api/routes/tests/viewV2.spec.ts @@ -19,8 +19,7 @@ import { ViewV2, } from "@budibase/types" import { generator, mocks } from "@budibase/backend-core/tests" -import * as uuid from "uuid" -import { databaseTestProviders } from "../../../integrations/tests/utils" +import { DatabaseName, getDatasource } from "../../../integrations/tests/utils" import merge from "lodash/merge" import { quotas } from "@budibase/pro" import { roles } from "@budibase/backend-core" @@ -30,10 +29,10 @@ jest.unmock("pg") describe.each([ ["internal", undefined], - ["postgres", databaseTestProviders.postgres], - ["mysql", databaseTestProviders.mysql], - ["mssql", databaseTestProviders.mssql], - ["mariadb", databaseTestProviders.mariadb], + [DatabaseName.POSTGRES, getDatasource(DatabaseName.POSTGRES)], + [DatabaseName.MYSQL, getDatasource(DatabaseName.MYSQL)], + [DatabaseName.SQL_SERVER, getDatasource(DatabaseName.SQL_SERVER)], + [DatabaseName.MARIADB, getDatasource(DatabaseName.MARIADB)], ])("/v2/views (%s)", (_, dsProvider) => { const config = setup.getConfig() const isInternal = !dsProvider @@ -42,10 +41,10 @@ describe.each([ let datasource: Datasource function saveTableRequest( - ...overrides: Partial[] + ...overrides: Partial>[] ): SaveTableRequest { const req: SaveTableRequest = { - name: uuid.v4().substring(0, 16), + name: generator.guid().replaceAll("-", "").substring(0, 16), type: "table", sourceType: datasource ? TableSourceType.EXTERNAL @@ -90,16 +89,13 @@ describe.each([ if (dsProvider) { datasource = await config.createDatasource({ - datasource: await dsProvider.datasource(), + datasource: await dsProvider, }) } table = await config.api.table.save(priceTable()) }) afterAll(async () => { - if (dsProvider) { - await dsProvider.stop() - } setup.afterAll() }) @@ -507,7 +503,6 @@ describe.each([ it("views have extra data trimmed", async () => { const table = await config.api.table.save( saveTableRequest({ - name: "orders", schema: { Country: { type: FieldType.STRING, @@ -523,7 +518,7 @@ describe.each([ const view = await config.api.viewV2.create({ tableId: table._id!, - name: uuid.v4(), + name: generator.guid(), schema: { Country: { visible: true, @@ -853,7 +848,6 @@ describe.each([ beforeAll(async () => { table = await config.api.table.save( saveTableRequest({ - name: `users_${uuid.v4()}`, type: "table", schema: { name: { diff --git a/packages/server/src/integrations/tests/utils/index.ts b/packages/server/src/integrations/tests/utils/index.ts index 650a1b414d..57aae02865 100644 --- a/packages/server/src/integrations/tests/utils/index.ts +++ b/packages/server/src/integrations/tests/utils/index.ts @@ -46,11 +46,3 @@ export async function getDatasources( ): Promise { return Promise.all(sourceNames.map(sourceName => providers[sourceName]())) } - -export const databaseTestProviders = { - postgres, - mongodb, - mysql, - mssql, - mariadb, -} From 1eae212f8392ef102690447c025b682bc64c803a Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Tue, 26 Mar 2024 15:41:51 +0000 Subject: [PATCH 10/97] Stop relying on config.request and create a supertest instance per request. --- globalSetup.ts | 2 ++ .../src/api/routes/tests/viewV2.spec.ts | 7 +++--- .../server/src/tests/utilities/api/base.ts | 22 +++++++++++-------- 3 files changed, 19 insertions(+), 12 deletions(-) diff --git a/globalSetup.ts b/globalSetup.ts index 4cb542a3c3..66d3f5fd8c 100644 --- a/globalSetup.ts +++ b/globalSetup.ts @@ -2,6 +2,8 @@ import { GenericContainer, Wait } from "testcontainers" export default async function setup() { await new GenericContainer("budibase/couchdb") + .withName("budibase-test-couchdb") + .withReuse() .withExposedPorts(5984) .withEnvironment({ COUCHDB_PASSWORD: "budibase", diff --git a/packages/server/src/api/routes/tests/viewV2.spec.ts b/packages/server/src/api/routes/tests/viewV2.spec.ts index ce959ac429..d3e38b0f23 100644 --- a/packages/server/src/api/routes/tests/viewV2.spec.ts +++ b/packages/server/src/api/routes/tests/viewV2.spec.ts @@ -227,7 +227,7 @@ describe.each([ view = await config.api.viewV2.create({ tableId: table._id!, - name: "View A", + name: generator.guid(), }) }) @@ -303,12 +303,13 @@ describe.each([ it("can update an existing view name", async () => { const tableId = table._id! - await config.api.viewV2.update({ ...view, name: "View B" }) + const newName = generator.guid() + await config.api.viewV2.update({ ...view, name: newName }) expect(await config.api.table.get(tableId)).toEqual( expect.objectContaining({ views: { - "View B": { ...view, name: "View B", schema: expect.anything() }, + [newName]: { ...view, name: newName, schema: expect.anything() }, }, }) ) diff --git a/packages/server/src/tests/utilities/api/base.ts b/packages/server/src/tests/utilities/api/base.ts index 4df58ff425..e4094b8126 100644 --- a/packages/server/src/tests/utilities/api/base.ts +++ b/packages/server/src/tests/utilities/api/base.ts @@ -1,6 +1,7 @@ import TestConfiguration from "../TestConfiguration" -import { SuperTest, Test, Response } from "supertest" +import request, { SuperTest, Test, Response } from "supertest" import { ReadStream } from "fs" +import { getServer } from "../../../app" type Headers = Record type Method = "get" | "post" | "put" | "patch" | "delete" @@ -107,26 +108,29 @@ export abstract class TestAPI { const headersFn = publicUser ? this.config.publicHeaders.bind(this.config) : this.config.defaultHeaders.bind(this.config) - let request = this.request[method](url).set( + + const app = getServer() + let req = request(app)[method](url) + req = req.set( headersFn({ "x-budibase-include-stacktrace": "true", }) ) if (headers) { - request = request.set(headers) + req = req.set(headers) } if (body) { - request = request.send(body) + req = req.send(body) } for (const [key, value] of Object.entries(fields)) { - request = request.field(key, value) + req = req.field(key, value) } for (const [key, value] of Object.entries(files)) { if (isAttachedFile(value)) { - request = request.attach(key, value.file, value.name) + req = req.attach(key, value.file, value.name) } else { - request = request.attach(key, value as any) + req = req.attach(key, value as any) } } if (expectations?.headers) { @@ -136,11 +140,11 @@ export abstract class TestAPI { `Got an undefined expected value for header "${key}", if you want to check for the absence of a header, use headersNotPresent` ) } - request = request.expect(key, value as any) + req = req.expect(key, value as any) } } - return await request + return await req } protected _checkResponse = ( From f1609e6763c0ff603dfe164b0b7e5e45a0549a4f Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Tue, 26 Mar 2024 17:25:24 +0000 Subject: [PATCH 11/97] Retry socket hangups. --- .../server/src/tests/utilities/api/base.ts | 32 +++++++++++++++++-- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/packages/server/src/tests/utilities/api/base.ts b/packages/server/src/tests/utilities/api/base.ts index e4094b8126..3a5f6529f8 100644 --- a/packages/server/src/tests/utilities/api/base.ts +++ b/packages/server/src/tests/utilities/api/base.ts @@ -77,7 +77,8 @@ export abstract class TestAPI { protected _requestRaw = async ( method: "get" | "post" | "put" | "patch" | "delete", url: string, - opts?: RequestOpts + opts?: RequestOpts, + attempt = 0 ): Promise => { const { headers = {}, @@ -144,7 +145,21 @@ export abstract class TestAPI { } } - return await req + try { + return await req + } catch (e: any) { + // We've found that occasionally the connection between supertest and the + // server supertest starts gets reset. Not sure why, but retrying it + // appears to work. I don't particularly like this, but it's better than + // flakiness. + if (e.code === "ECONNRESET") { + if (attempt > 2) { + throw e + } + return await this._requestRaw(method, url, opts, attempt + 1) + } + throw e + } } protected _checkResponse = ( @@ -174,7 +189,18 @@ export abstract class TestAPI { } } - throw new Error(message) + if (response.error) { + // Sometimes the error can be between supertest and the app, and when + // that happens response.error is sometimes populated with `text` that + // gives more detail about the error. The `message` is almost always + // useless from what I've seen. + if (response.error.text) { + response.error.message = response.error.text + } + throw new Error(message, { cause: response.error }) + } else { + throw new Error(message) + } } if (expectations?.headersNotPresent) { From 831c1743625f6c416808109518c1611033c10fe0 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Wed, 27 Mar 2024 15:25:37 +0000 Subject: [PATCH 12/97] Give SQL integrations their own database when fetching a new datasource. --- .../core/utilities/structures/generator.ts | 1 + packages/server/scripts/test.sh | 2 + .../routes/tests/queries/generic-sql.spec.ts | 66 ++++++------------- .../server/src/integration-test/mysql.spec.ts | 11 +++- .../src/integrations/tests/utils/index.ts | 39 +++++++---- .../src/integrations/tests/utils/mariadb.ts | 27 +++++--- .../src/integrations/tests/utils/mongodb.ts | 2 +- .../src/integrations/tests/utils/mssql.ts | 30 ++++++++- .../src/integrations/tests/utils/mysql.ts | 28 +++++++- .../src/integrations/tests/utils/postgres.ts | 30 ++++++++- 10 files changed, 162 insertions(+), 74 deletions(-) diff --git a/packages/backend-core/tests/core/utilities/structures/generator.ts b/packages/backend-core/tests/core/utilities/structures/generator.ts index 64eb5ecc97..2a7eba6bbe 100644 --- a/packages/backend-core/tests/core/utilities/structures/generator.ts +++ b/packages/backend-core/tests/core/utilities/structures/generator.ts @@ -1,3 +1,4 @@ import Chance from "./Chance" export const generator = new Chance() + diff --git a/packages/server/scripts/test.sh b/packages/server/scripts/test.sh index 3ecf8bb794..c9f063c409 100644 --- a/packages/server/scripts/test.sh +++ b/packages/server/scripts/test.sh @@ -1,6 +1,8 @@ #!/bin/bash set -e +export DEBUG=testcontainers* + if [[ -n $CI ]] then export NODE_OPTIONS="--max-old-space-size=4096 --no-node-snapshot $NODE_OPTIONS" diff --git a/packages/server/src/api/routes/tests/queries/generic-sql.spec.ts b/packages/server/src/api/routes/tests/queries/generic-sql.spec.ts index d393430060..ff77d3dc52 100644 --- a/packages/server/src/api/routes/tests/queries/generic-sql.spec.ts +++ b/packages/server/src/api/routes/tests/queries/generic-sql.spec.ts @@ -3,6 +3,7 @@ import * as setup from "../utilities" import { DatabaseName, getDatasource, + rawQuery, } from "../../../../integrations/tests/utils" import pg from "pg" import mysql from "mysql2/promise" @@ -46,6 +47,7 @@ describe.each( ].map(name => [name, getDatasource(name)]) )("queries (%s)", (dbName, dsProvider) => { const config = setup.getConfig() + let rawDatasource: Datasource let datasource: Datasource async function createQuery(query: Partial): Promise { @@ -62,56 +64,19 @@ describe.each( return await config.api.query.save({ ...defaultQuery, ...query }) } - async function rawQuery(sql: string): Promise { - // We re-fetch the datasource here because the one returned by - // config.api.datasource.create has the password field blanked out, and we - // need the password to connect to the database. - const ds = await dsProvider - switch (ds.source) { - case SourceName.POSTGRES: { - const client = new pg.Client(ds.config!) - await client.connect() - try { - const { rows } = await client.query(sql) - return rows - } finally { - await client.end() - } - } - case SourceName.MYSQL: { - const con = await mysql.createConnection(ds.config!) - try { - const [rows] = await con.query(sql) - return rows - } finally { - con.end() - } - } - case SourceName.SQL_SERVER: { - const pool = new mssql.ConnectionPool(ds.config! as mssql.config) - const client = await pool.connect() - try { - const { recordset } = await client.query(sql) - return recordset - } finally { - await pool.close() - } - } - } - } - beforeAll(async () => { await config.init() - datasource = await config.api.datasource.create(await dsProvider) + rawDatasource = await dsProvider + datasource = await config.api.datasource.create(rawDatasource) }) beforeEach(async () => { - await rawQuery(createTableSQL[datasource.source]) - await rawQuery(insertSQL) + await rawQuery(rawDatasource, createTableSQL[datasource.source]) + await rawQuery(rawDatasource, insertSQL) }) afterEach(async () => { - await rawQuery(dropTableSQL) + await rawQuery(rawDatasource, dropTableSQL) }) afterAll(async () => { @@ -145,7 +110,10 @@ describe.each( }, ]) - const rows = await rawQuery("SELECT * FROM test_table WHERE name = 'baz'") + const rows = await rawQuery( + rawDatasource, + "SELECT * FROM test_table WHERE name = 'baz'" + ) expect(rows).toHaveLength(1) }) @@ -173,6 +141,7 @@ describe.each( expect(result.data).toEqual([{ created: true }]) const rows = await rawQuery( + rawDatasource, `SELECT * FROM test_table WHERE birthday = '${date.toISOString()}'` ) expect(rows).toHaveLength(1) @@ -204,6 +173,7 @@ describe.each( expect(result.data).toEqual([{ created: true }]) const rows = await rawQuery( + rawDatasource, `SELECT * FROM test_table WHERE name = '${notDateStr}'` ) expect(rows).toHaveLength(1) @@ -340,7 +310,10 @@ describe.each( }, ]) - const rows = await rawQuery("SELECT * FROM test_table WHERE id = 1") + const rows = await rawQuery( + rawDatasource, + "SELECT * FROM test_table WHERE id = 1" + ) expect(rows).toEqual([ { id: 1, name: "foo", birthday: null, number: null }, ]) @@ -408,7 +381,10 @@ describe.each( }, ]) - const rows = await rawQuery("SELECT * FROM test_table WHERE id = 1") + const rows = await rawQuery( + rawDatasource, + "SELECT * FROM test_table WHERE id = 1" + ) expect(rows).toHaveLength(0) }) }) diff --git a/packages/server/src/integration-test/mysql.spec.ts b/packages/server/src/integration-test/mysql.spec.ts index 65fbe2949d..fb2d3c5285 100644 --- a/packages/server/src/integration-test/mysql.spec.ts +++ b/packages/server/src/integration-test/mysql.spec.ts @@ -18,6 +18,13 @@ import { generator } from "@budibase/backend-core/tests" // @ts-ignore fetch.mockSearch() +function uniqueTableName(length?: number): string { + return generator + .guid() + .replaceAll("-", "_") + .substring(0, length || 10) +} + const config = setup.getConfig()! jest.mock("../websockets", () => ({ @@ -53,7 +60,7 @@ describe("mysql integrations", () => { beforeEach(async () => { primaryMySqlTable = await config.createTable({ - name: generator.guid().replaceAll("-", "_").substring(0, 10), + name: uniqueTableName(), type: "table", primary: ["id"], schema: { @@ -249,7 +256,7 @@ describe("mysql integrations", () => { const addColumnToTable: TableRequest = { type: "table", sourceType: TableSourceType.EXTERNAL, - name: generator.guid().replaceAll("-", "_").substring(0, 10), + name: uniqueTableName(), sourceId: mysqlDatasource._id!, primary: ["id"], schema: { diff --git a/packages/server/src/integrations/tests/utils/index.ts b/packages/server/src/integrations/tests/utils/index.ts index 57aae02865..5760273d51 100644 --- a/packages/server/src/integrations/tests/utils/index.ts +++ b/packages/server/src/integrations/tests/utils/index.ts @@ -1,11 +1,11 @@ jest.unmock("pg") -import { Datasource } from "@budibase/types" -import { postgres } from "./postgres" -import { mongodb } from "./mongodb" -import { mysql } from "./mysql" -import { mssql } from "./mssql" -import { mariadb } from "./mariadb" +import { Datasource, SourceName } from "@budibase/types" +import * as postgres from "./postgres" +import * as mongodb from "./mongodb" +import * as mysql from "./mysql" +import * as mssql from "./mssql" +import * as mariadb from "./mariadb" export type DatasourceProvider = () => Promise @@ -18,11 +18,11 @@ export enum DatabaseName { } const providers: Record = { - [DatabaseName.POSTGRES]: postgres, - [DatabaseName.MONGODB]: mongodb, - [DatabaseName.MYSQL]: mysql, - [DatabaseName.SQL_SERVER]: mssql, - [DatabaseName.MARIADB]: mariadb, + [DatabaseName.POSTGRES]: postgres.getDatasource, + [DatabaseName.MONGODB]: mongodb.getDatasource, + [DatabaseName.MYSQL]: mysql.getDatasource, + [DatabaseName.SQL_SERVER]: mssql.getDatasource, + [DatabaseName.MARIADB]: mariadb.getDatasource, } export function getDatasourceProviders( @@ -46,3 +46,20 @@ export async function getDatasources( ): Promise { return Promise.all(sourceNames.map(sourceName => providers[sourceName]())) } + +export async function rawQuery(ds: Datasource, sql: string): Promise { + switch (ds.source) { + case SourceName.POSTGRES: { + return postgres.rawQuery(ds, sql) + } + case SourceName.MYSQL: { + return mysql.rawQuery(ds, sql) + } + case SourceName.SQL_SERVER: { + return mssql.rawQuery(ds, sql) + } + default: { + throw new Error(`Unsupported source: ${ds.source}`) + } + } +} diff --git a/packages/server/src/integrations/tests/utils/mariadb.ts b/packages/server/src/integrations/tests/utils/mariadb.ts index a10c36f9ff..c8890af1fb 100644 --- a/packages/server/src/integrations/tests/utils/mariadb.ts +++ b/packages/server/src/integrations/tests/utils/mariadb.ts @@ -1,6 +1,8 @@ import { Datasource, SourceName } from "@budibase/types" import { GenericContainer, Wait } from "testcontainers" import { AbstractWaitStrategy } from "testcontainers/build/wait-strategies/wait-strategy" +import { rawQuery } from "./mysql" +import { generator } from "@budibase/backend-core/tests" class MariaDBWaitStrategy extends AbstractWaitStrategy { async waitUntilReady(container: any, boundPorts: any, startTime?: Date) { @@ -19,7 +21,7 @@ class MariaDBWaitStrategy extends AbstractWaitStrategy { } } -export async function mariadb(): Promise { +export async function getDatasource(): Promise { const container = await new GenericContainer("mariadb:lts") .withName("budibase-test-mariadb") .withReuse() @@ -31,16 +33,23 @@ export async function mariadb(): Promise { const host = container.getHost() const port = container.getMappedPort(3306) - return { + const config = { + host, + port, + user: "root", + password: "password", + database: "mysql", + } + + const datasource = { type: "datasource_plus", source: SourceName.MYSQL, plus: true, - config: { - host, - port, - user: "root", - password: "password", - database: "mysql", - }, + config, } + + const database = generator.guid().replaceAll("-", "") + await rawQuery(datasource, `CREATE DATABASE \`${database}\``) + datasource.config.database = database + return datasource } diff --git a/packages/server/src/integrations/tests/utils/mongodb.ts b/packages/server/src/integrations/tests/utils/mongodb.ts index ff24bbc62e..6ab5b11191 100644 --- a/packages/server/src/integrations/tests/utils/mongodb.ts +++ b/packages/server/src/integrations/tests/utils/mongodb.ts @@ -1,7 +1,7 @@ import { Datasource, SourceName } from "@budibase/types" import { GenericContainer, Wait } from "testcontainers" -export async function mongodb(): Promise { +export async function getDatasource(): Promise { const container = await new GenericContainer("mongo:7.0-jammy") .withName("budibase-test-mongodb") .withReuse() diff --git a/packages/server/src/integrations/tests/utils/mssql.ts b/packages/server/src/integrations/tests/utils/mssql.ts index 0f4e290526..c0875b84db 100644 --- a/packages/server/src/integrations/tests/utils/mssql.ts +++ b/packages/server/src/integrations/tests/utils/mssql.ts @@ -1,7 +1,9 @@ import { Datasource, SourceName } from "@budibase/types" import { GenericContainer, Wait } from "testcontainers" +import mssql from "mssql" +import { generator } from "@budibase/backend-core/tests" -export async function mssql(): Promise { +export async function getDatasource(): Promise { const container = await new GenericContainer( "mcr.microsoft.com/mssql/server:2022-latest" ) @@ -27,7 +29,7 @@ export async function mssql(): Promise { const host = container.getHost() const port = container.getMappedPort(1433) - return { + const datasource: Datasource = { type: "datasource_plus", source: SourceName.SQL_SERVER, plus: true, @@ -41,4 +43,28 @@ export async function mssql(): Promise { }, }, } + + const database = generator.guid().replaceAll("-", "") + await rawQuery(datasource, `CREATE DATABASE "${database}"`) + datasource.config!.database = database + + return datasource +} + +export async function rawQuery(ds: Datasource, sql: string) { + if (!ds.config) { + throw new Error("Datasource config is missing") + } + if (ds.source !== SourceName.SQL_SERVER) { + throw new Error("Datasource source is not SQL Server") + } + + const pool = new mssql.ConnectionPool(ds.config! as mssql.config) + const client = await pool.connect() + try { + const { recordset } = await client.query(sql) + return recordset + } finally { + await pool.close() + } } diff --git a/packages/server/src/integrations/tests/utils/mysql.ts b/packages/server/src/integrations/tests/utils/mysql.ts index 665d6f0ecf..9fa8b0bd86 100644 --- a/packages/server/src/integrations/tests/utils/mysql.ts +++ b/packages/server/src/integrations/tests/utils/mysql.ts @@ -1,6 +1,8 @@ import { Datasource, SourceName } from "@budibase/types" import { GenericContainer, Wait } from "testcontainers" import { AbstractWaitStrategy } from "testcontainers/build/wait-strategies/wait-strategy" +import mysql from "mysql2/promise" +import { generator } from "@budibase/backend-core/tests" class MySQLWaitStrategy extends AbstractWaitStrategy { async waitUntilReady(container: any, boundPorts: any, startTime?: Date) { @@ -22,7 +24,7 @@ class MySQLWaitStrategy extends AbstractWaitStrategy { } } -export async function mysql(): Promise { +export async function getDatasource(): Promise { const container = await new GenericContainer("mysql:8.3") .withName("budibase-test-mysql") .withReuse() @@ -33,7 +35,7 @@ export async function mysql(): Promise { const host = container.getHost() const port = container.getMappedPort(3306) - return { + const datasource: Datasource = { type: "datasource_plus", source: SourceName.MYSQL, plus: true, @@ -45,4 +47,26 @@ export async function mysql(): Promise { database: "mysql", }, } + + const database = generator.guid().replaceAll("-", "") + await rawQuery(datasource, `CREATE DATABASE \`${database}\``) + datasource.config!.database = database + return datasource +} + +export async function rawQuery(ds: Datasource, sql: string) { + if (!ds.config) { + throw new Error("Datasource config is missing") + } + if (ds.source !== SourceName.MYSQL) { + throw new Error("Datasource source is not MySQL") + } + + const connection = await mysql.createConnection(ds.config) + try { + const [rows] = await connection.query(sql) + return rows + } finally { + connection.end() + } } diff --git a/packages/server/src/integrations/tests/utils/postgres.ts b/packages/server/src/integrations/tests/utils/postgres.ts index 896c7ea3e0..b10dfe44cf 100644 --- a/packages/server/src/integrations/tests/utils/postgres.ts +++ b/packages/server/src/integrations/tests/utils/postgres.ts @@ -1,7 +1,9 @@ import { Datasource, SourceName } from "@budibase/types" import { GenericContainer, Wait } from "testcontainers" +import pg from "pg" +import { generator } from "@budibase/backend-core/tests" -export async function postgres(): Promise { +export async function getDatasource(): Promise { const container = await new GenericContainer("postgres:16.1-bullseye") .withName("budibase-test-postgres") .withReuse() @@ -16,7 +18,7 @@ export async function postgres(): Promise { const host = container.getHost() const port = container.getMappedPort(5432) - return { + const datasource: Datasource = { type: "datasource_plus", source: SourceName.POSTGRES, plus: true, @@ -32,4 +34,28 @@ export async function postgres(): Promise { ca: false, }, } + + const database = generator.guid().replaceAll("-", "") + await rawQuery(datasource, `CREATE DATABASE "${database}"`) + datasource.config!.database = database + + return datasource +} + +export async function rawQuery(ds: Datasource, sql: string) { + if (!ds.config) { + throw new Error("Datasource config is missing") + } + if (ds.source !== SourceName.POSTGRES) { + throw new Error("Datasource source is not Postgres") + } + + const client = new pg.Client(ds.config) + await client.connect() + try { + const { rows } = await client.query(sql) + return rows + } finally { + await client.end() + } } From 385197a5f7da587cce795f68aebcd7ed27a03276 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Wed, 27 Mar 2024 15:55:22 +0000 Subject: [PATCH 13/97] Fix postgres.spec.ts --- .../src/integration-test/postgres.spec.ts | 89 +++++++++---------- 1 file changed, 44 insertions(+), 45 deletions(-) diff --git a/packages/server/src/integration-test/postgres.spec.ts b/packages/server/src/integration-test/postgres.spec.ts index 57da02b507..5ecc3ca3ef 100644 --- a/packages/server/src/integration-test/postgres.spec.ts +++ b/packages/server/src/integration-test/postgres.spec.ts @@ -16,8 +16,12 @@ import { import _ from "lodash" import { generator } from "@budibase/backend-core/tests" import { utils } from "@budibase/backend-core" -import { DatabaseName, getDatasource } from "../integrations/tests/utils" -import { Client } from "pg" +import { + DatabaseName, + getDatasource, + rawQuery, +} from "../integrations/tests/utils" + // @ts-ignore fetch.mockSearch() @@ -28,7 +32,8 @@ jest.mock("../websockets") describe("postgres integrations", () => { let makeRequest: MakeRequestResponse, - postgresDatasource: Datasource, + rawDatasource: Datasource, + datasource: Datasource, primaryPostgresTable: Table, oneToManyRelationshipInfo: ForeignTableInfo, manyToOneRelationshipInfo: ForeignTableInfo, @@ -40,9 +45,8 @@ describe("postgres integrations", () => { makeRequest = generateMakeRequest(apiKey, true) - postgresDatasource = await config.api.datasource.create( - await getDatasource(DatabaseName.POSTGRES) - ) + rawDatasource = await getDatasource(DatabaseName.POSTGRES) + datasource = await config.api.datasource.create(rawDatasource) }) beforeEach(async () => { @@ -66,7 +70,7 @@ describe("postgres integrations", () => { type: FieldType.STRING, }, }, - sourceId: postgresDatasource._id, + sourceId: datasource._id, sourceType: TableSourceType.EXTERNAL, }) } @@ -143,7 +147,7 @@ describe("postgres integrations", () => { main: true, }, }, - sourceId: postgresDatasource._id, + sourceId: datasource._id, sourceType: TableSourceType.EXTERNAL, }) }) @@ -260,7 +264,7 @@ describe("postgres integrations", () => { autocolumn: true, }, }, - sourceId: postgresDatasource._id, + sourceId: datasource._id, sourceType: TableSourceType.EXTERNAL, }) } @@ -298,19 +302,16 @@ describe("postgres integrations", () => { } it("validate table schema", async () => { - const res = await makeRequest( - "get", - `/api/datasources/${postgresDatasource._id}` - ) + const res = await makeRequest("get", `/api/datasources/${datasource._id}`) expect(res.status).toBe(200) expect(res.body).toEqual({ config: { ca: false, - database: "postgres", - host: postgresDatasource.config!.host, + database: expect.any(String), + host: datasource.config!.host, password: "--secret-value--", - port: postgresDatasource.config!.port, + port: datasource.config!.port, rejectUnauthorized: false, schema: "public", ssl: false, @@ -1078,7 +1079,7 @@ describe("postgres integrations", () => { it("should fetch information about postgres datasource", async () => { const primaryName = primaryPostgresTable.name const response = await makeRequest("post", "/api/datasources/info", { - datasource: postgresDatasource, + datasource: datasource, }) expect(response.status).toBe(200) expect(response.body.tableNames).toBeDefined() @@ -1087,26 +1088,22 @@ describe("postgres integrations", () => { }) describe("POST /api/datasources/:datasourceId/schema", () => { - let client: Client let tableName: string beforeEach(async () => { tableName = generator.guid().replaceAll("-", "").substring(0, 10) - client = new Client((await getDatasource(DatabaseName.POSTGRES)).config!) - await client.connect() }) afterEach(async () => { - await client.query(`DROP TABLE IF EXISTS "${tableName}"`) - await client.end() + await rawQuery(rawDatasource, `DROP TABLE IF EXISTS "${tableName}"`) }) it("recognises when a table has no primary key", async () => { - await client.query(`CREATE TABLE "${tableName}" (id SERIAL)`) + await rawQuery(rawDatasource, `CREATE TABLE "${tableName}" (id SERIAL)`) const response = await makeRequest( "post", - `/api/datasources/${postgresDatasource._id}/schema` + `/api/datasources/${datasource._id}/schema` ) expect(response.body.errors).toEqual({ @@ -1115,13 +1112,14 @@ describe("postgres integrations", () => { }) it("recognises when a table is using a reserved column name", async () => { - await client.query( + await rawQuery( + rawDatasource, `CREATE TABLE "${tableName}" (_id SERIAL PRIMARY KEY) ` ) const response = await makeRequest( "post", - `/api/datasources/${postgresDatasource._id}/schema` + `/api/datasources/${datasource._id}/schema` ) expect(response.body.errors).toEqual({ @@ -1131,8 +1129,8 @@ describe("postgres integrations", () => { }) describe("Integration compatibility with postgres search_path", () => { - let client: Client, - pathDatasource: Datasource, + let rawDatasource: Datasource, + datasource: Datasource, schema1: string, schema2: string @@ -1140,39 +1138,38 @@ describe("postgres integrations", () => { schema1 = generator.guid().replaceAll("-", "") schema2 = generator.guid().replaceAll("-", "") - const dsConfig = await getDatasource(DatabaseName.POSTGRES) - const dbConfig = dsConfig.config! + rawDatasource = await getDatasource(DatabaseName.POSTGRES) + const dbConfig = rawDatasource.config! - client = new Client(dbConfig) - await client.connect() - await client.query(`CREATE SCHEMA "${schema1}";`) - await client.query(`CREATE SCHEMA "${schema2}";`) + await rawQuery(rawDatasource, `CREATE SCHEMA "${schema1}";`) + await rawQuery(rawDatasource, `CREATE SCHEMA "${schema2}";`) const pathConfig: any = { - ...dsConfig, + ...rawDatasource, config: { ...dbConfig, schema: `${schema1}, ${schema2}`, }, } - pathDatasource = await config.api.datasource.create(pathConfig) + datasource = await config.api.datasource.create(pathConfig) }) afterEach(async () => { - await client.query(`DROP SCHEMA "${schema1}" CASCADE;`) - await client.query(`DROP SCHEMA "${schema2}" CASCADE;`) - await client.end() + await rawQuery(rawDatasource, `DROP SCHEMA "${schema1}" CASCADE;`) + await rawQuery(rawDatasource, `DROP SCHEMA "${schema2}" CASCADE;`) }) it("discovers tables from any schema in search path", async () => { - await client.query( + await rawQuery( + rawDatasource, `CREATE TABLE "${schema1}".table1 (id1 SERIAL PRIMARY KEY);` ) - await client.query( + await rawQuery( + rawDatasource, `CREATE TABLE "${schema2}".table2 (id2 SERIAL PRIMARY KEY);` ) const response = await makeRequest("post", "/api/datasources/info", { - datasource: pathDatasource, + datasource: datasource, }) expect(response.status).toBe(200) expect(response.body.tableNames).toBeDefined() @@ -1183,15 +1180,17 @@ describe("postgres integrations", () => { it("does not mix columns from different tables", async () => { const repeated_table_name = "table_same_name" - await client.query( + await rawQuery( + rawDatasource, `CREATE TABLE "${schema1}".${repeated_table_name} (id SERIAL PRIMARY KEY, val1 TEXT);` ) - await client.query( + await rawQuery( + rawDatasource, `CREATE TABLE "${schema2}".${repeated_table_name} (id2 SERIAL PRIMARY KEY, val2 TEXT);` ) const response = await makeRequest( "post", - `/api/datasources/${pathDatasource._id}/schema`, + `/api/datasources/${datasource._id}/schema`, { tablesFilter: [repeated_table_name], } From 1205cfcbcc3329a498ee8a8bcf297ea40df0abe9 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Wed, 27 Mar 2024 16:31:31 +0000 Subject: [PATCH 14/97] Fix mysql.spec.ts --- .../routes/tests/queries/generic-sql.spec.ts | 3 - .../server/src/integration-test/mysql.spec.ts | 112 ++++++++---------- 2 files changed, 48 insertions(+), 67 deletions(-) diff --git a/packages/server/src/api/routes/tests/queries/generic-sql.spec.ts b/packages/server/src/api/routes/tests/queries/generic-sql.spec.ts index ff77d3dc52..585288bc43 100644 --- a/packages/server/src/api/routes/tests/queries/generic-sql.spec.ts +++ b/packages/server/src/api/routes/tests/queries/generic-sql.spec.ts @@ -5,9 +5,6 @@ import { getDatasource, rawQuery, } from "../../../../integrations/tests/utils" -import pg from "pg" -import mysql from "mysql2/promise" -import mssql from "mssql" jest.unmock("pg") diff --git a/packages/server/src/integration-test/mysql.spec.ts b/packages/server/src/integration-test/mysql.spec.ts index fb2d3c5285..7e54b53b15 100644 --- a/packages/server/src/integration-test/mysql.spec.ts +++ b/packages/server/src/integration-test/mysql.spec.ts @@ -11,8 +11,11 @@ import { TableRequest, TableSourceType, } from "@budibase/types" -import { DatabaseName, getDatasource } from "../integrations/tests/utils" -import mysql from "mysql2/promise" +import { + DatabaseName, + getDatasource, + rawQuery, +} from "../integrations/tests/utils" import { builderSocket } from "../websockets" import { generator } from "@budibase/backend-core/tests" // @ts-ignore @@ -44,7 +47,8 @@ jest.mock("../websockets", () => ({ describe("mysql integrations", () => { let makeRequest: MakeRequestResponse, - mysqlDatasource: Datasource, + rawDatasource: Datasource, + datasource: Datasource, primaryMySqlTable: Table beforeAll(async () => { @@ -53,9 +57,8 @@ describe("mysql integrations", () => { makeRequest = generateMakeRequest(apiKey, true) - mysqlDatasource = await config.api.datasource.create( - await getDatasource(DatabaseName.MYSQL) - ) + rawDatasource = await getDatasource(DatabaseName.MYSQL) + datasource = await config.api.datasource.create(rawDatasource) }) beforeEach(async () => { @@ -82,7 +85,7 @@ describe("mysql integrations", () => { type: FieldType.NUMBER, }, }, - sourceId: mysqlDatasource._id, + sourceId: datasource._id, sourceType: TableSourceType.EXTERNAL, }) }) @@ -90,18 +93,15 @@ describe("mysql integrations", () => { afterAll(config.end) it("validate table schema", async () => { - const res = await makeRequest( - "get", - `/api/datasources/${mysqlDatasource._id}` - ) + const res = await makeRequest("get", `/api/datasources/${datasource._id}`) expect(res.status).toBe(200) expect(res.body).toEqual({ config: { - database: "mysql", - host: mysqlDatasource.config!.host, + database: expect.any(String), + host: datasource.config!.host, password: "--secret-value--", - port: mysqlDatasource.config!.port, + port: datasource.config!.port, user: "root", }, plus: true, @@ -120,7 +120,7 @@ describe("mysql integrations", () => { it("should be able to verify the connection", async () => { await config.api.datasource.verify( { - datasource: await getDatasource(DatabaseName.MYSQL), + datasource: rawDatasource, }, { body: { @@ -131,13 +131,12 @@ describe("mysql integrations", () => { }) it("should state an invalid datasource cannot connect", async () => { - const dbConfig = await getDatasource(DatabaseName.MYSQL) await config.api.datasource.verify( { datasource: { - ...dbConfig, + ...rawDatasource, config: { - ...dbConfig.config, + ...rawDatasource.config, password: "wrongpassword", }, }, @@ -157,7 +156,7 @@ describe("mysql integrations", () => { it("should fetch information about mysql datasource", async () => { const primaryName = primaryMySqlTable.name const response = await makeRequest("post", "/api/datasources/info", { - datasource: mysqlDatasource, + datasource: datasource, }) expect(response.status).toBe(200) expect(response.body.tableNames).toBeDefined() @@ -166,40 +165,38 @@ describe("mysql integrations", () => { }) describe("Integration compatibility with mysql search_path", () => { - let client: mysql.Connection, pathDatasource: Datasource - const database = "test1" - const database2 = "test-2" + let datasource: Datasource, rawDatasource: Datasource + const database = generator.guid() + const database2 = generator.guid() beforeAll(async () => { - const dsConfig = await getDatasource(DatabaseName.MYSQL) - const dbConfig = dsConfig.config! + rawDatasource = await getDatasource(DatabaseName.MYSQL) - client = await mysql.createConnection(dbConfig) - await client.query(`CREATE DATABASE \`${database}\`;`) - await client.query(`CREATE DATABASE \`${database2}\`;`) + await rawQuery(rawDatasource, `CREATE DATABASE \`${database}\`;`) + await rawQuery(rawDatasource, `CREATE DATABASE \`${database2}\`;`) const pathConfig: any = { - ...dsConfig, + ...rawDatasource, config: { - ...dbConfig, + ...rawDatasource.config!, database, }, } - pathDatasource = await config.api.datasource.create(pathConfig) + datasource = await config.api.datasource.create(pathConfig) }) afterAll(async () => { - await client.query(`DROP DATABASE \`${database}\`;`) - await client.query(`DROP DATABASE \`${database2}\`;`) - await client.end() + await rawQuery(rawDatasource, `DROP DATABASE \`${database}\`;`) + await rawQuery(rawDatasource, `DROP DATABASE \`${database2}\`;`) }) it("discovers tables from any schema in search path", async () => { - await client.query( + await rawQuery( + rawDatasource, `CREATE TABLE \`${database}\`.table1 (id1 SERIAL PRIMARY KEY);` ) const response = await makeRequest("post", "/api/datasources/info", { - datasource: pathDatasource, + datasource: datasource, }) expect(response.status).toBe(200) expect(response.body.tableNames).toBeDefined() @@ -210,15 +207,17 @@ describe("mysql integrations", () => { it("does not mix columns from different tables", async () => { const repeated_table_name = "table_same_name" - await client.query( + await rawQuery( + rawDatasource, `CREATE TABLE \`${database}\`.${repeated_table_name} (id SERIAL PRIMARY KEY, val1 TEXT);` ) - await client.query( + await rawQuery( + rawDatasource, `CREATE TABLE \`${database2}\`.${repeated_table_name} (id2 SERIAL PRIMARY KEY, val2 TEXT);` ) const response = await makeRequest( "post", - `/api/datasources/${pathDatasource._id}/schema`, + `/api/datasources/${datasource._id}/schema`, { tablesFilter: [repeated_table_name], } @@ -234,30 +233,14 @@ describe("mysql integrations", () => { }) describe("POST /api/tables/", () => { - let client: mysql.Connection const emitDatasourceUpdateMock = jest.fn() - beforeEach(async () => { - client = await mysql.createConnection( - ( - await getDatasource(DatabaseName.MYSQL) - ).config! - ) - mysqlDatasource = await config.api.datasource.create( - await getDatasource(DatabaseName.MYSQL) - ) - }) - - afterEach(async () => { - await client.end() - }) - it("will emit the datasource entity schema with externalType to the front-end when adding a new column", async () => { const addColumnToTable: TableRequest = { type: "table", sourceType: TableSourceType.EXTERNAL, name: uniqueTableName(), - sourceId: mysqlDatasource._id!, + sourceId: datasource._id!, primary: ["id"], schema: { id: { @@ -304,7 +287,7 @@ describe("mysql integrations", () => { }, }, created: true, - _id: `${mysqlDatasource._id}__${addColumnToTable.name}`, + _id: `${datasource._id}__${addColumnToTable.name}`, } delete expectedTable._add @@ -351,17 +334,18 @@ describe("mysql integrations", () => { "/api/tables/", renameColumnOnTable ) - mysqlDatasource = ( - await makeRequest( - "post", - `/api/datasources/${mysqlDatasource._id}/schema` - ) + + const ds = ( + await makeRequest("post", `/api/datasources/${datasource._id}/schema`) ).body.datasource expect(response.status).toEqual(200) - expect( - Object.keys(mysqlDatasource.entities![primaryMySqlTable.name].schema) - ).toEqual(["id", "name", "description", "age"]) + expect(Object.keys(ds.entities![primaryMySqlTable.name].schema)).toEqual([ + "id", + "name", + "description", + "age", + ]) }) }) }) From eb33dac9b1cd0afcee95f3990b957f5b75042f62 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Wed, 27 Mar 2024 16:40:41 +0000 Subject: [PATCH 15/97] Make container reuse optional, disabled by default. --- globalSetup.ts | 11 +++++++---- .../src/integrations/tests/utils/mariadb.ts | 15 +++++++++------ .../src/integrations/tests/utils/mongodb.ts | 15 +++++++++------ .../server/src/integrations/tests/utils/mssql.ts | 15 +++++++++------ .../server/src/integrations/tests/utils/mysql.ts | 16 ++++++++++------ .../src/integrations/tests/utils/postgres.ts | 16 ++++++++++------ 6 files changed, 54 insertions(+), 34 deletions(-) diff --git a/globalSetup.ts b/globalSetup.ts index 66d3f5fd8c..00d5e3f2dc 100644 --- a/globalSetup.ts +++ b/globalSetup.ts @@ -1,9 +1,7 @@ import { GenericContainer, Wait } from "testcontainers" export default async function setup() { - await new GenericContainer("budibase/couchdb") - .withName("budibase-test-couchdb") - .withReuse() + let couchdb = new GenericContainer("budibase/couchdb") .withExposedPorts(5984) .withEnvironment({ COUCHDB_PASSWORD: "budibase", @@ -23,5 +21,10 @@ export default async function setup() { "curl http://budibase:budibase@localhost:5984/_up" ).withStartupTimeout(20000) ) - .start() + + if (process.env.REUSE_CONTAINERS) { + couchdb = couchdb.withReuse() + } + + await couchdb.start() } diff --git a/packages/server/src/integrations/tests/utils/mariadb.ts b/packages/server/src/integrations/tests/utils/mariadb.ts index c8890af1fb..2634c9f913 100644 --- a/packages/server/src/integrations/tests/utils/mariadb.ts +++ b/packages/server/src/integrations/tests/utils/mariadb.ts @@ -22,16 +22,19 @@ class MariaDBWaitStrategy extends AbstractWaitStrategy { } export async function getDatasource(): Promise { - const container = await new GenericContainer("mariadb:lts") - .withName("budibase-test-mariadb") - .withReuse() + let container = new GenericContainer("mariadb:lts") .withExposedPorts(3306) .withEnvironment({ MARIADB_ROOT_PASSWORD: "password" }) .withWaitStrategy(new MariaDBWaitStrategy()) - .start() - const host = container.getHost() - const port = container.getMappedPort(3306) + if (process.env.REUSE_CONTAINERS) { + container = container.withReuse() + } + + const startedContainer = await container.start() + + const host = startedContainer.getHost() + const port = startedContainer.getMappedPort(3306) const config = { host, diff --git a/packages/server/src/integrations/tests/utils/mongodb.ts b/packages/server/src/integrations/tests/utils/mongodb.ts index 6ab5b11191..26fbff966e 100644 --- a/packages/server/src/integrations/tests/utils/mongodb.ts +++ b/packages/server/src/integrations/tests/utils/mongodb.ts @@ -2,9 +2,7 @@ import { Datasource, SourceName } from "@budibase/types" import { GenericContainer, Wait } from "testcontainers" export async function getDatasource(): Promise { - const container = await new GenericContainer("mongo:7.0-jammy") - .withName("budibase-test-mongodb") - .withReuse() + let container = new GenericContainer("mongo:7.0-jammy") .withExposedPorts(27017) .withEnvironment({ MONGO_INITDB_ROOT_USERNAME: "mongo", @@ -15,10 +13,15 @@ export async function getDatasource(): Promise { `mongosh --eval "db.version()"` ).withStartupTimeout(10000) ) - .start() - const host = container.getHost() - const port = container.getMappedPort(27017) + if (process.env.REUSE_CONTAINERS) { + container = container.withReuse() + } + + const startedContainer = await container.start() + + const host = startedContainer.getHost() + const port = startedContainer.getMappedPort(27017) return { type: "datasource", diff --git a/packages/server/src/integrations/tests/utils/mssql.ts b/packages/server/src/integrations/tests/utils/mssql.ts index c0875b84db..290bc78246 100644 --- a/packages/server/src/integrations/tests/utils/mssql.ts +++ b/packages/server/src/integrations/tests/utils/mssql.ts @@ -4,11 +4,9 @@ import mssql from "mssql" import { generator } from "@budibase/backend-core/tests" export async function getDatasource(): Promise { - const container = await new GenericContainer( + let container = new GenericContainer( "mcr.microsoft.com/mssql/server:2022-latest" ) - .withName("budibase-test-mssql") - .withReuse() .withExposedPorts(1433) .withEnvironment({ ACCEPT_EULA: "Y", @@ -24,10 +22,15 @@ export async function getDatasource(): Promise { "/opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P Password_123 -q 'SELECT 1'" ) ) - .start() - const host = container.getHost() - const port = container.getMappedPort(1433) + if (process.env.REUSE_CONTAINERS) { + container = container.withReuse() + } + + const startedContainer = await container.start() + + const host = startedContainer.getHost() + const port = startedContainer.getMappedPort(1433) const datasource: Datasource = { type: "datasource_plus", diff --git a/packages/server/src/integrations/tests/utils/mysql.ts b/packages/server/src/integrations/tests/utils/mysql.ts index 9fa8b0bd86..0f83128f26 100644 --- a/packages/server/src/integrations/tests/utils/mysql.ts +++ b/packages/server/src/integrations/tests/utils/mysql.ts @@ -25,15 +25,19 @@ class MySQLWaitStrategy extends AbstractWaitStrategy { } export async function getDatasource(): Promise { - const container = await new GenericContainer("mysql:8.3") - .withName("budibase-test-mysql") - .withReuse() + let container = new GenericContainer("mysql:8.3") .withExposedPorts(3306) .withEnvironment({ MYSQL_ROOT_PASSWORD: "password" }) .withWaitStrategy(new MySQLWaitStrategy().withStartupTimeout(10000)) - .start() - const host = container.getHost() - const port = container.getMappedPort(3306) + + if (process.env.REUSE_CONTAINERS) { + container = container.withReuse() + } + + const startedContainer = await container.start() + + const host = startedContainer.getHost() + const port = startedContainer.getMappedPort(3306) const datasource: Datasource = { type: "datasource_plus", diff --git a/packages/server/src/integrations/tests/utils/postgres.ts b/packages/server/src/integrations/tests/utils/postgres.ts index b10dfe44cf..237bc19a17 100644 --- a/packages/server/src/integrations/tests/utils/postgres.ts +++ b/packages/server/src/integrations/tests/utils/postgres.ts @@ -4,9 +4,7 @@ import pg from "pg" import { generator } from "@budibase/backend-core/tests" export async function getDatasource(): Promise { - const container = await new GenericContainer("postgres:16.1-bullseye") - .withName("budibase-test-postgres") - .withReuse() + let container = new GenericContainer("postgres:16.1-bullseye") .withExposedPorts(5432) .withEnvironment({ POSTGRES_PASSWORD: "password" }) .withWaitStrategy( @@ -14,9 +12,15 @@ export async function getDatasource(): Promise { "pg_isready -h localhost -p 5432" ).withStartupTimeout(10000) ) - .start() - const host = container.getHost() - const port = container.getMappedPort(5432) + + if (process.env.REUSE_CONTAINERS) { + container = container.withReuse() + } + + const startedContainer = await container.start() + + const host = startedContainer.getHost() + const port = startedContainer.getMappedPort(5432) const datasource: Datasource = { type: "datasource_plus", From 204b16876a74f549aff70bd5ee77a5e2bbe95aae Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Wed, 27 Mar 2024 16:46:11 +0000 Subject: [PATCH 16/97] Run yarn lint:fix --- .../backend-core/tests/core/utilities/structures/generator.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/backend-core/tests/core/utilities/structures/generator.ts b/packages/backend-core/tests/core/utilities/structures/generator.ts index 2a7eba6bbe..64eb5ecc97 100644 --- a/packages/backend-core/tests/core/utilities/structures/generator.ts +++ b/packages/backend-core/tests/core/utilities/structures/generator.ts @@ -1,4 +1,3 @@ import Chance from "./Chance" export const generator = new Chance() - From 4183a0aeb785c4081f9b1924e2e2ab716593300b Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Wed, 27 Mar 2024 16:57:31 +0000 Subject: [PATCH 17/97] Set default packages/server test timeout to 30 seconds, and unmock console.log. --- .../server/src/api/routes/public/tests/metrics.spec.js | 2 -- packages/server/src/api/routes/tests/appImport.spec.ts | 1 - .../server/src/api/routes/tests/automation.spec.ts | 2 -- packages/server/src/api/routes/tests/user.spec.ts | 2 -- packages/server/src/migrations/tests/index.spec.ts | 2 -- .../src/sdk/app/rows/search/tests/external.spec.ts | 2 -- packages/server/src/tests/jestSetup.ts | 10 ++-------- 7 files changed, 2 insertions(+), 19 deletions(-) diff --git a/packages/server/src/api/routes/public/tests/metrics.spec.js b/packages/server/src/api/routes/public/tests/metrics.spec.js index 8231596d59..2fb5e91000 100644 --- a/packages/server/src/api/routes/public/tests/metrics.spec.js +++ b/packages/server/src/api/routes/public/tests/metrics.spec.js @@ -1,7 +1,5 @@ const setup = require("../../tests/utilities") -jest.setTimeout(30000) - describe("/metrics", () => { let request = setup.getRequest() let config = setup.getConfig() diff --git a/packages/server/src/api/routes/tests/appImport.spec.ts b/packages/server/src/api/routes/tests/appImport.spec.ts index 75e9f91d63..bc211024d4 100644 --- a/packages/server/src/api/routes/tests/appImport.spec.ts +++ b/packages/server/src/api/routes/tests/appImport.spec.ts @@ -1,7 +1,6 @@ import * as setup from "./utilities" import path from "path" -jest.setTimeout(15000) const PASSWORD = "testtest" describe("/applications/:appId/import", () => { diff --git a/packages/server/src/api/routes/tests/automation.spec.ts b/packages/server/src/api/routes/tests/automation.spec.ts index 322694df75..7885e97fbf 100644 --- a/packages/server/src/api/routes/tests/automation.spec.ts +++ b/packages/server/src/api/routes/tests/automation.spec.ts @@ -23,8 +23,6 @@ let { collectAutomation, } = setup.structures -jest.setTimeout(30000) - describe("/automations", () => { let request = setup.getRequest() let config = setup.getConfig() diff --git a/packages/server/src/api/routes/tests/user.spec.ts b/packages/server/src/api/routes/tests/user.spec.ts index ff8c0d54b3..a46de8f3b3 100644 --- a/packages/server/src/api/routes/tests/user.spec.ts +++ b/packages/server/src/api/routes/tests/user.spec.ts @@ -3,8 +3,6 @@ import { checkPermissionsEndpoint } from "./utilities/TestFunctions" import * as setup from "./utilities" import { UserMetadata } from "@budibase/types" -jest.setTimeout(30000) - jest.mock("../../../utilities/workerRequests", () => ({ getGlobalUsers: jest.fn(() => { return {} diff --git a/packages/server/src/migrations/tests/index.spec.ts b/packages/server/src/migrations/tests/index.spec.ts index 8eb59b8a0e..d06cd37b69 100644 --- a/packages/server/src/migrations/tests/index.spec.ts +++ b/packages/server/src/migrations/tests/index.spec.ts @@ -25,8 +25,6 @@ const clearMigrations = async () => { } } -jest.setTimeout(10000) - describe("migrations", () => { const config = new TestConfig() diff --git a/packages/server/src/sdk/app/rows/search/tests/external.spec.ts b/packages/server/src/sdk/app/rows/search/tests/external.spec.ts index bae58d6a2c..596e41cece 100644 --- a/packages/server/src/sdk/app/rows/search/tests/external.spec.ts +++ b/packages/server/src/sdk/app/rows/search/tests/external.spec.ts @@ -17,8 +17,6 @@ import { generator, } from "@budibase/backend-core/tests" -jest.setTimeout(30000) - describe("external search", () => { const config = new TestConfiguration() diff --git a/packages/server/src/tests/jestSetup.ts b/packages/server/src/tests/jestSetup.ts index e233e7152e..c01f415f9e 100644 --- a/packages/server/src/tests/jestSetup.ts +++ b/packages/server/src/tests/jestSetup.ts @@ -2,17 +2,11 @@ import env from "../environment" import { env as coreEnv, timers } from "@budibase/backend-core" import { testContainerUtils } from "@budibase/backend-core/tests" -if (!process.env.DEBUG) { - global.console.log = jest.fn() // console.log are ignored in tests - global.console.warn = jest.fn() // console.warn are ignored in tests -} - if (!process.env.CI) { - // set a longer timeout in dev for debugging - // 100 seconds + // set a longer timeout in dev for debugging 100 seconds jest.setTimeout(100 * 1000) } else { - jest.setTimeout(10 * 1000) + jest.setTimeout(30 * 1000) } testContainerUtils.setupEnv(env, coreEnv) From ecdb5eec36accc0ed3b00aab5773ace31773efef Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Wed, 27 Mar 2024 16:59:27 +0000 Subject: [PATCH 18/97] Remove testcontainers debug logging. --- packages/server/scripts/test.sh | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/server/scripts/test.sh b/packages/server/scripts/test.sh index c9f063c409..3ecf8bb794 100644 --- a/packages/server/scripts/test.sh +++ b/packages/server/scripts/test.sh @@ -1,8 +1,6 @@ #!/bin/bash set -e -export DEBUG=testcontainers* - if [[ -n $CI ]] then export NODE_OPTIONS="--max-old-space-size=4096 --no-node-snapshot $NODE_OPTIONS" From d7f112dc9559b37f297c9bd8de23cf4956e2bedf Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Wed, 27 Mar 2024 17:47:03 +0000 Subject: [PATCH 19/97] Free up space in the server tests run. --- .github/workflows/budibase_ci.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/budibase_ci.yml b/.github/workflows/budibase_ci.yml index a9cb89d4e6..f1dd54ef18 100644 --- a/.github/workflows/budibase_ci.yml +++ b/.github/workflows/budibase_ci.yml @@ -153,6 +153,11 @@ jobs: node-version: 20.x cache: yarn + - name: Free Disk Space (Ubuntu) + uses: jlumbroso/free-disk-space@main + with: + android: true + - name: Pull testcontainers images run: | docker pull mcr.microsoft.com/mssql/server:2022-latest From 29a928eb09b07d1dec9a13353197cde200235978 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Wed, 27 Mar 2024 17:51:19 +0000 Subject: [PATCH 20/97] Turn off some expensive options on the disk free up. --- .github/workflows/budibase_ci.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/budibase_ci.yml b/.github/workflows/budibase_ci.yml index f1dd54ef18..be588549a1 100644 --- a/.github/workflows/budibase_ci.yml +++ b/.github/workflows/budibase_ci.yml @@ -156,6 +156,9 @@ jobs: - name: Free Disk Space (Ubuntu) uses: jlumbroso/free-disk-space@main with: + large-packages: false + swap-storage: false + docker-images: false android: true - name: Pull testcontainers images From f43f03a3b495b5ac3e97782824a961cdf49f93e4 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Thu, 28 Mar 2024 10:31:28 +0000 Subject: [PATCH 21/97] Use new larger runners. --- .github/workflows/budibase_ci.yml | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/.github/workflows/budibase_ci.yml b/.github/workflows/budibase_ci.yml index be588549a1..6ad5e68cbd 100644 --- a/.github/workflows/budibase_ci.yml +++ b/.github/workflows/budibase_ci.yml @@ -136,7 +136,7 @@ jobs: fi test-server: - runs-on: ubuntu-latest + runs-on: budi-tubby-tornado-quad-core-150gb env: DEBUG: testcontainers,testcontainers:exec,testcontainers:build,testcontainers:pull steps: @@ -153,14 +153,6 @@ jobs: node-version: 20.x cache: yarn - - name: Free Disk Space (Ubuntu) - uses: jlumbroso/free-disk-space@main - with: - large-packages: false - swap-storage: false - docker-images: false - android: true - - name: Pull testcontainers images run: | docker pull mcr.microsoft.com/mssql/server:2022-latest From 90cfdd661de448ebb484a426dc2822bf9c1feb2f Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Thu, 28 Mar 2024 17:36:26 +0000 Subject: [PATCH 22/97] Rework how we connect to containers. --- .github/workflows/budibase_ci.yml | 3 +- .../core/utilities/testContainerUtils.ts | 47 ++++++++++++----- .../src/integrations/tests/utils/index.ts | 25 ++++++++++ .../src/integrations/tests/utils/mariadb.ts | 29 ++++++----- .../src/integrations/tests/utils/mongodb.ts | 40 ++++++++------- .../src/integrations/tests/utils/mssql.ts | 50 +++++++++---------- .../src/integrations/tests/utils/mysql.ts | 26 +++++----- .../src/integrations/tests/utils/postgres.ts | 32 ++++++------ 8 files changed, 152 insertions(+), 100 deletions(-) diff --git a/.github/workflows/budibase_ci.yml b/.github/workflows/budibase_ci.yml index 6ad5e68cbd..224537d216 100644 --- a/.github/workflows/budibase_ci.yml +++ b/.github/workflows/budibase_ci.yml @@ -136,7 +136,8 @@ jobs: fi test-server: - runs-on: budi-tubby-tornado-quad-core-150gb + runs-on: + group: hosted-runners env: DEBUG: testcontainers,testcontainers:exec,testcontainers:build,testcontainers:pull steps: diff --git a/packages/backend-core/tests/core/utilities/testContainerUtils.ts b/packages/backend-core/tests/core/utilities/testContainerUtils.ts index 5d4f5a3c11..2f33db65d3 100644 --- a/packages/backend-core/tests/core/utilities/testContainerUtils.ts +++ b/packages/backend-core/tests/core/utilities/testContainerUtils.ts @@ -1,6 +1,8 @@ import { DatabaseImpl } from "../../../src/db" import { execSync } from "child_process" +const IPV4_PORT_REGEX = new RegExp(`0\\.0\\.0\\.0:(\\d+)->(\\d+)/tcp`, "g") + interface ContainerInfo { Command: string CreatedAt: string @@ -19,7 +21,10 @@ interface ContainerInfo { } function getTestcontainers(): ContainerInfo[] { - return execSync("docker ps --format json") + // We use --format json to make sure the output is nice and machine-readable, + // and we use --no-trunc so that the command returns full container IDs so we + // can filter on them correctly. + return execSync("docker ps --format json --no-trunc") .toString() .split("\n") .filter(x => x.length > 0) @@ -27,32 +32,51 @@ function getTestcontainers(): ContainerInfo[] { .filter(x => x.Labels.includes("org.testcontainers=true")) } -function getContainerByImage(image: string) { - return getTestcontainers().find(x => x.Image.startsWith(image)) +export function getContainerByImage(image: string) { + const containers = getTestcontainers().filter(x => x.Image.startsWith(image)) + if (containers.length > 1) { + throw new Error(`Multiple containers found with image: ${image}`) + } + return containers[0] } -function getExposedPort(container: ContainerInfo, port: number) { - const match = container.Ports.match(new RegExp(`0.0.0.0:(\\d+)->${port}/tcp`)) - if (!match) { - return undefined +export function getContainerById(id: string) { + return getTestcontainers().find(x => x.ID === id) +} + +export interface Port { + host: number + container: number +} + +export function getExposedV4Ports(container: ContainerInfo): Port[] { + let ports: Port[] = [] + for (const match of container.Ports.matchAll(IPV4_PORT_REGEX)) { + ports.push({ host: parseInt(match[1]), container: parseInt(match[2]) }) } - return parseInt(match[1]) + return ports +} + +export function getExposedV4Port(container: ContainerInfo, port: number) { + return getExposedV4Ports(container).find(x => x.container === port)?.host } export function setupEnv(...envs: any[]) { + // We start couchdb in globalSetup.ts, in the root of the monorepo, so it + // should be relatively safe to look for it by its image name. const couch = getContainerByImage("budibase/couchdb") if (!couch) { throw new Error("CouchDB container not found") } - const couchPort = getExposedPort(couch, 5984) + const couchPort = getExposedV4Port(couch, 5984) if (!couchPort) { throw new Error("CouchDB port not found") } const configs = [ { key: "COUCH_DB_PORT", value: `${couchPort}` }, - { key: "COUCH_DB_URL", value: `http://localhost:${couchPort}` }, + { key: "COUCH_DB_URL", value: `http://127.0.0.1:${couchPort}` }, ] for (const config of configs.filter(x => !!x.value)) { @@ -60,7 +84,4 @@ export function setupEnv(...envs: any[]) { env._set(config.key, config.value) } } - - // @ts-expect-error - DatabaseImpl.nano = undefined } diff --git a/packages/server/src/integrations/tests/utils/index.ts b/packages/server/src/integrations/tests/utils/index.ts index 5760273d51..bbdb41b38a 100644 --- a/packages/server/src/integrations/tests/utils/index.ts +++ b/packages/server/src/integrations/tests/utils/index.ts @@ -6,6 +6,8 @@ import * as mongodb from "./mongodb" import * as mysql from "./mysql" import * as mssql from "./mssql" import * as mariadb from "./mariadb" +import { GenericContainer } from "testcontainers" +import { testContainerUtils } from "@budibase/backend-core/tests" export type DatasourceProvider = () => Promise @@ -63,3 +65,26 @@ export async function rawQuery(ds: Datasource, sql: string): Promise { } } } + +export async function startContainer(container: GenericContainer) { + if (process.env.REUSE_CONTAINERS) { + container = container.withReuse() + } + + const startedContainer = await container.start() + + const info = testContainerUtils.getContainerById(startedContainer.getId()) + if (!info) { + throw new Error("Container not found") + } + + // Some Docker runtimes, when you expose a port, will bind it to both + // 127.0.0.1 and ::1, so ipv4 and ipv6. The port spaces of ipv4 and ipv6 + // addresses are not shared, and testcontainers will sometimes give you back + // the ipv6 port. There's no way to know that this has happened, and if you + // try to then connect to `localhost:port` you may attempt to bind to the v4 + // address which could be unbound or even an entirely different container. For + // that reason, we don't use testcontainers' `getExposedPort` function, + // preferring instead our own method that guaranteed v4 ports. + return testContainerUtils.getExposedV4Ports(info) +} diff --git a/packages/server/src/integrations/tests/utils/mariadb.ts b/packages/server/src/integrations/tests/utils/mariadb.ts index 2634c9f913..fcd79b8e56 100644 --- a/packages/server/src/integrations/tests/utils/mariadb.ts +++ b/packages/server/src/integrations/tests/utils/mariadb.ts @@ -2,7 +2,10 @@ import { Datasource, SourceName } from "@budibase/types" import { GenericContainer, Wait } from "testcontainers" import { AbstractWaitStrategy } from "testcontainers/build/wait-strategies/wait-strategy" import { rawQuery } from "./mysql" -import { generator } from "@budibase/backend-core/tests" +import { generator, testContainerUtils } from "@budibase/backend-core/tests" +import { startContainer } from "." + +let ports: Promise class MariaDBWaitStrategy extends AbstractWaitStrategy { async waitUntilReady(container: any, boundPorts: any, startTime?: Date) { @@ -22,22 +25,22 @@ class MariaDBWaitStrategy extends AbstractWaitStrategy { } export async function getDatasource(): Promise { - let container = new GenericContainer("mariadb:lts") - .withExposedPorts(3306) - .withEnvironment({ MARIADB_ROOT_PASSWORD: "password" }) - .withWaitStrategy(new MariaDBWaitStrategy()) - - if (process.env.REUSE_CONTAINERS) { - container = container.withReuse() + if (!ports) { + ports = startContainer( + new GenericContainer("mariadb:lts") + .withExposedPorts(3306) + .withEnvironment({ MARIADB_ROOT_PASSWORD: "password" }) + .withWaitStrategy(new MariaDBWaitStrategy()) + ) } - const startedContainer = await container.start() - - const host = startedContainer.getHost() - const port = startedContainer.getMappedPort(3306) + const port = (await ports).find(x => x.container === 3306)?.host + if (!port) { + throw new Error("MariaDB port not found") + } const config = { - host, + host: "127.0.0.1", port, user: "root", password: "password", diff --git a/packages/server/src/integrations/tests/utils/mongodb.ts b/packages/server/src/integrations/tests/utils/mongodb.ts index 26fbff966e..c5c0340dc9 100644 --- a/packages/server/src/integrations/tests/utils/mongodb.ts +++ b/packages/server/src/integrations/tests/utils/mongodb.ts @@ -1,34 +1,38 @@ +import { testContainerUtils } from "@budibase/backend-core/tests" import { Datasource, SourceName } from "@budibase/types" import { GenericContainer, Wait } from "testcontainers" +import { startContainer } from "." + +let ports: Promise export async function getDatasource(): Promise { - let container = new GenericContainer("mongo:7.0-jammy") - .withExposedPorts(27017) - .withEnvironment({ - MONGO_INITDB_ROOT_USERNAME: "mongo", - MONGO_INITDB_ROOT_PASSWORD: "password", - }) - .withWaitStrategy( - Wait.forSuccessfulCommand( - `mongosh --eval "db.version()"` - ).withStartupTimeout(10000) + if (!ports) { + ports = startContainer( + new GenericContainer("mongo:7.0-jammy") + .withExposedPorts(27017) + .withEnvironment({ + MONGO_INITDB_ROOT_USERNAME: "mongo", + MONGO_INITDB_ROOT_PASSWORD: "password", + }) + .withWaitStrategy( + Wait.forSuccessfulCommand( + `mongosh --eval "db.version()"` + ).withStartupTimeout(10000) + ) ) - - if (process.env.REUSE_CONTAINERS) { - container = container.withReuse() } - const startedContainer = await container.start() - - const host = startedContainer.getHost() - const port = startedContainer.getMappedPort(27017) + const port = (await ports).find(x => x.container === 27017) + if (!port) { + throw new Error("MongoDB port not found") + } return { type: "datasource", source: SourceName.MONGODB, plus: false, config: { - connectionString: `mongodb://mongo:password@${host}:${port}`, + connectionString: `mongodb://mongo:password@127.0.0.1:${port.host}`, db: "mongo", }, } diff --git a/packages/server/src/integrations/tests/utils/mssql.ts b/packages/server/src/integrations/tests/utils/mssql.ts index 290bc78246..647f461272 100644 --- a/packages/server/src/integrations/tests/utils/mssql.ts +++ b/packages/server/src/integrations/tests/utils/mssql.ts @@ -1,43 +1,41 @@ import { Datasource, SourceName } from "@budibase/types" import { GenericContainer, Wait } from "testcontainers" import mssql from "mssql" -import { generator } from "@budibase/backend-core/tests" +import { generator, testContainerUtils } from "@budibase/backend-core/tests" +import { startContainer } from "." + +let ports: Promise export async function getDatasource(): Promise { - let container = new GenericContainer( - "mcr.microsoft.com/mssql/server:2022-latest" - ) - .withExposedPorts(1433) - .withEnvironment({ - ACCEPT_EULA: "Y", - MSSQL_SA_PASSWORD: "Password_123", - // This is important, as Microsoft allow us to use the "Developer" edition - // of SQL Server for development and testing purposes. We can't use other - // versions without a valid license, and we cannot use the Developer - // version in production. - MSSQL_PID: "Developer", - }) - .withWaitStrategy( - Wait.forSuccessfulCommand( - "/opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P Password_123 -q 'SELECT 1'" - ) + if (!ports) { + ports = startContainer( + new GenericContainer("mcr.microsoft.com/mssql/server:2022-latest") + .withExposedPorts(1433) + .withEnvironment({ + ACCEPT_EULA: "Y", + MSSQL_SA_PASSWORD: "Password_123", + // This is important, as Microsoft allow us to use the "Developer" edition + // of SQL Server for development and testing purposes. We can't use other + // versions without a valid license, and we cannot use the Developer + // version in production. + MSSQL_PID: "Developer", + }) + .withWaitStrategy( + Wait.forSuccessfulCommand( + "/opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P Password_123 -q 'SELECT 1'" + ) + ) ) - - if (process.env.REUSE_CONTAINERS) { - container = container.withReuse() } - const startedContainer = await container.start() - - const host = startedContainer.getHost() - const port = startedContainer.getMappedPort(1433) + const port = (await ports).find(x => x.container === 1433)?.host const datasource: Datasource = { type: "datasource_plus", source: SourceName.SQL_SERVER, plus: true, config: { - server: host, + server: "127.0.0.1", port, user: "sa", password: "Password_123", diff --git a/packages/server/src/integrations/tests/utils/mysql.ts b/packages/server/src/integrations/tests/utils/mysql.ts index 0f83128f26..a78833e1de 100644 --- a/packages/server/src/integrations/tests/utils/mysql.ts +++ b/packages/server/src/integrations/tests/utils/mysql.ts @@ -2,7 +2,10 @@ import { Datasource, SourceName } from "@budibase/types" import { GenericContainer, Wait } from "testcontainers" import { AbstractWaitStrategy } from "testcontainers/build/wait-strategies/wait-strategy" import mysql from "mysql2/promise" -import { generator } from "@budibase/backend-core/tests" +import { generator, testContainerUtils } from "@budibase/backend-core/tests" +import { startContainer } from "." + +let ports: Promise class MySQLWaitStrategy extends AbstractWaitStrategy { async waitUntilReady(container: any, boundPorts: any, startTime?: Date) { @@ -25,26 +28,23 @@ class MySQLWaitStrategy extends AbstractWaitStrategy { } export async function getDatasource(): Promise { - let container = new GenericContainer("mysql:8.3") - .withExposedPorts(3306) - .withEnvironment({ MYSQL_ROOT_PASSWORD: "password" }) - .withWaitStrategy(new MySQLWaitStrategy().withStartupTimeout(10000)) - - if (process.env.REUSE_CONTAINERS) { - container = container.withReuse() + if (!ports) { + ports = startContainer( + new GenericContainer("mysql:8.3") + .withExposedPorts(3306) + .withEnvironment({ MYSQL_ROOT_PASSWORD: "password" }) + .withWaitStrategy(new MySQLWaitStrategy().withStartupTimeout(10000)) + ) } - const startedContainer = await container.start() - - const host = startedContainer.getHost() - const port = startedContainer.getMappedPort(3306) + const port = (await ports).find(x => x.container === 3306)?.host const datasource: Datasource = { type: "datasource_plus", source: SourceName.MYSQL, plus: true, config: { - host, + host: "127.0.0.1", port, user: "root", password: "password", diff --git a/packages/server/src/integrations/tests/utils/postgres.ts b/packages/server/src/integrations/tests/utils/postgres.ts index 237bc19a17..4191b107e9 100644 --- a/packages/server/src/integrations/tests/utils/postgres.ts +++ b/packages/server/src/integrations/tests/utils/postgres.ts @@ -1,33 +1,33 @@ import { Datasource, SourceName } from "@budibase/types" import { GenericContainer, Wait } from "testcontainers" import pg from "pg" -import { generator } from "@budibase/backend-core/tests" +import { generator, testContainerUtils } from "@budibase/backend-core/tests" +import { startContainer } from "." + +let ports: Promise export async function getDatasource(): Promise { - let container = new GenericContainer("postgres:16.1-bullseye") - .withExposedPorts(5432) - .withEnvironment({ POSTGRES_PASSWORD: "password" }) - .withWaitStrategy( - Wait.forSuccessfulCommand( - "pg_isready -h localhost -p 5432" - ).withStartupTimeout(10000) + if (!ports) { + ports = startContainer( + new GenericContainer("postgres:16.1-bullseye") + .withExposedPorts(5432) + .withEnvironment({ POSTGRES_PASSWORD: "password" }) + .withWaitStrategy( + Wait.forSuccessfulCommand( + "pg_isready -h localhost -p 5432" + ).withStartupTimeout(10000) + ) ) - - if (process.env.REUSE_CONTAINERS) { - container = container.withReuse() } - const startedContainer = await container.start() - - const host = startedContainer.getHost() - const port = startedContainer.getMappedPort(5432) + const port = (await ports).find(x => x.container === 5432)?.host const datasource: Datasource = { type: "datasource_plus", source: SourceName.POSTGRES, plus: true, config: { - host, + host: "127.0.0.1", port, database: "postgres", user: "postgres", From 567c3a6d10f728da25f28078fa5ee2487c36f087 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Tue, 2 Apr 2024 14:46:28 +0100 Subject: [PATCH 23/97] Add optional chaining to fix log out and log in issue --- packages/builder/src/components/common/HelpMenu.svelte | 2 +- packages/builder/src/stores/portal/apps.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/builder/src/components/common/HelpMenu.svelte b/packages/builder/src/components/common/HelpMenu.svelte index baff9a5a27..63156676d2 100644 --- a/packages/builder/src/components/common/HelpMenu.svelte +++ b/packages/builder/src/components/common/HelpMenu.svelte @@ -5,7 +5,7 @@ import { licensing } from "stores/portal" import { isPremiumOrAbove } from "helpers/planTitle" - $: premiumOrAboveLicense = isPremiumOrAbove($licensing?.license.plan.type) + $: premiumOrAboveLicense = isPremiumOrAbove($licensing?.license?.plan?.type) let show let hide diff --git a/packages/builder/src/stores/portal/apps.js b/packages/builder/src/stores/portal/apps.js index 84c4348075..6af9fa56ac 100644 --- a/packages/builder/src/stores/portal/apps.js +++ b/packages/builder/src/stores/portal/apps.js @@ -148,7 +148,7 @@ export const enrichedApps = derived([appsStore, auth], ([$store, $auth]) => { deployed: app.status === AppStatus.DEPLOYED, lockedYou: app.lockedBy && app.lockedBy.email === $auth.user?.email, lockedOther: app.lockedBy && app.lockedBy.email !== $auth.user?.email, - favourite: $auth?.user.appFavourites?.includes(app.appId), + favourite: $auth.user?.appFavourites?.includes(app.appId), })) : [] From 3e3e761f881f60e0c12ff027745d0a172fee6fc5 Mon Sep 17 00:00:00 2001 From: Budibase Staging Release Bot <> Date: Tue, 2 Apr 2024 13:55:58 +0000 Subject: [PATCH 24/97] Bump version to 2.22.14 --- lerna.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lerna.json b/lerna.json index bacdcb782f..906e9fba50 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "2.22.13", + "version": "2.22.14", "npmClient": "yarn", "packages": [ "packages/*", From 9762b2df0cf3bf4ec71a8b64d007b8f39bb36541 Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Tue, 2 Apr 2024 16:14:46 +0100 Subject: [PATCH 25/97] acct-portal-sub --- packages/account-portal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/account-portal b/packages/account-portal index 63ce32bca8..360ad2dc29 160000 --- a/packages/account-portal +++ b/packages/account-portal @@ -1 +1 @@ -Subproject commit 63ce32bca871f0a752323f5f7ebb5ec16bbbacc3 +Subproject commit 360ad2dc29c3f1fd5a1182ae258c45666b7f5eb1 From 8c326b501befe8716355b81ec4b6dad4881e6cc5 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Tue, 2 Apr 2024 17:12:31 +0100 Subject: [PATCH 26/97] Adding support for oneOf ID search of users, today the relationship picker attempts to use this for the users table, but it was not supported. --- packages/backend-core/src/users/users.ts | 13 +++++++++---- .../worker/src/api/controllers/global/users.ts | 2 +- .../src/api/routes/global/tests/users.spec.ts | 18 ++++++++++++++++++ 3 files changed, 28 insertions(+), 5 deletions(-) diff --git a/packages/backend-core/src/users/users.ts b/packages/backend-core/src/users/users.ts index 638da4a5b1..48920a3771 100644 --- a/packages/backend-core/src/users/users.ts +++ b/packages/backend-core/src/users/users.ts @@ -14,16 +14,16 @@ import { } from "../db" import { BulkDocsResponse, + ContextUser, + CouchFindOptions, + DatabaseQueryOpts, SearchQuery, SearchQueryOperators, SearchUsersRequest, User, - ContextUser, - DatabaseQueryOpts, - CouchFindOptions, } from "@budibase/types" -import { getGlobalDB } from "../context" import * as context from "../context" +import { getGlobalDB } from "../context" import { isCreator } from "./utils" import { UserDB } from "./db" @@ -48,6 +48,7 @@ export function isSupportedUserSearch(query: SearchQuery) { const allowed = [ { op: SearchQueryOperators.STRING, key: "email" }, { op: SearchQueryOperators.EQUAL, key: "_id" }, + { op: SearchQueryOperators.ONE_OF, key: "_id" }, ] for (let [key, operation] of Object.entries(query)) { if (typeof operation !== "object") { @@ -285,6 +286,10 @@ export async function paginatedUsers({ } else if (query?.string?.email) { userList = await searchGlobalUsersByEmail(query?.string?.email, opts) property = "email" + } else if (query?.oneOf?._id) { + userList = await bulkGetGlobalUsersById(query?.oneOf?._id, { + cleanup: true, + }) } else { // no search, query allDocs const response = await db.allDocs(getGlobalUserParams(null, opts)) diff --git a/packages/worker/src/api/controllers/global/users.ts b/packages/worker/src/api/controllers/global/users.ts index 93f35b4c37..0c1342fa08 100644 --- a/packages/worker/src/api/controllers/global/users.ts +++ b/packages/worker/src/api/controllers/global/users.ts @@ -229,7 +229,7 @@ export const search = async (ctx: Ctx) => { } // Validate we aren't trying to search on any illegal fields if (!userSdk.core.isSupportedUserSearch(body.query)) { - ctx.throw(400, "Can only search by string.email or equal._id") + ctx.throw(400, "Can only search by string.email, equal._id or oneOf._id") } } diff --git a/packages/worker/src/api/routes/global/tests/users.spec.ts b/packages/worker/src/api/routes/global/tests/users.spec.ts index 2198757be1..fe6089a621 100644 --- a/packages/worker/src/api/routes/global/tests/users.spec.ts +++ b/packages/worker/src/api/routes/global/tests/users.spec.ts @@ -649,6 +649,24 @@ describe("/api/global/users", () => { expect(response.body.data[0]._id).toBe(user._id) }) + it("should be able to search by oneOf _id", async () => { + const [user, user2, user3] = await Promise.all([ + config.createUser(), + config.createUser(), + config.createUser(), + ]) + const response = await config.api.users.searchUsers({ + query: { oneOf: { _id: [user._id, user2._id] } }, + }) + expect(response.body.data.length).toBe(2) + const foundUserIds = response.body.data.map((user: User) => user._id) + expect(foundUserIds).toContain(user._id) + expect(foundUserIds).toContain(user2._id) + expect( + response.body.data.find((user: User) => user._id === user3._id) + ).toBeUndefined() + }) + it("should be able to search by _id with numeric prefixing", async () => { const user = await config.createUser() const response = await config.api.users.searchUsers({ From 6b1829a7fcaff5f7662e24da4ec2e78153ef02cc Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Tue, 2 Apr 2024 17:24:30 +0100 Subject: [PATCH 27/97] remove QA core --- .eslintrc.json | 2 - .github/workflows/budibase_ci.yml | 29 - .gitignore | 1 - package.json | 8 +- qa-core/.gitignore | 5 - qa-core/README.md | 28 - qa-core/jest.config.ts | 21 - qa-core/package.json | 49 - qa-core/scripts/createEnv.js | 26 - qa-core/scripts/createUser.js | 49 - qa-core/scripts/testResultsWebhook.js | 130 - .../src/account-api/api/AccountInternalAPI.ts | 20 - .../api/AccountInternalAPIClient.ts | 89 - .../src/account-api/api/apis/AccountAPI.ts | 123 - qa-core/src/account-api/api/apis/AuthAPI.ts | 68 - qa-core/src/account-api/api/apis/BaseAPI.ts | 20 - .../src/account-api/api/apis/LicenseAPI.ts | 140 - qa-core/src/account-api/api/apis/StripeAPI.ts | 74 - qa-core/src/account-api/api/apis/index.ts | 4 - qa-core/src/account-api/api/index.ts | 1 - .../account-api/config/TestConfiguration.ts | 29 - qa-core/src/account-api/fixtures/accounts.ts | 24 - qa-core/src/account-api/fixtures/index.ts | 1 - qa-core/src/account-api/index.ts | 1 - .../accounts/accounts.cloud.internal.spec.ts | 32 - .../tests/accounts/accounts.cloud.spec.ts | 102 - .../account-api/tests/auth/auth.cloud.spec.ts | 46 - .../tests/licensing/license.activate.spec.ts | 68 - .../tests/licensing/license.manage.spec.ts | 116 - .../tests/licensing/offline.spec.ts | 79 - qa-core/src/environment.ts | 34 - .../external-schema/mssql.integration.spec.ts | 112 - .../external-schema/mysql.integration.spec.ts | 106 - .../postgres.integration.spec.ts | 376 -- .../validators/arango.integration.spec.ts | 77 - .../validators/couch.integration.spec.ts | 67 - .../validators/dynamodb.integration.spec.ts | 63 - .../validators/elastic.integration.spec.ts | 34 - .../validators/mongo.integration.spec.ts | 100 - .../validators/mssql.integration.spec.ts | 65 - .../validators/mysql.integration.spec.ts | 68 - .../validators/postgres.integration.spec.ts | 54 - .../validators/redis.integration.spec.ts | 72 - .../validators/s3.integration.spec.ts | 52 - .../internal-api/api/BudibaseInternalAPI.ts | 54 - .../api/BudibaseInternalAPIClient.ts | 80 - qa-core/src/internal-api/api/apis/AppAPI.ts | 152 - qa-core/src/internal-api/api/apis/AuthAPI.ts | 39 - qa-core/src/internal-api/api/apis/BaseAPI.ts | 56 - .../internal-api/api/apis/DatasourcesAPI.ts | 62 - .../internal-api/api/apis/EnvironmentAPI.ts | 21 - .../internal-api/api/apis/IntegrationsAPI.ts | 16 - .../src/internal-api/api/apis/LicenseAPI.ts | 63 - .../internal-api/api/apis/PermissionsAPI.ts | 14 - .../src/internal-api/api/apis/QueriesAPI.ts | 25 - qa-core/src/internal-api/api/apis/RoleAPI.ts | 20 - qa-core/src/internal-api/api/apis/RowAPI.ts | 57 - .../src/internal-api/api/apis/ScreenAPI.ts | 23 - qa-core/src/internal-api/api/apis/SelfAPI.ts | 29 - qa-core/src/internal-api/api/apis/TableAPI.ts | 48 - qa-core/src/internal-api/api/apis/UserAPI.ts | 107 - qa-core/src/internal-api/api/index.ts | 1 - .../internal-api/config/TestConfiguration.ts | 25 - qa-core/src/internal-api/config/index.ts | 1 - qa-core/src/internal-api/fixtures/accounts.ts | 20 - .../src/internal-api/fixtures/applications.ts | 27 - .../src/internal-api/fixtures/datasources.ts | 122 - qa-core/src/internal-api/fixtures/index.ts | 8 - qa-core/src/internal-api/fixtures/queries.ts | 123 - qa-core/src/internal-api/fixtures/rows.ts | 32 - qa-core/src/internal-api/fixtures/screens.ts | 33 - qa-core/src/internal-api/fixtures/tables.ts | 30 - qa-core/src/internal-api/fixtures/users.ts | 82 - qa-core/src/internal-api/index.ts | 1 - .../dataSources/mariaDB.integration.spec.ts | 106 - .../dataSources/mongoDB.integration.spec.ts | 69 - .../postgresSQL.integration.spec.ts | 69 - .../dataSources/restAPI.integration.spec.ts | 69 - .../tests/users/customRoles.spec.ts | 315 - .../tests/users/screenAccess.spec.ts | 175 - .../tests/users/tableAccess.spec.ts | 123 - qa-core/src/jest/globalSetup.ts | 114 - qa-core/src/jest/globalTeardown.ts | 32 - qa-core/src/jest/jest.extends.ts | 23 - qa-core/src/jest/jestSetup.ts | 3 - .../src/public-api/api/BudibasePublicAPI.ts | 23 - .../public-api/api/BudibasePublicAPIClient.ts | 79 - qa-core/src/public-api/api/apis/AppAPI.ts | 68 - qa-core/src/public-api/api/apis/RowAPI.ts | 56 - qa-core/src/public-api/api/apis/TableAPI.ts | 40 - qa-core/src/public-api/api/apis/UserAPI.ts | 36 - qa-core/src/public-api/api/index.ts | 1 - .../public-api/config/TestConfiguration.ts | 33 - qa-core/src/public-api/config/index.ts | 1 - qa-core/src/public-api/fixtures/accounts.ts | 20 - .../src/public-api/fixtures/applications.ts | 10 - qa-core/src/public-api/fixtures/index.ts | 5 - qa-core/src/public-api/fixtures/rows.ts | 10 - qa-core/src/public-api/fixtures/tables.ts | 47 - qa-core/src/public-api/fixtures/users.ts | 22 - qa-core/src/public-api/index.ts | 1 - .../src/shared/BudibaseTestConfiguration.ts | 98 - qa-core/src/shared/generator.ts | 3 - qa-core/src/shared/index.ts | 2 - qa-core/src/types/api.ts | 6 - qa-core/src/types/apiKeyResponse.ts | 6 - qa-core/src/types/appPackage.ts | 8 - qa-core/src/types/datasources.ts | 22 - qa-core/src/types/deploy.ts | 5 - qa-core/src/types/index.ts | 13 - qa-core/src/types/newAccount.ts | 5 - qa-core/src/types/responseMessage.ts | 3 - qa-core/src/types/routing.ts | 17 - qa-core/src/types/screens.ts | 32 - qa-core/src/types/state.ts | 8 - qa-core/src/types/unpublishAppResponse.ts | 7 - qa-core/tsconfig.json | 27 - qa-core/yarn.lock | 5099 ----------------- 118 files changed, 4 insertions(+), 10943 deletions(-) delete mode 100644 qa-core/.gitignore delete mode 100644 qa-core/README.md delete mode 100644 qa-core/jest.config.ts delete mode 100644 qa-core/package.json delete mode 100644 qa-core/scripts/createEnv.js delete mode 100644 qa-core/scripts/createUser.js delete mode 100644 qa-core/scripts/testResultsWebhook.js delete mode 100644 qa-core/src/account-api/api/AccountInternalAPI.ts delete mode 100644 qa-core/src/account-api/api/AccountInternalAPIClient.ts delete mode 100644 qa-core/src/account-api/api/apis/AccountAPI.ts delete mode 100644 qa-core/src/account-api/api/apis/AuthAPI.ts delete mode 100644 qa-core/src/account-api/api/apis/BaseAPI.ts delete mode 100644 qa-core/src/account-api/api/apis/LicenseAPI.ts delete mode 100644 qa-core/src/account-api/api/apis/StripeAPI.ts delete mode 100644 qa-core/src/account-api/api/apis/index.ts delete mode 100644 qa-core/src/account-api/api/index.ts delete mode 100644 qa-core/src/account-api/config/TestConfiguration.ts delete mode 100644 qa-core/src/account-api/fixtures/accounts.ts delete mode 100644 qa-core/src/account-api/fixtures/index.ts delete mode 100644 qa-core/src/account-api/index.ts delete mode 100644 qa-core/src/account-api/tests/accounts/accounts.cloud.internal.spec.ts delete mode 100644 qa-core/src/account-api/tests/accounts/accounts.cloud.spec.ts delete mode 100644 qa-core/src/account-api/tests/auth/auth.cloud.spec.ts delete mode 100644 qa-core/src/account-api/tests/licensing/license.activate.spec.ts delete mode 100644 qa-core/src/account-api/tests/licensing/license.manage.spec.ts delete mode 100644 qa-core/src/account-api/tests/licensing/offline.spec.ts delete mode 100644 qa-core/src/environment.ts delete mode 100644 qa-core/src/integrations/external-schema/mssql.integration.spec.ts delete mode 100644 qa-core/src/integrations/external-schema/mysql.integration.spec.ts delete mode 100644 qa-core/src/integrations/external-schema/postgres.integration.spec.ts delete mode 100644 qa-core/src/integrations/validators/arango.integration.spec.ts delete mode 100644 qa-core/src/integrations/validators/couch.integration.spec.ts delete mode 100644 qa-core/src/integrations/validators/dynamodb.integration.spec.ts delete mode 100644 qa-core/src/integrations/validators/elastic.integration.spec.ts delete mode 100644 qa-core/src/integrations/validators/mongo.integration.spec.ts delete mode 100644 qa-core/src/integrations/validators/mssql.integration.spec.ts delete mode 100644 qa-core/src/integrations/validators/mysql.integration.spec.ts delete mode 100644 qa-core/src/integrations/validators/postgres.integration.spec.ts delete mode 100644 qa-core/src/integrations/validators/redis.integration.spec.ts delete mode 100644 qa-core/src/integrations/validators/s3.integration.spec.ts delete mode 100644 qa-core/src/internal-api/api/BudibaseInternalAPI.ts delete mode 100644 qa-core/src/internal-api/api/BudibaseInternalAPIClient.ts delete mode 100644 qa-core/src/internal-api/api/apis/AppAPI.ts delete mode 100644 qa-core/src/internal-api/api/apis/AuthAPI.ts delete mode 100644 qa-core/src/internal-api/api/apis/BaseAPI.ts delete mode 100644 qa-core/src/internal-api/api/apis/DatasourcesAPI.ts delete mode 100644 qa-core/src/internal-api/api/apis/EnvironmentAPI.ts delete mode 100644 qa-core/src/internal-api/api/apis/IntegrationsAPI.ts delete mode 100644 qa-core/src/internal-api/api/apis/LicenseAPI.ts delete mode 100644 qa-core/src/internal-api/api/apis/PermissionsAPI.ts delete mode 100644 qa-core/src/internal-api/api/apis/QueriesAPI.ts delete mode 100644 qa-core/src/internal-api/api/apis/RoleAPI.ts delete mode 100644 qa-core/src/internal-api/api/apis/RowAPI.ts delete mode 100644 qa-core/src/internal-api/api/apis/ScreenAPI.ts delete mode 100644 qa-core/src/internal-api/api/apis/SelfAPI.ts delete mode 100644 qa-core/src/internal-api/api/apis/TableAPI.ts delete mode 100644 qa-core/src/internal-api/api/apis/UserAPI.ts delete mode 100644 qa-core/src/internal-api/api/index.ts delete mode 100644 qa-core/src/internal-api/config/TestConfiguration.ts delete mode 100644 qa-core/src/internal-api/config/index.ts delete mode 100644 qa-core/src/internal-api/fixtures/accounts.ts delete mode 100644 qa-core/src/internal-api/fixtures/applications.ts delete mode 100644 qa-core/src/internal-api/fixtures/datasources.ts delete mode 100644 qa-core/src/internal-api/fixtures/index.ts delete mode 100644 qa-core/src/internal-api/fixtures/queries.ts delete mode 100644 qa-core/src/internal-api/fixtures/rows.ts delete mode 100644 qa-core/src/internal-api/fixtures/screens.ts delete mode 100644 qa-core/src/internal-api/fixtures/tables.ts delete mode 100644 qa-core/src/internal-api/fixtures/users.ts delete mode 100644 qa-core/src/internal-api/index.ts delete mode 100644 qa-core/src/internal-api/tests/dataSources/mariaDB.integration.spec.ts delete mode 100644 qa-core/src/internal-api/tests/dataSources/mongoDB.integration.spec.ts delete mode 100644 qa-core/src/internal-api/tests/dataSources/postgresSQL.integration.spec.ts delete mode 100644 qa-core/src/internal-api/tests/dataSources/restAPI.integration.spec.ts delete mode 100644 qa-core/src/internal-api/tests/users/customRoles.spec.ts delete mode 100644 qa-core/src/internal-api/tests/users/screenAccess.spec.ts delete mode 100644 qa-core/src/internal-api/tests/users/tableAccess.spec.ts delete mode 100644 qa-core/src/jest/globalSetup.ts delete mode 100644 qa-core/src/jest/globalTeardown.ts delete mode 100644 qa-core/src/jest/jest.extends.ts delete mode 100644 qa-core/src/jest/jestSetup.ts delete mode 100644 qa-core/src/public-api/api/BudibasePublicAPI.ts delete mode 100644 qa-core/src/public-api/api/BudibasePublicAPIClient.ts delete mode 100644 qa-core/src/public-api/api/apis/AppAPI.ts delete mode 100644 qa-core/src/public-api/api/apis/RowAPI.ts delete mode 100644 qa-core/src/public-api/api/apis/TableAPI.ts delete mode 100644 qa-core/src/public-api/api/apis/UserAPI.ts delete mode 100644 qa-core/src/public-api/api/index.ts delete mode 100644 qa-core/src/public-api/config/TestConfiguration.ts delete mode 100644 qa-core/src/public-api/config/index.ts delete mode 100644 qa-core/src/public-api/fixtures/accounts.ts delete mode 100644 qa-core/src/public-api/fixtures/applications.ts delete mode 100644 qa-core/src/public-api/fixtures/index.ts delete mode 100644 qa-core/src/public-api/fixtures/rows.ts delete mode 100644 qa-core/src/public-api/fixtures/tables.ts delete mode 100644 qa-core/src/public-api/fixtures/users.ts delete mode 100644 qa-core/src/public-api/index.ts delete mode 100644 qa-core/src/shared/BudibaseTestConfiguration.ts delete mode 100644 qa-core/src/shared/generator.ts delete mode 100644 qa-core/src/shared/index.ts delete mode 100644 qa-core/src/types/api.ts delete mode 100644 qa-core/src/types/apiKeyResponse.ts delete mode 100644 qa-core/src/types/appPackage.ts delete mode 100644 qa-core/src/types/datasources.ts delete mode 100644 qa-core/src/types/deploy.ts delete mode 100644 qa-core/src/types/index.ts delete mode 100644 qa-core/src/types/newAccount.ts delete mode 100644 qa-core/src/types/responseMessage.ts delete mode 100644 qa-core/src/types/routing.ts delete mode 100644 qa-core/src/types/screens.ts delete mode 100644 qa-core/src/types/state.ts delete mode 100644 qa-core/src/types/unpublishAppResponse.ts delete mode 100644 qa-core/tsconfig.json delete mode 100644 qa-core/yarn.lock diff --git a/.eslintrc.json b/.eslintrc.json index 2a40c6cc29..525072dc6c 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -34,7 +34,6 @@ }, { "files": ["**/*.ts"], - "excludedFiles": ["qa-core/**"], "parser": "@typescript-eslint/parser", "plugins": ["@typescript-eslint"], "extends": ["eslint:recommended"], @@ -49,7 +48,6 @@ }, { "files": ["**/*.spec.ts"], - "excludedFiles": ["qa-core/**"], "parser": "@typescript-eslint/parser", "plugins": ["jest", "@typescript-eslint"], "extends": ["eslint:recommended", "plugin:jest/recommended"], diff --git a/.github/workflows/budibase_ci.yml b/.github/workflows/budibase_ci.yml index 030ad6578e..4ae0766242 100644 --- a/.github/workflows/budibase_ci.yml +++ b/.github/workflows/budibase_ci.yml @@ -175,35 +175,6 @@ jobs: yarn test --scope=@budibase/server fi - integration-test: - runs-on: ubuntu-latest - steps: - - name: Checkout repo - uses: actions/checkout@v4 - with: - submodules: ${{ env.IS_OSS_CONTRIBUTOR == 'false' }} - token: ${{ secrets.PERSONAL_ACCESS_TOKEN || github.token }} - - - name: Use Node.js 20.x - uses: actions/setup-node@v4 - with: - node-version: 20.x - cache: yarn - - run: yarn --frozen-lockfile - - name: Build packages - run: yarn build --scope @budibase/server --scope @budibase/worker - - name: Build backend-core for OSS contributor (required for pro) - if: ${{ env.IS_OSS_CONTRIBUTOR == 'true' }} - run: yarn build --scope @budibase/backend-core - - name: Run tests - run: | - cd qa-core - yarn setup - yarn serve:test:self:ci - env: - BB_ADMIN_USER_EMAIL: admin - BB_ADMIN_USER_PASSWORD: admin - check-pro-submodule: runs-on: ubuntu-latest if: inputs.run_as_oss != true && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'Budibase/budibase') diff --git a/.gitignore b/.gitignore index 661c60e95e..b68ddd975f 100644 --- a/.gitignore +++ b/.gitignore @@ -69,7 +69,6 @@ typings/ # dotenv environment variables file .env -!qa-core/.env !hosting/.env # parcel-bundler cache (https://parceljs.org/) diff --git a/package.json b/package.json index 32693a0b6f..c927002c88 100644 --- a/package.json +++ b/package.json @@ -58,11 +58,11 @@ "dev:built": "yarn run kill-all && cd packages/server && yarn dev:stack:up && cd ../../ && lerna run --stream dev:built", "dev:docker": "yarn build --scope @budibase/server --scope @budibase/worker && docker-compose -f hosting/docker-compose.build.yaml -f hosting/docker-compose.dev.yaml --env-file hosting/.env up --build --scale proxy-service=0", "test": "lerna run --stream test --stream", - "lint:eslint": "eslint packages qa-core --max-warnings=0", - "lint:prettier": "prettier --check \"packages/**/*.{js,ts,svelte}\" && prettier --write \"examples/**/*.{js,ts,svelte}\" && prettier --check \"qa-core/**/*.{js,ts,svelte}\"", + "lint:eslint": "eslint packages --max-warnings=0", + "lint:prettier": "prettier --check \"packages/**/*.{js,ts,svelte}\" && prettier --write \"examples/**/*.{js,ts,svelte}\"", "lint": "yarn run lint:eslint && yarn run lint:prettier", - "lint:fix:eslint": "eslint --fix --max-warnings=0 packages qa-core", - "lint:fix:prettier": "prettier --write \"packages/**/*.{js,ts,svelte}\" && prettier --write \"examples/**/*.{js,ts,svelte}\" && prettier --write \"qa-core/**/*.{js,ts,svelte}\"", + "lint:fix:eslint": "eslint --fix --max-warnings=0 packages", + "lint:fix:prettier": "prettier --write \"packages/**/*.{js,ts,svelte}\" && prettier --write \"examples/**/*.{js,ts,svelte}\"", "lint:fix": "yarn run lint:fix:eslint && yarn run lint:fix:prettier", "build:specs": "lerna run --stream specs", "build:docker:airgap": "node hosting/scripts/airgapped/airgappedDockerBuild", diff --git a/qa-core/.gitignore b/qa-core/.gitignore deleted file mode 100644 index 4c442c31dc..0000000000 --- a/qa-core/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -node_modules/ -.env -watchtower-hook.json -dist/ -testResults.json diff --git a/qa-core/README.md b/qa-core/README.md deleted file mode 100644 index 8390f276d9..0000000000 --- a/qa-core/README.md +++ /dev/null @@ -1,28 +0,0 @@ -# QA Core API Tests - -The QA Core API tests are a jest suite that run directly against the budibase backend APIs. - -## Auto Setup - -You can run the whole test suite with one command, that spins up the budibase server and runs the jest tests: - -`yarn test:ci` - -## Setup Server - -You can run the local development stack by following the instructions on the main readme. - -## Run Tests - -If you configured the server using the previous command, you can run the whole test suite by using: - -`yarn test` - -for watch mode, where the tests will run on every change: - -`yarn test:watch` - -To run tests locally against a cloud service you can update the configuration inside the `.env` file and run: - -`yarn test` - diff --git a/qa-core/jest.config.ts b/qa-core/jest.config.ts deleted file mode 100644 index eb942eca97..0000000000 --- a/qa-core/jest.config.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { Config } from "@jest/types" - -const config: Config.InitialOptions = { - preset: "ts-jest", - setupFiles: ["./src/jest/jestSetup.ts"], - setupFilesAfterEnv: ["./src/jest/jest.extends.ts"], - testEnvironment: "node", - transform: { - "^.+\\.ts?$": "@swc/jest", - }, - globalSetup: "./src/jest/globalSetup.ts", - globalTeardown: "./src/jest/globalTeardown.ts", - moduleNameMapper: { - "@budibase/types": "/../packages/types/src", - "@budibase/server": "/../packages/server/src", - "@budibase/backend-core": "/../packages/backend-core/src", - "@budibase/backend-core/(.*)": "/../packages/backend-core/$1", - }, -} - -export default config diff --git a/qa-core/package.json b/qa-core/package.json deleted file mode 100644 index 2d040af0ea..0000000000 --- a/qa-core/package.json +++ /dev/null @@ -1,49 +0,0 @@ -{ - "name": "@budibase/qa-core", - "email": "hi@budibase.com", - "version": "0.0.1", - "main": "index.js", - "description": "Budibase Integration Test Suite", - "repository": { - "type": "git", - "url": "https://github.com/Budibase/budibase.git" - }, - "scripts": { - "setup": "yarn && node scripts/createEnv.js", - "user": "yarn && node scripts/createEnv.js && node scripts/createUser.js", - "test": "jest --runInBand --json --outputFile=testResults.json --forceExit", - "test:watch": "yarn run test --watch", - "test:debug": "DEBUG=1 yarn run test", - "test:notify": "node scripts/testResultsWebhook", - "test:cloud:prod": "yarn run test --testPathIgnorePatterns=\\.integration\\.", - "test:cloud:qa": "yarn run test", - "test:self:ci": "yarn run test --testPathIgnorePatterns=\\.integration\\. \\.cloud\\. \\.licensing\\.", - "serve:test:self:ci": "start-server-and-test dev:built http://localhost:4001/health test:self:ci", - "serve": "start-server-and-test dev:built http://localhost:4001/health", - "dev:built": "cd ../ && DISABLE_RATE_LIMITING=1 yarn dev:built" - }, - "devDependencies": { - "@budibase/types": "^2.3.17", - "@swc/core": "1.3.71", - "@swc/jest": "0.2.27", - "@trendyol/jest-testcontainers": "2.1.1", - "@types/jest": "29.5.3", - "@types/node-fetch": "2.6.4", - "chance": "1.1.8", - "dotenv": "16.0.1", - "jest": "29.7.0", - "prettier": "2.7.1", - "start-server-and-test": "1.14.0", - "timekeeper": "2.2.0", - "ts-jest": "29.1.1", - "ts-node": "10.8.1", - "tsconfig-paths": "4.0.0", - "typescript": "5.2.2" - }, - "dependencies": { - "@budibase/backend-core": "^2.3.17", - "form-data": "^4.0.0", - "node-fetch": "2.6.7", - "stripe": "^14.11.0" - } -} diff --git a/qa-core/scripts/createEnv.js b/qa-core/scripts/createEnv.js deleted file mode 100644 index 64b9664049..0000000000 --- a/qa-core/scripts/createEnv.js +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/env node -const path = require("path") -const fs = require("fs") - -function init() { - const envFilePath = path.join(process.cwd(), ".env") - if (!fs.existsSync(envFilePath)) { - const envFileJson = { - BUDIBASE_URL: "http://localhost:10000", - ACCOUNT_PORTAL_URL: "http://localhost:10001", - ACCOUNT_PORTAL_API_KEY: "budibase", - BB_ADMIN_USER_EMAIL: "admin", - BB_ADMIN_USER_PASSWORD: "admin", - LOG_LEVEL: "info", - JEST_TIMEOUT: "60000", - DISABLE_PINO_LOGGER: "1", - } - let envFile = "" - Object.keys(envFileJson).forEach(key => { - envFile += `${key}=${envFileJson[key]}\n` - }) - fs.writeFileSync(envFilePath, envFile) - } -} - -init() diff --git a/qa-core/scripts/createUser.js b/qa-core/scripts/createUser.js deleted file mode 100644 index 200bf91fc4..0000000000 --- a/qa-core/scripts/createUser.js +++ /dev/null @@ -1,49 +0,0 @@ -const dotenv = require("dotenv") -const { join } = require("path") -const fs = require("fs") -const fetch = require("node-fetch") - -function getVarFromDotEnv(path, varName) { - const parsed = dotenv.parse(fs.readFileSync(path)) - return parsed[varName] -} - -async function createUser() { - const serverPath = join(__dirname, "..", "..", "packages", "server", ".env") - const qaCorePath = join(__dirname, "..", ".env") - const apiKey = getVarFromDotEnv(serverPath, "INTERNAL_API_KEY") - const username = getVarFromDotEnv(qaCorePath, "BB_ADMIN_USER_EMAIL") - const password = getVarFromDotEnv(qaCorePath, "BB_ADMIN_USER_PASSWORD") - const url = getVarFromDotEnv(qaCorePath, "BUDIBASE_URL") - const resp = await fetch(`${url}/api/public/v1/users`, { - method: "POST", - headers: { - "Content-Type": "application/json", - "x-budibase-api-key": apiKey, - }, - body: JSON.stringify({ - email: username, - password, - builder: { - global: true, - }, - admin: { - global: true, - }, - roles: {}, - }), - }) - if (resp.status !== 200) { - throw new Error(await resp.text()) - } else { - return await resp.json() - } -} - -createUser() - .then(() => { - console.log("User created - ready to use") - }) - .catch(err => { - console.error("Failed to create user - ", err) - }) diff --git a/qa-core/scripts/testResultsWebhook.js b/qa-core/scripts/testResultsWebhook.js deleted file mode 100644 index 5fbdd3a32e..0000000000 --- a/qa-core/scripts/testResultsWebhook.js +++ /dev/null @@ -1,130 +0,0 @@ -#!/usr/bin/env node - -const fetch = require("node-fetch") -const path = require("path") -const fs = require("fs") - -const WEBHOOK_URL = process.env.WEBHOOK_URL -const GIT_SHA = process.env.GITHUB_SHA -const GITHUB_ACTIONS_RUN_URL = process.env.GITHUB_ACTIONS_RUN_URL - -async function generateReport() { - // read the report file - const REPORT_PATH = path.resolve(__dirname, "..", "testResults.json") - const report = fs.readFileSync(REPORT_PATH, "utf-8") - return JSON.parse(report) -} - -const env = process.argv.slice(2)[0] - -if (!env) { - throw new Error("environment argument is required") -} - -async function discordResultsNotification(report) { - const { - numTotalTestSuites, - numTotalTests, - numPassedTests, - numPendingTests, - numFailedTests, - success, - startTime, - endTime, - } = report - - const OUTCOME = success ? "success" : "failure" - - const options = { - method: "POST", - headers: { - "Content-Type": "application/json", - Accept: "application/json", - }, - body: JSON.stringify({ - content: `**Tests Status**: ${OUTCOME}`, - embeds: [ - { - title: `Budi QA Bot - ${env}`, - description: `API Integration Tests`, - url: GITHUB_ACTIONS_RUN_URL, - color: OUTCOME === "success" ? 3066993 : 15548997, - timestamp: new Date(), - footer: { - icon_url: "http://bbui.budibase.com/budibase-logo.png", - text: "Budibase QA Bot", - }, - thumbnail: { - url: "http://bbui.budibase.com/budibase-logo.png", - }, - author: { - name: "Budibase QA Bot", - url: "https://discordapp.com", - icon_url: "http://bbui.budibase.com/budibase-logo.png", - }, - fields: [ - { - name: "Commit", - value: `https://github.com/Budibase/budibase/commit/${GIT_SHA}`, - }, - { - name: "Github Actions Run URL", - value: GITHUB_ACTIONS_RUN_URL || "None Supplied", - }, - { - name: "Test Suites", - value: numTotalTestSuites, - }, - { - name: "Tests", - value: numTotalTests, - }, - { - name: "Passed", - value: numPassedTests, - }, - { - name: "Pending", - value: numPendingTests, - }, - { - name: "Failures", - value: numFailedTests, - }, - { - name: "Duration", - value: endTime - ? `${(endTime - startTime) / 1000} Seconds` - : "DNF", - }, - { - name: "Pass Percentage", - value: Math.floor((numPassedTests / numTotalTests) * 100), - }, - ], - }, - ], - }), - } - - // Only post in discord when tests fail - if (success) { - return - } - - const response = await fetch(WEBHOOK_URL, options) - - if (response.status >= 201) { - const text = await response.text() - console.error( - `Error sending discord webhook. \nStatus: ${response.status}. \nResponse Body: ${text}. \nRequest Body: ${options.body}` - ) - } -} - -async function run() { - const report = await generateReport() - await discordResultsNotification(report) -} - -run() diff --git a/qa-core/src/account-api/api/AccountInternalAPI.ts b/qa-core/src/account-api/api/AccountInternalAPI.ts deleted file mode 100644 index f89bf556f2..0000000000 --- a/qa-core/src/account-api/api/AccountInternalAPI.ts +++ /dev/null @@ -1,20 +0,0 @@ -import AccountInternalAPIClient from "./AccountInternalAPIClient" -import { AccountAPI, LicenseAPI, AuthAPI, StripeAPI } from "./apis" -import { State } from "../../types" - -export default class AccountInternalAPI { - client: AccountInternalAPIClient - - auth: AuthAPI - accounts: AccountAPI - licenses: LicenseAPI - stripe: StripeAPI - - constructor(state: State) { - this.client = new AccountInternalAPIClient(state) - this.auth = new AuthAPI(this.client) - this.accounts = new AccountAPI(this.client) - this.licenses = new LicenseAPI(this.client) - this.stripe = new StripeAPI(this.client) - } -} diff --git a/qa-core/src/account-api/api/AccountInternalAPIClient.ts b/qa-core/src/account-api/api/AccountInternalAPIClient.ts deleted file mode 100644 index 9c5fbec3af..0000000000 --- a/qa-core/src/account-api/api/AccountInternalAPIClient.ts +++ /dev/null @@ -1,89 +0,0 @@ -import fetch, { Response, HeadersInit } from "node-fetch" -import env from "../../environment" -import { State } from "../../types" -import { Header } from "@budibase/backend-core" - -type APIMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE" - -interface ApiOptions { - method?: APIMethod - body?: object - headers?: HeadersInit | undefined - internal?: boolean -} - -export default class AccountInternalAPIClient { - state: State - host: string - - constructor(state: State) { - if (!env.ACCOUNT_PORTAL_URL) { - throw new Error("Must set ACCOUNT_PORTAL_URL env var") - } - if (!env.ACCOUNT_PORTAL_API_KEY) { - throw new Error("Must set ACCOUNT_PORTAL_API_KEY env var") - } - this.host = `${env.ACCOUNT_PORTAL_URL}` - this.state = state - } - - apiCall = - (method: APIMethod) => - async (url = "", options: ApiOptions = {}): Promise<[Response, any]> => { - const requestOptions = { - method, - body: JSON.stringify(options.body), - headers: { - "Content-Type": "application/json", - Accept: "application/json", - cookie: this.state.cookie, - redirect: "follow", - follow: 20, - ...options.headers, - }, - credentials: "include", - } - - if (options.internal) { - requestOptions.headers = { - ...requestOptions.headers, - ...{ [Header.API_KEY]: env.ACCOUNT_PORTAL_API_KEY }, - cookie: "", - } - } - - // @ts-ignore - const response = await fetch(`${this.host}${url}`, requestOptions) - - let body: any - const contentType = response.headers.get("content-type") - if (contentType && contentType.includes("application/json")) { - body = await response.json() - } else { - body = await response.text() - } - - const data = { - request: requestOptions.body, - response: body, - } - const message = `${method} ${url} - ${response.status}` - - const isDebug = process.env.LOG_LEVEL === "debug" - if (response.status > 499) { - console.error(message, data) - } else if (response.status >= 400) { - console.warn(message, data) - } else if (isDebug) { - console.debug(message, data) - } - - return [response, body] - } - - post = this.apiCall("POST") - get = this.apiCall("GET") - patch = this.apiCall("PATCH") - del = this.apiCall("DELETE") - put = this.apiCall("PUT") -} diff --git a/qa-core/src/account-api/api/apis/AccountAPI.ts b/qa-core/src/account-api/api/apis/AccountAPI.ts deleted file mode 100644 index 13c7e1709d..0000000000 --- a/qa-core/src/account-api/api/apis/AccountAPI.ts +++ /dev/null @@ -1,123 +0,0 @@ -import { Response } from "node-fetch" -import { - Account, - CreateAccountRequest, - SearchAccountsRequest, - SearchAccountsResponse, -} from "@budibase/types" -import AccountInternalAPIClient from "../AccountInternalAPIClient" -import { APIRequestOpts } from "../../../types" -import { Header } from "@budibase/backend-core" -import BaseAPI from "./BaseAPI" - -export default class AccountAPI extends BaseAPI { - client: AccountInternalAPIClient - - constructor(client: AccountInternalAPIClient) { - super() - this.client = client - } - - async validateEmail(email: string, opts: APIRequestOpts = { status: 200 }) { - return this.doRequest(() => { - return this.client.post(`/api/accounts/validate/email`, { - body: { email }, - }) - }, opts) - } - - async validateTenantId( - tenantId: string, - opts: APIRequestOpts = { status: 200 } - ) { - return this.doRequest(() => { - return this.client.post(`/api/accounts/validate/tenantId`, { - body: { tenantId }, - }) - }, opts) - } - - async create( - body: CreateAccountRequest, - opts: APIRequestOpts & { autoVerify: boolean } = { - status: 201, - autoVerify: false, - } - ): Promise<[Response, Account]> { - return this.doRequest(() => { - const headers = { - "no-verify": opts.autoVerify ? "1" : "0", - } - return this.client.post(`/api/accounts`, { - body, - headers, - }) - }, opts) - } - - async delete(accountID: string, opts: APIRequestOpts = { status: 204 }) { - return this.doRequest(() => { - return this.client.del(`/api/accounts/${accountID}`, { - internal: true, - }) - }, opts) - } - - async deleteCurrentAccount(opts: APIRequestOpts = { status: 204 }) { - return this.doRequest(() => { - return this.client.del(`/api/accounts`) - }, opts) - } - - async verifyAccount( - verificationCode: string, - opts: APIRequestOpts = { status: 200 } - ) { - return this.doRequest(() => { - return this.client.post(`/api/accounts/verify`, { - body: { verificationCode }, - }) - }, opts) - } - - async sendVerificationEmail( - email: string, - opts: APIRequestOpts = { status: 200 } - ): Promise<[Response, string]> { - return this.doRequest(async () => { - const [response] = await this.client.post(`/api/accounts/verify/send`, { - body: { email }, - headers: { - [Header.RETURN_VERIFICATION_CODE]: "1", - }, - }) - const code = response.headers.get(Header.VERIFICATION_CODE) - return [response, code] - }, opts) - } - - async search( - searchType: string, - search: "email" | "tenantId", - opts: APIRequestOpts = { status: 200 } - ): Promise<[Response, SearchAccountsResponse]> { - return this.doRequest(() => { - let body: SearchAccountsRequest = {} - if (search === "email") { - body.email = searchType - } else if (search === "tenantId") { - body.tenantId = searchType - } - return this.client.post(`/api/accounts/search`, { - body, - internal: true, - }) - }, opts) - } - - async self(opts: APIRequestOpts = { status: 200 }) { - return this.doRequest(() => { - return this.client.get(`/api/auth/self`) - }, opts) - } -} diff --git a/qa-core/src/account-api/api/apis/AuthAPI.ts b/qa-core/src/account-api/api/apis/AuthAPI.ts deleted file mode 100644 index 304b13db57..0000000000 --- a/qa-core/src/account-api/api/apis/AuthAPI.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { Response } from "node-fetch" -import AccountInternalAPIClient from "../AccountInternalAPIClient" -import { APIRequestOpts } from "../../../types" -import BaseAPI from "./BaseAPI" -import { Header } from "@budibase/backend-core" - -export default class AuthAPI extends BaseAPI { - client: AccountInternalAPIClient - - constructor(client: AccountInternalAPIClient) { - super() - this.client = client - } - - async login( - email: string, - password: string, - opts: APIRequestOpts = { doExpect: true, status: 200 } - ): Promise<[Response, string]> { - return this.doRequest(async () => { - const [res] = await this.client.post(`/api/auth/login`, { - body: { - email: email, - password: password, - }, - }) - const cookie = res.headers.get("set-cookie") - return [res, cookie] - }, opts) - } - - async logout(opts: APIRequestOpts = { status: 200 }) { - return this.doRequest(() => { - return this.client.post(`/api/auth/logout`) - }, opts) - } - - async resetPassword( - email: string, - opts: APIRequestOpts = { status: 200 } - ): Promise<[Response, string]> { - return this.doRequest(async () => { - const [response] = await this.client.post(`/api/auth/reset`, { - body: { email }, - headers: { - [Header.RETURN_RESET_PASSWORD_CODE]: "1", - }, - }) - const code = response.headers.get(Header.RESET_PASSWORD_CODE) - return [response, code] - }, opts) - } - - async resetPasswordUpdate( - resetCode: string, - password: string, - opts: APIRequestOpts = { status: 200 } - ) { - return this.doRequest(() => { - return this.client.post(`/api/auth/reset/update`, { - body: { - resetCode: resetCode, - password: password, - }, - }) - }, opts) - } -} diff --git a/qa-core/src/account-api/api/apis/BaseAPI.ts b/qa-core/src/account-api/api/apis/BaseAPI.ts deleted file mode 100644 index ed5d261e9e..0000000000 --- a/qa-core/src/account-api/api/apis/BaseAPI.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { Response } from "node-fetch" -import { APIRequestOpts } from "../../../types" - -export default class BaseAPI { - async doRequest( - request: () => Promise<[Response, any]>, - opts: APIRequestOpts - ): Promise<[Response, any]> { - const [response, body] = await request() - - // do expect on by default - if (opts.doExpect === undefined) { - opts.doExpect = true - } - if (opts.doExpect && opts.status) { - expect(response).toHaveStatusCode(opts.status) - } - return [response, body] - } -} diff --git a/qa-core/src/account-api/api/apis/LicenseAPI.ts b/qa-core/src/account-api/api/apis/LicenseAPI.ts deleted file mode 100644 index 8f6e705e5f..0000000000 --- a/qa-core/src/account-api/api/apis/LicenseAPI.ts +++ /dev/null @@ -1,140 +0,0 @@ -import AccountInternalAPIClient from "../AccountInternalAPIClient" -import { - Account, - CreateOfflineLicenseRequest, - GetLicenseKeyResponse, - GetOfflineLicenseResponse, - UpdateLicenseRequest, -} from "@budibase/types" -import { Response } from "node-fetch" -import BaseAPI from "./BaseAPI" -import { APIRequestOpts } from "../../../types" - -export default class LicenseAPI extends BaseAPI { - client: AccountInternalAPIClient - constructor(client: AccountInternalAPIClient) { - super() - this.client = client - } - async updateLicense( - accountId: string, - body: UpdateLicenseRequest, - opts: APIRequestOpts = { status: 200 } - ): Promise<[Response, Account]> { - return this.doRequest(() => { - return this.client.put(`/api/accounts/${accountId}/license`, { - body, - internal: true, - }) - }, opts) - } - // TODO: Better approach for setting tenant id header - async createOfflineLicense( - accountId: string, - tenantId: string, - body: CreateOfflineLicenseRequest, - opts: { status?: number } = {} - ): Promise { - const [response, json] = await this.client.post( - `/api/internal/accounts/${accountId}/license/offline`, - { - body, - internal: true, - headers: { - "x-budibase-tenant-id": tenantId, - }, - } - ) - expect(response.status).toBe(opts.status ? opts.status : 201) - return response - } - async getOfflineLicense( - accountId: string, - tenantId: string, - opts: { status?: number } = {} - ): Promise<[Response, GetOfflineLicenseResponse]> { - const [response, json] = await this.client.get( - `/api/internal/accounts/${accountId}/license/offline`, - { - internal: true, - headers: { - "x-budibase-tenant-id": tenantId, - }, - } - ) - expect(response.status).toBe(opts.status ? opts.status : 200) - return [response, json] - } - async getLicenseKey( - opts: { status?: number } = {} - ): Promise<[Response, GetLicenseKeyResponse]> { - const [response, json] = await this.client.get(`/api/license/key`) - expect(response.status).toBe(opts.status || 200) - return [response, json] - } - async activateLicense( - apiKey: string, - tenantId: string, - licenseKey: string, - opts: APIRequestOpts = { status: 200 } - ) { - return this.doRequest(() => { - return this.client.post(`/api/license/activate`, { - body: { - apiKey: apiKey, - tenantId: tenantId, - licenseKey: licenseKey, - }, - }) - }, opts) - } - async regenerateLicenseKey(opts: APIRequestOpts = { status: 200 }) { - return this.doRequest(() => { - return this.client.post(`/api/license/key/regenerate`, {}) - }, opts) - } - - async getPlans(opts: APIRequestOpts = { status: 200 }) { - return this.doRequest(() => { - return this.client.get(`/api/plans`) - }, opts) - } - - async updatePlan(priceId: string, opts: APIRequestOpts = { status: 200 }) { - return this.doRequest(() => { - return this.client.put(`/api/license/plan`, { - body: { priceId }, - }) - }, opts) - } - - async refreshAccountLicense( - accountId: string, - opts: { status?: number } = {} - ): Promise { - const [response, json] = await this.client.post( - `/api/accounts/${accountId}/license/refresh`, - { - internal: true, - } - ) - expect(response.status).toBe(opts.status ? opts.status : 201) - return response - } - - async getLicenseUsage(opts: APIRequestOpts = { status: 200 }) { - return this.doRequest(() => { - return this.client.get(`/api/license/usage`) - }, opts) - } - - async licenseUsageTriggered( - opts: { status?: number } = {} - ): Promise { - const [response, json] = await this.client.post( - `/api/license/usage/triggered` - ) - expect(response.status).toBe(opts.status ? opts.status : 201) - return response - } -} diff --git a/qa-core/src/account-api/api/apis/StripeAPI.ts b/qa-core/src/account-api/api/apis/StripeAPI.ts deleted file mode 100644 index aeb027f428..0000000000 --- a/qa-core/src/account-api/api/apis/StripeAPI.ts +++ /dev/null @@ -1,74 +0,0 @@ -import AccountInternalAPIClient from "../AccountInternalAPIClient" -import BaseAPI from "./BaseAPI" -import { APIRequestOpts } from "../../../types" - -export default class StripeAPI extends BaseAPI { - client: AccountInternalAPIClient - - constructor(client: AccountInternalAPIClient) { - super() - this.client = client - } - - async createCheckoutSession( - price: object, - opts: APIRequestOpts = { status: 200 } - ) { - return this.doRequest(() => { - return this.client.post(`/api/stripe/checkout-session`, { - body: { prices: [price] }, - }) - }, opts) - } - - async checkoutSuccess(opts: APIRequestOpts = { status: 200 }) { - return this.doRequest(() => { - return this.client.post(`/api/stripe/checkout-success`) - }, opts) - } - - async createPortalSession( - stripeCustomerId: string, - opts: APIRequestOpts = { status: 200 } - ) { - return this.doRequest(() => { - return this.client.post(`/api/stripe/portal-session`, { - body: { stripeCustomerId }, - }) - }, opts) - } - - async linkStripeCustomer( - accountId: string, - stripeCustomerId: string, - opts: APIRequestOpts = { status: 200 } - ) { - return this.doRequest(() => { - return this.client.post(`/api/stripe/link`, { - body: { - accountId, - stripeCustomerId, - }, - internal: true, - }) - }, opts) - } - - async getInvoices(opts: APIRequestOpts = { status: 200 }) { - return this.doRequest(() => { - return this.client.get(`/api/stripe/invoices`) - }, opts) - } - - async getUpcomingInvoice(opts: APIRequestOpts = { status: 200 }) { - return this.doRequest(() => { - return this.client.get(`/api/stripe/upcoming-invoice`) - }, opts) - } - - async getStripeCustomers(opts: APIRequestOpts = { status: 200 }) { - return this.doRequest(() => { - return this.client.get(`/api/stripe/customers`) - }, opts) - } -} diff --git a/qa-core/src/account-api/api/apis/index.ts b/qa-core/src/account-api/api/apis/index.ts deleted file mode 100644 index 5b0cf55110..0000000000 --- a/qa-core/src/account-api/api/apis/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export { default as AuthAPI } from "./AuthAPI" -export { default as AccountAPI } from "./AccountAPI" -export { default as LicenseAPI } from "./LicenseAPI" -export { default as StripeAPI } from "./StripeAPI" diff --git a/qa-core/src/account-api/api/index.ts b/qa-core/src/account-api/api/index.ts deleted file mode 100644 index 7a81f60d0b..0000000000 --- a/qa-core/src/account-api/api/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default as AccountInternalAPI } from "./AccountInternalAPI" diff --git a/qa-core/src/account-api/config/TestConfiguration.ts b/qa-core/src/account-api/config/TestConfiguration.ts deleted file mode 100644 index 66adb85ca2..0000000000 --- a/qa-core/src/account-api/config/TestConfiguration.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { AccountInternalAPI } from "../api" -import { BudibaseTestConfiguration } from "../../shared" - -export default class TestConfiguration extends BudibaseTestConfiguration { - // apis - api: AccountInternalAPI - - context: T - - constructor() { - super() - this.api = new AccountInternalAPI(this.state) - this.context = {} - } - - async beforeAll() { - await super.beforeAll() - await this.setApiKey() - } - - async afterAll() { - await super.afterAll() - } - - async setApiKey() { - const apiKeyResponse = await this.internalApi.self.getApiKey() - this.state.apiKey = apiKeyResponse.apiKey - } -} diff --git a/qa-core/src/account-api/fixtures/accounts.ts b/qa-core/src/account-api/fixtures/accounts.ts deleted file mode 100644 index 7a3e0598df..0000000000 --- a/qa-core/src/account-api/fixtures/accounts.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { generator } from "../../shared" -import { Hosting, CreateAccountRequest } from "@budibase/types" - -// TODO: Refactor me to central location -export const generateAccount = ( - partial: Partial -): CreateAccountRequest => { - const uuid = generator.guid() - - const email = `${uuid}@budibase.com` - const tenant = `tenant${uuid.replace(/-/g, "")}` - - return { - email, - hosting: Hosting.CLOUD, - name: email, - password: uuid, - profession: "software_engineer", - size: "10+", - tenantId: tenant, - tenantName: tenant, - ...partial, - } -} diff --git a/qa-core/src/account-api/fixtures/index.ts b/qa-core/src/account-api/fixtures/index.ts deleted file mode 100644 index 952f4a8cc8..0000000000 --- a/qa-core/src/account-api/fixtures/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * as accounts from "./accounts" diff --git a/qa-core/src/account-api/index.ts b/qa-core/src/account-api/index.ts deleted file mode 100644 index e1a716605a..0000000000 --- a/qa-core/src/account-api/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./api" diff --git a/qa-core/src/account-api/tests/accounts/accounts.cloud.internal.spec.ts b/qa-core/src/account-api/tests/accounts/accounts.cloud.internal.spec.ts deleted file mode 100644 index 56f9110322..0000000000 --- a/qa-core/src/account-api/tests/accounts/accounts.cloud.internal.spec.ts +++ /dev/null @@ -1,32 +0,0 @@ -import TestConfiguration from "../../config/TestConfiguration" -import * as fixtures from "../../fixtures" -import { generator } from "../../../shared" -import { Hosting } from "@budibase/types" - -describe("Account Internal Operations", () => { - const config = new TestConfiguration() - - beforeAll(async () => { - await config.beforeAll() - }) - - afterAll(async () => { - await config.afterAll() - }) - - it("performs account deletion by ID", async () => { - // Deleting by unknown id doesn't work - const accountId = generator.guid() - await config.api.accounts.delete(accountId, { status: 404 }) - - // Create new account - const [_, account] = await config.api.accounts.create({ - ...fixtures.accounts.generateAccount({ - hosting: Hosting.CLOUD, - }), - }) - - // New account can be deleted - await config.api.accounts.delete(account.accountId) - }) -}) diff --git a/qa-core/src/account-api/tests/accounts/accounts.cloud.spec.ts b/qa-core/src/account-api/tests/accounts/accounts.cloud.spec.ts deleted file mode 100644 index 01338b609c..0000000000 --- a/qa-core/src/account-api/tests/accounts/accounts.cloud.spec.ts +++ /dev/null @@ -1,102 +0,0 @@ -import TestConfiguration from "../../config/TestConfiguration" -import * as fixtures from "../../fixtures" -import { generator } from "../../../shared" -import { Hosting } from "@budibase/types" - -describe("Accounts", () => { - const config = new TestConfiguration() - - beforeAll(async () => { - await config.beforeAll() - }) - - afterAll(async () => { - await config.afterAll() - }) - - it("performs signup and deletion flow", async () => { - await config.doInNewState(async () => { - // Create account - const createAccountRequest = fixtures.accounts.generateAccount({ - hosting: Hosting.CLOUD, - }) - const email = createAccountRequest.email - const tenantId = createAccountRequest.tenantId - - // Validation - email and tenant ID allowed - await config.api.accounts.validateEmail(email) - await config.api.accounts.validateTenantId(tenantId) - - // Create unverified account - await config.api.accounts.create(createAccountRequest) - - // Validation - email and tenant ID no longer valid - await config.api.accounts.validateEmail(email, { status: 400 }) - await config.api.accounts.validateTenantId(tenantId, { status: 400 }) - - // Attempt to log in using unverified account - await config.loginAsAccount(createAccountRequest, { status: 400 }) - - // Re-send verification email to get access to code - const [_, code] = await config.accountsApi.accounts.sendVerificationEmail( - email - ) - - // Send the verification request - await config.accountsApi.accounts.verifyAccount(code!) - - // Verify self response is unauthorized - await config.api.accounts.self({ status: 403 }) - - // Can now log in to the account - await config.loginAsAccount(createAccountRequest) - - // Verify self response matches account - const [selfRes, selfBody] = await config.api.accounts.self() - expect(selfBody.email).toBe(email) - - // Delete account - await config.api.accounts.deleteCurrentAccount() - - // Can't log in - await config.loginAsAccount(createAccountRequest, { status: 403 }) - }) - }) - - describe("Searching accounts", () => { - it("search by tenant ID", async () => { - const tenantId = generator.string() - - // Empty result - const [_, emptyBody] = await config.api.accounts.search( - tenantId, - "tenantId" - ) - expect(emptyBody.length).toBe(0) - - // Hit result - const [hitRes, hitBody] = await config.api.accounts.search( - config.state.tenantId!, - "tenantId" - ) - expect(hitBody.length).toBe(1) - expect(hitBody[0].tenantId).toBe(config.state.tenantId) - }) - - it("searches by email", async () => { - const email = generator.email({ domain: "example.com" }) - - // Empty result - const [_, emptyBody] = await config.api.accounts.search(email, "email") - expect(emptyBody.length).toBe(0) - - // Hit result - const [hitRes, hitBody] = await config.api.accounts.search( - config.state.email!, - "email" - ) - expect(hitBody.length).toBe(1) - expect(hitBody[0].email).toBe(config.state.email) - }) - }) -}) diff --git a/qa-core/src/account-api/tests/auth/auth.cloud.spec.ts b/qa-core/src/account-api/tests/auth/auth.cloud.spec.ts deleted file mode 100644 index 075a52bef4..0000000000 --- a/qa-core/src/account-api/tests/auth/auth.cloud.spec.ts +++ /dev/null @@ -1,46 +0,0 @@ -import TestConfiguration from "../../config/TestConfiguration" -import * as fixtures from "../../fixtures" -import { generator } from "../../../shared" -import { Hosting } from "@budibase/types" - -describe("Password Management", () => { - const config = new TestConfiguration() - - beforeAll(async () => { - await config.beforeAll() - }) - - afterAll(async () => { - await config.afterAll() - }) - - it("performs password reset flow", async () => { - // Create account - const createAccountRequest = fixtures.accounts.generateAccount({ - hosting: Hosting.CLOUD, - }) - await config.api.accounts.create(createAccountRequest, { autoVerify: true }) - - // Request password reset to get code - const [_, code] = await config.api.auth.resetPassword( - createAccountRequest.email - ) - - // Change password using code - const password = generator.string() - await config.api.auth.resetPasswordUpdate(code, password) - - // Login using the new password - await config.api.auth.login(createAccountRequest.email, password) - - // Logout of account - await config.api.auth.logout() - - // Cannot log in using old password - await config.api.auth.login( - createAccountRequest.email, - createAccountRequest.password, - { status: 403 } - ) - }) -}) diff --git a/qa-core/src/account-api/tests/licensing/license.activate.spec.ts b/qa-core/src/account-api/tests/licensing/license.activate.spec.ts deleted file mode 100644 index 96c6eaea2a..0000000000 --- a/qa-core/src/account-api/tests/licensing/license.activate.spec.ts +++ /dev/null @@ -1,68 +0,0 @@ -import TestConfiguration from "../../config/TestConfiguration" -import * as fixures from "../../fixtures" -import { Feature, Hosting } from "@budibase/types" - -describe("license activation", () => { - const config = new TestConfiguration() - - beforeAll(async () => { - await config.beforeAll() - }) - - afterAll(async () => { - await config.afterAll() - }) - - it("creates, activates and deletes online license - self host", async () => { - // Remove existing license key - await config.internalApi.license.deleteLicenseKey() - - // Verify license key not found - await config.internalApi.license.getLicenseKey({ status: 404 }) - - // Create self host account - const createAccountRequest = fixures.accounts.generateAccount({ - hosting: Hosting.SELF, - }) - const [createAccountRes, account] = - await config.accountsApi.accounts.create(createAccountRequest, { - autoVerify: true, - }) - - let licenseKey: string = " " - await config.doInNewState(async () => { - await config.loginAsAccount(createAccountRequest) - // Retrieve license key - const [res, body] = await config.accountsApi.licenses.getLicenseKey() - licenseKey = body.licenseKey - }) - - const accountId = account.accountId! - - // Update license to have paid feature - const [res, acc] = await config.accountsApi.licenses.updateLicense( - accountId, - { - overrides: { - features: [Feature.APP_BACKUPS], - }, - } - ) - - // Activate license key - await config.internalApi.license.activateLicenseKey({ licenseKey }) - - // Verify license updated with new feature - await config.doInNewState(async () => { - await config.loginAsAccount(createAccountRequest) - const [selfRes, body] = await config.api.accounts.self() - expect(body.license.features[0]).toBe("appBackups") - }) - - // Remove license key - await config.internalApi.license.deleteLicenseKey() - - // Verify license key not found - await config.internalApi.license.getLicenseKey({ status: 404 }) - }) -}) diff --git a/qa-core/src/account-api/tests/licensing/license.manage.spec.ts b/qa-core/src/account-api/tests/licensing/license.manage.spec.ts deleted file mode 100644 index 85ee530bb7..0000000000 --- a/qa-core/src/account-api/tests/licensing/license.manage.spec.ts +++ /dev/null @@ -1,116 +0,0 @@ -import TestConfiguration from "../../config/TestConfiguration" -import * as fixtures from "../../fixtures" -import { Hosting, PlanType } from "@budibase/types" - -const stripe = require("stripe")(process.env.STRIPE_SECRET_KEY) - -describe("license management", () => { - const config = new TestConfiguration() - - beforeAll(async () => { - await config.beforeAll() - }) - - afterAll(async () => { - await config.afterAll() - }) - - it("retrieves plans, creates checkout session, and updates license", async () => { - // Create cloud account - const createAccountRequest = fixtures.accounts.generateAccount({ - hosting: Hosting.CLOUD, - }) - const [createAccountRes, account] = - await config.accountsApi.accounts.create(createAccountRequest, { - autoVerify: true, - }) - - // Self response has free license - await config.doInNewState(async () => { - await config.loginAsAccount(createAccountRequest) - const [selfRes, selfBody] = await config.api.accounts.self() - expect(selfBody.license.plan.type).toBe(PlanType.FREE) - }) - - // Retrieve plans - const [plansRes, planBody] = await config.api.licenses.getPlans() - - // Select priceId from premium plan - let premiumPrice = null - let businessPriceId: "" - for (const plan of planBody) { - if (plan.type === PlanType.PREMIUM_PLUS) { - premiumPrice = plan.prices[0] - } - if (plan.type === PlanType.ENTERPRISE_BASIC) { - businessPriceId = plan.prices[0].priceId - } - } - - // Create checkout session for price - const checkoutSessionRes = await config.api.stripe.createCheckoutSession({ - id: premiumPrice.priceId, - type: premiumPrice.type, - }) - const checkoutSessionUrl = checkoutSessionRes[1].url - expect(checkoutSessionUrl).toContain("checkout.stripe.com") - - // Create stripe customer - const customer = await stripe.customers.create({ - email: createAccountRequest.email, - }) - - // Create payment method - const paymentMethod = await stripe.paymentMethods.create({ - type: "card", - card: { - token: "tok_visa", // Test Visa Card - }, - }) - - // Attach payment method to customer - await stripe.paymentMethods.attach(paymentMethod.id, { - customer: customer.id, - }) - - // Update customer - await stripe.customers.update(customer.id, { - invoice_settings: { - default_payment_method: paymentMethod.id, - }, - }) - - // Create subscription for premium plan - const subscription = await stripe.subscriptions.create({ - customer: customer.id, - items: [ - { - price: premiumPrice.priceId, - quantity: 1, - }, - ], - default_payment_method: paymentMethod.id, - collection_method: "charge_automatically", - }) - - await config.doInNewState(async () => { - // License updated from Free to Premium - await config.loginAsAccount(createAccountRequest) - await config.api.stripe.linkStripeCustomer(account.accountId, customer.id) - const [_, selfBodyPremium] = await config.api.accounts.self() - expect(selfBodyPremium.license.plan.type).toBe(PlanType.PREMIUM_PLUS) - - // Create portal session - Check URL - const [portalRes, portalSessionBody] = - await config.api.stripe.createPortalSession(customer.id) - expect(portalSessionBody.url).toContain("billing.stripe.com") - - // Update subscription from premium to business license - await config.api.licenses.updatePlan(businessPriceId) - - // License updated to Business - const [selfRes, selfBodyBusiness] = await config.api.accounts.self() - expect(selfBodyBusiness.license.plan.type).toBe(PlanType.ENTERPRISE_BASIC) - }) - }) -}) diff --git a/qa-core/src/account-api/tests/licensing/offline.spec.ts b/qa-core/src/account-api/tests/licensing/offline.spec.ts deleted file mode 100644 index e524048153..0000000000 --- a/qa-core/src/account-api/tests/licensing/offline.spec.ts +++ /dev/null @@ -1,79 +0,0 @@ -import TestConfiguration from "../../config/TestConfiguration" -import * as fixures from "../../fixtures" -import { Hosting, Feature } from "@budibase/types" - -describe("offline", () => { - const config = new TestConfiguration() - - beforeAll(async () => { - await config.beforeAll() - }) - - afterAll(async () => { - await config.afterAll() - }) - - // TODO: Currently requires a self host install + account portal - // Ignored until we set this up - it.skip("creates, activates and deletes offline license", async () => { - // installation: Delete any token - await config.internalApi.license.deleteOfflineLicenseToken() - - // installation: Assert token not found - let [getTokenRes] = await config.internalApi.license.getOfflineLicenseToken( - { status: 404 } - ) - - // installation: Retrieve Identifier - const [getIdentifierRes, identifier] = - await config.internalApi.license.getOfflineIdentifier() - - // account-portal: Create self-host account - const createAccountRequest = fixures.accounts.generateAccount({ - hosting: Hosting.SELF, - }) - const [createAccountRes, account] = - await config.accountsApi.accounts.create(createAccountRequest) - const accountId = account.accountId! - const tenantId = account.tenantId! - - // account-portal: Enable feature on license - await config.accountsApi.licenses.updateLicense(accountId, { - overrides: { - features: [Feature.OFFLINE], - }, - }) - - // account-portal: Create offline token - const expireAt = new Date() - expireAt.setDate(new Date().getDate() + 1) - await config.accountsApi.licenses.createOfflineLicense( - accountId, - tenantId, - { - expireAt: expireAt.toISOString(), - installationIdentifierBase64: identifier.identifierBase64, - } - ) - - // account-portal: Retrieve offline token - const [getLicenseRes, offlineLicense] = - await config.accountsApi.licenses.getOfflineLicense(accountId, tenantId) - - // installation: Activate offline token - await config.internalApi.license.activateOfflineLicenseToken({ - offlineLicenseToken: offlineLicense.offlineLicenseToken, - }) - - // installation: Assert token found - await config.internalApi.license.getOfflineLicenseToken() - - // TODO: Assert on license for current user - - // installation: Remove the token - await config.internalApi.license.deleteOfflineLicenseToken() - - // installation: Assert token not found - await config.internalApi.license.getOfflineLicenseToken({ status: 404 }) - }) -}) diff --git a/qa-core/src/environment.ts b/qa-core/src/environment.ts deleted file mode 100644 index a805503474..0000000000 --- a/qa-core/src/environment.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { join } from "path" - -let LOADED = false -if (!LOADED) { - require("dotenv").config({ - path: join(__dirname, "..", ".env"), - }) - LOADED = true -} - -const env = { - BUDIBASE_URL: process.env.BUDIBASE_URL, - ACCOUNT_PORTAL_URL: process.env.ACCOUNT_PORTAL_URL, - ACCOUNT_PORTAL_API_KEY: process.env.ACCOUNT_PORTAL_API_KEY, - BB_ADMIN_USER_EMAIL: process.env.BB_ADMIN_USER_EMAIL, - BB_ADMIN_USER_PASSWORD: process.env.BB_ADMIN_USER_PASSWORD, - POSTGRES_HOST: process.env.POSTGRES_HOST, - POSTGRES_PORT: process.env.POSTGRES_PORT, - POSTGRES_DB: process.env.POSTGRES_DB, - POSTGRES_USER: process.env.POSTGRES_USER, - POSTGRES_PASSWORD: process.env.POSTGRES_PASSWORD, - MONGODB_CONNECTION_STRING: process.env.MONGODB_CONNECTION_STRING, - MONGODB_DB: process.env.MONGODB_DB, - REST_API_BASE_URL: process.env.REST_API_BASE_URL, - REST_API_KEY: process.env.REST_API_KEY, - MARIADB_HOST: process.env.MARIADB_HOST, - MARIADB_PORT: process.env.MARIADB_PORT, - MARIADB_DB: process.env.MARIADB_DB, - MARIADB_USER: process.env.MARIADB_USER, - MARIADB_PASSWORD: process.env.MARIADB_PASSWORD, - STRIPE_SECRET_KEY: process.env.STRIPE_SECRET_KEY, -} - -export = env diff --git a/qa-core/src/integrations/external-schema/mssql.integration.spec.ts b/qa-core/src/integrations/external-schema/mssql.integration.spec.ts deleted file mode 100644 index a43edc4379..0000000000 --- a/qa-core/src/integrations/external-schema/mssql.integration.spec.ts +++ /dev/null @@ -1,112 +0,0 @@ -import { GenericContainer, Wait } from "testcontainers" -import { Duration, TemporalUnit } from "node-duration" -import mssql from "../../../../packages/server/src/integrations/microsoftSqlServer" - -jest.unmock("mssql") - -describe("getExternalSchema", () => { - describe("mssql", () => { - let config: any - - beforeAll(async () => { - const password = "Str0Ng_p@ssW0rd!" - const container = await new GenericContainer( - "mcr.microsoft.com/mssql/server" - ) - .withExposedPorts(1433) - .withEnv("ACCEPT_EULA", "Y") - .withEnv("MSSQL_SA_PASSWORD", password) - .withEnv("MSSQL_PID", "Developer") - .withWaitStrategy(Wait.forHealthCheck()) - .withHealthCheck({ - test: `/opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P "${password}" -Q "SELECT 1" -b -o /dev/null`, - interval: new Duration(1000, TemporalUnit.MILLISECONDS), - timeout: new Duration(3, TemporalUnit.SECONDS), - retries: 20, - startPeriod: new Duration(100, TemporalUnit.MILLISECONDS), - }) - .start() - - const host = container.getContainerIpAddress() - const port = container.getMappedPort(1433) - config = { - user: "sa", - password, - server: host, - port: port, - database: "master", - schema: "dbo", - } - }) - - it("can export an empty database", async () => { - const integration = new mssql.integration(config) - const result = await integration.getExternalSchema() - expect(result).toMatchInlineSnapshot(`""`) - }) - - it("can export a database with tables", async () => { - const integration = new mssql.integration(config) - - await integration.connect() - await integration.internalQuery({ - sql: ` - CREATE TABLE users ( - id INT IDENTITY(1,1) PRIMARY KEY, - name VARCHAR(100) NOT NULL, - role VARCHAR(15) NOT NULL - ); - - CREATE TABLE products ( - id INT IDENTITY(1,1) PRIMARY KEY, - name VARCHAR(100) NOT NULL, - price DECIMAL(10, 2) NOT NULL - ); - `, - }) - - const result = await integration.getExternalSchema() - expect(result).toMatchInlineSnapshot(` - "CREATE TABLE [products] ( - id int(4) NOT NULL, - name varchar(100) NOT NULL, - price decimal(9) NOT NULL, - CONSTRAINT [PK_products] PRIMARY KEY (id) - ); - CREATE TABLE [users] ( - id int(4) NOT NULL, - name varchar(100) NOT NULL, - role varchar(15) NOT NULL, - CONSTRAINT [PK_users] PRIMARY KEY (id) - );" - `) - }) - - it("does not export a data", async () => { - const integration = new mssql.integration(config) - - await integration.connect() - await integration.internalQuery({ - sql: `INSERT INTO [users] ([name], [role]) VALUES ('John Doe', 'Administrator'); - INSERT INTO [products] ([name], [price]) VALUES ('Book', 7.68); - `, - }) - - const result = await integration.getExternalSchema() - expect(result).toMatchInlineSnapshot(` - "CREATE TABLE [products] ( - id int(4) NOT NULL, - name varchar(100) NOT NULL, - price decimal(9) NOT NULL, - CONSTRAINT [PK_products] PRIMARY KEY (id) - ); - CREATE TABLE [users] ( - id int(4) NOT NULL, - name varchar(100) NOT NULL, - role varchar(15) NOT NULL, - CONSTRAINT [PK_users] PRIMARY KEY (id) - );" - `) - }) - }) -}) diff --git a/qa-core/src/integrations/external-schema/mysql.integration.spec.ts b/qa-core/src/integrations/external-schema/mysql.integration.spec.ts deleted file mode 100644 index c8d285a021..0000000000 --- a/qa-core/src/integrations/external-schema/mysql.integration.spec.ts +++ /dev/null @@ -1,106 +0,0 @@ -import { GenericContainer } from "testcontainers" -import mysql from "../../../../packages/server/src/integrations/mysql" - -describe("datasource validators", () => { - describe("mysql", () => { - let config: any - - beforeAll(async () => { - const container = await new GenericContainer("mysql:8.3") - .withExposedPorts(3306) - .withEnv("MYSQL_ROOT_PASSWORD", "admin") - .withEnv("MYSQL_DATABASE", "db") - .withEnv("MYSQL_USER", "user") - .withEnv("MYSQL_PASSWORD", "password") - .start() - - const host = container.getContainerIpAddress() - const port = container.getMappedPort(3306) - config = { - host, - port, - user: "user", - database: "db", - password: "password", - rejectUnauthorized: true, - } - }) - - it("can export an empty database", async () => { - const integration = new mysql.integration(config) - const result = await integration.getExternalSchema() - expect(result).toMatchInlineSnapshot( - `"CREATE DATABASE \`db\` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci */ /*!80016 DEFAULT ENCRYPTION='N' */"` - ) - }) - - it("can export a database with tables", async () => { - const integration = new mysql.integration(config) - - await integration.internalQuery({ - sql: ` - CREATE TABLE users ( - id INT AUTO_INCREMENT, - name VARCHAR(100) NOT NULL, - role VARCHAR(15) NOT NULL, - PRIMARY KEY (id) - ); - - - CREATE TABLE products ( - id INT AUTO_INCREMENT, - name VARCHAR(100) NOT NULL, - price DECIMAL, - PRIMARY KEY (id) - ); - `, - }) - - const result = await integration.getExternalSchema() - expect(result).toMatchInlineSnapshot(` - "CREATE DATABASE \`db\` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci */ /*!80016 DEFAULT ENCRYPTION='N' */ - CREATE TABLE \`products\` ( - \`id\` int NOT NULL AUTO_INCREMENT, - \`name\` varchar(100) NOT NULL, - \`price\` decimal(10,0) DEFAULT NULL, - PRIMARY KEY (\`id\`) - ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci - CREATE TABLE \`users\` ( - \`id\` int NOT NULL AUTO_INCREMENT, - \`name\` varchar(100) NOT NULL, - \`role\` varchar(15) NOT NULL, - PRIMARY KEY (\`id\`) - ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci" - `) - }) - - it("does not export a data", async () => { - const integration = new mysql.integration(config) - - await integration.internalQuery({ - sql: `INSERT INTO users (name, role) VALUES ('John Doe', 'Administrator');`, - }) - - await integration.internalQuery({ - sql: `INSERT INTO products (name, price) VALUES ('Book', 7.68);`, - }) - - const result = await integration.getExternalSchema() - expect(result).toMatchInlineSnapshot(` - "CREATE DATABASE \`db\` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci */ /*!80016 DEFAULT ENCRYPTION='N' */ - CREATE TABLE \`products\` ( - \`id\` int NOT NULL AUTO_INCREMENT, - \`name\` varchar(100) NOT NULL, - \`price\` decimal(10,0) DEFAULT NULL, - PRIMARY KEY (\`id\`) - ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci - CREATE TABLE \`users\` ( - \`id\` int NOT NULL AUTO_INCREMENT, - \`name\` varchar(100) NOT NULL, - \`role\` varchar(15) NOT NULL, - PRIMARY KEY (\`id\`) - ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci" - `) - }) - }) -}) diff --git a/qa-core/src/integrations/external-schema/postgres.integration.spec.ts b/qa-core/src/integrations/external-schema/postgres.integration.spec.ts deleted file mode 100644 index 7581d7f88a..0000000000 --- a/qa-core/src/integrations/external-schema/postgres.integration.spec.ts +++ /dev/null @@ -1,376 +0,0 @@ -import { GenericContainer } from "testcontainers" -import postgres from "../../../../packages/server/src/integrations/postgres" - -jest.unmock("pg") - -describe("getExternalSchema", () => { - describe("postgres", () => { - let config: any - - // Remove versioning from the outputs to prevent failures when running different pg_dump versions - function stripResultsVersions(sql: string) { - const result = sql - .replace(/\n[^\n]+Dumped from database version[^\n]+\n/, "") - .replace(/\n[^\n]+Dumped by pg_dump version[^\n]+\n/, "") - .toString() - return result - } - - beforeAll(async () => { - const container = await new GenericContainer("postgres:16.1-bullseye") - .withExposedPorts(5432) - .withEnv("POSTGRES_PASSWORD", "password") - .start() - - const host = container.getContainerIpAddress() - const port = container.getMappedPort(5432) - - config = { - host, - port, - database: "postgres", - user: "postgres", - password: "password", - schema: "public", - ssl: false, - rejectUnauthorized: false, - } - }) - - it("can export an empty database", async () => { - const integration = new postgres.integration(config) - const result = await integration.getExternalSchema() - - expect(stripResultsVersions(result)).toMatchInlineSnapshot(` - "-- - -- PostgreSQL database dump - -- - SET statement_timeout = 0; - SET lock_timeout = 0; - SET idle_in_transaction_session_timeout = 0; - SET client_encoding = 'UTF8'; - SET standard_conforming_strings = on; - SELECT pg_catalog.set_config('search_path', '', false); - SET check_function_bodies = false; - SET xmloption = content; - SET client_min_messages = warning; - SET row_security = off; - - -- - -- PostgreSQL database dump complete - -- - - " - `) - }) - - it("can export a database with tables", async () => { - const integration = new postgres.integration(config) - - await integration.internalQuery( - { - sql: ` - CREATE TABLE "users" ( - "id" SERIAL, - "name" VARCHAR(100) NOT NULL, - "role" VARCHAR(15) NOT NULL, - PRIMARY KEY ("id") - ); - CREATE TABLE "products" ( - "id" SERIAL, - "name" VARCHAR(100) NOT NULL, - "price" DECIMAL NOT NULL, - "owner" INTEGER NULL, - PRIMARY KEY ("id") - ); - ALTER TABLE "products" ADD CONSTRAINT "fk_owner" FOREIGN KEY ("owner") REFERENCES "users" ("id");`, - }, - false - ) - - const result = await integration.getExternalSchema() - expect(stripResultsVersions(result)).toMatchInlineSnapshot(` - "-- - -- PostgreSQL database dump - -- - SET statement_timeout = 0; - SET lock_timeout = 0; - SET idle_in_transaction_session_timeout = 0; - SET client_encoding = 'UTF8'; - SET standard_conforming_strings = on; - SELECT pg_catalog.set_config('search_path', '', false); - SET check_function_bodies = false; - SET xmloption = content; - SET client_min_messages = warning; - SET row_security = off; - - SET default_tablespace = ''; - - SET default_table_access_method = heap; - - -- - -- Name: products; Type: TABLE; Schema: public; Owner: postgres - -- - - CREATE TABLE public.products ( - id integer NOT NULL, - name character varying(100) NOT NULL, - price numeric NOT NULL, - owner integer - ); - - - ALTER TABLE public.products OWNER TO postgres; - - -- - -- Name: products_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres - -- - - CREATE SEQUENCE public.products_id_seq - AS integer - START WITH 1 - INCREMENT BY 1 - NO MINVALUE - NO MAXVALUE - CACHE 1; - - - ALTER TABLE public.products_id_seq OWNER TO postgres; - - -- - -- Name: products_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres - -- - - ALTER SEQUENCE public.products_id_seq OWNED BY public.products.id; - - - -- - -- Name: users; Type: TABLE; Schema: public; Owner: postgres - -- - - CREATE TABLE public.users ( - id integer NOT NULL, - name character varying(100) NOT NULL, - role character varying(15) NOT NULL - ); - - - ALTER TABLE public.users OWNER TO postgres; - - -- - -- Name: users_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres - -- - - CREATE SEQUENCE public.users_id_seq - AS integer - START WITH 1 - INCREMENT BY 1 - NO MINVALUE - NO MAXVALUE - CACHE 1; - - - ALTER TABLE public.users_id_seq OWNER TO postgres; - - -- - -- Name: users_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres - -- - - ALTER SEQUENCE public.users_id_seq OWNED BY public.users.id; - - - -- - -- Name: products id; Type: DEFAULT; Schema: public; Owner: postgres - -- - - ALTER TABLE ONLY public.products ALTER COLUMN id SET DEFAULT nextval('public.products_id_seq'::regclass); - - - -- - -- Name: users id; Type: DEFAULT; Schema: public; Owner: postgres - -- - - ALTER TABLE ONLY public.users ALTER COLUMN id SET DEFAULT nextval('public.users_id_seq'::regclass); - - - -- - -- Name: products products_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres - -- - - ALTER TABLE ONLY public.products - ADD CONSTRAINT products_pkey PRIMARY KEY (id); - - - -- - -- Name: users users_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres - -- - - ALTER TABLE ONLY public.users - ADD CONSTRAINT users_pkey PRIMARY KEY (id); - - - -- - -- Name: products fk_owner; Type: FK CONSTRAINT; Schema: public; Owner: postgres - -- - - ALTER TABLE ONLY public.products - ADD CONSTRAINT fk_owner FOREIGN KEY (owner) REFERENCES public.users(id); - - - -- - -- PostgreSQL database dump complete - -- - - " - `) - }) - - it("does not export a data", async () => { - const integration = new postgres.integration(config) - - await integration.internalQuery( - { - sql: `INSERT INTO "users" ("name", "role") VALUES ('John Doe', 'Administrator'); - INSERT INTO "products" ("name", "price") VALUES ('Book', 7.68);`, - }, - false - ) - - const result = await integration.getExternalSchema() - expect(stripResultsVersions(result)).toMatchInlineSnapshot(` - "-- - -- PostgreSQL database dump - -- - SET statement_timeout = 0; - SET lock_timeout = 0; - SET idle_in_transaction_session_timeout = 0; - SET client_encoding = 'UTF8'; - SET standard_conforming_strings = on; - SELECT pg_catalog.set_config('search_path', '', false); - SET check_function_bodies = false; - SET xmloption = content; - SET client_min_messages = warning; - SET row_security = off; - - SET default_tablespace = ''; - - SET default_table_access_method = heap; - - -- - -- Name: products; Type: TABLE; Schema: public; Owner: postgres - -- - - CREATE TABLE public.products ( - id integer NOT NULL, - name character varying(100) NOT NULL, - price numeric NOT NULL, - owner integer - ); - - - ALTER TABLE public.products OWNER TO postgres; - - -- - -- Name: products_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres - -- - - CREATE SEQUENCE public.products_id_seq - AS integer - START WITH 1 - INCREMENT BY 1 - NO MINVALUE - NO MAXVALUE - CACHE 1; - - - ALTER TABLE public.products_id_seq OWNER TO postgres; - - -- - -- Name: products_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres - -- - - ALTER SEQUENCE public.products_id_seq OWNED BY public.products.id; - - - -- - -- Name: users; Type: TABLE; Schema: public; Owner: postgres - -- - - CREATE TABLE public.users ( - id integer NOT NULL, - name character varying(100) NOT NULL, - role character varying(15) NOT NULL - ); - - - ALTER TABLE public.users OWNER TO postgres; - - -- - -- Name: users_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres - -- - - CREATE SEQUENCE public.users_id_seq - AS integer - START WITH 1 - INCREMENT BY 1 - NO MINVALUE - NO MAXVALUE - CACHE 1; - - - ALTER TABLE public.users_id_seq OWNER TO postgres; - - -- - -- Name: users_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres - -- - - ALTER SEQUENCE public.users_id_seq OWNED BY public.users.id; - - - -- - -- Name: products id; Type: DEFAULT; Schema: public; Owner: postgres - -- - - ALTER TABLE ONLY public.products ALTER COLUMN id SET DEFAULT nextval('public.products_id_seq'::regclass); - - - -- - -- Name: users id; Type: DEFAULT; Schema: public; Owner: postgres - -- - - ALTER TABLE ONLY public.users ALTER COLUMN id SET DEFAULT nextval('public.users_id_seq'::regclass); - - - -- - -- Name: products products_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres - -- - - ALTER TABLE ONLY public.products - ADD CONSTRAINT products_pkey PRIMARY KEY (id); - - - -- - -- Name: users users_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres - -- - - ALTER TABLE ONLY public.users - ADD CONSTRAINT users_pkey PRIMARY KEY (id); - - - -- - -- Name: products fk_owner; Type: FK CONSTRAINT; Schema: public; Owner: postgres - -- - - ALTER TABLE ONLY public.products - ADD CONSTRAINT fk_owner FOREIGN KEY (owner) REFERENCES public.users(id); - - - -- - -- PostgreSQL database dump complete - -- - - " - `) - }) - }) -}) diff --git a/qa-core/src/integrations/validators/arango.integration.spec.ts b/qa-core/src/integrations/validators/arango.integration.spec.ts deleted file mode 100644 index 7c0faafd61..0000000000 --- a/qa-core/src/integrations/validators/arango.integration.spec.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { GenericContainer, Wait } from "testcontainers" -import arangodb from "../../../../packages/server/src/integrations/arangodb" -import { generator } from "../../shared" - -jest.unmock("arangojs") - -describe("datasource validators", () => { - describe("arangodb", () => { - let connectionSettings: { - user: string - password: string - url: string - } - - beforeAll(async () => { - const user = "root" - const password = generator.hash() - const container = await new GenericContainer("arangodb") - .withExposedPorts(8529) - .withEnv("ARANGO_ROOT_PASSWORD", password) - .withWaitStrategy( - Wait.forLogMessage("is ready for business. Have fun!") - ) - .start() - - connectionSettings = { - user, - password, - url: `http://${container.getContainerIpAddress()}:${container.getMappedPort( - 8529 - )}`, - } - }) - - it("test valid connection string", async () => { - const integration = new arangodb.integration({ - url: connectionSettings.url, - username: connectionSettings.user, - password: connectionSettings.password, - databaseName: "", - collection: "", - }) - const result = await integration.testConnection() - expect(result).toEqual({ connected: true }) - }) - - it("test wrong password", async () => { - const integration = new arangodb.integration({ - url: connectionSettings.url, - username: connectionSettings.user, - password: "wrong", - databaseName: "", - collection: "", - }) - const result = await integration.testConnection() - expect(result).toEqual({ - connected: false, - error: "not authorized to execute this request", - }) - }) - - it("test wrong url", async () => { - const integration = new arangodb.integration({ - url: "http://not.here", - username: connectionSettings.user, - password: connectionSettings.password, - databaseName: "", - collection: "", - }) - const result = await integration.testConnection() - expect(result).toEqual({ - connected: false, - error: "getaddrinfo ENOTFOUND not.here", - }) - }) - }) -}) diff --git a/qa-core/src/integrations/validators/couch.integration.spec.ts b/qa-core/src/integrations/validators/couch.integration.spec.ts deleted file mode 100644 index b0f4254610..0000000000 --- a/qa-core/src/integrations/validators/couch.integration.spec.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { GenericContainer } from "testcontainers" - -import couchdb from "../../../../packages/server/src/integrations/couchdb" -import { generator } from "../../shared" - -describe("datasource validators", () => { - describe("couchdb", () => { - let url: string - - beforeAll(async () => { - const user = generator.first() - const password = generator.hash() - - const container = await new GenericContainer("budibase/couchdb") - .withExposedPorts(5984) - .withEnv("COUCHDB_USER", user) - .withEnv("COUCHDB_PASSWORD", password) - .start() - - const host = container.getContainerIpAddress() - const port = container.getMappedPort(5984) - - await container.exec([ - `curl`, - `-u`, - `${user}:${password}`, - `-X`, - `PUT`, - `localhost:5984/db`, - ]) - url = `http://${user}:${password}@${host}:${port}` - }) - - it("test valid connection string", async () => { - const integration = new couchdb.integration({ - url, - database: "db", - }) - const result = await integration.testConnection() - expect(result).toEqual({ connected: true }) - }) - - it("test invalid database", async () => { - const integration = new couchdb.integration({ - url, - database: "random_db", - }) - const result = await integration.testConnection() - expect(result).toEqual({ - connected: false, - }) - }) - - it("test invalid url", async () => { - const integration = new couchdb.integration({ - url: "http://invalid:123", - database: "any", - }) - const result = await integration.testConnection() - expect(result).toEqual({ - connected: false, - error: - "request to http://invalid:123/any failed, reason: getaddrinfo ENOTFOUND invalid", - }) - }) - }) -}) diff --git a/qa-core/src/integrations/validators/dynamodb.integration.spec.ts b/qa-core/src/integrations/validators/dynamodb.integration.spec.ts deleted file mode 100644 index d1cec7bca1..0000000000 --- a/qa-core/src/integrations/validators/dynamodb.integration.spec.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { GenericContainer } from "testcontainers" -import { env } from "@budibase/backend-core" - -import dynamodb from "../../../../packages/server/src/integrations/dynamodb" -import { generator } from "../../shared" - -jest.unmock("aws-sdk") - -describe("datasource validators", () => { - describe("dynamodb", () => { - let connectionSettings: { - user: string - password: string - url: string - } - - beforeAll(async () => { - const user = "root" - const password = generator.hash() - const container = await new GenericContainer("amazon/dynamodb-local") - .withExposedPorts(8000) - .start() - - connectionSettings = { - user, - password, - url: `http://${container.getContainerIpAddress()}:${container.getMappedPort( - 8000 - )}`, - } - env._set("AWS_ACCESS_KEY_ID", "mockedkey") - env._set("AWS_SECRET_ACCESS_KEY", "mockedsecret") - }) - - it("test valid connection string", async () => { - const integration = new dynamodb.integration({ - endpoint: connectionSettings.url, - region: "", - accessKeyId: "", - secretAccessKey: "", - }) - - const result = await integration.testConnection() - expect(result).toEqual({ connected: true }) - }) - - it("test wrong endpoint", async () => { - const integration = new dynamodb.integration({ - endpoint: "http://wrong.url:2880", - region: "", - accessKeyId: "", - secretAccessKey: "", - }) - - const result = await integration.testConnection() - expect(result).toEqual({ - connected: false, - error: - "Inaccessible host: `wrong.url' at port `undefined'. This service may not be available in the `eu-west-1' region.", - }) - }) - }) -}) diff --git a/qa-core/src/integrations/validators/elastic.integration.spec.ts b/qa-core/src/integrations/validators/elastic.integration.spec.ts deleted file mode 100644 index 39fd732744..0000000000 --- a/qa-core/src/integrations/validators/elastic.integration.spec.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { ElasticsearchContainer } from "testcontainers" -import elastic from "../../../../packages/server/src/integrations/elasticsearch" - -jest.unmock("@elastic/elasticsearch") - -describe("datasource validators", () => { - describe("elastic search", () => { - let url: string - - beforeAll(async () => { - const container = await new ElasticsearchContainer().start() - url = container.getHttpUrl() - }) - - it("test valid connection string", async () => { - const integration = new elastic.integration({ - url, - }) - const result = await integration.testConnection() - expect(result).toEqual({ connected: true }) - }) - - it("test wrong connection string", async () => { - const integration = new elastic.integration({ - url: `http://localhost:5656`, - }) - const result = await integration.testConnection() - expect(result).toEqual({ - connected: false, - error: "connect ECONNREFUSED 127.0.0.1:5656", - }) - }) - }) -}) diff --git a/qa-core/src/integrations/validators/mongo.integration.spec.ts b/qa-core/src/integrations/validators/mongo.integration.spec.ts deleted file mode 100644 index b1bab3bd1f..0000000000 --- a/qa-core/src/integrations/validators/mongo.integration.spec.ts +++ /dev/null @@ -1,100 +0,0 @@ -import { GenericContainer } from "testcontainers" -import mongo from "../../../../packages/server/src/integrations/mongodb" -import { generator } from "../../shared" - -jest.unmock("mongodb") - -describe("datasource validators", () => { - describe("mongo", () => { - let connectionSettings: { - user: string - password: string - host: string - port: number - } - - function getConnectionString( - settings: Partial = {} - ) { - const { user, password, host, port } = { - ...connectionSettings, - ...settings, - } - return `mongodb://${user}:${password}@${host}:${port}` - } - - beforeAll(async () => { - const user = generator.name() - const password = generator.hash() - const container = await new GenericContainer("mongo:7.0-jammy") - .withExposedPorts(27017) - .withEnv("MONGO_INITDB_ROOT_USERNAME", user) - .withEnv("MONGO_INITDB_ROOT_PASSWORD", password) - .start() - - connectionSettings = { - user, - password, - host: container.getContainerIpAddress(), - port: container.getMappedPort(27017), - } - }) - - it("test valid connection string", async () => { - const integration = new mongo.integration({ - connectionString: getConnectionString(), - db: "", - tlsCertificateFile: "", - tlsCertificateKeyFile: "", - tlsCAFile: "", - }) - const result = await integration.testConnection() - expect(result).toEqual({ connected: true }) - }) - - it("test invalid password", async () => { - const integration = new mongo.integration({ - connectionString: getConnectionString({ password: "wrong" }), - db: "", - tlsCertificateFile: "", - tlsCertificateKeyFile: "", - tlsCAFile: "", - }) - const result = await integration.testConnection() - expect(result).toEqual({ - connected: false, - error: "Authentication failed.", - }) - }) - - it("test invalid username", async () => { - const integration = new mongo.integration({ - connectionString: getConnectionString({ user: "wrong" }), - db: "", - tlsCertificateFile: "", - tlsCertificateKeyFile: "", - tlsCAFile: "", - }) - const result = await integration.testConnection() - expect(result).toEqual({ - connected: false, - error: "Authentication failed.", - }) - }) - - it("test invalid connection", async () => { - const integration = new mongo.integration({ - connectionString: getConnectionString({ host: "http://nothinghere" }), - db: "", - tlsCertificateFile: "", - tlsCertificateKeyFile: "", - tlsCAFile: "", - }) - const result = await integration.testConnection() - expect(result).toEqual({ - connected: false, - error: "getaddrinfo ENOTFOUND http", - }) - }) - }) -}) diff --git a/qa-core/src/integrations/validators/mssql.integration.spec.ts b/qa-core/src/integrations/validators/mssql.integration.spec.ts deleted file mode 100644 index c07f1d1129..0000000000 --- a/qa-core/src/integrations/validators/mssql.integration.spec.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { GenericContainer, Wait } from "testcontainers" -import { Duration, TemporalUnit } from "node-duration" - -import mssql from "../../../../packages/server/src/integrations/microsoftSqlServer" - -jest.unmock("mssql") - -describe("datasource validators", () => { - describe("mssql", () => { - let host: string, port: number - - const password = "Str0Ng_p@ssW0rd!" - - beforeAll(async () => { - const container = await new GenericContainer( - "mcr.microsoft.com/mssql/server:2022-latest" - ) - .withExposedPorts(1433) - .withEnv("ACCEPT_EULA", "Y") - .withEnv("MSSQL_SA_PASSWORD", password) - .withEnv("MSSQL_PID", "Developer") - .withWaitStrategy(Wait.forHealthCheck()) - .withHealthCheck({ - test: `/opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P "${password}" -Q "SELECT 1" -b -o /dev/null`, - interval: new Duration(1000, TemporalUnit.MILLISECONDS), - timeout: new Duration(3, TemporalUnit.SECONDS), - retries: 20, - startPeriod: new Duration(100, TemporalUnit.MILLISECONDS), - }) - .start() - - host = container.getContainerIpAddress() - port = container.getMappedPort(1433) - }) - - it("test valid connection string", async () => { - const integration = new mssql.integration({ - user: "sa", - password, - server: host, - port: port, - database: "master", - schema: "dbo", - }) - const result = await integration.testConnection() - expect(result).toEqual({ connected: true }) - }) - - it("test invalid password", async () => { - const integration = new mssql.integration({ - user: "sa", - password: "wrong_pwd", - server: host, - port: port, - database: "master", - schema: "dbo", - }) - const result = await integration.testConnection() - expect(result).toEqual({ - connected: false, - error: "Login failed for user 'sa'.", - }) - }) - }) -}) diff --git a/qa-core/src/integrations/validators/mysql.integration.spec.ts b/qa-core/src/integrations/validators/mysql.integration.spec.ts deleted file mode 100644 index 95f7d4abbd..0000000000 --- a/qa-core/src/integrations/validators/mysql.integration.spec.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { GenericContainer } from "testcontainers" -import mysql from "../../../../packages/server/src/integrations/mysql" - -describe("datasource validators", () => { - describe("mysql", () => { - let host: string - let port: number - - beforeAll(async () => { - const container = await new GenericContainer("mysql:8.3") - .withExposedPorts(3306) - .withEnv("MYSQL_ROOT_PASSWORD", "admin") - .withEnv("MYSQL_DATABASE", "db") - .withEnv("MYSQL_USER", "user") - .withEnv("MYSQL_PASSWORD", "password") - .start() - - host = container.getContainerIpAddress() - port = container.getMappedPort(3306) - }) - - it("test valid connection string", async () => { - const integration = new mysql.integration({ - host, - port, - user: "user", - database: "db", - password: "password", - rejectUnauthorized: true, - }) - const result = await integration.testConnection() - expect(result).toEqual({ connected: true }) - }) - - it("test invalid database", async () => { - const integration = new mysql.integration({ - host, - port, - user: "user", - database: "test", - password: "password", - rejectUnauthorized: true, - }) - const result = await integration.testConnection() - expect(result).toEqual({ - connected: false, - error: "Access denied for user 'user'@'%' to database 'test'", - }) - }) - - it("test invalid password", async () => { - const integration = new mysql.integration({ - host, - port, - user: "root", - database: "test", - password: "wrong", - rejectUnauthorized: true, - }) - const result = await integration.testConnection() - expect(result).toEqual({ - connected: false, - error: - "Access denied for the specified user. User does not have the necessary privileges or the provided credentials are incorrect. Please verify the credentials, and ensure that the user has appropriate permissions.", - }) - }) - }) -}) diff --git a/qa-core/src/integrations/validators/postgres.integration.spec.ts b/qa-core/src/integrations/validators/postgres.integration.spec.ts deleted file mode 100644 index 9e3e1ab30f..0000000000 --- a/qa-core/src/integrations/validators/postgres.integration.spec.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { GenericContainer } from "testcontainers" -import postgres from "../../../../packages/server/src/integrations/postgres" - -jest.unmock("pg") - -describe("datasource validators", () => { - describe("postgres", () => { - let host: string - let port: number - - beforeAll(async () => { - const container = await new GenericContainer("postgres:16.1-bullseye") - .withExposedPorts(5432) - .withEnv("POSTGRES_PASSWORD", "password") - .start() - - host = container.getContainerIpAddress() - port = container.getMappedPort(5432) - }) - - it("test valid connection string", async () => { - const integration = new postgres.integration({ - host, - port, - database: "postgres", - user: "postgres", - password: "password", - schema: "public", - ssl: false, - rejectUnauthorized: false, - }) - const result = await integration.testConnection() - expect(result).toEqual({ connected: true }) - }) - - it("test invalid connection string", async () => { - const integration = new postgres.integration({ - host, - port, - database: "postgres", - user: "wrong", - password: "password", - schema: "public", - ssl: false, - rejectUnauthorized: false, - }) - const result = await integration.testConnection() - expect(result).toEqual({ - connected: false, - error: 'password authentication failed for user "wrong"', - }) - }) - }) -}) diff --git a/qa-core/src/integrations/validators/redis.integration.spec.ts b/qa-core/src/integrations/validators/redis.integration.spec.ts deleted file mode 100644 index 89ada2fe2d..0000000000 --- a/qa-core/src/integrations/validators/redis.integration.spec.ts +++ /dev/null @@ -1,72 +0,0 @@ -import redis from "../../../../packages/server/src/integrations/redis" -import { GenericContainer } from "testcontainers" -import { generator } from "../../shared" - -describe("datasource validators", () => { - describe("redis", () => { - describe("unsecured", () => { - let host: string - let port: number - - beforeAll(async () => { - const container = await new GenericContainer("redis") - .withExposedPorts(6379) - .start() - - host = container.getContainerIpAddress() - port = container.getMappedPort(6379) - }) - - it("test valid connection", async () => { - const integration = new redis.integration({ - host, - port, - username: "", - }) - const result = await integration.testConnection() - expect(result).toEqual({ connected: true }) - }) - - it("test invalid connection even with wrong user/password", async () => { - const integration = new redis.integration({ - host, - port, - username: generator.name(), - password: generator.hash(), - }) - const result = await integration.testConnection() - expect(result).toEqual({ - connected: false, - error: - "WRONGPASS invalid username-password pair or user is disabled.", - }) - }) - }) - - describe("secured", () => { - let host: string - let port: number - - beforeAll(async () => { - const container = await new GenericContainer("redis") - .withExposedPorts(6379) - .withCmd(["redis-server", "--requirepass", "P@ssW0rd!"]) - .start() - - host = container.getContainerIpAddress() - port = container.getMappedPort(6379) - }) - - it("test valid connection", async () => { - const integration = new redis.integration({ - host, - port, - username: "", - password: "P@ssW0rd!", - }) - const result = await integration.testConnection() - expect(result).toEqual({ connected: true }) - }) - }) - }) -}) diff --git a/qa-core/src/integrations/validators/s3.integration.spec.ts b/qa-core/src/integrations/validators/s3.integration.spec.ts deleted file mode 100644 index 7bb415ee3d..0000000000 --- a/qa-core/src/integrations/validators/s3.integration.spec.ts +++ /dev/null @@ -1,52 +0,0 @@ -import s3 from "../../../../packages/server/src/integrations/s3" -import { GenericContainer } from "testcontainers" - -jest.unmock("aws-sdk") - -describe("datasource validators", () => { - describe("s3", () => { - let host: string - let port: number - - beforeAll(async () => { - const container = await new GenericContainer("localstack/localstack") - .withExposedPorts(4566) - .withEnv("SERVICES", "s3") - .withEnv("DEFAULT_REGION", "eu-west-1") - .withEnv("AWS_ACCESS_KEY_ID", "testkey") - .withEnv("AWS_SECRET_ACCESS_KEY", "testsecret") - .start() - - host = container.getContainerIpAddress() - port = container.getMappedPort(4566) - }) - - it("test valid connection", async () => { - const integration = new s3.integration({ - region: "eu-west-1", - accessKeyId: "testkey", - secretAccessKey: "testsecret", - s3ForcePathStyle: false, - endpoint: `http://${host}:${port}`, - }) - const result = await integration.testConnection() - expect(result).toEqual({ connected: true }) - }) - - it("test wrong endpoint", async () => { - const integration = new s3.integration({ - region: "eu-west-2", - accessKeyId: "testkey", - secretAccessKey: "testsecret", - s3ForcePathStyle: false, - endpoint: `http://wrong:123`, - }) - const result = await integration.testConnection() - expect(result).toEqual({ - connected: false, - error: - "Inaccessible host: `wrong' at port `undefined'. This service may not be available in the `eu-west-2' region.", - }) - }) - }) -}) diff --git a/qa-core/src/internal-api/api/BudibaseInternalAPI.ts b/qa-core/src/internal-api/api/BudibaseInternalAPI.ts deleted file mode 100644 index 9b55c22deb..0000000000 --- a/qa-core/src/internal-api/api/BudibaseInternalAPI.ts +++ /dev/null @@ -1,54 +0,0 @@ -import AppAPI from "./apis/AppAPI" -import AuthAPI from "./apis/AuthAPI" -import EnvironmentAPI from "./apis/EnvironmentAPI" -import RoleAPI from "./apis/RoleAPI" -import RowAPI from "./apis/RowAPI" -import ScreenAPI from "./apis/ScreenAPI" -import SelfAPI from "./apis/SelfAPI" -import TableAPI from "./apis/TableAPI" -import UserAPI from "./apis/UserAPI" -import DatasourcesAPI from "./apis/DatasourcesAPI" -import IntegrationsAPI from "./apis/IntegrationsAPI" -import QueriesAPI from "./apis/QueriesAPI" -import PermissionsAPI from "./apis/PermissionsAPI" -import LicenseAPI from "./apis/LicenseAPI" -import BudibaseInternalAPIClient from "./BudibaseInternalAPIClient" -import { State } from "../../types" - -export default class BudibaseInternalAPI { - client: BudibaseInternalAPIClient - - apps: AppAPI - auth: AuthAPI - environment: EnvironmentAPI - roles: RoleAPI - rows: RowAPI - screens: ScreenAPI - self: SelfAPI - tables: TableAPI - users: UserAPI - datasources: DatasourcesAPI - integrations: IntegrationsAPI - queries: QueriesAPI - permissions: PermissionsAPI - license: LicenseAPI - - constructor(state: State) { - this.client = new BudibaseInternalAPIClient(state) - - this.apps = new AppAPI(this.client) - this.auth = new AuthAPI(this.client, state) - this.environment = new EnvironmentAPI(this.client) - this.roles = new RoleAPI(this.client) - this.rows = new RowAPI(this.client) - this.screens = new ScreenAPI(this.client) - this.self = new SelfAPI(this.client) - this.tables = new TableAPI(this.client) - this.users = new UserAPI(this.client) - this.datasources = new DatasourcesAPI(this.client) - this.integrations = new IntegrationsAPI(this.client) - this.queries = new QueriesAPI(this.client) - this.permissions = new PermissionsAPI(this.client) - this.license = new LicenseAPI(this.client) - } -} diff --git a/qa-core/src/internal-api/api/BudibaseInternalAPIClient.ts b/qa-core/src/internal-api/api/BudibaseInternalAPIClient.ts deleted file mode 100644 index ba70bb5ce0..0000000000 --- a/qa-core/src/internal-api/api/BudibaseInternalAPIClient.ts +++ /dev/null @@ -1,80 +0,0 @@ -import env from "../../environment" -import fetch, { HeadersInit } from "node-fetch" -import { State } from "../../types" - -type APIMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE" - -interface ApiOptions { - method?: APIMethod - body?: object - headers?: HeadersInit | undefined -} - -class BudibaseInternalAPIClient { - host: string - state: State - - constructor(state: State) { - if (!env.BUDIBASE_URL) { - throw new Error("Must set BUDIBASE_URL env var") - } - this.host = `${env.BUDIBASE_URL}/api` - this.state = state - } - - apiCall = - (method: APIMethod) => - async (url = "", options: ApiOptions = {}) => { - const requestOptions = { - method, - body: JSON.stringify(options.body), - headers: { - "x-budibase-app-id": this.state.appId, - "Content-Type": "application/json", - Accept: "application/json", - cookie: this.state.cookie, - redirect: "follow", - follow: 20, - ...options.headers, - }, - credentials: "include", - } - - // prettier-ignore - // @ts-ignore - const response = await fetch(`${this.host}${url}`, requestOptions) - - let body: any - const contentType = response.headers.get("content-type") - if (contentType && contentType.includes("application/json")) { - body = await response.json() - } else { - body = await response.text() - } - - const data = { - request: requestOptions.body, - response: body, - } - const message = `${method} ${url} - ${response.status}` - - const isDebug = process.env.LOG_LEVEL === "debug" - if (response.status > 499) { - console.error(message, data) - } else if (response.status >= 400) { - console.warn(message, data) - } else if (isDebug) { - console.debug(message, data) - } - - return [response, body] - } - - post = this.apiCall("POST") - get = this.apiCall("GET") - patch = this.apiCall("PATCH") - del = this.apiCall("DELETE") - put = this.apiCall("PUT") -} - -export default BudibaseInternalAPIClient diff --git a/qa-core/src/internal-api/api/apis/AppAPI.ts b/qa-core/src/internal-api/api/apis/AppAPI.ts deleted file mode 100644 index 8b291a628e..0000000000 --- a/qa-core/src/internal-api/api/apis/AppAPI.ts +++ /dev/null @@ -1,152 +0,0 @@ -import { App, CreateAppRequest } from "@budibase/types" -import { Response } from "node-fetch" -import { - RouteConfig, - AppPackageResponse, - DeployConfig, - MessageResponse, -} from "../../../types" -import BudibaseInternalAPIClient from "../BudibaseInternalAPIClient" -import BaseAPI from "./BaseAPI" - -interface RenameAppBody { - name: string -} - -export default class AppAPI extends BaseAPI { - constructor(client: BudibaseInternalAPIClient) { - super(client) - } - - // TODO Fix the fetch apps to receive an optional number of apps and compare if the received app is more or less. - // each possible scenario should have its own method. - async fetchEmptyAppList(): Promise<[Response, App[]]> { - const [response, json] = await this.get(`/applications?status=all`) - expect(json.length).toBeGreaterThanOrEqual(0) - return [response, json] - } - - async fetchAllApplications(): Promise<[Response, App[]]> { - const [response, json] = await this.get(`/applications?status=all`) - expect(json.length).toBeGreaterThanOrEqual(1) - return [response, json] - } - - async canRender(): Promise<[Response, boolean]> { - const [response, json] = await this.get("/routing/client") - const publishedAppRenders = Object.keys(json.routes).length > 0 - expect(publishedAppRenders).toBe(true) - return [response, publishedAppRenders] - } - - async getAppPackage(appId: string): Promise<[Response, AppPackageResponse]> { - const [response, json] = await this.get(`/applications/${appId}/appPackage`) - expect(json.application.appId).toEqual(appId) - return [response, json] - } - - async publish(appId: string | undefined): Promise<[Response, DeployConfig]> { - const [response, json] = await this.post(`/applications/${appId}/publish`) - return [response, json] - } - - async create(body: CreateAppRequest): Promise { - const [response, json] = await this.post(`/applications`, body) - expect(json._id).toBeDefined() - return json - } - - async read(id: string): Promise<[Response, App]> { - const [response, json] = await this.get(`/applications/${id}`) - return [response, json.data] - } - - async sync(appId: string): Promise<[Response, MessageResponse]> { - const [response, json] = await this.post(`/applications/${appId}/sync`) - return [response, json] - } - - // TODO - async updateClient(appId: string, body: any): Promise<[Response, App]> { - const [response, json] = await this.put( - `/applications/${appId}/client/update`, - { body } - ) - return [response, json] - } - - async revertPublished(appId: string): Promise<[Response, MessageResponse]> { - const [response, json] = await this.post(`/dev/${appId}/revert`) - expect(json).toEqual({ - message: "Reverted changes successfully.", - }) - return [response, json] - } - - async revertUnpublished(appId: string): Promise<[Response, MessageResponse]> { - const [response, json] = await this.post( - `/dev/${appId}/revert`, - undefined, - 400 - ) - expect(json).toEqual({ - message: "App has not yet been deployed", - status: 400, - }) - return [response, json] - } - - async delete(appId: string): Promise { - const [response, _] = await this.del(`/applications/${appId}`) - return response - } - - async rename( - appId: string, - oldName: string, - body: RenameAppBody - ): Promise<[Response, App]> { - const [response, json] = await this.put(`/applications/${appId}`, body) - expect(json.name).not.toEqual(oldName) - return [response, json] - } - - async getRoutes(screenExists?: boolean): Promise<[Response, RouteConfig]> { - const [response, json] = await this.get(`/routing`) - if (screenExists) { - expect(json.routes["/test"]).toBeTruthy() - } else { - expect(json.routes["/test"]).toBeUndefined() - } - - return [response, json] - } - - async unpublish(appId: string): Promise<[Response]> { - const [response, json] = await this.post( - `/applications/${appId}/unpublish`, - undefined, - 204 - ) - return [response] - } - - async unlock(appId: string): Promise<[Response, MessageResponse]> { - const [response, json] = await this.del(`/dev/${appId}/lock`) - expect(json.message).toEqual("Lock released successfully.") - return [response, json] - } - - async updateIcon(appId: string): Promise<[Response, App]> { - const body = { - icon: { - name: "ConversionFunnel", - color: "var(--spectrum-global-color-red-400)", - }, - } - const [response, json] = await this.put(`/applications/${appId}`, body) - expect(json.icon.name).toEqual(body.icon.name) - expect(json.icon.color).toEqual(body.icon.color) - return [response, json] - } -} diff --git a/qa-core/src/internal-api/api/apis/AuthAPI.ts b/qa-core/src/internal-api/api/apis/AuthAPI.ts deleted file mode 100644 index fec9f7e503..0000000000 --- a/qa-core/src/internal-api/api/apis/AuthAPI.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { Response } from "node-fetch" -import BudibaseInternalAPIClient from "../BudibaseInternalAPIClient" -import { APIRequestOpts, State } from "../../../types" - -export default class AuthAPI { - state: State - client: BudibaseInternalAPIClient - - constructor(client: BudibaseInternalAPIClient, state: State) { - this.client = client - this.state = state - } - - async login( - tenantId: string, - email: String, - password: String, - opts: APIRequestOpts = { doExpect: true } - ): Promise<[Response, string]> { - const [response, json] = await this.client.post( - `/global/auth/${tenantId}/login`, - { - body: { - username: email, - password: password, - }, - } - ) - if (opts.doExpect) { - expect(response).toHaveStatusCode(200) - } - const cookie = response.headers.get("set-cookie") - return [response, cookie!] - } - - async logout(): Promise { - return this.client.post(`/global/auth/logout`) - } -} diff --git a/qa-core/src/internal-api/api/apis/BaseAPI.ts b/qa-core/src/internal-api/api/apis/BaseAPI.ts deleted file mode 100644 index c0a3b344d6..0000000000 --- a/qa-core/src/internal-api/api/apis/BaseAPI.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { Response } from "node-fetch" -import BudibaseInternalAPIClient from "../BudibaseInternalAPIClient" - -export default class BaseAPI { - client: BudibaseInternalAPIClient - - constructor(client: BudibaseInternalAPIClient) { - this.client = client - } - - async get(url: string, status?: number): Promise<[Response, any]> { - const [response, json] = await this.client.get(url) - expect(response).toHaveStatusCode(status ? status : 200) - return [response, json] - } - - async post( - url: string, - body?: any, - statusCode?: number - ): Promise<[Response, any]> { - const [response, json] = await this.client.post(url, { body }) - expect(response).toHaveStatusCode(statusCode ? statusCode : 200) - return [response, json] - } - - async put( - url: string, - body?: any, - statusCode?: number - ): Promise<[Response, any]> { - const [response, json] = await this.client.put(url, { body }) - expect(response).toHaveStatusCode(statusCode ? statusCode : 200) - return [response, json] - } - - async patch( - url: string, - body?: any, - statusCode?: number - ): Promise<[Response, any]> { - const [response, json] = await this.client.patch(url, { body }) - expect(response).toHaveStatusCode(statusCode ? statusCode : 200) - return [response, json] - } - - async del( - url: string, - statusCode?: number, - body?: any - ): Promise<[Response, any]> { - const [response, json] = await this.client.del(url, { body }) - expect(response).toHaveStatusCode(statusCode ? statusCode : 200) - return [response, json] - } -} diff --git a/qa-core/src/internal-api/api/apis/DatasourcesAPI.ts b/qa-core/src/internal-api/api/apis/DatasourcesAPI.ts deleted file mode 100644 index 9067ed4ab4..0000000000 --- a/qa-core/src/internal-api/api/apis/DatasourcesAPI.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { Response } from "node-fetch" -import { - Datasource, - CreateDatasourceResponse, - UpdateDatasourceResponse, -} from "@budibase/types" -import BudibaseInternalAPIClient from "../BudibaseInternalAPIClient" -import BaseAPI from "./BaseAPI" -import { DatasourceRequest } from "../../../types" - -export default class DatasourcesAPI extends BaseAPI { - constructor(client: BudibaseInternalAPIClient) { - super(client) - } - - async getIntegrations(): Promise<[Response, any]> { - const [response, json] = await this.get(`/integrations`) - const integrationsCount = Object.keys(json).length - expect(integrationsCount).toBe(16) - return [response, json] - } - - async getAll(): Promise<[Response, Datasource[]]> { - const [response, json] = await this.get(`/datasources`) - expect(json.length).toBeGreaterThan(0) - return [response, json] - } - - async getTable(dataSourceId: string): Promise<[Response, Datasource]> { - const [response, json] = await this.get(`/datasources/${dataSourceId}`) - expect(json._id).toEqual(dataSourceId) - return [response, json] - } - - async add( - body: DatasourceRequest - ): Promise<[Response, CreateDatasourceResponse]> { - const [response, json] = await this.post(`/datasources`, body) - expect(json.datasource._id).toBeDefined() - expect(json.datasource._rev).toBeDefined() - - return [response, json] - } - - async update( - body: Datasource - ): Promise<[Response, UpdateDatasourceResponse]> { - const [response, json] = await this.put(`/datasources/${body._id}`, body) - expect(json.datasource._id).toBeDefined() - expect(json.datasource._rev).toBeDefined() - - return [response, json] - } - - async delete(dataSourceId: string, revId: string): Promise { - const [response, json] = await this.del( - `/datasources/${dataSourceId}/${revId}` - ) - - return response - } -} diff --git a/qa-core/src/internal-api/api/apis/EnvironmentAPI.ts b/qa-core/src/internal-api/api/apis/EnvironmentAPI.ts deleted file mode 100644 index c9458d92a5..0000000000 --- a/qa-core/src/internal-api/api/apis/EnvironmentAPI.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { GetEnvironmentResponse } from "@budibase/types" -import BudibaseInternalAPIClient from "../BudibaseInternalAPIClient" -import { APIRequestOpts } from "../../../types" - -export default class EnvironmentAPI { - client: BudibaseInternalAPIClient - - constructor(client: BudibaseInternalAPIClient) { - this.client = client - } - - async getEnvironment( - opts: APIRequestOpts = { doExpect: true } - ): Promise { - const [response, json] = await this.client.get(`/system/environment`) - if (opts.doExpect) { - expect(response.status).toBe(200) - } - return json - } -} diff --git a/qa-core/src/internal-api/api/apis/IntegrationsAPI.ts b/qa-core/src/internal-api/api/apis/IntegrationsAPI.ts deleted file mode 100644 index 66d086357a..0000000000 --- a/qa-core/src/internal-api/api/apis/IntegrationsAPI.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { Response } from "node-fetch" -import BudibaseInternalAPIClient from "../BudibaseInternalAPIClient" -import BaseAPI from "./BaseAPI" - -export default class IntegrationsAPI extends BaseAPI { - constructor(client: BudibaseInternalAPIClient) { - super(client) - } - - async getAll(): Promise<[Response, any]> { - const [response, json] = await this.get(`/integrations`) - const integrationsCount = Object.keys(json).length - expect(integrationsCount).toBeGreaterThan(0) - return [response, json] - } -} diff --git a/qa-core/src/internal-api/api/apis/LicenseAPI.ts b/qa-core/src/internal-api/api/apis/LicenseAPI.ts deleted file mode 100644 index ef322e069a..0000000000 --- a/qa-core/src/internal-api/api/apis/LicenseAPI.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { Response } from "node-fetch" -import { - ActivateLicenseKeyRequest, - ActivateOfflineLicenseTokenRequest, - GetLicenseKeyResponse, - GetOfflineIdentifierResponse, - GetOfflineLicenseTokenResponse, -} from "@budibase/types" -import BudibaseInternalAPIClient from "../BudibaseInternalAPIClient" -import BaseAPI from "./BaseAPI" -import { APIRequestOpts } from "../../../types" - -export default class LicenseAPI extends BaseAPI { - constructor(client: BudibaseInternalAPIClient) { - super(client) - } - async getOfflineLicenseToken( - opts: { status?: number } = {} - ): Promise<[Response, GetOfflineLicenseTokenResponse]> { - const [response, body] = await this.get( - `/global/license/offline`, - opts.status - ) - return [response, body] - } - async deleteOfflineLicenseToken(): Promise<[Response]> { - const [response] = await this.del(`/global/license/offline`, 204) - return [response] - } - async activateOfflineLicenseToken( - body: ActivateOfflineLicenseTokenRequest - ): Promise<[Response]> { - const [response] = await this.post(`/global/license/offline`, body) - return [response] - } - async getOfflineIdentifier(): Promise< - [Response, GetOfflineIdentifierResponse] - > { - const [response, body] = await this.get( - `/global/license/offline/identifier` - ) - return [response, body] - } - - async getLicenseKey( - opts: { status?: number } = {} - ): Promise<[Response, GetLicenseKeyResponse]> { - const [response, body] = await this.get(`/global/license/key`, opts.status) - return [response, body] - } - - async activateLicenseKey( - body: ActivateLicenseKeyRequest - ): Promise<[Response]> { - const [response] = await this.post(`/global/license/key`, body) - return [response] - } - - async deleteLicenseKey(): Promise<[Response]> { - const [response] = await this.del(`/global/license/key`, 204) - return [response] - } -} diff --git a/qa-core/src/internal-api/api/apis/PermissionsAPI.ts b/qa-core/src/internal-api/api/apis/PermissionsAPI.ts deleted file mode 100644 index 126d4eb430..0000000000 --- a/qa-core/src/internal-api/api/apis/PermissionsAPI.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { Response } from "node-fetch" -import BudibaseInternalAPIClient from "../BudibaseInternalAPIClient" -import BaseAPI from "./BaseAPI" - -export default class PermissionsAPI extends BaseAPI { - constructor(client: BudibaseInternalAPIClient) { - super(client) - } - - async getAll(id: string): Promise<[Response, any]> { - const [response, json] = await this.get(`/permissions/${id}`) - return [response, json] - } -} diff --git a/qa-core/src/internal-api/api/apis/QueriesAPI.ts b/qa-core/src/internal-api/api/apis/QueriesAPI.ts deleted file mode 100644 index e3b9d1e474..0000000000 --- a/qa-core/src/internal-api/api/apis/QueriesAPI.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { Response } from "node-fetch" -import BudibaseInternalAPIClient from "../BudibaseInternalAPIClient" -import { PreviewQueryRequest, Query } from "@budibase/types" -import BaseAPI from "./BaseAPI" - -export default class QueriesAPI extends BaseAPI { - constructor(client: BudibaseInternalAPIClient) { - super(client) - } - - async preview(body: PreviewQueryRequest): Promise<[Response, any]> { - const [response, json] = await this.post(`/queries/preview`, body) - return [response, json] - } - - async save(body: Query): Promise<[Response, any]> { - const [response, json] = await this.post(`/queries`, body) - return [response, json] - } - - async getQuery(queryId: string): Promise<[Response, any]> { - const [response, json] = await this.get(`/queries/${queryId}`) - return [response, json] - } -} diff --git a/qa-core/src/internal-api/api/apis/RoleAPI.ts b/qa-core/src/internal-api/api/apis/RoleAPI.ts deleted file mode 100644 index 370e287382..0000000000 --- a/qa-core/src/internal-api/api/apis/RoleAPI.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { Response } from "node-fetch" -import { Role, UserRoles } from "@budibase/types" -import BudibaseInternalAPIClient from "../BudibaseInternalAPIClient" -import BaseAPI from "./BaseAPI" - -export default class RoleAPI extends BaseAPI { - constructor(client: BudibaseInternalAPIClient) { - super(client) - } - - async getRoles(): Promise<[Response, Role[]]> { - const [response, json] = await this.get(`/roles`) - return [response, json] - } - - async createRole(body: Partial): Promise<[Response, UserRoles]> { - const [response, json] = await this.post(`/roles`, body) - return [response, json] - } -} diff --git a/qa-core/src/internal-api/api/apis/RowAPI.ts b/qa-core/src/internal-api/api/apis/RowAPI.ts deleted file mode 100644 index 18c4874157..0000000000 --- a/qa-core/src/internal-api/api/apis/RowAPI.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { Response } from "node-fetch" -import { Row } from "@budibase/types" -import BudibaseInternalAPIClient from "../BudibaseInternalAPIClient" -import BaseAPI from "./BaseAPI" - -export default class RowAPI extends BaseAPI { - rowAdded: boolean - - constructor(client: BudibaseInternalAPIClient) { - super(client) - this.rowAdded = false - } - - async getAll(tableId: string): Promise<[Response, Row[]]> { - const [response, json] = await this.get(`/${tableId}/rows`) - if (this.rowAdded) { - expect(json.length).toBeGreaterThanOrEqual(1) - } - return [response, json] - } - async add(tableId: string, body: Row): Promise<[Response, Row]> { - const [response, json] = await this.post(`/${tableId}/rows`, body) - expect(json._id).toBeDefined() - expect(json._rev).toBeDefined() - expect(json.tableId).toEqual(tableId) - this.rowAdded = true - return [response, json] - } - - async delete(tableId: string, body: Row): Promise<[Response, Row[]]> { - const [response, json] = await this.del( - `/${tableId}/rows/`, - undefined, - body - ) - return [response, json] - } - - async searchNoPagination( - tableId: string, - body: string - ): Promise<[Response, Row[]]> { - const [response, json] = await this.post(`/${tableId}/search`, body) - expect(json.hasNextPage).toEqual(false) - return [response, json.rows] - } - - async searchWithPagination( - tableId: string, - body: string - ): Promise<[Response, Row[]]> { - const [response, json] = await this.post(`/${tableId}/search`, body) - expect(json.hasNextPage).toEqual(true) - expect(json.rows.length).toEqual(10) - return [response, json.rows] - } -} diff --git a/qa-core/src/internal-api/api/apis/ScreenAPI.ts b/qa-core/src/internal-api/api/apis/ScreenAPI.ts deleted file mode 100644 index df3d699cbd..0000000000 --- a/qa-core/src/internal-api/api/apis/ScreenAPI.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Response } from "node-fetch" -import { Screen } from "@budibase/types" -import { ScreenRequest } from "../../../types/screens" -import BudibaseInternalAPIClient from "../BudibaseInternalAPIClient" -import BaseAPI from "./BaseAPI" - -export default class ScreenAPI extends BaseAPI { - constructor(client: BudibaseInternalAPIClient) { - super(client) - } - - async create(body: ScreenRequest): Promise<[Response, Screen]> { - const [response, json] = await this.post(`/screens`, body) - expect(json._id).toBeDefined() - expect(json.routing.roleId).toBe(body.routing.roleId) - return [response, json] - } - - async delete(screenId: string, rev: string): Promise<[Response, Screen]> { - const [response, json] = await this.del(`/screens/${screenId}/${rev}`) - return [response, json] - } -} diff --git a/qa-core/src/internal-api/api/apis/SelfAPI.ts b/qa-core/src/internal-api/api/apis/SelfAPI.ts deleted file mode 100644 index cd162053a1..0000000000 --- a/qa-core/src/internal-api/api/apis/SelfAPI.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { Response } from "node-fetch" -import { User } from "@budibase/types" -import BudibaseInternalAPIClient from "../BudibaseInternalAPIClient" -import { ApiKeyResponse } from "../../../types" -import BaseAPI from "./BaseAPI" - -export default class SelfAPI extends BaseAPI { - constructor(client: BudibaseInternalAPIClient) { - super(client) - } - - async getSelf(): Promise<[Response, Partial]> { - const [response, json] = await this.get(`/global/self`) - return [response, json] - } - - async changeSelfPassword(body: Partial): Promise<[Response, User]> { - const [response, json] = await this.post(`/global/self`, body) - expect(json._id).toEqual(body._id) - expect(json._rev).not.toEqual(body._rev) - return [response, json] - } - - async getApiKey(): Promise { - const [response, json] = await this.get(`/global/self/api_key`) - expect(json).toHaveProperty("apiKey") - return json - } -} diff --git a/qa-core/src/internal-api/api/apis/TableAPI.ts b/qa-core/src/internal-api/api/apis/TableAPI.ts deleted file mode 100644 index 88629381b1..0000000000 --- a/qa-core/src/internal-api/api/apis/TableAPI.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { Response } from "node-fetch" -import { Table } from "@budibase/types" -import BudibaseInternalAPIClient from "../BudibaseInternalAPIClient" -import { MessageResponse } from "../../../types" -import BaseAPI from "./BaseAPI" - -export default class TableAPI extends BaseAPI { - constructor(client: BudibaseInternalAPIClient) { - super(client) - } - - async getAll(expectedNumber: Number): Promise<[Response, Table[]]> { - const [response, json] = await this.get(`/tables`) - expect(json.length).toBe(expectedNumber) - return [response, json] - } - - async getTableById(id: string): Promise<[Response, Table]> { - const [response, json] = await this.get(`/tables/${id}`) - expect(json._id).toEqual(id) - return [response, json] - } - - async save(body: any, columnAdded?: boolean): Promise<[Response, Table]> { - const [response, json] = await this.post(`/tables`, body) - expect(json._id).toBeDefined() - expect(json._rev).toBeDefined() - if (columnAdded) { - expect(json.schema.TestColumn).toBeDefined() - } - - return [response, json] - } - - async forbiddenSave(body: any): Promise<[Response, Table]> { - const [response, json] = await this.post(`/tables`, body, 403) - return [response, json] - } - - async delete( - id: string, - revId: string - ): Promise<[Response, MessageResponse]> { - const [response, json] = await this.del(`/tables/${id}/${revId}`) - expect(json.message).toEqual(`Table ${id} deleted.`) - return [response, json] - } -} diff --git a/qa-core/src/internal-api/api/apis/UserAPI.ts b/qa-core/src/internal-api/api/apis/UserAPI.ts deleted file mode 100644 index 7ecb7da622..0000000000 --- a/qa-core/src/internal-api/api/apis/UserAPI.ts +++ /dev/null @@ -1,107 +0,0 @@ -import { Response } from "node-fetch" -import { Role, User, UserDeletedEvent, UserRoles } from "@budibase/types" -import BudibaseInternalAPIClient from "../BudibaseInternalAPIClient" -import { MessageResponse } from "../../../types" -import BaseAPI from "./BaseAPI" - -export default class UserAPI extends BaseAPI { - constructor(client: BudibaseInternalAPIClient) { - super(client) - } - - async search(): Promise<[Response, Partial[]]> { - const [response, json] = await this.post(`/global/users/search`, {}) - - expect(json.data.length).toBeGreaterThan(0) - return [response, json] - } - - async getSelf(): Promise<[Response, Partial]> { - const [response, json] = await this.get(`/global/self`) - - return [response, json] - } - - async getAll(): Promise<[Response, Partial[]]> { - const [response, json] = await this.get(`/global/users`) - - expect(json.length).toBeGreaterThan(0) - return [response, json] - } - - // This endpoint is used for one or more users when we want add users with passwords set. - async addMultiple(userList: Partial[]): Promise<[Response, any]> { - const body = { - create: { - users: userList, - groups: [], - }, - } - const [response, json] = await this.post(`/global/users/bulk`, body) - - expect(json.created.unsuccessful.length).toEqual(0) - expect(json.created.successful.length).toEqual(body.create.users.length) - return [response, json] - } - - async deleteMultiple(userId: string[]): Promise<[Response, MessageResponse]> { - const body = { - delete: { - userIds: [userId], - }, - } - const [response, json] = await this.post(`/global/users/bulk`, body) - expect(json.deleted.successful.length).toEqual(1) - expect(json.deleted.unsuccessful.length).toEqual(0) - expect(json.deleted.successful[0].userId).toEqual(userId) - return [response, json] - } - async delete(userId: string): Promise<[Response, UserDeletedEvent]> { - const [response, json] = await this.del(`/global/users/${userId}`) - expect(json.message).toEqual(`User ${userId} deleted.`) - return [response, json] - } - - async invite(body: any): Promise<[Response, MessageResponse]> { - const [response, json] = await this.post(`/global/users/multi/invite`, body) - expect(json.unsuccessful.length).toEqual(0) - expect(json.successful.length).toEqual(body.length) - return [response, json] - } - - async getRoles(): Promise<[Response, Role[]]> { - const [response, json] = await this.get(`/roles`) - return [response, json] - } - - async updateInfo(body: any): Promise<[Response, User]> { - const [response, json] = await this.post(`/global/users/`, body) - expect(json._id).toEqual(body._id) - expect(json._rev).not.toEqual(body._rev) - return [response, json] - } - - async forcePasswordReset(body: any): Promise<[Response, User]> { - const [response, json] = await this.post(`/global/users/`, body) - expect(json._id).toEqual(body._id) - expect(json._rev).not.toEqual(body._rev) - return [response, json] - } - - async getInfo(userId: string): Promise<[Response, User]> { - const [response, json] = await this.get(`/global/users/${userId}`) - return [response, json] - } - - async changeSelfPassword(body: Partial): Promise<[Response, User]> { - const [response, json] = await this.post(`/global/self`, body) - expect(json._id).toEqual(body._id) - expect(json._rev).not.toEqual(body._rev) - return [response, json] - } - - async createRole(body: Partial): Promise<[Response, UserRoles]> { - const [response, json] = await this.post(`/roles`, body) - return [response, json] - } -} diff --git a/qa-core/src/internal-api/api/index.ts b/qa-core/src/internal-api/api/index.ts deleted file mode 100644 index b6d01a710a..0000000000 --- a/qa-core/src/internal-api/api/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default as BudibaseInternalAPI } from "./BudibaseInternalAPI" diff --git a/qa-core/src/internal-api/config/TestConfiguration.ts b/qa-core/src/internal-api/config/TestConfiguration.ts deleted file mode 100644 index dc5fec73dc..0000000000 --- a/qa-core/src/internal-api/config/TestConfiguration.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { BudibaseInternalAPI } from "../api" -import { BudibaseTestConfiguration } from "../../shared" - -export default class TestConfiguration extends BudibaseTestConfiguration { - // apis - api: BudibaseInternalAPI - - // context - context: T - - constructor() { - super() - // for brevity - this.api = this.internalApi - this.context = {} - } - - async beforeAll() { - await super.beforeAll() - } - - async afterAll() { - await super.afterAll() - } -} diff --git a/qa-core/src/internal-api/config/index.ts b/qa-core/src/internal-api/config/index.ts deleted file mode 100644 index 182c7825b6..0000000000 --- a/qa-core/src/internal-api/config/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default as TestConfiguration } from "./TestConfiguration" diff --git a/qa-core/src/internal-api/fixtures/accounts.ts b/qa-core/src/internal-api/fixtures/accounts.ts deleted file mode 100644 index ff7042a1ad..0000000000 --- a/qa-core/src/internal-api/fixtures/accounts.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { generator } from "../../shared" -import { Hosting, CreateAccountRequest } from "@budibase/types" - -export const generateAccount = (): CreateAccountRequest => { - const uuid = generator.guid() - - const email = `qa+${uuid}@budibase.com` - const tenant = `tenant${uuid.replace(/-/g, "")}` - - return { - email, - hosting: Hosting.CLOUD, - name: email, - password: uuid, - profession: "software_engineer", - size: "10+", - tenantId: tenant, - tenantName: tenant, - } -} diff --git a/qa-core/src/internal-api/fixtures/applications.ts b/qa-core/src/internal-api/fixtures/applications.ts deleted file mode 100644 index 59f73ba863..0000000000 --- a/qa-core/src/internal-api/fixtures/applications.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { generator } from "../../shared" -import { CreateAppRequest } from "@budibase/types" - -function uniqueWord() { - return generator.word() + generator.hash() -} - -export const generateApp = ( - overrides: Partial = {} -): CreateAppRequest => ({ - name: uniqueWord(), - url: `/${uniqueWord()}`, - ...overrides, -}) - -// Applications type doesn't work here, save to add useTemplate parameter? -export const appFromTemplate = (): CreateAppRequest => { - return { - name: uniqueWord(), - url: `/${uniqueWord()}`, - // @ts-ignore - useTemplate: "true", - templateName: "Near Miss Register", - templateKey: "app/near-miss-register", - templateFile: undefined, - } -} diff --git a/qa-core/src/internal-api/fixtures/datasources.ts b/qa-core/src/internal-api/fixtures/datasources.ts deleted file mode 100644 index c7f969a98c..0000000000 --- a/qa-core/src/internal-api/fixtures/datasources.ts +++ /dev/null @@ -1,122 +0,0 @@ -import { Datasource } from "@budibase/types" -import { DatasourceRequest } from "../../types" -import { generator } from "../../shared" - -// Add information about the data source to the fixtures file from 1password -export const mongoDB = (): DatasourceRequest => { - return { - datasource: { - name: "MongoDB", - source: "MONGODB", - type: "datasource", - config: { - connectionString: process.env.MONGODB_CONNECTION_STRING, - db: process.env.MONGODB_DB, - }, - }, - - fetchSchema: false, - } -} - -export const postgresSQL = (): DatasourceRequest => { - return { - datasource: { - name: "PostgresSQL", - plus: true, - source: "POSTGRES", - type: "datasource", - config: { - database: process.env.POSTGRES_DB, - host: process.env.POSTGRES_HOST, - password: process.env.POSTGRES_PASSWORD, - port: process.env.POSTGRES_PORT, - schema: "public", - user: process.env.POSTGRES_USER, - }, - }, - fetchSchema: true, - } -} -export const mariaDB = (): DatasourceRequest => { - return { - datasource: { - name: "MariaDB", - plus: true, - source: "MYSQL", - type: "datasource", - config: { - database: process.env.MARIADB_DB, - host: process.env.MARIADB_HOST, - password: process.env.MARIADB_PASSWORD, - port: process.env.MARIADB_PORT, - schema: "public", - user: process.env.MARIADB_USER, - }, - }, - fetchSchema: true, - } -} - -export const restAPI = (): DatasourceRequest => { - return { - datasource: { - name: "RestAPI", - source: "REST", - type: "datasource", - config: { - defaultHeaders: {}, - rejectUnauthorized: true, - url: process.env.REST_API_BASE_URL, - }, - }, - fetchSchema: false, - } -} - -export const generateRelationshipForMySQL = ( - updatedDataSourceJson: any -): Datasource => { - const entities = updatedDataSourceJson!.datasource!.entities! - const datasourceId = updatedDataSourceJson!.datasource!._id! - const relationShipBody = { - ...updatedDataSourceJson.datasource, - entities: { - ...updatedDataSourceJson.datasource.entities, - employees: { - ...entities.employees, - schema: { - ...entities.employees.schema, - salaries: { - tableId: `${datasourceId}__salaries`, - name: "salaries", - relationshipType: "many-to-one", - fieldName: "salary", - type: "link", - main: true, - _id: generator.string(), - foreignKey: "emp_no", - }, - }, - }, - titles: { - ...entities.titles, - schema: { - ...entities.titles.schema, - employees: { - tableId: `${datasourceId}__employees`, - name: "employees", - relationshipType: "one-to-many", - fieldName: "emp_no", - type: "link", - main: true, - _id: generator.string(), - foreignKey: "emp_no", - }, - }, - }, - }, - } - - return relationShipBody -} diff --git a/qa-core/src/internal-api/fixtures/index.ts b/qa-core/src/internal-api/fixtures/index.ts deleted file mode 100644 index 38291052b8..0000000000 --- a/qa-core/src/internal-api/fixtures/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -export * as accounts from "./accounts" -export * as apps from "./applications" -export * as rows from "./rows" -export * as screens from "./screens" -export * as tables from "./tables" -export * as users from "./users" -export * as datasources from "./datasources" -export * as queries from "./queries" diff --git a/qa-core/src/internal-api/fixtures/queries.ts b/qa-core/src/internal-api/fixtures/queries.ts deleted file mode 100644 index 0a28c23ab7..0000000000 --- a/qa-core/src/internal-api/fixtures/queries.ts +++ /dev/null @@ -1,123 +0,0 @@ -import { PreviewQueryRequest } from "@budibase/types" - -const query = (datasourceId: string, fields: any): any => { - return { - datasourceId: datasourceId, - fields: fields, - name: "Query 1", - parameters: {}, - queryVerb: "read", - schema: {}, - transformer: "return data", - } -} - -export const mariaDB = (datasourceId: string): PreviewQueryRequest => { - const fields = { - sql: "SELECT * FROM employees LIMIT 10;", - } - return query(datasourceId, fields) -} - -export const mongoDB = (datasourceId: string): PreviewQueryRequest => { - const fields = { - extra: { - collection: "movies", - actionType: "find", - }, - json: "", - } - return query(datasourceId, fields) -} - -export const postgres = (datasourceId: string): PreviewQueryRequest => { - const fields = { - sql: "SELECT * FROM customers;", - } - return query(datasourceId, fields) -} - -export const expectedSchemaFields = { - mariaDB: { - birth_date: "string", - emp_no: "number", - first_name: "string", - gender: "string", - hire_date: "string", - last_name: "string", - }, - mongoDB: { - directors: "array", - genres: "array", - image: "string", - plot: "string", - rank: "number", - rating: "number", - release_date: "string", - running_time_secs: "number", - title: "string", - year: "number", - _id: "string", - }, - postgres: { - address: "string", - city: "string", - company_name: "string", - contact_name: "string", - contact_title: "string", - country: "string", - customer_id: "string", - fax: "string", - phone: "string", - postal_code: "string", - region: "string", - }, - restAPI: { - abilities: "array", - base_experience: "number", - forms: "array", - game_indices: "array", - height: "number", - held_items: "array", - id: "number", - is_default: "string", - location_area_encounters: "string", - moves: "array", - name: "string", - order: "number", - past_types: "array", - species: "json", - sprites: "json", - stats: "array", - types: "array", - weight: "number", - }, -} - -const request = (datasourceId: string, fields: any, flags: any): any => { - return { - datasourceId: datasourceId, - fields: fields, - flags: flags, - name: "Query 1", - parameters: {}, - queryVerb: "read", - schema: {}, - transformer: "return data", - } -} -export const restAPI = (datasourceId: string): PreviewQueryRequest => { - const fields = { - authConfigId: null, - bodyType: "none", - disabledHeaders: {}, - headers: {}, - pagination: {}, - path: `${process.env.REST_API_BASE_URL}/pokemon/ditto`, - queryString: "", - } - const flags = { - urlName: true, - } - return request(datasourceId, fields, flags) -} diff --git a/qa-core/src/internal-api/fixtures/rows.ts b/qa-core/src/internal-api/fixtures/rows.ts deleted file mode 100644 index f1f1c00493..0000000000 --- a/qa-core/src/internal-api/fixtures/rows.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { Row } from "@budibase/types" - -export const generateNewRowForTable = (tableId: string): Row => { - return { - TestColumn: "TestRow", - tableId: tableId, - } -} - -export const searchBody = (primaryDisplay: string): any => { - return { - bookmark: null, - limit: 10, - paginate: true, - query: { - contains: {}, - containsAny: {}, - empty: {}, - equal: {}, - fuzzy: {}, - notContains: {}, - notEmpty: {}, - notEqual: {}, - oneOf: {}, - range: {}, - string: {}, - }, - sort: primaryDisplay, - sortOrder: "ascending", - sortType: "string", - } -} diff --git a/qa-core/src/internal-api/fixtures/screens.ts b/qa-core/src/internal-api/fixtures/screens.ts deleted file mode 100644 index c28a5ed86f..0000000000 --- a/qa-core/src/internal-api/fixtures/screens.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { generator } from "../../shared" -import { ScreenRequest } from "../../types" - -const randomId = generator.guid() - -export const generateScreen = (roleId: string): ScreenRequest => ({ - showNavigation: true, - width: "Large", - name: randomId, - template: "createFromScratch", - props: { - _id: randomId, - _component: "@budibase/standard-components/container", - _styles: { - normal: {}, - hover: {}, - active: {}, - selected: {}, - }, - _children: [], - _instanceName: "New Screen", - direction: "column", - hAlign: "stretch", - vAlign: "top", - size: "grow", - gap: "M", - }, - routing: { - route: "/test", - roleId: roleId, - homeScreen: false, - }, -}) diff --git a/qa-core/src/internal-api/fixtures/tables.ts b/qa-core/src/internal-api/fixtures/tables.ts deleted file mode 100644 index 3de950fd9f..0000000000 --- a/qa-core/src/internal-api/fixtures/tables.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { Table } from "@budibase/types" - -export const generateTable = (): Table => { - return { - name: "Test Table", - schema: {}, - sourceId: "bb_internal", - type: "internal", - } -} - -export const generateNewColumnForTable = (tableData: any): Table => { - const newColumn = tableData - newColumn.schema = { - TestColumn: { - type: "string", - name: "TestColumn", - constraints: { - presence: { allowEmpty: false }, - length: { maximum: null }, - type: "string", - }, - }, - } - newColumn.indexes = { - 0: "TestColumn", - } - newColumn.updatedAt = new Date().toISOString() - return newColumn -} diff --git a/qa-core/src/internal-api/fixtures/users.ts b/qa-core/src/internal-api/fixtures/users.ts deleted file mode 100644 index 9979ef7663..0000000000 --- a/qa-core/src/internal-api/fixtures/users.ts +++ /dev/null @@ -1,82 +0,0 @@ -import { generator } from "../../shared" -import { User } from "@budibase/types" - -const generateDeveloper = (): Partial => { - const randomId = generator.guid() - return { - email: `${randomId}@budibase.com`, - password: randomId, - roles: {}, - forceResetPassword: true, - builder: { - global: true, - }, - } -} - -const generateAdmin = (): Partial => { - const randomId = generator.guid() - return { - email: `${randomId}@budibase.com`, - password: randomId, - roles: {}, - forceResetPassword: true, - admin: { - global: true, - }, - builder: { - global: true, - }, - } -} -const generateAppUser = (): Partial => { - const randomId = generator.guid() - return { - email: `${randomId}@budibase.com`, - password: randomId, - roles: {}, - forceResetPassword: true, - admin: { - global: false, - }, - builder: { - global: false, - }, - } -} - -export const generateInviteUser = (): Object[] => { - const randomId = generator.guid() - return [ - { - email: `${randomId}@budibase.com`, - userInfo: { - userGroups: [], - }, - }, - ] -} - -export const generateUser = ( - amount: number = 1, - role?: string -): Partial[] => { - const userList: Partial[] = [] - for (let i = 0; i < amount; i++) { - switch (role) { - case "admin": - userList.push(generateAdmin()) - break - case "developer": - userList.push(generateDeveloper()) - break - case "appUser": - userList.push(generateAppUser()) - break - default: - userList.push(generateAppUser()) - break - } - } - return userList -} diff --git a/qa-core/src/internal-api/index.ts b/qa-core/src/internal-api/index.ts deleted file mode 100644 index e1a716605a..0000000000 --- a/qa-core/src/internal-api/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./api" diff --git a/qa-core/src/internal-api/tests/dataSources/mariaDB.integration.spec.ts b/qa-core/src/internal-api/tests/dataSources/mariaDB.integration.spec.ts deleted file mode 100644 index 24c9cea94b..0000000000 --- a/qa-core/src/internal-api/tests/dataSources/mariaDB.integration.spec.ts +++ /dev/null @@ -1,106 +0,0 @@ -import TestConfiguration from "../../config/TestConfiguration" -import * as fixtures from "../../fixtures" -import { Query } from "@budibase/types" - -describe("Internal API - Data Sources: MariaDB", () => { - const config = new TestConfiguration() - - beforeAll(async () => { - await config.beforeAll() - }) - - afterAll(async () => { - await config.afterAll() - }) - - it("Create an app with a data source - MariaDB", async () => { - // Create app - await config.createApp() - - // Get all integrations - await config.api.integrations.getAll() - - // Add data source - const [dataSourceResponse, dataSourceJson] = - await config.api.datasources.add(fixtures.datasources.mariaDB()) - - // Update data source - const newDataSourceInfo = { - ...dataSourceJson.datasource, - name: "MariaDB2", - } - const [updatedDataSourceResponse, updatedDataSourceJson] = - await config.api.datasources.update(newDataSourceInfo) - - // Query data source - const [queryResponse, queryJson] = await config.api.queries.preview( - fixtures.queries.mariaDB(updatedDataSourceJson.datasource._id!) - ) - - expect(queryJson.rows.length).toEqual(10) - expect(queryJson.schemaFields).toEqual( - fixtures.queries.expectedSchemaFields.mariaDB - ) - - // Save query - const datasourcetoSave: Query = { - ...fixtures.queries.mariaDB(updatedDataSourceJson.datasource._id!), - parameters: [], - } - - const [saveQueryResponse, saveQueryJson] = await config.api.queries.save( - datasourcetoSave - ) - // Get Query - const [getQueryResponse, getQueryJson] = await config.api.queries.getQuery( - saveQueryJson._id - ) - - // Get Query permissions - const [getQueryPermissionsResponse, getQueryPermissionsJson] = - await config.api.permissions.getAll(saveQueryJson._id!) - - // Delete data source - const deleteResponse = await config.api.datasources.delete( - updatedDataSourceJson.datasource._id!, - updatedDataSourceJson.datasource._rev! - ) - }) - - it("Create a relationship", async () => { - // Create app - await config.createApp() - - // Get all integrations - await config.api.integrations.getAll() - - // Add data source - const [dataSourceResponse, dataSourceJson] = - await config.api.datasources.add(fixtures.datasources.mariaDB()) - - // Update data source - const newDataSourceInfo = { - ...dataSourceJson.datasource, - name: "MariaDB2", - } - const [updatedDataSourceResponse, updatedDataSourceJson] = - await config.api.datasources.update(newDataSourceInfo) - - // Query data source - const [queryResponse, queryJson] = await config.api.queries.preview( - fixtures.queries.mariaDB(updatedDataSourceJson.datasource._id!) - ) - - expect(queryJson.rows.length).toBeGreaterThan(9) - expect(queryJson.schemaFields).toEqual( - fixtures.queries.expectedSchemaFields.mariaDB - ) - - // Add relationship - const relationShipBody = fixtures.datasources.generateRelationshipForMySQL( - updatedDataSourceJson - ) - const [relationshipResponse, relationshipJson] = - await config.api.datasources.update(relationShipBody) - }) -}) diff --git a/qa-core/src/internal-api/tests/dataSources/mongoDB.integration.spec.ts b/qa-core/src/internal-api/tests/dataSources/mongoDB.integration.spec.ts deleted file mode 100644 index efa9eb2dd2..0000000000 --- a/qa-core/src/internal-api/tests/dataSources/mongoDB.integration.spec.ts +++ /dev/null @@ -1,69 +0,0 @@ -import TestConfiguration from "../../config/TestConfiguration" -import * as fixtures from "../../fixtures" -import { Query } from "@budibase/types" - -describe.skip("Internal API - Data Sources: MongoDB", () => { - const config = new TestConfiguration() - - beforeAll(async () => { - await config.beforeAll() - }) - - afterAll(async () => { - await config.afterAll() - }) - - it("Create an app with a data source - MongoDB", async () => { - // Create app - await config.createApp() - - // Get all integrations - await config.api.integrations.getAll() - - // Add data source - const [dataSourceResponse, dataSourceJson] = - await config.api.datasources.add(fixtures.datasources.mongoDB()) - - // Update data source - const newDataSourceInfo = { - ...dataSourceJson.datasource, - name: "MongoDB2", - } - const [updatedDataSourceResponse, updatedDataSourceJson] = - await config.api.datasources.update(newDataSourceInfo) - - // Query data source - const [queryResponse, queryJson] = await config.api.queries.preview( - fixtures.queries.mongoDB(updatedDataSourceJson.datasource._id!) - ) - - expect(queryJson.rows.length).toBeGreaterThan(10) - expect(queryJson.schemaFields).toEqual( - fixtures.queries.expectedSchemaFields.mongoDB - ) - - // Save query - const datasourcetoSave: Query = { - ...fixtures.queries.mongoDB(updatedDataSourceJson.datasource._id!), - parameters: [], - } - - const [saveQueryResponse, saveQueryJson] = await config.api.queries.save( - datasourcetoSave - ) - // Get Query - const [getQueryResponse, getQueryJson] = await config.api.queries.getQuery( - saveQueryJson._id - ) - - // Get Query permissions - const [getQueryPermissionsResponse, getQueryPermissionsJson] = - await config.api.permissions.getAll(saveQueryJson._id!) - - // Delete data source - const deleteResponse = await config.api.datasources.delete( - updatedDataSourceJson.datasource._id!, - updatedDataSourceJson.datasource._rev! - ) - }) -}) diff --git a/qa-core/src/internal-api/tests/dataSources/postgresSQL.integration.spec.ts b/qa-core/src/internal-api/tests/dataSources/postgresSQL.integration.spec.ts deleted file mode 100644 index 2aabd608bc..0000000000 --- a/qa-core/src/internal-api/tests/dataSources/postgresSQL.integration.spec.ts +++ /dev/null @@ -1,69 +0,0 @@ -import TestConfiguration from "../../config/TestConfiguration" -import * as fixtures from "../../fixtures" -import { Query } from "@budibase/types" - -describe("Internal API - Data Sources: PostgresSQL", () => { - const config = new TestConfiguration() - - beforeAll(async () => { - await config.beforeAll() - }) - - afterAll(async () => { - await config.afterAll() - }) - - it("Create an app with a data source - PostgresSQL", async () => { - // Create app - await config.createApp() - - // Get all integrations - await config.api.integrations.getAll() - - // Add data source - const [dataSourceResponse, dataSourceJson] = - await config.api.datasources.add(fixtures.datasources.postgresSQL()) - - // Update data source - const newDataSourceInfo = { - ...dataSourceJson.datasource, - name: "PostgresSQL2", - } - const [updatedDataSourceResponse, updatedDataSourceJson] = - await config.api.datasources.update(newDataSourceInfo) - - // Query data source - const [queryResponse, queryJson] = await config.api.queries.preview( - fixtures.queries.postgres(updatedDataSourceJson.datasource._id!) - ) - - expect(queryJson.rows.length).toBeGreaterThan(10) - expect(queryJson.schemaFields).toEqual( - fixtures.queries.expectedSchemaFields.postgres - ) - - // Save query - const datasourcetoSave: Query = { - ...fixtures.queries.postgres(updatedDataSourceJson.datasource._id!), - parameters: [], - } - - const [saveQueryResponse, saveQueryJson] = await config.api.queries.save( - datasourcetoSave - ) - // Get Query - const [getQueryResponse, getQueryJson] = await config.api.queries.getQuery( - saveQueryJson._id! - ) - - // Get Query permissions - const [getQueryPermissionsResponse, getQueryPermissionsJson] = - await config.api.permissions.getAll(saveQueryJson._id!) - - // Delete data source - const deleteResponse = await config.api.datasources.delete( - updatedDataSourceJson.datasource._id!, - updatedDataSourceJson.datasource._rev! - ) - }) -}) diff --git a/qa-core/src/internal-api/tests/dataSources/restAPI.integration.spec.ts b/qa-core/src/internal-api/tests/dataSources/restAPI.integration.spec.ts deleted file mode 100644 index 649e21db78..0000000000 --- a/qa-core/src/internal-api/tests/dataSources/restAPI.integration.spec.ts +++ /dev/null @@ -1,69 +0,0 @@ -import TestConfiguration from "../../config/TestConfiguration" -import * as fixtures from "../../fixtures" -import { Query } from "@budibase/types" - -describe("Internal API - Data Sources: REST API", () => { - const config = new TestConfiguration() - - beforeAll(async () => { - await config.beforeAll() - }) - - afterAll(async () => { - await config.afterAll() - }) - - it("Create an app with a data source - REST API", async () => { - // Create app - await config.createApp() - - // Get all integrations - await config.api.integrations.getAll() - - // Add data source - const [dataSourceResponse, dataSourceJson] = - await config.api.datasources.add(fixtures.datasources.restAPI()) - - // Update data source - const newDataSourceInfo = { - ...dataSourceJson.datasource, - name: "RestAPI - Updated", - } - const [updatedDataSourceResponse, updatedDataSourceJson] = - await config.api.datasources.update(newDataSourceInfo) - - // Query data source - const [queryResponse, queryJson] = await config.api.queries.preview( - fixtures.queries.restAPI(updatedDataSourceJson.datasource._id!) - ) - - expect(queryJson.rows.length).toEqual(1) - expect(queryJson.schemaFields).toEqual( - fixtures.queries.expectedSchemaFields.restAPI - ) - - // Save query - const datasourcetoSave: Query = { - ...fixtures.queries.postgres(updatedDataSourceJson.datasource._id!), - parameters: [], - } - - const [saveQueryResponse, saveQueryJson] = await config.api.queries.save( - datasourcetoSave - ) - // Get Query - const [getQueryResponse, getQueryJson] = await config.api.queries.getQuery( - saveQueryJson._id! - ) - - // Get Query permissions - const [getQueryPermissionsResponse, getQueryPermissionsJson] = - await config.api.permissions.getAll(saveQueryJson._id!) - - // Delete data source - const deleteResponse = await config.api.datasources.delete( - updatedDataSourceJson.datasource._id!, - updatedDataSourceJson.datasource._rev! - ) - }) -}) diff --git a/qa-core/src/internal-api/tests/users/customRoles.spec.ts b/qa-core/src/internal-api/tests/users/customRoles.spec.ts deleted file mode 100644 index a02e9f315a..0000000000 --- a/qa-core/src/internal-api/tests/users/customRoles.spec.ts +++ /dev/null @@ -1,315 +0,0 @@ -import TestConfiguration from "../../config/TestConfiguration" -import { App, User } from "@budibase/types" -import { db } from "@budibase/backend-core" -import * as fixtures from "./../../fixtures" - -describe.skip("Internal API - App Specific Roles & Permissions", () => { - const config = new TestConfiguration() - let app: Partial - - // Before each test, login as admin. Some tests will require login as a different user - beforeEach(async () => { - await config.beforeAll() - app = await config.createApp() - }) - - afterAll(async () => { - await config.afterAll() - }) - - it("Custom role access for level 1 permissions", async () => { - // Set up user - const appUser = fixtures.users.generateUser() - expect(appUser[0].builder?.global).toEqual(false) - expect(appUser[0].admin?.global).toEqual(false) - const [createUserResponse, createUserJson] = - await config.api.users.addMultiple(appUser) - - //Create level 1 role - const role = { - inherits: "BASIC", - permissionId: "public", - name: "level 1", - } - const [createRoleResponse, createRoleJson] = - await config.api.users.createRole(role) - - // Update user roles - const [userInfoResponse, userInfoJson] = await config.api.users.getInfo( - createUserJson.created.successful[0]._id - ) - const prodAppId = db.getProdAppID(app.appId!) - - // Roles must always be set with prod appID - const body: User = { - ...userInfoJson, - roles: { - [prodAppId]: createRoleJson._id, - }, - } - await config.api.users.updateInfo(body) - - const [changedUserInfoResponse, changedUserInfoJson] = - await config.api.users.getInfo(createUserJson.created.successful[0]._id) - expect(changedUserInfoJson.roles[prodAppId]).toBeDefined() - expect(changedUserInfoJson.roles[prodAppId]).toEqual(createRoleJson._id) - - await config.api.screens.create(fixtures.screens.generateScreen("BASIC")) - await config.api.screens.create(fixtures.screens.generateScreen("POWER")) - await config.api.screens.create(fixtures.screens.generateScreen("ADMIN")) - - await config.api.apps.publish(app.appId) - const [firstappPackageResponse, firstappPackageJson] = - await config.api.apps.getAppPackage(app.appId!) - expect(firstappPackageJson.screens).toBeDefined() - expect(firstappPackageJson.screens.length).toEqual(3) - - // login with level 1 user - await config.login( - config.state.tenantId!, - appUser[0].email!, - appUser[0].password! - ) - const [selfInfoResponse, selfInfoJson] = await config.api.users.getSelf() - - // fetch app package - - const [appPackageResponse, appPackageJson] = - await config.api.apps.getAppPackage(app.appId!) - expect(appPackageJson.screens).toBeDefined() - expect(appPackageJson.screens.length).toEqual(1) - }) - it("Custom role access for level 2 permissions", async () => { - // Set up user - const appUser = fixtures.users.generateUser() - expect(appUser[0].builder?.global).toEqual(false) - expect(appUser[0].admin?.global).toEqual(false) - const [createUserResponse, createUserJson] = - await config.api.users.addMultiple(appUser) - - // Create App - - //Create level 1 role - const role = { - inherits: "BASIC", - permissionId: "read_only", - name: "level 2", - } - const [createRoleResponse, createRoleJson] = - await config.api.users.createRole(role) - - // Update user roles - const [userInfoResponse, userInfoJson] = await config.api.users.getInfo( - createUserJson.created.successful[0]._id - ) - const prodAppId = db.getProdAppID(app.appId!) - - // Roles must always be set with prod appID - const body: User = { - ...userInfoJson, - roles: { - [prodAppId]: createRoleJson._id, - }, - } - await config.api.users.updateInfo(body) - - const [changedUserInfoResponse, changedUserInfoJson] = - await config.api.users.getInfo(createUserJson.created.successful[0]._id) - expect(changedUserInfoJson.roles[prodAppId]).toBeDefined() - expect(changedUserInfoJson.roles[prodAppId]).toEqual(createRoleJson._id) - - await config.api.screens.create(fixtures.screens.generateScreen("BASIC")) - await config.api.screens.create(fixtures.screens.generateScreen("POWER")) - await config.api.screens.create(fixtures.screens.generateScreen("ADMIN")) - - await config.api.apps.publish(app.appId) - const [firstappPackageResponse, firstappPackageJson] = - await config.api.apps.getAppPackage(app.appId!) - expect(firstappPackageJson.screens).toBeDefined() - expect(firstappPackageJson.screens.length).toEqual(3) - - // login with level 1 user - await config.login(appUser[0].email!, appUser[0].password!) - const [selfInfoResponse, selfInfoJson] = await config.api.users.getSelf() - - // fetch app package - const [appPackageResponse, appPackageJson] = - await config.api.apps.getAppPackage(app.appId!) - expect(appPackageJson.screens).toBeDefined() - expect(appPackageJson.screens.length).toEqual(1) - }) - it("Custom role access for level 3 permissions", async () => { - const appUser = fixtures.users.generateUser() - expect(appUser[0].builder?.global).toEqual(false) - expect(appUser[0].admin?.global).toEqual(false) - const [createUserResponse, createUserJson] = - await config.api.users.addMultiple(appUser) - - // Create App - - //Create level 1 role - const role = { - inherits: "BASIC", - permissionId: "write", - name: "level 3", - } - const [createRoleResponse, createRoleJson] = - await config.api.users.createRole(role) - - // Update user roles - const [userInfoResponse, userInfoJson] = await config.api.users.getInfo( - createUserJson.created.successful[0]._id - ) - const prodAppId = db.getProdAppID(app.appId!) - - // Roles must always be set with prod appID - const body: User = { - ...userInfoJson, - roles: { - [prodAppId]: createRoleJson._id, - }, - } - await config.api.users.updateInfo(body) - - const [changedUserInfoResponse, changedUserInfoJson] = - await config.api.users.getInfo(createUserJson.created.successful[0]._id) - expect(changedUserInfoJson.roles[prodAppId]).toBeDefined() - expect(changedUserInfoJson.roles[prodAppId]).toEqual(createRoleJson._id) - - await config.api.screens.create(fixtures.screens.generateScreen("BASIC")) - await config.api.screens.create(fixtures.screens.generateScreen("POWER")) - await config.api.screens.create(fixtures.screens.generateScreen("ADMIN")) - - await config.api.apps.publish(app.appId) - const [firstappPackageResponse, firstappPackageJson] = - await config.api.apps.getAppPackage(app.appId!) - expect(firstappPackageJson.screens).toBeDefined() - expect(firstappPackageJson.screens.length).toEqual(3) - - // login with level 1 user - await config.login(appUser[0].email!, appUser[0].password!) - const [selfInfoResponse, selfInfoJson] = await config.api.users.getSelf() - - // fetch app package - const [appPackageResponse, appPackageJson] = - await config.api.apps.getAppPackage(app.appId!) - expect(appPackageJson.screens).toBeDefined() - expect(appPackageJson.screens.length).toEqual(1) - }) - it("Custom role access for level 4 permissions", async () => { - const appUser = fixtures.users.generateUser() - expect(appUser[0].builder?.global).toEqual(false) - expect(appUser[0].admin?.global).toEqual(false) - const [createUserResponse, createUserJson] = - await config.api.users.addMultiple(appUser) - - // Create App - - //Create level 1 role - const role = { - inherits: "BASIC", - permissionId: "power", - name: "level 4", - } - const [createRoleResponse, createRoleJson] = - await config.api.users.createRole(role) - - // Update user roles - const [userInfoResponse, userInfoJson] = await config.api.users.getInfo( - createUserJson.created.successful[0]._id - ) - const prodAppId = db.getProdAppID(app.appId!) - - // Roles must always be set with prod appID - const body: User = { - ...userInfoJson, - roles: { - [prodAppId]: createRoleJson._id, - }, - } - await config.api.users.updateInfo(body) - - const [changedUserInfoResponse, changedUserInfoJson] = - await config.api.users.getInfo(createUserJson.created.successful[0]._id) - expect(changedUserInfoJson.roles[prodAppId]).toBeDefined() - expect(changedUserInfoJson.roles[prodAppId]).toEqual(createRoleJson._id) - - await config.api.screens.create(fixtures.screens.generateScreen("BASIC")) - await config.api.screens.create(fixtures.screens.generateScreen("POWER")) - await config.api.screens.create(fixtures.screens.generateScreen("ADMIN")) - - await config.api.apps.publish(app.appId) - const [firstappPackageResponse, firstappPackageJson] = - await config.api.apps.getAppPackage(app.appId!) - expect(firstappPackageJson.screens).toBeDefined() - expect(firstappPackageJson.screens.length).toEqual(3) - - // login with level 1 user - await config.login(appUser[0].email!, appUser[0].password!) - const [selfInfoResponse, selfInfoJson] = await config.api.users.getSelf() - - // fetch app package - const [appPackageResponse, appPackageJson] = - await config.api.apps.getAppPackage(app.appId!) - expect(appPackageJson.screens).toBeDefined() - expect(appPackageJson.screens.length).toEqual(1) - }) - it("Custom role access for level 5 permissions", async () => { - const appUser = fixtures.users.generateUser() - expect(appUser[0].builder?.global).toEqual(false) - expect(appUser[0].admin?.global).toEqual(false) - const [createUserResponse, createUserJson] = - await config.api.users.addMultiple(appUser) - - // Create App - - //Create level 1 role - const role = { - inherits: "BASIC", - permissionId: "admin", - name: "level 5", - } - const [createRoleResponse, createRoleJson] = - await config.api.users.createRole(role) - - // Update user roles - const [userInfoResponse, userInfoJson] = await config.api.users.getInfo( - createUserJson.created.successful[0]._id - ) - const prodAppId = db.getProdAppID(app.appId!) - - // Roles must always be set with prod appID - const body: User = { - ...userInfoJson, - roles: { - [prodAppId]: createRoleJson._id, - }, - } - await config.api.users.updateInfo(body) - - const [changedUserInfoResponse, changedUserInfoJson] = - await config.api.users.getInfo(createUserJson.created.successful[0]._id) - expect(changedUserInfoJson.roles[prodAppId]).toBeDefined() - expect(changedUserInfoJson.roles[prodAppId]).toEqual(createRoleJson._id) - - await config.api.screens.create(fixtures.screens.generateScreen("BASIC")) - await config.api.screens.create(fixtures.screens.generateScreen("POWER")) - await config.api.screens.create(fixtures.screens.generateScreen("ADMIN")) - - await config.api.apps.publish(app.appId) - const [firstappPackageResponse, firstappPackageJson] = - await config.api.apps.getAppPackage(app.appId!) - expect(firstappPackageJson.screens).toBeDefined() - expect(firstappPackageJson.screens.length).toEqual(3) - - // login with level 1 user - await config.login(appUser[0].email!, appUser[0].password!) - const [selfInfoResponse, selfInfoJson] = await config.api.users.getSelf() - - // fetch app package - const [appPackageResponse, appPackageJson] = - await config.api.apps.getAppPackage(app.appId!) - expect(appPackageJson.screens).toBeDefined() - expect(appPackageJson.screens.length).toEqual(1) - }) -}) diff --git a/qa-core/src/internal-api/tests/users/screenAccess.spec.ts b/qa-core/src/internal-api/tests/users/screenAccess.spec.ts deleted file mode 100644 index 563eebb6f8..0000000000 --- a/qa-core/src/internal-api/tests/users/screenAccess.spec.ts +++ /dev/null @@ -1,175 +0,0 @@ -import TestConfiguration from "../../config/TestConfiguration" -import { User } from "@budibase/types" -import { db } from "@budibase/backend-core" -import * as fixtures from "./../../fixtures" - -describe.skip("Internal API - Role screen access", () => { - const config = new TestConfiguration() - - // Before each test, login as admin. Some tests will require login as a different user - beforeEach(async () => { - await config.beforeAll() - }) - - afterAll(async () => { - await config.afterAll() - }) - - it("Check Screen access for BASIC Role", async () => { - // Set up user - const appUser = fixtures.users.generateUser() - expect(appUser[0].builder?.global).toEqual(false) - expect(appUser[0].admin?.global).toEqual(false) - const [createUserResponse, createUserJson] = - await config.api.users.addMultiple(appUser) - - // Create App - const app = await config.createApp() - - // Update user roles - const [userInfoResponse, userInfoJson] = await config.api.users.getInfo( - createUserJson.created.successful[0]._id - ) - const prodAppId = db.getProdAppID(app.appId!) - - // Roles must always be set with prod appID - const body: User = { - ...userInfoJson, - roles: { - [prodAppId]: "BASIC", - }, - } - await config.api.users.updateInfo(body) - - const [changedUserInfoResponse, changedUserInfoJson] = - await config.api.users.getInfo(createUserJson.created.successful[0]._id) - expect(changedUserInfoJson.roles[prodAppId]).toBeDefined() - expect(changedUserInfoJson.roles[prodAppId]).toEqual("BASIC") - - await config.api.screens.create(fixtures.screens.generateScreen("BASIC")) - await config.api.screens.create(fixtures.screens.generateScreen("POWER")) - await config.api.screens.create(fixtures.screens.generateScreen("ADMIN")) - - await config.api.apps.publish(app.appId) - const [firstappPackageResponse, firstappPackageJson] = - await config.api.apps.getAppPackage(app.appId!) - expect(firstappPackageJson.screens).toBeDefined() - expect(firstappPackageJson.screens.length).toEqual(3) - - // login with BASIC user - await config.login(appUser[0].email!, appUser[0].password!) - const [selfInfoResponse, selfInfoJson] = await config.api.users.getSelf() - - // fetch app package - - const [appPackageResponse, appPackageJson] = - await config.api.apps.getAppPackage(app.appId!) - expect(appPackageJson.screens).toBeDefined() - expect(appPackageJson.screens.length).toEqual(1) - expect(appPackageJson.screens[0].routing.roleId).toEqual("BASIC") - }) - - it("Check Screen access for POWER role", async () => { - // Set up user - const appUser = fixtures.users.generateUser() - expect(appUser[0].builder?.global).toEqual(false) - expect(appUser[0].admin?.global).toEqual(false) - const [createUserResponse, createUserJson] = - await config.api.users.addMultiple(appUser) - - // Create App - const app = await config.createApp() - - // Update user roles - const [userInfoResponse, userInfoJson] = await config.api.users.getInfo( - createUserJson.created.successful[0]._id - ) - const prodAppId = db.getProdAppID(app.appId!) - - // Roles must always be set with prod appID - const body: User = { - ...userInfoJson, - roles: { - [prodAppId]: "POWER", - }, - } - await config.api.users.updateInfo(body) - - const [changedUserInfoResponse, changedUserInfoJson] = - await config.api.users.getInfo(createUserJson.created.successful[0]._id) - expect(changedUserInfoJson.roles[prodAppId]).toBeDefined() - expect(changedUserInfoJson.roles[prodAppId]).toEqual("POWER") - - await config.api.screens.create(fixtures.screens.generateScreen("BASIC")) - await config.api.screens.create(fixtures.screens.generateScreen("POWER")) - await config.api.screens.create(fixtures.screens.generateScreen("ADMIN")) - - await config.api.apps.publish(app.appId) - const [firstappPackageResponse, firstappPackageJson] = - await config.api.apps.getAppPackage(app.appId!) - expect(firstappPackageJson.screens).toBeDefined() - expect(firstappPackageJson.screens.length).toEqual(3) - - // login with POWER user - await config.login(appUser[0].email!, appUser[0].password!) - const [selfInfoResponse, selfInfoJson] = await config.api.users.getSelf() - - // fetch app package - const [appPackageResponse, appPackageJson] = - await config.api.apps.getAppPackage(app.appId!) - expect(appPackageJson.screens).toBeDefined() - expect(appPackageJson.screens.length).toEqual(2) - }) - - it("Check Screen access for ADMIN role", async () => { - // Set up user - const appUser = fixtures.users.generateUser() - expect(appUser[0].builder?.global).toEqual(false) - expect(appUser[0].admin?.global).toEqual(false) - const [createUserResponse, createUserJson] = - await config.api.users.addMultiple(appUser) - - // Create App - const app = await config.createApp() - - // Update user roles - const [userInfoResponse, userInfoJson] = await config.api.users.getInfo( - createUserJson.created.successful[0]._id - ) - const prodAppId = db.getProdAppID(app.appId!) - - // Roles must always be set with prod appID - const body: User = { - ...userInfoJson, - roles: { - [prodAppId]: "ADMIN", - }, - } - await config.api.users.updateInfo(body) - - const [changedUserInfoResponse, changedUserInfoJson] = - await config.api.users.getInfo(createUserJson.created.successful[0]._id) - expect(changedUserInfoJson.roles[prodAppId]).toBeDefined() - expect(changedUserInfoJson.roles[prodAppId]).toEqual("ADMIN") - - await config.api.screens.create(fixtures.screens.generateScreen("BASIC")) - await config.api.screens.create(fixtures.screens.generateScreen("POWER")) - await config.api.screens.create(fixtures.screens.generateScreen("ADMIN")) - - await config.api.apps.publish(app.appId) - const [firstappPackageResponse, firstappPackageJson] = - await config.api.apps.getAppPackage(app.appId!) - expect(firstappPackageJson.screens).toBeDefined() - expect(firstappPackageJson.screens.length).toEqual(3) - - // login with ADMIN user - await config.login(appUser[0].email!, appUser[0].password!) - const [selfInfoResponse, selfInfoJson] = await config.api.users.getSelf() - - // fetch app package - const [appPackageResponse, appPackageJson] = - await config.api.apps.getAppPackage(app.appId!) - expect(appPackageJson.screens).toBeDefined() - expect(appPackageJson.screens.length).toEqual(3) - }) -}) diff --git a/qa-core/src/internal-api/tests/users/tableAccess.spec.ts b/qa-core/src/internal-api/tests/users/tableAccess.spec.ts deleted file mode 100644 index 0db3a8a85e..0000000000 --- a/qa-core/src/internal-api/tests/users/tableAccess.spec.ts +++ /dev/null @@ -1,123 +0,0 @@ -import TestConfiguration from "../../config/TestConfiguration" -import { User } from "@budibase/types" -import * as fixtures from "./../../fixtures" - -describe.skip("Internal API - Role table access", () => { - const config = new TestConfiguration() - - // Before each test, login as admin. Some tests will require login as a different user - beforeEach(async () => { - await config.beforeAll() - }) - - afterAll(async () => { - await config.afterAll() - }) - - it("Check Table access for app user", async () => { - const appUser = fixtures.users.generateUser() - expect(appUser[0].builder?.global).toEqual(false) - expect(appUser[0].admin?.global).toEqual(false) - const [createUserResponse, createUserJson] = - await config.api.users.addMultiple(appUser) - - const app = await config.createApp() - - const [userInfoResponse, userInfoJson] = await config.api.users.getInfo( - createUserJson.created.successful[0]._id - ) - const body: User = { - ...userInfoJson, - roles: { - [app.appId!]: "BASIC", - }, - } - await config.api.users.updateInfo(body) - - const [changedUserInfoResponse, changedUserInfoJson] = - await config.api.users.getInfo(createUserJson.created.successful[0]._id) - expect(changedUserInfoJson.roles[app.appId!]).toBeDefined() - expect(changedUserInfoJson.roles[app.appId!]).toEqual("BASIC") - - const [createdTableResponse, createdTableData] = - await config.api.tables.save(fixtures.tables.generateTable()) - - await config.login(appUser[0].email!, appUser[0].password!) - - const newColumn = - fixtures.tables.generateNewColumnForTable(createdTableData) - await config.api.tables.forbiddenSave(newColumn) - await config.api.tables.forbiddenSave(fixtures.tables.generateTable()) - }) - - it.skip("Check Table access for developer", async () => { - const developer = fixtures.users.generateUser(1, "developer") - expect(developer[0].builder?.global).toEqual(true) - - const [createUserResponse, createUserJson] = - await config.api.users.addMultiple(developer) - - const app = await config.createApp() - const [userInfoResponse, userInfoJson] = await config.api.users.getInfo( - createUserJson.created.successful[0]._id - ) - const body: User = { - ...userInfoJson, - roles: { - [app.appId!]: "POWER", - }, - } - await config.api.users.updateInfo(body) - - const [changedUserInfoResponse, changedUserInfoJson] = - await config.api.users.getInfo(createUserJson.created.successful[0]._id) - expect(changedUserInfoJson.roles[app.appId!]).toBeDefined() - expect(changedUserInfoJson.roles[app.appId!]).toEqual("POWER") - - const [createdTableResponse, createdTableData] = - await config.api.tables.save(fixtures.tables.generateTable()) - await config.login(developer[0].email!, developer[0].password!) - const newColumn = - fixtures.tables.generateNewColumnForTable(createdTableData) - const [addColumnResponse, addColumnData] = await config.api.tables.save( - newColumn, - true - ) - }) - - it.skip("Check Table access for admin", async () => { - const adminUser = fixtures.users.generateUser(1, "admin") - expect(adminUser[0].builder?.global).toEqual(true) - expect(adminUser[0].admin?.global).toEqual(true) - const [createUserResponse, createUserJson] = - await config.api.users.addMultiple(adminUser) - - const app = await config.createApp() - - const [userInfoResponse, userInfoJson] = await config.api.users.getInfo( - createUserJson.created.successful[0]._id - ) - const body: User = { - ...userInfoJson, - roles: { - [app.appId!]: "ADMIN", - }, - } - await config.api.users.updateInfo(body) - - const [changedUserInfoResponse, changedUserInfoJson] = - await config.api.users.getInfo(createUserJson.created.successful[0]._id) - expect(changedUserInfoJson.roles[app.appId!]).toBeDefined() - expect(changedUserInfoJson.roles[app.appId!]).toEqual("ADMIN") - - await config.login(adminUser[0].email!, adminUser[0].password!) - const [createdTableResponse, createdTableData] = - await config.api.tables.save(fixtures.tables.generateTable()) - const newColumn = - fixtures.tables.generateNewColumnForTable(createdTableData) - const [addColumnResponse, addColumnData] = await config.api.tables.save( - newColumn, - true - ) - }) -}) diff --git a/qa-core/src/jest/globalSetup.ts b/qa-core/src/jest/globalSetup.ts deleted file mode 100644 index 103126d74c..0000000000 --- a/qa-core/src/jest/globalSetup.ts +++ /dev/null @@ -1,114 +0,0 @@ -import { DEFAULT_TENANT_ID } from "@budibase/backend-core" -import { AccountInternalAPI } from "../account-api" -import * as fixtures from "../internal-api/fixtures" -import { BudibaseInternalAPI } from "../internal-api" -import { Account, CreateAccountRequest, Feature } from "@budibase/types" -import env from "../environment" -import { APIRequestOpts } from "../types" - -const accountsApi = new AccountInternalAPI({}) -const internalApi = new BudibaseInternalAPI({}) - -const API_OPTS: APIRequestOpts = { doExpect: false } - -// @ts-ignore -global.qa = {} - -async function createAccount(): Promise<[CreateAccountRequest, Account]> { - const account = fixtures.accounts.generateAccount() - await accountsApi.accounts.validateEmail(account.email, API_OPTS) - await accountsApi.accounts.validateTenantId(account.tenantId, API_OPTS) - const [res, newAccount] = await accountsApi.accounts.create(account, { - ...API_OPTS, - autoVerify: true, - }) - await updateLicense(newAccount.accountId) - return [account, newAccount] -} - -const UNLIMITED = { value: -1 } - -async function updateLicense(accountId: string) { - const [response] = await accountsApi.licenses.updateLicense( - accountId, - { - overrides: { - // add all features - features: Object.values(Feature), - quotas: { - usage: { - monthly: { - automations: UNLIMITED, - }, - static: { - rows: UNLIMITED, - users: UNLIMITED, - userGroups: UNLIMITED, - plugins: UNLIMITED, - }, - }, - }, - }, - }, - { doExpect: false } - ) - if (response.status !== 200) { - throw new Error( - `Could not update license for accountId=${accountId}: ${response.status}` - ) - } -} - -async function loginAsAdmin() { - const username = env.BB_ADMIN_USER_EMAIL! - const password = env.BB_ADMIN_USER_PASSWORD! - const tenantId = DEFAULT_TENANT_ID - const [res, cookie] = await internalApi.auth.login( - tenantId, - username, - password, - API_OPTS - ) - - // @ts-ignore - global.qa.authCookie = cookie -} - -async function loginAsAccount(account: CreateAccountRequest) { - const [res, cookie] = await accountsApi.auth.login( - account.email, - account.password, - API_OPTS - ) - - // @ts-ignore - global.qa.authCookie = cookie -} - -async function setup() { - console.log("\nGLOBAL SETUP STARTING") - const env = await internalApi.environment.getEnvironment(API_OPTS) - - console.log(`Environment: ${JSON.stringify(env)}`) - - if (env.multiTenancy) { - const [account, newAccount] = await createAccount() - // @ts-ignore - global.qa.tenantId = account.tenantId - // @ts-ignore - global.qa.email = account.email - // @ts-ignore - global.qa.accountId = newAccount.accountId - await loginAsAccount(account) - } else { - // @ts-ignore - global.qa.tenantId = DEFAULT_TENANT_ID - await loginAsAdmin() - } - - // @ts-ignore - console.log(`Tenant: ${global.qa.tenantId}`) - console.log("GLOBAL SETUP COMPLETE") -} - -export default setup diff --git a/qa-core/src/jest/globalTeardown.ts b/qa-core/src/jest/globalTeardown.ts deleted file mode 100644 index ff3fea7b7c..0000000000 --- a/qa-core/src/jest/globalTeardown.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { AccountInternalAPI } from "../account-api" -import { BudibaseInternalAPI } from "../internal-api" -import { APIRequestOpts } from "../types" - -const accountsApi = new AccountInternalAPI({}) -const internalApi = new BudibaseInternalAPI({}) - -const API_OPTS: APIRequestOpts = { doExpect: false } - -async function deleteAccount() { - // @ts-ignore - const accountID = global.qa.accountId - - const [response] = await accountsApi.accounts.delete(accountID, { - doExpect: false, - }) - if (response.status !== 204) { - throw new Error(`status: ${response.status} not equal to expected: 201`) - } -} - -async function teardown() { - console.log("\nGLOBAL TEARDOWN STARTING") - const env = await internalApi.environment.getEnvironment(API_OPTS) - if (env.multiTenancy) { - await deleteAccount() - } - - console.log("GLOBAL TEARDOWN COMPLETE") -} - -export default teardown diff --git a/qa-core/src/jest/jest.extends.ts b/qa-core/src/jest/jest.extends.ts deleted file mode 100644 index 653009f5f0..0000000000 --- a/qa-core/src/jest/jest.extends.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Response } from "node-fetch" - -// boilerplate to allow TS updates to the global scope -export {} - -declare global { - namespace jest { - interface Matchers { - toHaveStatusCode(code: number): R - } - } -} - -// Expect extensions -expect.extend({ - toHaveStatusCode(received: Response, code: number) { - const pass = received.status === code - return { - message: () => `expected ${received.status} to match status code ${code}`, - pass, - } - }, -}) diff --git a/qa-core/src/jest/jestSetup.ts b/qa-core/src/jest/jestSetup.ts deleted file mode 100644 index 928e547337..0000000000 --- a/qa-core/src/jest/jestSetup.ts +++ /dev/null @@ -1,3 +0,0 @@ -const envTimeout = process.env.JEST_TIMEOUT -const timeout = envTimeout && parseInt(envTimeout) -jest.setTimeout(timeout || 60000) diff --git a/qa-core/src/public-api/api/BudibasePublicAPI.ts b/qa-core/src/public-api/api/BudibasePublicAPI.ts deleted file mode 100644 index 4a79259d6e..0000000000 --- a/qa-core/src/public-api/api/BudibasePublicAPI.ts +++ /dev/null @@ -1,23 +0,0 @@ -import AppAPI from "./apis/AppAPI" -import UserAPI from "./apis/UserAPI" -import TableAPI from "./apis/TableAPI" -import RowAPI from "./apis/RowAPI" - -import BudibasePublicAPIClient from "./BudibasePublicAPIClient" -import { State } from "../../types" - -export default class BudibasePublicAPI { - client: BudibasePublicAPIClient - apps: AppAPI - users: UserAPI - tables: TableAPI - rows: RowAPI - - constructor(state: State) { - this.client = new BudibasePublicAPIClient(state) - this.apps = new AppAPI(this.client) - this.users = new UserAPI(this.client) - this.tables = new TableAPI(this.client) - this.rows = new RowAPI(this.client, state) - } -} diff --git a/qa-core/src/public-api/api/BudibasePublicAPIClient.ts b/qa-core/src/public-api/api/BudibasePublicAPIClient.ts deleted file mode 100644 index b75393bdaa..0000000000 --- a/qa-core/src/public-api/api/BudibasePublicAPIClient.ts +++ /dev/null @@ -1,79 +0,0 @@ -import env from "../../environment" -import fetch, { HeadersInit } from "node-fetch" -import { State } from "../../types" - -type APIMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE" - -interface ApiOptions { - method?: APIMethod - body?: object - headers?: HeadersInit | undefined -} - -class BudibasePublicAPIClient { - state: State - host: string - - constructor(state: State) { - if (!env.BUDIBASE_URL) { - throw new Error("Must set BUDIBASE_URL env var") - } - this.host = `${env.BUDIBASE_URL}/api/public/v1` - this.state = state - } - - apiCall = - (method: APIMethod) => - async (url = "", options: ApiOptions = {}) => { - const requestOptions = { - method, - body: JSON.stringify(options.body), - headers: { - "x-budibase-api-key": this.state.apiKey, - "x-budibase-app-id": this.state.appId, - "Content-Type": "application/json", - Accept: "application/json", - ...options.headers, - redirect: "follow", - follow: 20, - }, - } - - // prettier-ignore - // @ts-ignore - const response = await fetch(`${this.host}${url}`, requestOptions) - - let body: any - const contentType = response.headers.get("content-type") - if (contentType && contentType.includes("application/json")) { - body = await response.json() - } else { - body = await response.text() - } - - const data = { - request: requestOptions.body, - response: body, - } - const message = `${method} ${url} - ${response.status}` - - const isDebug = process.env.LOG_LEVEL === "debug" - if (response.status > 499) { - console.error(message, data) - } else if (response.status >= 400) { - console.warn(message, data) - } else if (isDebug) { - console.debug(message, data) - } - - return [response, body] - } - - post = this.apiCall("POST") - get = this.apiCall("GET") - patch = this.apiCall("PATCH") - del = this.apiCall("DELETE") - put = this.apiCall("PUT") -} - -export default BudibasePublicAPIClient diff --git a/qa-core/src/public-api/api/apis/AppAPI.ts b/qa-core/src/public-api/api/apis/AppAPI.ts deleted file mode 100644 index 71a9b007c6..0000000000 --- a/qa-core/src/public-api/api/apis/AppAPI.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { Response } from "node-fetch" -import BudibasePublicAPIClient from "../BudibasePublicAPIClient" -import * as fixtures from "../../fixtures" -import { - Application, - SearchInputParams, - CreateApplicationParams, -} from "../../../types" - -export default class AppAPI { - client: BudibasePublicAPIClient - - constructor(client: BudibasePublicAPIClient) { - this.client = client - } - - async seed(): Promise<[Response, Application]> { - return this.create(fixtures.apps.generateApp()) - } - - async create( - body: CreateApplicationParams - ): Promise<[Response, Application]> { - const [response, json] = await this.client.post(`/applications`, { body }) - return [response, json.data] - } - - async read(id: string): Promise<[Response, Application]> { - const [response, json] = await this.client.get(`/applications/${id}`) - return [response, json.data] - } - - async search(body: SearchInputParams): Promise<[Response, [Application]]> { - const [response, json] = await this.client.post(`/applications/search`, { - body, - }) - return [response, json.data] - } - - async update( - id: string, - body: Application - ): Promise<[Response, Application]> { - const [response, json] = await this.client.put(`/applications/${id}`, { - body, - }) - return [response, json.data] - } - - async delete(id: string): Promise<[Response, Application]> { - const [response, json] = await this.client.del(`/applications/${id}`) - return [response, json.data] - } - - async publish(id: string): Promise<[Response, any]> { - const [response, json] = await this.client.post( - `/applications/${id}/publish` - ) - return [response, json.data] - } - - async unpublish(id: string): Promise<[Response]> { - const [response, json] = await this.client.post( - `/applications/${id}/unpublish` - ) - return [response] - } -} diff --git a/qa-core/src/public-api/api/apis/RowAPI.ts b/qa-core/src/public-api/api/apis/RowAPI.ts deleted file mode 100644 index 8f8e78f365..0000000000 --- a/qa-core/src/public-api/api/apis/RowAPI.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { CreateRowParams, Row, SearchInputParams, State } from "../../../types" -import { HeadersInit, Response } from "node-fetch" -import BudibasePublicAPIClient from "../BudibasePublicAPIClient" -import * as fixtures from "../../fixtures" - -export default class RowAPI { - client: BudibasePublicAPIClient - - headers?: HeadersInit - - state: State - - constructor(client: BudibasePublicAPIClient, state: State) { - this.state = state - this.client = client - } - - async seed(tableId: string) { - return this.create(fixtures.rows.generateRow({ tableId })) - } - - async create(body: CreateRowParams): Promise<[Response, Row]> { - const [response, json] = await this.client.post( - `/tables/${this.state.tableId}/rows`, - { - body, - } - ) - return [response, json.data] - } - - async read(id: string): Promise<[Response, Row]> { - const [response, json] = await this.client.get( - `/tables/${this.state.tableId}/rows/${id}` - ) - return [response, json.data] - } - - async search(body: SearchInputParams): Promise<[Response, [Row]]> { - const [response, json] = await this.client.post( - `/tables/${this.state.tableId}/rows/search`, - { body } - ) - return [response, json.data] - } - - async update(id: string, body: Row): Promise<[Response, Row]> { - const [response, json] = await this.client.put( - `/tables/${this.state.tableId}/rows/${id}`, - { - body, - } - ) - return [response, json.data] - } -} diff --git a/qa-core/src/public-api/api/apis/TableAPI.ts b/qa-core/src/public-api/api/apis/TableAPI.ts deleted file mode 100644 index 4849953211..0000000000 --- a/qa-core/src/public-api/api/apis/TableAPI.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { Table, SearchInputParams, CreateTableParams } from "../../../types" -import { HeadersInit, Response } from "node-fetch" -import { generateTable } from "../../fixtures/tables" -import BudibasePublicAPIClient from "../BudibasePublicAPIClient" - -export default class TableAPI { - headers?: HeadersInit - - client: BudibasePublicAPIClient - - constructor(client: BudibasePublicAPIClient) { - this.client = client - } - - async seed() { - return this.create(generateTable()) - } - - async create(body: CreateTableParams): Promise<[Response, Table]> { - const [response, json] = await this.client.post(`/tables`, { - body, - }) - return [response, json.data] - } - - async read(id: string): Promise<[Response, Table]> { - const [response, json] = await this.client.get(`/tables/${id}`) - return [response, json.data] - } - - async search(body: SearchInputParams): Promise<[Response, [Table]]> { - const [response, json] = await this.client.post(`/tables/search`, { body }) - return [response, json.data] - } - - async update(id: string, body: Table): Promise<[Response, Table]> { - const [response, json] = await this.client.put(`/tables/${id}`, { body }) - return [response, json.data] - } -} diff --git a/qa-core/src/public-api/api/apis/UserAPI.ts b/qa-core/src/public-api/api/apis/UserAPI.ts deleted file mode 100644 index 589bec1d40..0000000000 --- a/qa-core/src/public-api/api/apis/UserAPI.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { CreateUserParams, SearchInputParams, User } from "../../../types" -import { Response } from "node-fetch" -import BudibasePublicAPIClient from "../BudibasePublicAPIClient" -import * as fixtures from "../../fixtures" - -export default class UserAPI { - client: BudibasePublicAPIClient - - constructor(client: BudibasePublicAPIClient) { - this.client = client - } - - async seed() { - return this.create(fixtures.users.generateUser()) - } - - async create(body: CreateUserParams): Promise<[Response, User]> { - const [response, json] = await this.client.post(`/users`, { body }) - return [response, json.data] - } - - async read(id: string): Promise<[Response, User]> { - const [response, json] = await this.client.get(`/users/${id}`) - return [response, json.data] - } - - async search(body: SearchInputParams): Promise<[Response, [User]]> { - const [response, json] = await this.client.post(`/users/search`, { body }) - return [response, json.data] - } - - async update(id: string, body: User): Promise<[Response, User]> { - const [response, json] = await this.client.put(`/users/${id}`, { body }) - return [response, json.data] - } -} diff --git a/qa-core/src/public-api/api/index.ts b/qa-core/src/public-api/api/index.ts deleted file mode 100644 index 54d6e44043..0000000000 --- a/qa-core/src/public-api/api/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default as BudibasePublicAPI } from "./BudibasePublicAPI" diff --git a/qa-core/src/public-api/config/TestConfiguration.ts b/qa-core/src/public-api/config/TestConfiguration.ts deleted file mode 100644 index d9c12ee267..0000000000 --- a/qa-core/src/public-api/config/TestConfiguration.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { BudibasePublicAPI } from "../api" -import { BudibaseTestConfiguration } from "../../shared" - -export default class TestConfiguration extends BudibaseTestConfiguration { - // apis - api: BudibasePublicAPI - - context: T - - constructor() { - super() - this.api = new BudibasePublicAPI(this.state) - this.context = {} - } - - // LIFECYCLE - - async beforeAll() { - await super.beforeAll() - await this.setApiKey() - } - - async afterAll() { - await super.afterAll() - } - - // AUTH - - async setApiKey() { - const apiKeyResponse = await this.internalApi.self.getApiKey() - this.state.apiKey = apiKeyResponse.apiKey - } -} diff --git a/qa-core/src/public-api/config/index.ts b/qa-core/src/public-api/config/index.ts deleted file mode 100644 index 182c7825b6..0000000000 --- a/qa-core/src/public-api/config/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default as TestConfiguration } from "./TestConfiguration" diff --git a/qa-core/src/public-api/fixtures/accounts.ts b/qa-core/src/public-api/fixtures/accounts.ts deleted file mode 100644 index 314d7d9176..0000000000 --- a/qa-core/src/public-api/fixtures/accounts.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { generator } from "../../shared" -import { CreateAccountRequest, Hosting } from "@budibase/types" - -export const generateAccount = (): CreateAccountRequest => { - const randomGuid = generator.guid() - //Needs to start with a letter - let tenant: string = "tenant" + randomGuid - tenant = tenant.replace(/-/g, "") - - return { - email: `qa+${randomGuid}@budibase.com`, - hosting: Hosting.CLOUD, - name: `qa+${randomGuid}@budibase.com`, - password: `${randomGuid}`, - profession: "software_engineer", - size: "10+", - tenantId: `${tenant}`, - tenantName: `${tenant}`, - } -} diff --git a/qa-core/src/public-api/fixtures/applications.ts b/qa-core/src/public-api/fixtures/applications.ts deleted file mode 100644 index b8595b4b33..0000000000 --- a/qa-core/src/public-api/fixtures/applications.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { generator } from "../../shared" -import { Application, CreateApplicationParams } from "../../types" - -export const generateApp = ( - overrides: Partial = {} -): CreateApplicationParams => ({ - name: generator.word(), - url: `/${generator.word()}`, - ...overrides, -}) diff --git a/qa-core/src/public-api/fixtures/index.ts b/qa-core/src/public-api/fixtures/index.ts deleted file mode 100644 index 02d48cd628..0000000000 --- a/qa-core/src/public-api/fixtures/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export * as accounts from "./accounts" -export * as apps from "./applications" -export * as rows from "./rows" -export * as tables from "./tables" -export * as users from "./users" diff --git a/qa-core/src/public-api/fixtures/rows.ts b/qa-core/src/public-api/fixtures/rows.ts deleted file mode 100644 index 50d999eaa4..0000000000 --- a/qa-core/src/public-api/fixtures/rows.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { CreateRowParams, Row } from "../../types" -import { generator } from "../../shared" - -export const generateRow = (overrides: Partial = {}): CreateRowParams => ({ - type: "row", - tableId: "seed_table", - testColumn: generator.string({ length: 32, alpha: true, numeric: true }), - relationship: [generator.string({ length: 32, alpha: true, numeric: true })], - ...overrides, -}) diff --git a/qa-core/src/public-api/fixtures/tables.ts b/qa-core/src/public-api/fixtures/tables.ts deleted file mode 100644 index 89e3df690a..0000000000 --- a/qa-core/src/public-api/fixtures/tables.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { CreateTableParams, Table } from "../../types" -import { generator } from "../../shared" - -export const generateTable = ( - overrides: Partial = {} -): CreateTableParams => ({ - name: generator.word(), - primaryDisplay: "testColumn", - schema: { - "Auto ID": { - autocolumn: true, - name: "Auto ID", - type: "number", - }, - "Created At": { - autocolumn: true, - name: "Created At", - type: "datetime", - }, - "Created By": { - autocolumn: true, - fieldName: generator.word(), - name: "Created By", - relationshipType: "many-to-many", - tableId: "ta_users", - type: "link", - }, - testColumn: { - name: "testColumn", - type: "string", - }, - "Updated At": { - autocolumn: true, - name: "Updated At", - type: "datetime", - }, - "Updated By": { - autocolumn: true, - fieldName: generator.word(), - name: "Updated By", - relationshipType: "many-to-many", - tableId: "ta_users", - type: "link", - }, - }, - ...overrides, -}) diff --git a/qa-core/src/public-api/fixtures/users.ts b/qa-core/src/public-api/fixtures/users.ts deleted file mode 100644 index 418b565d2a..0000000000 --- a/qa-core/src/public-api/fixtures/users.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { CreateUserParams, User } from "../../types" -import { generator } from "../../shared" - -export const generateUser = ( - overrides: Partial = {} -): CreateUserParams => ({ - email: generator.email({ domain: "example.com" }), - roles: { - [generator.string({ length: 32, alpha: true, numeric: true })]: - generator.word(), - }, - password: generator.word(), - status: "active", - forceResetPassword: generator.bool(), - builder: { - global: generator.bool(), - }, - admin: { - global: generator.bool(), - }, - ...overrides, -}) diff --git a/qa-core/src/public-api/index.ts b/qa-core/src/public-api/index.ts deleted file mode 100644 index e1a716605a..0000000000 --- a/qa-core/src/public-api/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./api" diff --git a/qa-core/src/shared/BudibaseTestConfiguration.ts b/qa-core/src/shared/BudibaseTestConfiguration.ts deleted file mode 100644 index 9a12f3e65d..0000000000 --- a/qa-core/src/shared/BudibaseTestConfiguration.ts +++ /dev/null @@ -1,98 +0,0 @@ -import { BudibaseInternalAPI } from "../internal-api" -import { AccountInternalAPI } from "../account-api" -import { APIRequestOpts, State } from "../types" -import * as fixtures from "../internal-api/fixtures" -import { CreateAccountRequest, CreateAppRequest } from "@budibase/types" - -export default class BudibaseTestConfiguration { - // apis - internalApi: BudibaseInternalAPI - accountsApi: AccountInternalAPI - - // state - state: State - - constructor() { - this.state = {} - this.internalApi = new BudibaseInternalAPI(this.state) - this.accountsApi = new AccountInternalAPI(this.state) - } - - // LIFECYCLE - - async beforeAll() { - // @ts-ignore - this.state.tenantId = global.qa.tenantId - // @ts-ignore - this.state.email = global.qa.email - // @ts-ignore - this.state.cookie = global.qa.authCookie - } - - async afterAll() { - // nothing yet - } - - async createApp(overrides: Partial = {}) { - const app = await this.internalApi.apps.create( - fixtures.apps.generateApp(overrides) - ) - this.state.appId = app.appId - return app - } - - // AUTH - - async doInNewState(task: () => Promise) { - return this.doWithState(task, {}) - } - - async doWithState(task: () => Promise, state: State) { - const original = { ...this.state } - - // override the state - this.state.apiKey = state.apiKey - this.state.appId = state.appId - this.state.cookie = state.cookie - this.state.tableId = state.tableId - this.state.tenantId = state.tenantId - this.state.email = state.email - - await task() - - // restore the state - this.state.apiKey = original.apiKey - this.state.appId = original.appId - this.state.cookie = original.cookie - this.state.tableId = original.tableId - this.state.tenantId = original.tenantId - this.state.email = original.email - } - - async loginAsAccount( - account: CreateAccountRequest, - opts: APIRequestOpts = {} - ) { - const [_, cookie] = await this.accountsApi.auth.login( - account.email, - account.password, - opts - ) - this.state.cookie = cookie - } - - async login(email: string, password: string, tenantId?: string) { - if (!tenantId && this.state.tenantId) { - tenantId = this.state.tenantId - } - if (!tenantId) { - throw new Error("Could not determine tenant id") - } - const [res, cookie] = await this.internalApi.auth.login( - tenantId, - email, - password - ) - this.state.cookie = cookie - } -} diff --git a/qa-core/src/shared/generator.ts b/qa-core/src/shared/generator.ts deleted file mode 100644 index 1789fc0f75..0000000000 --- a/qa-core/src/shared/generator.ts +++ /dev/null @@ -1,3 +0,0 @@ -import Chance from "chance" - -export default new Chance() diff --git a/qa-core/src/shared/index.ts b/qa-core/src/shared/index.ts deleted file mode 100644 index 0eba40e7d1..0000000000 --- a/qa-core/src/shared/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { default as generator } from "./generator" -export { default as BudibaseTestConfiguration } from "./BudibaseTestConfiguration" diff --git a/qa-core/src/types/api.ts b/qa-core/src/types/api.ts deleted file mode 100644 index 3d52445280..0000000000 --- a/qa-core/src/types/api.ts +++ /dev/null @@ -1,6 +0,0 @@ -export interface APIRequestOpts { - // in some cases we need to bypass the expect assertion in an api call - // e.g. during global setup where jest is not available - doExpect?: boolean - status?: number -} diff --git a/qa-core/src/types/apiKeyResponse.ts b/qa-core/src/types/apiKeyResponse.ts deleted file mode 100644 index 4a62d60796..0000000000 --- a/qa-core/src/types/apiKeyResponse.ts +++ /dev/null @@ -1,6 +0,0 @@ -export interface ApiKeyResponse { - apiKey: string - createdAt: string - updatedAt: string - userId: string -} diff --git a/qa-core/src/types/appPackage.ts b/qa-core/src/types/appPackage.ts deleted file mode 100644 index 817a4b7414..0000000000 --- a/qa-core/src/types/appPackage.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { Application } from "../types" -import { Layout, Screen } from "@budibase/types" -// Create type for getAppPackage response -export interface AppPackageResponse { - application: Partial - layout: Layout - screens: Screen[] -} diff --git a/qa-core/src/types/datasources.ts b/qa-core/src/types/datasources.ts deleted file mode 100644 index f919e4435f..0000000000 --- a/qa-core/src/types/datasources.ts +++ /dev/null @@ -1,22 +0,0 @@ -export interface DatasourceRequest { - datasource: { - name: string - plus?: boolean - source: string - type: string - config: { - connectionString?: string - db?: string - database?: string - host?: string - password?: string - port?: string - schema?: string - user?: string - defaultHeaders?: {} - rejectUnauthorized?: boolean - url?: string - } - } - fetchSchema: boolean -} diff --git a/qa-core/src/types/deploy.ts b/qa-core/src/types/deploy.ts deleted file mode 100644 index 77acb3bb52..0000000000 --- a/qa-core/src/types/deploy.ts +++ /dev/null @@ -1,5 +0,0 @@ -export interface DeployConfig { - appUrl: string - status: string - _id: string -} diff --git a/qa-core/src/types/index.ts b/qa-core/src/types/index.ts deleted file mode 100644 index a44df4ef3c..0000000000 --- a/qa-core/src/types/index.ts +++ /dev/null @@ -1,13 +0,0 @@ -export * from "./api" -export * from "./apiKeyResponse" -export * from "./appPackage" -export * from "./deploy" -export * from "./newAccount" -export * from "./responseMessage" -export * from "./routing" -export * from "./state" -export * from "./unpublishAppResponse" -export * from "./screens" -export * from "./datasources" -// re-export public api types -export * from "@budibase/server/api/controllers/public/mapping/types" diff --git a/qa-core/src/types/newAccount.ts b/qa-core/src/types/newAccount.ts deleted file mode 100644 index e7ad88e697..0000000000 --- a/qa-core/src/types/newAccount.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { Account } from "@budibase/types" - -export interface NewAccount extends Account { - password: string -} diff --git a/qa-core/src/types/responseMessage.ts b/qa-core/src/types/responseMessage.ts deleted file mode 100644 index 718ac207cc..0000000000 --- a/qa-core/src/types/responseMessage.ts +++ /dev/null @@ -1,3 +0,0 @@ -export interface MessageResponse { - message: string -} diff --git a/qa-core/src/types/routing.ts b/qa-core/src/types/routing.ts deleted file mode 100644 index c898d7fecd..0000000000 --- a/qa-core/src/types/routing.ts +++ /dev/null @@ -1,17 +0,0 @@ -export interface RouteConfig { - routes: Record -} - -export interface Route { - subpaths: Record -} - -export interface Subpath { - screens: ScreenRouteConfig -} - -export interface ScreenRouteConfig { - BASIC?: string - POWER?: string - ADMIN?: string -} diff --git a/qa-core/src/types/screens.ts b/qa-core/src/types/screens.ts deleted file mode 100644 index fae65e64fa..0000000000 --- a/qa-core/src/types/screens.ts +++ /dev/null @@ -1,32 +0,0 @@ -export interface ScreenRequest { - showNavigation: boolean - width: string - name: string - template: string - props: ScreenProps - routing: ScreenRouting -} - -interface ScreenProps { - _id: string - _component: string - _styles: { - normal: {} - hover: {} - active: {} - selected: {} - } - _children: [] - _instanceName: string - direction: string - hAlign: string - vAlign: string - size: string - gap: string -} - -interface ScreenRouting { - route: string - roleId: string - homeScreen: boolean -} diff --git a/qa-core/src/types/state.ts b/qa-core/src/types/state.ts deleted file mode 100644 index 8fab3998ce..0000000000 --- a/qa-core/src/types/state.ts +++ /dev/null @@ -1,8 +0,0 @@ -export interface State { - apiKey?: string - appId?: string - cookie?: string - tableId?: string - tenantId?: string - email?: string -} diff --git a/qa-core/src/types/unpublishAppResponse.ts b/qa-core/src/types/unpublishAppResponse.ts deleted file mode 100644 index 277970fccb..0000000000 --- a/qa-core/src/types/unpublishAppResponse.ts +++ /dev/null @@ -1,7 +0,0 @@ -export interface UnpublishAppResponse { - data: { - ok: boolean - } - ok: boolean - status: number -} diff --git a/qa-core/tsconfig.json b/qa-core/tsconfig.json deleted file mode 100644 index 68bd3dd87e..0000000000 --- a/qa-core/tsconfig.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "compilerOptions": { - "target": "es6", - "module": "commonjs", - "lib": ["es2020"], - "allowJs": true, - "strict": true, - "noImplicitAny": true, - "esModuleInterop": true, - "resolveJsonModule": true, - "incremental": true, - "types": ["node", "jest"], - "outDir": "dist", - "skipLibCheck": true, - "paths": { - "@budibase/types": ["../packages/types/src"], - "@budibase/backend-core": ["../packages/backend-core/src"], - "@budibase/backend-core/*": ["../packages/backend-core/*"], - "@budibase/server/*": ["../packages/server/src/*"] - } - }, - "ts-node": { - "require": ["tsconfig-paths/register"] - }, - "include": ["src/**/*"], - "exclude": ["node_modules", "dist"] -} diff --git a/qa-core/yarn.lock b/qa-core/yarn.lock deleted file mode 100644 index 0eba6dd4d7..0000000000 --- a/qa-core/yarn.lock +++ /dev/null @@ -1,5099 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -"@ampproject/remapping@^2.2.0": - version "2.2.0" - resolved "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz" - integrity sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w== - dependencies: - "@jridgewell/gen-mapping" "^0.1.0" - "@jridgewell/trace-mapping" "^0.3.9" - -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.21.4", "@babel/code-frame@^7.22.13", "@babel/code-frame@^7.23.5": - version "7.23.5" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.23.5.tgz#9009b69a8c602293476ad598ff53e4562e15c244" - integrity sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA== - dependencies: - "@babel/highlight" "^7.23.4" - chalk "^2.4.2" - -"@babel/compat-data@^7.21.4": - version "7.21.4" - resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.21.4.tgz" - integrity sha512-/DYyDpeCfaVinT40FPGdkkb+lYSKvsVuMjDAG7jPOWWiM1ibOaB9CXJAlc4d1QpP/U2q2P9jbrSlClKSErd55g== - -"@babel/core@^7.11.6", "@babel/core@^7.12.3": - version "7.21.4" - resolved "https://registry.npmjs.org/@babel/core/-/core-7.21.4.tgz" - integrity sha512-qt/YV149Jman/6AfmlxJ04LMIu8bMoyl3RB91yTFrxQmgbrSvQMy7cI8Q62FHx1t8wJ8B5fu0UDoLwHAhUo1QA== - dependencies: - "@ampproject/remapping" "^2.2.0" - "@babel/code-frame" "^7.21.4" - "@babel/generator" "^7.21.4" - "@babel/helper-compilation-targets" "^7.21.4" - "@babel/helper-module-transforms" "^7.21.2" - "@babel/helpers" "^7.21.0" - "@babel/parser" "^7.21.4" - "@babel/template" "^7.20.7" - "@babel/traverse" "^7.21.4" - "@babel/types" "^7.21.4" - convert-source-map "^1.7.0" - debug "^4.1.0" - gensync "^1.0.0-beta.2" - json5 "^2.2.2" - semver "^6.3.0" - -"@babel/generator@^7.21.4", "@babel/generator@^7.23.6", "@babel/generator@^7.7.2": - version "7.23.6" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.23.6.tgz#9e1fca4811c77a10580d17d26b57b036133f3c2e" - integrity sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw== - dependencies: - "@babel/types" "^7.23.6" - "@jridgewell/gen-mapping" "^0.3.2" - "@jridgewell/trace-mapping" "^0.3.17" - jsesc "^2.5.1" - -"@babel/helper-compilation-targets@^7.21.4": - version "7.21.4" - resolved "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.21.4.tgz" - integrity sha512-Fa0tTuOXZ1iL8IeDFUWCzjZcn+sJGd9RZdH9esYVjEejGmzf+FFYQpMi/kZUk2kPy/q1H3/GPw7np8qar/stfg== - dependencies: - "@babel/compat-data" "^7.21.4" - "@babel/helper-validator-option" "^7.21.0" - browserslist "^4.21.3" - lru-cache "^5.1.1" - semver "^6.3.0" - -"@babel/helper-environment-visitor@^7.18.9", "@babel/helper-environment-visitor@^7.22.20": - version "7.22.20" - resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz#96159db61d34a29dba454c959f5ae4a649ba9167" - integrity sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA== - -"@babel/helper-function-name@^7.23.0": - version "7.23.0" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz#1f9a3cdbd5b2698a670c30d2735f9af95ed52759" - integrity sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw== - dependencies: - "@babel/template" "^7.22.15" - "@babel/types" "^7.23.0" - -"@babel/helper-hoist-variables@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz#c01a007dac05c085914e8fb652b339db50d823bb" - integrity sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw== - dependencies: - "@babel/types" "^7.22.5" - -"@babel/helper-module-imports@^7.18.6": - version "7.21.4" - resolved "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.21.4.tgz" - integrity sha512-orajc5T2PsRYUN3ZryCEFeMDYwyw09c/pZeaQEZPH0MpKzSvn3e0uXsDBu3k03VI+9DBiRo+l22BfKTpKwa/Wg== - dependencies: - "@babel/types" "^7.21.4" - -"@babel/helper-module-transforms@^7.21.2": - version "7.21.2" - resolved "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz" - integrity sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ== - dependencies: - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-module-imports" "^7.18.6" - "@babel/helper-simple-access" "^7.20.2" - "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/helper-validator-identifier" "^7.19.1" - "@babel/template" "^7.20.7" - "@babel/traverse" "^7.21.2" - "@babel/types" "^7.21.2" - -"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.20.2", "@babel/helper-plugin-utils@^7.8.0": - version "7.20.2" - resolved "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz" - integrity sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ== - -"@babel/helper-simple-access@^7.20.2": - version "7.20.2" - resolved "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz" - integrity sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA== - dependencies: - "@babel/types" "^7.20.2" - -"@babel/helper-split-export-declaration@^7.18.6", "@babel/helper-split-export-declaration@^7.22.6": - version "7.22.6" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz#322c61b7310c0997fe4c323955667f18fcefb91c" - integrity sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g== - dependencies: - "@babel/types" "^7.22.5" - -"@babel/helper-string-parser@^7.23.4": - version "7.23.4" - resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz#9478c707febcbbe1ddb38a3d91a2e054ae622d83" - integrity sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ== - -"@babel/helper-validator-identifier@^7.19.1", "@babel/helper-validator-identifier@^7.22.20": - version "7.22.20" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0" - integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A== - -"@babel/helper-validator-option@^7.21.0": - version "7.21.0" - resolved "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz" - integrity sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ== - -"@babel/helpers@^7.21.0": - version "7.21.0" - resolved "https://registry.npmjs.org/@babel/helpers/-/helpers-7.21.0.tgz" - integrity sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA== - dependencies: - "@babel/template" "^7.20.7" - "@babel/traverse" "^7.21.0" - "@babel/types" "^7.21.0" - -"@babel/highlight@^7.23.4": - version "7.23.4" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.23.4.tgz#edaadf4d8232e1a961432db785091207ead0621b" - integrity sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A== - dependencies: - "@babel/helper-validator-identifier" "^7.22.20" - chalk "^2.4.2" - js-tokens "^4.0.0" - -"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.21.4", "@babel/parser@^7.22.15", "@babel/parser@^7.23.6": - version "7.23.6" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.6.tgz#ba1c9e512bda72a47e285ae42aff9d2a635a9e3b" - integrity sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ== - -"@babel/plugin-syntax-async-generators@^7.8.4": - version "7.8.4" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz" - integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-bigint@^7.8.3": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz" - integrity sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-class-properties@^7.8.3": - version "7.12.13" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz" - integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA== - dependencies: - "@babel/helper-plugin-utils" "^7.12.13" - -"@babel/plugin-syntax-import-meta@^7.8.3": - version "7.10.4" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz" - integrity sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g== - dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - -"@babel/plugin-syntax-json-strings@^7.8.3": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz" - integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-jsx@^7.7.2": - version "7.21.4" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.21.4.tgz" - integrity sha512-5hewiLct5OKyh6PLKEYaFclcqtIgCb6bmELouxjF6up5q3Sov7rOayW4RwhbaBL0dit8rA80GNfY+UuDp2mBbQ== - dependencies: - "@babel/helper-plugin-utils" "^7.20.2" - -"@babel/plugin-syntax-logical-assignment-operators@^7.8.3": - version "7.10.4" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz" - integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig== - dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - -"@babel/plugin-syntax-nullish-coalescing-operator@^7.8.3": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz" - integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-numeric-separator@^7.8.3": - version "7.10.4" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz" - integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug== - dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - -"@babel/plugin-syntax-object-rest-spread@^7.8.3": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz" - integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-optional-catch-binding@^7.8.3": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz" - integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-optional-chaining@^7.8.3": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz" - integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-top-level-await@^7.8.3": - version "7.14.5" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz" - integrity sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-syntax-typescript@^7.7.2": - version "7.21.4" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.21.4.tgz" - integrity sha512-xz0D39NvhQn4t4RNsHmDnnsaQizIlUkdtYvLs8La1BlfjQ6JEwxkJGeqJMW2tAXx+q6H+WFuUTXNdYVpEya0YA== - dependencies: - "@babel/helper-plugin-utils" "^7.20.2" - -"@babel/runtime@^7.15.4": - version "7.21.0" - resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.0.tgz" - integrity sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw== - dependencies: - regenerator-runtime "^0.13.11" - -"@babel/template@^7.20.7", "@babel/template@^7.22.15", "@babel/template@^7.3.3": - version "7.22.15" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.15.tgz#09576efc3830f0430f4548ef971dde1350ef2f38" - integrity sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w== - dependencies: - "@babel/code-frame" "^7.22.13" - "@babel/parser" "^7.22.15" - "@babel/types" "^7.22.15" - -"@babel/traverse@^7.21.0", "@babel/traverse@^7.21.2", "@babel/traverse@^7.21.4": - version "7.23.6" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.6.tgz#b53526a2367a0dd6edc423637f3d2d0f2521abc5" - integrity sha512-czastdK1e8YByZqezMPFiZ8ahwVMh/ESl9vPgvgdB9AmFMGP5jfpFax74AQgl5zj4XHzqeYAg2l8PuUeRS1MgQ== - dependencies: - "@babel/code-frame" "^7.23.5" - "@babel/generator" "^7.23.6" - "@babel/helper-environment-visitor" "^7.22.20" - "@babel/helper-function-name" "^7.23.0" - "@babel/helper-hoist-variables" "^7.22.5" - "@babel/helper-split-export-declaration" "^7.22.6" - "@babel/parser" "^7.23.6" - "@babel/types" "^7.23.6" - debug "^4.3.1" - globals "^11.1.0" - -"@babel/types@^7.0.0", "@babel/types@^7.20.2", "@babel/types@^7.20.7", "@babel/types@^7.21.0", "@babel/types@^7.21.2", "@babel/types@^7.21.4", "@babel/types@^7.22.15", "@babel/types@^7.22.5", "@babel/types@^7.23.0", "@babel/types@^7.23.6", "@babel/types@^7.3.0", "@babel/types@^7.3.3": - version "7.23.6" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.6.tgz#be33fdb151e1f5a56877d704492c240fc71c7ccd" - integrity sha512-+uarb83brBzPKN38NX1MkB6vb6+mwvR6amUulqAE7ccQw1pEl+bCia9TbdG1lsnFP7lZySvUn37CHyXQdfTwzg== - dependencies: - "@babel/helper-string-parser" "^7.23.4" - "@babel/helper-validator-identifier" "^7.22.20" - to-fast-properties "^2.0.0" - -"@balena/dockerignore@^1.0.2": - version "1.0.2" - resolved "https://registry.npmjs.org/@balena/dockerignore/-/dockerignore-1.0.2.tgz" - integrity sha512-wMue2Sy4GAVTk6Ic4tJVcnfdau+gx2EnG7S+uAEe+TWJFqE4YoWN4/H8MSLj4eYJKxGg26lZwboEniNiNwZQ6Q== - -"@bcoe/v8-coverage@^0.2.3": - version "0.2.3" - resolved "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz" - integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== - -"@budibase/backend-core@^2.3.17": - version "2.3.17" - resolved "https://registry.npmjs.org/@budibase/backend-core/-/backend-core-2.3.17.tgz" - integrity sha512-KcmF2OrNLjLbFtNbYD4ZufnsnwmN2Ez/occgWiecvFRAHOhpkm+Hoy6VggpG1YJBp1DG9kLh3WAZbeYI3QoJbw== - dependencies: - "@budibase/nano" "10.1.1" - "@budibase/types" "^2.3.17" - "@shopify/jest-koa-mocks" "5.0.1" - "@techpass/passport-openidconnect" "0.3.2" - aws-cloudfront-sign "2.2.0" - aws-sdk "2.1030.0" - bcrypt "5.0.1" - bcryptjs "2.4.3" - bull "4.10.1" - correlation-id "4.0.0" - dotenv "16.0.1" - emitter-listener "1.1.2" - ioredis "4.28.0" - joi "17.6.0" - jsonwebtoken "9.0.0" - koa-passport "4.1.4" - lodash "4.17.21" - lodash.isarguments "3.1.0" - node-fetch "2.6.7" - passport-google-oauth "2.0.0" - passport-jwt "4.0.0" - passport-local "1.0.0" - passport-oauth2-refresh "^2.1.0" - posthog-node "1.3.0" - pouchdb "7.3.0" - pouchdb-find "7.2.2" - pouchdb-replication-stream "1.2.9" - redlock "4.2.0" - sanitize-s3-objectkey "0.0.1" - semver "7.3.7" - tar-fs "2.1.1" - uuid "8.3.2" - zlib "1.0.5" - -"@budibase/nano@10.1.1": - version "10.1.1" - resolved "https://registry.npmjs.org/@budibase/nano/-/nano-10.1.1.tgz" - integrity sha512-kbMIzMkjVtl+xI0UPwVU0/pn8/ccxTyfzwBz6Z+ZiN2oUSb0fJCe0qwA6o8dxwSa8nZu4MbGAeMJl3CJndmWtA== - dependencies: - "@types/tough-cookie" "^4.0.2" - axios "^1.1.3" - http-cookie-agent "^4.0.2" - node-abort-controller "^3.0.1" - qs "^6.11.0" - tough-cookie "^4.1.2" - -"@budibase/types@^2.3.17": - version "2.3.17" - resolved "https://registry.npmjs.org/@budibase/types/-/types-2.3.17.tgz" - integrity sha512-p/6WgwNjVGfwyNLOofhPEG7S3tt5URxAVs+mPXuLn5bsAqRxxJ5XObvw8chijYXmewhGP0hjONQDkmDJ0FkHuA== - -"@cspotcode/source-map-support@^0.8.0": - version "0.8.1" - resolved "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz" - integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw== - dependencies: - "@jridgewell/trace-mapping" "0.3.9" - -"@hapi/hoek@^9.0.0": - version "9.3.0" - resolved "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz" - integrity sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ== - -"@hapi/topo@^5.0.0": - version "5.1.0" - resolved "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz" - integrity sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg== - dependencies: - "@hapi/hoek" "^9.0.0" - -"@istanbuljs/load-nyc-config@^1.0.0": - version "1.1.0" - resolved "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz" - integrity sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ== - dependencies: - camelcase "^5.3.1" - find-up "^4.1.0" - get-package-type "^0.1.0" - js-yaml "^3.13.1" - resolve-from "^5.0.0" - -"@istanbuljs/schema@^0.1.2": - version "0.1.3" - resolved "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz" - integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== - -"@jest/console@^29.7.0": - version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/console/-/console-29.7.0.tgz#cd4822dbdb84529265c5a2bdb529a3c9cc950ffc" - integrity sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg== - dependencies: - "@jest/types" "^29.6.3" - "@types/node" "*" - chalk "^4.0.0" - jest-message-util "^29.7.0" - jest-util "^29.7.0" - slash "^3.0.0" - -"@jest/core@^29.7.0": - version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/core/-/core-29.7.0.tgz#b6cccc239f30ff36609658c5a5e2291757ce448f" - integrity sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg== - dependencies: - "@jest/console" "^29.7.0" - "@jest/reporters" "^29.7.0" - "@jest/test-result" "^29.7.0" - "@jest/transform" "^29.7.0" - "@jest/types" "^29.6.3" - "@types/node" "*" - ansi-escapes "^4.2.1" - chalk "^4.0.0" - ci-info "^3.2.0" - exit "^0.1.2" - graceful-fs "^4.2.9" - jest-changed-files "^29.7.0" - jest-config "^29.7.0" - jest-haste-map "^29.7.0" - jest-message-util "^29.7.0" - jest-regex-util "^29.6.3" - jest-resolve "^29.7.0" - jest-resolve-dependencies "^29.7.0" - jest-runner "^29.7.0" - jest-runtime "^29.7.0" - jest-snapshot "^29.7.0" - jest-util "^29.7.0" - jest-validate "^29.7.0" - jest-watcher "^29.7.0" - micromatch "^4.0.4" - pretty-format "^29.7.0" - slash "^3.0.0" - strip-ansi "^6.0.0" - -"@jest/create-cache-key-function@^27.4.2": - version "27.5.1" - resolved "https://registry.yarnpkg.com/@jest/create-cache-key-function/-/create-cache-key-function-27.5.1.tgz#7448fae15602ea95c828f5eceed35c202a820b31" - integrity sha512-dmH1yW+makpTSURTy8VzdUwFnfQh1G8R+DxO2Ho2FFmBbKFEVm+3jWdvFhE2VqB/LATCTokkP0dotjyQyw5/AQ== - dependencies: - "@jest/types" "^27.5.1" - -"@jest/environment@^29.7.0": - version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-29.7.0.tgz#24d61f54ff1f786f3cd4073b4b94416383baf2a7" - integrity sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw== - dependencies: - "@jest/fake-timers" "^29.7.0" - "@jest/types" "^29.6.3" - "@types/node" "*" - jest-mock "^29.7.0" - -"@jest/expect-utils@^29.7.0": - version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.7.0.tgz#023efe5d26a8a70f21677d0a1afc0f0a44e3a1c6" - integrity sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA== - dependencies: - jest-get-type "^29.6.3" - -"@jest/expect@^29.7.0": - version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-29.7.0.tgz#76a3edb0cb753b70dfbfe23283510d3d45432bf2" - integrity sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ== - dependencies: - expect "^29.7.0" - jest-snapshot "^29.7.0" - -"@jest/fake-timers@^29.7.0": - version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-29.7.0.tgz#fd91bf1fffb16d7d0d24a426ab1a47a49881a565" - integrity sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ== - dependencies: - "@jest/types" "^29.6.3" - "@sinonjs/fake-timers" "^10.0.2" - "@types/node" "*" - jest-message-util "^29.7.0" - jest-mock "^29.7.0" - jest-util "^29.7.0" - -"@jest/globals@^29.7.0": - version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-29.7.0.tgz#8d9290f9ec47ff772607fa864ca1d5a2efae1d4d" - integrity sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ== - dependencies: - "@jest/environment" "^29.7.0" - "@jest/expect" "^29.7.0" - "@jest/types" "^29.6.3" - jest-mock "^29.7.0" - -"@jest/reporters@^29.7.0": - version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-29.7.0.tgz#04b262ecb3b8faa83b0b3d321623972393e8f4c7" - integrity sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg== - dependencies: - "@bcoe/v8-coverage" "^0.2.3" - "@jest/console" "^29.7.0" - "@jest/test-result" "^29.7.0" - "@jest/transform" "^29.7.0" - "@jest/types" "^29.6.3" - "@jridgewell/trace-mapping" "^0.3.18" - "@types/node" "*" - chalk "^4.0.0" - collect-v8-coverage "^1.0.0" - exit "^0.1.2" - glob "^7.1.3" - graceful-fs "^4.2.9" - istanbul-lib-coverage "^3.0.0" - istanbul-lib-instrument "^6.0.0" - istanbul-lib-report "^3.0.0" - istanbul-lib-source-maps "^4.0.0" - istanbul-reports "^3.1.3" - jest-message-util "^29.7.0" - jest-util "^29.7.0" - jest-worker "^29.7.0" - slash "^3.0.0" - string-length "^4.0.1" - strip-ansi "^6.0.0" - v8-to-istanbul "^9.0.1" - -"@jest/schemas@^29.6.3": - version "29.6.3" - resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.6.3.tgz#430b5ce8a4e0044a7e3819663305a7b3091c8e03" - integrity sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA== - dependencies: - "@sinclair/typebox" "^0.27.8" - -"@jest/source-map@^29.6.3": - version "29.6.3" - resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-29.6.3.tgz#d90ba772095cf37a34a5eb9413f1b562a08554c4" - integrity sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw== - dependencies: - "@jridgewell/trace-mapping" "^0.3.18" - callsites "^3.0.0" - graceful-fs "^4.2.9" - -"@jest/test-result@^29.7.0": - version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-29.7.0.tgz#8db9a80aa1a097bb2262572686734baed9b1657c" - integrity sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA== - dependencies: - "@jest/console" "^29.7.0" - "@jest/types" "^29.6.3" - "@types/istanbul-lib-coverage" "^2.0.0" - collect-v8-coverage "^1.0.0" - -"@jest/test-sequencer@^29.7.0": - version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz#6cef977ce1d39834a3aea887a1726628a6f072ce" - integrity sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw== - dependencies: - "@jest/test-result" "^29.7.0" - graceful-fs "^4.2.9" - jest-haste-map "^29.7.0" - slash "^3.0.0" - -"@jest/transform@^29.7.0": - version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-29.7.0.tgz#df2dd9c346c7d7768b8a06639994640c642e284c" - integrity sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw== - dependencies: - "@babel/core" "^7.11.6" - "@jest/types" "^29.6.3" - "@jridgewell/trace-mapping" "^0.3.18" - babel-plugin-istanbul "^6.1.1" - chalk "^4.0.0" - convert-source-map "^2.0.0" - fast-json-stable-stringify "^2.1.0" - graceful-fs "^4.2.9" - jest-haste-map "^29.7.0" - jest-regex-util "^29.6.3" - jest-util "^29.7.0" - micromatch "^4.0.4" - pirates "^4.0.4" - slash "^3.0.0" - write-file-atomic "^4.0.2" - -"@jest/types@^27.5.1": - version "27.5.1" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-27.5.1.tgz#3c79ec4a8ba61c170bf937bcf9e98a9df175ec80" - integrity sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw== - dependencies: - "@types/istanbul-lib-coverage" "^2.0.0" - "@types/istanbul-reports" "^3.0.0" - "@types/node" "*" - "@types/yargs" "^16.0.0" - chalk "^4.0.0" - -"@jest/types@^29.6.3": - version "29.6.3" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.6.3.tgz#1131f8cf634e7e84c5e77bab12f052af585fba59" - integrity sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw== - dependencies: - "@jest/schemas" "^29.6.3" - "@types/istanbul-lib-coverage" "^2.0.0" - "@types/istanbul-reports" "^3.0.0" - "@types/node" "*" - "@types/yargs" "^17.0.8" - chalk "^4.0.0" - -"@jridgewell/gen-mapping@^0.1.0": - version "0.1.1" - resolved "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz" - integrity sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w== - dependencies: - "@jridgewell/set-array" "^1.0.0" - "@jridgewell/sourcemap-codec" "^1.4.10" - -"@jridgewell/gen-mapping@^0.3.2": - version "0.3.2" - resolved "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz" - integrity sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A== - dependencies: - "@jridgewell/set-array" "^1.0.1" - "@jridgewell/sourcemap-codec" "^1.4.10" - "@jridgewell/trace-mapping" "^0.3.9" - -"@jridgewell/resolve-uri@3.1.0", "@jridgewell/resolve-uri@^3.0.3": - version "3.1.0" - resolved "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz" - integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== - -"@jridgewell/set-array@^1.0.0", "@jridgewell/set-array@^1.0.1": - version "1.1.2" - resolved "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz" - integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== - -"@jridgewell/sourcemap-codec@1.4.14", "@jridgewell/sourcemap-codec@^1.4.10": - version "1.4.14" - resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz" - integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== - -"@jridgewell/trace-mapping@0.3.9": - version "0.3.9" - resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz" - integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ== - dependencies: - "@jridgewell/resolve-uri" "^3.0.3" - "@jridgewell/sourcemap-codec" "^1.4.10" - -"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.9": - version "0.3.18" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz#25783b2086daf6ff1dcb53c9249ae480e4dd4cd6" - integrity sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA== - dependencies: - "@jridgewell/resolve-uri" "3.1.0" - "@jridgewell/sourcemap-codec" "1.4.14" - -"@mapbox/node-pre-gyp@^1.0.0": - version "1.0.10" - resolved "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.10.tgz" - integrity sha512-4ySo4CjzStuprMwk35H5pPbkymjv1SF3jGLj6rAHp/xT/RF7TL7bd9CTm1xDY49K2qF7jmR/g7k+SkLETP6opA== - dependencies: - detect-libc "^2.0.0" - https-proxy-agent "^5.0.0" - make-dir "^3.1.0" - node-fetch "^2.6.7" - nopt "^5.0.0" - npmlog "^5.0.1" - rimraf "^3.0.2" - semver "^7.3.5" - tar "^6.1.11" - -"@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.2": - version "3.0.2" - resolved "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.2.tgz" - integrity sha512-9bfjwDxIDWmmOKusUcqdS4Rw+SETlp9Dy39Xui9BEGEk19dDwH0jhipwFzEff/pFg95NKymc6TOTbRKcWeRqyQ== - -"@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.2": - version "3.0.2" - resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-3.0.2.tgz#f954f34355712212a8e06c465bc06c40852c6bb3" - integrity sha512-lwriRAHm1Yg4iDf23Oxm9n/t5Zpw1lVnxYU3HnJPTi2lJRkKTrps1KVgvL6m7WvmhYVt/FIsssWay+k45QHeuw== - -"@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.2": - version "3.0.2" - resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-3.0.2.tgz#45c63037f045c2b15c44f80f0393fa24f9655367" - integrity sha512-FU20Bo66/f7He9Fp9sP2zaJ1Q8L9uLPZQDub/WlUip78JlPeMbVL8546HbZfcW9LNciEXc8d+tThSJjSC+tmsg== - -"@msgpackr-extract/msgpackr-extract-linux-arm@3.0.2": - version "3.0.2" - resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-3.0.2.tgz#35707efeafe6d22b3f373caf9e8775e8920d1399" - integrity sha512-MOI9Dlfrpi2Cuc7i5dXdxPbFIgbDBGgKR5F2yWEa6FVEtSWncfVNKW5AKjImAQ6CZlBK9tympdsZJ2xThBiWWA== - -"@msgpackr-extract/msgpackr-extract-linux-x64@3.0.2": - version "3.0.2" - resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-3.0.2.tgz#091b1218b66c341f532611477ef89e83f25fae4f" - integrity sha512-gsWNDCklNy7Ajk0vBBf9jEx04RUxuDQfBse918Ww+Qb9HCPoGzS+XJTLe96iN3BVK7grnLiYghP/M4L8VsaHeA== - -"@msgpackr-extract/msgpackr-extract-win32-x64@3.0.2": - version "3.0.2" - resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.2.tgz#0f164b726869f71da3c594171df5ebc1c4b0a407" - integrity sha512-O+6Gs8UeDbyFpbSh2CPEz/UOrrdWPTBYNblZK5CxxLisYt4kGX3Sc+czffFonyjiGSq3jWLwJS/CCJc7tBr4sQ== - -"@shopify/jest-koa-mocks@5.0.1": - version "5.0.1" - resolved "https://registry.npmjs.org/@shopify/jest-koa-mocks/-/jest-koa-mocks-5.0.1.tgz" - integrity sha512-4YskS9q8+TEHNoyopmuoy2XyhInyqeOl7CF5ShJs19sm6m0EA/jGGvgf/osv2PeTfuf42/L2G9CzWUSg49yTSg== - dependencies: - koa "^2.13.4" - node-mocks-http "^1.11.0" - -"@sideway/address@^4.1.3": - version "4.1.4" - resolved "https://registry.npmjs.org/@sideway/address/-/address-4.1.4.tgz" - integrity sha512-7vwq+rOHVWjyXxVlR76Agnvhy8I9rpzjosTESvmhNeXOXdZZB15Fl+TI9x1SiHZH5Jv2wTGduSxFDIaq0m3DUw== - dependencies: - "@hapi/hoek" "^9.0.0" - -"@sideway/formula@^3.0.0": - version "3.0.1" - resolved "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz" - integrity sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg== - -"@sideway/pinpoint@^2.0.0": - version "2.0.0" - resolved "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz" - integrity sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ== - -"@sinclair/typebox@^0.27.8": - version "0.27.8" - resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" - integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA== - -"@sinonjs/commons@^2.0.0": - version "2.0.0" - resolved "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz" - integrity sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg== - dependencies: - type-detect "4.0.8" - -"@sinonjs/fake-timers@^10.0.2": - version "10.0.2" - resolved "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.0.2.tgz" - integrity sha512-SwUDyjWnah1AaNl7kxsa7cfLhlTYoiyhDAIgyh+El30YvXs/o7OLXpYH88Zdhyx9JExKrmHDJ+10bwIcY80Jmw== - dependencies: - "@sinonjs/commons" "^2.0.0" - -"@swc/core-darwin-arm64@1.3.71": - version "1.3.71" - resolved "https://registry.yarnpkg.com/@swc/core-darwin-arm64/-/core-darwin-arm64-1.3.71.tgz#3cc2bfa7e3f89ec18987af863b2260a5340ff0a7" - integrity sha512-xOm0hDbcO2ShwQu1CjLtq3fwrG9AvhuE0s8vtBc8AsamYExHmR8bo6GQHJUtfPG1FVPk5a8xoQSd1fs09FQjLg== - -"@swc/core-darwin-x64@1.3.71": - version "1.3.71" - resolved "https://registry.yarnpkg.com/@swc/core-darwin-x64/-/core-darwin-x64-1.3.71.tgz#0f5439994013480454dfe2a5aff8861e93316fe3" - integrity sha512-9sbDXBWgM22w/3Ll5kPhXMPkOiHRoqwMOyxLJBfGtIMnFlh5O+NRN3umRerK3pe4Q6/7hj2M5V+crEHYrXmuxg== - -"@swc/core-linux-arm-gnueabihf@1.3.71": - version "1.3.71" - resolved "https://registry.yarnpkg.com/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.3.71.tgz#77ea469736802ce2865fbc4893991b7abf369e3e" - integrity sha512-boKdMZsfKvhBs0FDeqH7KQj0lfYe0wCtrL1lv50oYMEeLajY9o4U5xSmc61Sg4HRXjlbR6dlM2cFfL84t7NpAA== - -"@swc/core-linux-arm64-gnu@1.3.71": - version "1.3.71" - resolved "https://registry.yarnpkg.com/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.3.71.tgz#8a17c17fac03a448484af41fa35e45458da312b5" - integrity sha512-yDatyHYMiOVwhyIA/LBwknPs2CUtLYWEMzPZjgLc+56PbgPs3oiEbNWeVUND5onPrfDQgK7NK1y8JeiXZqTgGQ== - -"@swc/core-linux-arm64-musl@1.3.71": - version "1.3.71" - resolved "https://registry.yarnpkg.com/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.3.71.tgz#bd3bf4310870a8a60a9dc834502d6852cd2b129b" - integrity sha512-xAdCA0L/hoa0ULL5SR4sMZCxkWk7C90DOU7wJalNVG9qNWYICfq3G7AR0E9Ohphzqyahfb5QJED/nA7N0+XwbQ== - -"@swc/core-linux-x64-gnu@1.3.71": - version "1.3.71" - resolved "https://registry.yarnpkg.com/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.3.71.tgz#5c1f5ecb8fa96456195e75ac12c40372896d4b89" - integrity sha512-j94qLXP/yqhu2afnABAq/xrJIU8TEqcNkp1TlsAeO3R2nVLYL1w4XX8GW71SPnXmd2bwF102c3Cfv/2ilf2y2A== - -"@swc/core-linux-x64-musl@1.3.71": - version "1.3.71" - resolved "https://registry.yarnpkg.com/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.3.71.tgz#5fa99bd115d3bf90aebcee8793644f998024fcbe" - integrity sha512-YiyU848ql6dLlmt0BHccGAaZ36Cf61VzCAMDKID/gd72snvzWcMCHrwSRW0gEFNXHsjBJrmNl+SLYZHfqoGwUA== - -"@swc/core-win32-arm64-msvc@1.3.71": - version "1.3.71" - resolved "https://registry.yarnpkg.com/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.3.71.tgz#4e39975a51c56911e1183efd2106c0e74fe89b1c" - integrity sha512-1UsJ+6hnIRe/PVdgDPexvgGaN4KpBncT/bAOqlWc9XC7KeBXAWcGA08LrPUz2Ei00DJXzR622IGZVEYOHNkUOw== - -"@swc/core-win32-ia32-msvc@1.3.71": - version "1.3.71" - resolved "https://registry.yarnpkg.com/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.3.71.tgz#6bb37d9fba8409078376d292711566ccf9a46145" - integrity sha512-KnuI89+zojR9lDFELdQYZpxzPZ6pBfLwJfWTSGatnpL1ZHhIsV3tK1jwqIdJK1zkRxpBwc6p6FzSZdZwCSpnJw== - -"@swc/core-win32-x64-msvc@1.3.71": - version "1.3.71" - resolved "https://registry.yarnpkg.com/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.3.71.tgz#33a53e4d5f93d13bae791451f3746d3da6a39984" - integrity sha512-Pcw7fFirpaBOZsU8fhO48ZCb7NxIjuLnLRPrHqWQ4Mapx1+w9ZNdGya2DKP9n8EAiUrJO20WDsrBNMT2MQSWkA== - -"@swc/core@1.3.71": - version "1.3.71" - resolved "https://registry.yarnpkg.com/@swc/core/-/core-1.3.71.tgz#7911038a5577005a5f12b9b2e31f6c804a0c4b7e" - integrity sha512-T8dqj+SV/S8laW/FGmKHhCGw1o4GRUvJ2jHfbYgEwiJpeutT9uavHvG02t39HJvObBJ52EZs/krGtni4U5928Q== - optionalDependencies: - "@swc/core-darwin-arm64" "1.3.71" - "@swc/core-darwin-x64" "1.3.71" - "@swc/core-linux-arm-gnueabihf" "1.3.71" - "@swc/core-linux-arm64-gnu" "1.3.71" - "@swc/core-linux-arm64-musl" "1.3.71" - "@swc/core-linux-x64-gnu" "1.3.71" - "@swc/core-linux-x64-musl" "1.3.71" - "@swc/core-win32-arm64-msvc" "1.3.71" - "@swc/core-win32-ia32-msvc" "1.3.71" - "@swc/core-win32-x64-msvc" "1.3.71" - -"@swc/jest@0.2.27": - version "0.2.27" - resolved "https://registry.yarnpkg.com/@swc/jest/-/jest-0.2.27.tgz#f6cbd0b6f95cf689c3344c63fc379fa680cdbf52" - integrity sha512-Xt8EJ6Wy0NYVL8KDPcDMsuUSzyV2UAByamyy28x2iDZCJw2eVz3acedCGBYxxlPR/DNr6QbA35OSymuXhC9QVA== - dependencies: - "@jest/create-cache-key-function" "^27.4.2" - jsonc-parser "^3.2.0" - -"@techpass/passport-openidconnect@0.3.2": - version "0.3.2" - resolved "https://registry.npmjs.org/@techpass/passport-openidconnect/-/passport-openidconnect-0.3.2.tgz" - integrity sha512-fnCtEiexXSHA029B//hJcCJlLJrT3lhpNCyA0rnz58Qttz0BLGCVv6yMT8HmOnGThH6vcDOVwdgKM3kbCQtEhw== - dependencies: - base64url "^3.0.1" - oauth "^0.9.15" - passport-strategy "^1.0.0" - request "^2.88.0" - webfinger "^0.4.2" - -"@trendyol/jest-testcontainers@2.1.1": - version "2.1.1" - resolved "https://registry.yarnpkg.com/@trendyol/jest-testcontainers/-/jest-testcontainers-2.1.1.tgz#dced95cf9c37b75efe0a65db9b75ae8912f2f14a" - integrity sha512-4iAc2pMsev4BTUzoA7jO1VvbTOU2N3juQUYa8TwiSPXPuQtxKwV9WB9ZEP+JQ+Pj15YqfGOXp5H0WNMPtapjiA== - dependencies: - cwd "^0.10.0" - node-duration "^1.0.4" - testcontainers "4.7.0" - -"@tsconfig/node10@^1.0.7": - version "1.0.9" - resolved "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz" - integrity sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA== - -"@tsconfig/node12@^1.0.7": - version "1.0.11" - resolved "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz" - integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag== - -"@tsconfig/node14@^1.0.0": - version "1.0.3" - resolved "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz" - integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow== - -"@tsconfig/node16@^1.0.2": - version "1.0.3" - resolved "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz" - integrity sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ== - -"@types/babel__core@^7.1.14": - version "7.20.0" - resolved "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.0.tgz" - integrity sha512-+n8dL/9GWblDO0iU6eZAwEIJVr5DWigtle+Q6HLOrh/pdbXOhOtqzq8VPPE2zvNJzSKY4vH/z3iT3tn0A3ypiQ== - dependencies: - "@babel/parser" "^7.20.7" - "@babel/types" "^7.20.7" - "@types/babel__generator" "*" - "@types/babel__template" "*" - "@types/babel__traverse" "*" - -"@types/babel__generator@*": - version "7.6.4" - resolved "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz" - integrity sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg== - dependencies: - "@babel/types" "^7.0.0" - -"@types/babel__template@*": - version "7.4.1" - resolved "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz" - integrity sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g== - dependencies: - "@babel/parser" "^7.1.0" - "@babel/types" "^7.0.0" - -"@types/babel__traverse@*", "@types/babel__traverse@^7.0.6": - version "7.18.3" - resolved "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.18.3.tgz" - integrity sha512-1kbcJ40lLB7MHsj39U4Sh1uTd2E7rLEa79kmDpI6cy+XiXsteB3POdQomoq4FxszMrO3ZYchkhYJw7A2862b3w== - dependencies: - "@babel/types" "^7.3.0" - -"@types/dockerode@^2.5.34": - version "2.5.34" - resolved "https://registry.npmjs.org/@types/dockerode/-/dockerode-2.5.34.tgz" - integrity sha512-LcbLGcvcBwBAvjH9UrUI+4qotY+A5WCer5r43DR5XHv2ZIEByNXFdPLo1XxR+v/BjkGjlggW8qUiXuVEhqfkpA== - dependencies: - "@types/node" "*" - -"@types/graceful-fs@^4.1.3": - version "4.1.6" - resolved "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.6.tgz" - integrity sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw== - dependencies: - "@types/node" "*" - -"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": - version "2.0.4" - resolved "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz" - integrity sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g== - -"@types/istanbul-lib-report@*": - version "3.0.0" - resolved "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz" - integrity sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg== - dependencies: - "@types/istanbul-lib-coverage" "*" - -"@types/istanbul-reports@^3.0.0": - version "3.0.1" - resolved "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz" - integrity sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw== - dependencies: - "@types/istanbul-lib-report" "*" - -"@types/jest@29.5.3": - version "29.5.3" - resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.3.tgz#7a35dc0044ffb8b56325c6802a4781a626b05777" - integrity sha512-1Nq7YrO/vJE/FYnqYyw0FS8LdrjExSgIiHyKg7xPpn+yi8Q4huZryKnkJatN1ZRH89Kw2v33/8ZMB7DuZeSLlA== - dependencies: - expect "^29.0.0" - pretty-format "^29.0.0" - -"@types/node-fetch@2.6.4": - version "2.6.4" - resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.4.tgz#1bc3a26de814f6bf466b25aeb1473fa1afe6a660" - integrity sha512-1ZX9fcN4Rvkvgv4E6PAY5WXUFWFcRWxZa3EW83UjycOB9ljJCedb2CupIP4RZMEwF/M3eTcCihbBRgwtGbg5Rg== - dependencies: - "@types/node" "*" - form-data "^3.0.0" - -"@types/node@*": - version "18.15.1" - resolved "https://registry.npmjs.org/@types/node/-/node-18.15.1.tgz" - integrity sha512-U2TWca8AeHSmbpi314QBESRk7oPjSZjDsR+c+H4ECC1l+kFgpZf8Ydhv3SJpPy51VyZHHqxlb6mTTqYNNRVAIw== - -"@types/node@>=8.1.0": - version "20.11.0" - resolved "https://registry.yarnpkg.com/@types/node/-/node-20.11.0.tgz#8e0b99e70c0c1ade1a86c4a282f7b7ef87c9552f" - integrity sha512-o9bjXmDNcF7GbM4CNQpmi+TutCgap/K3w1JyKgxAjqx41zp9qlIAVFi0IhCNsJcXolEqLWhbFbEeL0PvYm4pcQ== - dependencies: - undici-types "~5.26.4" - -"@types/stack-utils@^2.0.0": - version "2.0.1" - resolved "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz" - integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw== - -"@types/tough-cookie@^4.0.2": - version "4.0.2" - resolved "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.2.tgz" - integrity sha512-Q5vtl1W5ue16D+nIaW8JWebSSraJVlK+EthKn7e7UcD4KWsaSJ8BqGPXNaPghgtcn/fhvrN17Tv8ksUsQpiplw== - -"@types/yargs-parser@*": - version "21.0.0" - resolved "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz" - integrity sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA== - -"@types/yargs@^16.0.0": - version "16.0.5" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-16.0.5.tgz#12cc86393985735a283e387936398c2f9e5f88e3" - integrity sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ== - dependencies: - "@types/yargs-parser" "*" - -"@types/yargs@^17.0.8": - version "17.0.22" - resolved "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.22.tgz" - integrity sha512-pet5WJ9U8yPVRhkwuEIp5ktAeAqRZOq4UdAyWLWzxbtpyXnzbtLdKiXAjJzi/KLmPGS9wk86lUFWZFN6sISo4g== - dependencies: - "@types/yargs-parser" "*" - -abbrev@1: - version "1.1.1" - resolved "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz" - integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== - -abort-controller@3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz" - integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg== - dependencies: - event-target-shim "^5.0.0" - -abstract-leveldown@^6.2.1, abstract-leveldown@~6.2.1, abstract-leveldown@~6.2.3: - version "6.2.3" - resolved "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-6.2.3.tgz" - integrity sha512-BsLm5vFMRUrrLeCcRc+G0t2qOaTzpoJQLOubq2XM72eNpjF5UdU5o/5NvlNhx95XHcAvcl8OMXr4mlg/fRgUXQ== - dependencies: - buffer "^5.5.0" - immediate "^3.2.3" - level-concat-iterator "~2.0.0" - level-supports "~1.0.0" - xtend "~4.0.0" - -accepts@^1.3.5, accepts@^1.3.7: - version "1.3.8" - resolved "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz" - integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== - dependencies: - mime-types "~2.1.34" - negotiator "0.6.3" - -acorn-walk@^8.1.1: - version "8.2.0" - resolved "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz" - integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== - -acorn@^8.4.1: - version "8.8.2" - resolved "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz" - integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw== - -agent-base@6, agent-base@^6.0.2: - version "6.0.2" - resolved "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz" - integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== - dependencies: - debug "4" - -ajv@^6.12.3: - version "6.12.6" - resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz" - integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== - dependencies: - fast-deep-equal "^3.1.1" - fast-json-stable-stringify "^2.0.0" - json-schema-traverse "^0.4.1" - uri-js "^4.2.2" - -ansi-escapes@^4.2.1: - version "4.3.2" - resolved "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz" - integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== - dependencies: - type-fest "^0.21.3" - -ansi-regex@^5.0.1: - version "5.0.1" - resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz" - integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== - -ansi-styles@^3.2.1: - version "3.2.1" - resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz" - integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== - dependencies: - color-convert "^1.9.0" - -ansi-styles@^4.0.0, ansi-styles@^4.1.0: - version "4.3.0" - resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz" - integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== - dependencies: - color-convert "^2.0.1" - -ansi-styles@^5.0.0: - version "5.2.0" - resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz" - integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== - -any-promise@^1.1.0: - version "1.3.0" - resolved "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz" - integrity sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A== - -anymatch@^3.0.3: - version "3.1.3" - resolved "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz" - integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== - dependencies: - normalize-path "^3.0.0" - picomatch "^2.0.4" - -"aproba@^1.0.3 || ^2.0.0": - version "2.0.0" - resolved "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz" - integrity sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ== - -are-we-there-yet@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz" - integrity sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw== - dependencies: - delegates "^1.0.0" - readable-stream "^3.6.0" - -arg@^4.1.0: - version "4.1.3" - resolved "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz" - integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== - -argparse@^1.0.7: - version "1.0.10" - resolved "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz" - integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== - dependencies: - sprintf-js "~1.0.2" - -argsarray@0.0.1: - version "0.0.1" - resolved "https://registry.npmjs.org/argsarray/-/argsarray-0.0.1.tgz" - integrity sha512-u96dg2GcAKtpTrBdDoFIM7PjcBA+6rSP0OR94MOReNRyUECL6MtQt5XXmRr4qrftYaef9+l5hcpO5te7sML1Cg== - -asn1@^0.2.6, asn1@~0.2.3: - version "0.2.6" - resolved "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz" - integrity sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ== - dependencies: - safer-buffer "~2.1.0" - -assert-plus@1.0.0, assert-plus@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" - integrity sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw== - -asynckit@^0.4.0: - version "0.4.0" - resolved "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz" - integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== - -aws-cloudfront-sign@2.2.0: - version "2.2.0" - resolved "https://registry.npmjs.org/aws-cloudfront-sign/-/aws-cloudfront-sign-2.2.0.tgz" - integrity sha512-qG+rwZMP3KRTPPbVmWY8DlrT56AkA4iVOeo23vkdK2EXeW/brJFN2haSNKzVz+oYhFMEIzVVloeAcrEzuRkuVQ== - dependencies: - lodash "^3.6.0" - -aws-sdk@2.1030.0: - version "2.1030.0" - resolved "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1030.0.tgz" - integrity sha512-to0STOb8DsSGuSsUb/WCbg/UFnMGfIYavnJH5ZlRCHzvCFjTyR+vfE8ku+qIZvfFM4+5MNTQC/Oxfun2X/TuyA== - dependencies: - buffer "4.9.2" - events "1.1.1" - ieee754 "1.1.13" - jmespath "0.15.0" - querystring "0.2.0" - sax "1.2.1" - url "0.10.3" - uuid "3.3.2" - xml2js "0.4.19" - -aws-sign2@~0.7.0: - version "0.7.0" - resolved "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz" - integrity sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA== - -aws4@^1.8.0: - version "1.12.0" - resolved "https://registry.npmjs.org/aws4/-/aws4-1.12.0.tgz" - integrity sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg== - -axios-retry@^3.1.9: - version "3.4.0" - resolved "https://registry.npmjs.org/axios-retry/-/axios-retry-3.4.0.tgz" - integrity sha512-VdgaP+gHH4iQYCCNUWF2pcqeciVOdGrBBAYUfTY+wPcO5Ltvp/37MLFNCmJKo7Gj3SHvCSdL8ouI1qLYJN3liA== - dependencies: - "@babel/runtime" "^7.15.4" - is-retry-allowed "^2.2.0" - -axios@0.24.0: - version "0.24.0" - resolved "https://registry.npmjs.org/axios/-/axios-0.24.0.tgz" - integrity sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA== - dependencies: - follow-redirects "^1.14.4" - -axios@^0.21.1: - version "0.21.4" - resolved "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz" - integrity sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg== - dependencies: - follow-redirects "^1.14.0" - -axios@^1.1.3: - version "1.6.2" - resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.2.tgz#de67d42c755b571d3e698df1b6504cde9b0ee9f2" - integrity sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A== - dependencies: - follow-redirects "^1.15.0" - form-data "^4.0.0" - proxy-from-env "^1.1.0" - -babel-jest@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.7.0.tgz#f4369919225b684c56085998ac63dbd05be020d5" - integrity sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg== - dependencies: - "@jest/transform" "^29.7.0" - "@types/babel__core" "^7.1.14" - babel-plugin-istanbul "^6.1.1" - babel-preset-jest "^29.6.3" - chalk "^4.0.0" - graceful-fs "^4.2.9" - slash "^3.0.0" - -babel-plugin-istanbul@^6.1.1: - version "6.1.1" - resolved "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz" - integrity sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@istanbuljs/load-nyc-config" "^1.0.0" - "@istanbuljs/schema" "^0.1.2" - istanbul-lib-instrument "^5.0.4" - test-exclude "^6.0.0" - -babel-plugin-jest-hoist@^29.6.3: - version "29.6.3" - resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz#aadbe943464182a8922c3c927c3067ff40d24626" - integrity sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg== - dependencies: - "@babel/template" "^7.3.3" - "@babel/types" "^7.3.3" - "@types/babel__core" "^7.1.14" - "@types/babel__traverse" "^7.0.6" - -babel-preset-current-node-syntax@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz" - integrity sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ== - dependencies: - "@babel/plugin-syntax-async-generators" "^7.8.4" - "@babel/plugin-syntax-bigint" "^7.8.3" - "@babel/plugin-syntax-class-properties" "^7.8.3" - "@babel/plugin-syntax-import-meta" "^7.8.3" - "@babel/plugin-syntax-json-strings" "^7.8.3" - "@babel/plugin-syntax-logical-assignment-operators" "^7.8.3" - "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" - "@babel/plugin-syntax-numeric-separator" "^7.8.3" - "@babel/plugin-syntax-object-rest-spread" "^7.8.3" - "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" - "@babel/plugin-syntax-optional-chaining" "^7.8.3" - "@babel/plugin-syntax-top-level-await" "^7.8.3" - -babel-preset-jest@^29.6.3: - version "29.6.3" - resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz#fa05fa510e7d493896d7b0dd2033601c840f171c" - integrity sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA== - dependencies: - babel-plugin-jest-hoist "^29.6.3" - babel-preset-current-node-syntax "^1.0.0" - -balanced-match@^1.0.0: - version "1.0.2" - resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz" - integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== - -base64-js@^1.0.2, base64-js@^1.3.1: - version "1.5.1" - resolved "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz" - integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== - -base64url@3.x.x, base64url@^3.0.1: - version "3.0.1" - resolved "https://registry.npmjs.org/base64url/-/base64url-3.0.1.tgz" - integrity sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A== - -bcrypt-pbkdf@^1.0.0, bcrypt-pbkdf@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz" - integrity sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w== - dependencies: - tweetnacl "^0.14.3" - -bcrypt@5.0.1: - version "5.0.1" - resolved "https://registry.npmjs.org/bcrypt/-/bcrypt-5.0.1.tgz" - integrity sha512-9BTgmrhZM2t1bNuDtrtIMVSmmxZBrJ71n8Wg+YgdjHuIWYF7SjjmCPZFB+/5i/o/PIeRpwVJR3P+NrpIItUjqw== - dependencies: - "@mapbox/node-pre-gyp" "^1.0.0" - node-addon-api "^3.1.0" - -bcryptjs@2.4.3: - version "2.4.3" - resolved "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz" - integrity sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ== - -bl@^4.0.3: - version "4.1.0" - resolved "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz" - integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== - dependencies: - buffer "^5.5.0" - inherits "^2.0.4" - readable-stream "^3.4.0" - -bluebird@3.7.2, bluebird@^3.7.2: - version "3.7.2" - resolved "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz" - integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== - -brace-expansion@^1.1.7: - version "1.1.11" - resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz" - integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== - dependencies: - balanced-match "^1.0.0" - concat-map "0.0.1" - -braces@^3.0.2: - version "3.0.2" - resolved "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz" - integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== - dependencies: - fill-range "^7.0.1" - -browserslist@^4.21.3: - version "4.21.5" - resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz" - integrity sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w== - dependencies: - caniuse-lite "^1.0.30001449" - electron-to-chromium "^1.4.284" - node-releases "^2.0.8" - update-browserslist-db "^1.0.10" - -bs-logger@0.x: - version "0.2.6" - resolved "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz" - integrity sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog== - dependencies: - fast-json-stable-stringify "2.x" - -bser@2.1.1: - version "2.1.1" - resolved "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz" - integrity sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ== - dependencies: - node-int64 "^0.4.0" - -buffer-equal-constant-time@1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz" - integrity sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA== - -buffer-from@1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz" - integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== - -buffer-from@1.1.2, buffer-from@^1.0.0: - version "1.1.2" - resolved "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz" - integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== - -buffer@4.9.2: - version "4.9.2" - resolved "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz" - integrity sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg== - dependencies: - base64-js "^1.0.2" - ieee754 "^1.1.4" - isarray "^1.0.0" - -buffer@^5.5.0, buffer@^5.6.0: - version "5.7.1" - resolved "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz" - integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== - dependencies: - base64-js "^1.3.1" - ieee754 "^1.1.13" - -buildcheck@~0.0.6: - version "0.0.6" - resolved "https://registry.npmjs.org/buildcheck/-/buildcheck-0.0.6.tgz" - integrity sha512-8f9ZJCUXyT1M35Jx7MkBgmBMo3oHTTBIPLiY9xyL0pl3T5RwcPEY8cUHr5LBNfu/fk6c2T4DJZuVM/8ZZT2D2A== - -bull@4.10.1: - version "4.10.1" - resolved "https://registry.npmjs.org/bull/-/bull-4.10.1.tgz" - integrity sha512-Fp21tRPb2EaZPVfmM+ONZKVz2RA+to+zGgaTLyCKt3JMSU8OOBqK8143OQrnGuGpsyE5G+9FevFAGhdZZfQP2g== - dependencies: - cron-parser "^4.2.1" - debuglog "^1.0.0" - get-port "^5.1.1" - ioredis "^4.28.5" - lodash "^4.17.21" - msgpackr "^1.5.2" - p-timeout "^3.2.0" - semver "^7.3.2" - uuid "^8.3.0" - -byline@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/byline/-/byline-5.0.0.tgz" - integrity sha512-s6webAy+R4SR8XVuJWt2V2rGvhnrhxN+9S15GNuTK3wKPOXFF6RNc+8ug2XhH+2s4f+uudG4kUVYmYOQWL2g0Q== - -cache-content-type@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/cache-content-type/-/cache-content-type-1.0.1.tgz" - integrity sha512-IKufZ1o4Ut42YUrZSo8+qnMTrFuKkvyoLXUywKz9GJ5BrhOFGhLdkx9sG4KAnVvbY6kEcSFjLQul+DVmBm2bgA== - dependencies: - mime-types "^2.1.18" - ylru "^1.2.0" - -call-bind@^1.0.0: - version "1.0.2" - resolved "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz" - integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== - dependencies: - function-bind "^1.1.1" - get-intrinsic "^1.0.2" - -callsites@^3.0.0: - version "3.1.0" - resolved "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz" - integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== - -camelcase@^5.3.1: - version "5.3.1" - resolved "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz" - integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== - -camelcase@^6.2.0: - version "6.3.0" - resolved "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz" - integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== - -caniuse-lite@^1.0.30001449: - version "1.0.30001474" - resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001474.tgz" - integrity sha512-iaIZ8gVrWfemh5DG3T9/YqarVZoYf0r188IjaGwx68j4Pf0SGY6CQkmJUIE+NZHkkecQGohzXmBGEwWDr9aM3Q== - -caseless@~0.12.0: - version "0.12.0" - resolved "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz" - integrity sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw== - -chalk@^2.4.2: - version "2.4.2" - resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz" - integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== - dependencies: - ansi-styles "^3.2.1" - escape-string-regexp "^1.0.5" - supports-color "^5.3.0" - -chalk@^4.0.0: - version "4.1.2" - resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz" - integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== - dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" - -chance@1.1.8: - version "1.1.8" - resolved "https://registry.npmjs.org/chance/-/chance-1.1.8.tgz" - integrity sha512-v7fi5Hj2VbR6dJEGRWLmJBA83LJMS47pkAbmROFxHWd9qmE1esHRZW8Clf1Fhzr3rjxnNZVCjOEv/ivFxeIMtg== - -char-regex@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz" - integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== - -charenc@0.0.2: - version "0.0.2" - resolved "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz" - integrity sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA== - -check-more-types@2.24.0: - version "2.24.0" - resolved "https://registry.npmjs.org/check-more-types/-/check-more-types-2.24.0.tgz" - integrity sha512-Pj779qHxV2tuapviy1bSZNEL1maXr13bPYpsvSDB68HlYcYuhlDrmGd63i0JHMCLKzc7rUSNIrpdJlhVlNwrxA== - -chownr@^1.1.1: - version "1.1.4" - resolved "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz" - integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== - -chownr@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz" - integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== - -ci-info@^3.2.0: - version "3.8.0" - resolved "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz" - integrity sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw== - -cjs-module-lexer@^1.0.0: - version "1.2.2" - resolved "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz" - integrity sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA== - -cliui@^8.0.1: - version "8.0.1" - resolved "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz" - integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== - dependencies: - string-width "^4.2.0" - strip-ansi "^6.0.1" - wrap-ansi "^7.0.0" - -clone-buffer@1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/clone-buffer/-/clone-buffer-1.0.0.tgz" - integrity sha512-KLLTJWrvwIP+OPfMn0x2PheDEP20RPUcGXj/ERegTgdmPEZylALQldygiqrPPu8P45uNuPs7ckmReLY6v/iA5g== - -cluster-key-slot@^1.1.0: - version "1.1.2" - resolved "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz" - integrity sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA== - -co@^4.6.0: - version "4.6.0" - resolved "https://registry.npmjs.org/co/-/co-4.6.0.tgz" - integrity sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ== - -collect-v8-coverage@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz" - integrity sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg== - -color-convert@^1.9.0: - version "1.9.3" - resolved "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz" - integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== - dependencies: - color-name "1.1.3" - -color-convert@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz" - integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== - dependencies: - color-name "~1.1.4" - -color-name@1.1.3: - version "1.1.3" - resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz" - integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== - -color-name@~1.1.4: - version "1.1.4" - resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" - integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== - -color-support@^1.1.2: - version "1.1.3" - resolved "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz" - integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== - -combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6: - version "1.0.8" - resolved "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz" - integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== - dependencies: - delayed-stream "~1.0.0" - -component-type@^1.2.1: - version "1.2.1" - resolved "https://registry.npmjs.org/component-type/-/component-type-1.2.1.tgz" - integrity sha512-Kgy+2+Uwr75vAi6ChWXgHuLvd+QLD7ssgpaRq2zCvt80ptvAfMc/hijcJxXkBa2wMlEZcJvC2H8Ubo+A9ATHIg== - -concat-map@0.0.1: - version "0.0.1" - resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" - integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== - -console-control-strings@^1.0.0, console-control-strings@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz" - integrity sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ== - -content-disposition@^0.5.3, content-disposition@~0.5.2: - version "0.5.4" - resolved "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz" - integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== - dependencies: - safe-buffer "5.2.1" - -content-type@^1.0.4: - version "1.0.5" - resolved "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz" - integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== - -convert-source-map@^1.6.0, convert-source-map@^1.7.0: - version "1.9.0" - resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz" - integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== - -convert-source-map@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz" - integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== - -cookies@~0.8.0: - version "0.8.0" - resolved "https://registry.npmjs.org/cookies/-/cookies-0.8.0.tgz" - integrity sha512-8aPsApQfebXnuI+537McwYsDtjVxGm8gTIzQI3FDW6t5t/DAhERxtnbEPN/8RX+uZthoz4eCOgloXaE5cYyNow== - dependencies: - depd "~2.0.0" - keygrip "~1.1.0" - -core-util-is@1.0.2, core-util-is@~1.0.0: - version "1.0.2" - resolved "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz" - integrity sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ== - -correlation-id@4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/correlation-id/-/correlation-id-4.0.0.tgz" - integrity sha512-WvXtJBlovvOBKqTz/YwWP2gm6CXJZJArfGimp9s/ehmhJMPFbmnPMQe3K60Q9idGNixMvKojMjleyDhZEFdHfg== - dependencies: - uuid "^8.3.1" - -cpu-features@~0.0.8: - version "0.0.8" - resolved "https://registry.npmjs.org/cpu-features/-/cpu-features-0.0.8.tgz" - integrity sha512-BbHBvtYhUhksqTjr6bhNOjGgMnhwhGTQmOoZGD+K7BCaQDCuZl/Ve1ZxUSMRwVC4D/rkCPQ2MAIeYzrWyK7eEg== - dependencies: - buildcheck "~0.0.6" - nan "^2.17.0" - -create-jest@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/create-jest/-/create-jest-29.7.0.tgz#a355c5b3cb1e1af02ba177fe7afd7feee49a5320" - integrity sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q== - dependencies: - "@jest/types" "^29.6.3" - chalk "^4.0.0" - exit "^0.1.2" - graceful-fs "^4.2.9" - jest-config "^29.7.0" - jest-util "^29.7.0" - prompts "^2.0.1" - -create-require@^1.1.0: - version "1.1.1" - resolved "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz" - integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== - -cron-parser@^4.2.1: - version "4.8.1" - resolved "https://registry.npmjs.org/cron-parser/-/cron-parser-4.8.1.tgz" - integrity sha512-jbokKWGcyU4gl6jAfX97E1gDpY12DJ1cLJZmoDzaAln/shZ+S3KBFBuA2Q6WeUN4gJf/8klnV1EfvhA2lK5IRQ== - dependencies: - luxon "^3.2.1" - -cross-spawn@^7.0.3: - version "7.0.3" - resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz" - integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== - dependencies: - path-key "^3.1.0" - shebang-command "^2.0.0" - which "^2.0.1" - -crypt@0.0.2: - version "0.0.2" - resolved "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz" - integrity sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow== - -cwd@^0.10.0: - version "0.10.0" - resolved "https://registry.npmjs.org/cwd/-/cwd-0.10.0.tgz" - integrity sha512-YGZxdTTL9lmLkCUTpg4j0zQ7IhRB5ZmqNBbGCl3Tg6MP/d5/6sY7L5mmTjzbc6JKgVZYiqTQTNhPFsbXNGlRaA== - dependencies: - find-pkg "^0.1.2" - fs-exists-sync "^0.1.0" - -dashdash@^1.12.0: - version "1.14.1" - resolved "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz" - integrity sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g== - dependencies: - assert-plus "^1.0.0" - -debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2: - version "4.3.4" - resolved "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz" - integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== - dependencies: - ms "2.1.2" - -debug@4.3.2: - version "4.3.2" - resolved "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz" - integrity sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw== - dependencies: - ms "2.1.2" - -debuglog@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/debuglog/-/debuglog-1.0.1.tgz" - integrity sha512-syBZ+rnAK3EgMsH2aYEOLUW7mZSY9Gb+0wUMCFsZvcmiz+HigA0LOcq/HoQqVuGG+EKykunc7QG2bzrponfaSw== - -dedent@^1.0.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/dedent/-/dedent-1.3.0.tgz#15d6809eb15b581d5587a2dc208f34118e35bee3" - integrity sha512-7glNLfvdsMzZm3FpRY1CHuI2lbYDR+71YmrhmTZjYFD5pfT0ACgnGRdrrC9Mk2uICnzkcdelCx5at787UDGOvg== - -deep-equal@~1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz" - integrity sha512-bHtC0iYvWhyaTzvV3CZgPeZQqCOBGyGsVV7v4eevpdkLHfiSrXUdBG+qAuSz4RI70sszvjQ1QSZ98An1yNwpSw== - -deepmerge@^4.2.2: - version "4.3.1" - resolved "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz" - integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== - -deferred-leveldown@~5.3.0: - version "5.3.0" - resolved "https://registry.npmjs.org/deferred-leveldown/-/deferred-leveldown-5.3.0.tgz" - integrity sha512-a59VOT+oDy7vtAbLRCZwWgxu2BaCfd5Hk7wxJd48ei7I+nsg8Orlb9CLG0PMZienk9BSUKgeAqkO2+Lw+1+Ukw== - dependencies: - abstract-leveldown "~6.2.1" - inherits "^2.0.3" - -delayed-stream@~1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz" - integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== - -delegates@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz" - integrity sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ== - -denque@^1.1.0: - version "1.5.1" - resolved "https://registry.npmjs.org/denque/-/denque-1.5.1.tgz" - integrity sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw== - -depd@^1.1.0, depd@~1.1.2: - version "1.1.2" - resolved "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz" - integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ== - -depd@^2.0.0, depd@~2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz" - integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== - -destroy@^1.0.4: - version "1.2.0" - resolved "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz" - integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== - -detect-libc@^2.0.0: - version "2.0.1" - resolved "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz" - integrity sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w== - -detect-newline@^3.0.0: - version "3.1.0" - resolved "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz" - integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== - -diff-sequences@^29.6.3: - version "29.6.3" - resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.6.3.tgz#4deaf894d11407c51efc8418012f9e70b84ea921" - integrity sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q== - -diff@^4.0.1: - version "4.0.2" - resolved "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz" - integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== - -docker-compose@^0.23.5: - version "0.23.19" - resolved "https://registry.npmjs.org/docker-compose/-/docker-compose-0.23.19.tgz" - integrity sha512-v5vNLIdUqwj4my80wxFDkNH+4S85zsRuH29SO7dCWVWPCMt/ohZBsGN6g6KXWifT0pzQ7uOxqEKCYCDPJ8Vz4g== - dependencies: - yaml "^1.10.2" - -docker-modem@^3.0.0: - version "3.0.8" - resolved "https://registry.npmjs.org/docker-modem/-/docker-modem-3.0.8.tgz" - integrity sha512-f0ReSURdM3pcKPNS30mxOHSbaFLcknGmQjwSfmbcdOw1XWKXVhukM3NJHhr7NpY9BIyyWQb0EBo3KQvvuU5egQ== - dependencies: - debug "^4.1.1" - readable-stream "^3.5.0" - split-ca "^1.0.1" - ssh2 "^1.11.0" - -dockerode@^3.2.1: - version "3.3.5" - resolved "https://registry.npmjs.org/dockerode/-/dockerode-3.3.5.tgz" - integrity sha512-/0YNa3ZDNeLr/tSckmD69+Gq+qVNhvKfAHNeZJBnp7EOP6RGKV8ORrJHkUn20So5wU+xxT7+1n5u8PjHbfjbSA== - dependencies: - "@balena/dockerignore" "^1.0.2" - docker-modem "^3.0.0" - tar-fs "~2.0.1" - -dotenv@16.0.1: - version "16.0.1" - resolved "https://registry.npmjs.org/dotenv/-/dotenv-16.0.1.tgz" - integrity sha512-1K6hR6wtk2FviQ4kEiSjFiH5rpzEVi8WW0x96aztHVMhEspNpc4DVOUTEHtEva5VThQ8IaBX1Pe4gSzpVVUsKQ== - -double-ended-queue@2.1.0-0: - version "2.1.0-0" - resolved "https://registry.npmjs.org/double-ended-queue/-/double-ended-queue-2.1.0-0.tgz" - integrity sha512-+BNfZ+deCo8hMNpDqDnvT+c0XpJ5cUa6mqYq89bho2Ifze4URTqRkcwR399hWoTrTkbZ/XJYDgP6rc7pRgffEQ== - -duplexer@~0.1.1: - version "0.1.2" - resolved "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz" - integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg== - -ecc-jsbn@~0.1.1: - version "0.1.2" - resolved "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz" - integrity sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw== - dependencies: - jsbn "~0.1.0" - safer-buffer "^2.1.0" - -ecdsa-sig-formatter@1.0.11: - version "1.0.11" - resolved "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz" - integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ== - dependencies: - safe-buffer "^5.0.1" - -ee-first@1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz" - integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== - -electron-to-chromium@^1.4.284: - version "1.4.353" - resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.353.tgz" - integrity sha512-IdJVpMHJoBT/nn0GQ02wPfbhogDVpd1ud95lP//FTf5l35wzxKJwibB4HBdY7Q+xKPA1nkZ0UDLOMyRj5U5IAQ== - -emitter-listener@1.1.2: - version "1.1.2" - resolved "https://registry.npmjs.org/emitter-listener/-/emitter-listener-1.1.2.tgz" - integrity sha512-Bt1sBAGFHY9DKY+4/2cV6izcKJUf5T7/gkdmkxzX/qv9CcGH8xSwVRW5mtX03SWJtRTWSOpzCuWN9rBFYZepZQ== - dependencies: - shimmer "^1.2.0" - -emittery@^0.13.1: - version "0.13.1" - resolved "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz" - integrity sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ== - -emoji-regex@^8.0.0: - version "8.0.0" - resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz" - integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== - -encodeurl@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz" - integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== - -encoding-down@^6.3.0: - version "6.3.0" - resolved "https://registry.npmjs.org/encoding-down/-/encoding-down-6.3.0.tgz" - integrity sha512-QKrV0iKR6MZVJV08QY0wp1e7vF6QbhnbQhb07bwpEyuz4uZiZgPlEGdkCROuFkUwdxlFaiPIhjyarH1ee/3vhw== - dependencies: - abstract-leveldown "^6.2.1" - inherits "^2.0.3" - level-codec "^9.0.0" - level-errors "^2.0.0" - -end-of-stream@^1.1.0, end-of-stream@^1.4.1: - version "1.4.4" - resolved "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz" - integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== - dependencies: - once "^1.4.0" - -end-stream@~0.1.0: - version "0.1.0" - resolved "https://registry.npmjs.org/end-stream/-/end-stream-0.1.0.tgz" - integrity sha512-Brl10T8kYnc75IepKizW6Y9liyW8ikz1B7n/xoHrJxoVSSjoqPn30sb7XVFfQERK4QfUMYRGs9dhWwtt2eu6uA== - dependencies: - write-stream "~0.4.3" - -errno@~0.1.1: - version "0.1.8" - resolved "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz" - integrity sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A== - dependencies: - prr "~1.0.1" - -error-ex@^1.3.1: - version "1.3.2" - resolved "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz" - integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== - dependencies: - is-arrayish "^0.2.1" - -escalade@^3.1.1: - version "3.1.1" - resolved "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz" - integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== - -escape-html@^1.0.3: - version "1.0.3" - resolved "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz" - integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== - -escape-string-regexp@^1.0.5: - version "1.0.5" - resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" - integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== - -escape-string-regexp@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz" - integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== - -esprima@^4.0.0: - version "4.0.1" - resolved "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz" - integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== - -event-stream@=3.3.4: - version "3.3.4" - resolved "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz" - integrity sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g== - dependencies: - duplexer "~0.1.1" - from "~0" - map-stream "~0.1.0" - pause-stream "0.0.11" - split "0.3" - stream-combiner "~0.0.4" - through "~2.3.1" - -event-target-shim@^5.0.0: - version "5.0.1" - resolved "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz" - integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== - -events@1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/events/-/events-1.1.1.tgz" - integrity sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw== - -execa@5.1.1, execa@^5.0.0: - version "5.1.1" - resolved "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz" - integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== - dependencies: - cross-spawn "^7.0.3" - get-stream "^6.0.0" - human-signals "^2.1.0" - is-stream "^2.0.0" - merge-stream "^2.0.0" - npm-run-path "^4.0.1" - onetime "^5.1.2" - signal-exit "^3.0.3" - strip-final-newline "^2.0.0" - -exit@^0.1.2: - version "0.1.2" - resolved "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz" - integrity sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ== - -expand-tilde@^1.2.2: - version "1.2.2" - resolved "https://registry.npmjs.org/expand-tilde/-/expand-tilde-1.2.2.tgz" - integrity sha512-rtmc+cjLZqnu9dSYosX9EWmSJhTwpACgJQTfj4hgg2JjOD/6SIQalZrt4a3aQeh++oNxkazcaxrhPUj6+g5G/Q== - dependencies: - os-homedir "^1.0.1" - -expect@^29.0.0, expect@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/expect/-/expect-29.7.0.tgz#578874590dcb3214514084c08115d8aee61e11bc" - integrity sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw== - dependencies: - "@jest/expect-utils" "^29.7.0" - jest-get-type "^29.6.3" - jest-matcher-utils "^29.7.0" - jest-message-util "^29.7.0" - jest-util "^29.7.0" - -extend@~3.0.2: - version "3.0.2" - resolved "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz" - integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== - -extsprintf@1.3.0, extsprintf@^1.2.0: - version "1.3.0" - resolved "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz" - integrity sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g== - -fast-deep-equal@^3.1.1: - version "3.1.3" - resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz" - integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== - -fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0, fast-json-stable-stringify@^2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz" - integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== - -fb-watchman@^2.0.0: - version "2.0.2" - resolved "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz" - integrity sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA== - dependencies: - bser "2.1.1" - -fetch-cookie@0.10.1: - version "0.10.1" - resolved "https://registry.npmjs.org/fetch-cookie/-/fetch-cookie-0.10.1.tgz" - integrity sha512-beB+VEd4cNeVG1PY+ee74+PkuCQnik78pgLi5Ah/7qdUfov8IctU0vLUbBT8/10Ma5GMBeI4wtxhGrEfKNYs2g== - dependencies: - tough-cookie "^2.3.3 || ^3.0.1 || ^4.0.0" - -fetch-cookie@0.11.0: - version "0.11.0" - resolved "https://registry.npmjs.org/fetch-cookie/-/fetch-cookie-0.11.0.tgz" - integrity sha512-BQm7iZLFhMWFy5CZ/162sAGjBfdNWb7a8LEqqnzsHFhxT/X/SVj/z2t2nu3aJvjlbQkrAlTUApplPRjWyH4mhA== - dependencies: - tough-cookie "^2.3.3 || ^3.0.1 || ^4.0.0" - -fill-range@^7.0.1: - version "7.0.1" - resolved "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz" - integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== - dependencies: - to-regex-range "^5.0.1" - -find-file-up@^0.1.2: - version "0.1.3" - resolved "https://registry.npmjs.org/find-file-up/-/find-file-up-0.1.3.tgz" - integrity sha512-mBxmNbVyjg1LQIIpgO8hN+ybWBgDQK8qjht+EbrTCGmmPV/sc7RF1i9stPTD6bpvXZywBdrwRYxhSdJv867L6A== - dependencies: - fs-exists-sync "^0.1.0" - resolve-dir "^0.1.0" - -find-pkg@^0.1.2: - version "0.1.2" - resolved "https://registry.npmjs.org/find-pkg/-/find-pkg-0.1.2.tgz" - integrity sha512-0rnQWcFwZr7eO0513HahrWafsc3CTFioEB7DRiEYCUM/70QXSY8f3mCST17HXLcPvEhzH/Ty/Bxd72ZZsr/yvw== - dependencies: - find-file-up "^0.1.2" - -find-up@^4.0.0, find-up@^4.1.0: - version "4.1.0" - resolved "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz" - integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== - dependencies: - locate-path "^5.0.0" - path-exists "^4.0.0" - -follow-redirects@^1.14.0, follow-redirects@^1.14.4, follow-redirects@^1.15.0: - version "1.15.6" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b" - integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA== - -forever-agent@~0.6.1: - version "0.6.1" - resolved "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz" - integrity sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw== - -form-data@^3.0.0: - version "3.0.1" - resolved "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz" - integrity sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.8" - mime-types "^2.1.12" - -form-data@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz" - integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.8" - mime-types "^2.1.12" - -form-data@~2.3.2: - version "2.3.3" - resolved "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz" - integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.6" - mime-types "^2.1.12" - -fresh@^0.5.2, fresh@~0.5.2: - version "0.5.2" - resolved "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz" - integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== - -from@~0: - version "0.1.7" - resolved "https://registry.npmjs.org/from/-/from-0.1.7.tgz" - integrity sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g== - -fs-constants@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz" - integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== - -fs-exists-sync@^0.1.0: - version "0.1.0" - resolved "https://registry.npmjs.org/fs-exists-sync/-/fs-exists-sync-0.1.0.tgz" - integrity sha512-cR/vflFyPZtrN6b38ZyWxpWdhlXrzZEBawlpBQMq7033xVY7/kg0GDMBK5jg8lDYQckdJ5x/YC88lM3C7VMsLg== - -fs-minipass@^2.0.0: - version "2.1.0" - resolved "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz" - integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== - dependencies: - minipass "^3.0.0" - -fs.realpath@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" - integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== - -fsevents@^2.3.2: - version "2.3.2" - resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz" - integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== - -function-bind@^1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz" - integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== - -gauge@^3.0.0: - version "3.0.2" - resolved "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz" - integrity sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q== - dependencies: - aproba "^1.0.3 || ^2.0.0" - color-support "^1.1.2" - console-control-strings "^1.0.0" - has-unicode "^2.0.1" - object-assign "^4.1.1" - signal-exit "^3.0.0" - string-width "^4.2.3" - strip-ansi "^6.0.1" - wide-align "^1.1.2" - -gensync@^1.0.0-beta.2: - version "1.0.0-beta.2" - resolved "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz" - integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== - -get-caller-file@^2.0.5: - version "2.0.5" - resolved "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz" - integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== - -get-intrinsic@^1.0.2: - version "1.2.0" - resolved "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz" - integrity sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q== - dependencies: - function-bind "^1.1.1" - has "^1.0.3" - has-symbols "^1.0.3" - -get-package-type@^0.1.0: - version "0.1.0" - resolved "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz" - integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== - -get-port@^5.1.1: - version "5.1.1" - resolved "https://registry.npmjs.org/get-port/-/get-port-5.1.1.tgz" - integrity sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ== - -get-stream@^6.0.0: - version "6.0.1" - resolved "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz" - integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== - -getpass@^0.1.1: - version "0.1.7" - resolved "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz" - integrity sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng== - dependencies: - assert-plus "^1.0.0" - -glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: - version "7.2.3" - resolved "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz" - integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.1.1" - once "^1.3.0" - path-is-absolute "^1.0.0" - -global-modules@^0.2.3: - version "0.2.3" - resolved "https://registry.npmjs.org/global-modules/-/global-modules-0.2.3.tgz" - integrity sha512-JeXuCbvYzYXcwE6acL9V2bAOeSIGl4dD+iwLY9iUx2VBJJ80R18HCn+JCwHM9Oegdfya3lEkGCdaRkSyc10hDA== - dependencies: - global-prefix "^0.1.4" - is-windows "^0.2.0" - -global-prefix@^0.1.4: - version "0.1.5" - resolved "https://registry.npmjs.org/global-prefix/-/global-prefix-0.1.5.tgz" - integrity sha512-gOPiyxcD9dJGCEArAhF4Hd0BAqvAe/JzERP7tYumE4yIkmIedPUVXcJFWbV3/p/ovIIvKjkrTk+f1UVkq7vvbw== - dependencies: - homedir-polyfill "^1.0.0" - ini "^1.3.4" - is-windows "^0.2.0" - which "^1.2.12" - -globals@^11.1.0: - version "11.12.0" - resolved "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz" - integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== - -graceful-fs@^4.2.9: - version "4.2.10" - resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz" - integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== - -har-schema@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz" - integrity sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q== - -har-validator@~5.1.3: - version "5.1.5" - resolved "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz" - integrity sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w== - dependencies: - ajv "^6.12.3" - har-schema "^2.0.0" - -has-flag@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz" - integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== - -has-flag@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz" - integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== - -has-symbols@^1.0.2, has-symbols@^1.0.3: - version "1.0.3" - resolved "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz" - integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== - -has-tostringtag@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz" - integrity sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ== - dependencies: - has-symbols "^1.0.2" - -has-unicode@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz" - integrity sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ== - -has@^1.0.3: - version "1.0.3" - resolved "https://registry.npmjs.org/has/-/has-1.0.3.tgz" - integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== - dependencies: - function-bind "^1.1.1" - -homedir-polyfill@^1.0.0: - version "1.0.3" - resolved "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz" - integrity sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA== - dependencies: - parse-passwd "^1.0.0" - -html-escaper@^2.0.0: - version "2.0.2" - resolved "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz" - integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== - -http-assert@^1.3.0: - version "1.5.0" - resolved "https://registry.npmjs.org/http-assert/-/http-assert-1.5.0.tgz" - integrity sha512-uPpH7OKX4H25hBmU6G1jWNaqJGpTXxey+YOUizJUAgu0AjLUeC8D73hTrhvDS5D+GJN1DN1+hhc/eF/wpxtp0w== - dependencies: - deep-equal "~1.0.1" - http-errors "~1.8.0" - -http-cookie-agent@^4.0.2: - version "4.0.2" - resolved "https://registry.npmjs.org/http-cookie-agent/-/http-cookie-agent-4.0.2.tgz" - integrity sha512-noTmxdH5CuytTnLj/Qv3Z84e/YFq8yLXAw3pqIYZ25Edhb9pQErIAC+ednw40Cic6Le/h9ryph5/TqsvkOaUCw== - dependencies: - agent-base "^6.0.2" - -http-errors@^1.6.3, http-errors@~1.8.0: - version "1.8.1" - resolved "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz" - integrity sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g== - dependencies: - depd "~1.1.2" - inherits "2.0.4" - setprototypeof "1.2.0" - statuses ">= 1.5.0 < 2" - toidentifier "1.0.1" - -http-signature@~1.2.0: - version "1.2.0" - resolved "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz" - integrity sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ== - dependencies: - assert-plus "^1.0.0" - jsprim "^1.2.2" - sshpk "^1.7.0" - -https-proxy-agent@^5.0.0: - version "5.0.1" - resolved "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz" - integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== - dependencies: - agent-base "6" - debug "4" - -human-signals@^2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz" - integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== - -ieee754@1.1.13, ieee754@^1.1.13, ieee754@^1.1.4: - version "1.1.13" - resolved "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz" - integrity sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg== - -immediate@3.3.0, immediate@^3.2.3: - version "3.3.0" - resolved "https://registry.npmjs.org/immediate/-/immediate-3.3.0.tgz" - integrity sha512-HR7EVodfFUdQCTIeySw+WDRFJlPcLOJbXfwwZ7Oom6tjsvZ3bOkCDJHehQC3nxJrv7+f9XecwazynjU8e4Vw3Q== - -immediate@~3.0.5: - version "3.0.6" - resolved "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz" - integrity sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ== - -import-local@^3.0.2: - version "3.1.0" - resolved "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz" - integrity sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg== - dependencies: - pkg-dir "^4.2.0" - resolve-cwd "^3.0.0" - -imurmurhash@^0.1.4: - version "0.1.4" - resolved "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz" - integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== - -inflight@^1.0.4: - version "1.0.6" - resolved "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz" - integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== - dependencies: - once "^1.3.0" - wrappy "1" - -inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3: - version "2.0.4" - resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" - integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== - -ini@^1.3.4: - version "1.3.8" - resolved "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz" - integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== - -ioredis@4.28.0: - version "4.28.0" - resolved "https://registry.npmjs.org/ioredis/-/ioredis-4.28.0.tgz" - integrity sha512-I+zkeeWp3XFgPT2CtJKxvaF5FjGBGt4yGYljRjQecdQKteThuAsKqffeF1lgHVlYnuNeozRbPOCDNZ7tDWPeig== - dependencies: - cluster-key-slot "^1.1.0" - debug "^4.3.1" - denque "^1.1.0" - lodash.defaults "^4.2.0" - lodash.flatten "^4.4.0" - lodash.isarguments "^3.1.0" - p-map "^2.1.0" - redis-commands "1.7.0" - redis-errors "^1.2.0" - redis-parser "^3.0.0" - standard-as-callback "^2.1.0" - -ioredis@^4.28.5: - version "4.28.5" - resolved "https://registry.npmjs.org/ioredis/-/ioredis-4.28.5.tgz" - integrity sha512-3GYo0GJtLqgNXj4YhrisLaNNvWSNwSS2wS4OELGfGxH8I69+XfNdnmV1AyN+ZqMh0i7eX+SWjrwFKDBDgfBC1A== - dependencies: - cluster-key-slot "^1.1.0" - debug "^4.3.1" - denque "^1.1.0" - lodash.defaults "^4.2.0" - lodash.flatten "^4.4.0" - lodash.isarguments "^3.1.0" - p-map "^2.1.0" - redis-commands "1.7.0" - redis-errors "^1.2.0" - redis-parser "^3.0.0" - standard-as-callback "^2.1.0" - -is-arrayish@^0.2.1: - version "0.2.1" - resolved "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz" - integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== - -is-buffer@~1.1.6: - version "1.1.6" - resolved "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz" - integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== - -is-core-module@^2.11.0: - version "2.11.0" - resolved "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz" - integrity sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw== - dependencies: - has "^1.0.3" - -is-fullwidth-code-point@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz" - integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== - -is-generator-fn@^2.0.0: - version "2.1.0" - resolved "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz" - integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== - -is-generator-function@^1.0.7: - version "1.0.10" - resolved "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz" - integrity sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A== - dependencies: - has-tostringtag "^1.0.0" - -is-number@^7.0.0: - version "7.0.0" - resolved "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz" - integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== - -is-retry-allowed@^2.2.0: - version "2.2.0" - resolved "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-2.2.0.tgz" - integrity sha512-XVm7LOeLpTW4jV19QSH38vkswxoLud8sQ57YwJVTPWdiaI9I8keEhGFpBlslyVsgdQy4Opg8QOLb8YRgsyZiQg== - -is-stream@^2.0.0: - version "2.0.1" - resolved "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz" - integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== - -is-typedarray@~1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz" - integrity sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA== - -is-windows@^0.2.0: - version "0.2.0" - resolved "https://registry.npmjs.org/is-windows/-/is-windows-0.2.0.tgz" - integrity sha512-n67eJYmXbniZB7RF4I/FTjK1s6RPOCTxhYrVYLRaCt3lF0mpWZPKr3T2LSZAqyjQsxR2qMmGYXXzK0YWwcPM1Q== - -isarray@0.0.1: - version "0.0.1" - resolved "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" - integrity sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ== - -isarray@^1.0.0, isarray@~1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" - integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== - -isexe@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz" - integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== - -isstream@~0.1.2: - version "0.1.2" - resolved "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz" - integrity sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g== - -istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: - version "3.2.0" - resolved "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz" - integrity sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw== - -istanbul-lib-instrument@^5.0.4: - version "5.2.1" - resolved "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz" - integrity sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg== - dependencies: - "@babel/core" "^7.12.3" - "@babel/parser" "^7.14.7" - "@istanbuljs/schema" "^0.1.2" - istanbul-lib-coverage "^3.2.0" - semver "^6.3.0" - -istanbul-lib-instrument@^6.0.0: - version "6.0.1" - resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.1.tgz#71e87707e8041428732518c6fb5211761753fbdf" - integrity sha512-EAMEJBsYuyyztxMxW3g7ugGPkrZsV57v0Hmv3mm1uQsmB+QnZuepg731CRaIgeUVSdmsTngOkSnauNF8p7FIhA== - dependencies: - "@babel/core" "^7.12.3" - "@babel/parser" "^7.14.7" - "@istanbuljs/schema" "^0.1.2" - istanbul-lib-coverage "^3.2.0" - semver "^7.5.4" - -istanbul-lib-report@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz" - integrity sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw== - dependencies: - istanbul-lib-coverage "^3.0.0" - make-dir "^3.0.0" - supports-color "^7.1.0" - -istanbul-lib-source-maps@^4.0.0: - version "4.0.1" - resolved "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz" - integrity sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw== - dependencies: - debug "^4.1.1" - istanbul-lib-coverage "^3.0.0" - source-map "^0.6.1" - -istanbul-reports@^3.1.3: - version "3.1.5" - resolved "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz" - integrity sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w== - dependencies: - html-escaper "^2.0.0" - istanbul-lib-report "^3.0.0" - -jest-changed-files@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-29.7.0.tgz#1c06d07e77c78e1585d020424dedc10d6e17ac3a" - integrity sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w== - dependencies: - execa "^5.0.0" - jest-util "^29.7.0" - p-limit "^3.1.0" - -jest-circus@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-29.7.0.tgz#b6817a45fcc835d8b16d5962d0c026473ee3668a" - integrity sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw== - dependencies: - "@jest/environment" "^29.7.0" - "@jest/expect" "^29.7.0" - "@jest/test-result" "^29.7.0" - "@jest/types" "^29.6.3" - "@types/node" "*" - chalk "^4.0.0" - co "^4.6.0" - dedent "^1.0.0" - is-generator-fn "^2.0.0" - jest-each "^29.7.0" - jest-matcher-utils "^29.7.0" - jest-message-util "^29.7.0" - jest-runtime "^29.7.0" - jest-snapshot "^29.7.0" - jest-util "^29.7.0" - p-limit "^3.1.0" - pretty-format "^29.7.0" - pure-rand "^6.0.0" - slash "^3.0.0" - stack-utils "^2.0.3" - -jest-cli@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-29.7.0.tgz#5592c940798e0cae677eec169264f2d839a37995" - integrity sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg== - dependencies: - "@jest/core" "^29.7.0" - "@jest/test-result" "^29.7.0" - "@jest/types" "^29.6.3" - chalk "^4.0.0" - create-jest "^29.7.0" - exit "^0.1.2" - import-local "^3.0.2" - jest-config "^29.7.0" - jest-util "^29.7.0" - jest-validate "^29.7.0" - yargs "^17.3.1" - -jest-config@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-29.7.0.tgz#bcbda8806dbcc01b1e316a46bb74085a84b0245f" - integrity sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ== - dependencies: - "@babel/core" "^7.11.6" - "@jest/test-sequencer" "^29.7.0" - "@jest/types" "^29.6.3" - babel-jest "^29.7.0" - chalk "^4.0.0" - ci-info "^3.2.0" - deepmerge "^4.2.2" - glob "^7.1.3" - graceful-fs "^4.2.9" - jest-circus "^29.7.0" - jest-environment-node "^29.7.0" - jest-get-type "^29.6.3" - jest-regex-util "^29.6.3" - jest-resolve "^29.7.0" - jest-runner "^29.7.0" - jest-util "^29.7.0" - jest-validate "^29.7.0" - micromatch "^4.0.4" - parse-json "^5.2.0" - pretty-format "^29.7.0" - slash "^3.0.0" - strip-json-comments "^3.1.1" - -jest-diff@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.7.0.tgz#017934a66ebb7ecf6f205e84699be10afd70458a" - integrity sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw== - dependencies: - chalk "^4.0.0" - diff-sequences "^29.6.3" - jest-get-type "^29.6.3" - pretty-format "^29.7.0" - -jest-docblock@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-29.7.0.tgz#8fddb6adc3cdc955c93e2a87f61cfd350d5d119a" - integrity sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g== - dependencies: - detect-newline "^3.0.0" - -jest-each@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-29.7.0.tgz#162a9b3f2328bdd991beaabffbb74745e56577d1" - integrity sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ== - dependencies: - "@jest/types" "^29.6.3" - chalk "^4.0.0" - jest-get-type "^29.6.3" - jest-util "^29.7.0" - pretty-format "^29.7.0" - -jest-environment-node@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-29.7.0.tgz#0b93e111dda8ec120bc8300e6d1fb9576e164376" - integrity sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw== - dependencies: - "@jest/environment" "^29.7.0" - "@jest/fake-timers" "^29.7.0" - "@jest/types" "^29.6.3" - "@types/node" "*" - jest-mock "^29.7.0" - jest-util "^29.7.0" - -jest-get-type@^29.6.3: - version "29.6.3" - resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.6.3.tgz#36f499fdcea197c1045a127319c0481723908fd1" - integrity sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw== - -jest-haste-map@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-29.7.0.tgz#3c2396524482f5a0506376e6c858c3bbcc17b104" - integrity sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA== - dependencies: - "@jest/types" "^29.6.3" - "@types/graceful-fs" "^4.1.3" - "@types/node" "*" - anymatch "^3.0.3" - fb-watchman "^2.0.0" - graceful-fs "^4.2.9" - jest-regex-util "^29.6.3" - jest-util "^29.7.0" - jest-worker "^29.7.0" - micromatch "^4.0.4" - walker "^1.0.8" - optionalDependencies: - fsevents "^2.3.2" - -jest-leak-detector@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz#5b7ec0dadfdfec0ca383dc9aa016d36b5ea4c728" - integrity sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw== - dependencies: - jest-get-type "^29.6.3" - pretty-format "^29.7.0" - -jest-matcher-utils@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz#ae8fec79ff249fd592ce80e3ee474e83a6c44f12" - integrity sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g== - dependencies: - chalk "^4.0.0" - jest-diff "^29.7.0" - jest-get-type "^29.6.3" - pretty-format "^29.7.0" - -jest-message-util@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.7.0.tgz#8bc392e204e95dfe7564abbe72a404e28e51f7f3" - integrity sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w== - dependencies: - "@babel/code-frame" "^7.12.13" - "@jest/types" "^29.6.3" - "@types/stack-utils" "^2.0.0" - chalk "^4.0.0" - graceful-fs "^4.2.9" - micromatch "^4.0.4" - pretty-format "^29.7.0" - slash "^3.0.0" - stack-utils "^2.0.3" - -jest-mock@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-29.7.0.tgz#4e836cf60e99c6fcfabe9f99d017f3fdd50a6347" - integrity sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw== - dependencies: - "@jest/types" "^29.6.3" - "@types/node" "*" - jest-util "^29.7.0" - -jest-pnp-resolver@^1.2.2: - version "1.2.3" - resolved "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz" - integrity sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w== - -jest-regex-util@^29.6.3: - version "29.6.3" - resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-29.6.3.tgz#4a556d9c776af68e1c5f48194f4d0327d24e8a52" - integrity sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg== - -jest-resolve-dependencies@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz#1b04f2c095f37fc776ff40803dc92921b1e88428" - integrity sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA== - dependencies: - jest-regex-util "^29.6.3" - jest-snapshot "^29.7.0" - -jest-resolve@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-29.7.0.tgz#64d6a8992dd26f635ab0c01e5eef4399c6bcbc30" - integrity sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA== - dependencies: - chalk "^4.0.0" - graceful-fs "^4.2.9" - jest-haste-map "^29.7.0" - jest-pnp-resolver "^1.2.2" - jest-util "^29.7.0" - jest-validate "^29.7.0" - resolve "^1.20.0" - resolve.exports "^2.0.0" - slash "^3.0.0" - -jest-runner@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-29.7.0.tgz#809af072d408a53dcfd2e849a4c976d3132f718e" - integrity sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ== - dependencies: - "@jest/console" "^29.7.0" - "@jest/environment" "^29.7.0" - "@jest/test-result" "^29.7.0" - "@jest/transform" "^29.7.0" - "@jest/types" "^29.6.3" - "@types/node" "*" - chalk "^4.0.0" - emittery "^0.13.1" - graceful-fs "^4.2.9" - jest-docblock "^29.7.0" - jest-environment-node "^29.7.0" - jest-haste-map "^29.7.0" - jest-leak-detector "^29.7.0" - jest-message-util "^29.7.0" - jest-resolve "^29.7.0" - jest-runtime "^29.7.0" - jest-util "^29.7.0" - jest-watcher "^29.7.0" - jest-worker "^29.7.0" - p-limit "^3.1.0" - source-map-support "0.5.13" - -jest-runtime@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-29.7.0.tgz#efecb3141cf7d3767a3a0cc8f7c9990587d3d817" - integrity sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ== - dependencies: - "@jest/environment" "^29.7.0" - "@jest/fake-timers" "^29.7.0" - "@jest/globals" "^29.7.0" - "@jest/source-map" "^29.6.3" - "@jest/test-result" "^29.7.0" - "@jest/transform" "^29.7.0" - "@jest/types" "^29.6.3" - "@types/node" "*" - chalk "^4.0.0" - cjs-module-lexer "^1.0.0" - collect-v8-coverage "^1.0.0" - glob "^7.1.3" - graceful-fs "^4.2.9" - jest-haste-map "^29.7.0" - jest-message-util "^29.7.0" - jest-mock "^29.7.0" - jest-regex-util "^29.6.3" - jest-resolve "^29.7.0" - jest-snapshot "^29.7.0" - jest-util "^29.7.0" - slash "^3.0.0" - strip-bom "^4.0.0" - -jest-snapshot@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-29.7.0.tgz#c2c574c3f51865da1bb329036778a69bf88a6be5" - integrity sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw== - dependencies: - "@babel/core" "^7.11.6" - "@babel/generator" "^7.7.2" - "@babel/plugin-syntax-jsx" "^7.7.2" - "@babel/plugin-syntax-typescript" "^7.7.2" - "@babel/types" "^7.3.3" - "@jest/expect-utils" "^29.7.0" - "@jest/transform" "^29.7.0" - "@jest/types" "^29.6.3" - babel-preset-current-node-syntax "^1.0.0" - chalk "^4.0.0" - expect "^29.7.0" - graceful-fs "^4.2.9" - jest-diff "^29.7.0" - jest-get-type "^29.6.3" - jest-matcher-utils "^29.7.0" - jest-message-util "^29.7.0" - jest-util "^29.7.0" - natural-compare "^1.4.0" - pretty-format "^29.7.0" - semver "^7.5.3" - -jest-util@^29.0.0, jest-util@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.7.0.tgz#23c2b62bfb22be82b44de98055802ff3710fc0bc" - integrity sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA== - dependencies: - "@jest/types" "^29.6.3" - "@types/node" "*" - chalk "^4.0.0" - ci-info "^3.2.0" - graceful-fs "^4.2.9" - picomatch "^2.2.3" - -jest-validate@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-29.7.0.tgz#7bf705511c64da591d46b15fce41400d52147d9c" - integrity sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw== - dependencies: - "@jest/types" "^29.6.3" - camelcase "^6.2.0" - chalk "^4.0.0" - jest-get-type "^29.6.3" - leven "^3.1.0" - pretty-format "^29.7.0" - -jest-watcher@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-29.7.0.tgz#7810d30d619c3a62093223ce6bb359ca1b28a2f2" - integrity sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g== - dependencies: - "@jest/test-result" "^29.7.0" - "@jest/types" "^29.6.3" - "@types/node" "*" - ansi-escapes "^4.2.1" - chalk "^4.0.0" - emittery "^0.13.1" - jest-util "^29.7.0" - string-length "^4.0.1" - -jest-worker@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-29.7.0.tgz#acad073acbbaeb7262bd5389e1bcf43e10058d4a" - integrity sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw== - dependencies: - "@types/node" "*" - jest-util "^29.7.0" - merge-stream "^2.0.0" - supports-color "^8.0.0" - -jest@29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest/-/jest-29.7.0.tgz#994676fc24177f088f1c5e3737f5697204ff2613" - integrity sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw== - dependencies: - "@jest/core" "^29.7.0" - "@jest/types" "^29.6.3" - import-local "^3.0.2" - jest-cli "^29.7.0" - -jmespath@0.15.0: - version "0.15.0" - resolved "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz" - integrity sha512-+kHj8HXArPfpPEKGLZ+kB5ONRTCiGQXo8RQYL0hH8t6pWXUBBK5KkkQmTNOwKK4LEsd0yTsgtjJVm4UBSZea4w== - -joi@17.6.0, joi@^17.4.0: - version "17.6.0" - resolved "https://registry.npmjs.org/joi/-/joi-17.6.0.tgz" - integrity sha512-OX5dG6DTbcr/kbMFj0KGYxuew69HPcAE3K/sZpEV2nP6e/j/C0HV+HNiBPCASxdx5T7DMoa0s8UeHWMnb6n2zw== - dependencies: - "@hapi/hoek" "^9.0.0" - "@hapi/topo" "^5.0.0" - "@sideway/address" "^4.1.3" - "@sideway/formula" "^3.0.0" - "@sideway/pinpoint" "^2.0.0" - -join-component@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/join-component/-/join-component-1.1.0.tgz" - integrity sha512-bF7vcQxbODoGK1imE2P9GS9aw4zD0Sd+Hni68IMZLj7zRnquH7dXUmMw9hDI5S/Jzt7q+IyTXN0rSg2GI0IKhQ== - -js-tokens@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz" - integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== - -js-yaml@^3.13.1: - version "3.14.1" - resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz" - integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== - dependencies: - argparse "^1.0.7" - esprima "^4.0.0" - -jsbn@~0.1.0: - version "0.1.1" - resolved "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz" - integrity sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg== - -jsesc@^2.5.1: - version "2.5.2" - resolved "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz" - integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== - -json-parse-even-better-errors@^2.3.0: - version "2.3.1" - resolved "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz" - integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== - -json-schema-traverse@^0.4.1: - version "0.4.1" - resolved "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz" - integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== - -json-schema@0.4.0: - version "0.4.0" - resolved "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz" - integrity sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA== - -json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1: - version "5.0.1" - resolved "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz" - integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA== - -json5@^2.2.1, json5@^2.2.2, json5@^2.2.3: - version "2.2.3" - resolved "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz" - integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== - -jsonc-parser@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.2.0.tgz#31ff3f4c2b9793f89c67212627c51c6394f88e76" - integrity sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w== - -jsonwebtoken@9.0.0: - version "9.0.0" - resolved "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.0.tgz" - integrity sha512-tuGfYXxkQGDPnLJ7SibiQgVgeDgfbPq2k2ICcbgqW8WxWLBAxKQM/ZCu/IT8SOSwmaYl4dpTFCW5xZv7YbbWUw== - dependencies: - jws "^3.2.2" - lodash "^4.17.21" - ms "^2.1.1" - semver "^7.3.8" - -jsonwebtoken@^8.2.0: - version "8.5.1" - resolved "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz" - integrity sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w== - dependencies: - jws "^3.2.2" - lodash.includes "^4.3.0" - lodash.isboolean "^3.0.3" - lodash.isinteger "^4.0.4" - lodash.isnumber "^3.0.3" - lodash.isplainobject "^4.0.6" - lodash.isstring "^4.0.1" - lodash.once "^4.0.0" - ms "^2.1.1" - semver "^5.6.0" - -jsprim@^1.2.2: - version "1.4.2" - resolved "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz" - integrity sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw== - dependencies: - assert-plus "1.0.0" - extsprintf "1.3.0" - json-schema "0.4.0" - verror "1.10.0" - -jwa@^1.4.1: - version "1.4.1" - resolved "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz" - integrity sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA== - dependencies: - buffer-equal-constant-time "1.0.1" - ecdsa-sig-formatter "1.0.11" - safe-buffer "^5.0.1" - -jws@^3.2.2: - version "3.2.2" - resolved "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz" - integrity sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA== - dependencies: - jwa "^1.4.1" - safe-buffer "^5.0.1" - -keygrip@~1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/keygrip/-/keygrip-1.1.0.tgz" - integrity sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ== - dependencies: - tsscmp "1.0.6" - -kleur@^3.0.3: - version "3.0.3" - resolved "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz" - integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== - -koa-compose@^4.1.0: - version "4.1.0" - resolved "https://registry.npmjs.org/koa-compose/-/koa-compose-4.1.0.tgz" - integrity sha512-8ODW8TrDuMYvXRwra/Kh7/rJo9BtOfPc6qO8eAfC80CnCvSjSl0bkRM24X6/XBBEyj0v1nRUQ1LyOy3dbqOWXw== - -koa-convert@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/koa-convert/-/koa-convert-2.0.0.tgz" - integrity sha512-asOvN6bFlSnxewce2e/DK3p4tltyfC4VM7ZwuTuepI7dEQVcvpyFuBcEARu1+Hxg8DIwytce2n7jrZtRlPrARA== - dependencies: - co "^4.6.0" - koa-compose "^4.1.0" - -koa-passport@4.1.4: - version "4.1.4" - resolved "https://registry.npmjs.org/koa-passport/-/koa-passport-4.1.4.tgz" - integrity sha512-dJBCkl4X+zdYxbI2V2OtoGy0PUenpvp2ZLLWObc8UJhsId0iQpTFT8RVcuA0709AL2txGwRHnSPoT1bYNGa6Kg== - dependencies: - passport "^0.4.0" - -koa@^2.13.4: - version "2.14.1" - resolved "https://registry.npmjs.org/koa/-/koa-2.14.1.tgz" - integrity sha512-USJFyZgi2l0wDgqkfD27gL4YGno7TfUkcmOe6UOLFOVuN+J7FwnNu4Dydl4CUQzraM1lBAiGed0M9OVJoT0Kqw== - dependencies: - accepts "^1.3.5" - cache-content-type "^1.0.0" - content-disposition "~0.5.2" - content-type "^1.0.4" - cookies "~0.8.0" - debug "^4.3.2" - delegates "^1.0.0" - depd "^2.0.0" - destroy "^1.0.4" - encodeurl "^1.0.2" - escape-html "^1.0.3" - fresh "~0.5.2" - http-assert "^1.3.0" - http-errors "^1.6.3" - is-generator-function "^1.0.7" - koa-compose "^4.1.0" - koa-convert "^2.0.0" - on-finished "^2.3.0" - only "~0.0.2" - parseurl "^1.3.2" - statuses "^1.5.0" - type-is "^1.6.16" - vary "^1.1.2" - -lazy-ass@1.6.0: - version "1.6.0" - resolved "https://registry.npmjs.org/lazy-ass/-/lazy-ass-1.6.0.tgz" - integrity sha512-cc8oEVoctTvsFZ/Oje/kGnHbpWHYBe8IAJe4C0QNc3t8uM/0Y8+erSz/7Y1ALuXTEZTMvxXwO6YbX1ey3ujiZw== - -level-codec@9.0.2, level-codec@^9.0.0: - version "9.0.2" - resolved "https://registry.npmjs.org/level-codec/-/level-codec-9.0.2.tgz" - integrity sha512-UyIwNb1lJBChJnGfjmO0OR+ezh2iVu1Kas3nvBS/BzGnx79dv6g7unpKIDNPMhfdTEGoc7mC8uAu51XEtX+FHQ== - dependencies: - buffer "^5.6.0" - -level-concat-iterator@~2.0.0: - version "2.0.1" - resolved "https://registry.npmjs.org/level-concat-iterator/-/level-concat-iterator-2.0.1.tgz" - integrity sha512-OTKKOqeav2QWcERMJR7IS9CUo1sHnke2C0gkSmcR7QuEtFNLLzHQAvnMw8ykvEcv0Qtkg0p7FOwP1v9e5Smdcw== - -level-errors@^2.0.0, level-errors@~2.0.0: - version "2.0.1" - resolved "https://registry.npmjs.org/level-errors/-/level-errors-2.0.1.tgz" - integrity sha512-UVprBJXite4gPS+3VznfgDSU8PTRuVX0NXwoWW50KLxd2yw4Y1t2JUR5In1itQnudZqRMT9DlAM3Q//9NCjCFw== - dependencies: - errno "~0.1.1" - -level-iterator-stream@~4.0.0: - version "4.0.2" - resolved "https://registry.npmjs.org/level-iterator-stream/-/level-iterator-stream-4.0.2.tgz" - integrity sha512-ZSthfEqzGSOMWoUGhTXdX9jv26d32XJuHz/5YnuHZzH6wldfWMOVwI9TBtKcya4BKTyTt3XVA0A3cF3q5CY30Q== - dependencies: - inherits "^2.0.4" - readable-stream "^3.4.0" - xtend "^4.0.2" - -level-js@^5.0.0: - version "5.0.2" - resolved "https://registry.npmjs.org/level-js/-/level-js-5.0.2.tgz" - integrity sha512-SnBIDo2pdO5VXh02ZmtAyPP6/+6YTJg2ibLtl9C34pWvmtMEmRTWpra+qO/hifkUtBTOtfx6S9vLDjBsBK4gRg== - dependencies: - abstract-leveldown "~6.2.3" - buffer "^5.5.0" - inherits "^2.0.3" - ltgt "^2.1.2" - -level-packager@^5.1.0: - version "5.1.1" - resolved "https://registry.npmjs.org/level-packager/-/level-packager-5.1.1.tgz" - integrity sha512-HMwMaQPlTC1IlcwT3+swhqf/NUO+ZhXVz6TY1zZIIZlIR0YSn8GtAAWmIvKjNY16ZkEg/JcpAuQskxsXqC0yOQ== - dependencies: - encoding-down "^6.3.0" - levelup "^4.3.2" - -level-supports@~1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/level-supports/-/level-supports-1.0.1.tgz" - integrity sha512-rXM7GYnW8gsl1vedTJIbzOrRv85c/2uCMpiiCzO2fndd06U/kUXEEU9evYn4zFggBOg36IsBW8LzqIpETwwQzg== - dependencies: - xtend "^4.0.2" - -level-write-stream@1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/level-write-stream/-/level-write-stream-1.0.0.tgz" - integrity sha512-bBNKOEOMl8msO+uIM9YX/gUO6ckokZ/4pCwTm/lwvs46x6Xs8Zy0sn3Vh37eDqse4mhy4fOMIb/JsSM2nyQFtw== - dependencies: - end-stream "~0.1.0" - -level@6.0.1: - version "6.0.1" - resolved "https://registry.npmjs.org/level/-/level-6.0.1.tgz" - integrity sha512-psRSqJZCsC/irNhfHzrVZbmPYXDcEYhA5TVNwr+V92jF44rbf86hqGp8fiT702FyiArScYIlPSBTDUASCVNSpw== - dependencies: - level-js "^5.0.0" - level-packager "^5.1.0" - leveldown "^5.4.0" - -leveldown@5.6.0, leveldown@^5.4.0: - version "5.6.0" - resolved "https://registry.npmjs.org/leveldown/-/leveldown-5.6.0.tgz" - integrity sha512-iB8O/7Db9lPaITU1aA2txU/cBEXAt4vWwKQRrrWuS6XDgbP4QZGj9BL2aNbwb002atoQ/lIotJkfyzz+ygQnUQ== - dependencies: - abstract-leveldown "~6.2.1" - napi-macros "~2.0.0" - node-gyp-build "~4.1.0" - -levelup@4.4.0, levelup@^4.3.2: - version "4.4.0" - resolved "https://registry.npmjs.org/levelup/-/levelup-4.4.0.tgz" - integrity sha512-94++VFO3qN95cM/d6eBXvd894oJE0w3cInq9USsyQzzoJxmiYzPAocNcuGCPGGjoXqDVJcr3C1jzt1TSjyaiLQ== - dependencies: - deferred-leveldown "~5.3.0" - level-errors "~2.0.0" - level-iterator-stream "~4.0.0" - level-supports "~1.0.0" - xtend "~4.0.0" - -leven@^3.1.0: - version "3.1.0" - resolved "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz" - integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== - -lie@3.1.1: - version "3.1.1" - resolved "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz" - integrity sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw== - dependencies: - immediate "~3.0.5" - -lines-and-columns@^1.1.6: - version "1.2.4" - resolved "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz" - integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== - -locate-path@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz" - integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== - dependencies: - p-locate "^4.1.0" - -lodash.defaults@^4.2.0: - version "4.2.0" - resolved "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz" - integrity sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ== - -lodash.flatten@^4.4.0: - version "4.4.0" - resolved "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz" - integrity sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g== - -lodash.includes@^4.3.0: - version "4.3.0" - resolved "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz" - integrity sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w== - -lodash.isarguments@3.1.0, lodash.isarguments@^3.1.0: - version "3.1.0" - resolved "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz" - integrity sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg== - -lodash.isboolean@^3.0.3: - version "3.0.3" - resolved "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz" - integrity sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg== - -lodash.isinteger@^4.0.4: - version "4.0.4" - resolved "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz" - integrity sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA== - -lodash.isnumber@^3.0.3: - version "3.0.3" - resolved "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz" - integrity sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw== - -lodash.isplainobject@^4.0.6: - version "4.0.6" - resolved "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz" - integrity sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA== - -lodash.isstring@^4.0.1: - version "4.0.1" - resolved "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz" - integrity sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw== - -lodash.memoize@4.x: - version "4.1.2" - resolved "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz" - integrity sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag== - -lodash.once@^4.0.0: - version "4.1.1" - resolved "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz" - integrity sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg== - -lodash.pick@^4.0.0: - version "4.4.0" - resolved "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz" - integrity sha512-hXt6Ul/5yWjfklSGvLQl8vM//l3FtyHZeuelpzK6mm99pNvN9yTDruNZPEJZD1oWrqo+izBmB7oUfWgcCX7s4Q== - -lodash@4.17.21, lodash@^4.17.21: - version "4.17.21" - resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz" - integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== - -lodash@^3.6.0: - version "3.10.1" - resolved "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz" - integrity sha512-9mDDwqVIma6OZX79ZlDACZl8sBm0TEnkf99zV3iMA4GzkIT/9hiqP5mY0HoT1iNLCrKc/R1HByV+yJfRWVJryQ== - -lru-cache@^5.1.1: - version "5.1.1" - resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz" - integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== - dependencies: - yallist "^3.0.2" - -lru-cache@^6.0.0: - version "6.0.0" - resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz" - integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== - dependencies: - yallist "^4.0.0" - -ltgt@2.2.1, ltgt@^2.1.2: - version "2.2.1" - resolved "https://registry.npmjs.org/ltgt/-/ltgt-2.2.1.tgz" - integrity sha512-AI2r85+4MquTw9ZYqabu4nMwy9Oftlfa/e/52t9IjtfG+mGBbTNdAoZ3RQKLHR6r0wQnwZnPIEh/Ya6XTWAKNA== - -luxon@^3.2.1: - version "3.3.0" - resolved "https://registry.npmjs.org/luxon/-/luxon-3.3.0.tgz" - integrity sha512-An0UCfG/rSiqtAIiBPO0Y9/zAnHUZxAMiCpTd5h2smgsj7GGmcenvrvww2cqNA8/4A5ZrD1gJpHN2mIHZQF+Mg== - -make-dir@^3.0.0, make-dir@^3.1.0: - version "3.1.0" - resolved "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz" - integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== - dependencies: - semver "^6.0.0" - -make-error@1.x, make-error@^1.1.1: - version "1.3.6" - resolved "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz" - integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== - -makeerror@1.0.12: - version "1.0.12" - resolved "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz" - integrity sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg== - dependencies: - tmpl "1.0.5" - -map-stream@~0.1.0: - version "0.1.0" - resolved "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz" - integrity sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g== - -md5@^2.3.0: - version "2.3.0" - resolved "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz" - integrity sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g== - dependencies: - charenc "0.0.2" - crypt "0.0.2" - is-buffer "~1.1.6" - -media-typer@0.3.0: - version "0.3.0" - resolved "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz" - integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== - -merge-descriptors@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz" - integrity sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w== - -merge-stream@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz" - integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== - -methods@^1.1.2: - version "1.1.2" - resolved "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz" - integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== - -micromatch@^4.0.4: - version "4.0.5" - resolved "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz" - integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== - dependencies: - braces "^3.0.2" - picomatch "^2.3.1" - -mime-db@1.52.0: - version "1.52.0" - resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz" - integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== - -mime-types@^2.1.12, mime-types@^2.1.18, mime-types@~2.1.19, mime-types@~2.1.24, mime-types@~2.1.34: - version "2.1.35" - resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz" - integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== - dependencies: - mime-db "1.52.0" - -mime@^1.3.4: - version "1.6.0" - resolved "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz" - integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== - -mimic-fn@^2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz" - integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== - -minimatch@^3.0.4, minimatch@^3.1.1: - version "3.1.2" - resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz" - integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== - dependencies: - brace-expansion "^1.1.7" - -minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6: - version "1.2.6" - resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz" - integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== - -minipass@^3.0.0: - version "3.3.6" - resolved "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz" - integrity sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw== - dependencies: - yallist "^4.0.0" - -minipass@^4.0.0: - version "4.2.5" - resolved "https://registry.npmjs.org/minipass/-/minipass-4.2.5.tgz" - integrity sha512-+yQl7SX3bIT83Lhb4BVorMAHVuqsskxRdlmO9kTpyukp8vsm2Sn/fUOV9xlnG8/a5JsypJzap21lz/y3FBMJ8Q== - -minizlib@^2.1.1: - version "2.1.2" - resolved "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz" - integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== - dependencies: - minipass "^3.0.0" - yallist "^4.0.0" - -mkdirp-classic@^0.5.2: - version "0.5.3" - resolved "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz" - integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== - -mkdirp@^1.0.3: - version "1.0.4" - resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz" - integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== - -ms@2.1.2: - version "2.1.2" - resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz" - integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== - -ms@^2.1.1, ms@^2.1.3: - version "2.1.3" - resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz" - integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== - -msgpackr-extract@^3.0.1: - version "3.0.2" - resolved "https://registry.npmjs.org/msgpackr-extract/-/msgpackr-extract-3.0.2.tgz" - integrity sha512-SdzXp4kD/Qf8agZ9+iTu6eql0m3kWm1A2y1hkpTeVNENutaB0BwHlSvAIaMxwntmRUAUjon2V4L8Z/njd0Ct8A== - dependencies: - node-gyp-build-optional-packages "5.0.7" - optionalDependencies: - "@msgpackr-extract/msgpackr-extract-darwin-arm64" "3.0.2" - "@msgpackr-extract/msgpackr-extract-darwin-x64" "3.0.2" - "@msgpackr-extract/msgpackr-extract-linux-arm" "3.0.2" - "@msgpackr-extract/msgpackr-extract-linux-arm64" "3.0.2" - "@msgpackr-extract/msgpackr-extract-linux-x64" "3.0.2" - "@msgpackr-extract/msgpackr-extract-win32-x64" "3.0.2" - -msgpackr@^1.5.2: - version "1.8.5" - resolved "https://registry.npmjs.org/msgpackr/-/msgpackr-1.8.5.tgz" - integrity sha512-mpPs3qqTug6ahbblkThoUY2DQdNXcm4IapwOS3Vm/87vmpzLVelvp9h3It1y9l1VPpiFLV11vfOXnmeEwiIXwg== - optionalDependencies: - msgpackr-extract "^3.0.1" - -nan@^2.17.0: - version "2.17.0" - resolved "https://registry.npmjs.org/nan/-/nan-2.17.0.tgz" - integrity sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ== - -napi-macros@~2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/napi-macros/-/napi-macros-2.0.0.tgz" - integrity sha512-A0xLykHtARfueITVDernsAWdtIMbOJgKgcluwENp3AlsKN/PloyO10HtmoqnFAQAcxPkgZN7wdfPfEd0zNGxbg== - -natural-compare@^1.4.0: - version "1.4.0" - resolved "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz" - integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== - -ndjson@^1.4.3: - version "1.5.0" - resolved "https://registry.npmjs.org/ndjson/-/ndjson-1.5.0.tgz" - integrity sha512-hUPLuaziboGjNF7wHngkgVc0FOclR8dDk/HfEvTtDr/iUrqBWiRcRSTK3/nLOqKH33th714BrMmTPtObI9gZxQ== - dependencies: - json-stringify-safe "^5.0.1" - minimist "^1.2.0" - split2 "^2.1.0" - through2 "^2.0.3" - -negotiator@0.6.3: - version "0.6.3" - resolved "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz" - integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== - -node-abort-controller@^3.0.1: - version "3.1.1" - resolved "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz" - integrity sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ== - -node-addon-api@^3.1.0: - version "3.2.1" - resolved "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz" - integrity sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A== - -node-duration@^1.0.4: - version "1.0.4" - resolved "https://registry.npmjs.org/node-duration/-/node-duration-1.0.4.tgz" - integrity sha512-eUXYNSY7DL53vqfTosggWkvyIW3bhAcqBDIlolgNYlZhianXTrCL50rlUJWD1eRqkIxMppXTfiFbp+9SjpPrgA== - -node-fetch@2.6.0: - version "2.6.0" - resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz" - integrity sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA== - -node-fetch@2.6.7, node-fetch@^2.6.7: - version "2.6.7" - resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz" - integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== - dependencies: - whatwg-url "^5.0.0" - -node-gyp-build-optional-packages@5.0.7: - version "5.0.7" - resolved "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.0.7.tgz" - integrity sha512-YlCCc6Wffkx0kHkmam79GKvDQ6x+QZkMjFGrIMxgFNILFvGSbCp2fCBC55pGTT9gVaz8Na5CLmxt/urtzRv36w== - -node-gyp-build@~4.1.0: - version "4.1.1" - resolved "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.1.1.tgz" - integrity sha512-dSq1xmcPDKPZ2EED2S6zw/b9NKsqzXRE6dVr8TVQnI3FJOTteUMuqF3Qqs6LZg+mLGYJWqQzMbIjMtJqTv87nQ== - -node-int64@^0.4.0: - version "0.4.0" - resolved "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz" - integrity sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw== - -node-mocks-http@^1.11.0: - version "1.12.2" - resolved "https://registry.npmjs.org/node-mocks-http/-/node-mocks-http-1.12.2.tgz" - integrity sha512-xhWwC0dh35R9rf0j3bRZXuISXdHxxtMx0ywZQBwjrg3yl7KpRETzogfeCamUIjltpn0Fxvs/ZhGJul1vPLrdJQ== - dependencies: - accepts "^1.3.7" - content-disposition "^0.5.3" - depd "^1.1.0" - fresh "^0.5.2" - merge-descriptors "^1.0.1" - methods "^1.1.2" - mime "^1.3.4" - parseurl "^1.3.3" - range-parser "^1.2.0" - type-is "^1.6.18" - -node-releases@^2.0.8: - version "2.0.10" - resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz" - integrity sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w== - -nopt@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz" - integrity sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ== - dependencies: - abbrev "1" - -normalize-path@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz" - integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== - -npm-run-path@^4.0.1: - version "4.0.1" - resolved "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz" - integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== - dependencies: - path-key "^3.0.0" - -npmlog@^5.0.1: - version "5.0.1" - resolved "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz" - integrity sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw== - dependencies: - are-we-there-yet "^2.0.0" - console-control-strings "^1.1.0" - gauge "^3.0.0" - set-blocking "^2.0.0" - -oauth-sign@~0.9.0: - version "0.9.0" - resolved "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz" - integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== - -oauth@0.9.x, oauth@^0.9.15: - version "0.9.15" - resolved "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz" - integrity sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA== - -object-assign@^4.1.1: - version "4.1.1" - resolved "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" - integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== - -object-inspect@^1.9.0: - version "1.12.3" - resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz" - integrity sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g== - -on-finished@^2.3.0: - version "2.4.1" - resolved "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz" - integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== - dependencies: - ee-first "1.1.1" - -once@^1.3.0, once@^1.3.1, once@^1.4.0: - version "1.4.0" - resolved "https://registry.npmjs.org/once/-/once-1.4.0.tgz" - integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== - dependencies: - wrappy "1" - -onetime@^5.1.2: - version "5.1.2" - resolved "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz" - integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== - dependencies: - mimic-fn "^2.1.0" - -only@~0.0.2: - version "0.0.2" - resolved "https://registry.npmjs.org/only/-/only-0.0.2.tgz" - integrity sha512-Fvw+Jemq5fjjyWz6CpKx6w9s7xxqo3+JCyM0WXWeCSOboZ8ABkyvP8ID4CZuChA/wxSx+XSJmdOm8rGVyJ1hdQ== - -os-homedir@^1.0.1: - version "1.0.2" - resolved "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz" - integrity sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ== - -p-finally@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz" - integrity sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow== - -p-limit@^2.2.0: - version "2.3.0" - resolved "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz" - integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== - dependencies: - p-try "^2.0.0" - -p-limit@^3.1.0: - version "3.1.0" - resolved "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz" - integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== - dependencies: - yocto-queue "^0.1.0" - -p-locate@^4.1.0: - version "4.1.0" - resolved "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz" - integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== - dependencies: - p-limit "^2.2.0" - -p-map@^2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz" - integrity sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw== - -p-timeout@^3.2.0: - version "3.2.0" - resolved "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz" - integrity sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg== - dependencies: - p-finally "^1.0.0" - -p-try@^2.0.0: - version "2.2.0" - resolved "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz" - integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== - -parse-json@^5.2.0: - version "5.2.0" - resolved "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz" - integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== - dependencies: - "@babel/code-frame" "^7.0.0" - error-ex "^1.3.1" - json-parse-even-better-errors "^2.3.0" - lines-and-columns "^1.1.6" - -parse-passwd@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz" - integrity sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q== - -parseurl@^1.3.2, parseurl@^1.3.3: - version "1.3.3" - resolved "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz" - integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== - -passport-google-oauth1@1.x.x: - version "1.0.0" - resolved "https://registry.npmjs.org/passport-google-oauth1/-/passport-google-oauth1-1.0.0.tgz" - integrity sha512-qpCEhuflJgYrdg5zZIpAq/K3gTqa1CtHjbubsEsidIdpBPLkEVq6tB1I8kBNcH89RdSiYbnKpCBXAZXX/dtx1Q== - dependencies: - passport-oauth1 "1.x.x" - -passport-google-oauth20@2.x.x: - version "2.0.0" - resolved "https://registry.npmjs.org/passport-google-oauth20/-/passport-google-oauth20-2.0.0.tgz" - integrity sha512-KSk6IJ15RoxuGq7D1UKK/8qKhNfzbLeLrG3gkLZ7p4A6DBCcv7xpyQwuXtWdpyR0+E0mwkpjY1VfPOhxQrKzdQ== - dependencies: - passport-oauth2 "1.x.x" - -passport-google-oauth@2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/passport-google-oauth/-/passport-google-oauth-2.0.0.tgz" - integrity sha512-JKxZpBx6wBQXX1/a1s7VmdBgwOugohH+IxCy84aPTZNq/iIPX6u7Mqov1zY7MKRz3niFPol0KJz8zPLBoHKtYA== - dependencies: - passport-google-oauth1 "1.x.x" - passport-google-oauth20 "2.x.x" - -passport-jwt@4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/passport-jwt/-/passport-jwt-4.0.0.tgz" - integrity sha512-BwC0n2GP/1hMVjR4QpnvqA61TxenUMlmfNjYNgK0ZAs0HK4SOQkHcSv4L328blNTLtHq7DbmvyNJiH+bn6C5Mg== - dependencies: - jsonwebtoken "^8.2.0" - passport-strategy "^1.0.0" - -passport-local@1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/passport-local/-/passport-local-1.0.0.tgz" - integrity sha512-9wCE6qKznvf9mQYYbgJ3sVOHmCWoUNMVFoZzNoznmISbhnNNPhN9xfY3sLmScHMetEJeoY7CXwfhCe7argfQow== - dependencies: - passport-strategy "1.x.x" - -passport-oauth1@1.x.x: - version "1.3.0" - resolved "https://registry.npmjs.org/passport-oauth1/-/passport-oauth1-1.3.0.tgz" - integrity sha512-8T/nX4gwKTw0PjxP1xfD0QhrydQNakzeOpZ6M5Uqdgz9/a/Ag62RmJxnZQ4LkbdXGrRehQHIAHNAu11rCP46Sw== - dependencies: - oauth "0.9.x" - passport-strategy "1.x.x" - utils-merge "1.x.x" - -passport-oauth2-refresh@^2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/passport-oauth2-refresh/-/passport-oauth2-refresh-2.1.0.tgz" - integrity sha512-4ML7ooCESCqiTgdDBzNUFTBcPR8zQq9iM6eppEUGMMvLdsjqRL93jKwWm4Az3OJcI+Q2eIVyI8sVRcPFvxcF/A== - -passport-oauth2@1.x.x: - version "1.7.0" - resolved "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.7.0.tgz" - integrity sha512-j2gf34szdTF2Onw3+76alNnaAExlUmHvkc7cL+cmaS5NzHzDP/BvFHJruueQ9XAeNOdpI+CH+PWid8RA7KCwAQ== - dependencies: - base64url "3.x.x" - oauth "0.9.x" - passport-strategy "1.x.x" - uid2 "0.0.x" - utils-merge "1.x.x" - -passport-strategy@1.x.x, passport-strategy@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz" - integrity sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA== - -passport@^0.4.0: - version "0.4.1" - resolved "https://registry.npmjs.org/passport/-/passport-0.4.1.tgz" - integrity sha512-IxXgZZs8d7uFSt3eqNjM9NQ3g3uQCW5avD8mRNoXV99Yig50vjuaez6dQK2qC0kVWPRTujxY0dWgGfT09adjYg== - dependencies: - passport-strategy "1.x.x" - pause "0.0.1" - -path-exists@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz" - integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== - -path-is-absolute@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" - integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== - -path-key@^3.0.0, path-key@^3.1.0: - version "3.1.1" - resolved "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz" - integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== - -path-parse@^1.0.7: - version "1.0.7" - resolved "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz" - integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== - -pause-stream@0.0.11: - version "0.0.11" - resolved "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz" - integrity sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A== - dependencies: - through "~2.3" - -pause@0.0.1: - version "0.0.1" - resolved "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz" - integrity sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg== - -performance-now@^2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz" - integrity sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow== - -picocolors@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz" - integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== - -picomatch@^2.0.4, picomatch@^2.2.3, picomatch@^2.3.1: - version "2.3.1" - resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz" - integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== - -pirates@^4.0.4: - version "4.0.5" - resolved "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz" - integrity sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ== - -pkg-dir@^4.2.0: - version "4.2.0" - resolved "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz" - integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== - dependencies: - find-up "^4.0.0" - -posthog-node@1.3.0: - version "1.3.0" - resolved "https://registry.npmjs.org/posthog-node/-/posthog-node-1.3.0.tgz" - integrity sha512-2+VhqiY/rKIqKIXyvemBFHbeijHE25sP7eKltnqcFqAssUE6+sX6vusN9A4luzToOqHQkUZexiCKxvuGagh7JA== - dependencies: - axios "0.24.0" - axios-retry "^3.1.9" - component-type "^1.2.1" - join-component "^1.1.0" - md5 "^2.3.0" - ms "^2.1.3" - remove-trailing-slash "^0.1.1" - uuid "^8.3.2" - -pouch-stream@^0.4.0: - version "0.4.1" - resolved "https://registry.npmjs.org/pouch-stream/-/pouch-stream-0.4.1.tgz" - integrity sha512-RAWFhsGDbG4xZQpvrrQlhrITVUNVCKmglfe5WWDnJaDf1u9DMaRLHv//m65tBZevuo4QTGjwcyggwYxd7AGLsg== - dependencies: - inherits "^2.0.1" - readable-stream "^1.0.27-1" - -pouchdb-abstract-mapreduce@7.2.2: - version "7.2.2" - resolved "https://registry.npmjs.org/pouchdb-abstract-mapreduce/-/pouchdb-abstract-mapreduce-7.2.2.tgz" - integrity sha512-7HWN/2yV2JkwMnGnlp84lGvFtnm0Q55NiBUdbBcaT810+clCGKvhssBCrXnmwShD1SXTwT83aszsgiSfW+SnBA== - dependencies: - pouchdb-binary-utils "7.2.2" - pouchdb-collate "7.2.2" - pouchdb-collections "7.2.2" - pouchdb-errors "7.2.2" - pouchdb-fetch "7.2.2" - pouchdb-mapreduce-utils "7.2.2" - pouchdb-md5 "7.2.2" - pouchdb-utils "7.2.2" - -pouchdb-binary-utils@7.2.2: - version "7.2.2" - resolved "https://registry.npmjs.org/pouchdb-binary-utils/-/pouchdb-binary-utils-7.2.2.tgz" - integrity sha512-shacxlmyHbUrNfE6FGYpfyAJx7Q0m91lDdEAaPoKZM3SzAmbtB1i+OaDNtYFztXjJl16yeudkDb3xOeokVL3Qw== - dependencies: - buffer-from "1.1.1" - -pouchdb-collate@7.2.2: - version "7.2.2" - resolved "https://registry.npmjs.org/pouchdb-collate/-/pouchdb-collate-7.2.2.tgz" - integrity sha512-/SMY9GGasslknivWlCVwXMRMnQ8myKHs4WryQ5535nq1Wj/ehpqWloMwxEQGvZE1Sda3LOm7/5HwLTcB8Our+w== - -pouchdb-collections@7.2.2: - version "7.2.2" - resolved "https://registry.npmjs.org/pouchdb-collections/-/pouchdb-collections-7.2.2.tgz" - integrity sha512-6O9zyAYlp3UdtfneiMYuOCWdUCQNo2bgdjvNsMSacQX+3g8WvIoFQCYJjZZCpTttQGb+MHeRMr8m2U95lhJTew== - -pouchdb-errors@7.2.2: - version "7.2.2" - resolved "https://registry.npmjs.org/pouchdb-errors/-/pouchdb-errors-7.2.2.tgz" - integrity sha512-6GQsiWc+7uPfgEHeavG+7wuzH3JZW29Dnrvz8eVbDFE50kVFxNDVm3EkYHskvo5isG7/IkOx7PV7RPTA3keG3g== - dependencies: - inherits "2.0.4" - -pouchdb-fetch@7.2.2: - version "7.2.2" - resolved "https://registry.npmjs.org/pouchdb-fetch/-/pouchdb-fetch-7.2.2.tgz" - integrity sha512-lUHmaG6U3zjdMkh8Vob9GvEiRGwJfXKE02aZfjiVQgew+9SLkuOxNw3y2q4d1B6mBd273y1k2Lm0IAziRNxQnA== - dependencies: - abort-controller "3.0.0" - fetch-cookie "0.10.1" - node-fetch "2.6.0" - -pouchdb-find@7.2.2: - version "7.2.2" - resolved "https://registry.npmjs.org/pouchdb-find/-/pouchdb-find-7.2.2.tgz" - integrity sha512-BmFeFVQ0kHmDehvJxNZl9OmIztCjPlZlVSdpijuFbk/Fi1EFPU1BAv3kLC+6DhZuOqU/BCoaUBY9sn66pPY2ag== - dependencies: - pouchdb-abstract-mapreduce "7.2.2" - pouchdb-collate "7.2.2" - pouchdb-errors "7.2.2" - pouchdb-fetch "7.2.2" - pouchdb-md5 "7.2.2" - pouchdb-selector-core "7.2.2" - pouchdb-utils "7.2.2" - -pouchdb-mapreduce-utils@7.2.2: - version "7.2.2" - resolved "https://registry.npmjs.org/pouchdb-mapreduce-utils/-/pouchdb-mapreduce-utils-7.2.2.tgz" - integrity sha512-rAllb73hIkU8rU2LJNbzlcj91KuulpwQu804/F6xF3fhZKC/4JQMClahk+N/+VATkpmLxp1zWmvmgdlwVU4HtQ== - dependencies: - argsarray "0.0.1" - inherits "2.0.4" - pouchdb-collections "7.2.2" - pouchdb-utils "7.2.2" - -pouchdb-md5@7.2.2: - version "7.2.2" - resolved "https://registry.npmjs.org/pouchdb-md5/-/pouchdb-md5-7.2.2.tgz" - integrity sha512-c/RvLp2oSh8PLAWU5vFBnp6ejJABIdKqboZwRRUrWcfGDf+oyX8RgmJFlYlzMMOh4XQLUT1IoaDV8cwlsuryZw== - dependencies: - pouchdb-binary-utils "7.2.2" - spark-md5 "3.0.1" - -pouchdb-promise@^6.0.4: - version "6.4.3" - resolved "https://registry.npmjs.org/pouchdb-promise/-/pouchdb-promise-6.4.3.tgz" - integrity sha512-ruJaSFXwzsxRHQfwNHjQfsj58LBOY1RzGzde4PM5CWINZwFjCQAhZwfMrch2o/0oZT6d+Xtt0HTWhq35p3b0qw== - dependencies: - lie "3.1.1" - -pouchdb-replication-stream@1.2.9: - version "1.2.9" - resolved "https://registry.npmjs.org/pouchdb-replication-stream/-/pouchdb-replication-stream-1.2.9.tgz" - integrity sha512-hM8XRBfamTTUwRhKwLS/jSNouBhn9R/4ugdHNRD1EvJzwV8iImh6sDYbCU9PGuznjyOjXz6vpFRzKeI2KYfwnQ== - dependencies: - argsarray "0.0.1" - inherits "^2.0.3" - lodash.pick "^4.0.0" - ndjson "^1.4.3" - pouch-stream "^0.4.0" - pouchdb-promise "^6.0.4" - through2 "^2.0.0" - -pouchdb-selector-core@7.2.2: - version "7.2.2" - resolved "https://registry.npmjs.org/pouchdb-selector-core/-/pouchdb-selector-core-7.2.2.tgz" - integrity sha512-XYKCNv9oiNmSXV5+CgR9pkEkTFqxQGWplnVhO3W9P154H08lU0ZoNH02+uf+NjZ2kjse7Q1fxV4r401LEcGMMg== - dependencies: - pouchdb-collate "7.2.2" - pouchdb-utils "7.2.2" - -pouchdb-utils@7.2.2: - version "7.2.2" - resolved "https://registry.npmjs.org/pouchdb-utils/-/pouchdb-utils-7.2.2.tgz" - integrity sha512-XmeM5ioB4KCfyB2MGZXu1Bb2xkElNwF1qG+zVFbQsKQij0zvepdOUfGuWvLRHxTOmt4muIuSOmWZObZa3NOgzQ== - dependencies: - argsarray "0.0.1" - clone-buffer "1.0.0" - immediate "3.3.0" - inherits "2.0.4" - pouchdb-collections "7.2.2" - pouchdb-errors "7.2.2" - pouchdb-md5 "7.2.2" - uuid "8.1.0" - -pouchdb@7.3.0: - version "7.3.0" - resolved "https://registry.npmjs.org/pouchdb/-/pouchdb-7.3.0.tgz" - integrity sha512-OwsIQGXsfx3TrU1pLruj6PGSwFH+h5k4hGNxFkZ76Um7/ZI8F5TzUHFrpldVVIhfXYi2vP31q0q7ot1FSLFYOw== - dependencies: - abort-controller "3.0.0" - argsarray "0.0.1" - buffer-from "1.1.2" - clone-buffer "1.0.0" - double-ended-queue "2.1.0-0" - fetch-cookie "0.11.0" - immediate "3.3.0" - inherits "2.0.4" - level "6.0.1" - level-codec "9.0.2" - level-write-stream "1.0.0" - leveldown "5.6.0" - levelup "4.4.0" - ltgt "2.2.1" - node-fetch "2.6.7" - readable-stream "1.1.14" - spark-md5 "3.0.2" - through2 "3.0.2" - uuid "8.3.2" - vuvuzela "1.0.3" - -prettier@2.7.1: - version "2.7.1" - resolved "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz" - integrity sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g== - -pretty-format@^29.0.0, pretty-format@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.7.0.tgz#ca42c758310f365bfa71a0bda0a807160b776812" - integrity sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ== - dependencies: - "@jest/schemas" "^29.6.3" - ansi-styles "^5.0.0" - react-is "^18.0.0" - -process-nextick-args@~2.0.0: - version "2.0.1" - resolved "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz" - integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== - -prompts@^2.0.1: - version "2.4.2" - resolved "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz" - integrity sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q== - dependencies: - kleur "^3.0.3" - sisteransi "^1.0.5" - -proxy-from-env@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz" - integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== - -prr@~1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz" - integrity sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw== - -ps-tree@1.2.0: - version "1.2.0" - resolved "https://registry.npmjs.org/ps-tree/-/ps-tree-1.2.0.tgz" - integrity sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA== - dependencies: - event-stream "=3.3.4" - -psl@^1.1.28, psl@^1.1.33: - version "1.9.0" - resolved "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz" - integrity sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag== - -pump@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz" - integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== - dependencies: - end-of-stream "^1.1.0" - once "^1.3.1" - -punycode@1.3.2: - version "1.3.2" - resolved "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz" - integrity sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw== - -punycode@^2.1.0, punycode@^2.1.1: - version "2.3.0" - resolved "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz" - integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA== - -pure-rand@^6.0.0: - version "6.0.1" - resolved "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.1.tgz" - integrity sha512-t+x1zEHDjBwkDGY5v5ApnZ/utcd4XYDiJsaQQoptTXgUXX95sDg1elCdJghzicm7n2mbCBJ3uYWr6M22SO19rg== - -qs@^6.11.0: - version "6.11.1" - resolved "https://registry.npmjs.org/qs/-/qs-6.11.1.tgz" - integrity sha512-0wsrzgTz/kAVIeuxSjnpGC56rzYtr6JT/2BwEvMaPhFIoYa1aGO8LbzuU1R0uUYQkLpWBTOj0l/CLAJB64J6nQ== - dependencies: - side-channel "^1.0.4" - -qs@~6.5.2: - version "6.5.3" - resolved "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz" - integrity sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA== - -querystring@0.2.0: - version "0.2.0" - resolved "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz" - integrity sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g== - -querystringify@^2.1.1: - version "2.2.0" - resolved "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz" - integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ== - -range-parser@^1.2.0: - version "1.2.1" - resolved "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz" - integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== - -react-is@^18.0.0: - version "18.2.0" - resolved "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz" - integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w== - -readable-stream@1.1.14, readable-stream@^1.0.27-1: - version "1.1.14" - resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz" - integrity sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ== - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.1" - isarray "0.0.1" - string_decoder "~0.10.x" - -"readable-stream@2 || 3", readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.5.0, readable-stream@^3.6.0: - version "3.6.2" - resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz" - integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== - dependencies: - inherits "^2.0.3" - string_decoder "^1.1.1" - util-deprecate "^1.0.1" - -readable-stream@~0.0.2: - version "0.0.4" - resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-0.0.4.tgz" - integrity sha512-azrivNydKRYt7zwLV5wWUK7YzKTWs3q87xSmY6DlHapPrCvaT6ZrukvM5erV+yCSSPmZT8zkSdttOHQpWWm9zw== - -readable-stream@~2.3.6: - version "2.3.8" - resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz" - integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.3" - isarray "~1.0.0" - process-nextick-args "~2.0.0" - safe-buffer "~5.1.1" - string_decoder "~1.1.1" - util-deprecate "~1.0.1" - -redis-commands@1.7.0: - version "1.7.0" - resolved "https://registry.npmjs.org/redis-commands/-/redis-commands-1.7.0.tgz" - integrity sha512-nJWqw3bTFy21hX/CPKHth6sfhZbdiHP6bTawSgQBlKOVRG7EZkfHbbHwQJnrE4vsQf0CMNE+3gJ4Fmm16vdVlQ== - -redis-errors@^1.0.0, redis-errors@^1.2.0: - version "1.2.0" - resolved "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz" - integrity sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w== - -redis-parser@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz" - integrity sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A== - dependencies: - redis-errors "^1.0.0" - -redlock@4.2.0: - version "4.2.0" - resolved "https://registry.npmjs.org/redlock/-/redlock-4.2.0.tgz" - integrity sha512-j+oQlG+dOwcetUt2WJWttu4CZVeRzUrcVcISFmEmfyuwCVSJ93rDT7YSgg7H7rnxwoRyk/jU46kycVka5tW7jA== - dependencies: - bluebird "^3.7.2" - -regenerator-runtime@^0.13.11: - version "0.13.11" - resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz" - integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg== - -remove-trailing-slash@^0.1.1: - version "0.1.1" - resolved "https://registry.npmjs.org/remove-trailing-slash/-/remove-trailing-slash-0.1.1.tgz" - integrity sha512-o4S4Qh6L2jpnCy83ysZDau+VORNvnFw07CKSAymkd6ICNVEPisMyzlc00KlvvicsxKck94SEwhDnMNdICzO+tA== - -request@^2.88.0: - version "2.88.2" - resolved "https://registry.npmjs.org/request/-/request-2.88.2.tgz" - integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== - dependencies: - aws-sign2 "~0.7.0" - aws4 "^1.8.0" - caseless "~0.12.0" - combined-stream "~1.0.6" - extend "~3.0.2" - forever-agent "~0.6.1" - form-data "~2.3.2" - har-validator "~5.1.3" - http-signature "~1.2.0" - is-typedarray "~1.0.0" - isstream "~0.1.2" - json-stringify-safe "~5.0.1" - mime-types "~2.1.19" - oauth-sign "~0.9.0" - performance-now "^2.1.0" - qs "~6.5.2" - safe-buffer "^5.1.2" - tough-cookie "~2.5.0" - tunnel-agent "^0.6.0" - uuid "^3.3.2" - -require-directory@^2.1.1: - version "2.1.1" - resolved "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz" - integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== - -requires-port@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz" - integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ== - -resolve-cwd@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz" - integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg== - dependencies: - resolve-from "^5.0.0" - -resolve-dir@^0.1.0: - version "0.1.1" - resolved "https://registry.npmjs.org/resolve-dir/-/resolve-dir-0.1.1.tgz" - integrity sha512-QxMPqI6le2u0dCLyiGzgy92kjkkL6zO0XyvHzjdTNH3zM6e5Hz3BwG6+aEyNgiQ5Xz6PwTwgQEj3U50dByPKIA== - dependencies: - expand-tilde "^1.2.2" - global-modules "^0.2.3" - -resolve-from@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz" - integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== - -resolve.exports@^2.0.0: - version "2.0.2" - resolved "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz" - integrity sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg== - -resolve@^1.20.0: - version "1.22.2" - resolved "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz" - integrity sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g== - dependencies: - is-core-module "^2.11.0" - path-parse "^1.0.7" - supports-preserve-symlinks-flag "^1.0.0" - -rimraf@^3.0.2: - version "3.0.2" - resolved "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz" - integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== - dependencies: - glob "^7.1.3" - -rxjs@^7.1.0: - version "7.8.0" - resolved "https://registry.npmjs.org/rxjs/-/rxjs-7.8.0.tgz" - integrity sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg== - dependencies: - tslib "^2.1.0" - -safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@^5.1.2, safe-buffer@~5.2.0: - version "5.2.1" - resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" - integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== - -safe-buffer@~5.1.0, safe-buffer@~5.1.1: - version "5.1.2" - resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz" - integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== - -safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: - version "2.1.2" - resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz" - integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== - -sanitize-s3-objectkey@0.0.1: - version "0.0.1" - resolved "https://registry.npmjs.org/sanitize-s3-objectkey/-/sanitize-s3-objectkey-0.0.1.tgz" - integrity sha512-ZTk7aqLxy4sD40GWcYWoLfbe05XLmkKvh6vGKe13ADlei24xlezcvjgKy1qRArlaIbIMYaqK7PCalvZtulZlaQ== - -sax@1.2.1: - version "1.2.1" - resolved "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz" - integrity sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA== - -sax@>=0.1.1, sax@>=0.6.0: - version "1.2.4" - resolved "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz" - integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== - -semver@7.3.7: - version "7.3.7" - resolved "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz" - integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g== - dependencies: - lru-cache "^6.0.0" - -semver@^5.6.0: - version "5.7.1" - resolved "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz" - integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== - -semver@^6.0.0, semver@^6.3.0: - version "6.3.0" - resolved "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz" - integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== - -semver@^7.3.2, semver@^7.3.5, semver@^7.3.8, semver@^7.5.3, semver@^7.5.4: - version "7.5.4" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" - integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== - dependencies: - lru-cache "^6.0.0" - -set-blocking@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz" - integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== - -setprototypeof@1.2.0: - version "1.2.0" - resolved "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz" - integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== - -shebang-command@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz" - integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== - dependencies: - shebang-regex "^3.0.0" - -shebang-regex@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz" - integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== - -shimmer@^1.2.0: - version "1.2.1" - resolved "https://registry.npmjs.org/shimmer/-/shimmer-1.2.1.tgz" - integrity sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw== - -side-channel@^1.0.4: - version "1.0.4" - resolved "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz" - integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== - dependencies: - call-bind "^1.0.0" - get-intrinsic "^1.0.2" - object-inspect "^1.9.0" - -signal-exit@^3.0.0, signal-exit@^3.0.3, signal-exit@^3.0.7: - version "3.0.7" - resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz" - integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== - -sisteransi@^1.0.5: - version "1.0.5" - resolved "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz" - integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== - -slash@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz" - integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== - -source-map-support@0.5.13: - version "0.5.13" - resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz" - integrity sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w== - dependencies: - buffer-from "^1.0.0" - source-map "^0.6.0" - -source-map@^0.6.0, source-map@^0.6.1: - version "0.6.1" - resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz" - integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== - -spark-md5@3.0.1: - version "3.0.1" - resolved "https://registry.npmjs.org/spark-md5/-/spark-md5-3.0.1.tgz" - integrity sha512-0tF3AGSD1ppQeuffsLDIOWlKUd3lS92tFxcsrh5Pe3ZphhnoK+oXIBTzOAThZCiuINZLvpiLH/1VS1/ANEJVig== - -spark-md5@3.0.2: - version "3.0.2" - resolved "https://registry.npmjs.org/spark-md5/-/spark-md5-3.0.2.tgz" - integrity sha512-wcFzz9cDfbuqe0FZzfi2or1sgyIrsDwmPwfZC4hiNidPdPINjeUwNfv5kldczoEAcjl9Y1L3SM7Uz2PUEQzxQw== - -split-ca@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/split-ca/-/split-ca-1.0.1.tgz" - integrity sha512-Q5thBSxp5t8WPTTJQS59LrGqOZqOsrhDGDVm8azCqIBjSBd7nd9o2PM+mDulQQkh8h//4U6hFZnc/mul8t5pWQ== - -split2@^2.1.0: - version "2.2.0" - resolved "https://registry.npmjs.org/split2/-/split2-2.2.0.tgz" - integrity sha512-RAb22TG39LhI31MbreBgIuKiIKhVsawfTgEGqKHTK87aG+ul/PB8Sqoi3I7kVdRWiCfrKxK3uo4/YUkpNvhPbw== - dependencies: - through2 "^2.0.2" - -split@0.3: - version "0.3.3" - resolved "https://registry.npmjs.org/split/-/split-0.3.3.tgz" - integrity sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA== - dependencies: - through "2" - -sprintf-js@~1.0.2: - version "1.0.3" - resolved "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz" - integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== - -ssh2@^1.11.0: - version "1.14.0" - resolved "https://registry.npmjs.org/ssh2/-/ssh2-1.14.0.tgz" - integrity sha512-AqzD1UCqit8tbOKoj6ztDDi1ffJZ2rV2SwlgrVVrHPkV5vWqGJOVp5pmtj18PunkPJAuKQsnInyKV+/Nb2bUnA== - dependencies: - asn1 "^0.2.6" - bcrypt-pbkdf "^1.0.2" - optionalDependencies: - cpu-features "~0.0.8" - nan "^2.17.0" - -sshpk@^1.7.0: - version "1.17.0" - resolved "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz" - integrity sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ== - dependencies: - asn1 "~0.2.3" - assert-plus "^1.0.0" - bcrypt-pbkdf "^1.0.0" - dashdash "^1.12.0" - ecc-jsbn "~0.1.1" - getpass "^0.1.1" - jsbn "~0.1.0" - safer-buffer "^2.0.2" - tweetnacl "~0.14.0" - -stack-utils@^2.0.3: - version "2.0.6" - resolved "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz" - integrity sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ== - dependencies: - escape-string-regexp "^2.0.0" - -standard-as-callback@^2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz" - integrity sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A== - -start-server-and-test@1.14.0: - version "1.14.0" - resolved "https://registry.npmjs.org/start-server-and-test/-/start-server-and-test-1.14.0.tgz" - integrity sha512-on5ELuxO2K0t8EmNj9MtVlFqwBMxfWOhu4U7uZD1xccVpFlOQKR93CSe0u98iQzfNxRyaNTb/CdadbNllplTsw== - dependencies: - bluebird "3.7.2" - check-more-types "2.24.0" - debug "4.3.2" - execa "5.1.1" - lazy-ass "1.6.0" - ps-tree "1.2.0" - wait-on "6.0.0" - -"statuses@>= 1.5.0 < 2", statuses@^1.5.0: - version "1.5.0" - resolved "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz" - integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== - -step@0.0.x: - version "0.0.6" - resolved "https://registry.npmjs.org/step/-/step-0.0.6.tgz" - integrity sha512-qSSeQinUJk2w38vUFobjFoE307GqsozMC8VisOCkJLpklvKPT0ptPHwWOrENoag8rgLudvTkfP3bancwP93/Jw== - -stream-combiner@~0.0.4: - version "0.0.4" - resolved "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz" - integrity sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw== - dependencies: - duplexer "~0.1.1" - -stream-to-array@^2.3.0: - version "2.3.0" - resolved "https://registry.npmjs.org/stream-to-array/-/stream-to-array-2.3.0.tgz" - integrity sha512-UsZtOYEn4tWU2RGLOXr/o/xjRBftZRlG3dEWoaHr8j4GuypJ3isitGbVyjQKAuMu+xbiop8q224TjiZWc4XTZA== - dependencies: - any-promise "^1.1.0" - -string-length@^4.0.1: - version "4.0.2" - resolved "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz" - integrity sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ== - dependencies: - char-regex "^1.0.2" - strip-ansi "^6.0.0" - -"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: - version "4.2.3" - resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - -string_decoder@^1.1.1: - version "1.3.0" - resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz" - integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== - dependencies: - safe-buffer "~5.2.0" - -string_decoder@~0.10.x: - version "0.10.31" - resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" - integrity sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ== - -string_decoder@~1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz" - integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== - dependencies: - safe-buffer "~5.1.0" - -strip-ansi@^6.0.0, strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - -strip-bom@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz" - integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA== - -strip-bom@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz" - integrity sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w== - -strip-final-newline@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz" - integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== - -strip-json-comments@^3.1.1: - version "3.1.1" - resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz" - integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== - -stripe@^14.11.0: - version "14.11.0" - resolved "https://registry.yarnpkg.com/stripe/-/stripe-14.11.0.tgz#1df63c31bcff3b136457c2b7584f917509e8030c" - integrity sha512-NmFEkDC0PldP7CQtdPgKs5dVZA/pF+IepldbmB+Kk9B4d7EBkWnbANp0y+/zJcbRGul48s8hmQzeqNWUlWW0wg== - dependencies: - "@types/node" ">=8.1.0" - qs "^6.11.0" - -supports-color@^5.3.0: - version "5.5.0" - resolved "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz" - integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== - dependencies: - has-flag "^3.0.0" - -supports-color@^7.1.0: - version "7.2.0" - resolved "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz" - integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== - dependencies: - has-flag "^4.0.0" - -supports-color@^8.0.0: - version "8.1.1" - resolved "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz" - integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== - dependencies: - has-flag "^4.0.0" - -supports-preserve-symlinks-flag@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz" - integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== - -tar-fs@2.1.1, tar-fs@^2.1.0: - version "2.1.1" - resolved "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz" - integrity sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng== - dependencies: - chownr "^1.1.1" - mkdirp-classic "^0.5.2" - pump "^3.0.0" - tar-stream "^2.1.4" - -tar-fs@~2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/tar-fs/-/tar-fs-2.0.1.tgz" - integrity sha512-6tzWDMeroL87uF/+lin46k+Q+46rAJ0SyPGz7OW7wTgblI273hsBqk2C1j0/xNadNLKDTUL9BukSjB7cwgmlPA== - dependencies: - chownr "^1.1.1" - mkdirp-classic "^0.5.2" - pump "^3.0.0" - tar-stream "^2.0.0" - -tar-stream@^2.0.0, tar-stream@^2.1.4: - version "2.2.0" - resolved "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz" - integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ== - dependencies: - bl "^4.0.3" - end-of-stream "^1.4.1" - fs-constants "^1.0.0" - inherits "^2.0.3" - readable-stream "^3.1.1" - -tar@^6.1.11: - version "6.1.13" - resolved "https://registry.npmjs.org/tar/-/tar-6.1.13.tgz" - integrity sha512-jdIBIN6LTIe2jqzay/2vtYLlBHa3JF42ot3h1dW8Q0PaAG4v8rm0cvpVePtau5C6OKXGGcgO9q2AMNSWxiLqKw== - dependencies: - chownr "^2.0.0" - fs-minipass "^2.0.0" - minipass "^4.0.0" - minizlib "^2.1.1" - mkdirp "^1.0.3" - yallist "^4.0.0" - -test-exclude@^6.0.0: - version "6.0.0" - resolved "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz" - integrity sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w== - dependencies: - "@istanbuljs/schema" "^0.1.2" - glob "^7.1.4" - minimatch "^3.0.4" - -testcontainers@4.7.0: - version "4.7.0" - resolved "https://registry.npmjs.org/testcontainers/-/testcontainers-4.7.0.tgz" - integrity sha512-5SrG9RMfDRRZig34fDZeMcGD5i3lHCOJzn0kjouyK4TiEWjZB3h7kCk8524lwNRHROFE1j6DGjceonv/5hl5ag== - dependencies: - "@types/dockerode" "^2.5.34" - byline "^5.0.0" - debug "^4.1.1" - docker-compose "^0.23.5" - dockerode "^3.2.1" - get-port "^5.1.1" - glob "^7.1.6" - node-duration "^1.0.4" - slash "^3.0.0" - stream-to-array "^2.3.0" - tar-fs "^2.1.0" - -through2@3.0.2: - version "3.0.2" - resolved "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz" - integrity sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ== - dependencies: - inherits "^2.0.4" - readable-stream "2 || 3" - -through2@^2.0.0, through2@^2.0.2, through2@^2.0.3: - version "2.0.5" - resolved "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz" - integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ== - dependencies: - readable-stream "~2.3.6" - xtend "~4.0.1" - -through@2, through@~2.3, through@~2.3.1: - version "2.3.8" - resolved "https://registry.npmjs.org/through/-/through-2.3.8.tgz" - integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== - -timekeeper@2.2.0: - version "2.2.0" - resolved "https://registry.npmjs.org/timekeeper/-/timekeeper-2.2.0.tgz" - integrity sha512-W3AmPTJWZkRwu+iSNxPIsLZ2ByADsOLbbLxe46UJyWj3mlYLlwucKiq+/dPm0l9wTzqoF3/2PH0AGFCebjq23A== - -tmpl@1.0.5: - version "1.0.5" - resolved "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz" - integrity sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw== - -to-fast-properties@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz" - integrity sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog== - -to-regex-range@^5.0.1: - version "5.0.1" - resolved "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz" - integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== - dependencies: - is-number "^7.0.0" - -toidentifier@1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz" - integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== - -"tough-cookie@^2.3.3 || ^3.0.1 || ^4.0.0", tough-cookie@^4.1.2: - version "4.1.3" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.1.3.tgz#97b9adb0728b42280aa3d814b6b999b2ff0318bf" - integrity sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw== - dependencies: - psl "^1.1.33" - punycode "^2.1.1" - universalify "^0.2.0" - url-parse "^1.5.3" - -tough-cookie@~2.5.0: - version "2.5.0" - resolved "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz" - integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== - dependencies: - psl "^1.1.28" - punycode "^2.1.1" - -tr46@~0.0.3: - version "0.0.3" - resolved "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz" - integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== - -ts-jest@29.1.1: - version "29.1.1" - resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.1.1.tgz#f58fe62c63caf7bfcc5cc6472082f79180f0815b" - integrity sha512-D6xjnnbP17cC85nliwGiL+tpoKN0StpgE0TeOjXQTU6MVCfsB4v7aW05CgQ/1OywGb0x/oy9hHFnN+sczTiRaA== - dependencies: - bs-logger "0.x" - fast-json-stable-stringify "2.x" - jest-util "^29.0.0" - json5 "^2.2.3" - lodash.memoize "4.x" - make-error "1.x" - semver "^7.5.3" - yargs-parser "^21.0.1" - -ts-node@10.8.1: - version "10.8.1" - resolved "https://registry.npmjs.org/ts-node/-/ts-node-10.8.1.tgz" - integrity sha512-Wwsnao4DQoJsN034wePSg5nZiw4YKXf56mPIAeD6wVmiv+RytNSWqc2f3fKvcUoV+Yn2+yocD71VOfQHbmVX4g== - dependencies: - "@cspotcode/source-map-support" "^0.8.0" - "@tsconfig/node10" "^1.0.7" - "@tsconfig/node12" "^1.0.7" - "@tsconfig/node14" "^1.0.0" - "@tsconfig/node16" "^1.0.2" - acorn "^8.4.1" - acorn-walk "^8.1.1" - arg "^4.1.0" - create-require "^1.1.0" - diff "^4.0.1" - make-error "^1.1.1" - v8-compile-cache-lib "^3.0.1" - yn "3.1.1" - -tsconfig-paths@4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.0.0.tgz" - integrity sha512-SLBg2GBKlR6bVtMgJJlud/o3waplKtL7skmLkExomIiaAtLGtVsoXIqP3SYdjbcH9lq/KVv7pMZeCBpLYOit6Q== - dependencies: - json5 "^2.2.1" - minimist "^1.2.6" - strip-bom "^3.0.0" - -tslib@^2.1.0: - version "2.5.0" - resolved "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz" - integrity sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg== - -tsscmp@1.0.6: - version "1.0.6" - resolved "https://registry.npmjs.org/tsscmp/-/tsscmp-1.0.6.tgz" - integrity sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA== - -tunnel-agent@^0.6.0: - version "0.6.0" - resolved "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz" - integrity sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w== - dependencies: - safe-buffer "^5.0.1" - -tweetnacl@^0.14.3, tweetnacl@~0.14.0: - version "0.14.5" - resolved "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz" - integrity sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA== - -type-detect@4.0.8: - version "4.0.8" - resolved "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz" - integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== - -type-fest@^0.21.3: - version "0.21.3" - resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz" - integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== - -type-is@^1.6.16, type-is@^1.6.18: - version "1.6.18" - resolved "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz" - integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== - dependencies: - media-typer "0.3.0" - mime-types "~2.1.24" - -typescript@5.2.2: - version "5.2.2" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.2.2.tgz#5ebb5e5a5b75f085f22bc3f8460fba308310fa78" - integrity sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w== - -uid2@0.0.x: - version "0.0.4" - resolved "https://registry.npmjs.org/uid2/-/uid2-0.0.4.tgz" - integrity sha512-IevTus0SbGwQzYh3+fRsAMTVVPOoIVufzacXcHPmdlle1jUpq7BRL+mw3dgeLanvGZdwwbWhRV6XrcFNdBmjWA== - -undici-types@~5.26.4: - version "5.26.5" - resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" - integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== - -universalify@^0.2.0: - version "0.2.0" - resolved "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz" - integrity sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg== - -update-browserslist-db@^1.0.10: - version "1.0.10" - resolved "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz" - integrity sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ== - dependencies: - escalade "^3.1.1" - picocolors "^1.0.0" - -uri-js@^4.2.2: - version "4.4.1" - resolved "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz" - integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== - dependencies: - punycode "^2.1.0" - -url-parse@^1.5.3: - version "1.5.10" - resolved "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz" - integrity sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ== - dependencies: - querystringify "^2.1.1" - requires-port "^1.0.0" - -url@0.10.3: - version "0.10.3" - resolved "https://registry.npmjs.org/url/-/url-0.10.3.tgz" - integrity sha512-hzSUW2q06EqL1gKM/a+obYHLIO6ct2hwPuviqTTOcfFVc61UbfJ2Q32+uGL/HCPxKqrdGB5QUwIe7UqlDgwsOQ== - dependencies: - punycode "1.3.2" - querystring "0.2.0" - -util-deprecate@^1.0.1, util-deprecate@~1.0.1: - version "1.0.2" - resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" - integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== - -utils-merge@1.x.x: - version "1.0.1" - resolved "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz" - integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== - -uuid@3.3.2: - version "3.3.2" - resolved "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz" - integrity sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA== - -uuid@8.1.0: - version "8.1.0" - resolved "https://registry.npmjs.org/uuid/-/uuid-8.1.0.tgz" - integrity sha512-CI18flHDznR0lq54xBycOVmphdCYnQLKn8abKn7PXUiKUGdEd+/l9LWNJmugXel4hXq7S+RMNl34ecyC9TntWg== - -uuid@8.3.2, uuid@^8.3.0, uuid@^8.3.1, uuid@^8.3.2: - version "8.3.2" - resolved "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz" - integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== - -uuid@^3.3.2: - version "3.4.0" - resolved "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz" - integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== - -v8-compile-cache-lib@^3.0.1: - version "3.0.1" - resolved "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz" - integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== - -v8-to-istanbul@^9.0.1: - version "9.1.0" - resolved "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.1.0.tgz" - integrity sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA== - dependencies: - "@jridgewell/trace-mapping" "^0.3.12" - "@types/istanbul-lib-coverage" "^2.0.1" - convert-source-map "^1.6.0" - -vary@^1.1.2: - version "1.1.2" - resolved "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz" - integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== - -verror@1.10.0: - version "1.10.0" - resolved "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz" - integrity sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw== - dependencies: - assert-plus "^1.0.0" - core-util-is "1.0.2" - extsprintf "^1.2.0" - -vuvuzela@1.0.3: - version "1.0.3" - resolved "https://registry.npmjs.org/vuvuzela/-/vuvuzela-1.0.3.tgz" - integrity sha512-Tm7jR1xTzBbPW+6y1tknKiEhz04Wf/1iZkcTJjSFcpNko43+dFW6+OOeQe9taJIug3NdfUAjFKgUSyQrIKaDvQ== - -wait-on@6.0.0: - version "6.0.0" - resolved "https://registry.npmjs.org/wait-on/-/wait-on-6.0.0.tgz" - integrity sha512-tnUJr9p5r+bEYXPUdRseolmz5XqJTTj98JgOsfBn7Oz2dxfE2g3zw1jE+Mo8lopM3j3et/Mq1yW7kKX6qw7RVw== - dependencies: - axios "^0.21.1" - joi "^17.4.0" - lodash "^4.17.21" - minimist "^1.2.5" - rxjs "^7.1.0" - -walker@^1.0.8: - version "1.0.8" - resolved "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz" - integrity sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ== - dependencies: - makeerror "1.0.12" - -webfinger@^0.4.2: - version "0.4.2" - resolved "https://registry.npmjs.org/webfinger/-/webfinger-0.4.2.tgz" - integrity sha512-PvvQ/k74HkC3q5G7bGu4VYeKDt3ePZMzT5qFPtEnOL8eyIU1/06OtDn9X5vlkQ23BlegA3eN89rDLiYUife3xQ== - dependencies: - step "0.0.x" - xml2js "0.1.x" - -webidl-conversions@^3.0.0: - version "3.0.1" - resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz" - integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== - -whatwg-url@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz" - integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== - dependencies: - tr46 "~0.0.3" - webidl-conversions "^3.0.0" - -which@^1.2.12: - version "1.3.1" - resolved "https://registry.npmjs.org/which/-/which-1.3.1.tgz" - integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== - dependencies: - isexe "^2.0.0" - -which@^2.0.1: - version "2.0.2" - resolved "https://registry.npmjs.org/which/-/which-2.0.2.tgz" - integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== - dependencies: - isexe "^2.0.0" - -wide-align@^1.1.2: - version "1.1.5" - resolved "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz" - integrity sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg== - dependencies: - string-width "^1.0.2 || 2 || 3 || 4" - -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - -wrappy@1: - version "1.0.2" - resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" - integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== - -write-file-atomic@^4.0.2: - version "4.0.2" - resolved "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz" - integrity sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg== - dependencies: - imurmurhash "^0.1.4" - signal-exit "^3.0.7" - -write-stream@~0.4.3: - version "0.4.3" - resolved "https://registry.npmjs.org/write-stream/-/write-stream-0.4.3.tgz" - integrity sha512-IJrvkhbAnj89W/GAVdVgbnPiVw5Ntg/B4tc/MUCIEwj/g6JIww1DWJyB/yBMT3yw2/TkT6IUZ0+IYef3flEw8A== - dependencies: - readable-stream "~0.0.2" - -xml2js@0.1.x: - version "0.1.14" - resolved "https://registry.npmjs.org/xml2js/-/xml2js-0.1.14.tgz" - integrity sha512-pbdws4PPPNc1HPluSUKamY4GWMk592K7qwcj6BExbVOhhubub8+pMda/ql68b6L3luZs/OGjGSB5goV7SnmgnA== - dependencies: - sax ">=0.1.1" - -xml2js@0.4.19: - version "0.4.19" - resolved "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz" - integrity sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q== - dependencies: - sax ">=0.6.0" - xmlbuilder "~9.0.1" - -xmlbuilder@~9.0.1: - version "9.0.7" - resolved "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz" - integrity sha512-7YXTQc3P2l9+0rjaUbLwMKRhtmwg1M1eDf6nag7urC7pIPYLD9W/jmzQ4ptRSUbodw5S0jfoGTflLemQibSpeQ== - -xtend@^4.0.2, xtend@~4.0.0, xtend@~4.0.1: - version "4.0.2" - resolved "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz" - integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== - -y18n@^5.0.5: - version "5.0.8" - resolved "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz" - integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== - -yallist@^3.0.2: - version "3.1.1" - resolved "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz" - integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== - -yallist@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz" - integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== - -yaml@^1.10.2: - version "1.10.2" - resolved "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz" - integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== - -yargs-parser@^21.0.1, yargs-parser@^21.1.1: - version "21.1.1" - resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz" - integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== - -yargs@^17.3.1: - version "17.7.1" - resolved "https://registry.npmjs.org/yargs/-/yargs-17.7.1.tgz" - integrity sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw== - dependencies: - cliui "^8.0.1" - escalade "^3.1.1" - get-caller-file "^2.0.5" - require-directory "^2.1.1" - string-width "^4.2.3" - y18n "^5.0.5" - yargs-parser "^21.1.1" - -ylru@^1.2.0: - version "1.3.2" - resolved "https://registry.npmjs.org/ylru/-/ylru-1.3.2.tgz" - integrity sha512-RXRJzMiK6U2ye0BlGGZnmpwJDPgakn6aNQ0A7gHRbD4I0uvK4TW6UqkK1V0pp9jskjJBAXd3dRrbzWkqJ+6cxA== - -yn@3.1.1: - version "3.1.1" - resolved "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz" - integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== - -yocto-queue@^0.1.0: - version "0.1.0" - resolved "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz" - integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== - -zlib@1.0.5: - version "1.0.5" - resolved "https://registry.npmjs.org/zlib/-/zlib-1.0.5.tgz" - integrity sha512-40fpE2II+Cd3k8HWTWONfeKE2jL+P42iWJ1zzps5W51qcTsOUKM5Q5m2PFb0CLxlmFAaUuUdJGc3OfZy947v0w== From 8c8e386d2d3228ae765fd5af20d07b7b168b6d87 Mon Sep 17 00:00:00 2001 From: Budibase Staging Release Bot <> Date: Wed, 3 Apr 2024 09:01:14 +0000 Subject: [PATCH 28/97] Bump version to 2.22.15 --- lerna.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lerna.json b/lerna.json index 906e9fba50..93b103ee00 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "2.22.14", + "version": "2.22.15", "npmClient": "yarn", "packages": [ "packages/*", From d431f633d3b54b4bd4e8617b24cafdbde98ce8ef Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 26 Mar 2024 09:57:13 +0100 Subject: [PATCH 29/97] Use types --- .../src/components/backend/DataTable/formula.js | 15 +++++++-------- .../components/backend/TableNavigator/utils.js | 2 +- packages/builder/src/helpers/schemaGenerator.js | 12 ++++++------ packages/builder/src/helpers/utils.js | 9 +++++---- packages/builder/src/stores/builder/tables.js | 7 ++++--- 5 files changed, 23 insertions(+), 22 deletions(-) diff --git a/packages/builder/src/components/backend/DataTable/formula.js b/packages/builder/src/components/backend/DataTable/formula.js index f5ff3caec4..e3da4249bc 100644 --- a/packages/builder/src/components/backend/DataTable/formula.js +++ b/packages/builder/src/components/backend/DataTable/formula.js @@ -1,3 +1,4 @@ +import { FieldType } from "@budibase/types" import { FIELDS } from "constants/backend" import { tables } from "stores/builder" import { get as svelteGet } from "svelte/store" @@ -5,14 +6,12 @@ import { get as svelteGet } from "svelte/store" // currently supported level of relationship depth (server side) const MAX_DEPTH = 1 -//https://github.com/Budibase/budibase/issues/3030 -const internalType = "internal" - const TYPES_TO_SKIP = [ - FIELDS.FORMULA.type, - FIELDS.LONGFORM.type, - FIELDS.ATTACHMENT.type, - internalType, + FieldType.FORMULA, + FieldType.LONGFORM, + FieldType.ATTACHMENT, + //https://github.com/Budibase/budibase/issues/3030 + FieldType.INTERNAL, ] export function getBindings({ @@ -26,7 +25,7 @@ export function getBindings({ return bindings } for (let [column, schema] of Object.entries(table.schema)) { - const isRelationship = schema.type === FIELDS.LINK.type + const isRelationship = schema.type === FieldType.LINK // skip relationships after a certain depth and types which // can't bind to if ( diff --git a/packages/builder/src/components/backend/TableNavigator/utils.js b/packages/builder/src/components/backend/TableNavigator/utils.js index b7e46042be..ae7aaa0f0a 100644 --- a/packages/builder/src/components/backend/TableNavigator/utils.js +++ b/packages/builder/src/components/backend/TableNavigator/utils.js @@ -12,7 +12,7 @@ const getDefaultSchema = rows => { newSchema[column] = { name: column, type: "string", - constraints: FIELDS["STRING"].constraints, + constraints: FIELDS.STRING.constraints, } }) }) diff --git a/packages/builder/src/helpers/schemaGenerator.js b/packages/builder/src/helpers/schemaGenerator.js index 33115fc997..eb044496f6 100644 --- a/packages/builder/src/helpers/schemaGenerator.js +++ b/packages/builder/src/helpers/schemaGenerator.js @@ -1,17 +1,17 @@ -import { FIELDS } from "constants/backend" +import { FieldType } from "@budibase/types" function baseConversion(type) { if (type === "string") { return { - type: FIELDS.STRING.type, + type: FieldType.STRING, } } else if (type === "boolean") { return { - type: FIELDS.BOOLEAN.type, + type: FieldType.BOOLEAN, } } else if (type === "number") { return { - type: FIELDS.NUMBER.type, + type: FieldType.NUMBER, } } } @@ -31,7 +31,7 @@ function recurse(schemaLevel = {}, objectLevel) { const schema = recurse(schemaLevel[key], value[0]) if (schema) { schemaLevel[key] = { - type: FIELDS.ARRAY.type, + type: FieldType.ARRAY, schema, } } @@ -45,7 +45,7 @@ function recurse(schemaLevel = {}, objectLevel) { } } if (!schemaLevel.type) { - return { type: FIELDS.JSON.type, schema: schemaLevel } + return { type: FieldType.JSON, schema: schemaLevel } } else { return schemaLevel } diff --git a/packages/builder/src/helpers/utils.js b/packages/builder/src/helpers/utils.js index 6bb333f3c4..a1f9b34e3d 100644 --- a/packages/builder/src/helpers/utils.js +++ b/packages/builder/src/helpers/utils.js @@ -1,3 +1,4 @@ +import { FieldType } from "@budibase/types" import { ActionStepID } from "constants/backend/automations" import { TableNames } from "constants" import { @@ -20,20 +21,20 @@ export function buildAutoColumn(tableName, name, subtype) { switch (subtype) { case AUTO_COLUMN_SUB_TYPES.UPDATED_BY: case AUTO_COLUMN_SUB_TYPES.CREATED_BY: - type = FIELDS.LINK.type + type = FieldType.LINK constraints = FIELDS.LINK.constraints break case AUTO_COLUMN_SUB_TYPES.AUTO_ID: - type = FIELDS.NUMBER.type + type = FieldType.NUMBER constraints = FIELDS.NUMBER.constraints break case AUTO_COLUMN_SUB_TYPES.UPDATED_AT: case AUTO_COLUMN_SUB_TYPES.CREATED_AT: - type = FIELDS.DATETIME.type + type = FieldType.DATETIME constraints = FIELDS.DATETIME.constraints break default: - type = FIELDS.STRING.type + type = FieldType.STRING constraints = FIELDS.STRING.constraints break } diff --git a/packages/builder/src/stores/builder/tables.js b/packages/builder/src/stores/builder/tables.js index f86b37ab85..0163281480 100644 --- a/packages/builder/src/stores/builder/tables.js +++ b/packages/builder/src/stores/builder/tables.js @@ -1,7 +1,8 @@ +import { FieldType } from "@budibase/types" import { get, writable, derived } from "svelte/store" import { cloneDeep } from "lodash/fp" import { API } from "api" -import { SWITCHABLE_TYPES, FIELDS } from "constants/backend" +import { SWITCHABLE_TYPES } from "constants/backend" export function createTablesStore() { const store = writable({ @@ -83,14 +84,14 @@ export function createTablesStore() { // make sure tables up to date (related) let newTableIds = [] for (let column of Object.values(updatedTable?.schema || {})) { - if (column.type === FIELDS.LINK.type) { + if (column.type === FieldType.LINK) { newTableIds.push(column.tableId) } } let oldTableIds = [] for (let column of Object.values(oldTable?.schema || {})) { - if (column.type === FIELDS.LINK.type) { + if (column.type === FieldType.LINK) { oldTableIds.push(column.tableId) } } From 890059829f1df88473c7339956c9e3d588471df3 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 26 Mar 2024 10:04:51 +0100 Subject: [PATCH 30/97] More types --- .../backend/Datasources/relationshipErrors.js | 2 +- .../builder/src/constants/backend/index.js | 55 +++++++++---------- 2 files changed, 28 insertions(+), 29 deletions(-) diff --git a/packages/builder/src/components/backend/Datasources/relationshipErrors.js b/packages/builder/src/components/backend/Datasources/relationshipErrors.js index 259484e9a9..610ff9f1fe 100644 --- a/packages/builder/src/components/backend/Datasources/relationshipErrors.js +++ b/packages/builder/src/components/backend/Datasources/relationshipErrors.js @@ -1,4 +1,4 @@ -import { RelationshipType } from "constants/backend" +import { RelationshipType } from "@budibase/types" const typeMismatch = "Column type of the foreign key must match the primary key" const columnBeingUsed = "Column name cannot be an existing column" diff --git a/packages/builder/src/constants/backend/index.js b/packages/builder/src/constants/backend/index.js index f1e3e1e2c2..cf38aa3153 100644 --- a/packages/builder/src/constants/backend/index.js +++ b/packages/builder/src/constants/backend/index.js @@ -1,12 +1,12 @@ -import { FieldType, FieldSubtype } from "@budibase/types" +import { + FieldType, + FieldSubtype, + INTERNAL_TABLE_SOURCE_ID, + AutoFieldSubType, + Hosting, +} from "@budibase/types" -export const AUTO_COLUMN_SUB_TYPES = { - AUTO_ID: "autoID", - CREATED_BY: "createdBy", - CREATED_AT: "createdAt", - UPDATED_BY: "updatedBy", - UPDATED_AT: "updatedAt", -} +export const AUTO_COLUMN_SUB_TYPES = AutoFieldSubType export const AUTO_COLUMN_DISPLAY_NAMES = { AUTO_ID: "Auto ID", @@ -108,12 +108,20 @@ export const FIELDS = { ATTACHMENT: { name: "Attachment", type: FieldType.ATTACHMENT, - icon: "Folder", + icon: "Multiple", constraints: { type: "array", presence: false, }, }, + ATTACHMENT_SINGLE: { + name: "Attachment", + type: FieldType.ATTACHMENT_SINGLE, + icon: "Folder", + constraints: { + presence: false, + }, + }, LINK: { name: "Relationship", type: FieldType.LINK, @@ -167,10 +175,7 @@ export const FILE_TYPES = { DOCUMENT: ["odf", "docx", "doc", "pdf", "csv"], } -export const HostingTypes = { - CLOUD: "cloud", - SELF: "self", -} +export const HostingTypes = Hosting export const Roles = { ADMIN: "ADMIN", @@ -187,12 +192,6 @@ export function isAutoColumnUserRelationship(subtype) { ) } -export const RelationshipType = { - MANY_TO_MANY: "many-to-many", - ONE_TO_MANY: "one-to-many", - MANY_TO_ONE: "many-to-one", -} - export const PrettyRelationshipDefinitions = { MANY: "Many rows", ONE: "One row", @@ -218,7 +217,7 @@ export const SWITCHABLE_TYPES = [ ...ALLOWABLE_NUMBER_TYPES, ] -export const BUDIBASE_INTERNAL_DB_ID = "bb_internal" +export const BUDIBASE_INTERNAL_DB_ID = INTERNAL_TABLE_SOURCE_ID export const DEFAULT_BB_DATASOURCE_ID = "datasource_internal_bb_default" export const BUDIBASE_DATASOURCE_TYPE = "budibase" export const DB_TYPE_INTERNAL = "internal" @@ -265,10 +264,10 @@ export const IntegrationNames = { } export const SchemaTypeOptions = [ - { label: "Text", value: "string" }, - { label: "Number", value: "number" }, - { label: "Boolean", value: "boolean" }, - { label: "Datetime", value: "datetime" }, + { label: "Text", value: FieldType.STRING }, + { label: "Number", value: FieldType.NUMBER }, + { label: "Boolean", value: FieldType.BOOLEAN }, + { label: "Datetime", value: FieldType.DATETIME }, ] export const SchemaTypeOptionsExpanded = SchemaTypeOptions.map(el => ({ @@ -305,10 +304,10 @@ export const PaginationLocations = [ ] export const BannedSearchTypes = [ - "link", - "attachment", - "formula", - "json", + FieldType.LINK, + FieldType.ATTACHMENT, + FieldType.FORMULA, + FieldType.JSON, "jsonarray", "queryarray", ] From fcda81434c7cc5098cd5821ba8e4e075d8ead153 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 26 Mar 2024 10:07:16 +0100 Subject: [PATCH 31/97] Fix types --- packages/builder/src/constants/backend/index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/builder/src/constants/backend/index.js b/packages/builder/src/constants/backend/index.js index cf38aa3153..928eb6bbfd 100644 --- a/packages/builder/src/constants/backend/index.js +++ b/packages/builder/src/constants/backend/index.js @@ -5,6 +5,7 @@ import { AutoFieldSubType, Hosting, } from "@budibase/types" +export { RelationshipType } from "@budibase/types" export const AUTO_COLUMN_SUB_TYPES = AutoFieldSubType From 3d45dcea5f5839354af731648133548e32c5cfd9 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 26 Mar 2024 10:19:52 +0100 Subject: [PATCH 32/97] Type renderers --- packages/frontend-core/package.json | 1 + .../src/components/grid/lib/renderers.js | 28 ++++++++++--------- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/packages/frontend-core/package.json b/packages/frontend-core/package.json index fd37af63dc..4ca88de8f2 100644 --- a/packages/frontend-core/package.json +++ b/packages/frontend-core/package.json @@ -8,6 +8,7 @@ "dependencies": { "@budibase/bbui": "0.0.0", "@budibase/shared-core": "0.0.0", + "@budibase/types": "0.0.0", "dayjs": "^1.10.8", "lodash": "4.17.21", "socket.io-client": "^4.6.1" diff --git a/packages/frontend-core/src/components/grid/lib/renderers.js b/packages/frontend-core/src/components/grid/lib/renderers.js index f5d4cfe297..19bf63312d 100644 --- a/packages/frontend-core/src/components/grid/lib/renderers.js +++ b/packages/frontend-core/src/components/grid/lib/renderers.js @@ -1,3 +1,5 @@ +import { FieldType } from "@budibase/types" + import OptionsCell from "../cells/OptionsCell.svelte" import DateCell from "../cells/DateCell.svelte" import MultiSelectCell from "../cells/MultiSelectCell.svelte" @@ -12,19 +14,19 @@ import AttachmentCell from "../cells/AttachmentCell.svelte" import BBReferenceCell from "../cells/BBReferenceCell.svelte" const TypeComponentMap = { - text: TextCell, - options: OptionsCell, - datetime: DateCell, - barcodeqr: TextCell, - longform: LongFormCell, - array: MultiSelectCell, - number: NumberCell, - boolean: BooleanCell, - attachment: AttachmentCell, - link: RelationshipCell, - formula: FormulaCell, - json: JSONCell, - bb_reference: BBReferenceCell, + [FieldType.STRING]: TextCell, + [FieldType.OPTIONS]: OptionsCell, + [FieldType.DATETIME]: DateCell, + [FieldType.BARCODEQR]: TextCell, + [FieldType.LONGFORM]: LongFormCell, + [FieldType.ARRAY]: MultiSelectCell, + [FieldType.NUMBER]: NumberCell, + [FieldType.BOOLEAN]: BooleanCell, + [FieldType.ATTACHMENT]: AttachmentCell, + [FieldType.LINK]: RelationshipCell, + [FieldType.FORMULA]: FormulaCell, + [FieldType.JSON]: JSONCell, + [FieldType.BB_REFERENCE]: BBReferenceCell, } export const getCellRenderer = column => { return TypeComponentMap[column?.schema?.type] || TextCell From edd3ce8f0f6e2dc3340edf8f4978c948122d000b Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 3 Apr 2024 12:13:33 +0200 Subject: [PATCH 33/97] More types --- .../builder/src/templates/commonComponents.js | 18 ++----------- .../app/blocks/FormBlockComponent.svelte | 26 +++++++++---------- 2 files changed, 15 insertions(+), 29 deletions(-) diff --git a/packages/builder/src/templates/commonComponents.js b/packages/builder/src/templates/commonComponents.js index 1a953224a7..884419ae6c 100644 --- a/packages/builder/src/templates/commonComponents.js +++ b/packages/builder/src/templates/commonComponents.js @@ -1,21 +1,7 @@ +import { FieldTypeToComponentMap } from "components/design/settings/controls/FieldConfiguration/utils" import { Component } from "./Component" import { getSchemaForDatasource } from "dataBinding" -const fieldTypeToComponentMap = { - string: "stringfield", - number: "numberfield", - bigint: "bigintfield", - options: "optionsfield", - array: "multifieldselect", - boolean: "booleanfield", - longform: "longformfield", - datetime: "datetimefield", - attachment: "attachmentfield", - link: "relationshipfield", - json: "jsonfield", - barcodeqr: "codescanner", -} - export function makeDatasourceFormComponents(datasource) { const { schema } = getSchemaForDatasource(null, datasource, { formSchema: true, @@ -30,7 +16,7 @@ export function makeDatasourceFormComponents(datasource) { } const fieldType = typeof fieldSchema === "object" ? fieldSchema.type : fieldSchema - const componentType = fieldTypeToComponentMap[fieldType] + const componentType = FieldTypeToComponentMap[fieldType] const fullComponentType = `@budibase/standard-components/${componentType}` if (componentType) { const component = new Component(fullComponentType) diff --git a/packages/client/src/components/app/blocks/FormBlockComponent.svelte b/packages/client/src/components/app/blocks/FormBlockComponent.svelte index ea1c3b0a37..34168355c4 100644 --- a/packages/client/src/components/app/blocks/FormBlockComponent.svelte +++ b/packages/client/src/components/app/blocks/FormBlockComponent.svelte @@ -7,19 +7,19 @@ export let order const FieldTypeToComponentMap = { - string: "stringfield", - number: "numberfield", - bigint: "bigintfield", - options: "optionsfield", - array: "multifieldselect", - boolean: "booleanfield", - longform: "longformfield", - datetime: "datetimefield", - attachment: "attachmentfield", - link: "relationshipfield", - json: "jsonfield", - barcodeqr: "codescanner", - bb_reference: "bbreferencefield", + [FieldType.STRING]: "stringfield", + [FieldType.NUMBER]: "numberfield", + [FieldType.BIGINT]: "bigintfield", + [FieldType.OPTIONS]: "optionsfield", + [FieldType.ARRAY]: "multifieldselect", + [FieldType.BOOLEAN]: "booleanfield", + [FieldType.LONGFORM]: "longformfield", + [FieldType.DATETIME]: "datetimefield", + [FieldType.ATTACHMENT]: "attachmentfield", + [FieldType.LINK]: "relationshipfield", + [FieldType.JSON]: "jsonfield", + [FieldType.BARCODEQR]: "codescanner", + [FieldType.BB_REFERENCE]: "bbreferencefield", } const getFieldSchema = field => { From f771cc17a770feae75db288622b196523f857bb4 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 3 Apr 2024 12:15:47 +0200 Subject: [PATCH 34/97] Type FieldTypeToComponentMap --- .../controls/FieldConfiguration/utils.js | 28 ++++++++++--------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/packages/builder/src/components/design/settings/controls/FieldConfiguration/utils.js b/packages/builder/src/components/design/settings/controls/FieldConfiguration/utils.js index c929263db1..18ebf57d98 100644 --- a/packages/builder/src/components/design/settings/controls/FieldConfiguration/utils.js +++ b/packages/builder/src/components/design/settings/controls/FieldConfiguration/utils.js @@ -1,3 +1,5 @@ +import { FieldType } from "@budibase/types" + export const convertOldFieldFormat = fields => { if (!fields) { return [] @@ -31,17 +33,17 @@ export const getComponentForField = (field, schema) => { } export const FieldTypeToComponentMap = { - string: "stringfield", - number: "numberfield", - bigint: "bigintfield", - options: "optionsfield", - array: "multifieldselect", - boolean: "booleanfield", - longform: "longformfield", - datetime: "datetimefield", - attachment: "attachmentfield", - link: "relationshipfield", - json: "jsonfield", - barcodeqr: "codescanner", - bb_reference: "bbreferencefield", + [FieldType.STRING]: "stringfield", + [FieldType.NUMBER]: "numberfield", + [FieldType.BIGINT]: "bigintfield", + [FieldType.OPTIONS]: "optionsfield", + [FieldType.ARRAY]: "multifieldselect", + [FieldType.BOOLEAN]: "booleanfield", + [FieldType.LONGFORM]: "longformfield", + [FieldType.DATETIME]: "datetimefield", + [FieldType.ATTACHMENT]: "attachmentfield", + [FieldType.LINK]: "relationshipfield", + [FieldType.JSON]: "jsonfield", + [FieldType.BARCODEQR]: "codescanner", + [FieldType.BB_REFERENCE]: "bbreferencefield", } From dbccfd115b9b92d40beb12450eb5ef2192df7319 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Wed, 3 Apr 2024 11:16:20 +0100 Subject: [PATCH 35/97] Update CI workflow to use new larger runners. --- .github/workflows/budibase_ci.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/budibase_ci.yml b/.github/workflows/budibase_ci.yml index f7efb82711..919ad7dfb2 100644 --- a/.github/workflows/budibase_ci.yml +++ b/.github/workflows/budibase_ci.yml @@ -138,8 +138,7 @@ jobs: fi test-server: - runs-on: - group: hosted-runners + runs-on: budi-tubby-tornado-quad-core-150gb env: DEBUG: testcontainers,testcontainers:exec,testcontainers:build,testcontainers:pull steps: From 3dff4bf334ad0021ccbdafda0bc54a1ad3239d0e Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Wed, 3 Apr 2024 11:20:43 +0100 Subject: [PATCH 36/97] Fix lint. --- packages/backend-core/tests/core/utilities/testContainerUtils.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/backend-core/tests/core/utilities/testContainerUtils.ts b/packages/backend-core/tests/core/utilities/testContainerUtils.ts index 2f33db65d3..dbb6fc9861 100644 --- a/packages/backend-core/tests/core/utilities/testContainerUtils.ts +++ b/packages/backend-core/tests/core/utilities/testContainerUtils.ts @@ -1,4 +1,3 @@ -import { DatabaseImpl } from "../../../src/db" import { execSync } from "child_process" const IPV4_PORT_REGEX = new RegExp(`0\\.0\\.0\\.0:(\\d+)->(\\d+)/tcp`, "g") From 94890eae3e1d3f81193c7073e7b46f9163c3c0e6 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 3 Apr 2024 12:26:25 +0200 Subject: [PATCH 37/97] Type TypeIconMap --- .../src/components/grid/lib/utils.js | 32 ++++++++++--------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/packages/frontend-core/src/components/grid/lib/utils.js b/packages/frontend-core/src/components/grid/lib/utils.js index 80bc3d9d67..12a46d3147 100644 --- a/packages/frontend-core/src/components/grid/lib/utils.js +++ b/packages/frontend-core/src/components/grid/lib/utils.js @@ -1,3 +1,5 @@ +import { FieldType, FieldTypeSubtypes } from "@budibase/types" + export const getColor = (idx, opacity = 0.3) => { if (idx == null || idx === -1) { idx = 0 @@ -7,21 +9,21 @@ export const getColor = (idx, opacity = 0.3) => { const TypeIconMap = { text: "Text", - options: "Dropdown", - datetime: "Date", - barcodeqr: "Camera", - longform: "TextAlignLeft", - array: "Dropdown", - number: "123", - boolean: "Boolean", - attachment: "AppleFiles", - link: "DataCorrelated", - formula: "Calculator", - json: "Brackets", - bigint: "TagBold", - bb_reference: { - user: "User", - users: "UserGroup", + [FieldType.OPTIONS]: "Dropdown", + [FieldType.DATETIME]: "Date", + [FieldType.BARCODEQR]: "Camera", + [FieldType.LONGFORM]: "TextAlignLeft", + [FieldType.ARRAY]: "Dropdown", + [FieldType.NUMBER]: "123", + [FieldType.BOOLEAN]: "Boolean", + [FieldType.ATTACHMENT]: "AppleFiles", + [FieldType.LINK]: "DataCorrelated", + [FieldType.FORMULA]: "Calculator", + [FieldType.JSON]: "Brackets", + [FieldType.BIGINT]: "TagBold", + [FieldType.BB_REFERENCE]: { + [FieldTypeSubtypes.BB_REFERENCE.USER]: "User", + [FieldTypeSubtypes.BB_REFERENCE.USERS]: "UserGroup", }, } From 5f55b85e98b2fe3f4f1f2efcfbece84a8a50ce33 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 3 Apr 2024 12:29:13 +0200 Subject: [PATCH 38/97] Remove single --- packages/builder/src/constants/backend/index.js | 8 -------- 1 file changed, 8 deletions(-) diff --git a/packages/builder/src/constants/backend/index.js b/packages/builder/src/constants/backend/index.js index 928eb6bbfd..379b8ed713 100644 --- a/packages/builder/src/constants/backend/index.js +++ b/packages/builder/src/constants/backend/index.js @@ -115,14 +115,6 @@ export const FIELDS = { presence: false, }, }, - ATTACHMENT_SINGLE: { - name: "Attachment", - type: FieldType.ATTACHMENT_SINGLE, - icon: "Folder", - constraints: { - presence: false, - }, - }, LINK: { name: "Relationship", type: FieldType.LINK, From 8cffdeda568396f176068051b97135d38c27bd30 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Wed, 3 Apr 2024 11:33:39 +0100 Subject: [PATCH 39/97] Re-use containers and create namespaces for each test. --- .github/workflows/budibase_ci.yml | 5 +++++ globalSetup.ts | 6 ++++++ .../tests/core/utilities/testContainerUtils.ts | 12 +++++++++++- .../server/src/integrations/tests/utils/index.ts | 6 ++++++ 4 files changed, 28 insertions(+), 1 deletion(-) diff --git a/.github/workflows/budibase_ci.yml b/.github/workflows/budibase_ci.yml index 919ad7dfb2..3d166dc262 100644 --- a/.github/workflows/budibase_ci.yml +++ b/.github/workflows/budibase_ci.yml @@ -91,6 +91,9 @@ jobs: test-libraries: runs-on: ubuntu-latest + env: + REUSE_CONTAINERS: true + CONTAINER_NAMESPACE: test-server steps: - name: Checkout repo uses: actions/checkout@v4 @@ -141,6 +144,8 @@ jobs: runs-on: budi-tubby-tornado-quad-core-150gb env: DEBUG: testcontainers,testcontainers:exec,testcontainers:build,testcontainers:pull + REUSE_CONTAINERS: true + CONTAINER_NAMESPACE: test-server steps: - name: Checkout repo uses: actions/checkout@v4 diff --git a/globalSetup.ts b/globalSetup.ts index 00d5e3f2dc..a1f66d4fc8 100644 --- a/globalSetup.ts +++ b/globalSetup.ts @@ -26,5 +26,11 @@ export default async function setup() { couchdb = couchdb.withReuse() } + if (process.env.CONTAINER_NAMESPACE) { + couchdb = couchdb.withLabels({ + "org.testcontainers.namespace": process.env.CONTAINER_NAMESPACE, + }) + } + await couchdb.start() } diff --git a/packages/backend-core/tests/core/utilities/testContainerUtils.ts b/packages/backend-core/tests/core/utilities/testContainerUtils.ts index dbb6fc9861..b0fbc2228a 100644 --- a/packages/backend-core/tests/core/utilities/testContainerUtils.ts +++ b/packages/backend-core/tests/core/utilities/testContainerUtils.ts @@ -23,12 +23,22 @@ function getTestcontainers(): ContainerInfo[] { // We use --format json to make sure the output is nice and machine-readable, // and we use --no-trunc so that the command returns full container IDs so we // can filter on them correctly. - return execSync("docker ps --format json --no-trunc") + let containers = execSync("docker ps --format json --no-trunc") .toString() .split("\n") .filter(x => x.length > 0) .map(x => JSON.parse(x) as ContainerInfo) .filter(x => x.Labels.includes("org.testcontainers=true")) + + if (process.env.CONTAINER_NAMESPACE) { + containers = containers.filter(x => + x.Labels.includes( + `org.testcontainers.namespace=${process.env.CONTAINER_NAMESPACE}` + ) + ) + } + + return containers } export function getContainerByImage(image: string) { diff --git a/packages/server/src/integrations/tests/utils/index.ts b/packages/server/src/integrations/tests/utils/index.ts index bbdb41b38a..3286f9a3b8 100644 --- a/packages/server/src/integrations/tests/utils/index.ts +++ b/packages/server/src/integrations/tests/utils/index.ts @@ -67,6 +67,12 @@ export async function rawQuery(ds: Datasource, sql: string): Promise { } export async function startContainer(container: GenericContainer) { + if (process.env.CONTAINER_NAMESPACE) { + container = container.withLabels({ + "org.testcontainers.namespace": process.env.CONTAINER_NAMESPACE, + }) + } + if (process.env.REUSE_CONTAINERS) { container = container.withReuse() } From 1fd4365cbadaa6e1912400ed2f50788ad51b3e0e Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 3 Apr 2024 12:38:17 +0200 Subject: [PATCH 40/97] Lint --- packages/builder/src/constants/backend/index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/builder/src/constants/backend/index.js b/packages/builder/src/constants/backend/index.js index 379b8ed713..b25bee9972 100644 --- a/packages/builder/src/constants/backend/index.js +++ b/packages/builder/src/constants/backend/index.js @@ -5,6 +5,7 @@ import { AutoFieldSubType, Hosting, } from "@budibase/types" + export { RelationshipType } from "@budibase/types" export const AUTO_COLUMN_SUB_TYPES = AutoFieldSubType From 19c7c6aa9c218023d3c40396d243754e311abc5d Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Wed, 3 Apr 2024 11:38:42 +0100 Subject: [PATCH 41/97] upgrading posthog-js to support surveys --- packages/builder/package.json | 2 +- yarn.lock | 182 ++++++++++++++++++++++++++++------ 2 files changed, 150 insertions(+), 34 deletions(-) diff --git a/packages/builder/package.json b/packages/builder/package.json index f61ac4fe26..253f5a0c14 100644 --- a/packages/builder/package.json +++ b/packages/builder/package.json @@ -72,7 +72,7 @@ "fast-json-patch": "^3.1.1", "json-format-highlight": "^1.0.4", "lodash": "4.17.21", - "posthog-js": "^1.36.0", + "posthog-js": "^1.116.6", "remixicon": "2.5.0", "sanitize-html": "^2.7.0", "shortid": "2.2.15", diff --git a/yarn.lock b/yarn.lock index 6acdcce3b6..23587e790f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7249,7 +7249,37 @@ axios-retry@^3.1.9: "@babel/runtime" "^7.15.4" is-retry-allowed "^2.2.0" -axios@0.24.0, axios@1.1.3, axios@1.6.3, axios@^0.21.1, axios@^0.21.4, axios@^0.26.0, axios@^1.0.0, axios@^1.1.3, axios@^1.5.0: +axios@0.24.0: + version "0.24.0" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.24.0.tgz#804e6fa1e4b9c5288501dd9dff56a7a0940d20d6" + integrity sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA== + dependencies: + follow-redirects "^1.14.4" + +axios@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.1.3.tgz#8274250dada2edf53814ed7db644b9c2866c1e35" + integrity sha512-00tXVRwKx/FZr/IDVFt4C+f9FYairX517WoGCL6dpOntqLkZofjhu43F/Xl44UOpqa+9sLFDrG/XAnFsUYgkDA== + dependencies: + follow-redirects "^1.15.0" + form-data "^4.0.0" + proxy-from-env "^1.1.0" + +axios@^0.21.1, axios@^0.21.4: + version "0.21.4" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.4.tgz#c67b90dc0568e5c1cf2b0b858c43ba28e2eda575" + integrity sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg== + dependencies: + follow-redirects "^1.14.0" + +axios@^0.26.0: + version "0.26.1" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.26.1.tgz#1ede41c51fcf51bbbd6fd43669caaa4f0495aaa9" + integrity sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA== + dependencies: + follow-redirects "^1.14.8" + +axios@^1.0.0, axios@^1.1.3, axios@^1.5.0: version "1.6.3" resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.3.tgz#7f50f23b3aa246eff43c54834272346c396613f4" integrity sha512-fWyNdeawGam70jXSVlKl+SUNVcL6j6W79CuSIPfi6HnDUmSCH6gyUys/HrqHeA/wU0Az41rRgean494d0Jb+ww== @@ -11022,7 +11052,7 @@ fetch-cookie@0.11.0: dependencies: tough-cookie "^2.3.3 || ^3.0.1 || ^4.0.0" -fflate@^0.4.1, fflate@^0.4.8: +fflate@^0.4.8: version "0.4.8" resolved "https://registry.yarnpkg.com/fflate/-/fflate-0.4.8.tgz#f90b82aefbd8ac174213abb338bd7ef848f0f5ae" integrity sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA== @@ -11200,6 +11230,11 @@ fn.name@1.x.x: resolved "https://registry.yarnpkg.com/fn.name/-/fn.name-1.1.0.tgz#26cad8017967aea8731bc42961d04a3d5988accc" integrity sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw== +follow-redirects@^1.14.0, follow-redirects@^1.14.4, follow-redirects@^1.14.8: + version "1.15.6" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b" + integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA== + follow-redirects@^1.15.0: version "1.15.2" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13" @@ -12318,7 +12353,12 @@ http-assert@^1.3.0: deep-equal "~1.0.1" http-errors "~1.8.0" -http-cache-semantics@3.8.1, http-cache-semantics@4.1.1, http-cache-semantics@^4.0.0, http-cache-semantics@^4.1.0, http-cache-semantics@^4.1.1: +http-cache-semantics@3.8.1: + version "3.8.1" + resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz#39b0e16add9b605bf0a9ef3d9daaf4843b4cacd2" + integrity sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w== + +http-cache-semantics@^4.0.0, http-cache-semantics@^4.1.0, http-cache-semantics@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a" integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ== @@ -13263,11 +13303,6 @@ isobject@^3.0.1: resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== -isobject@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/isobject/-/isobject-4.0.0.tgz#3f1c9155e73b192022a80819bacd0343711697b0" - integrity sha512-S/2fF5wH8SJA/kmwr6HYhK/RI/OkhD84k8ntalo0iJjZikgq1XFvR5M8NPT1x5F7fBwCG3qHfnzeP/Vh/ZxCUA== - isolated-vm@^4.7.2: version "4.7.2" resolved "https://registry.yarnpkg.com/isolated-vm/-/isolated-vm-4.7.2.tgz#5670d5cce1d92004f9b825bec5b0b11fc7501b65" @@ -15862,7 +15897,7 @@ msgpackr-extract@^3.0.2: "@msgpackr-extract/msgpackr-extract-linux-x64" "3.0.2" "@msgpackr-extract/msgpackr-extract-win32-x64" "3.0.2" -msgpackr@1.10.1, msgpackr@^1.5.2: +msgpackr@^1.5.2: version "1.10.1" resolved "https://registry.yarnpkg.com/msgpackr/-/msgpackr-1.10.1.tgz#51953bb4ce4f3494f0c4af3f484f01cfbb306555" integrity sha512-r5VRLv9qouXuLiIBrLpl2d5ZvPt8svdQTl5/vMvE4nzDMyEX4sgW5yWhuBBj5UmgwOTWj8CIdSXn5sAfsHAWIQ== @@ -16066,13 +16101,25 @@ node-addon-api@^6.1.0: resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-6.1.0.tgz#ac8470034e58e67d0c6f1204a18ae6995d9c0d76" integrity sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA== -node-fetch@2.6.0, node-fetch@2.6.7, node-fetch@^2.6.0, node-fetch@^2.6.1, node-fetch@^2.6.7, node-fetch@^2.6.9, node-fetch@^2.7.0: +node-fetch@2.6.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.0.tgz#e633456386d4aa55863f676a7ab0daa8fdecb0fd" + integrity sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA== + +node-fetch@2.6.7, node-fetch@^2.6.0, node-fetch@^2.6.1, node-fetch@^2.6.7: version "2.6.7" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== dependencies: whatwg-url "^5.0.0" +node-fetch@^2.6.9, node-fetch@^2.7.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d" + integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== + dependencies: + whatwg-url "^5.0.0" + node-forge@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3" @@ -17217,7 +17264,15 @@ passport-strategy@1.x.x, passport-strategy@^1.0.0: resolved "https://registry.yarnpkg.com/passport-strategy/-/passport-strategy-1.0.0.tgz#b5539aa8fc225a3d1ad179476ddf236b440f52e4" integrity sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA== -passport@0.6.0, passport@^0.4.0, passport@^0.6.0: +passport@^0.4.0: + version "0.4.1" + resolved "https://registry.yarnpkg.com/passport/-/passport-0.4.1.tgz#941446a21cb92fc688d97a0861c38ce9f738f270" + integrity sha512-IxXgZZs8d7uFSt3eqNjM9NQ3g3uQCW5avD8mRNoXV99Yig50vjuaez6dQK2qC0kVWPRTujxY0dWgGfT09adjYg== + dependencies: + passport-strategy "1.x.x" + pause "0.0.1" + +passport@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/passport/-/passport-0.6.0.tgz#e869579fab465b5c0b291e841e6cc95c005fac9d" integrity sha512-0fe+p3ZnrWRW74fe8+SvCyf4a3Pb2/h7gFkQ8yTJpAO50gDzlfjZUZTO1k5Eg9kUct22OxHLqDZoKUWRHOh9ug== @@ -17935,6 +17990,14 @@ postgres-interval@^1.1.0: dependencies: xtend "^4.0.0" +posthog-js@^1.116.6: + version "1.116.6" + resolved "https://registry.yarnpkg.com/posthog-js/-/posthog-js-1.116.6.tgz#9a5c9f49230a76642f4c44d93b96710f886c2880" + integrity sha512-rvt8HxzJD4c2B/xsUa4jle8ApdqljeBI2Qqjp4XJMohQf18DXRyM6b96H5/UMs8jxYuZG14Er0h/kEIWeU6Fmw== + dependencies: + fflate "^0.4.8" + preact "^10.19.3" + posthog-js@^1.13.4: version "1.103.1" resolved "https://registry.yarnpkg.com/posthog-js/-/posthog-js-1.103.1.tgz#f846c413c28aca204dc1527f49d39f651348f3c4" @@ -17943,13 +18006,6 @@ posthog-js@^1.13.4: fflate "^0.4.8" preact "^10.19.3" -posthog-js@^1.36.0: - version "1.96.1" - resolved "https://registry.yarnpkg.com/posthog-js/-/posthog-js-1.96.1.tgz#4f9719a24e4e14037b0e72d430194d7cdb576447" - integrity sha512-kv1vQqYMt2BV3YHS+wxsbGuP+tz+M3y1AzNhz8TfkpY1HT8W/ONT0i0eQpeRr9Y+d4x/fZ6M4cXG5GMvi9lRCA== - dependencies: - fflate "^0.4.1" - posthog-node@1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/posthog-node/-/posthog-node-1.3.0.tgz#804ed2f213a2f05253f798bf9569d55a9cad94f7" @@ -18517,7 +18573,7 @@ pseudomap@^1.0.2: resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" integrity sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ== -psl@^1.1.33: +psl@^1.1.28, psl@^1.1.33: version "1.9.0" resolved "https://registry.yarnpkg.com/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7" integrity sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag== @@ -19534,6 +19590,11 @@ sax@1.2.1: resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.1.tgz#7b8e656190b228e81a66aea748480d828cd2d37a" integrity sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA== +sax@>=0.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.3.0.tgz#a5dbe77db3be05c9d1ee7785dbd3ea9de51593d0" + integrity sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA== + sax@>=0.6.0: version "1.2.4" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" @@ -19615,13 +19676,40 @@ semver-diff@^3.1.1: dependencies: semver "^6.3.0" -"semver@2 || 3 || 4 || 5", semver@7.5.3, semver@^5.5.0, semver@^5.6.0, semver@^5.7.1, semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0, semver@^6.3.1, semver@^7.0.0, semver@^7.1.1, semver@^7.1.2, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.3, semver@^7.5.4, semver@~2.3.1, semver@~7.0.0: +"semver@2 || 3 || 4 || 5", semver@^5.5.0, semver@^5.6.0, semver@^5.7.1: + version "5.7.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" + integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== + +semver@7.5.3, semver@^7.0.0, semver@^7.1.1, semver@^7.1.2, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.3: version "7.5.3" resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.3.tgz#161ce8c2c6b4b3bdca6caadc9fa3317a4c4fe88e" integrity sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ== dependencies: lru-cache "^6.0.0" +semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0, semver@^6.3.1: + version "6.3.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== + +semver@^7.5.4: + version "7.6.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.0.tgz#1a46a4db4bffcccd97b743b5005c8325f23d4e2d" + integrity sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg== + dependencies: + lru-cache "^6.0.0" + +semver@~2.3.1: + version "2.3.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-2.3.2.tgz#b9848f25d6cf36333073ec9ef8856d42f1233e52" + integrity sha512-abLdIKCosKfpnmhS52NCTjO4RiLspDfsn37prjzGrp9im5DPJOgh82Os92vtwGh6XdQryKI/7SREZnV+aqiXrA== + +semver@~7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e" + integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A== + seq-queue@^0.0.5: version "0.0.5" resolved "https://registry.yarnpkg.com/seq-queue/-/seq-queue-0.0.5.tgz#d56812e1c017a6e4e7c3e3a37a1da6d78dd3c93e" @@ -21207,7 +21295,7 @@ touch@^3.1.0: dependencies: nopt "~1.0.10" -tough-cookie@4.1.3, "tough-cookie@^2.3.3 || ^3.0.1 || ^4.0.0", tough-cookie@^4.0.0, tough-cookie@^4.1.2, tough-cookie@~2.5.0: +"tough-cookie@^2.3.3 || ^3.0.1 || ^4.0.0", tough-cookie@^4.0.0, tough-cookie@^4.1.2: version "4.1.3" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.1.3.tgz#97b9adb0728b42280aa3d814b6b999b2ff0318bf" integrity sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw== @@ -21217,6 +21305,14 @@ tough-cookie@4.1.3, "tough-cookie@^2.3.3 || ^3.0.1 || ^4.0.0", tough-cookie@^4.0 universalify "^0.2.0" url-parse "^1.5.3" +tough-cookie@~2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" + integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== + dependencies: + psl "^1.1.28" + punycode "^2.1.1" + tr46@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/tr46/-/tr46-2.1.0.tgz#fa87aa81ca5d5941da8cbf1f9b749dc969a4e240" @@ -21693,14 +21789,6 @@ unpipe@1.0.0: resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== -unset-value@2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-2.0.1.tgz#57bed0c22d26f28d69acde5df9a11b77c74d2df3" - integrity sha512-2hvrBfjUE00PkqN+q0XP6yRAOGrR06uSiUoIQGZkc7GxvQ9H7v8quUPNtZjMg4uux69i8HWpIjLPUKwCuRGyNg== - dependencies: - has-value "^2.0.2" - isobject "^4.0.0" - untildify@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/untildify/-/untildify-4.0.0.tgz#2bc947b953652487e4600949fb091e3ae8cd919b" @@ -22471,10 +22559,33 @@ xml-parse-from-string@^1.0.0: resolved "https://registry.yarnpkg.com/xml-parse-from-string/-/xml-parse-from-string-1.0.1.tgz#a9029e929d3dbcded169f3c6e28238d95a5d5a28" integrity sha512-ErcKwJTF54uRzzNMXq2X5sMIy88zJvfN2DmdoQvy7PAFJ+tPRU6ydWuOKNMyfmOjdyBQTFREi60s0Y0SyI0G0g== -xml2js@0.1.x, xml2js@0.4.19, xml2js@0.5.0, xml2js@0.6.2, xml2js@^0.4.19, xml2js@^0.4.5: - version "0.6.2" - resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.6.2.tgz#dd0b630083aa09c161e25a4d0901e2b2a929b499" - integrity sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA== +xml2js@0.1.x: + version "0.1.14" + resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.1.14.tgz#5274e67f5a64c5f92974cd85139e0332adc6b90c" + integrity sha512-pbdws4PPPNc1HPluSUKamY4GWMk592K7qwcj6BExbVOhhubub8+pMda/ql68b6L3luZs/OGjGSB5goV7SnmgnA== + dependencies: + sax ">=0.1.1" + +xml2js@0.4.19: + version "0.4.19" + resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.19.tgz#686c20f213209e94abf0d1bcf1efaa291c7827a7" + integrity sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q== + dependencies: + sax ">=0.6.0" + xmlbuilder "~9.0.1" + +xml2js@0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.5.0.tgz#d9440631fbb2ed800203fad106f2724f62c493b7" + integrity sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA== + dependencies: + sax ">=0.6.0" + xmlbuilder "~11.0.0" + +xml2js@^0.4.19, xml2js@^0.4.5: + version "0.4.23" + resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.23.tgz#a0c69516752421eb2ac758ee4d4ccf58843eac66" + integrity sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug== dependencies: sax ">=0.6.0" xmlbuilder "~11.0.0" @@ -22484,6 +22595,11 @@ xmlbuilder@~11.0.0: resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz#be9bae1c8a046e76b31127726347d0ad7002beb3" integrity sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA== +xmlbuilder@~9.0.1: + version "9.0.7" + resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-9.0.7.tgz#132ee63d2ec5565c557e20f4c22df9aca686b10d" + integrity sha512-7YXTQc3P2l9+0rjaUbLwMKRhtmwg1M1eDf6nag7urC7pIPYLD9W/jmzQ4ptRSUbodw5S0jfoGTflLemQibSpeQ== + xmlchars@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" From 258226ddef46cecd6768f0d4f6cdc4f7ade09950 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Wed, 3 Apr 2024 11:40:14 +0100 Subject: [PATCH 42/97] Better error message when multiple images are found. --- .../backend-core/tests/core/utilities/testContainerUtils.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/backend-core/tests/core/utilities/testContainerUtils.ts b/packages/backend-core/tests/core/utilities/testContainerUtils.ts index b0fbc2228a..eeb9d7e6d3 100644 --- a/packages/backend-core/tests/core/utilities/testContainerUtils.ts +++ b/packages/backend-core/tests/core/utilities/testContainerUtils.ts @@ -44,7 +44,11 @@ function getTestcontainers(): ContainerInfo[] { export function getContainerByImage(image: string) { const containers = getTestcontainers().filter(x => x.Image.startsWith(image)) if (containers.length > 1) { - throw new Error(`Multiple containers found with image: ${image}`) + let errorMessage = `Multiple containers found starting with image: "${image}"\n\n` + for (const container of containers) { + errorMessage += JSON.stringify(container, null, 2) + } + throw new Error(errorMessage) } return containers[0] } From fba24987af24b56d9d76f3ea8ff2afd72ecf48eb Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 3 Apr 2024 12:41:32 +0200 Subject: [PATCH 43/97] Undo icon change --- packages/builder/src/constants/backend/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/builder/src/constants/backend/index.js b/packages/builder/src/constants/backend/index.js index b25bee9972..dd751d4e13 100644 --- a/packages/builder/src/constants/backend/index.js +++ b/packages/builder/src/constants/backend/index.js @@ -110,7 +110,7 @@ export const FIELDS = { ATTACHMENT: { name: "Attachment", type: FieldType.ATTACHMENT, - icon: "Multiple", + icon: "Folder", constraints: { type: "array", presence: false, From a6c56c35d4bf4c8a77268853b46c2e528e1df8f0 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 3 Apr 2024 12:43:51 +0200 Subject: [PATCH 44/97] Type --- packages/frontend-core/src/components/grid/lib/utils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frontend-core/src/components/grid/lib/utils.js b/packages/frontend-core/src/components/grid/lib/utils.js index 12a46d3147..8382bfece8 100644 --- a/packages/frontend-core/src/components/grid/lib/utils.js +++ b/packages/frontend-core/src/components/grid/lib/utils.js @@ -8,7 +8,7 @@ export const getColor = (idx, opacity = 0.3) => { } const TypeIconMap = { - text: "Text", + [FieldType.STRING]: "Text", [FieldType.OPTIONS]: "Dropdown", [FieldType.DATETIME]: "Date", [FieldType.BARCODEQR]: "Camera", From c31614a582e746d4c51c6cb916d938556c4986fe Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Wed, 3 Apr 2024 11:45:41 +0100 Subject: [PATCH 45/97] Give test-libraries its own container namespace, give packages/server/scripts/test.sh some new params in the larger runners. --- .github/workflows/budibase_ci.yml | 2 +- packages/server/scripts/test.sh | 9 ++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/.github/workflows/budibase_ci.yml b/.github/workflows/budibase_ci.yml index 3d166dc262..b72d6793af 100644 --- a/.github/workflows/budibase_ci.yml +++ b/.github/workflows/budibase_ci.yml @@ -93,7 +93,7 @@ jobs: runs-on: ubuntu-latest env: REUSE_CONTAINERS: true - CONTAINER_NAMESPACE: test-server + CONTAINER_NAMESPACE: test-libraries steps: - name: Checkout repo uses: actions/checkout@v4 diff --git a/packages/server/scripts/test.sh b/packages/server/scripts/test.sh index 3ecf8bb794..3100d1911f 100644 --- a/packages/server/scripts/test.sh +++ b/packages/server/scripts/test.sh @@ -1,14 +1,13 @@ #!/bin/bash set -e +export NODE_OPTIONS="--no-node-snapshot $NODE_OPTIONS" + if [[ -n $CI ]] then - export NODE_OPTIONS="--max-old-space-size=4096 --no-node-snapshot $NODE_OPTIONS" - echo "jest --coverage --maxWorkers=2 --forceExit --workerIdleMemoryLimit=2000MB --bail $@" - jest --coverage --maxWorkers=2 --forceExit --workerIdleMemoryLimit=2000MB --bail $@ + echo "jest --coverage --maxWorkers=4 --forceExit --bail $@" + jest --coverage --maxWorkers=2 --forceExit --bail $@ else - # --maxWorkers performs better in development - export NODE_OPTIONS="--no-node-snapshot $NODE_OPTIONS" echo "jest --coverage --maxWorkers=2 --forceExit $@" jest --coverage --maxWorkers=2 --forceExit $@ fi \ No newline at end of file From 38bfdcd74921648be3b4378b51b1ec8c29989e8c Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Mon, 25 Mar 2024 18:39:03 +0100 Subject: [PATCH 46/97] Rename attachment to attachment list --- packages/builder/src/constants/backend/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/builder/src/constants/backend/index.js b/packages/builder/src/constants/backend/index.js index dd751d4e13..5b79443f79 100644 --- a/packages/builder/src/constants/backend/index.js +++ b/packages/builder/src/constants/backend/index.js @@ -108,7 +108,7 @@ export const FIELDS = { }, }, ATTACHMENT: { - name: "Attachment", + name: "Attachment List", type: FieldType.ATTACHMENT, icon: "Folder", constraints: { From f7c7ee91e29f5b0fa1f68d3f66b648799fa0a74a Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 26 Mar 2024 10:08:33 +0100 Subject: [PATCH 47/97] Add single attachment type --- .../components/backend/DataTable/modals/CreateEditColumn.svelte | 1 + packages/frontend-core/src/components/grid/lib/utils.js | 1 + packages/types/src/documents/app/row.ts | 1 + 3 files changed, 3 insertions(+) diff --git a/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte b/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte index cfc6e9a7be..2701c30359 100644 --- a/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte +++ b/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte @@ -395,6 +395,7 @@ FIELDS.BOOLEAN, FIELDS.DATETIME, FIELDS.ATTACHMENT, + FIELDS.ATTACHMENT_SINGLE, FIELDS.LINK, FIELDS.FORMULA, FIELDS.JSON, diff --git a/packages/frontend-core/src/components/grid/lib/utils.js b/packages/frontend-core/src/components/grid/lib/utils.js index 8382bfece8..75578133a9 100644 --- a/packages/frontend-core/src/components/grid/lib/utils.js +++ b/packages/frontend-core/src/components/grid/lib/utils.js @@ -17,6 +17,7 @@ const TypeIconMap = { [FieldType.NUMBER]: "123", [FieldType.BOOLEAN]: "Boolean", [FieldType.ATTACHMENT]: "AppleFiles", + [FieldType.ATTACHMENTS]: "AppleFiles", [FieldType.LINK]: "DataCorrelated", [FieldType.FORMULA]: "Calculator", [FieldType.JSON]: "Brackets", diff --git a/packages/types/src/documents/app/row.ts b/packages/types/src/documents/app/row.ts index aa8f50d4a8..5aab87fe6c 100644 --- a/packages/types/src/documents/app/row.ts +++ b/packages/types/src/documents/app/row.ts @@ -9,6 +9,7 @@ export enum FieldType { ARRAY = "array", DATETIME = "datetime", ATTACHMENT = "attachment", + ATTACHMENT_SINGLE = "attachment_single", LINK = "link", FORMULA = "formula", AUTO = "auto", From 7523cf8be31f29fbae3c81c1774f94dc53cfeb5d Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 26 Mar 2024 11:05:06 +0100 Subject: [PATCH 48/97] Single attachment cell --- .../design/settings/controls/FieldConfiguration/utils.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/builder/src/components/design/settings/controls/FieldConfiguration/utils.js b/packages/builder/src/components/design/settings/controls/FieldConfiguration/utils.js index 18ebf57d98..665ffffc63 100644 --- a/packages/builder/src/components/design/settings/controls/FieldConfiguration/utils.js +++ b/packages/builder/src/components/design/settings/controls/FieldConfiguration/utils.js @@ -42,6 +42,7 @@ export const FieldTypeToComponentMap = { [FieldType.LONGFORM]: "longformfield", [FieldType.DATETIME]: "datetimefield", [FieldType.ATTACHMENT]: "attachmentfield", + [FieldType.ATTACHMENT_SINGLE]: "attachmentfield", [FieldType.LINK]: "relationshipfield", [FieldType.JSON]: "jsonfield", [FieldType.BARCODEQR]: "codescanner", From 7d885482557a1dc6bd0cbcfa5eadf938aecc0f21 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 26 Mar 2024 11:05:45 +0100 Subject: [PATCH 49/97] More types --- .../client/src/components/app/blocks/FormBlockComponent.svelte | 1 + packages/frontend-core/src/components/grid/lib/renderers.js | 1 + 2 files changed, 2 insertions(+) diff --git a/packages/client/src/components/app/blocks/FormBlockComponent.svelte b/packages/client/src/components/app/blocks/FormBlockComponent.svelte index 34168355c4..9a06fc10ec 100644 --- a/packages/client/src/components/app/blocks/FormBlockComponent.svelte +++ b/packages/client/src/components/app/blocks/FormBlockComponent.svelte @@ -16,6 +16,7 @@ [FieldType.LONGFORM]: "longformfield", [FieldType.DATETIME]: "datetimefield", [FieldType.ATTACHMENT]: "attachmentfield", + [FieldType.ATTACHMENT_SINGLE]: "attachmentfield", [FieldType.LINK]: "relationshipfield", [FieldType.JSON]: "jsonfield", [FieldType.BARCODEQR]: "codescanner", diff --git a/packages/frontend-core/src/components/grid/lib/renderers.js b/packages/frontend-core/src/components/grid/lib/renderers.js index 19bf63312d..d4ac8b86ec 100644 --- a/packages/frontend-core/src/components/grid/lib/renderers.js +++ b/packages/frontend-core/src/components/grid/lib/renderers.js @@ -23,6 +23,7 @@ const TypeComponentMap = { [FieldType.NUMBER]: NumberCell, [FieldType.BOOLEAN]: BooleanCell, [FieldType.ATTACHMENT]: AttachmentCell, + [FieldType.ATTACHMENT_SINGLE]: AttachmentCell, [FieldType.LINK]: RelationshipCell, [FieldType.FORMULA]: FormulaCell, [FieldType.JSON]: JSONCell, From c96e8b273d2ff2cdfb2f41b458d38129ddff5b5d Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 26 Mar 2024 11:38:55 +0100 Subject: [PATCH 50/97] Fix types --- packages/shared-core/src/table.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/shared-core/src/table.ts b/packages/shared-core/src/table.ts index 5eab2fc340..e1f48ce176 100644 --- a/packages/shared-core/src/table.ts +++ b/packages/shared-core/src/table.ts @@ -11,10 +11,10 @@ const allowDisplayColumnByType: Record = { [FieldType.INTERNAL]: true, [FieldType.BARCODEQR]: true, [FieldType.BIGINT]: true, - [FieldType.BOOLEAN]: false, [FieldType.ARRAY]: false, [FieldType.ATTACHMENT]: false, + [FieldType.ATTACHMENT_SINGLE]: false, [FieldType.LINK]: false, [FieldType.JSON]: false, [FieldType.BB_REFERENCE]: false, @@ -35,6 +35,7 @@ const allowSortColumnByType: Record = { [FieldType.FORMULA]: false, [FieldType.ATTACHMENT]: false, + [FieldType.ATTACHMENT_SINGLE]: false, [FieldType.ARRAY]: false, [FieldType.LINK]: false, [FieldType.BB_REFERENCE]: false, From ed228a2288bd4f84a561ec6c61aff05c64605059 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 26 Mar 2024 11:45:09 +0100 Subject: [PATCH 51/97] Single attachment cell --- .../grid/cells/AttachmentCell.svelte | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/packages/frontend-core/src/components/grid/cells/AttachmentCell.svelte b/packages/frontend-core/src/components/grid/cells/AttachmentCell.svelte index a1f5c4f2aa..5f26a9d9b1 100644 --- a/packages/frontend-core/src/components/grid/cells/AttachmentCell.svelte +++ b/packages/frontend-core/src/components/grid/cells/AttachmentCell.svelte @@ -1,6 +1,7 @@ @@ -87,7 +97,7 @@ > {#if fieldState} From d74af112308cc7c3a5f87dee5c2b29f442babb42 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 26 Mar 2024 14:53:41 +0100 Subject: [PATCH 55/97] Handle deletions --- packages/bbui/src/Form/Core/Dropzone.svelte | 2 +- .../src/components/app/forms/AttachmentField.svelte | 13 ++++++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/packages/bbui/src/Form/Core/Dropzone.svelte b/packages/bbui/src/Form/Core/Dropzone.svelte index 3d803c0961..dd72167791 100644 --- a/packages/bbui/src/Form/Core/Dropzone.svelte +++ b/packages/bbui/src/Form/Core/Dropzone.svelte @@ -67,7 +67,7 @@ } $: showDropzone = - (!maximum || (maximum && value?.length < maximum)) && !disabled + (!maximum || (maximum && (value?.length || 0) < maximum)) && !disabled async function processFileList(fileList) { if ( diff --git a/packages/client/src/components/app/forms/AttachmentField.svelte b/packages/client/src/components/app/forms/AttachmentField.svelte index 70fa8717d9..63f6d2b39b 100644 --- a/packages/client/src/components/app/forms/AttachmentField.svelte +++ b/packages/client/src/components/app/forms/AttachmentField.svelte @@ -20,9 +20,12 @@ let fieldState let fieldApi + $: isSingle = schema?.type === FieldType.ATTACHMENT_SINGLE $: value = - fieldState?.value && type === FieldType.ATTACHMENT_SINGLE - ? [fieldState.value] + isSingle && !Array.isArray(fieldState?.value) + ? fieldState?.value + ? [fieldState.value] + : [] : fieldState?.value const { API, notificationStore } = getContext("sdk") @@ -72,8 +75,8 @@ const handleChange = e => { let value = e.detail - if (type === FieldType.ATTACHMENT_SINGLE) { - value = value[0] + if (isSingle) { + value = value[0] || null } const changed = fieldApi.setValue(value) if (onChange && changed) { @@ -105,7 +108,7 @@ {deleteAttachments} {handleFileTooLarge} {handleTooManyFiles} - maximum={type === FieldType.ATTACHMENT_SINGLE ? 1 : maximum} + maximum={isSingle ? 1 : maximum} {extensions} {compact} /> From 47ab920bd118171ddef120e4036a83bbbecd54de Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 26 Mar 2024 14:54:17 +0100 Subject: [PATCH 56/97] Fix --- packages/client/src/components/app/forms/AttachmentField.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/src/components/app/forms/AttachmentField.svelte b/packages/client/src/components/app/forms/AttachmentField.svelte index 63f6d2b39b..b38d305a7d 100644 --- a/packages/client/src/components/app/forms/AttachmentField.svelte +++ b/packages/client/src/components/app/forms/AttachmentField.svelte @@ -20,7 +20,7 @@ let fieldState let fieldApi - $: isSingle = schema?.type === FieldType.ATTACHMENT_SINGLE + $: isSingle = type === FieldType.ATTACHMENT_SINGLE $: value = isSingle && !Array.isArray(fieldState?.value) ? fieldState?.value From ac5bae0e232909ff0749b9aad9c5262812b02e87 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 27 Mar 2024 08:07:49 +0100 Subject: [PATCH 57/97] Lint --- packages/client/src/components/app/forms/AttachmentField.svelte | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/client/src/components/app/forms/AttachmentField.svelte b/packages/client/src/components/app/forms/AttachmentField.svelte index b38d305a7d..eff0b8c408 100644 --- a/packages/client/src/components/app/forms/AttachmentField.svelte +++ b/packages/client/src/components/app/forms/AttachmentField.svelte @@ -62,7 +62,6 @@ } const deleteAttachments = async fileList => { - console.error({ fileList }) try { return await API.deleteAttachments({ keys: fileList, From 21597a44c93ae6aa32136707cd59549e9c44fdd9 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 27 Mar 2024 09:25:37 +0100 Subject: [PATCH 58/97] SingleAttachmentField component --- packages/client/manifest.json | 99 ++++++++++++++++++- .../app/blocks/FormBlockComponent.svelte | 7 +- .../app/forms/SingleAttachmentField.svelte | 6 ++ .../client/src/components/app/forms/index.js | 1 + 4 files changed, 106 insertions(+), 7 deletions(-) create mode 100644 packages/client/src/components/app/forms/SingleAttachmentField.svelte diff --git a/packages/client/manifest.json b/packages/client/manifest.json index 08d614391b..11b38216fe 100644 --- a/packages/client/manifest.json +++ b/packages/client/manifest.json @@ -4226,7 +4226,7 @@ ] }, "attachmentfield": { - "name": "Attachment", + "name": "Attachment list", "icon": "Attach", "styles": ["size"], "requiredAncestors": ["form"], @@ -4322,6 +4322,103 @@ } ] }, + "singleattachmentfield": { + "name": "Single Attachment", + "icon": "Attach", + "styles": ["size"], + "requiredAncestors": ["form"], + "editable": true, + "size": { + "width": 400, + "height": 200 + }, + "settings": [ + { + "type": "field/attachment_single", + "label": "Field", + "key": "field", + "required": true + }, + { + "type": "text", + "label": "Label", + "key": "label" + }, + { + "type": "text", + "label": "Help text", + "key": "helpText" + }, + { + "type": "text", + "label": "Extensions", + "key": "extensions" + }, + { + "type": "number", + "label": "Max attachments", + "key": "maximum", + "min": 1 + }, + { + "type": "event", + "label": "On change", + "key": "onChange", + "context": [ + { + "label": "Field Value", + "key": "value" + } + ] + }, + { + "type": "boolean", + "label": "Compact", + "key": "compact", + "defaultValue": false + }, + { + "type": "boolean", + "label": "Read only", + "key": "disabled", + "defaultValue": false + }, + { + "type": "validation/attachment", + "label": "Validation", + "key": "validation" + }, + { + "type": "select", + "label": "Layout", + "key": "span", + "defaultValue": 6, + "hidden": true, + "showInBar": true, + "barStyle": "buttons", + "options": [ + { + "label": "1 column", + "value": 6, + "barIcon": "Stop", + "barTitle": "1 column" + }, + { + "label": "2 columns", + "value": 3, + "barIcon": "ColumnTwoA", + "barTitle": "2 columns" + }, + { + "label": "3 columns", + "value": 2, + "barIcon": "ViewColumn", + "barTitle": "3 columns" + } + ] + } + ] + }, "relationshipfield": { "name": "Relationship Picker", "icon": "TaskList", diff --git a/packages/client/src/components/app/blocks/FormBlockComponent.svelte b/packages/client/src/components/app/blocks/FormBlockComponent.svelte index acac79a97f..c88b8f1499 100644 --- a/packages/client/src/components/app/blocks/FormBlockComponent.svelte +++ b/packages/client/src/components/app/blocks/FormBlockComponent.svelte @@ -16,7 +16,7 @@ [FieldType.LONGFORM]: "longformfield", [FieldType.DATETIME]: "datetimefield", [FieldType.ATTACHMENT]: "attachmentfield", - [FieldType.ATTACHMENT_SINGLE]: "attachmentfield", + [FieldType.ATTACHMENT_SINGLE]: "singleattachmentfield", [FieldType.LINK]: "relationshipfield", [FieldType.JSON]: "jsonfield", [FieldType.BARCODEQR]: "codescanner", @@ -66,11 +66,6 @@ maximum: schema?.constraints?.length?.maximum, } }, - [FieldType.ATTACHMENT_SINGLE]: () => { - return { - type: FieldType.ATTACHMENT_SINGLE, - } - }, } const fieldSchema = getFieldSchema(field) diff --git a/packages/client/src/components/app/forms/SingleAttachmentField.svelte b/packages/client/src/components/app/forms/SingleAttachmentField.svelte new file mode 100644 index 0000000000..4810f4e848 --- /dev/null +++ b/packages/client/src/components/app/forms/SingleAttachmentField.svelte @@ -0,0 +1,6 @@ + + + diff --git a/packages/client/src/components/app/forms/index.js b/packages/client/src/components/app/forms/index.js index 5804d3a79d..5f0241c913 100644 --- a/packages/client/src/components/app/forms/index.js +++ b/packages/client/src/components/app/forms/index.js @@ -9,6 +9,7 @@ export { default as booleanfield } from "./BooleanField.svelte" export { default as longformfield } from "./LongFormField.svelte" export { default as datetimefield } from "./DateTimeField.svelte" export { default as attachmentfield } from "./AttachmentField.svelte" +export { default as singleattachmentfield } from "./SingleAttachmentField.svelte" export { default as relationshipfield } from "./RelationshipField.svelte" export { default as passwordfield } from "./PasswordField.svelte" export { default as formstep } from "./FormStep.svelte" From 5df68cfca7703220abe383f60722f9c8d95a0c54 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 27 Mar 2024 10:04:12 +0100 Subject: [PATCH 59/97] Extract mapper --- .../app/forms/AttachmentField.svelte | 21 +++++++------------ .../app/forms/SingleAttachmentField.svelte | 12 ++++++++++- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/packages/client/src/components/app/forms/AttachmentField.svelte b/packages/client/src/components/app/forms/AttachmentField.svelte index eff0b8c408..7ce1d21998 100644 --- a/packages/client/src/components/app/forms/AttachmentField.svelte +++ b/packages/client/src/components/app/forms/AttachmentField.svelte @@ -16,18 +16,14 @@ export let span export let helpText = null export let type = FieldType.ATTACHMENT + export let fieldApiMapper = { + get: value => value, + set: value => value, + } let fieldState let fieldApi - $: isSingle = type === FieldType.ATTACHMENT_SINGLE - $: value = - isSingle && !Array.isArray(fieldState?.value) - ? fieldState?.value - ? [fieldState.value] - : [] - : fieldState?.value - const { API, notificationStore } = getContext("sdk") const formContext = getContext("form") const BYTES_IN_MB = 1000000 @@ -73,10 +69,7 @@ } const handleChange = e => { - let value = e.detail - if (isSingle) { - value = value[0] || null - } + const value = fieldApiMapper.set(e.detail) const changed = fieldApi.setValue(value) if (onChange && changed) { onChange({ value }) @@ -99,7 +92,7 @@ > {#if fieldState} diff --git a/packages/client/src/components/app/forms/SingleAttachmentField.svelte b/packages/client/src/components/app/forms/SingleAttachmentField.svelte index 4810f4e848..1eab255b9e 100644 --- a/packages/client/src/components/app/forms/SingleAttachmentField.svelte +++ b/packages/client/src/components/app/forms/SingleAttachmentField.svelte @@ -1,6 +1,16 @@ - + From b99a51d48b80bcce38e3669f331b0367e42a4dac Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 27 Mar 2024 10:10:01 +0100 Subject: [PATCH 60/97] Rename --- packages/client/manifest.json | 2 +- .../client/src/components/app/blocks/FormBlockComponent.svelte | 2 +- ...ingleAttachmentField.svelte => AttachmentSingleField.svelte} | 0 packages/client/src/components/app/forms/index.js | 2 +- 4 files changed, 3 insertions(+), 3 deletions(-) rename packages/client/src/components/app/forms/{SingleAttachmentField.svelte => AttachmentSingleField.svelte} (100%) diff --git a/packages/client/manifest.json b/packages/client/manifest.json index 11b38216fe..cc5c68ea83 100644 --- a/packages/client/manifest.json +++ b/packages/client/manifest.json @@ -4322,7 +4322,7 @@ } ] }, - "singleattachmentfield": { + "attachmentsinglefield": { "name": "Single Attachment", "icon": "Attach", "styles": ["size"], diff --git a/packages/client/src/components/app/blocks/FormBlockComponent.svelte b/packages/client/src/components/app/blocks/FormBlockComponent.svelte index c88b8f1499..1fc7cd3aba 100644 --- a/packages/client/src/components/app/blocks/FormBlockComponent.svelte +++ b/packages/client/src/components/app/blocks/FormBlockComponent.svelte @@ -16,7 +16,7 @@ [FieldType.LONGFORM]: "longformfield", [FieldType.DATETIME]: "datetimefield", [FieldType.ATTACHMENT]: "attachmentfield", - [FieldType.ATTACHMENT_SINGLE]: "singleattachmentfield", + [FieldType.ATTACHMENT_SINGLE]: "attachmentsinglefield", [FieldType.LINK]: "relationshipfield", [FieldType.JSON]: "jsonfield", [FieldType.BARCODEQR]: "codescanner", diff --git a/packages/client/src/components/app/forms/SingleAttachmentField.svelte b/packages/client/src/components/app/forms/AttachmentSingleField.svelte similarity index 100% rename from packages/client/src/components/app/forms/SingleAttachmentField.svelte rename to packages/client/src/components/app/forms/AttachmentSingleField.svelte diff --git a/packages/client/src/components/app/forms/index.js b/packages/client/src/components/app/forms/index.js index 5f0241c913..aa54204454 100644 --- a/packages/client/src/components/app/forms/index.js +++ b/packages/client/src/components/app/forms/index.js @@ -9,7 +9,7 @@ export { default as booleanfield } from "./BooleanField.svelte" export { default as longformfield } from "./LongFormField.svelte" export { default as datetimefield } from "./DateTimeField.svelte" export { default as attachmentfield } from "./AttachmentField.svelte" -export { default as singleattachmentfield } from "./SingleAttachmentField.svelte" +export { default as attachmentsinglefield } from "./AttachmentSingleField.svelte" export { default as relationshipfield } from "./RelationshipField.svelte" export { default as passwordfield } from "./PasswordField.svelte" export { default as formstep } from "./FormStep.svelte" From 39ac734edd705d8fc28be584606c8dc6431ba8a4 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 27 Mar 2024 10:21:26 +0100 Subject: [PATCH 61/97] Clean components --- .../grid/cells/AttachmentCell.svelte | 24 ++++--------------- .../grid/cells/AttachmentSingleCell.svelte | 20 ++++++++++++++++ .../src/components/grid/lib/renderers.js | 3 ++- 3 files changed, 27 insertions(+), 20 deletions(-) create mode 100644 packages/frontend-core/src/components/grid/cells/AttachmentSingleCell.svelte diff --git a/packages/frontend-core/src/components/grid/cells/AttachmentCell.svelte b/packages/frontend-core/src/components/grid/cells/AttachmentCell.svelte index 6ed6aa0b82..3a1f165b6e 100644 --- a/packages/frontend-core/src/components/grid/cells/AttachmentCell.svelte +++ b/packages/frontend-core/src/components/grid/cells/AttachmentCell.svelte @@ -1,7 +1,6 @@ + + diff --git a/packages/frontend-core/src/components/grid/lib/renderers.js b/packages/frontend-core/src/components/grid/lib/renderers.js index d4ac8b86ec..6d94d16c2d 100644 --- a/packages/frontend-core/src/components/grid/lib/renderers.js +++ b/packages/frontend-core/src/components/grid/lib/renderers.js @@ -11,6 +11,7 @@ import BooleanCell from "../cells/BooleanCell.svelte" import FormulaCell from "../cells/FormulaCell.svelte" import JSONCell from "../cells/JSONCell.svelte" import AttachmentCell from "../cells/AttachmentCell.svelte" +import AttachmentSingleCell from "../cells/AttachmentSingleCell.svelte" import BBReferenceCell from "../cells/BBReferenceCell.svelte" const TypeComponentMap = { @@ -23,7 +24,7 @@ const TypeComponentMap = { [FieldType.NUMBER]: NumberCell, [FieldType.BOOLEAN]: BooleanCell, [FieldType.ATTACHMENT]: AttachmentCell, - [FieldType.ATTACHMENT_SINGLE]: AttachmentCell, + [FieldType.ATTACHMENT_SINGLE]: AttachmentSingleCell, [FieldType.LINK]: RelationshipCell, [FieldType.FORMULA]: FormulaCell, [FieldType.JSON]: JSONCell, From 1992fbc5d25b32e959990cf3ed11e5e2dde379fd Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 27 Mar 2024 10:29:38 +0100 Subject: [PATCH 62/97] Fix mapping --- .../design/settings/controls/FieldConfiguration/utils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/builder/src/components/design/settings/controls/FieldConfiguration/utils.js b/packages/builder/src/components/design/settings/controls/FieldConfiguration/utils.js index 665ffffc63..eeda2ff991 100644 --- a/packages/builder/src/components/design/settings/controls/FieldConfiguration/utils.js +++ b/packages/builder/src/components/design/settings/controls/FieldConfiguration/utils.js @@ -42,7 +42,7 @@ export const FieldTypeToComponentMap = { [FieldType.LONGFORM]: "longformfield", [FieldType.DATETIME]: "datetimefield", [FieldType.ATTACHMENT]: "attachmentfield", - [FieldType.ATTACHMENT_SINGLE]: "attachmentfield", + [FieldType.ATTACHMENT_SINGLE]: "attachmentsinglefield", [FieldType.LINK]: "relationshipfield", [FieldType.JSON]: "jsonfield", [FieldType.BARCODEQR]: "codescanner", From a8c30110717d03108d5eab3e712b5ded1bda152e Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 3 Apr 2024 10:44:35 +0200 Subject: [PATCH 63/97] Rename FieldType.ATTACHMENT to FieldType.ATTACHMENTS --- .../builder/src/components/backend/DataTable/formula.js | 2 +- .../design/settings/controls/FieldConfiguration/utils.js | 2 +- packages/builder/src/constants/backend/index.js | 4 ++-- .../src/components/app/blocks/FormBlockComponent.svelte | 4 ++-- .../client/src/components/app/forms/AttachmentField.svelte | 2 +- packages/frontend-core/src/components/grid/lib/renderers.js | 2 +- packages/server/src/api/controllers/table/utils.ts | 2 +- packages/server/src/api/routes/tests/row.spec.ts | 4 ++-- packages/server/src/db/defaultData/datasource_bb_default.ts | 4 ++-- packages/server/src/sdk/app/rows/attachments.ts | 2 +- packages/server/src/sdk/app/rows/utils.ts | 4 ++-- packages/server/src/utilities/rowProcessor/attachments.ts | 6 +++--- packages/server/src/utilities/rowProcessor/index.ts | 4 ++-- packages/server/src/utilities/rowProcessor/map.ts | 2 +- .../src/utilities/rowProcessor/tests/attachments.spec.ts | 2 +- .../utilities/rowProcessor/tests/outputProcessing.spec.ts | 2 +- packages/shared-core/src/table.ts | 4 ++-- packages/types/src/documents/app/row.ts | 2 +- packages/types/src/documents/app/table/schema.ts | 6 +++--- 19 files changed, 30 insertions(+), 30 deletions(-) diff --git a/packages/builder/src/components/backend/DataTable/formula.js b/packages/builder/src/components/backend/DataTable/formula.js index e3da4249bc..b339729391 100644 --- a/packages/builder/src/components/backend/DataTable/formula.js +++ b/packages/builder/src/components/backend/DataTable/formula.js @@ -9,7 +9,7 @@ const MAX_DEPTH = 1 const TYPES_TO_SKIP = [ FieldType.FORMULA, FieldType.LONGFORM, - FieldType.ATTACHMENT, + FieldType.ATTACHMENTS, //https://github.com/Budibase/budibase/issues/3030 FieldType.INTERNAL, ] diff --git a/packages/builder/src/components/design/settings/controls/FieldConfiguration/utils.js b/packages/builder/src/components/design/settings/controls/FieldConfiguration/utils.js index eeda2ff991..d0f9afda40 100644 --- a/packages/builder/src/components/design/settings/controls/FieldConfiguration/utils.js +++ b/packages/builder/src/components/design/settings/controls/FieldConfiguration/utils.js @@ -41,7 +41,7 @@ export const FieldTypeToComponentMap = { [FieldType.BOOLEAN]: "booleanfield", [FieldType.LONGFORM]: "longformfield", [FieldType.DATETIME]: "datetimefield", - [FieldType.ATTACHMENT]: "attachmentfield", + [FieldType.ATTACHMENTS]: "attachmentfield", [FieldType.ATTACHMENT_SINGLE]: "attachmentsinglefield", [FieldType.LINK]: "relationshipfield", [FieldType.JSON]: "jsonfield", diff --git a/packages/builder/src/constants/backend/index.js b/packages/builder/src/constants/backend/index.js index 5b79443f79..d88637d670 100644 --- a/packages/builder/src/constants/backend/index.js +++ b/packages/builder/src/constants/backend/index.js @@ -109,7 +109,7 @@ export const FIELDS = { }, ATTACHMENT: { name: "Attachment List", - type: FieldType.ATTACHMENT, + type: FieldType.ATTACHMENTS, icon: "Folder", constraints: { type: "array", @@ -299,7 +299,7 @@ export const PaginationLocations = [ export const BannedSearchTypes = [ FieldType.LINK, - FieldType.ATTACHMENT, + FieldType.ATTACHMENTS, FieldType.FORMULA, FieldType.JSON, "jsonarray", diff --git a/packages/client/src/components/app/blocks/FormBlockComponent.svelte b/packages/client/src/components/app/blocks/FormBlockComponent.svelte index 1fc7cd3aba..968ed36b8b 100644 --- a/packages/client/src/components/app/blocks/FormBlockComponent.svelte +++ b/packages/client/src/components/app/blocks/FormBlockComponent.svelte @@ -15,7 +15,7 @@ [FieldType.BOOLEAN]: "booleanfield", [FieldType.LONGFORM]: "longformfield", [FieldType.DATETIME]: "datetimefield", - [FieldType.ATTACHMENT]: "attachmentfield", + [FieldType.ATTACHMENTS]: "attachmentfield", [FieldType.ATTACHMENT_SINGLE]: "attachmentsinglefield", [FieldType.LINK]: "relationshipfield", [FieldType.JSON]: "jsonfield", @@ -61,7 +61,7 @@ function getPropsByType(field) { const propsMapByType = { - [FieldType.ATTACHMENT]: (_field, schema) => { + [FieldType.ATTACHMENTS]: (_field, schema) => { return { maximum: schema?.constraints?.length?.maximum, } diff --git a/packages/client/src/components/app/forms/AttachmentField.svelte b/packages/client/src/components/app/forms/AttachmentField.svelte index 7ce1d21998..8be70bc011 100644 --- a/packages/client/src/components/app/forms/AttachmentField.svelte +++ b/packages/client/src/components/app/forms/AttachmentField.svelte @@ -15,7 +15,7 @@ export let maximum = undefined export let span export let helpText = null - export let type = FieldType.ATTACHMENT + export let type = FieldType.ATTACHMENTS export let fieldApiMapper = { get: value => value, set: value => value, diff --git a/packages/frontend-core/src/components/grid/lib/renderers.js b/packages/frontend-core/src/components/grid/lib/renderers.js index 6d94d16c2d..c3ee276ff9 100644 --- a/packages/frontend-core/src/components/grid/lib/renderers.js +++ b/packages/frontend-core/src/components/grid/lib/renderers.js @@ -23,7 +23,7 @@ const TypeComponentMap = { [FieldType.ARRAY]: MultiSelectCell, [FieldType.NUMBER]: NumberCell, [FieldType.BOOLEAN]: BooleanCell, - [FieldType.ATTACHMENT]: AttachmentCell, + [FieldType.ATTACHMENTS]: AttachmentCell, [FieldType.ATTACHMENT_SINGLE]: AttachmentSingleCell, [FieldType.LINK]: RelationshipCell, [FieldType.FORMULA]: FormulaCell, diff --git a/packages/server/src/api/controllers/table/utils.ts b/packages/server/src/api/controllers/table/utils.ts index 0c9933a4cf..1dea1a09f9 100644 --- a/packages/server/src/api/controllers/table/utils.ts +++ b/packages/server/src/api/controllers/table/utils.ts @@ -95,7 +95,7 @@ export async function checkForColumnUpdates( updatedTable.schema ).filter( (column): column is AttachmentFieldMetadata => - column.type === FieldType.ATTACHMENT && + column.type === FieldType.ATTACHMENTS && column.subtype !== oldTable?.schema[column.name]?.subtype ) for (const attachmentColumn of changedAttachmentSubtypeColumns) { diff --git a/packages/server/src/api/routes/tests/row.spec.ts b/packages/server/src/api/routes/tests/row.spec.ts index f638f2c4bf..77b64c6975 100644 --- a/packages/server/src/api/routes/tests/row.spec.ts +++ b/packages/server/src/api/routes/tests/row.spec.ts @@ -232,7 +232,7 @@ describe.each([ constraints: { type: "string", presence: false }, } const attachment: FieldSchema = { - type: FieldType.ATTACHMENT, + type: FieldType.ATTACHMENTS, name: "attachment", constraints: { type: "array", presence: false }, } @@ -791,7 +791,7 @@ describe.each([ defaultTable({ schema: { attachment: { - type: FieldType.ATTACHMENT, + type: FieldType.ATTACHMENTS, name: "attachment", constraints: { type: "array", presence: false }, }, diff --git a/packages/server/src/db/defaultData/datasource_bb_default.ts b/packages/server/src/db/defaultData/datasource_bb_default.ts index 03aed3c118..68d49b2d8b 100644 --- a/packages/server/src/db/defaultData/datasource_bb_default.ts +++ b/packages/server/src/db/defaultData/datasource_bb_default.ts @@ -299,7 +299,7 @@ export const DEFAULT_EMPLOYEE_TABLE_SCHEMA: Table = { sortable: false, }, "Badge Photo": { - type: FieldType.ATTACHMENT, + type: FieldType.ATTACHMENTS, constraints: { type: FieldType.ARRAY, presence: false, @@ -607,7 +607,7 @@ export const DEFAULT_EXPENSES_TABLE_SCHEMA: Table = { ignoreTimezones: true, }, Attachment: { - type: FieldType.ATTACHMENT, + type: FieldType.ATTACHMENTS, constraints: { type: FieldType.ARRAY, presence: false, diff --git a/packages/server/src/sdk/app/rows/attachments.ts b/packages/server/src/sdk/app/rows/attachments.ts index 2ab9e83c47..8fd2ccf795 100644 --- a/packages/server/src/sdk/app/rows/attachments.ts +++ b/packages/server/src/sdk/app/rows/attachments.ts @@ -30,7 +30,7 @@ export async function getRowsWithAttachments(appId: string, table: Table) { const db = dbCore.getDB(appId) const attachmentCols: string[] = [] for (let [key, column] of Object.entries(table.schema)) { - if (column.type === FieldType.ATTACHMENT) { + if (column.type === FieldType.ATTACHMENTS) { attachmentCols.push(key) } } diff --git a/packages/server/src/sdk/app/rows/utils.ts b/packages/server/src/sdk/app/rows/utils.ts index 8aa017d238..e1a0fbb5c4 100644 --- a/packages/server/src/sdk/app/rows/utils.ts +++ b/packages/server/src/sdk/app/rows/utils.ts @@ -175,13 +175,13 @@ export async function validate({ errors[fieldName] = [`${fieldName} is required`] } } else if ( - (type === FieldType.ATTACHMENT || type === FieldType.JSON) && + (type === FieldType.ATTACHMENTS || type === FieldType.JSON) && typeof row[fieldName] === "string" ) { // this should only happen if there is an error try { const json = JSON.parse(row[fieldName]) - if (type === FieldType.ATTACHMENT) { + if (type === FieldType.ATTACHMENTS) { if (Array.isArray(json)) { row[fieldName] = json } else { diff --git a/packages/server/src/utilities/rowProcessor/attachments.ts b/packages/server/src/utilities/rowProcessor/attachments.ts index e1c83352d4..652851a48b 100644 --- a/packages/server/src/utilities/rowProcessor/attachments.ts +++ b/packages/server/src/utilities/rowProcessor/attachments.ts @@ -34,7 +34,7 @@ export class AttachmentCleanup { let files: string[] = [] const tableSchema = opts.oldTable?.schema || table.schema for (let [key, schema] of Object.entries(tableSchema)) { - if (schema.type !== FieldType.ATTACHMENT) { + if (schema.type !== FieldType.ATTACHMENTS) { continue } const columnRemoved = opts.oldTable && !table.schema[key] @@ -68,7 +68,7 @@ export class AttachmentCleanup { return AttachmentCleanup.coreCleanup(() => { let files: string[] = [] for (let [key, schema] of Object.entries(table.schema)) { - if (schema.type !== FieldType.ATTACHMENT) { + if (schema.type !== FieldType.ATTACHMENTS) { continue } rows.forEach(row => { @@ -88,7 +88,7 @@ export class AttachmentCleanup { return AttachmentCleanup.coreCleanup(() => { let files: string[] = [] for (let [key, schema] of Object.entries(table.schema)) { - if (schema.type !== FieldType.ATTACHMENT) { + if (schema.type !== FieldType.ATTACHMENTS) { continue } const oldKeys = diff --git a/packages/server/src/utilities/rowProcessor/index.ts b/packages/server/src/utilities/rowProcessor/index.ts index 0015680e77..c421929888 100644 --- a/packages/server/src/utilities/rowProcessor/index.ts +++ b/packages/server/src/utilities/rowProcessor/index.ts @@ -148,7 +148,7 @@ export async function inputProcessing( } // remove any attachment urls, they are generated on read - if (field.type === FieldType.ATTACHMENT) { + if (field.type === FieldType.ATTACHMENTS) { const attachments = clonedRow[key] if (attachments?.length) { attachments.forEach((attachment: RowAttachment) => { @@ -216,7 +216,7 @@ export async function outputProcessing( // process complex types: attachements, bb references... for (let [property, column] of Object.entries(table.schema)) { - if (column.type === FieldType.ATTACHMENT) { + if (column.type === FieldType.ATTACHMENTS) { for (let row of enriched) { if (row[property] == null || !Array.isArray(row[property])) { continue diff --git a/packages/server/src/utilities/rowProcessor/map.ts b/packages/server/src/utilities/rowProcessor/map.ts index 60fe5a001b..2e0ac9efe1 100644 --- a/packages/server/src/utilities/rowProcessor/map.ts +++ b/packages/server/src/utilities/rowProcessor/map.ts @@ -106,7 +106,7 @@ export const TYPE_TRANSFORM_MAP: any = { return date }, }, - [FieldType.ATTACHMENT]: { + [FieldType.ATTACHMENTS]: { //@ts-ignore [null]: [], //@ts-ignore diff --git a/packages/server/src/utilities/rowProcessor/tests/attachments.spec.ts b/packages/server/src/utilities/rowProcessor/tests/attachments.spec.ts index cefea7e504..1b36a4cb81 100644 --- a/packages/server/src/utilities/rowProcessor/tests/attachments.spec.ts +++ b/packages/server/src/utilities/rowProcessor/tests/attachments.spec.ts @@ -34,7 +34,7 @@ function table(): Table { schema: { attach: { name: "attach", - type: FieldType.ATTACHMENT, + type: FieldType.ATTACHMENTS, constraints: {}, }, }, diff --git a/packages/server/src/utilities/rowProcessor/tests/outputProcessing.spec.ts b/packages/server/src/utilities/rowProcessor/tests/outputProcessing.spec.ts index a17bd5f393..93404e0469 100644 --- a/packages/server/src/utilities/rowProcessor/tests/outputProcessing.spec.ts +++ b/packages/server/src/utilities/rowProcessor/tests/outputProcessing.spec.ts @@ -82,7 +82,7 @@ describe("rowProcessor - outputProcessing", () => { sourceType: TableSourceType.INTERNAL, schema: { attach: { - type: FieldType.ATTACHMENT, + type: FieldType.ATTACHMENTS, name: "attach", constraints: {}, }, diff --git a/packages/shared-core/src/table.ts b/packages/shared-core/src/table.ts index e1f48ce176..26a7e77cd0 100644 --- a/packages/shared-core/src/table.ts +++ b/packages/shared-core/src/table.ts @@ -13,7 +13,7 @@ const allowDisplayColumnByType: Record = { [FieldType.BIGINT]: true, [FieldType.BOOLEAN]: false, [FieldType.ARRAY]: false, - [FieldType.ATTACHMENT]: false, + [FieldType.ATTACHMENTS]: false, [FieldType.ATTACHMENT_SINGLE]: false, [FieldType.LINK]: false, [FieldType.JSON]: false, @@ -34,7 +34,7 @@ const allowSortColumnByType: Record = { [FieldType.JSON]: true, [FieldType.FORMULA]: false, - [FieldType.ATTACHMENT]: false, + [FieldType.ATTACHMENTS]: false, [FieldType.ATTACHMENT_SINGLE]: false, [FieldType.ARRAY]: false, [FieldType.LINK]: false, diff --git a/packages/types/src/documents/app/row.ts b/packages/types/src/documents/app/row.ts index 5aab87fe6c..27daec1df1 100644 --- a/packages/types/src/documents/app/row.ts +++ b/packages/types/src/documents/app/row.ts @@ -8,7 +8,7 @@ export enum FieldType { BOOLEAN = "boolean", ARRAY = "array", DATETIME = "datetime", - ATTACHMENT = "attachment", + ATTACHMENTS = "attachment", ATTACHMENT_SINGLE = "attachment_single", LINK = "link", FORMULA = "formula", diff --git a/packages/types/src/documents/app/table/schema.ts b/packages/types/src/documents/app/table/schema.ts index 45e39268ac..946df6836e 100644 --- a/packages/types/src/documents/app/table/schema.ts +++ b/packages/types/src/documents/app/table/schema.ts @@ -114,7 +114,7 @@ export interface BBReferenceFieldMetadata export interface AttachmentFieldMetadata extends Omit { - type: FieldType.ATTACHMENT + type: FieldType.ATTACHMENTS subtype?: FieldSubtype.SINGLE } @@ -164,7 +164,7 @@ interface OtherFieldMetadata extends BaseFieldSchema { | FieldType.NUMBER | FieldType.LONGFORM | FieldType.BB_REFERENCE - | FieldType.ATTACHMENT + | FieldType.ATTACHMENTS > } @@ -217,5 +217,5 @@ export function isBBReferenceField( export function isAttachmentField( field: FieldSchema ): field is AttachmentFieldMetadata { - return field.type === FieldType.ATTACHMENT + return field.type === FieldType.ATTACHMENTS } From 757284d6c053b1b6958e89b8e0f762f013b5c49d Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 3 Apr 2024 10:58:52 +0200 Subject: [PATCH 64/97] Fix tsconfig --- packages/builder/tsconfig.json | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/builder/tsconfig.json b/packages/builder/tsconfig.json index be79dfd85c..a7a4c3d800 100644 --- a/packages/builder/tsconfig.json +++ b/packages/builder/tsconfig.json @@ -1,9 +1,6 @@ { "extends": "./tsconfig.build.json", "compilerOptions": { - "composite": true, - "declaration": true, - "sourceMap": true, "baseUrl": ".", "paths": { "assets/*": ["./assets/*"], From aabee4d90e199a5d45471c9a43e9643408c75eae Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 3 Apr 2024 11:02:25 +0200 Subject: [PATCH 65/97] Rename ATTACHMENT to ATTACHMENTS --- .../components/backend/DataTable/modals/CreateEditColumn.svelte | 2 +- packages/builder/src/constants/backend/index.js | 2 +- packages/client/src/components/app/forms/validation.js | 2 +- packages/server/src/api/controllers/table/utils.ts | 2 +- packages/types/src/documents/app/row.ts | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte b/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte index 2701c30359..615ea73f54 100644 --- a/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte +++ b/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte @@ -394,7 +394,7 @@ FIELDS.BIGINT, FIELDS.BOOLEAN, FIELDS.DATETIME, - FIELDS.ATTACHMENT, + FIELDS.ATTACHMENTS, FIELDS.ATTACHMENT_SINGLE, FIELDS.LINK, FIELDS.FORMULA, diff --git a/packages/builder/src/constants/backend/index.js b/packages/builder/src/constants/backend/index.js index d88637d670..bafecae074 100644 --- a/packages/builder/src/constants/backend/index.js +++ b/packages/builder/src/constants/backend/index.js @@ -107,7 +107,7 @@ export const FIELDS = { }, }, }, - ATTACHMENT: { + ATTACHMENTS: { name: "Attachment List", type: FieldType.ATTACHMENTS, icon: "Folder", diff --git a/packages/client/src/components/app/forms/validation.js b/packages/client/src/components/app/forms/validation.js index 3b3a5d6e1d..cdedd85cf2 100644 --- a/packages/client/src/components/app/forms/validation.js +++ b/packages/client/src/components/app/forms/validation.js @@ -192,7 +192,7 @@ const parseType = (value, type) => { } // Parse attachments, treating no elements as null - if (type === FieldTypes.ATTACHMENT) { + if (type === FieldTypes.ATTACHMENTS) { if (!Array.isArray(value) || !value.length) { return null } diff --git a/packages/server/src/api/controllers/table/utils.ts b/packages/server/src/api/controllers/table/utils.ts index 1dea1a09f9..0611a7ae0d 100644 --- a/packages/server/src/api/controllers/table/utils.ts +++ b/packages/server/src/api/controllers/table/utils.ts @@ -99,7 +99,7 @@ export async function checkForColumnUpdates( column.subtype !== oldTable?.schema[column.name]?.subtype ) for (const attachmentColumn of changedAttachmentSubtypeColumns) { - if (attachmentColumn.subtype === FieldTypeSubtypes.ATTACHMENT.SINGLE) { + if (attachmentColumn.subtype === FieldTypeSubtypes.ATTACHMENTS.SINGLE) { attachmentColumn.constraints ??= { length: {} } attachmentColumn.constraints.length ??= {} attachmentColumn.constraints.length.maximum = 1 diff --git a/packages/types/src/documents/app/row.ts b/packages/types/src/documents/app/row.ts index 27daec1df1..b17aececd7 100644 --- a/packages/types/src/documents/app/row.ts +++ b/packages/types/src/documents/app/row.ts @@ -48,7 +48,7 @@ export const FieldTypeSubtypes = { USER: FieldSubtype.USER as FieldSubtype.USER, USERS: FieldSubtype.USERS as FieldSubtype.USERS, }, - ATTACHMENT: { + ATTACHMENTS: { SINGLE: FieldSubtype.SINGLE as FieldSubtype.SINGLE, }, } From 32d11498d2cd164f46f7f64634c11b93da693cd9 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 3 Apr 2024 11:07:57 +0200 Subject: [PATCH 66/97] Change icons --- packages/builder/src/constants/backend/index.js | 10 +++++++++- .../frontend-core/src/components/grid/lib/utils.js | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/packages/builder/src/constants/backend/index.js b/packages/builder/src/constants/backend/index.js index bafecae074..e32cbbd0a4 100644 --- a/packages/builder/src/constants/backend/index.js +++ b/packages/builder/src/constants/backend/index.js @@ -110,12 +110,20 @@ export const FIELDS = { ATTACHMENTS: { name: "Attachment List", type: FieldType.ATTACHMENTS, - icon: "Folder", + icon: "AppleFiles", constraints: { type: "array", presence: false, }, }, + ATTACHMENT_SINGLE: { + name: "Attachment", + type: FieldType.ATTACHMENT_SINGLE, + icon: "Document", + constraints: { + presence: false, + }, + }, LINK: { name: "Relationship", type: FieldType.LINK, diff --git a/packages/frontend-core/src/components/grid/lib/utils.js b/packages/frontend-core/src/components/grid/lib/utils.js index 75578133a9..49d6b0a439 100644 --- a/packages/frontend-core/src/components/grid/lib/utils.js +++ b/packages/frontend-core/src/components/grid/lib/utils.js @@ -16,8 +16,8 @@ const TypeIconMap = { [FieldType.ARRAY]: "Dropdown", [FieldType.NUMBER]: "123", [FieldType.BOOLEAN]: "Boolean", - [FieldType.ATTACHMENT]: "AppleFiles", [FieldType.ATTACHMENTS]: "AppleFiles", + [FieldType.ATTACHMENT_SINGLE]: "Document", [FieldType.LINK]: "DataCorrelated", [FieldType.FORMULA]: "Calculator", [FieldType.JSON]: "Brackets", From 291ad618e385381337eda376419fa22512cb4b41 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 3 Apr 2024 11:09:05 +0200 Subject: [PATCH 67/97] Change order --- .../DataTable/modals/CreateEditColumn.svelte | 2 +- packages/builder/src/constants/backend/index.js | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte b/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte index 615ea73f54..92501bec3b 100644 --- a/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte +++ b/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte @@ -394,8 +394,8 @@ FIELDS.BIGINT, FIELDS.BOOLEAN, FIELDS.DATETIME, - FIELDS.ATTACHMENTS, FIELDS.ATTACHMENT_SINGLE, + FIELDS.ATTACHMENTS, FIELDS.LINK, FIELDS.FORMULA, FIELDS.JSON, diff --git a/packages/builder/src/constants/backend/index.js b/packages/builder/src/constants/backend/index.js index e32cbbd0a4..e3888a52e0 100644 --- a/packages/builder/src/constants/backend/index.js +++ b/packages/builder/src/constants/backend/index.js @@ -107,6 +107,14 @@ export const FIELDS = { }, }, }, + ATTACHMENT_SINGLE: { + name: "Attachment", + type: FieldType.ATTACHMENT_SINGLE, + icon: "Document", + constraints: { + presence: false, + }, + }, ATTACHMENTS: { name: "Attachment List", type: FieldType.ATTACHMENTS, @@ -116,14 +124,6 @@ export const FIELDS = { presence: false, }, }, - ATTACHMENT_SINGLE: { - name: "Attachment", - type: FieldType.ATTACHMENT_SINGLE, - icon: "Document", - constraints: { - presence: false, - }, - }, LINK: { name: "Relationship", type: FieldType.LINK, From 234c7feab2b117c4121c91773e3d3c567eecb0fb Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 3 Apr 2024 12:36:12 +0200 Subject: [PATCH 68/97] Remove single subtype from attachments --- .../server/src/api/controllers/table/utils.ts | 20 ------------------- packages/types/src/documents/app/row.ts | 4 ---- .../types/src/documents/app/table/schema.ts | 1 - 3 files changed, 25 deletions(-) diff --git a/packages/server/src/api/controllers/table/utils.ts b/packages/server/src/api/controllers/table/utils.ts index 0611a7ae0d..ac3a2a5c8a 100644 --- a/packages/server/src/api/controllers/table/utils.ts +++ b/packages/server/src/api/controllers/table/utils.ts @@ -91,26 +91,6 @@ export async function checkForColumnUpdates( await checkForViewUpdates(updatedTable, deletedColumns, columnRename) } - const changedAttachmentSubtypeColumns = Object.values( - updatedTable.schema - ).filter( - (column): column is AttachmentFieldMetadata => - column.type === FieldType.ATTACHMENTS && - column.subtype !== oldTable?.schema[column.name]?.subtype - ) - for (const attachmentColumn of changedAttachmentSubtypeColumns) { - if (attachmentColumn.subtype === FieldTypeSubtypes.ATTACHMENTS.SINGLE) { - attachmentColumn.constraints ??= { length: {} } - attachmentColumn.constraints.length ??= {} - attachmentColumn.constraints.length.maximum = 1 - attachmentColumn.constraints.length.message = - "cannot contain multiple files" - } else { - delete attachmentColumn.constraints?.length?.maximum - delete attachmentColumn.constraints?.length?.message - } - } - return { rows: updatedRows, table: updatedTable } } diff --git a/packages/types/src/documents/app/row.ts b/packages/types/src/documents/app/row.ts index b17aececd7..222c346591 100644 --- a/packages/types/src/documents/app/row.ts +++ b/packages/types/src/documents/app/row.ts @@ -39,7 +39,6 @@ export interface Row extends Document { export enum FieldSubtype { USER = "user", USERS = "users", - SINGLE = "single", } // The 'as' are required for typescript not to type the outputs as generic FieldSubtype @@ -48,7 +47,4 @@ export const FieldTypeSubtypes = { USER: FieldSubtype.USER as FieldSubtype.USER, USERS: FieldSubtype.USERS as FieldSubtype.USERS, }, - ATTACHMENTS: { - SINGLE: FieldSubtype.SINGLE as FieldSubtype.SINGLE, - }, } diff --git a/packages/types/src/documents/app/table/schema.ts b/packages/types/src/documents/app/table/schema.ts index 946df6836e..3eeeee0a81 100644 --- a/packages/types/src/documents/app/table/schema.ts +++ b/packages/types/src/documents/app/table/schema.ts @@ -115,7 +115,6 @@ export interface BBReferenceFieldMetadata export interface AttachmentFieldMetadata extends Omit { type: FieldType.ATTACHMENTS - subtype?: FieldSubtype.SINGLE } export interface FieldConstraints { From dae0c749cac55e3f95ddbb4ece35b2d9ba15483c Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 3 Apr 2024 12:39:24 +0200 Subject: [PATCH 69/97] Lint --- packages/server/src/api/controllers/table/utils.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/server/src/api/controllers/table/utils.ts b/packages/server/src/api/controllers/table/utils.ts index ac3a2a5c8a..5649a1d996 100644 --- a/packages/server/src/api/controllers/table/utils.ts +++ b/packages/server/src/api/controllers/table/utils.ts @@ -30,8 +30,6 @@ import { View, RelationshipFieldMetadata, FieldType, - FieldTypeSubtypes, - AttachmentFieldMetadata, } from "@budibase/types" export async function clearColumns(table: Table, columnNames: string[]) { From c764b0f22e7c7fe73f469f163565d577b99a3c4a Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Wed, 3 Apr 2024 11:55:38 +0100 Subject: [PATCH 70/97] Testcontainer debug logging in test-libraries. --- .github/workflows/budibase_ci.yml | 1 + packages/server/scripts/test.sh | 9 +++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/budibase_ci.yml b/.github/workflows/budibase_ci.yml index b72d6793af..2876d60f51 100644 --- a/.github/workflows/budibase_ci.yml +++ b/.github/workflows/budibase_ci.yml @@ -92,6 +92,7 @@ jobs: test-libraries: runs-on: ubuntu-latest env: + DEBUG: testcontainers,testcontainers:exec,testcontainers:build,testcontainers:pull REUSE_CONTAINERS: true CONTAINER_NAMESPACE: test-libraries steps: diff --git a/packages/server/scripts/test.sh b/packages/server/scripts/test.sh index 3100d1911f..48766026aa 100644 --- a/packages/server/scripts/test.sh +++ b/packages/server/scripts/test.sh @@ -1,13 +1,14 @@ #!/bin/bash set -e -export NODE_OPTIONS="--no-node-snapshot $NODE_OPTIONS" - if [[ -n $CI ]] then - echo "jest --coverage --maxWorkers=4 --forceExit --bail $@" - jest --coverage --maxWorkers=2 --forceExit --bail $@ + export NODE_OPTIONS="--max-old-space-size=4096 --no-node-snapshot $NODE_OPTIONS" + echo "jest --coverage --maxWorkers=4 --forceExit --workerIdleMemoryLimit=2000MB --bail $@" + jest --coverage --maxWorkers=4 --forceExit --workerIdleMemoryLimit=2000MB --bail $@ else + # --maxWorkers performs better in development + export NODE_OPTIONS="--no-node-snapshot $NODE_OPTIONS" echo "jest --coverage --maxWorkers=2 --forceExit $@" jest --coverage --maxWorkers=2 --forceExit $@ fi \ No newline at end of file From 2c28e074ed164df837829a62ac72adead61f99a9 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 3 Apr 2024 12:59:04 +0200 Subject: [PATCH 71/97] Fix types --- packages/types/src/documents/app/table/schema.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/types/src/documents/app/table/schema.ts b/packages/types/src/documents/app/table/schema.ts index 3eeeee0a81..86c34b6a5c 100644 --- a/packages/types/src/documents/app/table/schema.ts +++ b/packages/types/src/documents/app/table/schema.ts @@ -112,8 +112,7 @@ export interface BBReferenceFieldMetadata relationshipType?: RelationshipType } -export interface AttachmentFieldMetadata - extends Omit { +export interface AttachmentFieldMetadata extends BaseFieldSchema { type: FieldType.ATTACHMENTS } From dda0d1fb5be3f437443fafe45eeec4c3e78a5840 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Wed, 3 Apr 2024 12:06:49 +0100 Subject: [PATCH 72/97] Lock globalSetup to prevent multiple containers starting. --- .github/workflows/budibase_ci.yml | 20 +++++---- globalSetup.ts | 69 +++++++++++++++++++------------ package.json | 5 ++- yarn.lock | 12 ++++++ 4 files changed, 71 insertions(+), 35 deletions(-) diff --git a/.github/workflows/budibase_ci.yml b/.github/workflows/budibase_ci.yml index 2876d60f51..6dccaa9f46 100644 --- a/.github/workflows/budibase_ci.yml +++ b/.github/workflows/budibase_ci.yml @@ -108,6 +108,10 @@ jobs: with: node-version: 20.x cache: yarn + - name: Pull testcontainers images + run: | + docker pull budibase/couchdb + - run: yarn --frozen-lockfile - name: Test run: | @@ -163,13 +167,15 @@ jobs: - name: Pull testcontainers images run: | - docker pull mcr.microsoft.com/mssql/server:2022-latest - docker pull mysql:8.3 - docker pull postgres:16.1-bullseye - docker pull mongo:7.0-jammy - docker pull mariadb:lts - docker pull testcontainers/ryuk:0.5.1 - docker pull budibase/couchdb + docker pull mcr.microsoft.com/mssql/server:2022-latest & + docker pull mysql:8.3 & + docker pull postgres:16.1-bullseye & + docker pull mongo:7.0-jammy & + docker pull mariadb:lts & + docker pull testcontainers/ryuk:0.5.1 & + docker pull budibase/couchdb & + + wait $(jobs -p) - run: yarn --frozen-lockfile diff --git a/globalSetup.ts b/globalSetup.ts index a1f66d4fc8..cc3ec6bb24 100644 --- a/globalSetup.ts +++ b/globalSetup.ts @@ -1,36 +1,51 @@ import { GenericContainer, Wait } from "testcontainers" +import lockfile from "proper-lockfile" export default async function setup() { - let couchdb = new GenericContainer("budibase/couchdb") - .withExposedPorts(5984) - .withEnvironment({ - COUCHDB_PASSWORD: "budibase", - COUCHDB_USER: "budibase", - }) - .withCopyContentToContainer([ - { - content: ` + if (process.env.REUSE_CONTAINERS) { + // If you run multiple tests at the same time, it's possible for the CouchDB + // shared container to get started multiple times despite having an + // identical reuse hash. To avoid that, we do a filesystem-based lock so + // that only one globalSetup.ts is running at a time. + lockfile.lockSync("globalSetup.lock") + } + + try { + let couchdb = new GenericContainer("budibase/couchdb") + .withExposedPorts(5984) + .withEnvironment({ + COUCHDB_PASSWORD: "budibase", + COUCHDB_USER: "budibase", + }) + .withCopyContentToContainer([ + { + content: ` [log] level = warn `, - target: "/opt/couchdb/etc/local.d/test-couchdb.ini", - }, - ]) - .withWaitStrategy( - Wait.forSuccessfulCommand( - "curl http://budibase:budibase@localhost:5984/_up" - ).withStartupTimeout(20000) - ) + target: "/opt/couchdb/etc/local.d/test-couchdb.ini", + }, + ]) + .withWaitStrategy( + Wait.forSuccessfulCommand( + "curl http://budibase:budibase@localhost:5984/_up" + ).withStartupTimeout(20000) + ) - if (process.env.REUSE_CONTAINERS) { - couchdb = couchdb.withReuse() + if (process.env.REUSE_CONTAINERS) { + couchdb = couchdb.withReuse() + } + + if (process.env.CONTAINER_NAMESPACE) { + couchdb = couchdb.withLabels({ + "org.testcontainers.namespace": process.env.CONTAINER_NAMESPACE, + }) + } + + await couchdb.start() + } finally { + if (process.env.REUSE_CONTAINERS) { + lockfile.unlockSync("globalSetup.lock") + } } - - if (process.env.CONTAINER_NAMESPACE) { - couchdb = couchdb.withLabels({ - "org.testcontainers.namespace": process.env.CONTAINER_NAMESPACE, - }) - } - - await couchdb.start() } diff --git a/package.json b/package.json index c927002c88..90675ce8c8 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ "@babel/preset-env": "^7.22.5", "@esbuild-plugins/tsconfig-paths": "^0.1.2", "@types/node": "20.10.0", + "@types/proper-lockfile": "^4.1.4", "@typescript-eslint/parser": "6.9.0", "esbuild": "^0.18.17", "esbuild-node-externals": "^1.8.0", @@ -116,5 +117,7 @@ "engines": { "node": ">=20.0.0 <21.0.0" }, - "dependencies": {} + "dependencies": { + "proper-lockfile": "^4.1.2" + } } diff --git a/yarn.lock b/yarn.lock index 6acdcce3b6..516ec66c30 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5875,6 +5875,13 @@ "@types/pouchdb-node" "*" "@types/pouchdb-replication" "*" +"@types/proper-lockfile@^4.1.4": + version "4.1.4" + resolved "https://registry.yarnpkg.com/@types/proper-lockfile/-/proper-lockfile-4.1.4.tgz#cd9fab92bdb04730c1ada542c356f03620f84008" + integrity sha512-uo2ABllncSqg9F1D4nugVl9v93RmjxF6LJzQLMLDdPaXCUIDPeOJ21Gbqi43xNKzBi/WQ0Q0dICqufzQbMjipQ== + dependencies: + "@types/retry" "*" + "@types/qs@*": version "6.9.7" resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb" @@ -5937,6 +5944,11 @@ dependencies: "@types/node" "*" +"@types/retry@*": + version "0.12.5" + resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.5.tgz#f090ff4bd8d2e5b940ff270ab39fd5ca1834a07e" + integrity sha512-3xSjTp3v03X/lSQLkczaN9UIEwJMoMCA1+Nb5HfbJEQWogdeQIyVtTvxPXDQjZ5zws8rFQfVfRdz03ARihPJgw== + "@types/rimraf@^3.0.2": version "3.0.2" resolved "https://registry.yarnpkg.com/@types/rimraf/-/rimraf-3.0.2.tgz#a63d175b331748e5220ad48c901d7bbf1f44eef8" From b25912bc5edfa7ef0a414656aa255ebdc25eab0b Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Wed, 3 Apr 2024 12:11:28 +0100 Subject: [PATCH 73/97] Correct the lock path. --- globalSetup.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/globalSetup.ts b/globalSetup.ts index cc3ec6bb24..0d0ec25bf8 100644 --- a/globalSetup.ts +++ b/globalSetup.ts @@ -1,13 +1,15 @@ import { GenericContainer, Wait } from "testcontainers" +import path from "path" import lockfile from "proper-lockfile" export default async function setup() { + const lockPath = path.resolve(__dirname, "globalSetup.ts") if (process.env.REUSE_CONTAINERS) { // If you run multiple tests at the same time, it's possible for the CouchDB // shared container to get started multiple times despite having an // identical reuse hash. To avoid that, we do a filesystem-based lock so // that only one globalSetup.ts is running at a time. - lockfile.lockSync("globalSetup.lock") + lockfile.lockSync(lockPath) } try { @@ -45,7 +47,7 @@ export default async function setup() { await couchdb.start() } finally { if (process.env.REUSE_CONTAINERS) { - lockfile.unlockSync("globalSetup.lock") + lockfile.unlockSync(lockPath) } } } From 04aa53c3068e806f29728c0e338c4eec34e3a77e Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Wed, 3 Apr 2024 12:22:11 +0100 Subject: [PATCH 74/97] Trying with 8 workers. --- .github/workflows/budibase_ci.yml | 7 ++++++- packages/server/scripts/test.sh | 4 ++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/.github/workflows/budibase_ci.yml b/.github/workflows/budibase_ci.yml index 6dccaa9f46..38874b48fd 100644 --- a/.github/workflows/budibase_ci.yml +++ b/.github/workflows/budibase_ci.yml @@ -110,7 +110,11 @@ jobs: cache: yarn - name: Pull testcontainers images run: | - docker pull budibase/couchdb + docker pull testcontainers/ryuk:0.5.1 & + docker pull budibase/couchdb & + docker pull redis & + + wait $(jobs -p) - run: yarn --frozen-lockfile - name: Test @@ -174,6 +178,7 @@ jobs: docker pull mariadb:lts & docker pull testcontainers/ryuk:0.5.1 & docker pull budibase/couchdb & + docker pull redis & wait $(jobs -p) diff --git a/packages/server/scripts/test.sh b/packages/server/scripts/test.sh index 48766026aa..a147843bb3 100644 --- a/packages/server/scripts/test.sh +++ b/packages/server/scripts/test.sh @@ -4,8 +4,8 @@ set -e if [[ -n $CI ]] then export NODE_OPTIONS="--max-old-space-size=4096 --no-node-snapshot $NODE_OPTIONS" - echo "jest --coverage --maxWorkers=4 --forceExit --workerIdleMemoryLimit=2000MB --bail $@" - jest --coverage --maxWorkers=4 --forceExit --workerIdleMemoryLimit=2000MB --bail $@ + echo "jest --coverage --maxWorkers=8 --forceExit --workerIdleMemoryLimit=2000MB --bail $@" + jest --coverage --maxWorkers=8 --forceExit --workerIdleMemoryLimit=2000MB --bail $@ else # --maxWorkers performs better in development export NODE_OPTIONS="--no-node-snapshot $NODE_OPTIONS" From b98e80f6873fd3f0330c834d0aacc8188755a128 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Wed, 3 Apr 2024 12:28:00 +0100 Subject: [PATCH 75/97] Back down to 4. --- packages/server/scripts/test.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/server/scripts/test.sh b/packages/server/scripts/test.sh index a147843bb3..48766026aa 100644 --- a/packages/server/scripts/test.sh +++ b/packages/server/scripts/test.sh @@ -4,8 +4,8 @@ set -e if [[ -n $CI ]] then export NODE_OPTIONS="--max-old-space-size=4096 --no-node-snapshot $NODE_OPTIONS" - echo "jest --coverage --maxWorkers=8 --forceExit --workerIdleMemoryLimit=2000MB --bail $@" - jest --coverage --maxWorkers=8 --forceExit --workerIdleMemoryLimit=2000MB --bail $@ + echo "jest --coverage --maxWorkers=4 --forceExit --workerIdleMemoryLimit=2000MB --bail $@" + jest --coverage --maxWorkers=4 --forceExit --workerIdleMemoryLimit=2000MB --bail $@ else # --maxWorkers performs better in development export NODE_OPTIONS="--no-node-snapshot $NODE_OPTIONS" From 79184e70aff2f42f42294ef8cccdbe3aa33eafb6 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Wed, 3 Apr 2024 12:32:29 +0100 Subject: [PATCH 76/97] Remove CONTAINER_NAMESPACE, it wasn't the solution. --- .github/workflows/budibase_ci.yml | 2 -- globalSetup.ts | 6 ------ .../tests/core/utilities/testContainerUtils.ts | 12 +----------- .../server/src/integrations/tests/utils/index.ts | 6 ------ 4 files changed, 1 insertion(+), 25 deletions(-) diff --git a/.github/workflows/budibase_ci.yml b/.github/workflows/budibase_ci.yml index 38874b48fd..42d73ba8bb 100644 --- a/.github/workflows/budibase_ci.yml +++ b/.github/workflows/budibase_ci.yml @@ -94,7 +94,6 @@ jobs: env: DEBUG: testcontainers,testcontainers:exec,testcontainers:build,testcontainers:pull REUSE_CONTAINERS: true - CONTAINER_NAMESPACE: test-libraries steps: - name: Checkout repo uses: actions/checkout@v4 @@ -154,7 +153,6 @@ jobs: env: DEBUG: testcontainers,testcontainers:exec,testcontainers:build,testcontainers:pull REUSE_CONTAINERS: true - CONTAINER_NAMESPACE: test-server steps: - name: Checkout repo uses: actions/checkout@v4 diff --git a/globalSetup.ts b/globalSetup.ts index 0d0ec25bf8..7bf5e2152c 100644 --- a/globalSetup.ts +++ b/globalSetup.ts @@ -38,12 +38,6 @@ export default async function setup() { couchdb = couchdb.withReuse() } - if (process.env.CONTAINER_NAMESPACE) { - couchdb = couchdb.withLabels({ - "org.testcontainers.namespace": process.env.CONTAINER_NAMESPACE, - }) - } - await couchdb.start() } finally { if (process.env.REUSE_CONTAINERS) { diff --git a/packages/backend-core/tests/core/utilities/testContainerUtils.ts b/packages/backend-core/tests/core/utilities/testContainerUtils.ts index eeb9d7e6d3..951a6f0517 100644 --- a/packages/backend-core/tests/core/utilities/testContainerUtils.ts +++ b/packages/backend-core/tests/core/utilities/testContainerUtils.ts @@ -23,22 +23,12 @@ function getTestcontainers(): ContainerInfo[] { // We use --format json to make sure the output is nice and machine-readable, // and we use --no-trunc so that the command returns full container IDs so we // can filter on them correctly. - let containers = execSync("docker ps --format json --no-trunc") + return execSync("docker ps --format json --no-trunc") .toString() .split("\n") .filter(x => x.length > 0) .map(x => JSON.parse(x) as ContainerInfo) .filter(x => x.Labels.includes("org.testcontainers=true")) - - if (process.env.CONTAINER_NAMESPACE) { - containers = containers.filter(x => - x.Labels.includes( - `org.testcontainers.namespace=${process.env.CONTAINER_NAMESPACE}` - ) - ) - } - - return containers } export function getContainerByImage(image: string) { diff --git a/packages/server/src/integrations/tests/utils/index.ts b/packages/server/src/integrations/tests/utils/index.ts index 3286f9a3b8..bbdb41b38a 100644 --- a/packages/server/src/integrations/tests/utils/index.ts +++ b/packages/server/src/integrations/tests/utils/index.ts @@ -67,12 +67,6 @@ export async function rawQuery(ds: Datasource, sql: string): Promise { } export async function startContainer(container: GenericContainer) { - if (process.env.CONTAINER_NAMESPACE) { - container = container.withLabels({ - "org.testcontainers.namespace": process.env.CONTAINER_NAMESPACE, - }) - } - if (process.env.REUSE_CONTAINERS) { container = container.withReuse() } From 0881f3450840645a4bb3a81d0e4740694a93977a Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Wed, 3 Apr 2024 12:46:05 +0100 Subject: [PATCH 77/97] Move proper-lockfile to devDependencies. --- package.json | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 90675ce8c8..4b6716f7e7 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "nx-cloud": "16.0.5", "prettier": "2.8.8", "prettier-plugin-svelte": "^2.3.0", + "proper-lockfile": "^4.1.2", "svelte": "^4.2.10", "svelte-eslint-parser": "^0.33.1", "typescript": "5.2.2", @@ -117,7 +118,5 @@ "engines": { "node": ">=20.0.0 <21.0.0" }, - "dependencies": { - "proper-lockfile": "^4.1.2" - } + "dependencies": {} } From db877b7802ded722419b53ecb2d4bca006aadf5f Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Wed, 3 Apr 2024 12:55:44 +0100 Subject: [PATCH 78/97] Create unique MongoDB databases. --- .../src/api/routes/tests/queries/mongodb.spec.ts | 16 +++++++++------- .../src/integrations/tests/utils/mongodb.ts | 4 ++-- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/packages/server/src/api/routes/tests/queries/mongodb.spec.ts b/packages/server/src/api/routes/tests/queries/mongodb.spec.ts index 148f2c15ec..bdcfd85437 100644 --- a/packages/server/src/api/routes/tests/queries/mongodb.spec.ts +++ b/packages/server/src/api/routes/tests/queries/mongodb.spec.ts @@ -4,7 +4,7 @@ import { DatabaseName, getDatasource, } from "../../../../integrations/tests/utils" -import { MongoClient, type Collection, BSON } from "mongodb" +import { MongoClient, type Collection, BSON, Db } from "mongodb" import { generator } from "@budibase/backend-core/tests" const expectValidId = expect.stringMatching(/^\w{24}$/) @@ -40,8 +40,7 @@ describe("/queries", () => { async function withClient( callback: (client: MongoClient) => Promise ): Promise { - const ds = await getDatasource(DatabaseName.MONGODB) - const client = new MongoClient(ds.config!.connectionString) + const client = new MongoClient(datasource.config!.connectionString) await client.connect() try { return await callback(client) @@ -50,13 +49,16 @@ describe("/queries", () => { } } + async function withDb(callback: (db: Db) => Promise): Promise { + return await withClient(async client => { + return await callback(client.db(datasource.config!.db)) + }) + } + async function withCollection( callback: (collection: Collection) => Promise ): Promise { - return await withClient(async client => { - const db = client.db( - (await getDatasource(DatabaseName.MONGODB)).config!.db - ) + return await withDb(async db => { return await callback(db.collection(collection)) }) } diff --git a/packages/server/src/integrations/tests/utils/mongodb.ts b/packages/server/src/integrations/tests/utils/mongodb.ts index c5c0340dc9..0bdbb2808c 100644 --- a/packages/server/src/integrations/tests/utils/mongodb.ts +++ b/packages/server/src/integrations/tests/utils/mongodb.ts @@ -1,4 +1,4 @@ -import { testContainerUtils } from "@budibase/backend-core/tests" +import { generator, testContainerUtils } from "@budibase/backend-core/tests" import { Datasource, SourceName } from "@budibase/types" import { GenericContainer, Wait } from "testcontainers" import { startContainer } from "." @@ -33,7 +33,7 @@ export async function getDatasource(): Promise { plus: false, config: { connectionString: `mongodb://mongo:password@127.0.0.1:${port.host}`, - db: "mongo", + db: generator.guid(), }, } } From dc91aba52c8b030cb48c3a841a052b76c65688a9 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 3 Apr 2024 15:37:00 +0200 Subject: [PATCH 79/97] authStore undefined check --- .../client/src/components/devtools/DevToolsStatsTab.svelte | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/client/src/components/devtools/DevToolsStatsTab.svelte b/packages/client/src/components/devtools/DevToolsStatsTab.svelte index 24f587332c..bc0b1a562b 100644 --- a/packages/client/src/components/devtools/DevToolsStatsTab.svelte +++ b/packages/client/src/components/devtools/DevToolsStatsTab.svelte @@ -23,6 +23,6 @@ label="Components" value={$componentStore.mountedComponentCount} /> - - + + From 110e20c0cee0660ccfb62a32448e6cc548bce1f6 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Wed, 3 Apr 2024 15:30:15 +0100 Subject: [PATCH 80/97] Update submodules. --- packages/account-portal | 2 +- packages/pro | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/account-portal b/packages/account-portal index f5b467b6b1..c51458d26f 160000 --- a/packages/account-portal +++ b/packages/account-portal @@ -1 +1 @@ -Subproject commit f5b467b6b1c55c48847545db41be7b1c035e167a +Subproject commit c51458d26f6bded1984ba9ac5d2948480ac809ce diff --git a/packages/pro b/packages/pro index dd748e045f..f8e8f87bd5 160000 --- a/packages/pro +++ b/packages/pro @@ -1 +1 @@ -Subproject commit dd748e045ffdbc6662c5d2b76075f01d65a96a2f +Subproject commit f8e8f87bd52081e1303a5ae92c432ea5b38f3bb4 From 193d40f86cdc87f9a87f123a73e9bc7454b9df80 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Wed, 3 Apr 2024 15:40:10 +0100 Subject: [PATCH 81/97] Fix some tests. --- .../routes/tests/queries/query.seq.spec.ts | 774 ------------------ .../automations/tests/executeQuery.spec.ts | 18 +- 2 files changed, 10 insertions(+), 782 deletions(-) delete mode 100644 packages/server/src/api/routes/tests/queries/query.seq.spec.ts diff --git a/packages/server/src/api/routes/tests/queries/query.seq.spec.ts b/packages/server/src/api/routes/tests/queries/query.seq.spec.ts deleted file mode 100644 index 4c25a762b8..0000000000 --- a/packages/server/src/api/routes/tests/queries/query.seq.spec.ts +++ /dev/null @@ -1,774 +0,0 @@ -import tk from "timekeeper" - -const pg = require("pg") - -// Mock out postgres for this -jest.mock("pg") -jest.mock("node-fetch") - -// Mock isProdAppID to we can later mock the implementation and pretend we are -// using prod app IDs -jest.mock("@budibase/backend-core", () => { - const core = jest.requireActual("@budibase/backend-core") - return { - ...core, - db: { - ...core.db, - isProdAppID: jest.fn(), - }, - } -}) -import * as setup from "../utilities" -import { checkBuilderEndpoint } from "../utilities/TestFunctions" -import { checkCacheForDynamicVariable } from "../../../../threads/utils" - -const { basicQuery, basicDatasource } = setup.structures -import { events, db as dbCore } from "@budibase/backend-core" -import { - Datasource, - Query, - SourceName, - QueryPreview, - QueryParameter, -} from "@budibase/types" - -tk.freeze(Date.now()) - -const mockIsProdAppID = dbCore.isProdAppID as jest.MockedFunction< - typeof dbCore.isProdAppID -> - -describe("/queries", () => { - let request = setup.getRequest() - let config = setup.getConfig() - let datasource: Datasource & Required>, query: Query - - afterAll(setup.afterAll) - - const setupTest = async () => { - await config.init() - datasource = await config.createDatasource() - query = await config.createQuery() - } - - beforeAll(async () => { - await setupTest() - }) - - const createQuery = async (query: Query) => { - return request - .post(`/api/queries`) - .send(query) - .set(config.defaultHeaders()) - .expect("Content-Type", /json/) - .expect(200) - } - - describe("create", () => { - it("should create a new query", async () => { - const { _id } = await config.createDatasource() - const query = basicQuery(_id) - jest.clearAllMocks() - const res = await createQuery(query) - - expect((res as any).res.statusMessage).toEqual( - `Query ${query.name} saved successfully.` - ) - expect(res.body).toEqual({ - _rev: res.body._rev, - _id: res.body._id, - ...query, - nullDefaultSupport: true, - createdAt: new Date().toISOString(), - updatedAt: new Date().toISOString(), - }) - expect(events.query.created).toHaveBeenCalledTimes(1) - expect(events.query.updated).not.toHaveBeenCalled() - }) - }) - - describe("update", () => { - it("should update query", async () => { - const { _id } = await config.createDatasource() - const query = basicQuery(_id) - const res = await createQuery(query) - jest.clearAllMocks() - query._id = res.body._id - query._rev = res.body._rev - await createQuery(query) - - expect((res as any).res.statusMessage).toEqual( - `Query ${query.name} saved successfully.` - ) - expect(res.body).toEqual({ - _rev: res.body._rev, - _id: res.body._id, - ...query, - nullDefaultSupport: true, - createdAt: new Date().toISOString(), - updatedAt: new Date().toISOString(), - }) - expect(events.query.created).not.toHaveBeenCalled() - expect(events.query.updated).toHaveBeenCalledTimes(1) - }) - }) - - describe("fetch", () => { - beforeEach(async () => { - await setupTest() - }) - - it("returns all the queries from the server", async () => { - const res = await request - .get(`/api/queries`) - .set(config.defaultHeaders()) - .expect("Content-Type", /json/) - .expect(200) - - const queries = res.body - expect(queries).toEqual([ - { - _rev: query._rev, - _id: query._id, - createdAt: new Date().toISOString(), - ...basicQuery(datasource._id), - nullDefaultSupport: true, - updatedAt: new Date().toISOString(), - readable: true, - }, - ]) - }) - - it("should apply authorization to endpoint", async () => { - await checkBuilderEndpoint({ - config, - method: "GET", - url: `/api/datasources`, - }) - }) - }) - - describe("find", () => { - it("should find a query in builder", async () => { - const query = await config.createQuery() - const res = await request - .get(`/api/queries/${query._id}`) - .set(config.defaultHeaders()) - .expect("Content-Type", /json/) - .expect(200) - expect(res.body._id).toEqual(query._id) - }) - - it("should find a query in cloud", async () => { - await config.withEnv({ SELF_HOSTED: "true" }, async () => { - const query = await config.createQuery() - const res = await request - .get(`/api/queries/${query._id}`) - .set(await config.defaultHeaders()) - .expect(200) - .expect("Content-Type", /json/) - expect(res.body.fields).toBeDefined() - expect(res.body.parameters).toBeDefined() - expect(res.body.schema).toBeDefined() - }) - }) - - it("should remove sensitive info for prod apps", async () => { - // Mock isProdAppID to pretend we are using a prod app - mockIsProdAppID.mockClear() - mockIsProdAppID.mockImplementation(() => true) - - const query = await config.createQuery() - const res = await request - .get(`/api/queries/${query._id}`) - .set(await config.defaultHeaders()) - .expect("Content-Type", /json/) - .expect(200) - expect(res.body._id).toEqual(query._id) - expect(res.body.fields).toBeUndefined() - expect(res.body.parameters).toBeUndefined() - expect(res.body.schema).toBeDefined() - - // Reset isProdAppID mock - expect(dbCore.isProdAppID).toHaveBeenCalledTimes(1) - mockIsProdAppID.mockImplementation(() => false) - }) - }) - - describe("destroy", () => { - beforeEach(async () => { - await setupTest() - }) - - it("deletes a query and returns a success message", async () => { - await request - .delete(`/api/queries/${query._id}/${query._rev}`) - .set(config.defaultHeaders()) - .expect(200) - - const res = await request - .get(`/api/queries`) - .set(config.defaultHeaders()) - .expect("Content-Type", /json/) - .expect(200) - - expect(res.body).toEqual([]) - expect(events.query.deleted).toHaveBeenCalledTimes(1) - expect(events.query.deleted).toHaveBeenCalledWith(datasource, query) - }) - - it("should apply authorization to endpoint", async () => { - const query = await config.createQuery() - await checkBuilderEndpoint({ - config, - method: "DELETE", - url: `/api/queries/${query._id}/${query._rev}`, - }) - }) - }) - - describe("preview", () => { - it("should be able to preview the query", async () => { - const queryPreview: QueryPreview = { - datasourceId: datasource._id, - queryVerb: "read", - fields: {}, - parameters: [], - transformer: "return data", - name: datasource.name!, - schema: {}, - readable: true, - } - const responseBody = await config.api.query.previewQuery(queryPreview) - // these responses come from the mock - expect(responseBody.schema).toEqual({ - a: { type: "string", name: "a" }, - b: { type: "number", name: "b" }, - }) - expect(responseBody.rows.length).toEqual(1) - expect(events.query.previewed).toHaveBeenCalledTimes(1) - delete datasource.config - expect(events.query.previewed).toHaveBeenCalledWith(datasource, { - ...queryPreview, - nullDefaultSupport: true, - }) - }) - - it("should apply authorization to endpoint", async () => { - await checkBuilderEndpoint({ - config, - method: "POST", - url: `/api/queries/preview`, - }) - }) - - it("should not error when trying to generate a nested schema for an empty array", async () => { - const queryPreview: QueryPreview = { - datasourceId: datasource._id, - parameters: [], - fields: {}, - queryVerb: "read", - name: datasource.name!, - transformer: "return data", - schema: {}, - readable: true, - } - const rows = [ - { - contacts: [], - }, - ] - pg.queryMock.mockImplementation(() => ({ - rows, - })) - - const responseBody = await config.api.query.previewQuery(queryPreview) - expect(responseBody).toEqual({ - nestedSchemaFields: {}, - rows, - schema: { - contacts: { type: "array", name: "contacts" }, - }, - }) - expect(responseBody.rows.length).toEqual(1) - delete datasource.config - }) - - it("should generate a nested schema based on all the nested items", async () => { - const queryPreview: QueryPreview = { - datasourceId: datasource._id, - parameters: [], - fields: {}, - queryVerb: "read", - name: datasource.name!, - transformer: "return data", - schema: {}, - readable: true, - } - const rows = [ - { - contacts: [ - { - address: "123 Lane", - }, - { - address: "456 Drive", - }, - { - postcode: "BT1 12N", - lat: 54.59, - long: -5.92, - }, - { - city: "Belfast", - }, - { - address: "789 Avenue", - phoneNumber: "0800-999-5555", - }, - { - name: "Name", - isActive: false, - }, - ], - }, - ] - - pg.queryMock.mockImplementation(() => ({ - rows, - })) - - const responseBody = await config.api.query.previewQuery(queryPreview) - expect(responseBody).toEqual({ - nestedSchemaFields: { - contacts: { - address: { - type: "string", - name: "address", - }, - postcode: { - type: "string", - name: "postcode", - }, - lat: { - type: "number", - name: "lat", - }, - long: { - type: "number", - name: "long", - }, - city: { - type: "string", - name: "city", - }, - phoneNumber: { - type: "string", - name: "phoneNumber", - }, - name: { - type: "string", - name: "name", - }, - isActive: { - type: "boolean", - name: "isActive", - }, - }, - }, - rows, - schema: { - contacts: { type: "json", name: "contacts", subtype: "array" }, - }, - }) - expect(responseBody.rows.length).toEqual(1) - delete datasource.config - }) - }) - - describe("execute", () => { - beforeEach(async () => { - await setupTest() - }) - - it("should be able to execute the query", async () => { - const res = await request - .post(`/api/queries/${query._id}`) - .send({ - parameters: {}, - }) - .set(config.defaultHeaders()) - .expect("Content-Type", /json/) - .expect(200) - expect(res.body.length).toEqual(1) - }) - - it("should fail with invalid integration type", async () => { - const datasource: Datasource = { - ...basicDatasource().datasource, - source: "INVALID_INTEGRATION" as SourceName, - } - await config.api.datasource.create(datasource, { - status: 500, - body: { - message: "No datasource implementation found.", - }, - }) - }) - - it("shouldn't allow handlebars to be passed as parameters", async () => { - const res = await request - .post(`/api/queries/${query._id}`) - .send({ - parameters: { - a: "{{ 'test' }}", - }, - }) - .set(config.defaultHeaders()) - .expect(400) - expect(res.body.message).toEqual( - "Parameter 'a' input contains a handlebars binding - this is not allowed." - ) - }) - }) - - describe("variables", () => { - async function preview(datasource: Datasource, fields: any) { - const queryPreview: QueryPreview = { - datasourceId: datasource._id!, - parameters: [], - fields, - queryVerb: "read", - name: datasource.name!, - transformer: "return data", - schema: {}, - readable: true, - } - return await config.api.query.previewQuery(queryPreview) - } - - it("should work with static variables", async () => { - const datasource = await config.restDatasource({ - staticVariables: { - variable: "google", - variable2: "1", - }, - }) - const responseBody = await preview(datasource, { - path: "www.{{ variable }}.com", - queryString: "test={{ variable2 }}", - }) - // these responses come from the mock - expect(responseBody.schema).toEqual({ - opts: { type: "json", name: "opts" }, - url: { type: "string", name: "url" }, - value: { type: "string", name: "value" }, - }) - expect(responseBody.rows[0].url).toEqual("http://www.google.com?test=1") - }) - - it("should work with dynamic variables", async () => { - const { datasource } = await config.dynamicVariableDatasource() - const responseBody = await preview(datasource, { - path: "www.google.com", - queryString: "test={{ variable3 }}", - }) - expect(responseBody.schema).toEqual({ - opts: { type: "json", name: "opts" }, - url: { type: "string", name: "url" }, - value: { type: "string", name: "value" }, - }) - expect(responseBody.rows[0].url).toContain("doctype%20html") - }) - - it("check that it automatically retries on fail with cached dynamics", async () => { - const { datasource, query: base } = - await config.dynamicVariableDatasource() - // preview once to cache - await preview(datasource, { - path: "www.google.com", - queryString: "test={{ variable3 }}", - }) - // check its in cache - const contents = await checkCacheForDynamicVariable( - base._id!, - "variable3" - ) - expect(contents.rows.length).toEqual(1) - const responseBody = await preview(datasource, { - path: "www.failonce.com", - queryString: "test={{ variable3 }}", - }) - expect(responseBody.schema).toEqual({ - fails: { type: "number", name: "fails" }, - opts: { type: "json", name: "opts" }, - url: { type: "string", name: "url" }, - }) - expect(responseBody.rows[0].fails).toEqual(1) - }) - - it("deletes variables when linked query is deleted", async () => { - const { datasource, query: base } = - await config.dynamicVariableDatasource() - // preview once to cache - await preview(datasource, { - path: "www.google.com", - queryString: "test={{ variable3 }}", - }) - // check its in cache - let contents = await checkCacheForDynamicVariable(base._id!, "variable3") - expect(contents.rows.length).toEqual(1) - - // delete the query - await request - .delete(`/api/queries/${base._id}/${base._rev}`) - .set(config.defaultHeaders()) - .expect(200) - - // check variables no longer in cache - contents = await checkCacheForDynamicVariable(base._id!, "variable3") - expect(contents).toBe(null) - }) - }) - - describe("Current User Request Mapping", () => { - async function previewGet( - datasource: Datasource, - fields: any, - params: QueryParameter[] - ) { - const queryPreview: QueryPreview = { - datasourceId: datasource._id!, - parameters: params, - fields, - queryVerb: "read", - name: datasource.name!, - transformer: "return data", - schema: {}, - readable: true, - } - return await config.api.query.previewQuery(queryPreview) - } - - async function previewPost( - datasource: Datasource, - fields: any, - params: QueryParameter[] - ) { - const queryPreview: QueryPreview = { - datasourceId: datasource._id!, - parameters: params, - fields, - queryVerb: "create", - name: datasource.name!, - transformer: null, - schema: {}, - readable: false, - } - return await config.api.query.previewQuery(queryPreview) - } - - it("should parse global and query level header mappings", async () => { - const userDetails = config.getUserDetails() - - const datasource = await config.restDatasource({ - defaultHeaders: { - test: "headerVal", - emailHdr: "{{[user].[email]}}", - }, - }) - const responseBody = await previewGet( - datasource, - { - path: "www.google.com", - queryString: "email={{[user].[email]}}", - headers: { - queryHdr: "{{[user].[firstName]}}", - secondHdr: "1234", - }, - }, - [] - ) - - const parsedRequest = JSON.parse(responseBody.extra.raw) - expect(parsedRequest.opts.headers).toEqual({ - test: "headerVal", - emailHdr: userDetails.email, - queryHdr: userDetails.firstName, - secondHdr: "1234", - }) - expect(responseBody.rows[0].url).toEqual( - "http://www.google.com?email=" + userDetails.email.replace("@", "%40") - ) - }) - - it("should bind the current user to query parameters", async () => { - const userDetails = config.getUserDetails() - - const datasource = await config.restDatasource() - - const responseBody = await previewGet( - datasource, - { - path: "www.google.com", - queryString: - "test={{myEmail}}&testName={{myName}}&testParam={{testParam}}", - }, - [ - { name: "myEmail", default: "{{[user].[email]}}" }, - { name: "myName", default: "{{[user].[firstName]}}" }, - { name: "testParam", default: "1234" }, - ] - ) - - expect(responseBody.rows[0].url).toEqual( - "http://www.google.com?test=" + - userDetails.email.replace("@", "%40") + - "&testName=" + - userDetails.firstName + - "&testParam=1234" - ) - }) - - it("should bind the current user the request body - plain text", async () => { - const userDetails = config.getUserDetails() - const datasource = await config.restDatasource() - - const responseBody = await previewPost( - datasource, - { - path: "www.google.com", - queryString: "testParam={{testParam}}", - requestBody: - "This is plain text and this is my email: {{[user].[email]}}. This is a test param: {{testParam}}", - bodyType: "text", - }, - [{ name: "testParam", default: "1234" }] - ) - - const parsedRequest = JSON.parse(responseBody.extra.raw) - expect(parsedRequest.opts.body).toEqual( - `This is plain text and this is my email: ${userDetails.email}. This is a test param: 1234` - ) - expect(responseBody.rows[0].url).toEqual( - "http://www.google.com?testParam=1234" - ) - }) - - it("should bind the current user the request body - json", async () => { - const userDetails = config.getUserDetails() - const datasource = await config.restDatasource() - - const responseBody = await previewPost( - datasource, - { - path: "www.google.com", - queryString: "testParam={{testParam}}", - requestBody: - '{"email":"{{[user].[email]}}","queryCode":{{testParam}},"userRef":"{{userRef}}"}', - bodyType: "json", - }, - [ - { name: "testParam", default: "1234" }, - { name: "userRef", default: "{{[user].[firstName]}}" }, - ] - ) - - const parsedRequest = JSON.parse(responseBody.extra.raw) - const test = `{"email":"${userDetails.email}","queryCode":1234,"userRef":"${userDetails.firstName}"}` - expect(parsedRequest.opts.body).toEqual(test) - expect(responseBody.rows[0].url).toEqual( - "http://www.google.com?testParam=1234" - ) - }) - - it("should bind the current user the request body - xml", async () => { - const userDetails = config.getUserDetails() - const datasource = await config.restDatasource() - - const responseBody = await previewPost( - datasource, - { - path: "www.google.com", - queryString: "testParam={{testParam}}", - requestBody: - " {{[user].[email]}} {{testParam}} " + - "{{userId}} testing ", - bodyType: "xml", - }, - [ - { name: "testParam", default: "1234" }, - { name: "userId", default: "{{[user].[firstName]}}" }, - ] - ) - - const parsedRequest = JSON.parse(responseBody.extra.raw) - const test = ` ${userDetails.email} 1234 ${userDetails.firstName} testing ` - - expect(parsedRequest.opts.body).toEqual(test) - expect(responseBody.rows[0].url).toEqual( - "http://www.google.com?testParam=1234" - ) - }) - - it("should bind the current user the request body - form-data", async () => { - const userDetails = config.getUserDetails() - const datasource = await config.restDatasource() - - const responseBody = await previewPost( - datasource, - { - path: "www.google.com", - queryString: "testParam={{testParam}}", - requestBody: - '{"email":"{{[user].[email]}}","queryCode":{{testParam}},"userRef":"{{userRef}}"}', - bodyType: "form", - }, - [ - { name: "testParam", default: "1234" }, - { name: "userRef", default: "{{[user].[firstName]}}" }, - ] - ) - - const parsedRequest = JSON.parse(responseBody.extra.raw) - - const emailData = parsedRequest.opts.body._streams[1] - expect(emailData).toEqual(userDetails.email) - - const queryCodeData = parsedRequest.opts.body._streams[4] - expect(queryCodeData).toEqual("1234") - - const userRef = parsedRequest.opts.body._streams[7] - expect(userRef).toEqual(userDetails.firstName) - - expect(responseBody.rows[0].url).toEqual( - "http://www.google.com?testParam=1234" - ) - }) - - it("should bind the current user the request body - encoded", async () => { - const userDetails = config.getUserDetails() - const datasource = await config.restDatasource() - - const responseBody = await previewPost( - datasource, - { - path: "www.google.com", - queryString: "testParam={{testParam}}", - requestBody: - '{"email":"{{[user].[email]}}","queryCode":{{testParam}},"userRef":"{{userRef}}"}', - bodyType: "encoded", - }, - [ - { name: "testParam", default: "1234" }, - { name: "userRef", default: "{{[user].[firstName]}}" }, - ] - ) - const parsedRequest = JSON.parse(responseBody.extra.raw) - - expect(parsedRequest.opts.body.email).toEqual(userDetails.email) - expect(parsedRequest.opts.body.queryCode).toEqual("1234") - expect(parsedRequest.opts.body.userRef).toEqual(userDetails.firstName) - }) - }) -}) diff --git a/packages/server/src/automations/tests/executeQuery.spec.ts b/packages/server/src/automations/tests/executeQuery.spec.ts index 1d25a31b92..996e44af79 100644 --- a/packages/server/src/automations/tests/executeQuery.spec.ts +++ b/packages/server/src/automations/tests/executeQuery.spec.ts @@ -1,6 +1,6 @@ import { Datasource, Query, SourceName } from "@budibase/types" import * as setup from "./utilities" -import { databaseTestProviders } from "../../integrations/tests/utils" +import { DatabaseName, getDatasource } from "../../integrations/tests/utils" import knex, { Knex } from "knex" import { generator } from "@budibase/backend-core/tests" @@ -16,12 +16,14 @@ function getKnexClientName(source: SourceName) { throw new Error(`Unsupported source: ${source}`) } -describe.each([ - ["postgres", databaseTestProviders.postgres], - ["mysql", databaseTestProviders.mysql], - ["mssql", databaseTestProviders.mssql], - ["mariadb", databaseTestProviders.mariadb], -])("execute query action (%s)", (__, dsProvider) => { +describe.each( + [ + DatabaseName.POSTGRES, + DatabaseName.MYSQL, + DatabaseName.SQL_SERVER, + DatabaseName.MARIADB, + ].map(name => [name, getDatasource(name)]) +)("execute query action (%s)", (_, dsProvider) => { let tableName: string let client: Knex let datasource: Datasource @@ -31,7 +33,7 @@ describe.each([ beforeAll(async () => { await config.init() - const ds = await dsProvider.datasource() + const ds = await dsProvider datasource = await config.api.datasource.create(ds) client = knex({ client: getKnexClientName(ds.source), From 1cfe4da0278f7784d7a65eee3a77c3ab0c746eeb Mon Sep 17 00:00:00 2001 From: Michael Drury Date: Wed, 3 Apr 2024 16:01:45 +0100 Subject: [PATCH 82/97] Revert "Single attachment column type" --- packages/bbui/src/Form/Core/Dropzone.svelte | 2 +- .../components/backend/DataTable/formula.js | 2 +- .../DataTable/modals/CreateEditColumn.svelte | 3 +- .../controls/FieldConfiguration/utils.js | 3 +- .../builder/src/constants/backend/index.js | 16 +-- packages/builder/tsconfig.json | 3 + packages/client/manifest.json | 99 +------------------ .../app/blocks/FormBlockComponent.svelte | 5 +- .../app/forms/AttachmentField.svelte | 15 +-- .../app/forms/AttachmentSingleField.svelte | 16 --- .../client/src/components/app/forms/index.js | 1 - .../src/components/app/forms/validation.js | 2 +- .../grid/cells/AttachmentCell.svelte | 3 +- .../grid/cells/AttachmentSingleCell.svelte | 20 ---- .../src/components/grid/lib/renderers.js | 4 +- .../src/components/grid/lib/utils.js | 3 +- .../server/src/api/controllers/table/utils.ts | 22 +++++ .../server/src/api/routes/tests/row.spec.ts | 4 +- .../db/defaultData/datasource_bb_default.ts | 4 +- .../server/src/sdk/app/rows/attachments.ts | 2 +- packages/server/src/sdk/app/rows/utils.ts | 4 +- .../src/utilities/rowProcessor/attachments.ts | 6 +- .../src/utilities/rowProcessor/index.ts | 4 +- .../server/src/utilities/rowProcessor/map.ts | 2 +- .../rowProcessor/tests/attachments.spec.ts | 2 +- .../tests/outputProcessing.spec.ts | 2 +- packages/shared-core/src/table.ts | 7 +- packages/types/src/documents/app/row.ts | 7 +- .../types/src/documents/app/table/schema.ts | 10 +- 29 files changed, 73 insertions(+), 200 deletions(-) delete mode 100644 packages/client/src/components/app/forms/AttachmentSingleField.svelte delete mode 100644 packages/frontend-core/src/components/grid/cells/AttachmentSingleCell.svelte diff --git a/packages/bbui/src/Form/Core/Dropzone.svelte b/packages/bbui/src/Form/Core/Dropzone.svelte index dd72167791..3d803c0961 100644 --- a/packages/bbui/src/Form/Core/Dropzone.svelte +++ b/packages/bbui/src/Form/Core/Dropzone.svelte @@ -67,7 +67,7 @@ } $: showDropzone = - (!maximum || (maximum && (value?.length || 0) < maximum)) && !disabled + (!maximum || (maximum && value?.length < maximum)) && !disabled async function processFileList(fileList) { if ( diff --git a/packages/builder/src/components/backend/DataTable/formula.js b/packages/builder/src/components/backend/DataTable/formula.js index b339729391..e3da4249bc 100644 --- a/packages/builder/src/components/backend/DataTable/formula.js +++ b/packages/builder/src/components/backend/DataTable/formula.js @@ -9,7 +9,7 @@ const MAX_DEPTH = 1 const TYPES_TO_SKIP = [ FieldType.FORMULA, FieldType.LONGFORM, - FieldType.ATTACHMENTS, + FieldType.ATTACHMENT, //https://github.com/Budibase/budibase/issues/3030 FieldType.INTERNAL, ] diff --git a/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte b/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte index 92501bec3b..cfc6e9a7be 100644 --- a/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte +++ b/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte @@ -394,8 +394,7 @@ FIELDS.BIGINT, FIELDS.BOOLEAN, FIELDS.DATETIME, - FIELDS.ATTACHMENT_SINGLE, - FIELDS.ATTACHMENTS, + FIELDS.ATTACHMENT, FIELDS.LINK, FIELDS.FORMULA, FIELDS.JSON, diff --git a/packages/builder/src/components/design/settings/controls/FieldConfiguration/utils.js b/packages/builder/src/components/design/settings/controls/FieldConfiguration/utils.js index d0f9afda40..18ebf57d98 100644 --- a/packages/builder/src/components/design/settings/controls/FieldConfiguration/utils.js +++ b/packages/builder/src/components/design/settings/controls/FieldConfiguration/utils.js @@ -41,8 +41,7 @@ export const FieldTypeToComponentMap = { [FieldType.BOOLEAN]: "booleanfield", [FieldType.LONGFORM]: "longformfield", [FieldType.DATETIME]: "datetimefield", - [FieldType.ATTACHMENTS]: "attachmentfield", - [FieldType.ATTACHMENT_SINGLE]: "attachmentsinglefield", + [FieldType.ATTACHMENT]: "attachmentfield", [FieldType.LINK]: "relationshipfield", [FieldType.JSON]: "jsonfield", [FieldType.BARCODEQR]: "codescanner", diff --git a/packages/builder/src/constants/backend/index.js b/packages/builder/src/constants/backend/index.js index e3888a52e0..dd751d4e13 100644 --- a/packages/builder/src/constants/backend/index.js +++ b/packages/builder/src/constants/backend/index.js @@ -107,18 +107,10 @@ export const FIELDS = { }, }, }, - ATTACHMENT_SINGLE: { + ATTACHMENT: { name: "Attachment", - type: FieldType.ATTACHMENT_SINGLE, - icon: "Document", - constraints: { - presence: false, - }, - }, - ATTACHMENTS: { - name: "Attachment List", - type: FieldType.ATTACHMENTS, - icon: "AppleFiles", + type: FieldType.ATTACHMENT, + icon: "Folder", constraints: { type: "array", presence: false, @@ -307,7 +299,7 @@ export const PaginationLocations = [ export const BannedSearchTypes = [ FieldType.LINK, - FieldType.ATTACHMENTS, + FieldType.ATTACHMENT, FieldType.FORMULA, FieldType.JSON, "jsonarray", diff --git a/packages/builder/tsconfig.json b/packages/builder/tsconfig.json index a7a4c3d800..be79dfd85c 100644 --- a/packages/builder/tsconfig.json +++ b/packages/builder/tsconfig.json @@ -1,6 +1,9 @@ { "extends": "./tsconfig.build.json", "compilerOptions": { + "composite": true, + "declaration": true, + "sourceMap": true, "baseUrl": ".", "paths": { "assets/*": ["./assets/*"], diff --git a/packages/client/manifest.json b/packages/client/manifest.json index cc5c68ea83..08d614391b 100644 --- a/packages/client/manifest.json +++ b/packages/client/manifest.json @@ -4226,7 +4226,7 @@ ] }, "attachmentfield": { - "name": "Attachment list", + "name": "Attachment", "icon": "Attach", "styles": ["size"], "requiredAncestors": ["form"], @@ -4322,103 +4322,6 @@ } ] }, - "attachmentsinglefield": { - "name": "Single Attachment", - "icon": "Attach", - "styles": ["size"], - "requiredAncestors": ["form"], - "editable": true, - "size": { - "width": 400, - "height": 200 - }, - "settings": [ - { - "type": "field/attachment_single", - "label": "Field", - "key": "field", - "required": true - }, - { - "type": "text", - "label": "Label", - "key": "label" - }, - { - "type": "text", - "label": "Help text", - "key": "helpText" - }, - { - "type": "text", - "label": "Extensions", - "key": "extensions" - }, - { - "type": "number", - "label": "Max attachments", - "key": "maximum", - "min": 1 - }, - { - "type": "event", - "label": "On change", - "key": "onChange", - "context": [ - { - "label": "Field Value", - "key": "value" - } - ] - }, - { - "type": "boolean", - "label": "Compact", - "key": "compact", - "defaultValue": false - }, - { - "type": "boolean", - "label": "Read only", - "key": "disabled", - "defaultValue": false - }, - { - "type": "validation/attachment", - "label": "Validation", - "key": "validation" - }, - { - "type": "select", - "label": "Layout", - "key": "span", - "defaultValue": 6, - "hidden": true, - "showInBar": true, - "barStyle": "buttons", - "options": [ - { - "label": "1 column", - "value": 6, - "barIcon": "Stop", - "barTitle": "1 column" - }, - { - "label": "2 columns", - "value": 3, - "barIcon": "ColumnTwoA", - "barTitle": "2 columns" - }, - { - "label": "3 columns", - "value": 2, - "barIcon": "ViewColumn", - "barTitle": "3 columns" - } - ] - } - ] - }, "relationshipfield": { "name": "Relationship Picker", "icon": "TaskList", diff --git a/packages/client/src/components/app/blocks/FormBlockComponent.svelte b/packages/client/src/components/app/blocks/FormBlockComponent.svelte index 968ed36b8b..34168355c4 100644 --- a/packages/client/src/components/app/blocks/FormBlockComponent.svelte +++ b/packages/client/src/components/app/blocks/FormBlockComponent.svelte @@ -15,8 +15,7 @@ [FieldType.BOOLEAN]: "booleanfield", [FieldType.LONGFORM]: "longformfield", [FieldType.DATETIME]: "datetimefield", - [FieldType.ATTACHMENTS]: "attachmentfield", - [FieldType.ATTACHMENT_SINGLE]: "attachmentsinglefield", + [FieldType.ATTACHMENT]: "attachmentfield", [FieldType.LINK]: "relationshipfield", [FieldType.JSON]: "jsonfield", [FieldType.BARCODEQR]: "codescanner", @@ -61,7 +60,7 @@ function getPropsByType(field) { const propsMapByType = { - [FieldType.ATTACHMENTS]: (_field, schema) => { + [FieldType.ATTACHMENT]: (_field, schema) => { return { maximum: schema?.constraints?.length?.maximum, } diff --git a/packages/client/src/components/app/forms/AttachmentField.svelte b/packages/client/src/components/app/forms/AttachmentField.svelte index 8be70bc011..b61f88daeb 100644 --- a/packages/client/src/components/app/forms/AttachmentField.svelte +++ b/packages/client/src/components/app/forms/AttachmentField.svelte @@ -1,7 +1,6 @@ @@ -85,14 +78,14 @@ {validation} {span} {helpText} - {type} + type="attachment" bind:fieldState bind:fieldApi defaultValue={[]} > {#if fieldState} - import { FieldType } from "@budibase/types" - import AttachmentField from "./AttachmentField.svelte" - - const fieldApiMapper = { - get: value => (!Array.isArray(value) && value ? [value] : value) || [], - set: value => value[0] || null, - } - - - diff --git a/packages/client/src/components/app/forms/index.js b/packages/client/src/components/app/forms/index.js index aa54204454..5804d3a79d 100644 --- a/packages/client/src/components/app/forms/index.js +++ b/packages/client/src/components/app/forms/index.js @@ -9,7 +9,6 @@ export { default as booleanfield } from "./BooleanField.svelte" export { default as longformfield } from "./LongFormField.svelte" export { default as datetimefield } from "./DateTimeField.svelte" export { default as attachmentfield } from "./AttachmentField.svelte" -export { default as attachmentsinglefield } from "./AttachmentSingleField.svelte" export { default as relationshipfield } from "./RelationshipField.svelte" export { default as passwordfield } from "./PasswordField.svelte" export { default as formstep } from "./FormStep.svelte" diff --git a/packages/client/src/components/app/forms/validation.js b/packages/client/src/components/app/forms/validation.js index cdedd85cf2..3b3a5d6e1d 100644 --- a/packages/client/src/components/app/forms/validation.js +++ b/packages/client/src/components/app/forms/validation.js @@ -192,7 +192,7 @@ const parseType = (value, type) => { } // Parse attachments, treating no elements as null - if (type === FieldTypes.ATTACHMENTS) { + if (type === FieldTypes.ATTACHMENT) { if (!Array.isArray(value) || !value.length) { return null } diff --git a/packages/frontend-core/src/components/grid/cells/AttachmentCell.svelte b/packages/frontend-core/src/components/grid/cells/AttachmentCell.svelte index 3a1f165b6e..a1f5c4f2aa 100644 --- a/packages/frontend-core/src/components/grid/cells/AttachmentCell.svelte +++ b/packages/frontend-core/src/components/grid/cells/AttachmentCell.svelte @@ -10,7 +10,6 @@ export let invertX = false export let invertY = false export let schema - export let maximum const { API, notifications } = getContext("grid") const imageExtensions = ["png", "tiff", "gif", "raw", "jpg", "jpeg"] @@ -99,7 +98,7 @@ {value} compact on:change={e => onChange(e.detail)} - maximum={maximum || schema.constraints?.length?.maximum} + maximum={schema.constraints?.length?.maximum} {processFiles} {deleteAttachments} {handleFileTooLarge} diff --git a/packages/frontend-core/src/components/grid/cells/AttachmentSingleCell.svelte b/packages/frontend-core/src/components/grid/cells/AttachmentSingleCell.svelte deleted file mode 100644 index c2e29b6ede..0000000000 --- a/packages/frontend-core/src/components/grid/cells/AttachmentSingleCell.svelte +++ /dev/null @@ -1,20 +0,0 @@ - - - diff --git a/packages/frontend-core/src/components/grid/lib/renderers.js b/packages/frontend-core/src/components/grid/lib/renderers.js index c3ee276ff9..19bf63312d 100644 --- a/packages/frontend-core/src/components/grid/lib/renderers.js +++ b/packages/frontend-core/src/components/grid/lib/renderers.js @@ -11,7 +11,6 @@ import BooleanCell from "../cells/BooleanCell.svelte" import FormulaCell from "../cells/FormulaCell.svelte" import JSONCell from "../cells/JSONCell.svelte" import AttachmentCell from "../cells/AttachmentCell.svelte" -import AttachmentSingleCell from "../cells/AttachmentSingleCell.svelte" import BBReferenceCell from "../cells/BBReferenceCell.svelte" const TypeComponentMap = { @@ -23,8 +22,7 @@ const TypeComponentMap = { [FieldType.ARRAY]: MultiSelectCell, [FieldType.NUMBER]: NumberCell, [FieldType.BOOLEAN]: BooleanCell, - [FieldType.ATTACHMENTS]: AttachmentCell, - [FieldType.ATTACHMENT_SINGLE]: AttachmentSingleCell, + [FieldType.ATTACHMENT]: AttachmentCell, [FieldType.LINK]: RelationshipCell, [FieldType.FORMULA]: FormulaCell, [FieldType.JSON]: JSONCell, diff --git a/packages/frontend-core/src/components/grid/lib/utils.js b/packages/frontend-core/src/components/grid/lib/utils.js index 49d6b0a439..8382bfece8 100644 --- a/packages/frontend-core/src/components/grid/lib/utils.js +++ b/packages/frontend-core/src/components/grid/lib/utils.js @@ -16,8 +16,7 @@ const TypeIconMap = { [FieldType.ARRAY]: "Dropdown", [FieldType.NUMBER]: "123", [FieldType.BOOLEAN]: "Boolean", - [FieldType.ATTACHMENTS]: "AppleFiles", - [FieldType.ATTACHMENT_SINGLE]: "Document", + [FieldType.ATTACHMENT]: "AppleFiles", [FieldType.LINK]: "DataCorrelated", [FieldType.FORMULA]: "Calculator", [FieldType.JSON]: "Brackets", diff --git a/packages/server/src/api/controllers/table/utils.ts b/packages/server/src/api/controllers/table/utils.ts index 5649a1d996..0c9933a4cf 100644 --- a/packages/server/src/api/controllers/table/utils.ts +++ b/packages/server/src/api/controllers/table/utils.ts @@ -30,6 +30,8 @@ import { View, RelationshipFieldMetadata, FieldType, + FieldTypeSubtypes, + AttachmentFieldMetadata, } from "@budibase/types" export async function clearColumns(table: Table, columnNames: string[]) { @@ -89,6 +91,26 @@ export async function checkForColumnUpdates( await checkForViewUpdates(updatedTable, deletedColumns, columnRename) } + const changedAttachmentSubtypeColumns = Object.values( + updatedTable.schema + ).filter( + (column): column is AttachmentFieldMetadata => + column.type === FieldType.ATTACHMENT && + column.subtype !== oldTable?.schema[column.name]?.subtype + ) + for (const attachmentColumn of changedAttachmentSubtypeColumns) { + if (attachmentColumn.subtype === FieldTypeSubtypes.ATTACHMENT.SINGLE) { + attachmentColumn.constraints ??= { length: {} } + attachmentColumn.constraints.length ??= {} + attachmentColumn.constraints.length.maximum = 1 + attachmentColumn.constraints.length.message = + "cannot contain multiple files" + } else { + delete attachmentColumn.constraints?.length?.maximum + delete attachmentColumn.constraints?.length?.message + } + } + return { rows: updatedRows, table: updatedTable } } diff --git a/packages/server/src/api/routes/tests/row.spec.ts b/packages/server/src/api/routes/tests/row.spec.ts index 0fe8beb7ea..8910522565 100644 --- a/packages/server/src/api/routes/tests/row.spec.ts +++ b/packages/server/src/api/routes/tests/row.spec.ts @@ -234,7 +234,7 @@ describe.each([ constraints: { type: "string", presence: false }, } const attachment: FieldSchema = { - type: FieldType.ATTACHMENTS, + type: FieldType.ATTACHMENT, name: "attachment", constraints: { type: "array", presence: false }, } @@ -790,7 +790,7 @@ describe.each([ defaultTable({ schema: { attachment: { - type: FieldType.ATTACHMENTS, + type: FieldType.ATTACHMENT, name: "attachment", constraints: { type: "array", presence: false }, }, diff --git a/packages/server/src/db/defaultData/datasource_bb_default.ts b/packages/server/src/db/defaultData/datasource_bb_default.ts index 68d49b2d8b..03aed3c118 100644 --- a/packages/server/src/db/defaultData/datasource_bb_default.ts +++ b/packages/server/src/db/defaultData/datasource_bb_default.ts @@ -299,7 +299,7 @@ export const DEFAULT_EMPLOYEE_TABLE_SCHEMA: Table = { sortable: false, }, "Badge Photo": { - type: FieldType.ATTACHMENTS, + type: FieldType.ATTACHMENT, constraints: { type: FieldType.ARRAY, presence: false, @@ -607,7 +607,7 @@ export const DEFAULT_EXPENSES_TABLE_SCHEMA: Table = { ignoreTimezones: true, }, Attachment: { - type: FieldType.ATTACHMENTS, + type: FieldType.ATTACHMENT, constraints: { type: FieldType.ARRAY, presence: false, diff --git a/packages/server/src/sdk/app/rows/attachments.ts b/packages/server/src/sdk/app/rows/attachments.ts index 8fd2ccf795..2ab9e83c47 100644 --- a/packages/server/src/sdk/app/rows/attachments.ts +++ b/packages/server/src/sdk/app/rows/attachments.ts @@ -30,7 +30,7 @@ export async function getRowsWithAttachments(appId: string, table: Table) { const db = dbCore.getDB(appId) const attachmentCols: string[] = [] for (let [key, column] of Object.entries(table.schema)) { - if (column.type === FieldType.ATTACHMENTS) { + if (column.type === FieldType.ATTACHMENT) { attachmentCols.push(key) } } diff --git a/packages/server/src/sdk/app/rows/utils.ts b/packages/server/src/sdk/app/rows/utils.ts index e1a0fbb5c4..8aa017d238 100644 --- a/packages/server/src/sdk/app/rows/utils.ts +++ b/packages/server/src/sdk/app/rows/utils.ts @@ -175,13 +175,13 @@ export async function validate({ errors[fieldName] = [`${fieldName} is required`] } } else if ( - (type === FieldType.ATTACHMENTS || type === FieldType.JSON) && + (type === FieldType.ATTACHMENT || type === FieldType.JSON) && typeof row[fieldName] === "string" ) { // this should only happen if there is an error try { const json = JSON.parse(row[fieldName]) - if (type === FieldType.ATTACHMENTS) { + if (type === FieldType.ATTACHMENT) { if (Array.isArray(json)) { row[fieldName] = json } else { diff --git a/packages/server/src/utilities/rowProcessor/attachments.ts b/packages/server/src/utilities/rowProcessor/attachments.ts index 652851a48b..e1c83352d4 100644 --- a/packages/server/src/utilities/rowProcessor/attachments.ts +++ b/packages/server/src/utilities/rowProcessor/attachments.ts @@ -34,7 +34,7 @@ export class AttachmentCleanup { let files: string[] = [] const tableSchema = opts.oldTable?.schema || table.schema for (let [key, schema] of Object.entries(tableSchema)) { - if (schema.type !== FieldType.ATTACHMENTS) { + if (schema.type !== FieldType.ATTACHMENT) { continue } const columnRemoved = opts.oldTable && !table.schema[key] @@ -68,7 +68,7 @@ export class AttachmentCleanup { return AttachmentCleanup.coreCleanup(() => { let files: string[] = [] for (let [key, schema] of Object.entries(table.schema)) { - if (schema.type !== FieldType.ATTACHMENTS) { + if (schema.type !== FieldType.ATTACHMENT) { continue } rows.forEach(row => { @@ -88,7 +88,7 @@ export class AttachmentCleanup { return AttachmentCleanup.coreCleanup(() => { let files: string[] = [] for (let [key, schema] of Object.entries(table.schema)) { - if (schema.type !== FieldType.ATTACHMENTS) { + if (schema.type !== FieldType.ATTACHMENT) { continue } const oldKeys = diff --git a/packages/server/src/utilities/rowProcessor/index.ts b/packages/server/src/utilities/rowProcessor/index.ts index c421929888..0015680e77 100644 --- a/packages/server/src/utilities/rowProcessor/index.ts +++ b/packages/server/src/utilities/rowProcessor/index.ts @@ -148,7 +148,7 @@ export async function inputProcessing( } // remove any attachment urls, they are generated on read - if (field.type === FieldType.ATTACHMENTS) { + if (field.type === FieldType.ATTACHMENT) { const attachments = clonedRow[key] if (attachments?.length) { attachments.forEach((attachment: RowAttachment) => { @@ -216,7 +216,7 @@ export async function outputProcessing( // process complex types: attachements, bb references... for (let [property, column] of Object.entries(table.schema)) { - if (column.type === FieldType.ATTACHMENTS) { + if (column.type === FieldType.ATTACHMENT) { for (let row of enriched) { if (row[property] == null || !Array.isArray(row[property])) { continue diff --git a/packages/server/src/utilities/rowProcessor/map.ts b/packages/server/src/utilities/rowProcessor/map.ts index 2e0ac9efe1..60fe5a001b 100644 --- a/packages/server/src/utilities/rowProcessor/map.ts +++ b/packages/server/src/utilities/rowProcessor/map.ts @@ -106,7 +106,7 @@ export const TYPE_TRANSFORM_MAP: any = { return date }, }, - [FieldType.ATTACHMENTS]: { + [FieldType.ATTACHMENT]: { //@ts-ignore [null]: [], //@ts-ignore diff --git a/packages/server/src/utilities/rowProcessor/tests/attachments.spec.ts b/packages/server/src/utilities/rowProcessor/tests/attachments.spec.ts index 1b36a4cb81..cefea7e504 100644 --- a/packages/server/src/utilities/rowProcessor/tests/attachments.spec.ts +++ b/packages/server/src/utilities/rowProcessor/tests/attachments.spec.ts @@ -34,7 +34,7 @@ function table(): Table { schema: { attach: { name: "attach", - type: FieldType.ATTACHMENTS, + type: FieldType.ATTACHMENT, constraints: {}, }, }, diff --git a/packages/server/src/utilities/rowProcessor/tests/outputProcessing.spec.ts b/packages/server/src/utilities/rowProcessor/tests/outputProcessing.spec.ts index 93404e0469..a17bd5f393 100644 --- a/packages/server/src/utilities/rowProcessor/tests/outputProcessing.spec.ts +++ b/packages/server/src/utilities/rowProcessor/tests/outputProcessing.spec.ts @@ -82,7 +82,7 @@ describe("rowProcessor - outputProcessing", () => { sourceType: TableSourceType.INTERNAL, schema: { attach: { - type: FieldType.ATTACHMENTS, + type: FieldType.ATTACHMENT, name: "attach", constraints: {}, }, diff --git a/packages/shared-core/src/table.ts b/packages/shared-core/src/table.ts index 26a7e77cd0..5eab2fc340 100644 --- a/packages/shared-core/src/table.ts +++ b/packages/shared-core/src/table.ts @@ -11,10 +11,10 @@ const allowDisplayColumnByType: Record = { [FieldType.INTERNAL]: true, [FieldType.BARCODEQR]: true, [FieldType.BIGINT]: true, + [FieldType.BOOLEAN]: false, [FieldType.ARRAY]: false, - [FieldType.ATTACHMENTS]: false, - [FieldType.ATTACHMENT_SINGLE]: false, + [FieldType.ATTACHMENT]: false, [FieldType.LINK]: false, [FieldType.JSON]: false, [FieldType.BB_REFERENCE]: false, @@ -34,8 +34,7 @@ const allowSortColumnByType: Record = { [FieldType.JSON]: true, [FieldType.FORMULA]: false, - [FieldType.ATTACHMENTS]: false, - [FieldType.ATTACHMENT_SINGLE]: false, + [FieldType.ATTACHMENT]: false, [FieldType.ARRAY]: false, [FieldType.LINK]: false, [FieldType.BB_REFERENCE]: false, diff --git a/packages/types/src/documents/app/row.ts b/packages/types/src/documents/app/row.ts index 222c346591..aa8f50d4a8 100644 --- a/packages/types/src/documents/app/row.ts +++ b/packages/types/src/documents/app/row.ts @@ -8,8 +8,7 @@ export enum FieldType { BOOLEAN = "boolean", ARRAY = "array", DATETIME = "datetime", - ATTACHMENTS = "attachment", - ATTACHMENT_SINGLE = "attachment_single", + ATTACHMENT = "attachment", LINK = "link", FORMULA = "formula", AUTO = "auto", @@ -39,6 +38,7 @@ export interface Row extends Document { export enum FieldSubtype { USER = "user", USERS = "users", + SINGLE = "single", } // The 'as' are required for typescript not to type the outputs as generic FieldSubtype @@ -47,4 +47,7 @@ export const FieldTypeSubtypes = { USER: FieldSubtype.USER as FieldSubtype.USER, USERS: FieldSubtype.USERS as FieldSubtype.USERS, }, + ATTACHMENT: { + SINGLE: FieldSubtype.SINGLE as FieldSubtype.SINGLE, + }, } diff --git a/packages/types/src/documents/app/table/schema.ts b/packages/types/src/documents/app/table/schema.ts index 86c34b6a5c..45e39268ac 100644 --- a/packages/types/src/documents/app/table/schema.ts +++ b/packages/types/src/documents/app/table/schema.ts @@ -112,8 +112,10 @@ export interface BBReferenceFieldMetadata relationshipType?: RelationshipType } -export interface AttachmentFieldMetadata extends BaseFieldSchema { - type: FieldType.ATTACHMENTS +export interface AttachmentFieldMetadata + extends Omit { + type: FieldType.ATTACHMENT + subtype?: FieldSubtype.SINGLE } export interface FieldConstraints { @@ -162,7 +164,7 @@ interface OtherFieldMetadata extends BaseFieldSchema { | FieldType.NUMBER | FieldType.LONGFORM | FieldType.BB_REFERENCE - | FieldType.ATTACHMENTS + | FieldType.ATTACHMENT > } @@ -215,5 +217,5 @@ export function isBBReferenceField( export function isAttachmentField( field: FieldSchema ): field is AttachmentFieldMetadata { - return field.type === FieldType.ATTACHMENTS + return field.type === FieldType.ATTACHMENT } From 6d41b890db6b1053acf8952f043e4cdcb747a8b3 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Wed, 3 Apr 2024 16:09:12 +0100 Subject: [PATCH 83/97] Fix test that doesn't work if you run it with reuse containers. --- .../server/src/sdk/tests/attachments.spec.ts | 103 ++++++++---------- 1 file changed, 45 insertions(+), 58 deletions(-) diff --git a/packages/server/src/sdk/tests/attachments.spec.ts b/packages/server/src/sdk/tests/attachments.spec.ts index 0fd43ac5a8..c1736e6f8e 100644 --- a/packages/server/src/sdk/tests/attachments.spec.ts +++ b/packages/server/src/sdk/tests/attachments.spec.ts @@ -1,4 +1,12 @@ -import newid from "../../db/newid" +import TestConfig from "../../tests/utilities/TestConfiguration" +import { db as dbCore } from "@budibase/backend-core" +import sdk from "../index" +import { + FieldType, + INTERNAL_TABLE_SOURCE_ID, + TableSourceType, +} from "@budibase/types" +import { FIND_LIMIT } from "../app/rows/attachments" const attachment = { size: 73479, @@ -8,69 +16,48 @@ const attachment = { key: "app_bbb/attachments/a.png", } -const row = { - _id: "ro_ta_aaa", - photo: [attachment], - otherCol: "string", -} - -const table = { - _id: "ta_aaa", - name: "photos", - schema: { - photo: { - type: "attachment", - name: "photo", - }, - otherCol: { - type: "string", - name: "otherCol", - }, - }, -} - -jest.mock("@budibase/backend-core", () => { - const core = jest.requireActual("@budibase/backend-core") - return { - ...core, - db: { - ...core.db, - directCouchFind: jest.fn(), - }, - } -}) - -import { db as dbCore } from "@budibase/backend-core" -import sdk from "../index" - describe("should be able to re-write attachment URLs", () => { + const config = new TestConfig() + + beforeAll(async () => { + await config.init() + }) + it("should update URLs on a number of rows over the limit", async () => { - const db = dbCore.getDB("app_aaa") - await db.put(table) - const limit = 30 - let rows = [] - for (let i = 0; i < limit; i++) { - const rowToWrite = { - ...row, - _id: `${row._id}_${newid()}`, - } - const { rev } = await db.put(rowToWrite) - rows.push({ - ...rowToWrite, - _rev: rev, + const table = await config.api.table.save({ + name: "photos", + type: "table", + sourceId: INTERNAL_TABLE_SOURCE_ID, + sourceType: TableSourceType.INTERNAL, + schema: { + photo: { + type: FieldType.ATTACHMENT, + name: "photo", + }, + otherCol: { + type: FieldType.STRING, + name: "otherCol", + }, + }, + }) + + for (let i = 0; i < FIND_LIMIT * 4; i++) { + await config.api.row.save(table._id!, { + photo: [attachment], + otherCol: "string", }) } - dbCore.directCouchFind - // @ts-ignore - .mockReturnValueOnce({ rows: rows.slice(0, 25), bookmark: "aaa" }) - .mockReturnValueOnce({ rows: rows.slice(25, limit), bookmark: "bbb" }) + const db = dbCore.getDB(config.getAppId()) await sdk.backups.updateAttachmentColumns(db.name, db) - const finalRows = await sdk.rows.getAllInternalRows(db.name) - for (let rowToCheck of finalRows) { - expect(rowToCheck.otherCol).toBe(row.otherCol) - expect(rowToCheck.photo[0].url).toBe("") - expect(rowToCheck.photo[0].key).toBe(`${db.name}/attachments/a.png`) + + const rows = (await sdk.rows.getAllInternalRows(db.name)).filter( + row => row.tableId === table._id + ) + for (const row of rows) { + expect(row.otherCol).toBe("string") + expect(row.photo[0].url).toBe("") + expect(row.photo[0].key).toBe(`${db.name}/attachments/a.png`) } }) }) From afa757f21ac23c7f0e57ac9945464ce7b321c091 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Wed, 3 Apr 2024 16:24:19 +0100 Subject: [PATCH 84/97] Remove catch-all error handler in preview endpoint to help debug tests. --- .../server/src/api/controllers/query/index.ts | 72 +++++++++---------- 1 file changed, 34 insertions(+), 38 deletions(-) diff --git a/packages/server/src/api/controllers/query/index.ts b/packages/server/src/api/controllers/query/index.ts index 8ccd3daec9..02a2bf25b0 100644 --- a/packages/server/src/api/controllers/query/index.ts +++ b/packages/server/src/api/controllers/query/index.ts @@ -281,48 +281,44 @@ export async function preview( return { previewSchema, nestedSchemaFields } } - try { - const inputs: QueryEvent = { - appId: ctx.appId, - queryVerb: query.queryVerb, - fields: query.fields, - parameters: enrichParameters(query), - transformer: query.transformer, - schema: query.schema, - nullDefaultSupport: query.nullDefaultSupport, - queryId, - datasource, - // have to pass down to the thread runner - can't put into context now - environmentVariables: envVars, - ctx: { - user: ctx.user, - auth: { ...authConfigCtx }, - }, - } + const inputs: QueryEvent = { + appId: ctx.appId, + queryVerb: query.queryVerb, + fields: query.fields, + parameters: enrichParameters(query), + transformer: query.transformer, + schema: query.schema, + nullDefaultSupport: query.nullDefaultSupport, + queryId, + datasource, + // have to pass down to the thread runner - can't put into context now + environmentVariables: envVars, + ctx: { + user: ctx.user, + auth: { ...authConfigCtx }, + }, + } - const { rows, keys, info, extra } = await Runner.run(inputs) - const { previewSchema, nestedSchemaFields } = getSchemaFields(rows, keys) + const { rows, keys, info, extra } = await Runner.run(inputs) + const { previewSchema, nestedSchemaFields } = getSchemaFields(rows, keys) - // if existing schema, update to include any previous schema keys - if (existingSchema) { - for (let key of Object.keys(previewSchema)) { - if (existingSchema[key]) { - previewSchema[key] = existingSchema[key] - } + // if existing schema, update to include any previous schema keys + if (existingSchema) { + for (let key of Object.keys(previewSchema)) { + if (existingSchema[key]) { + previewSchema[key] = existingSchema[key] } } - // remove configuration before sending event - delete datasource.config - await events.query.previewed(datasource, ctx.request.body) - ctx.body = { - rows, - nestedSchemaFields, - schema: previewSchema, - info, - extra, - } - } catch (err: any) { - ctx.throw(400, err) + } + // remove configuration before sending event + delete datasource.config + await events.query.previewed(datasource, ctx.request.body) + ctx.body = { + rows, + nestedSchemaFields, + schema: previewSchema, + info, + extra, } } From 94084db77d60e6fd121953a3d1087048e94229de Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Wed, 3 Apr 2024 16:36:01 +0100 Subject: [PATCH 85/97] Exclude isolated-vm.ts from coverage. --- packages/server/jest.config.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/server/jest.config.ts b/packages/server/jest.config.ts index 428ffd5761..d0c184248c 100644 --- a/packages/server/jest.config.ts +++ b/packages/server/jest.config.ts @@ -56,6 +56,10 @@ const config: Config.InitialOptions = { "!src/tests/**/*.{js,ts}", // The use of coverage in the JS runner bundles breaks tests "!src/jsRunner/bundles/**/*.{js,ts}", + // We have a polyfill for the TextDecoder class in here that gets + // injected into the vm for deserializing BSON. If it gets coveraged + // it breaks the tests. + "!src/jsRunner/vm/isolated-vm.ts", ], coverageReporters: ["lcov", "json", "clover"], } From 51af154215faf465bc1dc4f3f06dceefa757d0cf Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Wed, 3 Apr 2024 16:49:42 +0100 Subject: [PATCH 86/97] Add --coverage back to local tests for packages/server --- packages/server/scripts/test.sh | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/server/scripts/test.sh b/packages/server/scripts/test.sh index 688cfbc35f..4b456e4731 100644 --- a/packages/server/scripts/test.sh +++ b/packages/server/scripts/test.sh @@ -4,11 +4,9 @@ set -e if [[ -n $CI ]] then export NODE_OPTIONS="--max-old-space-size=4096 --no-node-snapshot $NODE_OPTIONS" - echo "jest --coverage --maxWorkers=4 --forceExit --workerIdleMemoryLimit=2000MB --bail $@" jest --coverage --maxWorkers=4 --forceExit --workerIdleMemoryLimit=2000MB --bail $@ else # --maxWorkers performs better in development export NODE_OPTIONS="--no-node-snapshot $NODE_OPTIONS" - echo "jest --coverage --maxWorkers=2 --forceExit $@" - jest --maxWorkers=2 --forceExit $@ + jest --coverage --maxWorkers=2 --forceExit $@ fi \ No newline at end of file From a8ae8391864fc4fe9be1f888af63429df520d03d Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Wed, 3 Apr 2024 17:03:10 +0100 Subject: [PATCH 87/97] Tidy up function rename. --- packages/server/src/threads/query.ts | 4 ++-- packages/server/src/threads/utils.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/server/src/threads/query.ts b/packages/server/src/threads/query.ts index 97e7a05cf7..54322b1156 100644 --- a/packages/server/src/threads/query.ts +++ b/packages/server/src/threads/query.ts @@ -167,7 +167,7 @@ class QueryRunner { this.hasRerun = true } - await threadUtils.invalidateDynamicVariables(this.cachedVariables) + await threadUtils.invalidateCachedVariable(this.cachedVariables) return this.execute() } @@ -254,7 +254,7 @@ class QueryRunner { let { parameters } = this const queryId = variable.queryId, name = variable.name - let value = await threadUtils.checkCacheForDynamicVariable(queryId, name) + let value = await threadUtils.getCachedVariable(queryId, name) if (!value) { value = this.queryResponse[queryId] ? this.queryResponse[queryId] diff --git a/packages/server/src/threads/utils.ts b/packages/server/src/threads/utils.ts index 46752975a2..bf0d8f2231 100644 --- a/packages/server/src/threads/utils.ts +++ b/packages/server/src/threads/utils.ts @@ -85,7 +85,7 @@ export default { hasExtraData, formatResponse, storeDynamicVariable, - invalidateDynamicVariables: invalidateCachedVariable, - checkCacheForDynamicVariable: getCachedVariable, + invalidateCachedVariable, + getCachedVariable, threadSetup, } From 14a7ab65449e5e97f8d8fe39ebbb92d8d6d748ea Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Wed, 3 Apr 2024 19:45:01 +0100 Subject: [PATCH 88/97] update account portal SHA --- packages/account-portal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/account-portal b/packages/account-portal index 360ad2dc29..1828e332c1 160000 --- a/packages/account-portal +++ b/packages/account-portal @@ -1 +1 @@ -Subproject commit 360ad2dc29c3f1fd5a1182ae258c45666b7f5eb1 +Subproject commit 1828e332c1d477b13a608a13e6ed4aea6d55b366 From e943555473debfd2da2a0e76d091031b4e9b863b Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Wed, 3 Apr 2024 20:24:04 +0100 Subject: [PATCH 89/97] acct portal sha --- packages/account-portal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/account-portal b/packages/account-portal index 1828e332c1..011fa3c175 160000 --- a/packages/account-portal +++ b/packages/account-portal @@ -1 +1 @@ -Subproject commit 1828e332c1d477b13a608a13e6ed4aea6d55b366 +Subproject commit 011fa3c175ae0a1bbbb0f6e1341ba0154bca5c76 From 996984ec37cbc62b6c2f8825f1e4e039d81f0fcd Mon Sep 17 00:00:00 2001 From: Budibase Staging Release Bot <> Date: Thu, 4 Apr 2024 07:55:39 +0000 Subject: [PATCH 90/97] Bump version to 2.22.16 --- lerna.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lerna.json b/lerna.json index 93b103ee00..7186c0ca17 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "2.22.15", + "version": "2.22.16", "npmClient": "yarn", "packages": [ "packages/*", From b6431e57ffa8eb450b43007200871a2e5623f17b Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Thu, 4 Apr 2024 10:28:19 +0100 Subject: [PATCH 91/97] Attempt to make JS timeout tests more consistent. --- packages/server/src/api/routes/tests/row.spec.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/server/src/api/routes/tests/row.spec.ts b/packages/server/src/api/routes/tests/row.spec.ts index 92573f9652..1010a8ed55 100644 --- a/packages/server/src/api/routes/tests/row.spec.ts +++ b/packages/server/src/api/routes/tests/row.spec.ts @@ -1295,7 +1295,7 @@ describe.each([ describe("Formula JS protection", () => { it("should time out JS execution if a single cell takes too long", async () => { - await config.withEnv({ JS_PER_INVOCATION_TIMEOUT_MS: 20 }, async () => { + await config.withEnv({ JS_PER_INVOCATION_TIMEOUT_MS: 40 }, async () => { const js = Buffer.from( ` let i = 0; @@ -1332,11 +1332,11 @@ describe.each([ }) }) - it("should time out JS execution if a multiple cells take too long", async () => { + it.only("should time out JS execution if a multiple cells take too long", async () => { await config.withEnv( { - JS_PER_INVOCATION_TIMEOUT_MS: 20, - JS_PER_REQUEST_TIMEOUT_MS: 40, + JS_PER_INVOCATION_TIMEOUT_MS: 40, + JS_PER_REQUEST_TIMEOUT_MS: 80, }, async () => { const js = Buffer.from( From a1a06b5d186623d765a14b643b3fbc5679bd48e8 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Thu, 4 Apr 2024 13:52:57 +0200 Subject: [PATCH 92/97] Fix import --- packages/server/src/utilities/schema.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/src/utilities/schema.ts b/packages/server/src/utilities/schema.ts index 85dfdd3506..34113759ed 100644 --- a/packages/server/src/utilities/schema.ts +++ b/packages/server/src/utilities/schema.ts @@ -54,7 +54,7 @@ export function validate(rows: Rows, schema: TableSchema): ValidationResults { type: columnType, subtype: columnSubtype, autocolumn: isAutoColumn, - } = schema[columnName] + } = schema[columnName] || {} // If the column had an invalid value we don't want to override it if (results.schemaValidation[columnName] === false) { From 5a82de01dd31c2fde07172d154ed41a1234b4f12 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Thu, 4 Apr 2024 13:54:45 +0200 Subject: [PATCH 93/97] Display "Attachment" on UI --- .../backend/TableNavigator/ExistingTableDataImport.svelte | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/builder/src/components/backend/TableNavigator/ExistingTableDataImport.svelte b/packages/builder/src/components/backend/TableNavigator/ExistingTableDataImport.svelte index eb1e7bc7ff..efbfd26565 100644 --- a/packages/builder/src/components/backend/TableNavigator/ExistingTableDataImport.svelte +++ b/packages/builder/src/components/backend/TableNavigator/ExistingTableDataImport.svelte @@ -49,7 +49,10 @@ label: "Long Form Text", value: FIELDS.LONGFORM.type, }, - + { + label: "Attachment", + value: FIELDS.ATTACHMENT.type, + }, { label: "User", value: `${FIELDS.USER.type}${FIELDS.USER.subtype}`, From 60f782d33d5fd0bde71cfe9489656d7af544f8fb Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Thu, 4 Apr 2024 14:14:51 +0200 Subject: [PATCH 94/97] Update account-portal --- packages/account-portal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/account-portal b/packages/account-portal index 011fa3c175..532c4db35c 160000 --- a/packages/account-portal +++ b/packages/account-portal @@ -1 +1 @@ -Subproject commit 011fa3c175ae0a1bbbb0f6e1341ba0154bca5c76 +Subproject commit 532c4db35cecd346b5c24f0b89ab7b397a122a36 From d0a5ae6ae3b990bb1a2588a32143077b9a6a4dca Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Thu, 4 Apr 2024 15:27:50 +0100 Subject: [PATCH 95/97] Respond to Mike's feedback. --- packages/server/jest.config.ts | 9 +++------ packages/server/package.json | 2 +- packages/server/src/api/controllers/query/index.ts | 9 ++++++++- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/packages/server/jest.config.ts b/packages/server/jest.config.ts index d0c184248c..6341c8e5bd 100644 --- a/packages/server/jest.config.ts +++ b/packages/server/jest.config.ts @@ -54,12 +54,9 @@ const config: Config.InitialOptions = { "!src/db/views/staticViews.*", "!src/**/*.spec.{js,ts}", "!src/tests/**/*.{js,ts}", - // The use of coverage in the JS runner bundles breaks tests - "!src/jsRunner/bundles/**/*.{js,ts}", - // We have a polyfill for the TextDecoder class in here that gets - // injected into the vm for deserializing BSON. If it gets coveraged - // it breaks the tests. - "!src/jsRunner/vm/isolated-vm.ts", + // The use of coverage in the JS runner breaks tests by inserting + // coverage functions into code that will run inside of the isolate. + "!src/jsRunner/**/*.{js,ts}", ], coverageReporters: ["lcov", "json", "clover"], } diff --git a/packages/server/package.json b/packages/server/package.json index d138577090..4d1df4d734 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -143,7 +143,7 @@ "jest": "29.7.0", "jest-openapi": "0.14.2", "jest-runner": "29.7.0", - "nock": "^13.5.4", + "nock": "13.5.4", "nodemon": "2.0.15", "openapi-typescript": "5.2.0", "path-to-regexp": "6.2.0", diff --git a/packages/server/src/api/controllers/query/index.ts b/packages/server/src/api/controllers/query/index.ts index 02a2bf25b0..b52cea553f 100644 --- a/packages/server/src/api/controllers/query/index.ts +++ b/packages/server/src/api/controllers/query/index.ts @@ -299,7 +299,14 @@ export async function preview( }, } - const { rows, keys, info, extra } = await Runner.run(inputs) + let queryResponse: QueryResponse + try { + queryResponse = await Runner.run(inputs) + } catch (err: any) { + ctx.throw(400, err) + } + + const { rows, keys, info, extra } = queryResponse const { previewSchema, nestedSchemaFields } = getSchemaFields(rows, keys) // if existing schema, update to include any previous schema keys From d5a04d2569821a3ba84cfe33b68e7c32a74ea33a Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Thu, 4 Apr 2024 15:29:09 +0100 Subject: [PATCH 96/97] Update account-portal submodule to latest master. --- packages/account-portal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/account-portal b/packages/account-portal index c51458d26f..532c4db35c 160000 --- a/packages/account-portal +++ b/packages/account-portal @@ -1 +1 @@ -Subproject commit c51458d26f6bded1984ba9ac5d2948480ac809ce +Subproject commit 532c4db35cecd346b5c24f0b89ab7b397a122a36 From db6aec725b2d862b3d6151d1c686aeabefe09ed5 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Thu, 4 Apr 2024 15:32:05 +0100 Subject: [PATCH 97/97] Removed focused test. --- packages/server/src/api/routes/tests/row.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/src/api/routes/tests/row.spec.ts b/packages/server/src/api/routes/tests/row.spec.ts index 1010a8ed55..f9e05c5bd8 100644 --- a/packages/server/src/api/routes/tests/row.spec.ts +++ b/packages/server/src/api/routes/tests/row.spec.ts @@ -1332,7 +1332,7 @@ describe.each([ }) }) - it.only("should time out JS execution if a multiple cells take too long", async () => { + it("should time out JS execution if a multiple cells take too long", async () => { await config.withEnv( { JS_PER_INVOCATION_TIMEOUT_MS: 40,