From f8f9c4da8efc6ec94e1dd52fe525fd9cbfdeb7af Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Fri, 25 Oct 2024 17:32:42 +0100 Subject: [PATCH 01/31] Work in progress - this is the scaffolding for views to be added to the public API. --- packages/server/specs/resources/view.ts | 193 ++++++++++++++++++ .../src/api/controllers/public/views.ts | 56 +++++ .../server/src/api/routes/public/views.ts | 165 +++++++++++++++ .../server/src/api/routes/utils/validators.ts | 4 + 4 files changed, 418 insertions(+) create mode 100644 packages/server/specs/resources/view.ts create mode 100644 packages/server/src/api/controllers/public/views.ts create mode 100644 packages/server/src/api/routes/public/views.ts diff --git a/packages/server/specs/resources/view.ts b/packages/server/specs/resources/view.ts new file mode 100644 index 0000000000..6458af15fc --- /dev/null +++ b/packages/server/specs/resources/view.ts @@ -0,0 +1,193 @@ +import { FieldType, FormulaType, RelationshipType } from "@budibase/types" +import { object } from "./utils" +import Resource from "./utils/Resource" + +const view = { + _id: "ta_5b1649e42a5b41dea4ef7742a36a7a70", + name: "People", + schema: { + name: { + type: "string", + name: "name", + }, + age: { + type: "number", + name: "age", + }, + relationship: { + type: "link", + name: "relationship", + tableId: "ta_...", + fieldName: "relatedColumn", + relationshipType: "many-to-many", + }, + }, +} + +const baseColumnDef = { + type: { + type: "string", + enum: Object.values(FieldType), + description: + "Defines the type of the column, most explain themselves, a link column is a relationship.", + }, + constraints: { + type: "object", + description: + "A constraint can be applied to the column which will be validated against when a row is saved.", + properties: { + type: { + type: "string", + enum: ["string", "number", "object", "boolean"], + }, + presence: { + type: "boolean", + description: "Defines whether the column is required or not.", + }, + }, + }, + name: { + type: "string", + description: "The name of the column.", + }, + autocolumn: { + type: "boolean", + description: "Defines whether the column is automatically generated.", + }, +} + +const viewSchema = { + description: "The table to be created/updated.", + type: "object", + required: ["name", "schema"], + properties: { + name: { + description: "The name of the table.", + type: "string", + }, + primaryDisplay: { + type: "string", + description: + "The name of the column which should be used in relationship tags when relating to this table.", + }, + schema: { + type: "object", + additionalProperties: { + oneOf: [ + // relationship + { + type: "object", + properties: { + ...baseColumnDef, + type: { + type: "string", + enum: [FieldType.LINK], + description: "A relationship column.", + }, + fieldName: { + type: "string", + description: + "The name of the column which a relationship column is related to in another table.", + }, + tableId: { + type: "string", + description: + "The ID of the table which a relationship column is related to.", + }, + relationshipType: { + type: "string", + enum: Object.values(RelationshipType), + description: + "Defines the type of relationship that this column will be used for.", + }, + through: { + type: "string", + description: + "When using a SQL table that contains many to many relationships this defines the table the relationships are linked through.", + }, + foreignKey: { + type: "string", + description: + "When using a SQL table that contains a one to many relationship this defines the foreign key.", + }, + throughFrom: { + type: "string", + description: + "When using a SQL table that utilises a through table, this defines the primary key in the through table for this table.", + }, + throughTo: { + type: "string", + description: + "When using a SQL table that utilises a through table, this defines the primary key in the through table for the related table.", + }, + }, + }, + { + type: "object", + properties: { + ...baseColumnDef, + type: { + type: "string", + enum: [FieldType.FORMULA], + description: "A formula column.", + }, + formula: { + type: "string", + description: + "Defines a Handlebars or JavaScript formula to use, note that Javascript formulas are expected to be provided in the base64 format.", + }, + formulaType: { + type: "string", + enum: Object.values(FormulaType), + description: + "Defines whether this is a static or dynamic formula.", + }, + }, + }, + { + type: "object", + properties: baseColumnDef, + }, + ], + }, + }, + }, +} + +const viewOutputSchema = { + ...viewSchema, + properties: { + ...viewSchema.properties, + _id: { + description: "The ID of the view.", + type: "string", + }, + }, + required: [...viewSchema.required, "_id"], +} + +export default new Resource() + .setExamples({ + view: { + value: { + data: view, + }, + }, + views: { + value: { + data: [view], + }, + }, + }) + .setSchemas({ + view: viewSchema, + viewOutput: object({ + data: viewOutputSchema, + }), + viewSearch: object({ + data: { + type: "array", + items: viewOutputSchema, + }, + }), + }) diff --git a/packages/server/src/api/controllers/public/views.ts b/packages/server/src/api/controllers/public/views.ts new file mode 100644 index 0000000000..11fb4cc344 --- /dev/null +++ b/packages/server/src/api/controllers/public/views.ts @@ -0,0 +1,56 @@ +import { search as stringSearch } from "./utils" +import * as controller from "../view" +import { ViewV2, UserCtx } from "@budibase/types" +import { Next } from "koa" + +function fixView(view: ViewV2, params: any) { + if (!params || !view) { + return view + } + if (params.viewId) { + view.id = params.viewId + } + return view +} + +export async function search(ctx: UserCtx, next: Next) { + const { name } = ctx.request.body + // TODO: need a view search endpoint + // await controller.v2.fetch(ctx) + ctx.body = stringSearch(ctx.body, name) + await next() +} + +export async function create(ctx: UserCtx, next: Next) { + await controller.v2.create(ctx) + await next() +} + +export async function read(ctx: UserCtx, next: Next) { + await controller.v2.get(ctx) + await next() +} + +export async function update(ctx: UserCtx, next: Next) { + // TODO: this is more complex - no rev on views + // ctx.request.body = await addRev( + // fixView(ctx.request.body, ctx.params), + // ctx.params.tableId + // ) + await controller.v2.update(ctx) + await next() +} + +export async function destroy(ctx: UserCtx, next: Next) { + await controller.v2.remove(ctx) + ctx.body = ctx.table + await next() +} + +export default { + create, + read, + update, + destroy, + search, +} diff --git a/packages/server/src/api/routes/public/views.ts b/packages/server/src/api/routes/public/views.ts new file mode 100644 index 0000000000..7c182d105f --- /dev/null +++ b/packages/server/src/api/routes/public/views.ts @@ -0,0 +1,165 @@ +import controller from "../../controllers/public/views" +import Endpoint from "./utils/Endpoint" +import { viewValidator, nameValidator } from "../utils/validators" + +const read = [], + write = [] + +/** + * @openapi + * /views: + * post: + * operationId: viewCreate + * summary: Create a view + * description: Create a view, this can be against an internal or external table. + * tags: + * - views + * parameters: + * - $ref: '#/components/parameters/appId' + * requestBody: + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/view' + * examples: + * view: + * $ref: '#/components/examples/view' + * responses: + * 200: + * description: Returns the created view, including the ID which has been generated for it. + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/viewOutput' + * examples: + * view: + * $ref: '#/components/examples/view' + */ +write.push( + new Endpoint("post", "/views", controller.create).addMiddleware( + viewValidator() + ) +) + +/** + * @openapi + * /views/{viewId}: + * put: + * operationId: viewUpdate + * summary: Update a view + * description: Update a view, this can be against an internal or external table. + * tags: + * - views + * parameters: + * - $ref: '#/components/parameters/viewId' + * - $ref: '#/components/parameters/appId' + * requestBody: + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/view' + * examples: + * view: + * $ref: '#/components/examples/view' + * responses: + * 200: + * description: Returns the updated view. + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/viewOutput' + * examples: + * view: + * $ref: '#/components/examples/view' + */ +write.push( + new Endpoint("put", "/views/:viewId", controller.update).addMiddleware( + viewValidator() + ) +) + +/** + * @openapi + * /views/{viewId}: + * delete: + * operationId: viewDestroy + * summary: Delete a view + * description: Delete a view, this can be against an internal or external table. + * tags: + * - views + * parameters: + * - $ref: '#/components/parameters/viewId' + * - $ref: '#/components/parameters/appId' + * responses: + * 200: + * description: Returns the deleted view. + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/viewOutput' + * examples: + * view: + * $ref: '#/components/examples/view' + */ +write.push(new Endpoint("delete", "/views/:viewId", controller.destroy)) + +/** + * @openapi + * /views/{viewId}: + * get: + * operationId: viewGetById + * summary: Retrieve a view + * description: Lookup a view, this could be internal or external. + * tags: + * - views + * parameters: + * - $ref: '#/components/parameters/viewId' + * - $ref: '#/components/parameters/appId' + * responses: + * 200: + * description: Returns the retrieved view. + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/viewOutput' + * examples: + * view: + * $ref: '#/components/examples/view' + */ +read.push(new Endpoint("get", "/views/:viewId", controller.read)) + +/** + * @openapi + * /views/search: + * post: + * operationId: viewSearch + * summary: Search for views + * description: Based on view properties (currently only name) search for views. + * tags: + * - views + * parameters: + * - $ref: '#/components/parameters/appId' + * requestBody: + * required: true + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/viewSearch' + * responses: + * 200: + * description: Returns the found views, based on the search parameters. + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/viewSearch' + * examples: + * views: + * $ref: '#/components/examples/views' + */ +read.push( + new Endpoint("post", "/views/search", controller.search).addMiddleware( + nameValidator() + ) +) + +export default { read, write } diff --git a/packages/server/src/api/routes/utils/validators.ts b/packages/server/src/api/routes/utils/validators.ts index 862d8c30c5..68ebd72c5e 100644 --- a/packages/server/src/api/routes/utils/validators.ts +++ b/packages/server/src/api/routes/utils/validators.ts @@ -66,6 +66,10 @@ export function tableValidator() { ) } +export function viewValidator() { + return auth.joiValidator.body(Joi.object()) +} + export function nameValidator() { return auth.joiValidator.body( Joi.object({ From eeb78b2c4578e1a6a1b181e7e1377af8b38e47ff Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Fri, 25 Oct 2024 17:51:52 +0100 Subject: [PATCH 02/31] Getting schemas correct for views. --- packages/server/specs/resources/view.ts | 160 +++++------------- .../src/api/controllers/public/views.ts | 8 +- 2 files changed, 45 insertions(+), 123 deletions(-) diff --git a/packages/server/specs/resources/view.ts b/packages/server/specs/resources/view.ts index 6458af15fc..5afdf78a0e 100644 --- a/packages/server/specs/resources/view.ts +++ b/packages/server/specs/resources/view.ts @@ -1,154 +1,72 @@ -import { FieldType, FormulaType, RelationshipType } from "@budibase/types" import { object } from "./utils" import Resource from "./utils/Resource" const view = { - _id: "ta_5b1649e42a5b41dea4ef7742a36a7a70", - name: "People", + name: "peopleView", + tableId: "ta_896a325f7e8147d2a2cda93c5d236511", schema: { name: { - type: "string", - name: "name", + visible: true, + readonly: false, + order: 1, + width: 300, }, age: { - type: "number", - name: "age", + visible: true, + readonly: true, + order: 2, + width: 200, }, - relationship: { - type: "link", - name: "relationship", - tableId: "ta_...", - fieldName: "relatedColumn", - relationshipType: "many-to-many", + salary: { + visible: false, + readonly: false, }, }, + primaryDisplay: "name", } const baseColumnDef = { - type: { - type: "string", - enum: Object.values(FieldType), - description: - "Defines the type of the column, most explain themselves, a link column is a relationship.", - }, - constraints: { - type: "object", - description: - "A constraint can be applied to the column which will be validated against when a row is saved.", - properties: { - type: { - type: "string", - enum: ["string", "number", "object", "boolean"], - }, - presence: { - type: "boolean", - description: "Defines whether the column is required or not.", - }, - }, - }, - name: { - type: "string", - description: "The name of the column.", - }, - autocolumn: { + visible: { type: "boolean", - description: "Defines whether the column is automatically generated.", + description: + "Defines whether the column is visible or not - rows retrieved/updated through this view will not be able to access it.", + }, + readonly: { + type: "boolean", + description: + "When used in combination with 'visible: true' the column will be visible in row responses but cannot be updated.", + }, + order: { + type: "integer", + description: + "A number defining where the column shows up in tables, lowest being first.", + }, + width: { + type: "integer", + description: + "A width for the column, defined in pixels - this affects rendering in tables.", }, } const viewSchema = { - description: "The table to be created/updated.", + description: "The view to be created/updated.", type: "object", required: ["name", "schema"], properties: { name: { - description: "The name of the table.", + description: "The name of the view.", type: "string", }, primaryDisplay: { type: "string", description: - "The name of the column which should be used in relationship tags when relating to this table.", + "A column used to display rows from this view - usually used when rendered in tables.", }, schema: { type: "object", additionalProperties: { - oneOf: [ - // relationship - { - type: "object", - properties: { - ...baseColumnDef, - type: { - type: "string", - enum: [FieldType.LINK], - description: "A relationship column.", - }, - fieldName: { - type: "string", - description: - "The name of the column which a relationship column is related to in another table.", - }, - tableId: { - type: "string", - description: - "The ID of the table which a relationship column is related to.", - }, - relationshipType: { - type: "string", - enum: Object.values(RelationshipType), - description: - "Defines the type of relationship that this column will be used for.", - }, - through: { - type: "string", - description: - "When using a SQL table that contains many to many relationships this defines the table the relationships are linked through.", - }, - foreignKey: { - type: "string", - description: - "When using a SQL table that contains a one to many relationship this defines the foreign key.", - }, - throughFrom: { - type: "string", - description: - "When using a SQL table that utilises a through table, this defines the primary key in the through table for this table.", - }, - throughTo: { - type: "string", - description: - "When using a SQL table that utilises a through table, this defines the primary key in the through table for the related table.", - }, - }, - }, - { - type: "object", - properties: { - ...baseColumnDef, - type: { - type: "string", - enum: [FieldType.FORMULA], - description: "A formula column.", - }, - formula: { - type: "string", - description: - "Defines a Handlebars or JavaScript formula to use, note that Javascript formulas are expected to be provided in the base64 format.", - }, - formulaType: { - type: "string", - enum: Object.values(FormulaType), - description: - "Defines whether this is a static or dynamic formula.", - }, - }, - }, - { - type: "object", - properties: baseColumnDef, - }, - ], + type: "object", + properties: baseColumnDef, }, }, }, @@ -158,12 +76,12 @@ const viewOutputSchema = { ...viewSchema, properties: { ...viewSchema.properties, - _id: { + id: { description: "The ID of the view.", type: "string", }, }, - required: [...viewSchema.required, "_id"], + required: [...viewSchema.required, "id"], } export default new Resource() diff --git a/packages/server/src/api/controllers/public/views.ts b/packages/server/src/api/controllers/public/views.ts index 11fb4cc344..85979126ff 100644 --- a/packages/server/src/api/controllers/public/views.ts +++ b/packages/server/src/api/controllers/public/views.ts @@ -3,13 +3,16 @@ import * as controller from "../view" import { ViewV2, UserCtx } from "@budibase/types" import { Next } from "koa" -function fixView(view: ViewV2, params: any) { +function fixView(view: ViewV2, params?: { viewId: string }) { if (!params || !view) { return view } - if (params.viewId) { + if (params?.viewId) { view.id = params.viewId } + if (!view.version) { + view.version = 2 + } return view } @@ -22,6 +25,7 @@ export async function search(ctx: UserCtx, next: Next) { } export async function create(ctx: UserCtx, next: Next) { + ctx.body = fixView(ctx.body) await controller.v2.create(ctx) await next() } From c055e2ae729943f10cb697fe51c41e32bf89e457 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Mon, 28 Oct 2024 18:09:04 +0000 Subject: [PATCH 03/31] Adding fetch endpoint for views (will be used by search for views in Public API). --- .../src/api/controllers/view/viewsV2.ts | 7 +++++ packages/server/src/api/routes/view.ts | 5 ++++ packages/server/src/sdk/app/views/index.ts | 29 +++++++++++++++++-- packages/types/src/api/web/app/view.ts | 4 +++ 4 files changed, 43 insertions(+), 2 deletions(-) diff --git a/packages/server/src/api/controllers/view/viewsV2.ts b/packages/server/src/api/controllers/view/viewsV2.ts index f864df9e9e..6f92e7399c 100644 --- a/packages/server/src/api/controllers/view/viewsV2.ts +++ b/packages/server/src/api/controllers/view/viewsV2.ts @@ -12,6 +12,7 @@ import { RelationSchemaField, ViewFieldMetadata, CalculationType, + ViewFetchResponseEnriched, } from "@budibase/types" import { builderSocket, gridSocket } from "../../../websockets" import { helpers } from "@budibase/shared-core" @@ -119,6 +120,12 @@ export async function get(ctx: Ctx) { } } +export async function fetch(ctx: Ctx) { + ctx.body = { + data: await sdk.views.getAllEnriched(), + } +} + export async function create(ctx: Ctx) { const view = ctx.request.body const { tableId } = view diff --git a/packages/server/src/api/routes/view.ts b/packages/server/src/api/routes/view.ts index 807d8e2f28..92e5f02a18 100644 --- a/packages/server/src/api/routes/view.ts +++ b/packages/server/src/api/routes/view.ts @@ -8,6 +8,11 @@ import { permissions } from "@budibase/backend-core" const router: Router = new Router() router + .get( + "/api/v2/views", + authorized(permissions.BUILDER), + viewController.v2.fetch + ) .get( "/api/v2/views/:viewId", authorizedResource( diff --git a/packages/server/src/sdk/app/views/index.ts b/packages/server/src/sdk/app/views/index.ts index cbf3513dd0..fc2341722f 100644 --- a/packages/server/src/sdk/app/views/index.ts +++ b/packages/server/src/sdk/app/views/index.ts @@ -27,6 +27,7 @@ import { isExternalTableID } from "../../../integrations/utils" import * as internal from "./internal" import * as external from "./external" import sdk from "../../../sdk" +import { ensureQueryUISet } from "./utils" function pickApi(tableId: any) { if (isExternalTableID(tableId)) { @@ -45,6 +46,24 @@ export async function getEnriched(viewId: string): Promise { return pickApi(tableId).getEnriched(viewId) } +export async function getAllEnriched(): Promise { + const tables = await sdk.tables.getAllTables() + let views: ViewV2Enriched[] = [] + for (let table of tables) { + if (!table.views || Object.keys(table.views).length === 0) { + continue + } + const v2Views = Object.values(table.views).filter(isV2) + const enrichedViews = await Promise.all( + v2Views.map(view => + enrichSchema(ensureQueryUISet(view), table.schema, tables) + ) + ) + views = views.concat(enrichedViews) + } + return views +} + export async function getTable(view: string | ViewV2): Promise { const viewId = typeof view === "string" ? view : view.id const cached = context.getTableForView(viewId) @@ -303,13 +322,19 @@ export function allowedFields( export async function enrichSchema( view: ViewV2, - tableSchema: TableSchema + tableSchema: TableSchema, + tables?: Table[] ): Promise { async function populateRelTableSchema( tableId: string, viewFields: Record ) { - const relTable = await sdk.tables.getTable(tableId) + let relTable = tables + ? tables?.find(t => t._id === tableId) + : await sdk.tables.getTable(tableId) + if (!relTable) { + throw new Error("Cannot enrich relationship, table not found") + } const result: Record = {} for (const relTableFieldName of Object.keys(relTable.schema)) { const relTableField = relTable.schema[relTableFieldName] diff --git a/packages/types/src/api/web/app/view.ts b/packages/types/src/api/web/app/view.ts index a6be5e2986..2560f7507f 100644 --- a/packages/types/src/api/web/app/view.ts +++ b/packages/types/src/api/web/app/view.ts @@ -9,6 +9,10 @@ export interface ViewResponseEnriched { data: ViewV2Enriched } +export interface ViewFetchResponseEnriched { + data: ViewV2Enriched[] +} + export interface CreateViewRequest extends Omit {} export interface UpdateViewRequest extends ViewV2 {} From 06984548556d00993aba791271c9f4a7e6dce5b0 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Mon, 28 Oct 2024 18:09:22 +0000 Subject: [PATCH 04/31] Fixing an issue with getAllExternalTables getter also returning internal tables. --- packages/server/src/sdk/app/tables/getters.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/server/src/sdk/app/tables/getters.ts b/packages/server/src/sdk/app/tables/getters.ts index a8ad606647..ebf7b3547b 100644 --- a/packages/server/src/sdk/app/tables/getters.ts +++ b/packages/server/src/sdk/app/tables/getters.ts @@ -82,8 +82,11 @@ export async function getAllInternalTables(db?: Database): Promise { } async function getAllExternalTables(): Promise { + // this is all datasources, we'll need to filter out internal const datasources = await sdk.datasources.fetch({ enriched: true }) - const allEntities = datasources.map(datasource => datasource.entities) + const allEntities = datasources + .filter(datasource => datasource._id !== INTERNAL_TABLE_SOURCE_ID) + .map(datasource => datasource.entities) let final: Table[] = [] for (let entities of allEntities) { if (entities) { From 68a7f88db667ad30dfa0998dae4c0b62a049589e Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Mon, 28 Oct 2024 18:09:38 +0000 Subject: [PATCH 05/31] Adding test case for fetch. --- .../src/api/routes/tests/viewV2.spec.ts | 32 +++++++++++++++++++ .../server/src/tests/utilities/api/viewV2.ts | 7 ++++ 2 files changed, 39 insertions(+) diff --git a/packages/server/src/api/routes/tests/viewV2.spec.ts b/packages/server/src/api/routes/tests/viewV2.spec.ts index 2af11b513b..b344e50bb4 100644 --- a/packages/server/src/api/routes/tests/viewV2.spec.ts +++ b/packages/server/src/api/routes/tests/viewV2.spec.ts @@ -2592,6 +2592,38 @@ describe.each([ }) }) + describe("fetch", () => { + let view: ViewV2, view2: ViewV2 + let table: Table, table2: Table + + beforeEach(async () => { + // clean app to make sure fixed amount of views + await config.init() + table = await config.api.table.save(saveTableRequest()) + table2 = await config.api.table.save(saveTableRequest()) + + view = await config.api.viewV2.create({ + tableId: table._id!, + name: generator.guid(), + schema: {}, + }) + view2 = await config.api.viewV2.create({ + tableId: table2._id!, + name: generator.guid(), + schema: {}, + }) + }) + + it("should be able to list views", async () => { + const response = await config.api.viewV2.fetch({ + status: 200, + }) + expect(response.data.length).toEqual(2) + expect(response.data.find(v => v.id === view.id)).toBeDefined() + expect(response.data.find(v => v.id === view2.id)).toBeDefined() + }) + }) + describe("read", () => { let view: ViewV2 let table: Table diff --git a/packages/server/src/tests/utilities/api/viewV2.ts b/packages/server/src/tests/utilities/api/viewV2.ts index 9741240f27..7cc57673a0 100644 --- a/packages/server/src/tests/utilities/api/viewV2.ts +++ b/packages/server/src/tests/utilities/api/viewV2.ts @@ -5,6 +5,7 @@ import { SearchViewRowRequest, PaginatedSearchRowResponse, ViewResponseEnriched, + ViewFetchResponseEnriched, } from "@budibase/types" import { Expectations, TestAPI } from "./base" @@ -49,6 +50,12 @@ export class ViewV2API extends TestAPI { .data } + fetch = async (expectations?: Expectations) => { + return await this._get(`/api/v2/views`, { + expectations, + }) + } + search = async ( viewId: string, params?: SearchViewRowRequest, From dd0764eb600f7c47e6ace83eb94a347b58a4a58d Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Wed, 30 Oct 2024 14:08:03 +0000 Subject: [PATCH 06/31] Putting view mapping in place, updating openAPI spec. --- packages/server/specs/openapi.json | 423 +++++++++++++++++- packages/server/specs/openapi.yaml | 290 +++++++++++- packages/server/specs/resources/index.ts | 4 + .../api/controllers/public/mapping/index.ts | 2 + .../api/controllers/public/mapping/types.ts | 3 + .../api/controllers/public/mapping/views.ts | 26 ++ .../src/api/controllers/public/views.ts | 3 +- .../api/routes/public/middleware/mapper.ts | 30 +- packages/server/src/definitions/openapi.ts | 180 ++++++++ 9 files changed, 943 insertions(+), 18 deletions(-) create mode 100644 packages/server/src/api/controllers/public/mapping/views.ts diff --git a/packages/server/specs/openapi.json b/packages/server/specs/openapi.json index f3091a1fc7..c80f947fad 100644 --- a/packages/server/specs/openapi.json +++ b/packages/server/specs/openapi.json @@ -423,6 +423,62 @@ }, "metrics": { "value": "# HELP budibase_os_uptime Time in seconds that the host operating system has been up.\n# TYPE budibase_os_uptime counter\nbudibase_os_uptime 54958\n# HELP budibase_os_free_mem Bytes of memory free for usage on the host operating system.\n# TYPE budibase_os_free_mem gauge\nbudibase_os_free_mem 804507648\n# HELP budibase_os_total_mem Total bytes of memory on the host operating system.\n# TYPE budibase_os_total_mem gauge\nbudibase_os_total_mem 16742404096\n# HELP budibase_os_used_mem Total bytes of memory in use on the host operating system.\n# TYPE budibase_os_used_mem gauge\nbudibase_os_used_mem 15937896448\n# HELP budibase_os_load1 Host operating system load average.\n# TYPE budibase_os_load1 gauge\nbudibase_os_load1 1.91\n# HELP budibase_os_load5 Host operating system load average.\n# TYPE budibase_os_load5 gauge\nbudibase_os_load5 1.75\n# HELP budibase_os_load15 Host operating system load average.\n# TYPE budibase_os_load15 gauge\nbudibase_os_load15 1.56\n# HELP budibase_tenant_user_count The number of users created.\n# TYPE budibase_tenant_user_count gauge\nbudibase_tenant_user_count 1\n# HELP budibase_tenant_app_count The number of apps created by a user.\n# TYPE budibase_tenant_app_count gauge\nbudibase_tenant_app_count 2\n# HELP budibase_tenant_production_app_count The number of apps a user has published.\n# TYPE budibase_tenant_production_app_count gauge\nbudibase_tenant_production_app_count 1\n# HELP budibase_tenant_dev_app_count The number of apps a user has unpublished in development.\n# TYPE budibase_tenant_dev_app_count gauge\nbudibase_tenant_dev_app_count 1\n# HELP budibase_tenant_db_count The number of couchdb databases including global tables such as _users.\n# TYPE budibase_tenant_db_count gauge\nbudibase_tenant_db_count 3\n# HELP budibase_quota_usage_apps The number of apps created.\n# TYPE budibase_quota_usage_apps gauge\nbudibase_quota_usage_apps 1\n# HELP budibase_quota_limit_apps The limit on the number of apps that can be created.\n# TYPE budibase_quota_limit_apps gauge\nbudibase_quota_limit_apps 9007199254740991\n# HELP budibase_quota_usage_rows The number of database rows used from the quota.\n# TYPE budibase_quota_usage_rows gauge\nbudibase_quota_usage_rows 0\n# HELP budibase_quota_limit_rows The limit on the number of rows that can be created.\n# TYPE budibase_quota_limit_rows gauge\nbudibase_quota_limit_rows 9007199254740991\n# HELP budibase_quota_usage_plugins The number of plugins in use.\n# TYPE budibase_quota_usage_plugins gauge\nbudibase_quota_usage_plugins 0\n# HELP budibase_quota_limit_plugins The limit on the number of plugins that can be created.\n# TYPE budibase_quota_limit_plugins gauge\nbudibase_quota_limit_plugins 9007199254740991\n# HELP budibase_quota_usage_user_groups The number of user groups created.\n# TYPE budibase_quota_usage_user_groups gauge\nbudibase_quota_usage_user_groups 0\n# HELP budibase_quota_limit_user_groups The limit on the number of user groups that can be created.\n# TYPE budibase_quota_limit_user_groups gauge\nbudibase_quota_limit_user_groups 9007199254740991\n# HELP budibase_quota_usage_queries The number of queries used in the current month.\n# TYPE budibase_quota_usage_queries gauge\nbudibase_quota_usage_queries 0\n# HELP budibase_quota_limit_queries The limit on the number of queries for the current month.\n# TYPE budibase_quota_limit_queries gauge\nbudibase_quota_limit_queries 9007199254740991\n# HELP budibase_quota_usage_automations The number of automations used in the current month.\n# TYPE budibase_quota_usage_automations gauge\nbudibase_quota_usage_automations 0\n# HELP budibase_quota_limit_automations The limit on the number of automations that can be created.\n# TYPE budibase_quota_limit_automations gauge\nbudibase_quota_limit_automations 9007199254740991\n" + }, + "view": { + "value": { + "data": { + "name": "peopleView", + "tableId": "ta_896a325f7e8147d2a2cda93c5d236511", + "schema": { + "name": { + "visible": true, + "readonly": false, + "order": 1, + "width": 300 + }, + "age": { + "visible": true, + "readonly": true, + "order": 2, + "width": 200 + }, + "salary": { + "visible": false, + "readonly": false + } + }, + "primaryDisplay": "name" + } + } + }, + "views": { + "value": { + "data": [ + { + "name": "peopleView", + "tableId": "ta_896a325f7e8147d2a2cda93c5d236511", + "schema": { + "name": { + "visible": true, + "readonly": false, + "order": 1, + "width": 300 + }, + "age": { + "visible": true, + "readonly": true, + "order": 2, + "width": 200 + }, + "salary": { + "visible": false, + "readonly": false + } + }, + "primaryDisplay": "name" + } + ] + } } }, "securitySchemes": { @@ -831,8 +887,7 @@ "type": "string", "enum": [ "static", - "dynamic", - "ai" + "dynamic" ], "description": "Defines whether this is a static or dynamic formula." } @@ -1042,8 +1097,7 @@ "type": "string", "enum": [ "static", - "dynamic", - "ai" + "dynamic" ], "description": "Defines whether this is a static or dynamic formula." } @@ -1264,8 +1318,7 @@ "type": "string", "enum": [ "static", - "dynamic", - "ai" + "dynamic" ], "description": "Defines whether this is a static or dynamic formula." } @@ -2024,6 +2077,161 @@ "required": [ "data" ] + }, + "view": { + "description": "The view to be created/updated.", + "type": "object", + "required": [ + "name", + "schema" + ], + "properties": { + "name": { + "description": "The name of the view.", + "type": "string" + }, + "primaryDisplay": { + "type": "string", + "description": "A column used to display rows from this view - usually used when rendered in tables." + }, + "schema": { + "type": "object", + "additionalProperties": { + "type": "object", + "properties": { + "visible": { + "type": "boolean", + "description": "Defines whether the column is visible or not - rows retrieved/updated through this view will not be able to access it." + }, + "readonly": { + "type": "boolean", + "description": "When used in combination with 'visible: true' the column will be visible in row responses but cannot be updated." + }, + "order": { + "type": "integer", + "description": "A number defining where the column shows up in tables, lowest being first." + }, + "width": { + "type": "integer", + "description": "A width for the column, defined in pixels - this affects rendering in tables." + } + } + } + } + } + }, + "viewOutput": { + "type": "object", + "properties": { + "data": { + "description": "The view to be created/updated.", + "type": "object", + "required": [ + "name", + "schema", + "id" + ], + "properties": { + "name": { + "description": "The name of the view.", + "type": "string" + }, + "primaryDisplay": { + "type": "string", + "description": "A column used to display rows from this view - usually used when rendered in tables." + }, + "schema": { + "type": "object", + "additionalProperties": { + "type": "object", + "properties": { + "visible": { + "type": "boolean", + "description": "Defines whether the column is visible or not - rows retrieved/updated through this view will not be able to access it." + }, + "readonly": { + "type": "boolean", + "description": "When used in combination with 'visible: true' the column will be visible in row responses but cannot be updated." + }, + "order": { + "type": "integer", + "description": "A number defining where the column shows up in tables, lowest being first." + }, + "width": { + "type": "integer", + "description": "A width for the column, defined in pixels - this affects rendering in tables." + } + } + } + }, + "id": { + "description": "The ID of the view.", + "type": "string" + } + } + } + }, + "required": [ + "data" + ] + }, + "viewSearch": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "description": "The view to be created/updated.", + "type": "object", + "required": [ + "name", + "schema", + "id" + ], + "properties": { + "name": { + "description": "The name of the view.", + "type": "string" + }, + "primaryDisplay": { + "type": "string", + "description": "A column used to display rows from this view - usually used when rendered in tables." + }, + "schema": { + "type": "object", + "additionalProperties": { + "type": "object", + "properties": { + "visible": { + "type": "boolean", + "description": "Defines whether the column is visible or not - rows retrieved/updated through this view will not be able to access it." + }, + "readonly": { + "type": "boolean", + "description": "When used in combination with 'visible: true' the column will be visible in row responses but cannot be updated." + }, + "order": { + "type": "integer", + "description": "A number defining where the column shows up in tables, lowest being first." + }, + "width": { + "type": "integer", + "description": "A width for the column, defined in pixels - this affects rendering in tables." + } + } + } + }, + "id": { + "description": "The ID of the view.", + "type": "string" + } + } + } + } + }, + "required": [ + "data" + ] } } }, @@ -3115,6 +3323,209 @@ } } } + }, + "/views": { + "post": { + "operationId": "viewCreate", + "summary": "Create a view", + "description": "Create a view, this can be against an internal or external table.", + "tags": [ + "views" + ], + "parameters": [ + { + "$ref": "#/components/parameters/appId" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/view" + }, + "examples": { + "view": { + "$ref": "#/components/examples/view" + } + } + } + } + }, + "responses": { + "200": { + "description": "Returns the created view, including the ID which has been generated for it.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/viewOutput" + }, + "examples": { + "view": { + "$ref": "#/components/examples/view" + } + } + } + } + } + } + } + }, + "/views/{viewId}": { + "put": { + "operationId": "viewUpdate", + "summary": "Update a view", + "description": "Update a view, this can be against an internal or external table.", + "tags": [ + "views" + ], + "parameters": [ + { + "$ref": "#/components/parameters/viewId" + }, + { + "$ref": "#/components/parameters/appId" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/view" + }, + "examples": { + "view": { + "$ref": "#/components/examples/view" + } + } + } + } + }, + "responses": { + "200": { + "description": "Returns the updated view.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/viewOutput" + }, + "examples": { + "view": { + "$ref": "#/components/examples/view" + } + } + } + } + } + } + }, + "delete": { + "operationId": "viewDestroy", + "summary": "Delete a view", + "description": "Delete a view, this can be against an internal or external table.", + "tags": [ + "views" + ], + "parameters": [ + { + "$ref": "#/components/parameters/viewId" + }, + { + "$ref": "#/components/parameters/appId" + } + ], + "responses": { + "200": { + "description": "Returns the deleted view.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/viewOutput" + }, + "examples": { + "view": { + "$ref": "#/components/examples/view" + } + } + } + } + } + } + }, + "get": { + "operationId": "viewGetById", + "summary": "Retrieve a view", + "description": "Lookup a view, this could be internal or external.", + "tags": [ + "views" + ], + "parameters": [ + { + "$ref": "#/components/parameters/viewId" + }, + { + "$ref": "#/components/parameters/appId" + } + ], + "responses": { + "200": { + "description": "Returns the retrieved view.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/viewOutput" + }, + "examples": { + "view": { + "$ref": "#/components/examples/view" + } + } + } + } + } + } + } + }, + "/views/search": { + "post": { + "operationId": "viewSearch", + "summary": "Search for views", + "description": "Based on view properties (currently only name) search for views.", + "tags": [ + "views" + ], + "parameters": [ + { + "$ref": "#/components/parameters/appId" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/viewSearch" + } + } + } + }, + "responses": { + "200": { + "description": "Returns the found views, based on the search parameters.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/viewSearch" + }, + "examples": { + "views": { + "$ref": "#/components/examples/views" + } + } + } + } + } + } + } } }, "tags": [] diff --git a/packages/server/specs/openapi.yaml b/packages/server/specs/openapi.yaml index 1e9b9921cf..9a2ce6e0da 100644 --- a/packages/server/specs/openapi.yaml +++ b/packages/server/specs/openapi.yaml @@ -442,6 +442,46 @@ components: # TYPE budibase_quota_limit_automations gauge budibase_quota_limit_automations 9007199254740991 + view: + value: + data: + name: peopleView + tableId: ta_896a325f7e8147d2a2cda93c5d236511 + schema: + name: + visible: true + readonly: false + order: 1 + width: 300 + age: + visible: true + readonly: true + order: 2 + width: 200 + salary: + visible: false + readonly: false + primaryDisplay: name + views: + value: + data: + - name: peopleView + tableId: ta_896a325f7e8147d2a2cda93c5d236511 + schema: + name: + visible: true + readonly: false + order: 1 + width: 300 + age: + visible: true + readonly: true + order: 2 + width: 200 + salary: + visible: false + readonly: false + primaryDisplay: name securitySchemes: ApiKeyAuth: type: apiKey @@ -761,7 +801,6 @@ components: enum: - static - dynamic - - ai description: Defines whether this is a static or dynamic formula. - type: object properties: @@ -931,7 +970,6 @@ components: enum: - static - dynamic - - ai description: Defines whether this is a static or dynamic formula. - type: object properties: @@ -1108,7 +1146,6 @@ components: enum: - static - dynamic - - ai description: Defines whether this is a static or dynamic formula. - type: object properties: @@ -1704,6 +1741,134 @@ components: - userIds required: - data + view: + description: The view to be created/updated. + type: object + required: + - name + - schema + properties: + name: + description: The name of the view. + type: string + primaryDisplay: + type: string + description: A column used to display rows from this view - usually used when + rendered in tables. + schema: + type: object + additionalProperties: + type: object + properties: + visible: + type: boolean + description: Defines whether the column is visible or not - rows + retrieved/updated through this view will not be able to access + it. + readonly: + type: boolean + description: "When used in combination with 'visible: true' the column will be + visible in row responses but cannot be updated." + order: + type: integer + description: A number defining where the column shows up in tables, lowest being + first. + width: + type: integer + description: A width for the column, defined in pixels - this affects rendering + in tables. + viewOutput: + type: object + properties: + data: + description: The view to be created/updated. + type: object + required: + - name + - schema + - id + properties: + name: + description: The name of the view. + type: string + primaryDisplay: + type: string + description: A column used to display rows from this view - usually used when + rendered in tables. + schema: + type: object + additionalProperties: + type: object + properties: + visible: + type: boolean + description: Defines whether the column is visible or not - rows + retrieved/updated through this view will not be able to + access it. + readonly: + type: boolean + description: "When used in combination with 'visible: true' the column will be + visible in row responses but cannot be updated." + order: + type: integer + description: A number defining where the column shows up in tables, lowest being + first. + width: + type: integer + description: A width for the column, defined in pixels - this affects rendering + in tables. + id: + description: The ID of the view. + type: string + required: + - data + viewSearch: + type: object + properties: + data: + type: array + items: + description: The view to be created/updated. + type: object + required: + - name + - schema + - id + properties: + name: + description: The name of the view. + type: string + primaryDisplay: + type: string + description: A column used to display rows from this view - usually used when + rendered in tables. + schema: + type: object + additionalProperties: + type: object + properties: + visible: + type: boolean + description: Defines whether the column is visible or not - rows + retrieved/updated through this view will not be able to + access it. + readonly: + type: boolean + description: "When used in combination with 'visible: true' the column will be + visible in row responses but cannot be updated." + order: + type: integer + description: A number defining where the column shows up in tables, lowest being + first. + width: + type: integer + description: A width for the column, defined in pixels - this affects rendering + in tables. + id: + description: The ID of the view. + type: string + required: + - data security: - ApiKeyAuth: [] paths: @@ -2359,4 +2524,123 @@ paths: examples: users: $ref: "#/components/examples/users" + /views: + post: + operationId: viewCreate + summary: Create a view + description: Create a view, this can be against an internal or external table. + tags: + - views + parameters: + - $ref: "#/components/parameters/appId" + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/view" + examples: + view: + $ref: "#/components/examples/view" + responses: + "200": + description: Returns the created view, including the ID which has been generated + for it. + content: + application/json: + schema: + $ref: "#/components/schemas/viewOutput" + examples: + view: + $ref: "#/components/examples/view" + "/views/{viewId}": + put: + operationId: viewUpdate + summary: Update a view + description: Update a view, this can be against an internal or external table. + tags: + - views + parameters: + - $ref: "#/components/parameters/viewId" + - $ref: "#/components/parameters/appId" + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/view" + examples: + view: + $ref: "#/components/examples/view" + responses: + "200": + description: Returns the updated view. + content: + application/json: + schema: + $ref: "#/components/schemas/viewOutput" + examples: + view: + $ref: "#/components/examples/view" + delete: + operationId: viewDestroy + summary: Delete a view + description: Delete a view, this can be against an internal or external table. + tags: + - views + parameters: + - $ref: "#/components/parameters/viewId" + - $ref: "#/components/parameters/appId" + responses: + "200": + description: Returns the deleted view. + content: + application/json: + schema: + $ref: "#/components/schemas/viewOutput" + examples: + view: + $ref: "#/components/examples/view" + get: + operationId: viewGetById + summary: Retrieve a view + description: Lookup a view, this could be internal or external. + tags: + - views + parameters: + - $ref: "#/components/parameters/viewId" + - $ref: "#/components/parameters/appId" + responses: + "200": + description: Returns the retrieved view. + content: + application/json: + schema: + $ref: "#/components/schemas/viewOutput" + examples: + view: + $ref: "#/components/examples/view" + /views/search: + post: + operationId: viewSearch + summary: Search for views + description: Based on view properties (currently only name) search for views. + tags: + - views + parameters: + - $ref: "#/components/parameters/appId" + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/viewSearch" + responses: + "200": + description: Returns the found views, based on the search parameters. + content: + application/json: + schema: + $ref: "#/components/schemas/viewSearch" + examples: + views: + $ref: "#/components/examples/views" tags: [] diff --git a/packages/server/specs/resources/index.ts b/packages/server/specs/resources/index.ts index c06148b7de..0d32f2a007 100644 --- a/packages/server/specs/resources/index.ts +++ b/packages/server/specs/resources/index.ts @@ -6,6 +6,7 @@ import user from "./user" import metrics from "./metrics" import misc from "./misc" import roles from "./roles" +import view from "./view" export const examples = { ...application.getExamples(), @@ -15,6 +16,8 @@ export const examples = { ...user.getExamples(), ...misc.getExamples(), ...metrics.getExamples(), + ...roles.getExamples(), + ...view.getExamples(), } export const schemas = { @@ -25,4 +28,5 @@ export const schemas = { ...user.getSchemas(), ...misc.getSchemas(), ...roles.getSchemas(), + ...view.getSchemas(), } diff --git a/packages/server/src/api/controllers/public/mapping/index.ts b/packages/server/src/api/controllers/public/mapping/index.ts index 0cdcfbbe4b..b765f4bd76 100644 --- a/packages/server/src/api/controllers/public/mapping/index.ts +++ b/packages/server/src/api/controllers/public/mapping/index.ts @@ -3,6 +3,7 @@ import applications from "./applications" import users from "./users" import rows from "./rows" import queries from "./queries" +import views from "./views" export default { ...tables, @@ -10,4 +11,5 @@ export default { ...users, ...rows, ...queries, + ...views, } diff --git a/packages/server/src/api/controllers/public/mapping/types.ts b/packages/server/src/api/controllers/public/mapping/types.ts index 9fea9b7213..6cbcfddb92 100644 --- a/packages/server/src/api/controllers/public/mapping/types.ts +++ b/packages/server/src/api/controllers/public/mapping/types.ts @@ -9,6 +9,9 @@ export type CreateApplicationParams = components["schemas"]["application"] export type Table = components["schemas"]["tableOutput"]["data"] export type CreateTableParams = components["schemas"]["table"] +export type View = components["schemas"]["viewOutput"]["data"] +export type CreateViewParams = components["schemas"]["view"] + export type Row = components["schemas"]["rowOutput"]["data"] export type RowSearch = components["schemas"]["searchOutput"] export type CreateRowParams = components["schemas"]["row"] diff --git a/packages/server/src/api/controllers/public/mapping/views.ts b/packages/server/src/api/controllers/public/mapping/views.ts new file mode 100644 index 0000000000..b6cf64542e --- /dev/null +++ b/packages/server/src/api/controllers/public/mapping/views.ts @@ -0,0 +1,26 @@ +import { View } from "./types" + +function view(body: any): View { + return { + id: body.id, + name: body.name, + schema: body.schema, + primaryDisplay: body.primaryDisplay, + } +} + +function mapView(ctx: any): { data: View } { + return { + data: view(ctx.body), + } +} + +function mapViews(ctx: any): { data: View[] } { + const tables = ctx.body.map((body: any) => view(body)) + return { data: tables } +} + +export default { + mapView, + mapViews, +} diff --git a/packages/server/src/api/controllers/public/views.ts b/packages/server/src/api/controllers/public/views.ts index 85979126ff..f3265aa70d 100644 --- a/packages/server/src/api/controllers/public/views.ts +++ b/packages/server/src/api/controllers/public/views.ts @@ -18,8 +18,7 @@ function fixView(view: ViewV2, params?: { viewId: string }) { export async function search(ctx: UserCtx, next: Next) { const { name } = ctx.request.body - // TODO: need a view search endpoint - // await controller.v2.fetch(ctx) + await controller.v2.fetch(ctx) ctx.body = stringSearch(ctx.body, name) await next() } diff --git a/packages/server/src/api/routes/public/middleware/mapper.ts b/packages/server/src/api/routes/public/middleware/mapper.ts index 03feb6cc5c..9d0cae5d61 100644 --- a/packages/server/src/api/routes/public/middleware/mapper.ts +++ b/packages/server/src/api/routes/public/middleware/mapper.ts @@ -1,9 +1,10 @@ import { Ctx } from "@budibase/types" import mapping from "../../../controllers/public/mapping" -enum Resources { +enum Resource { APPLICATION = "applications", TABLES = "tables", + VIEWS = "views", ROWS = "rows", USERS = "users", QUERIES = "queries", @@ -15,7 +16,7 @@ function isAttachment(ctx: Ctx) { } function isArrayResponse(ctx: Ctx) { - return ctx.url.endsWith(Resources.SEARCH) || Array.isArray(ctx.body) + return ctx.url.endsWith(Resource.SEARCH) || Array.isArray(ctx.body) } function noResponse(ctx: Ctx) { @@ -38,6 +39,14 @@ function processTables(ctx: Ctx) { } } +function processViews(ctx: Ctx) { + if (isArrayResponse(ctx)) { + return mapping.mapViews(ctx) + } else { + return mapping.mapView(ctx) + } +} + function processRows(ctx: Ctx) { if (isArrayResponse(ctx)) { return mapping.mapRowSearch(ctx) @@ -71,20 +80,27 @@ export default async (ctx: Ctx, next: any) => { let body = {} switch (urlParts[0]) { - case Resources.APPLICATION: + case Resource.APPLICATION: body = processApplications(ctx) break - case Resources.TABLES: - if (urlParts[2] === Resources.ROWS) { + case Resource.TABLES: + if (urlParts[2] === Resource.ROWS) { body = processRows(ctx) } else { body = processTables(ctx) } break - case Resources.USERS: + case Resource.VIEWS: + if (urlParts[2] === Resource.ROWS) { + body = processRows(ctx) + } else { + body = processViews(ctx) + } + break + case Resource.USERS: body = processUsers(ctx) break - case Resources.QUERIES: + case Resource.QUERIES: body = processQueries(ctx) break } diff --git a/packages/server/src/definitions/openapi.ts b/packages/server/src/definitions/openapi.ts index 4479c89d9d..9d68e11185 100644 --- a/packages/server/src/definitions/openapi.ts +++ b/packages/server/src/definitions/openapi.ts @@ -93,6 +93,22 @@ export interface paths { /** Based on user properties (currently only name) search for users. */ post: operations["userSearch"]; }; + "/views": { + /** Create a view, this can be against an internal or external table. */ + post: operations["viewCreate"]; + }; + "/views/{viewId}": { + /** Lookup a view, this could be internal or external. */ + get: operations["viewGetById"]; + /** Update a view, this can be against an internal or external table. */ + put: operations["viewUpdate"]; + /** Delete a view, this can be against an internal or external table. */ + delete: operations["viewDestroy"]; + }; + "/views/search": { + /** Based on view properties (currently only name) search for views. */ + post: operations["viewSearch"]; + }; } export interface components { @@ -813,6 +829,70 @@ export interface components { userIds: string[]; }; }; + /** @description The view to be created/updated. */ + view: { + /** @description The name of the view. */ + name: string; + /** @description A column used to display rows from this view - usually used when rendered in tables. */ + primaryDisplay?: string; + schema: { + [key: string]: { + /** @description Defines whether the column is visible or not - rows retrieved/updated through this view will not be able to access it. */ + visible?: boolean; + /** @description When used in combination with 'visible: true' the column will be visible in row responses but cannot be updated. */ + readonly?: boolean; + /** @description A number defining where the column shows up in tables, lowest being first. */ + order?: number; + /** @description A width for the column, defined in pixels - this affects rendering in tables. */ + width?: number; + }; + }; + }; + viewOutput: { + /** @description The view to be created/updated. */ + data: { + /** @description The name of the view. */ + name: string; + /** @description A column used to display rows from this view - usually used when rendered in tables. */ + primaryDisplay?: string; + schema: { + [key: string]: { + /** @description Defines whether the column is visible or not - rows retrieved/updated through this view will not be able to access it. */ + visible?: boolean; + /** @description When used in combination with 'visible: true' the column will be visible in row responses but cannot be updated. */ + readonly?: boolean; + /** @description A number defining where the column shows up in tables, lowest being first. */ + order?: number; + /** @description A width for the column, defined in pixels - this affects rendering in tables. */ + width?: number; + }; + }; + /** @description The ID of the view. */ + id: string; + }; + }; + viewSearch: { + data: { + /** @description The name of the view. */ + name: string; + /** @description A column used to display rows from this view - usually used when rendered in tables. */ + primaryDisplay?: string; + schema: { + [key: string]: { + /** @description Defines whether the column is visible or not - rows retrieved/updated through this view will not be able to access it. */ + visible?: boolean; + /** @description When used in combination with 'visible: true' the column will be visible in row responses but cannot be updated. */ + readonly?: boolean; + /** @description A number defining where the column shows up in tables, lowest being first. */ + order?: number; + /** @description A width for the column, defined in pixels - this affects rendering in tables. */ + width?: number; + }; + }; + /** @description The ID of the view. */ + id: string; + }[]; + }; }; parameters: { /** @description The ID of the table which this request is targeting. */ @@ -1409,6 +1489,106 @@ export interface operations { }; }; }; + /** Create a view, this can be against an internal or external table. */ + viewCreate: { + parameters: { + header: { + /** The ID of the app which this request is targeting. */ + "x-budibase-app-id": components["parameters"]["appId"]; + }; + }; + responses: { + /** Returns the created view, including the ID which has been generated for it. */ + 200: { + content: { + "application/json": components["schemas"]["viewOutput"]; + }; + }; + }; + requestBody: { + content: { + "application/json": components["schemas"]["view"]; + }; + }; + }; + /** Lookup a view, this could be internal or external. */ + viewGetById: { + parameters: { + header: { + /** The ID of the app which this request is targeting. */ + "x-budibase-app-id": components["parameters"]["appId"]; + }; + }; + responses: { + /** Returns the retrieved view. */ + 200: { + content: { + "application/json": components["schemas"]["viewOutput"]; + }; + }; + }; + }; + /** Update a view, this can be against an internal or external table. */ + viewUpdate: { + parameters: { + header: { + /** The ID of the app which this request is targeting. */ + "x-budibase-app-id": components["parameters"]["appId"]; + }; + }; + responses: { + /** Returns the updated view. */ + 200: { + content: { + "application/json": components["schemas"]["viewOutput"]; + }; + }; + }; + requestBody: { + content: { + "application/json": components["schemas"]["view"]; + }; + }; + }; + /** Delete a view, this can be against an internal or external table. */ + viewDestroy: { + parameters: { + header: { + /** The ID of the app which this request is targeting. */ + "x-budibase-app-id": components["parameters"]["appId"]; + }; + }; + responses: { + /** Returns the deleted view. */ + 200: { + content: { + "application/json": components["schemas"]["viewOutput"]; + }; + }; + }; + }; + /** Based on view properties (currently only name) search for views. */ + viewSearch: { + parameters: { + header: { + /** The ID of the app which this request is targeting. */ + "x-budibase-app-id": components["parameters"]["appId"]; + }; + }; + responses: { + /** Returns the found views, based on the search parameters. */ + 200: { + content: { + "application/json": components["schemas"]["viewSearch"]; + }; + }; + }; + requestBody: { + content: { + "application/json": components["schemas"]["viewSearch"]; + }; + }; + }; } export interface external {} From 38760897bf4a5579a1df4e5ca1e1b8bae7ed5916 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Wed, 30 Oct 2024 14:40:13 +0000 Subject: [PATCH 07/31] Updating specs again. --- packages/server/specs/openapi.json | 9 +++++++++ packages/server/specs/openapi.yaml | 7 +++++++ packages/server/specs/parameters.ts | 10 ++++++++++ packages/server/src/definitions/openapi.ts | 14 ++++++++++++++ 4 files changed, 40 insertions(+) diff --git a/packages/server/specs/openapi.json b/packages/server/specs/openapi.json index c80f947fad..3af15e1f02 100644 --- a/packages/server/specs/openapi.json +++ b/packages/server/specs/openapi.json @@ -32,6 +32,15 @@ "type": "string" } }, + "viewId": { + "in": "path", + "name": "viewId", + "required": true, + "description": "The ID of the view which this request is targeting.", + "schema": { + "type": "string" + } + }, "rowId": { "in": "path", "name": "rowId", diff --git a/packages/server/specs/openapi.yaml b/packages/server/specs/openapi.yaml index 9a2ce6e0da..094957805f 100644 --- a/packages/server/specs/openapi.yaml +++ b/packages/server/specs/openapi.yaml @@ -23,6 +23,13 @@ components: description: The ID of the table which this request is targeting. schema: type: string + viewId: + in: path + name: viewId + required: true + description: The ID of the view which this request is targeting. + schema: + type: string rowId: in: path name: rowId diff --git a/packages/server/specs/parameters.ts b/packages/server/specs/parameters.ts index a4f2b27ae4..abc26c48d6 100644 --- a/packages/server/specs/parameters.ts +++ b/packages/server/specs/parameters.ts @@ -8,6 +8,16 @@ export const tableId = { }, } +export const viewId = { + in: "path", + name: "viewId", + required: true, + description: "The ID of the view which this request is targeting.", + schema: { + type: "string", + }, +} + export const rowId = { in: "path", name: "rowId", diff --git a/packages/server/src/definitions/openapi.ts b/packages/server/src/definitions/openapi.ts index 9d68e11185..a1e5d8202f 100644 --- a/packages/server/src/definitions/openapi.ts +++ b/packages/server/src/definitions/openapi.ts @@ -897,6 +897,8 @@ export interface components { parameters: { /** @description The ID of the table which this request is targeting. */ tableId: string; + /** @description The ID of the view which this request is targeting. */ + viewId: string; /** @description The ID of the row which this request is targeting. */ rowId: string; /** @description The ID of the app which this request is targeting. */ @@ -1514,6 +1516,10 @@ export interface operations { /** Lookup a view, this could be internal or external. */ viewGetById: { parameters: { + path: { + /** The ID of the view which this request is targeting. */ + viewId: components["parameters"]["viewId"]; + }; header: { /** The ID of the app which this request is targeting. */ "x-budibase-app-id": components["parameters"]["appId"]; @@ -1531,6 +1537,10 @@ export interface operations { /** Update a view, this can be against an internal or external table. */ viewUpdate: { parameters: { + path: { + /** The ID of the view which this request is targeting. */ + viewId: components["parameters"]["viewId"]; + }; header: { /** The ID of the app which this request is targeting. */ "x-budibase-app-id": components["parameters"]["appId"]; @@ -1553,6 +1563,10 @@ export interface operations { /** Delete a view, this can be against an internal or external table. */ viewDestroy: { parameters: { + path: { + /** The ID of the view which this request is targeting. */ + viewId: components["parameters"]["viewId"]; + }; header: { /** The ID of the app which this request is targeting. */ "x-budibase-app-id": components["parameters"]["appId"]; From 037a33dc7d6e8ce34e009d13cd0a8b0ca63211d0 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Wed, 30 Oct 2024 17:12:48 +0000 Subject: [PATCH 08/31] Updating specs to correct some issues with app ID. --- packages/server/specs/openapi.json | 6 +++--- packages/server/specs/openapi.yaml | 6 +++--- packages/server/specs/parameters.ts | 4 ++-- packages/server/src/api/controllers/public/views.ts | 9 +++++++-- packages/server/src/api/routes/public/index.ts | 10 +++++++--- packages/server/src/api/routes/public/views.ts | 2 +- packages/server/src/definitions/openapi.ts | 2 +- packages/server/src/middleware/publicApi.ts | 4 ++-- 8 files changed, 26 insertions(+), 17 deletions(-) diff --git a/packages/server/specs/openapi.json b/packages/server/specs/openapi.json index 3af15e1f02..1864dd8252 100644 --- a/packages/server/specs/openapi.json +++ b/packages/server/specs/openapi.json @@ -56,7 +56,7 @@ "required": true, "description": "The ID of the app which this request is targeting.", "schema": { - "default": "{{ appId }}", + "default": "{{appId}}", "type": "string" } }, @@ -66,7 +66,7 @@ "required": true, "description": "The ID of the app which this request is targeting.", "schema": { - "default": "{{ appId }}", + "default": "{{appId}}", "type": "string" } }, @@ -3512,7 +3512,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/viewSearch" + "$ref": "#/components/schemas/nameSearch" } } } diff --git a/packages/server/specs/openapi.yaml b/packages/server/specs/openapi.yaml index 094957805f..cc10882b77 100644 --- a/packages/server/specs/openapi.yaml +++ b/packages/server/specs/openapi.yaml @@ -43,7 +43,7 @@ components: required: true description: The ID of the app which this request is targeting. schema: - default: "{{ appId }}" + default: "{{appId}}" type: string appIdUrl: in: path @@ -51,7 +51,7 @@ components: required: true description: The ID of the app which this request is targeting. schema: - default: "{{ appId }}" + default: "{{appId}}" type: string queryId: in: path @@ -2639,7 +2639,7 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/viewSearch" + $ref: "#/components/schemas/nameSearch" responses: "200": description: Returns the found views, based on the search parameters. diff --git a/packages/server/specs/parameters.ts b/packages/server/specs/parameters.ts index abc26c48d6..b3fb274567 100644 --- a/packages/server/specs/parameters.ts +++ b/packages/server/specs/parameters.ts @@ -34,7 +34,7 @@ export const appId = { required: true, description: "The ID of the app which this request is targeting.", schema: { - default: "{{ appId }}", + default: "{{appId}}", type: "string", }, } @@ -45,7 +45,7 @@ export const appIdUrl = { required: true, description: "The ID of the app which this request is targeting.", schema: { - default: "{{ appId }}", + default: "{{appId}}", type: "string", }, } diff --git a/packages/server/src/api/controllers/public/views.ts b/packages/server/src/api/controllers/public/views.ts index f3265aa70d..d43f1592b1 100644 --- a/packages/server/src/api/controllers/public/views.ts +++ b/packages/server/src/api/controllers/public/views.ts @@ -19,7 +19,7 @@ function fixView(view: ViewV2, params?: { viewId: string }) { export async function search(ctx: UserCtx, next: Next) { const { name } = ctx.request.body await controller.v2.fetch(ctx) - ctx.body = stringSearch(ctx.body, name) + ctx.body = stringSearch(ctx.body.data, name) await next() } @@ -30,7 +30,12 @@ export async function create(ctx: UserCtx, next: Next) { } export async function read(ctx: UserCtx, next: Next) { - await controller.v2.get(ctx) + await controller.v2.get({ + ...ctx, + params: { + viewId: ctx.params.viewId, + }, + }) await next() } diff --git a/packages/server/src/api/routes/public/index.ts b/packages/server/src/api/routes/public/index.ts index 36e0f74bee..bcec8f3166 100644 --- a/packages/server/src/api/routes/public/index.ts +++ b/packages/server/src/api/routes/public/index.ts @@ -4,18 +4,20 @@ import queryEndpoints from "./queries" import tableEndpoints from "./tables" import rowEndpoints from "./rows" import userEndpoints from "./users" +import viewEndpoints from "./views" +import roleEndpoints from "./roles" import authorized from "../../../middleware/authorized" import publicApi from "../../../middleware/publicApi" import { paramResource, paramSubResource } from "../../../middleware/resourceId" -import { PermissionType, PermissionLevel } from "@budibase/types" +import { PermissionLevel, PermissionType } from "@budibase/types" import { CtxFn } from "./utils/Endpoint" import mapperMiddleware from "./middleware/mapper" import env from "../../../environment" +import { middleware, redis } from "@budibase/backend-core" +import { SelectableDatabase } from "@budibase/backend-core/src/redis/utils" // below imports don't have declaration files const Router = require("@koa/router") const { RateLimit, Stores } = require("koa2-ratelimit") -import { middleware, redis } from "@budibase/backend-core" -import { SelectableDatabase } from "@budibase/backend-core/src/redis/utils" interface KoaRateLimitOptions { socket: { @@ -145,8 +147,10 @@ function applyRoutes( } applyAdminRoutes(metricEndpoints) +applyAdminRoutes(roleEndpoints) applyRoutes(appEndpoints, PermissionType.APP, "appId") applyRoutes(tableEndpoints, PermissionType.TABLE, "tableId") +applyRoutes(viewEndpoints, PermissionType.VIEW, "viewId") applyRoutes(userEndpoints, PermissionType.USER, "userId") applyRoutes(queryEndpoints, PermissionType.QUERY, "queryId") // needs to be applied last for routing purposes, don't override other endpoints diff --git a/packages/server/src/api/routes/public/views.ts b/packages/server/src/api/routes/public/views.ts index 7c182d105f..139d79be1f 100644 --- a/packages/server/src/api/routes/public/views.ts +++ b/packages/server/src/api/routes/public/views.ts @@ -144,7 +144,7 @@ read.push(new Endpoint("get", "/views/:viewId", controller.read)) * content: * application/json: * schema: - * $ref: '#/components/schemas/viewSearch' + * $ref: '#/components/schemas/nameSearch' * responses: * 200: * description: Returns the found views, based on the search parameters. diff --git a/packages/server/src/definitions/openapi.ts b/packages/server/src/definitions/openapi.ts index a1e5d8202f..4d6ab6a06c 100644 --- a/packages/server/src/definitions/openapi.ts +++ b/packages/server/src/definitions/openapi.ts @@ -1599,7 +1599,7 @@ export interface operations { }; requestBody: { content: { - "application/json": components["schemas"]["viewSearch"]; + "application/json": components["schemas"]["nameSearch"]; }; }; }; diff --git a/packages/server/src/middleware/publicApi.ts b/packages/server/src/middleware/publicApi.ts index e3897d1a49..da10dd3539 100644 --- a/packages/server/src/middleware/publicApi.ts +++ b/packages/server/src/middleware/publicApi.ts @@ -1,8 +1,8 @@ import { constants, utils } from "@budibase/backend-core" -import { BBContext } from "@budibase/types" +import { Ctx } from "@budibase/types" export default function ({ requiresAppId }: { requiresAppId?: boolean } = {}) { - return async (ctx: BBContext, next: any) => { + return async (ctx: Ctx, next: any) => { const appId = await utils.getAppIdFromCtx(ctx) if (requiresAppId && !appId) { ctx.throw( From 9d8566189387a954b9986ec6627b7ec0b6ae0e60 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Thu, 31 Oct 2024 17:42:46 +0000 Subject: [PATCH 09/31] Finishing specs to explain how to configure a calculation view. --- packages/server/specs/openapi.json | 287 ++++++++++++++---- packages/server/specs/openapi.yaml | 254 ++++++++++++---- packages/server/specs/resources/view.ts | 48 ++- .../server/src/api/controllers/public/rows.ts | 20 +- .../src/api/controllers/public/views.ts | 16 +- packages/server/src/api/routes/public/rows.ts | 36 +++ packages/server/src/definitions/openapi.ts | 151 +++++++-- 7 files changed, 661 insertions(+), 151 deletions(-) diff --git a/packages/server/specs/openapi.json b/packages/server/specs/openapi.json index 1864dd8252..d45a8f28c9 100644 --- a/packages/server/specs/openapi.json +++ b/packages/server/specs/openapi.json @@ -2099,6 +2099,13 @@ "description": "The name of the view.", "type": "string" }, + "type": { + "description": "The type of view - standard (empty value) or calculation.", + "type": "string", + "enum": [ + "calculation" + ] + }, "primaryDisplay": { "type": "string", "description": "A column used to display rows from this view - usually used when rendered in tables." @@ -2106,25 +2113,65 @@ "schema": { "type": "object", "additionalProperties": { - "type": "object", - "properties": { - "visible": { - "type": "boolean", - "description": "Defines whether the column is visible or not - rows retrieved/updated through this view will not be able to access it." + "oneOf": [ + { + "type": "object", + "properties": { + "visible": { + "type": "boolean", + "description": "Defines whether the column is visible or not - rows retrieved/updated through this view will not be able to access it." + }, + "readonly": { + "type": "boolean", + "description": "When used in combination with 'visible: true' the column will be visible in row responses but cannot be updated." + }, + "order": { + "type": "integer", + "description": "A number defining where the column shows up in tables, lowest being first." + }, + "width": { + "type": "integer", + "description": "A width for the column, defined in pixels - this affects rendering in tables." + }, + "column": { + "type": "array", + "description": "If this is a relationship column, we can set the columns we wish to include", + "items": { + "type": "object", + "properties": { + "readonly": { + "type": "boolean" + } + } + } + } + } }, - "readonly": { - "type": "boolean", - "description": "When used in combination with 'visible: true' the column will be visible in row responses but cannot be updated." - }, - "order": { - "type": "integer", - "description": "A number defining where the column shows up in tables, lowest being first." - }, - "width": { - "type": "integer", - "description": "A width for the column, defined in pixels - this affects rendering in tables." + { + "type": "object", + "properties": { + "calculationType": { + "type": "string", + "description": "This column should be built from a calculation, specifying a type and field. It is important to note when a calculation is configured all non-calculation columns will be used for grouping.", + "enum": [ + "sum", + "avg", + "count", + "min", + "max" + ] + }, + "field": { + "type": "string", + "description": "The field from the table to perform the calculation on." + }, + "distinct": { + "type": "boolean", + "description": "Can be used in tandem with the count calculation type, to count unique entries." + } + } } - } + ] } } } @@ -2145,6 +2192,13 @@ "description": "The name of the view.", "type": "string" }, + "type": { + "description": "The type of view - standard (empty value) or calculation.", + "type": "string", + "enum": [ + "calculation" + ] + }, "primaryDisplay": { "type": "string", "description": "A column used to display rows from this view - usually used when rendered in tables." @@ -2152,25 +2206,65 @@ "schema": { "type": "object", "additionalProperties": { - "type": "object", - "properties": { - "visible": { - "type": "boolean", - "description": "Defines whether the column is visible or not - rows retrieved/updated through this view will not be able to access it." + "oneOf": [ + { + "type": "object", + "properties": { + "visible": { + "type": "boolean", + "description": "Defines whether the column is visible or not - rows retrieved/updated through this view will not be able to access it." + }, + "readonly": { + "type": "boolean", + "description": "When used in combination with 'visible: true' the column will be visible in row responses but cannot be updated." + }, + "order": { + "type": "integer", + "description": "A number defining where the column shows up in tables, lowest being first." + }, + "width": { + "type": "integer", + "description": "A width for the column, defined in pixels - this affects rendering in tables." + }, + "column": { + "type": "array", + "description": "If this is a relationship column, we can set the columns we wish to include", + "items": { + "type": "object", + "properties": { + "readonly": { + "type": "boolean" + } + } + } + } + } }, - "readonly": { - "type": "boolean", - "description": "When used in combination with 'visible: true' the column will be visible in row responses but cannot be updated." - }, - "order": { - "type": "integer", - "description": "A number defining where the column shows up in tables, lowest being first." - }, - "width": { - "type": "integer", - "description": "A width for the column, defined in pixels - this affects rendering in tables." + { + "type": "object", + "properties": { + "calculationType": { + "type": "string", + "description": "This column should be built from a calculation, specifying a type and field. It is important to note when a calculation is configured all non-calculation columns will be used for grouping.", + "enum": [ + "sum", + "avg", + "count", + "min", + "max" + ] + }, + "field": { + "type": "string", + "description": "The field from the table to perform the calculation on." + }, + "distinct": { + "type": "boolean", + "description": "Can be used in tandem with the count calculation type, to count unique entries." + } + } } - } + ] } }, "id": { @@ -2202,6 +2296,13 @@ "description": "The name of the view.", "type": "string" }, + "type": { + "description": "The type of view - standard (empty value) or calculation.", + "type": "string", + "enum": [ + "calculation" + ] + }, "primaryDisplay": { "type": "string", "description": "A column used to display rows from this view - usually used when rendered in tables." @@ -2209,25 +2310,65 @@ "schema": { "type": "object", "additionalProperties": { - "type": "object", - "properties": { - "visible": { - "type": "boolean", - "description": "Defines whether the column is visible or not - rows retrieved/updated through this view will not be able to access it." + "oneOf": [ + { + "type": "object", + "properties": { + "visible": { + "type": "boolean", + "description": "Defines whether the column is visible or not - rows retrieved/updated through this view will not be able to access it." + }, + "readonly": { + "type": "boolean", + "description": "When used in combination with 'visible: true' the column will be visible in row responses but cannot be updated." + }, + "order": { + "type": "integer", + "description": "A number defining where the column shows up in tables, lowest being first." + }, + "width": { + "type": "integer", + "description": "A width for the column, defined in pixels - this affects rendering in tables." + }, + "column": { + "type": "array", + "description": "If this is a relationship column, we can set the columns we wish to include", + "items": { + "type": "object", + "properties": { + "readonly": { + "type": "boolean" + } + } + } + } + } }, - "readonly": { - "type": "boolean", - "description": "When used in combination with 'visible: true' the column will be visible in row responses but cannot be updated." - }, - "order": { - "type": "integer", - "description": "A number defining where the column shows up in tables, lowest being first." - }, - "width": { - "type": "integer", - "description": "A width for the column, defined in pixels - this affects rendering in tables." + { + "type": "object", + "properties": { + "calculationType": { + "type": "string", + "description": "This column should be built from a calculation, specifying a type and field. It is important to note when a calculation is configured all non-calculation columns will be used for grouping.", + "enum": [ + "sum", + "avg", + "count", + "min", + "max" + ] + }, + "field": { + "type": "string", + "description": "The field from the table to perform the calculation on." + }, + "distinct": { + "type": "boolean", + "description": "Can be used in tandem with the count calculation type, to count unique entries." + } + } } - } + ] } }, "id": { @@ -2958,6 +3099,50 @@ } } }, + "/views/{viewId}/rows/search": { + "post": { + "operationId": "rowViewSearch", + "summary": "Search for rows in a view", + "tags": [ + "rows" + ], + "parameters": [ + { + "$ref": "#/components/parameters/viewId" + }, + { + "$ref": "#/components/parameters/appId" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/rowSearch" + } + } + } + }, + "responses": { + "200": { + "description": "The response will contain an array of rows that match the search parameters.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/searchOutput" + }, + "examples": { + "search": { + "$ref": "#/components/examples/rows" + } + } + } + } + } + } + } + }, "/tables": { "post": { "operationId": "tableCreate", diff --git a/packages/server/specs/openapi.yaml b/packages/server/specs/openapi.yaml index cc10882b77..0e551207ed 100644 --- a/packages/server/specs/openapi.yaml +++ b/packages/server/specs/openapi.yaml @@ -1758,6 +1758,11 @@ components: name: description: The name of the view. type: string + type: + description: The type of view - standard (empty value) or calculation. + type: string + enum: + - calculation primaryDisplay: type: string description: A column used to display rows from this view - usually used when @@ -1765,47 +1770,8 @@ components: schema: type: object additionalProperties: - type: object - properties: - visible: - type: boolean - description: Defines whether the column is visible or not - rows - retrieved/updated through this view will not be able to access - it. - readonly: - type: boolean - description: "When used in combination with 'visible: true' the column will be - visible in row responses but cannot be updated." - order: - type: integer - description: A number defining where the column shows up in tables, lowest being - first. - width: - type: integer - description: A width for the column, defined in pixels - this affects rendering - in tables. - viewOutput: - type: object - properties: - data: - description: The view to be created/updated. - type: object - required: - - name - - schema - - id - properties: - name: - description: The name of the view. - type: string - primaryDisplay: - type: string - description: A column used to display rows from this view - usually used when - rendered in tables. - schema: - type: object - additionalProperties: - type: object + oneOf: + - type: object properties: visible: type: boolean @@ -1824,6 +1790,112 @@ components: type: integer description: A width for the column, defined in pixels - this affects rendering in tables. + column: + type: array + description: If this is a relationship column, we can set the columns we wish to + include + items: + type: object + properties: + readonly: + type: boolean + - type: object + properties: + calculationType: + type: string + description: This column should be built from a calculation, specifying a type + and field. It is important to note when a calculation is + configured all non-calculation columns will be used for + grouping. + enum: + - sum + - avg + - count + - min + - max + field: + type: string + description: The field from the table to perform the calculation on. + distinct: + type: boolean + description: Can be used in tandem with the count calculation type, to count + unique entries. + viewOutput: + type: object + properties: + data: + description: The view to be created/updated. + type: object + required: + - name + - schema + - id + properties: + name: + description: The name of the view. + type: string + type: + description: The type of view - standard (empty value) or calculation. + type: string + enum: + - calculation + primaryDisplay: + type: string + description: A column used to display rows from this view - usually used when + rendered in tables. + schema: + type: object + additionalProperties: + oneOf: + - type: object + properties: + visible: + type: boolean + description: Defines whether the column is visible or not - rows + retrieved/updated through this view will not be able + to access it. + readonly: + type: boolean + description: "When used in combination with 'visible: true' the column will be + visible in row responses but cannot be updated." + order: + type: integer + description: A number defining where the column shows up in tables, lowest being + first. + width: + type: integer + description: A width for the column, defined in pixels - this affects rendering + in tables. + column: + type: array + description: If this is a relationship column, we can set the columns we wish to + include + items: + type: object + properties: + readonly: + type: boolean + - type: object + properties: + calculationType: + type: string + description: This column should be built from a calculation, specifying a type + and field. It is important to note when a calculation + is configured all non-calculation columns will be used + for grouping. + enum: + - sum + - avg + - count + - min + - max + field: + type: string + description: The field from the table to perform the calculation on. + distinct: + type: boolean + description: Can be used in tandem with the count calculation type, to count + unique entries. id: description: The ID of the view. type: string @@ -1845,6 +1917,11 @@ components: name: description: The name of the view. type: string + type: + description: The type of view - standard (empty value) or calculation. + type: string + enum: + - calculation primaryDisplay: type: string description: A column used to display rows from this view - usually used when @@ -1852,25 +1929,56 @@ components: schema: type: object additionalProperties: - type: object - properties: - visible: - type: boolean - description: Defines whether the column is visible or not - rows - retrieved/updated through this view will not be able to - access it. - readonly: - type: boolean - description: "When used in combination with 'visible: true' the column will be - visible in row responses but cannot be updated." - order: - type: integer - description: A number defining where the column shows up in tables, lowest being - first. - width: - type: integer - description: A width for the column, defined in pixels - this affects rendering - in tables. + oneOf: + - type: object + properties: + visible: + type: boolean + description: Defines whether the column is visible or not - rows + retrieved/updated through this view will not be able + to access it. + readonly: + type: boolean + description: "When used in combination with 'visible: true' the column will be + visible in row responses but cannot be updated." + order: + type: integer + description: A number defining where the column shows up in tables, lowest being + first. + width: + type: integer + description: A width for the column, defined in pixels - this affects rendering + in tables. + column: + type: array + description: If this is a relationship column, we can set the columns we wish to + include + items: + type: object + properties: + readonly: + type: boolean + - type: object + properties: + calculationType: + type: string + description: This column should be built from a calculation, specifying a type + and field. It is important to note when a + calculation is configured all non-calculation + columns will be used for grouping. + enum: + - sum + - avg + - count + - min + - max + field: + type: string + description: The field from the table to perform the calculation on. + distinct: + type: boolean + description: Can be used in tandem with the count calculation type, to count + unique entries. id: description: The ID of the view. type: string @@ -2308,6 +2416,32 @@ paths: examples: search: $ref: "#/components/examples/rows" + "/views/{viewId}/rows/search": + post: + operationId: rowViewSearch + summary: Search for rows in a view + tags: + - rows + parameters: + - $ref: "#/components/parameters/viewId" + - $ref: "#/components/parameters/appId" + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/rowSearch" + responses: + "200": + description: The response will contain an array of rows that match the search + parameters. + content: + application/json: + schema: + $ref: "#/components/schemas/searchOutput" + examples: + search: + $ref: "#/components/examples/rows" /tables: post: operationId: tableCreate diff --git a/packages/server/specs/resources/view.ts b/packages/server/specs/resources/view.ts index 5afdf78a0e..e798883ced 100644 --- a/packages/server/specs/resources/view.ts +++ b/packages/server/specs/resources/view.ts @@ -1,5 +1,6 @@ import { object } from "./utils" import Resource from "./utils/Resource" +import { CalculationType } from "@budibase/types" const view = { name: "peopleView", @@ -46,6 +47,19 @@ const baseColumnDef = { description: "A width for the column, defined in pixels - this affects rendering in tables.", }, + column: { + type: "array", + description: + "If this is a relationship column, we can set the columns we wish to include", + items: { + type: "object", + properties: { + readonly: { + type: "boolean", + }, + }, + }, + }, } const viewSchema = { @@ -57,6 +71,11 @@ const viewSchema = { description: "The name of the view.", type: "string", }, + type: { + description: "The type of view - standard (empty value) or calculation.", + type: "string", + enum: ["calculation"], + }, primaryDisplay: { type: "string", description: @@ -65,8 +84,33 @@ const viewSchema = { schema: { type: "object", additionalProperties: { - type: "object", - properties: baseColumnDef, + oneOf: [ + { + type: "object", + properties: baseColumnDef, + }, + { + type: "object", + properties: { + calculationType: { + type: "string", + description: + "This column should be built from a calculation, specifying a type and field. It is important to note when a calculation is configured all non-calculation columns will be used for grouping.", + enum: Object.values(CalculationType), + }, + field: { + type: "string", + description: + "The field from the table to perform the calculation on.", + }, + distinct: { + type: "boolean", + description: + "Can be used in tandem with the count calculation type, to count unique entries.", + }, + }, + }, + ], }, }, }, diff --git a/packages/server/src/api/controllers/public/rows.ts b/packages/server/src/api/controllers/public/rows.ts index 16403b06c9..634a41ed85 100644 --- a/packages/server/src/api/controllers/public/rows.ts +++ b/packages/server/src/api/controllers/public/rows.ts @@ -22,13 +22,13 @@ export function fixRow(row: Row, params: any) { return row } -export async function search(ctx: UserCtx, next: Next) { +function getSearchParameters(ctx: UserCtx) { let { sort, paginate, bookmark, limit, query } = ctx.request.body // update the body to the correct format of the internal search if (!sort) { sort = {} } - ctx.request.body = { + return { sort: sort.column, sortType: sort.type, sortOrder: sort.order, @@ -37,10 +37,25 @@ export async function search(ctx: UserCtx, next: Next) { limit, query, } +} + +export async function search(ctx: UserCtx, next: Next) { + ctx.request.body = getSearchParameters(ctx) await rowController.search(ctx) await next() } +export async function viewSearch(ctx: UserCtx, next: Next) { + ctx.request.body = getSearchParameters(ctx) + await rowController.views.searchView({ + ...ctx, + params: { + viewId: ctx.params.viewId, + }, + }) + await next() +} + export async function create(ctx: UserCtx, next: Next) { ctx.request.body = fixRow(ctx.request.body, ctx.params) await rowController.save(ctx) @@ -79,4 +94,5 @@ export default { update, destroy, search, + viewSearch, } diff --git a/packages/server/src/api/controllers/public/views.ts b/packages/server/src/api/controllers/public/views.ts index d43f1592b1..e400e850ea 100644 --- a/packages/server/src/api/controllers/public/views.ts +++ b/packages/server/src/api/controllers/public/views.ts @@ -40,12 +40,16 @@ export async function read(ctx: UserCtx, next: Next) { } export async function update(ctx: UserCtx, next: Next) { - // TODO: this is more complex - no rev on views - // ctx.request.body = await addRev( - // fixView(ctx.request.body, ctx.params), - // ctx.params.tableId - // ) - await controller.v2.update(ctx) + const viewId = ctx.params.viewId + await controller.v2.update({ + ...ctx, + body: { + data: fixView(ctx.body, { viewId }), + }, + params: { + viewId, + }, + }) await next() } diff --git a/packages/server/src/api/routes/public/rows.ts b/packages/server/src/api/routes/public/rows.ts index 80ad6d6434..2085c5cf0f 100644 --- a/packages/server/src/api/routes/public/rows.ts +++ b/packages/server/src/api/routes/public/rows.ts @@ -168,4 +168,40 @@ read.push( ).addMiddleware(externalSearchValidator()) ) +/** + * @openapi + * /views/{viewId}/rows/search: + * post: + * operationId: rowViewSearch + * summary: Search for rows in a view + * tags: + * - rows + * parameters: + * - $ref: '#/components/parameters/viewId' + * - $ref: '#/components/parameters/appId' + * requestBody: + * required: true + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/rowSearch' + * responses: + * 200: + * description: The response will contain an array of rows that match the search parameters. + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/searchOutput' + * examples: + * search: + * $ref: '#/components/examples/rows' + */ +read.push( + new Endpoint( + "post", + "/views/:viewId/rows/search", + controller.search + ).addMiddleware(externalSearchValidator()) +) + export default { read, write } diff --git a/packages/server/src/definitions/openapi.ts b/packages/server/src/definitions/openapi.ts index 4d6ab6a06c..fed19ec5cf 100644 --- a/packages/server/src/definitions/openapi.ts +++ b/packages/server/src/definitions/openapi.ts @@ -65,6 +65,9 @@ export interface paths { "/tables/{tableId}/rows/search": { post: operations["rowSearch"]; }; + "/views/{viewId}/rows/search": { + post: operations["rowViewSearch"]; + }; "/tables": { /** Create a table, this could be internal or external. */ post: operations["tableCreate"]; @@ -833,19 +836,40 @@ export interface components { view: { /** @description The name of the view. */ name: string; + /** + * @description The type of view - standard (empty value) or calculation. + * @enum {string} + */ + type?: "calculation"; /** @description A column used to display rows from this view - usually used when rendered in tables. */ primaryDisplay?: string; schema: { - [key: string]: { - /** @description Defines whether the column is visible or not - rows retrieved/updated through this view will not be able to access it. */ - visible?: boolean; - /** @description When used in combination with 'visible: true' the column will be visible in row responses but cannot be updated. */ - readonly?: boolean; - /** @description A number defining where the column shows up in tables, lowest being first. */ - order?: number; - /** @description A width for the column, defined in pixels - this affects rendering in tables. */ - width?: number; - }; + [key: string]: + | { + /** @description Defines whether the column is visible or not - rows retrieved/updated through this view will not be able to access it. */ + visible?: boolean; + /** @description When used in combination with 'visible: true' the column will be visible in row responses but cannot be updated. */ + readonly?: boolean; + /** @description A number defining where the column shows up in tables, lowest being first. */ + order?: number; + /** @description A width for the column, defined in pixels - this affects rendering in tables. */ + width?: number; + /** @description If this is a relationship column, we can set the columns we wish to include */ + column?: { + readonly?: boolean; + }[]; + } + | { + /** + * @description This column should be built from a calculation, specifying a type and field. It is important to note when a calculation is configured all non-calculation columns will be used for grouping. + * @enum {string} + */ + calculationType?: "sum" | "avg" | "count" | "min" | "max"; + /** @description The field from the table to perform the calculation on. */ + field?: string; + /** @description Can be used in tandem with the count calculation type, to count unique entries. */ + distinct?: boolean; + }; }; }; viewOutput: { @@ -853,19 +877,40 @@ export interface components { data: { /** @description The name of the view. */ name: string; + /** + * @description The type of view - standard (empty value) or calculation. + * @enum {string} + */ + type?: "calculation"; /** @description A column used to display rows from this view - usually used when rendered in tables. */ primaryDisplay?: string; schema: { - [key: string]: { - /** @description Defines whether the column is visible or not - rows retrieved/updated through this view will not be able to access it. */ - visible?: boolean; - /** @description When used in combination with 'visible: true' the column will be visible in row responses but cannot be updated. */ - readonly?: boolean; - /** @description A number defining where the column shows up in tables, lowest being first. */ - order?: number; - /** @description A width for the column, defined in pixels - this affects rendering in tables. */ - width?: number; - }; + [key: string]: + | { + /** @description Defines whether the column is visible or not - rows retrieved/updated through this view will not be able to access it. */ + visible?: boolean; + /** @description When used in combination with 'visible: true' the column will be visible in row responses but cannot be updated. */ + readonly?: boolean; + /** @description A number defining where the column shows up in tables, lowest being first. */ + order?: number; + /** @description A width for the column, defined in pixels - this affects rendering in tables. */ + width?: number; + /** @description If this is a relationship column, we can set the columns we wish to include */ + column?: { + readonly?: boolean; + }[]; + } + | { + /** + * @description This column should be built from a calculation, specifying a type and field. It is important to note when a calculation is configured all non-calculation columns will be used for grouping. + * @enum {string} + */ + calculationType?: "sum" | "avg" | "count" | "min" | "max"; + /** @description The field from the table to perform the calculation on. */ + field?: string; + /** @description Can be used in tandem with the count calculation type, to count unique entries. */ + distinct?: boolean; + }; }; /** @description The ID of the view. */ id: string; @@ -875,19 +920,40 @@ export interface components { data: { /** @description The name of the view. */ name: string; + /** + * @description The type of view - standard (empty value) or calculation. + * @enum {string} + */ + type?: "calculation"; /** @description A column used to display rows from this view - usually used when rendered in tables. */ primaryDisplay?: string; schema: { - [key: string]: { - /** @description Defines whether the column is visible or not - rows retrieved/updated through this view will not be able to access it. */ - visible?: boolean; - /** @description When used in combination with 'visible: true' the column will be visible in row responses but cannot be updated. */ - readonly?: boolean; - /** @description A number defining where the column shows up in tables, lowest being first. */ - order?: number; - /** @description A width for the column, defined in pixels - this affects rendering in tables. */ - width?: number; - }; + [key: string]: + | { + /** @description Defines whether the column is visible or not - rows retrieved/updated through this view will not be able to access it. */ + visible?: boolean; + /** @description When used in combination with 'visible: true' the column will be visible in row responses but cannot be updated. */ + readonly?: boolean; + /** @description A number defining where the column shows up in tables, lowest being first. */ + order?: number; + /** @description A width for the column, defined in pixels - this affects rendering in tables. */ + width?: number; + /** @description If this is a relationship column, we can set the columns we wish to include */ + column?: { + readonly?: boolean; + }[]; + } + | { + /** + * @description This column should be built from a calculation, specifying a type and field. It is important to note when a calculation is configured all non-calculation columns will be used for grouping. + * @enum {string} + */ + calculationType?: "sum" | "avg" | "count" | "min" | "max"; + /** @description The field from the table to perform the calculation on. */ + field?: string; + /** @description Can be used in tandem with the count calculation type, to count unique entries. */ + distinct?: boolean; + }; }; /** @description The ID of the view. */ id: string; @@ -1295,6 +1361,31 @@ export interface operations { }; }; }; + rowViewSearch: { + parameters: { + path: { + /** The ID of the view which this request is targeting. */ + viewId: components["parameters"]["viewId"]; + }; + header: { + /** The ID of the app which this request is targeting. */ + "x-budibase-app-id": components["parameters"]["appId"]; + }; + }; + responses: { + /** The response will contain an array of rows that match the search parameters. */ + 200: { + content: { + "application/json": components["schemas"]["searchOutput"]; + }; + }; + }; + requestBody: { + content: { + "application/json": components["schemas"]["rowSearch"]; + }; + }; + }; /** Create a table, this could be internal or external. */ tableCreate: { parameters: { From b3ee2660230f426489b9a6b49860e3af7a1f61a0 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Fri, 1 Nov 2024 17:53:26 +0000 Subject: [PATCH 10/31] Adding test case for creation, updating validator. --- .../src/api/controllers/public/views.ts | 3 + .../src/api/routes/public/tests/Request.ts | 82 ++++++++++++++++++- .../src/api/routes/public/tests/views.spec.ts | 32 ++++++++ .../server/src/api/routes/utils/validators.ts | 36 +++++++- 4 files changed, 145 insertions(+), 8 deletions(-) create mode 100644 packages/server/src/api/routes/public/tests/views.spec.ts diff --git a/packages/server/src/api/controllers/public/views.ts b/packages/server/src/api/controllers/public/views.ts index e400e850ea..135d3c0128 100644 --- a/packages/server/src/api/controllers/public/views.ts +++ b/packages/server/src/api/controllers/public/views.ts @@ -13,6 +13,9 @@ function fixView(view: ViewV2, params?: { viewId: string }) { if (!view.version) { view.version = 2 } + if (!view.query) { + view.query = {} + } return view } diff --git a/packages/server/src/api/routes/public/tests/Request.ts b/packages/server/src/api/routes/public/tests/Request.ts index 92a4996124..7b3c5b6e06 100644 --- a/packages/server/src/api/routes/public/tests/Request.ts +++ b/packages/server/src/api/routes/public/tests/Request.ts @@ -1,10 +1,19 @@ -import { User, Table, SearchFilters, Row } from "@budibase/types" +import { + User, + Table, + SearchFilters, + Row, + ViewV2Schema, + ViewV2, + ViewV2Type, +} from "@budibase/types" import { HttpMethod, MakeRequestResponse, generateMakeRequest } from "./utils" import TestConfiguration from "../../../../tests/utilities/TestConfiguration" -import { Expectations } from "../../../../tests/utilities/api/base" type RequestOpts = { internal?: boolean; appId?: string } +type Response = { data: T } + export interface PublicAPIExpectations { status?: number body?: Record @@ -15,6 +24,7 @@ export class PublicAPIRequest { private appId: string | undefined tables: PublicTableAPI + views: PublicViewAPI rows: PublicRowAPI apiKey: string @@ -28,6 +38,7 @@ export class PublicAPIRequest { this.appId = appId this.tables = new PublicTableAPI(this) this.rows = new PublicRowAPI(this) + this.views = new PublicViewAPI(this) } static async init(config: TestConfiguration, user: User, opts?: RequestOpts) { @@ -73,7 +84,7 @@ export class PublicTableAPI { async create( table: Table, expectations?: PublicAPIExpectations - ): Promise<{ data: Table }> { + ): Promise> { return this.request.send("post", "/tables", table, expectations) } } @@ -89,7 +100,7 @@ export class PublicRowAPI { tableId: string, query: SearchFilters, expectations?: PublicAPIExpectations - ): Promise<{ data: Row[] }> { + ): Promise> { return this.request.send( "post", `/tables/${tableId}/rows/search`, @@ -99,4 +110,67 @@ export class PublicRowAPI { expectations ) } + + async viewSearch( + viewId: string, + query: SearchFilters, + expectations?: PublicAPIExpectations + ): Promise> { + return this.request.send( + "post", + `/views/${viewId}/rows/search`, + { + query, + }, + expectations + ) + } +} + +export class PublicViewAPI { + request: PublicAPIRequest + + constructor(request: PublicAPIRequest) { + this.request = request + } + + async create( + view: Omit, + expectations?: PublicAPIExpectations + ): Promise> { + return this.request.send("post", "/views", view, expectations) + } + + async update( + viewId: string, + view: Omit, + expectations?: PublicAPIExpectations + ): Promise> { + return this.request.send("put", `/views/${viewId}`, view, expectations) + } + + async destroy( + viewId: string, + expectations?: PublicAPIExpectations + ): Promise { + return this.request.send( + "delete", + `/views/${viewId}`, + undefined, + expectations + ) + } + + async find( + viewId: string, + expectations?: PublicAPIExpectations + ): Promise> { + return this.request.send("get", `/views/${viewId}`, undefined, expectations) + } + + async fetch( + expectations?: PublicAPIExpectations + ): Promise> { + return this.request.send("get", "/views", undefined, expectations) + } } diff --git a/packages/server/src/api/routes/public/tests/views.spec.ts b/packages/server/src/api/routes/public/tests/views.spec.ts new file mode 100644 index 0000000000..dce2e1ab7a --- /dev/null +++ b/packages/server/src/api/routes/public/tests/views.spec.ts @@ -0,0 +1,32 @@ +import * as setup from "../../tests/utilities" +import { basicTable } from "../../../../tests/utilities/structures" +import { Table } from "@budibase/types" +import { PublicAPIRequest } from "./Request" + +describe("check public API security", () => { + const config = setup.getConfig() + let request: PublicAPIRequest, table: Table + + beforeAll(async () => { + await config.init() + request = await PublicAPIRequest.init(config, await config.globalUser()) + table = (await request.tables.create(basicTable())).data + }) + + it("should be able to create a view", async () => { + await request.views.create( + { + name: "view", + tableId: table._id!, + query: {}, + schema: { + name: { + readonly: true, + visible: true, + }, + }, + }, + { status: 201 } + ) + }) +}) diff --git a/packages/server/src/api/routes/utils/validators.ts b/packages/server/src/api/routes/utils/validators.ts index 68ebd72c5e..9bae8b64d9 100644 --- a/packages/server/src/api/routes/utils/validators.ts +++ b/packages/server/src/api/routes/utils/validators.ts @@ -9,6 +9,9 @@ import { Table, WebhookActionType, BuiltinPermissionID, + ViewV2Type, + SortOrder, + SortType, } from "@budibase/types" import Joi, { CustomValidator } from "joi" import { ValidSnippetNameRegex, helpers } from "@budibase/shared-core" @@ -67,7 +70,26 @@ export function tableValidator() { } export function viewValidator() { - return auth.joiValidator.body(Joi.object()) + return auth.joiValidator.body( + Joi.object({ + id: OPTIONAL_STRING, + tableId: Joi.string().required(), + name: Joi.string().required(), + type: Joi.string().optional().valid(null, ViewV2Type.CALCULATION), + primaryDisplay: OPTIONAL_STRING, + schema: Joi.object().required(), + query: searchFiltersValidator().optional(), + sort: Joi.object({ + field: Joi.string().required(), + order: Joi.string() + .optional() + .valid(...Object.values(SortOrder)), + type: Joi.string() + .optional() + .valid(...Object.values(SortType)), + }).optional(), + }) + ) } export function nameValidator() { @@ -95,8 +117,7 @@ export function datasourceValidator() { ) } -function filterObject(opts?: { unknown: boolean }) { - const { unknown = true } = opts || {} +function searchFiltersValidator() { const conditionalFilteringObject = () => Joi.object({ conditions: Joi.array().items(Joi.link("#schema")).required(), @@ -123,7 +144,14 @@ function filterObject(opts?: { unknown: boolean }) { fuzzyOr: Joi.forbidden(), documentType: Joi.forbidden(), } - return Joi.object(filtersValidators).unknown(unknown).id("schema") + + return Joi.object(filtersValidators) +} + +function filterObject(opts?: { unknown: boolean }) { + const { unknown = true } = opts || {} + + return searchFiltersValidator().unknown(unknown).id("schema") } export function internalSearchValidator() { From 6e5aab1f3f9269f5da24084ed204d5bcb6df3ae5 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Fri, 1 Nov 2024 17:53:38 +0000 Subject: [PATCH 11/31] Updating view definition to include complete query and sorting. --- packages/server/specs/openapi.json | 348 +++++++++++++++++++++ packages/server/specs/openapi.yaml | 301 ++++++++++++++++++ packages/server/specs/resources/misc.ts | 182 +++++------ packages/server/specs/resources/view.ts | 25 +- packages/server/src/definitions/openapi.ts | 174 +++++++++++ 5 files changed, 939 insertions(+), 91 deletions(-) diff --git a/packages/server/specs/openapi.json b/packages/server/specs/openapi.json index d45a8f28c9..fcabba8eba 100644 --- a/packages/server/specs/openapi.json +++ b/packages/server/specs/openapi.json @@ -2110,6 +2110,122 @@ "type": "string", "description": "A column used to display rows from this view - usually used when rendered in tables." }, + "query": { + "type": "object", + "properties": { + "allOr": { + "type": "boolean", + "description": "Specifies that a row should be returned if it satisfies any of the specified options, rather than requiring it to fulfill all the search parameters. This defaults to false, meaning AND logic will be used." + }, + "string": { + "type": "object", + "example": { + "columnName1": "value", + "columnName2": "value" + }, + "description": "A map of field name to the string to search for, this will look for rows that have a value starting with the string value.", + "additionalProperties": { + "type": "string", + "description": "The value to search for in the column." + } + }, + "fuzzy": { + "type": "object", + "description": "Searches for a sub-string within a string column, e.g. searching for 'dib' will match 'Budibase'." + }, + "range": { + "type": "object", + "description": "Searches within a range, the format of this must be in the format of an object with a \"low\" and \"high\" property.", + "example": { + "columnName1": { + "low": 10, + "high": 20 + } + } + }, + "equal": { + "type": "object", + "description": "Searches for rows that have a column value that is exactly the value set." + }, + "notEqual": { + "type": "object", + "description": "Searches for any row which does not contain the specified column value." + }, + "empty": { + "type": "object", + "description": "Searches for rows which do not contain the specified column. The object should simply contain keys of the column names, these can map to any value.", + "example": { + "columnName1": "" + } + }, + "notEmpty": { + "type": "object", + "description": "Searches for rows which have the specified column." + }, + "oneOf": { + "type": "object", + "description": "Searches for rows which have a column value that is any of the specified values. The format of this must be columnName -> [value1, value2]." + }, + "contains": { + "type": "object", + "description": "Searches for a value, or set of values in array column types (such as a multi-select). If an array of search options is provided then it must match all.", + "example": { + "arrayColumn": [ + "a", + "b" + ] + } + }, + "notContains": { + "type": "object", + "description": "The logical inverse of contains. Only works on array column types. If an array of values is passed, the row must not match any of them to be returned in the response.", + "example": { + "arrayColumn": [ + "a", + "b" + ] + } + }, + "containsAny": { + "type": "object", + "description": "As with the contains search, only works on array column types and searches for any of the provided values when given an array.", + "example": { + "arrayColumn": [ + "a", + "b" + ] + } + } + } + }, + "sort": { + "type": "object", + "required": [ + "field" + ], + "properties": { + "field": { + "type": "string", + "description": "The field from the table/view schema to sort on." + }, + "order": { + "type": "string", + "description": "The order in which to sort.", + "enum": [ + "ascending", + "descending" + ] + }, + "type": { + "type": "string", + "description": "The type of sort to perform (by number, or by alphabetically).", + "enum": [ + "string", + "number" + ] + } + } + }, "schema": { "type": "object", "additionalProperties": { @@ -2203,6 +2319,122 @@ "type": "string", "description": "A column used to display rows from this view - usually used when rendered in tables." }, + "query": { + "type": "object", + "properties": { + "allOr": { + "type": "boolean", + "description": "Specifies that a row should be returned if it satisfies any of the specified options, rather than requiring it to fulfill all the search parameters. This defaults to false, meaning AND logic will be used." + }, + "string": { + "type": "object", + "example": { + "columnName1": "value", + "columnName2": "value" + }, + "description": "A map of field name to the string to search for, this will look for rows that have a value starting with the string value.", + "additionalProperties": { + "type": "string", + "description": "The value to search for in the column." + } + }, + "fuzzy": { + "type": "object", + "description": "Searches for a sub-string within a string column, e.g. searching for 'dib' will match 'Budibase'." + }, + "range": { + "type": "object", + "description": "Searches within a range, the format of this must be in the format of an object with a \"low\" and \"high\" property.", + "example": { + "columnName1": { + "low": 10, + "high": 20 + } + } + }, + "equal": { + "type": "object", + "description": "Searches for rows that have a column value that is exactly the value set." + }, + "notEqual": { + "type": "object", + "description": "Searches for any row which does not contain the specified column value." + }, + "empty": { + "type": "object", + "description": "Searches for rows which do not contain the specified column. The object should simply contain keys of the column names, these can map to any value.", + "example": { + "columnName1": "" + } + }, + "notEmpty": { + "type": "object", + "description": "Searches for rows which have the specified column." + }, + "oneOf": { + "type": "object", + "description": "Searches for rows which have a column value that is any of the specified values. The format of this must be columnName -> [value1, value2]." + }, + "contains": { + "type": "object", + "description": "Searches for a value, or set of values in array column types (such as a multi-select). If an array of search options is provided then it must match all.", + "example": { + "arrayColumn": [ + "a", + "b" + ] + } + }, + "notContains": { + "type": "object", + "description": "The logical inverse of contains. Only works on array column types. If an array of values is passed, the row must not match any of them to be returned in the response.", + "example": { + "arrayColumn": [ + "a", + "b" + ] + } + }, + "containsAny": { + "type": "object", + "description": "As with the contains search, only works on array column types and searches for any of the provided values when given an array.", + "example": { + "arrayColumn": [ + "a", + "b" + ] + } + } + } + }, + "sort": { + "type": "object", + "required": [ + "field" + ], + "properties": { + "field": { + "type": "string", + "description": "The field from the table/view schema to sort on." + }, + "order": { + "type": "string", + "description": "The order in which to sort.", + "enum": [ + "ascending", + "descending" + ] + }, + "type": { + "type": "string", + "description": "The type of sort to perform (by number, or by alphabetically).", + "enum": [ + "string", + "number" + ] + } + } + }, "schema": { "type": "object", "additionalProperties": { @@ -2307,6 +2539,122 @@ "type": "string", "description": "A column used to display rows from this view - usually used when rendered in tables." }, + "query": { + "type": "object", + "properties": { + "allOr": { + "type": "boolean", + "description": "Specifies that a row should be returned if it satisfies any of the specified options, rather than requiring it to fulfill all the search parameters. This defaults to false, meaning AND logic will be used." + }, + "string": { + "type": "object", + "example": { + "columnName1": "value", + "columnName2": "value" + }, + "description": "A map of field name to the string to search for, this will look for rows that have a value starting with the string value.", + "additionalProperties": { + "type": "string", + "description": "The value to search for in the column." + } + }, + "fuzzy": { + "type": "object", + "description": "Searches for a sub-string within a string column, e.g. searching for 'dib' will match 'Budibase'." + }, + "range": { + "type": "object", + "description": "Searches within a range, the format of this must be in the format of an object with a \"low\" and \"high\" property.", + "example": { + "columnName1": { + "low": 10, + "high": 20 + } + } + }, + "equal": { + "type": "object", + "description": "Searches for rows that have a column value that is exactly the value set." + }, + "notEqual": { + "type": "object", + "description": "Searches for any row which does not contain the specified column value." + }, + "empty": { + "type": "object", + "description": "Searches for rows which do not contain the specified column. The object should simply contain keys of the column names, these can map to any value.", + "example": { + "columnName1": "" + } + }, + "notEmpty": { + "type": "object", + "description": "Searches for rows which have the specified column." + }, + "oneOf": { + "type": "object", + "description": "Searches for rows which have a column value that is any of the specified values. The format of this must be columnName -> [value1, value2]." + }, + "contains": { + "type": "object", + "description": "Searches for a value, or set of values in array column types (such as a multi-select). If an array of search options is provided then it must match all.", + "example": { + "arrayColumn": [ + "a", + "b" + ] + } + }, + "notContains": { + "type": "object", + "description": "The logical inverse of contains. Only works on array column types. If an array of values is passed, the row must not match any of them to be returned in the response.", + "example": { + "arrayColumn": [ + "a", + "b" + ] + } + }, + "containsAny": { + "type": "object", + "description": "As with the contains search, only works on array column types and searches for any of the provided values when given an array.", + "example": { + "arrayColumn": [ + "a", + "b" + ] + } + } + } + }, + "sort": { + "type": "object", + "required": [ + "field" + ], + "properties": { + "field": { + "type": "string", + "description": "The field from the table/view schema to sort on." + }, + "order": { + "type": "string", + "description": "The order in which to sort.", + "enum": [ + "ascending", + "descending" + ] + }, + "type": { + "type": "string", + "description": "The type of sort to perform (by number, or by alphabetically).", + "enum": [ + "string", + "number" + ] + } + } + }, "schema": { "type": "object", "additionalProperties": { diff --git a/packages/server/specs/openapi.yaml b/packages/server/specs/openapi.yaml index 0e551207ed..4d2a4fc5bd 100644 --- a/packages/server/specs/openapi.yaml +++ b/packages/server/specs/openapi.yaml @@ -1767,6 +1767,106 @@ components: type: string description: A column used to display rows from this view - usually used when rendered in tables. + query: + type: object + properties: + allOr: + type: boolean + description: Specifies that a row should be returned if it satisfies any of the + specified options, rather than requiring it to fulfill all the + search parameters. This defaults to false, meaning AND logic + will be used. + string: + type: object + example: + columnName1: value + columnName2: value + description: A map of field name to the string to search for, this will look for + rows that have a value starting with the string value. + additionalProperties: + type: string + description: The value to search for in the column. + fuzzy: + type: object + description: Searches for a sub-string within a string column, e.g. searching + for 'dib' will match 'Budibase'. + range: + type: object + description: Searches within a range, the format of this must be in the format + of an object with a "low" and "high" property. + example: + columnName1: + low: 10 + high: 20 + equal: + type: object + description: Searches for rows that have a column value that is exactly the + value set. + notEqual: + type: object + description: Searches for any row which does not contain the specified column + value. + empty: + type: object + description: Searches for rows which do not contain the specified column. The + object should simply contain keys of the column names, these can + map to any value. + example: + columnName1: "" + notEmpty: + type: object + description: Searches for rows which have the specified column. + oneOf: + type: object + description: Searches for rows which have a column value that is any of the + specified values. The format of this must be columnName -> + [value1, value2]. + contains: + type: object + description: Searches for a value, or set of values in array column types (such + as a multi-select). If an array of search options is provided + then it must match all. + example: + arrayColumn: + - a + - b + notContains: + type: object + description: The logical inverse of contains. Only works on array column types. + If an array of values is passed, the row must not match any of + them to be returned in the response. + example: + arrayColumn: + - a + - b + containsAny: + type: object + description: As with the contains search, only works on array column types and + searches for any of the provided values when given an array. + example: + arrayColumn: + - a + - b + sort: + type: object + required: + - field + properties: + field: + type: string + description: The field from the table/view schema to sort on. + order: + type: string + description: The order in which to sort. + enum: + - ascending + - descending + type: + type: string + description: The type of sort to perform (by number, or by alphabetically). + enum: + - string + - number schema: type: object additionalProperties: @@ -1843,6 +1943,106 @@ components: type: string description: A column used to display rows from this view - usually used when rendered in tables. + query: + type: object + properties: + allOr: + type: boolean + description: Specifies that a row should be returned if it satisfies any of the + specified options, rather than requiring it to fulfill all + the search parameters. This defaults to false, meaning AND + logic will be used. + string: + type: object + example: + columnName1: value + columnName2: value + description: A map of field name to the string to search for, this will look for + rows that have a value starting with the string value. + additionalProperties: + type: string + description: The value to search for in the column. + fuzzy: + type: object + description: Searches for a sub-string within a string column, e.g. searching + for 'dib' will match 'Budibase'. + range: + type: object + description: Searches within a range, the format of this must be in the format + of an object with a "low" and "high" property. + example: + columnName1: + low: 10 + high: 20 + equal: + type: object + description: Searches for rows that have a column value that is exactly the + value set. + notEqual: + type: object + description: Searches for any row which does not contain the specified column + value. + empty: + type: object + description: Searches for rows which do not contain the specified column. The + object should simply contain keys of the column names, these + can map to any value. + example: + columnName1: "" + notEmpty: + type: object + description: Searches for rows which have the specified column. + oneOf: + type: object + description: Searches for rows which have a column value that is any of the + specified values. The format of this must be columnName -> + [value1, value2]. + contains: + type: object + description: Searches for a value, or set of values in array column types (such + as a multi-select). If an array of search options is + provided then it must match all. + example: + arrayColumn: + - a + - b + notContains: + type: object + description: The logical inverse of contains. Only works on array column types. + If an array of values is passed, the row must not match any + of them to be returned in the response. + example: + arrayColumn: + - a + - b + containsAny: + type: object + description: As with the contains search, only works on array column types and + searches for any of the provided values when given an array. + example: + arrayColumn: + - a + - b + sort: + type: object + required: + - field + properties: + field: + type: string + description: The field from the table/view schema to sort on. + order: + type: string + description: The order in which to sort. + enum: + - ascending + - descending + type: + type: string + description: The type of sort to perform (by number, or by alphabetically). + enum: + - string + - number schema: type: object additionalProperties: @@ -1926,6 +2126,107 @@ components: type: string description: A column used to display rows from this view - usually used when rendered in tables. + query: + type: object + properties: + allOr: + type: boolean + description: Specifies that a row should be returned if it satisfies any of the + specified options, rather than requiring it to fulfill all + the search parameters. This defaults to false, meaning AND + logic will be used. + string: + type: object + example: + columnName1: value + columnName2: value + description: A map of field name to the string to search for, this will look for + rows that have a value starting with the string value. + additionalProperties: + type: string + description: The value to search for in the column. + fuzzy: + type: object + description: Searches for a sub-string within a string column, e.g. searching + for 'dib' will match 'Budibase'. + range: + type: object + description: Searches within a range, the format of this must be in the format + of an object with a "low" and "high" property. + example: + columnName1: + low: 10 + high: 20 + equal: + type: object + description: Searches for rows that have a column value that is exactly the + value set. + notEqual: + type: object + description: Searches for any row which does not contain the specified column + value. + empty: + type: object + description: Searches for rows which do not contain the specified column. The + object should simply contain keys of the column names, + these can map to any value. + example: + columnName1: "" + notEmpty: + type: object + description: Searches for rows which have the specified column. + oneOf: + type: object + description: Searches for rows which have a column value that is any of the + specified values. The format of this must be columnName -> + [value1, value2]. + contains: + type: object + description: Searches for a value, or set of values in array column types (such + as a multi-select). If an array of search options is + provided then it must match all. + example: + arrayColumn: + - a + - b + notContains: + type: object + description: The logical inverse of contains. Only works on array column types. + If an array of values is passed, the row must not match + any of them to be returned in the response. + example: + arrayColumn: + - a + - b + containsAny: + type: object + description: As with the contains search, only works on array column types and + searches for any of the provided values when given an + array. + example: + arrayColumn: + - a + - b + sort: + type: object + required: + - field + properties: + field: + type: string + description: The field from the table/view schema to sort on. + order: + type: string + description: The order in which to sort. + enum: + - ascending + - descending + type: + type: string + description: The type of sort to perform (by number, or by alphabetically). + enum: + - string + - number schema: type: object additionalProperties: diff --git a/packages/server/specs/resources/misc.ts b/packages/server/specs/resources/misc.ts index f56dff3301..8f77d2b22a 100644 --- a/packages/server/specs/resources/misc.ts +++ b/packages/server/specs/resources/misc.ts @@ -1,99 +1,101 @@ import { object } from "./utils" import Resource from "./utils/Resource" +export const searchSchema = { + type: "object", + properties: { + allOr: { + type: "boolean", + description: + "Specifies that a row should be returned if it satisfies any of the specified options, rather than requiring it to fulfill all the search parameters. This defaults to false, meaning AND logic will be used.", + }, + string: { + type: "object", + example: { + columnName1: "value", + columnName2: "value", + }, + description: + "A map of field name to the string to search for, this will look for rows that have a value starting with the string value.", + additionalProperties: { + type: "string", + description: "The value to search for in the column.", + }, + }, + fuzzy: { + type: "object", + description: + "Searches for a sub-string within a string column, e.g. searching for 'dib' will match 'Budibase'.", + }, + range: { + type: "object", + description: + 'Searches within a range, the format of this must be in the format of an object with a "low" and "high" property.', + example: { + columnName1: { + low: 10, + high: 20, + }, + }, + }, + equal: { + type: "object", + description: + "Searches for rows that have a column value that is exactly the value set.", + }, + notEqual: { + type: "object", + description: + "Searches for any row which does not contain the specified column value.", + }, + empty: { + type: "object", + description: + "Searches for rows which do not contain the specified column. The object should simply contain keys of the column names, these can map to any value.", + example: { + columnName1: "", + }, + }, + notEmpty: { + type: "object", + description: "Searches for rows which have the specified column.", + }, + oneOf: { + type: "object", + description: + "Searches for rows which have a column value that is any of the specified values. The format of this must be columnName -> [value1, value2].", + }, + contains: { + type: "object", + description: + "Searches for a value, or set of values in array column types (such as a multi-select). If an array of search options is provided then it must match all.", + example: { + arrayColumn: ["a", "b"], + }, + }, + notContains: { + type: "object", + description: + "The logical inverse of contains. Only works on array column types. If an array of values is passed, the row must not match any of them to be returned in the response.", + example: { + arrayColumn: ["a", "b"], + }, + }, + containsAny: { + type: "object", + description: + "As with the contains search, only works on array column types and searches for any of the provided values when given an array.", + example: { + arrayColumn: ["a", "b"], + }, + }, + }, +} + export default new Resource().setSchemas({ rowSearch: object( { - query: { - type: "object", - properties: { - allOr: { - type: "boolean", - description: - "Specifies that a row should be returned if it satisfies any of the specified options, rather than requiring it to fulfill all the search parameters. This defaults to false, meaning AND logic will be used.", - }, - string: { - type: "object", - example: { - columnName1: "value", - columnName2: "value", - }, - description: - "A map of field name to the string to search for, this will look for rows that have a value starting with the string value.", - additionalProperties: { - type: "string", - description: "The value to search for in the column.", - }, - }, - fuzzy: { - type: "object", - description: - "Searches for a sub-string within a string column, e.g. searching for 'dib' will match 'Budibase'.", - }, - range: { - type: "object", - description: - 'Searches within a range, the format of this must be in the format of an object with a "low" and "high" property.', - example: { - columnName1: { - low: 10, - high: 20, - }, - }, - }, - equal: { - type: "object", - description: - "Searches for rows that have a column value that is exactly the value set.", - }, - notEqual: { - type: "object", - description: - "Searches for any row which does not contain the specified column value.", - }, - empty: { - type: "object", - description: - "Searches for rows which do not contain the specified column. The object should simply contain keys of the column names, these can map to any value.", - example: { - columnName1: "", - }, - }, - notEmpty: { - type: "object", - description: "Searches for rows which have the specified column.", - }, - oneOf: { - type: "object", - description: - "Searches for rows which have a column value that is any of the specified values. The format of this must be columnName -> [value1, value2].", - }, - contains: { - type: "object", - description: - "Searches for a value, or set of values in array column types (such as a multi-select). If an array of search options is provided then it must match all.", - example: { - arrayColumn: ["a", "b"], - }, - }, - notContains: { - type: "object", - description: - "The logical inverse of contains. Only works on array column types. If an array of values is passed, the row must not match any of them to be returned in the response.", - example: { - arrayColumn: ["a", "b"], - }, - }, - containsAny: { - type: "object", - description: - "As with the contains search, only works on array column types and searches for any of the provided values when given an array.", - example: { - arrayColumn: ["a", "b"], - }, - }, - }, - }, + query: searchSchema, paginate: { type: "boolean", description: "Enables pagination, by default this is disabled.", diff --git a/packages/server/specs/resources/view.ts b/packages/server/specs/resources/view.ts index e798883ced..364e7badd2 100644 --- a/packages/server/specs/resources/view.ts +++ b/packages/server/specs/resources/view.ts @@ -1,6 +1,7 @@ import { object } from "./utils" import Resource from "./utils/Resource" -import { CalculationType } from "@budibase/types" +import { CalculationType, SortOrder, SortType } from "@budibase/types" +import { searchSchema } from "./misc" const view = { name: "peopleView", @@ -81,6 +82,28 @@ const viewSchema = { description: "A column used to display rows from this view - usually used when rendered in tables.", }, + query: searchSchema, + sort: { + type: "object", + required: ["field"], + properties: { + field: { + type: "string", + description: "The field from the table/view schema to sort on.", + }, + order: { + type: "string", + description: "The order in which to sort.", + enum: Object.values(SortOrder), + }, + type: { + type: "string", + description: + "The type of sort to perform (by number, or by alphabetically).", + enum: Object.values(SortType), + }, + }, + }, schema: { type: "object", additionalProperties: { diff --git a/packages/server/src/definitions/openapi.ts b/packages/server/src/definitions/openapi.ts index fed19ec5cf..6f53df0ab5 100644 --- a/packages/server/src/definitions/openapi.ts +++ b/packages/server/src/definitions/openapi.ts @@ -843,6 +843,64 @@ export interface components { type?: "calculation"; /** @description A column used to display rows from this view - usually used when rendered in tables. */ primaryDisplay?: string; + query?: { + /** @description Specifies that a row should be returned if it satisfies any of the specified options, rather than requiring it to fulfill all the search parameters. This defaults to false, meaning AND logic will be used. */ + allOr?: boolean; + /** + * @description A map of field name to the string to search for, this will look for rows that have a value starting with the string value. + * @example [object Object] + */ + string?: { [key: string]: string }; + /** @description Searches for a sub-string within a string column, e.g. searching for 'dib' will match 'Budibase'. */ + fuzzy?: { [key: string]: unknown }; + /** + * @description Searches within a range, the format of this must be in the format of an object with a "low" and "high" property. + * @example [object Object] + */ + range?: { [key: string]: unknown }; + /** @description Searches for rows that have a column value that is exactly the value set. */ + equal?: { [key: string]: unknown }; + /** @description Searches for any row which does not contain the specified column value. */ + notEqual?: { [key: string]: unknown }; + /** + * @description Searches for rows which do not contain the specified column. The object should simply contain keys of the column names, these can map to any value. + * @example [object Object] + */ + empty?: { [key: string]: unknown }; + /** @description Searches for rows which have the specified column. */ + notEmpty?: { [key: string]: unknown }; + /** @description Searches for rows which have a column value that is any of the specified values. The format of this must be columnName -> [value1, value2]. */ + oneOf?: { [key: string]: unknown }; + /** + * @description Searches for a value, or set of values in array column types (such as a multi-select). If an array of search options is provided then it must match all. + * @example [object Object] + */ + contains?: { [key: string]: unknown }; + /** + * @description The logical inverse of contains. Only works on array column types. If an array of values is passed, the row must not match any of them to be returned in the response. + * @example [object Object] + */ + notContains?: { [key: string]: unknown }; + /** + * @description As with the contains search, only works on array column types and searches for any of the provided values when given an array. + * @example [object Object] + */ + containsAny?: { [key: string]: unknown }; + }; + sort?: { + /** @description The field from the table/view schema to sort on. */ + field: string; + /** + * @description The order in which to sort. + * @enum {string} + */ + order?: "ascending" | "descending"; + /** + * @description The type of sort to perform (by number, or by alphabetically). + * @enum {string} + */ + type?: "string" | "number"; + }; schema: { [key: string]: | { @@ -884,6 +942,64 @@ export interface components { type?: "calculation"; /** @description A column used to display rows from this view - usually used when rendered in tables. */ primaryDisplay?: string; + query?: { + /** @description Specifies that a row should be returned if it satisfies any of the specified options, rather than requiring it to fulfill all the search parameters. This defaults to false, meaning AND logic will be used. */ + allOr?: boolean; + /** + * @description A map of field name to the string to search for, this will look for rows that have a value starting with the string value. + * @example [object Object] + */ + string?: { [key: string]: string }; + /** @description Searches for a sub-string within a string column, e.g. searching for 'dib' will match 'Budibase'. */ + fuzzy?: { [key: string]: unknown }; + /** + * @description Searches within a range, the format of this must be in the format of an object with a "low" and "high" property. + * @example [object Object] + */ + range?: { [key: string]: unknown }; + /** @description Searches for rows that have a column value that is exactly the value set. */ + equal?: { [key: string]: unknown }; + /** @description Searches for any row which does not contain the specified column value. */ + notEqual?: { [key: string]: unknown }; + /** + * @description Searches for rows which do not contain the specified column. The object should simply contain keys of the column names, these can map to any value. + * @example [object Object] + */ + empty?: { [key: string]: unknown }; + /** @description Searches for rows which have the specified column. */ + notEmpty?: { [key: string]: unknown }; + /** @description Searches for rows which have a column value that is any of the specified values. The format of this must be columnName -> [value1, value2]. */ + oneOf?: { [key: string]: unknown }; + /** + * @description Searches for a value, or set of values in array column types (such as a multi-select). If an array of search options is provided then it must match all. + * @example [object Object] + */ + contains?: { [key: string]: unknown }; + /** + * @description The logical inverse of contains. Only works on array column types. If an array of values is passed, the row must not match any of them to be returned in the response. + * @example [object Object] + */ + notContains?: { [key: string]: unknown }; + /** + * @description As with the contains search, only works on array column types and searches for any of the provided values when given an array. + * @example [object Object] + */ + containsAny?: { [key: string]: unknown }; + }; + sort?: { + /** @description The field from the table/view schema to sort on. */ + field: string; + /** + * @description The order in which to sort. + * @enum {string} + */ + order?: "ascending" | "descending"; + /** + * @description The type of sort to perform (by number, or by alphabetically). + * @enum {string} + */ + type?: "string" | "number"; + }; schema: { [key: string]: | { @@ -927,6 +1043,64 @@ export interface components { type?: "calculation"; /** @description A column used to display rows from this view - usually used when rendered in tables. */ primaryDisplay?: string; + query?: { + /** @description Specifies that a row should be returned if it satisfies any of the specified options, rather than requiring it to fulfill all the search parameters. This defaults to false, meaning AND logic will be used. */ + allOr?: boolean; + /** + * @description A map of field name to the string to search for, this will look for rows that have a value starting with the string value. + * @example [object Object] + */ + string?: { [key: string]: string }; + /** @description Searches for a sub-string within a string column, e.g. searching for 'dib' will match 'Budibase'. */ + fuzzy?: { [key: string]: unknown }; + /** + * @description Searches within a range, the format of this must be in the format of an object with a "low" and "high" property. + * @example [object Object] + */ + range?: { [key: string]: unknown }; + /** @description Searches for rows that have a column value that is exactly the value set. */ + equal?: { [key: string]: unknown }; + /** @description Searches for any row which does not contain the specified column value. */ + notEqual?: { [key: string]: unknown }; + /** + * @description Searches for rows which do not contain the specified column. The object should simply contain keys of the column names, these can map to any value. + * @example [object Object] + */ + empty?: { [key: string]: unknown }; + /** @description Searches for rows which have the specified column. */ + notEmpty?: { [key: string]: unknown }; + /** @description Searches for rows which have a column value that is any of the specified values. The format of this must be columnName -> [value1, value2]. */ + oneOf?: { [key: string]: unknown }; + /** + * @description Searches for a value, or set of values in array column types (such as a multi-select). If an array of search options is provided then it must match all. + * @example [object Object] + */ + contains?: { [key: string]: unknown }; + /** + * @description The logical inverse of contains. Only works on array column types. If an array of values is passed, the row must not match any of them to be returned in the response. + * @example [object Object] + */ + notContains?: { [key: string]: unknown }; + /** + * @description As with the contains search, only works on array column types and searches for any of the provided values when given an array. + * @example [object Object] + */ + containsAny?: { [key: string]: unknown }; + }; + sort?: { + /** @description The field from the table/view schema to sort on. */ + field: string; + /** + * @description The order in which to sort. + * @enum {string} + */ + order?: "ascending" | "descending"; + /** + * @description The type of sort to perform (by number, or by alphabetically). + * @enum {string} + */ + type?: "string" | "number"; + }; schema: { [key: string]: | { From ce529d2f45c3a6c9d900c777718fbc80e6d5e645 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Fri, 8 Nov 2024 18:04:53 +0000 Subject: [PATCH 12/31] Some final updates to view public API, adding test cases. --- packages/server/specs/openapi.json | 27 ++++++++- packages/server/specs/openapi.yaml | 18 ++++++ packages/server/specs/resources/view.ts | 11 +++- .../api/controllers/public/mapping/views.ts | 20 ++++--- .../src/api/controllers/public/views.ts | 44 ++++++++------ .../src/api/routes/public/tests/Request.ts | 14 +++++ .../src/api/routes/public/tests/views.spec.ts | 58 +++++++++++++++---- packages/server/src/definitions/openapi.ts | 6 ++ 8 files changed, 161 insertions(+), 37 deletions(-) diff --git a/packages/server/specs/openapi.json b/packages/server/specs/openapi.json index fcabba8eba..e98843b0fa 100644 --- a/packages/server/specs/openapi.json +++ b/packages/server/specs/openapi.json @@ -456,6 +456,11 @@ "readonly": false } }, + "query": { + "string": { + "column": "value" + } + }, "primaryDisplay": "name" } } @@ -484,6 +489,11 @@ "readonly": false } }, + "query": { + "string": { + "column": "value" + } + }, "primaryDisplay": "name" } ] @@ -2092,13 +2102,18 @@ "type": "object", "required": [ "name", - "schema" + "schema", + "tableId" ], "properties": { "name": { "description": "The name of the view.", "type": "string" }, + "tableId": { + "description": "The ID of the table this view is based on.", + "type": "string" + }, "type": { "description": "The type of view - standard (empty value) or calculation.", "type": "string", @@ -2301,6 +2316,7 @@ "required": [ "name", "schema", + "tableId", "id" ], "properties": { @@ -2308,6 +2324,10 @@ "description": "The name of the view.", "type": "string" }, + "tableId": { + "description": "The ID of the table this view is based on.", + "type": "string" + }, "type": { "description": "The type of view - standard (empty value) or calculation.", "type": "string", @@ -2521,6 +2541,7 @@ "required": [ "name", "schema", + "tableId", "id" ], "properties": { @@ -2528,6 +2549,10 @@ "description": "The name of the view.", "type": "string" }, + "tableId": { + "description": "The ID of the table this view is based on.", + "type": "string" + }, "type": { "description": "The type of view - standard (empty value) or calculation.", "type": "string", diff --git a/packages/server/specs/openapi.yaml b/packages/server/specs/openapi.yaml index 4d2a4fc5bd..0ba9f6d983 100644 --- a/packages/server/specs/openapi.yaml +++ b/packages/server/specs/openapi.yaml @@ -468,6 +468,9 @@ components: salary: visible: false readonly: false + query: + string: + column: value primaryDisplay: name views: value: @@ -488,6 +491,9 @@ components: salary: visible: false readonly: false + query: + string: + column: value primaryDisplay: name securitySchemes: ApiKeyAuth: @@ -1754,10 +1760,14 @@ components: required: - name - schema + - tableId properties: name: description: The name of the view. type: string + tableId: + description: The ID of the table this view is based on. + type: string type: description: The type of view - standard (empty value) or calculation. type: string @@ -1929,11 +1939,15 @@ components: required: - name - schema + - tableId - id properties: name: description: The name of the view. type: string + tableId: + description: The ID of the table this view is based on. + type: string type: description: The type of view - standard (empty value) or calculation. type: string @@ -2112,11 +2126,15 @@ components: required: - name - schema + - tableId - id properties: name: description: The name of the view. type: string + tableId: + description: The ID of the table this view is based on. + type: string type: description: The type of view - standard (empty value) or calculation. type: string diff --git a/packages/server/specs/resources/view.ts b/packages/server/specs/resources/view.ts index 364e7badd2..eda934d15d 100644 --- a/packages/server/specs/resources/view.ts +++ b/packages/server/specs/resources/view.ts @@ -24,6 +24,11 @@ const view = { readonly: false, }, }, + query: { + string: { + column: "value", + }, + }, primaryDisplay: "name", } @@ -66,12 +71,16 @@ const baseColumnDef = { const viewSchema = { description: "The view to be created/updated.", type: "object", - required: ["name", "schema"], + required: ["name", "schema", "tableId"], properties: { name: { description: "The name of the view.", type: "string", }, + tableId: { + description: "The ID of the table this view is based on.", + type: "string", + }, type: { description: "The type of view - standard (empty value) or calculation.", type: "string", diff --git a/packages/server/src/api/controllers/public/mapping/views.ts b/packages/server/src/api/controllers/public/mapping/views.ts index b6cf64542e..7eee164368 100644 --- a/packages/server/src/api/controllers/public/mapping/views.ts +++ b/packages/server/src/api/controllers/public/mapping/views.ts @@ -1,23 +1,29 @@ import { View } from "./types" +import { ViewV2, Ctx } from "@budibase/types" +import { dataFilters } from "@budibase/shared-core" -function view(body: any): View { +function view(body: ViewV2): View { return { id: body.id, + tableId: body.tableId, + type: body.type, name: body.name, - schema: body.schema, + schema: body.schema!, primaryDisplay: body.primaryDisplay, + query: dataFilters.buildQuery(body.query), + sort: body.sort, } } -function mapView(ctx: any): { data: View } { +function mapView(ctx: Ctx<{ data: ViewV2 }>): { data: View } { return { - data: view(ctx.body), + data: view(ctx.body.data), } } -function mapViews(ctx: any): { data: View[] } { - const tables = ctx.body.map((body: any) => view(body)) - return { data: tables } +function mapViews(ctx: Ctx<{ data: ViewV2[] }>): { data: View[] } { + const views = ctx.body.data.map((body: ViewV2) => view(body)) + return { data: views } } export default { diff --git a/packages/server/src/api/controllers/public/views.ts b/packages/server/src/api/controllers/public/views.ts index 135d3c0128..51b18b4426 100644 --- a/packages/server/src/api/controllers/public/views.ts +++ b/packages/server/src/api/controllers/public/views.ts @@ -2,6 +2,7 @@ import { search as stringSearch } from "./utils" import * as controller from "../view" import { ViewV2, UserCtx } from "@budibase/types" import { Next } from "koa" +import { merge } from "lodash" function fixView(view: ViewV2, params?: { viewId: string }) { if (!params || !view) { @@ -27,32 +28,41 @@ export async function search(ctx: UserCtx, next: Next) { } export async function create(ctx: UserCtx, next: Next) { - ctx.body = fixView(ctx.body) - await controller.v2.create(ctx) + await controller.v2.create( + merge(ctx, { + request: { + body: fixView(ctx.request.body), + }, + }) + ) await next() } export async function read(ctx: UserCtx, next: Next) { - await controller.v2.get({ - ...ctx, - params: { - viewId: ctx.params.viewId, - }, - }) + await controller.v2.get( + merge(ctx, { + params: { + viewId: ctx.params.viewId, + }, + }) + ) await next() } export async function update(ctx: UserCtx, next: Next) { const viewId = ctx.params.viewId - await controller.v2.update({ - ...ctx, - body: { - data: fixView(ctx.body, { viewId }), - }, - params: { - viewId, - }, - }) + await controller.v2.update( + merge(ctx, { + request: { + body: { + data: fixView(ctx.request.body, { viewId }), + }, + }, + params: { + viewId, + }, + }) + ) await next() } diff --git a/packages/server/src/api/routes/public/tests/Request.ts b/packages/server/src/api/routes/public/tests/Request.ts index 7b3c5b6e06..5b9a7210c9 100644 --- a/packages/server/src/api/routes/public/tests/Request.ts +++ b/packages/server/src/api/routes/public/tests/Request.ts @@ -173,4 +173,18 @@ export class PublicViewAPI { ): Promise> { return this.request.send("get", "/views", undefined, expectations) } + + async search( + viewName: string, + expectations?: PublicAPIExpectations + ): Promise> { + return this.request.send( + "post", + "/views/search", + { + name: viewName, + }, + expectations + ) + } } diff --git a/packages/server/src/api/routes/public/tests/views.spec.ts b/packages/server/src/api/routes/public/tests/views.spec.ts index dce2e1ab7a..5c6aee3af1 100644 --- a/packages/server/src/api/routes/public/tests/views.spec.ts +++ b/packages/server/src/api/routes/public/tests/views.spec.ts @@ -2,6 +2,7 @@ import * as setup from "../../tests/utilities" import { basicTable } from "../../../../tests/utilities/structures" import { Table } from "@budibase/types" import { PublicAPIRequest } from "./Request" +import { generator } from "@budibase/backend-core/tests" describe("check public API security", () => { const config = setup.getConfig() @@ -13,20 +14,55 @@ describe("check public API security", () => { table = (await request.tables.create(basicTable())).data }) - it("should be able to create a view", async () => { - await request.views.create( - { - name: "view", - tableId: table._id!, - query: {}, - schema: { - name: { - readonly: true, - visible: true, - }, + function baseView() { + return { + name: generator.word(), + tableId: table._id!, + query: {}, + schema: { + name: { + readonly: true, + visible: true, }, }, + } + } + + it("should be able to create a view", async () => { + await request.views.create(baseView(), { status: 201 }) + }) + + it("should be able to update a view", async () => { + const view = await request.views.create(baseView(), { status: 201 }) + await request.views.update( + view.data.id, + { + ...view.data, + name: "new name", + }, + { status: 200 } + ) + }) + + it("should be able to search views", async () => { + const viewName = "view to search for" + const view = await request.views.create( + { + ...baseView(), + name: viewName, + }, { status: 201 } ) + const results = await request.views.search(viewName, { + status: 200, + }) + expect(results.data.length).toEqual(1) + expect(results.data[0].id).toEqual(view.data.id) + }) + + it("should be able to delete a view", async () => { + const view = await request.views.create(baseView(), { status: 201 }) + const result = await request.views.destroy(view.data.id, { status: 204 }) + expect(result).toBeDefined() }) }) diff --git a/packages/server/src/definitions/openapi.ts b/packages/server/src/definitions/openapi.ts index 6f53df0ab5..a0e18ea0f3 100644 --- a/packages/server/src/definitions/openapi.ts +++ b/packages/server/src/definitions/openapi.ts @@ -836,6 +836,8 @@ export interface components { view: { /** @description The name of the view. */ name: string; + /** @description The ID of the table this view is based on. */ + tableId: string; /** * @description The type of view - standard (empty value) or calculation. * @enum {string} @@ -935,6 +937,8 @@ export interface components { data: { /** @description The name of the view. */ name: string; + /** @description The ID of the table this view is based on. */ + tableId: string; /** * @description The type of view - standard (empty value) or calculation. * @enum {string} @@ -1036,6 +1040,8 @@ export interface components { data: { /** @description The name of the view. */ name: string; + /** @description The ID of the table this view is based on. */ + tableId: string; /** * @description The type of view - standard (empty value) or calculation. * @enum {string} From 82c7c089cbb349d34f344950dc61ba7b4ecfa836 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Fri, 15 Nov 2024 17:24:21 +0000 Subject: [PATCH 13/31] Updating schema. --- packages/server/specs/openapi.json | 725 ++++++++++++------ packages/server/specs/openapi.yaml | 583 ++++++++------ packages/server/specs/resources/view.ts | 99 ++- .../src/api/controllers/public/views.ts | 4 +- .../src/api/routes/public/tests/Request.ts | 13 + .../src/api/routes/public/tests/views.spec.ts | 22 + packages/server/src/definitions/openapi.ts | 351 ++++++--- 7 files changed, 1198 insertions(+), 599 deletions(-) diff --git a/packages/server/specs/openapi.json b/packages/server/specs/openapi.json index e98843b0fa..c47a14cf21 100644 --- a/packages/server/specs/openapi.json +++ b/packages/server/specs/openapi.json @@ -457,9 +457,28 @@ } }, "query": { - "string": { - "column": "value" - } + "logicalOperator": "all", + "onEmptyFilter": "none", + "groups": [ + { + "logicalOperator": "any", + "filters": [ + { + "operator": "string", + "field": "name", + "value": "John" + }, + { + "operator": "range", + "field": "age", + "value": { + "low": 18, + "high": 100 + } + } + ] + } + ] }, "primaryDisplay": "name" } @@ -490,9 +509,28 @@ } }, "query": { - "string": { - "column": "value" - } + "logicalOperator": "all", + "onEmptyFilter": "none", + "groups": [ + { + "logicalOperator": "any", + "filters": [ + { + "operator": "string", + "field": "name", + "value": "John" + }, + { + "operator": "range", + "field": "age", + "value": { + "low": 18, + "high": 100 + } + } + ] + } + ] }, "primaryDisplay": "name" } @@ -2126,90 +2164,159 @@ "description": "A column used to display rows from this view - usually used when rendered in tables." }, "query": { + "description": "Search parameters for view", "type": "object", + "required": [], "properties": { - "allOr": { - "type": "boolean", - "description": "Specifies that a row should be returned if it satisfies any of the specified options, rather than requiring it to fulfill all the search parameters. This defaults to false, meaning AND logic will be used." + "logicalOperator": { + "description": "When using groups this defines whether all of the filters must match, or only one of them.", + "type": "string", + "enum": [ + "all", + "any" + ] }, - "string": { - "type": "object", - "example": { - "columnName1": "value", - "columnName2": "value" - }, - "description": "A map of field name to the string to search for, this will look for rows that have a value starting with the string value.", - "additionalProperties": { - "type": "string", - "description": "The value to search for in the column." - } + "onEmptyFilter": { + "description": "If no filters match, should the view return all rows, or no rows.", + "type": "string", + "enum": [ + "all", + "none" + ] }, - "fuzzy": { - "type": "object", - "description": "Searches for a sub-string within a string column, e.g. searching for 'dib' will match 'Budibase'." - }, - "range": { - "type": "object", - "description": "Searches within a range, the format of this must be in the format of an object with a \"low\" and \"high\" property.", - "example": { - "columnName1": { - "low": 10, - "high": 20 + "groups": { + "description": "A grouping of filters to be applied.", + "type": "array", + "items": { + "type": "object", + "properties": { + "logicalOperator": { + "description": "When using groups this defines whether all of the filters must match, or only one of them.", + "type": "string", + "enum": [ + "all", + "any" + ] + }, + "filters": { + "description": "A list of filters to apply", + "type": "array", + "items": { + "type": "object", + "properties": { + "operator": { + "type": "string", + "description": "The type of search operation which is being performed.", + "enum": [ + "equal", + "notEqual", + "empty", + "notEmpty", + "fuzzy", + "string", + "contains", + "notContains", + "containsAny", + "oneOf", + "range" + ] + }, + "field": { + "type": "string", + "description": "The field in the view to perform the search on." + }, + "value": { + "description": "The value to search for - the type will depend on the operator in use.", + "oneOf": [ + { + "type": "string" + }, + { + "type": "number" + }, + { + "type": "boolean" + }, + { + "type": "object" + }, + { + "type": "array" + } + ] + } + } + } + }, + "groups": { + "description": "A grouping of filters to be applied.", + "type": "array", + "items": { + "type": "object", + "properties": { + "logicalOperator": { + "description": "When using groups this defines whether all of the filters must match, or only one of them.", + "type": "string", + "enum": [ + "all", + "any" + ] + }, + "filters": { + "description": "A list of filters to apply", + "type": "array", + "items": { + "type": "object", + "properties": { + "operator": { + "type": "string", + "description": "The type of search operation which is being performed.", + "enum": [ + "equal", + "notEqual", + "empty", + "notEmpty", + "fuzzy", + "string", + "contains", + "notContains", + "containsAny", + "oneOf", + "range" + ] + }, + "field": { + "type": "string", + "description": "The field in the view to perform the search on." + }, + "value": { + "description": "The value to search for - the type will depend on the operator in use.", + "oneOf": [ + { + "type": "string" + }, + { + "type": "number" + }, + { + "type": "boolean" + }, + { + "type": "object" + }, + { + "type": "array" + } + ] + } + } + } + } + } + } + } } } - }, - "equal": { - "type": "object", - "description": "Searches for rows that have a column value that is exactly the value set." - }, - "notEqual": { - "type": "object", - "description": "Searches for any row which does not contain the specified column value." - }, - "empty": { - "type": "object", - "description": "Searches for rows which do not contain the specified column. The object should simply contain keys of the column names, these can map to any value.", - "example": { - "columnName1": "" - } - }, - "notEmpty": { - "type": "object", - "description": "Searches for rows which have the specified column." - }, - "oneOf": { - "type": "object", - "description": "Searches for rows which have a column value that is any of the specified values. The format of this must be columnName -> [value1, value2]." - }, - "contains": { - "type": "object", - "description": "Searches for a value, or set of values in array column types (such as a multi-select). If an array of search options is provided then it must match all.", - "example": { - "arrayColumn": [ - "a", - "b" - ] - } - }, - "notContains": { - "type": "object", - "description": "The logical inverse of contains. Only works on array column types. If an array of values is passed, the row must not match any of them to be returned in the response.", - "example": { - "arrayColumn": [ - "a", - "b" - ] - } - }, - "containsAny": { - "type": "object", - "description": "As with the contains search, only works on array column types and searches for any of the provided values when given an array.", - "example": { - "arrayColumn": [ - "a", - "b" - ] - } } } }, @@ -2340,90 +2447,159 @@ "description": "A column used to display rows from this view - usually used when rendered in tables." }, "query": { + "description": "Search parameters for view", "type": "object", + "required": [], "properties": { - "allOr": { - "type": "boolean", - "description": "Specifies that a row should be returned if it satisfies any of the specified options, rather than requiring it to fulfill all the search parameters. This defaults to false, meaning AND logic will be used." + "logicalOperator": { + "description": "When using groups this defines whether all of the filters must match, or only one of them.", + "type": "string", + "enum": [ + "all", + "any" + ] }, - "string": { - "type": "object", - "example": { - "columnName1": "value", - "columnName2": "value" - }, - "description": "A map of field name to the string to search for, this will look for rows that have a value starting with the string value.", - "additionalProperties": { - "type": "string", - "description": "The value to search for in the column." - } + "onEmptyFilter": { + "description": "If no filters match, should the view return all rows, or no rows.", + "type": "string", + "enum": [ + "all", + "none" + ] }, - "fuzzy": { - "type": "object", - "description": "Searches for a sub-string within a string column, e.g. searching for 'dib' will match 'Budibase'." - }, - "range": { - "type": "object", - "description": "Searches within a range, the format of this must be in the format of an object with a \"low\" and \"high\" property.", - "example": { - "columnName1": { - "low": 10, - "high": 20 + "groups": { + "description": "A grouping of filters to be applied.", + "type": "array", + "items": { + "type": "object", + "properties": { + "logicalOperator": { + "description": "When using groups this defines whether all of the filters must match, or only one of them.", + "type": "string", + "enum": [ + "all", + "any" + ] + }, + "filters": { + "description": "A list of filters to apply", + "type": "array", + "items": { + "type": "object", + "properties": { + "operator": { + "type": "string", + "description": "The type of search operation which is being performed.", + "enum": [ + "equal", + "notEqual", + "empty", + "notEmpty", + "fuzzy", + "string", + "contains", + "notContains", + "containsAny", + "oneOf", + "range" + ] + }, + "field": { + "type": "string", + "description": "The field in the view to perform the search on." + }, + "value": { + "description": "The value to search for - the type will depend on the operator in use.", + "oneOf": [ + { + "type": "string" + }, + { + "type": "number" + }, + { + "type": "boolean" + }, + { + "type": "object" + }, + { + "type": "array" + } + ] + } + } + } + }, + "groups": { + "description": "A grouping of filters to be applied.", + "type": "array", + "items": { + "type": "object", + "properties": { + "logicalOperator": { + "description": "When using groups this defines whether all of the filters must match, or only one of them.", + "type": "string", + "enum": [ + "all", + "any" + ] + }, + "filters": { + "description": "A list of filters to apply", + "type": "array", + "items": { + "type": "object", + "properties": { + "operator": { + "type": "string", + "description": "The type of search operation which is being performed.", + "enum": [ + "equal", + "notEqual", + "empty", + "notEmpty", + "fuzzy", + "string", + "contains", + "notContains", + "containsAny", + "oneOf", + "range" + ] + }, + "field": { + "type": "string", + "description": "The field in the view to perform the search on." + }, + "value": { + "description": "The value to search for - the type will depend on the operator in use.", + "oneOf": [ + { + "type": "string" + }, + { + "type": "number" + }, + { + "type": "boolean" + }, + { + "type": "object" + }, + { + "type": "array" + } + ] + } + } + } + } + } + } + } } } - }, - "equal": { - "type": "object", - "description": "Searches for rows that have a column value that is exactly the value set." - }, - "notEqual": { - "type": "object", - "description": "Searches for any row which does not contain the specified column value." - }, - "empty": { - "type": "object", - "description": "Searches for rows which do not contain the specified column. The object should simply contain keys of the column names, these can map to any value.", - "example": { - "columnName1": "" - } - }, - "notEmpty": { - "type": "object", - "description": "Searches for rows which have the specified column." - }, - "oneOf": { - "type": "object", - "description": "Searches for rows which have a column value that is any of the specified values. The format of this must be columnName -> [value1, value2]." - }, - "contains": { - "type": "object", - "description": "Searches for a value, or set of values in array column types (such as a multi-select). If an array of search options is provided then it must match all.", - "example": { - "arrayColumn": [ - "a", - "b" - ] - } - }, - "notContains": { - "type": "object", - "description": "The logical inverse of contains. Only works on array column types. If an array of values is passed, the row must not match any of them to be returned in the response.", - "example": { - "arrayColumn": [ - "a", - "b" - ] - } - }, - "containsAny": { - "type": "object", - "description": "As with the contains search, only works on array column types and searches for any of the provided values when given an array.", - "example": { - "arrayColumn": [ - "a", - "b" - ] - } } } }, @@ -2565,90 +2741,159 @@ "description": "A column used to display rows from this view - usually used when rendered in tables." }, "query": { + "description": "Search parameters for view", "type": "object", + "required": [], "properties": { - "allOr": { - "type": "boolean", - "description": "Specifies that a row should be returned if it satisfies any of the specified options, rather than requiring it to fulfill all the search parameters. This defaults to false, meaning AND logic will be used." + "logicalOperator": { + "description": "When using groups this defines whether all of the filters must match, or only one of them.", + "type": "string", + "enum": [ + "all", + "any" + ] }, - "string": { - "type": "object", - "example": { - "columnName1": "value", - "columnName2": "value" - }, - "description": "A map of field name to the string to search for, this will look for rows that have a value starting with the string value.", - "additionalProperties": { - "type": "string", - "description": "The value to search for in the column." - } + "onEmptyFilter": { + "description": "If no filters match, should the view return all rows, or no rows.", + "type": "string", + "enum": [ + "all", + "none" + ] }, - "fuzzy": { - "type": "object", - "description": "Searches for a sub-string within a string column, e.g. searching for 'dib' will match 'Budibase'." - }, - "range": { - "type": "object", - "description": "Searches within a range, the format of this must be in the format of an object with a \"low\" and \"high\" property.", - "example": { - "columnName1": { - "low": 10, - "high": 20 + "groups": { + "description": "A grouping of filters to be applied.", + "type": "array", + "items": { + "type": "object", + "properties": { + "logicalOperator": { + "description": "When using groups this defines whether all of the filters must match, or only one of them.", + "type": "string", + "enum": [ + "all", + "any" + ] + }, + "filters": { + "description": "A list of filters to apply", + "type": "array", + "items": { + "type": "object", + "properties": { + "operator": { + "type": "string", + "description": "The type of search operation which is being performed.", + "enum": [ + "equal", + "notEqual", + "empty", + "notEmpty", + "fuzzy", + "string", + "contains", + "notContains", + "containsAny", + "oneOf", + "range" + ] + }, + "field": { + "type": "string", + "description": "The field in the view to perform the search on." + }, + "value": { + "description": "The value to search for - the type will depend on the operator in use.", + "oneOf": [ + { + "type": "string" + }, + { + "type": "number" + }, + { + "type": "boolean" + }, + { + "type": "object" + }, + { + "type": "array" + } + ] + } + } + } + }, + "groups": { + "description": "A grouping of filters to be applied.", + "type": "array", + "items": { + "type": "object", + "properties": { + "logicalOperator": { + "description": "When using groups this defines whether all of the filters must match, or only one of them.", + "type": "string", + "enum": [ + "all", + "any" + ] + }, + "filters": { + "description": "A list of filters to apply", + "type": "array", + "items": { + "type": "object", + "properties": { + "operator": { + "type": "string", + "description": "The type of search operation which is being performed.", + "enum": [ + "equal", + "notEqual", + "empty", + "notEmpty", + "fuzzy", + "string", + "contains", + "notContains", + "containsAny", + "oneOf", + "range" + ] + }, + "field": { + "type": "string", + "description": "The field in the view to perform the search on." + }, + "value": { + "description": "The value to search for - the type will depend on the operator in use.", + "oneOf": [ + { + "type": "string" + }, + { + "type": "number" + }, + { + "type": "boolean" + }, + { + "type": "object" + }, + { + "type": "array" + } + ] + } + } + } + } + } + } + } } } - }, - "equal": { - "type": "object", - "description": "Searches for rows that have a column value that is exactly the value set." - }, - "notEqual": { - "type": "object", - "description": "Searches for any row which does not contain the specified column value." - }, - "empty": { - "type": "object", - "description": "Searches for rows which do not contain the specified column. The object should simply contain keys of the column names, these can map to any value.", - "example": { - "columnName1": "" - } - }, - "notEmpty": { - "type": "object", - "description": "Searches for rows which have the specified column." - }, - "oneOf": { - "type": "object", - "description": "Searches for rows which have a column value that is any of the specified values. The format of this must be columnName -> [value1, value2]." - }, - "contains": { - "type": "object", - "description": "Searches for a value, or set of values in array column types (such as a multi-select). If an array of search options is provided then it must match all.", - "example": { - "arrayColumn": [ - "a", - "b" - ] - } - }, - "notContains": { - "type": "object", - "description": "The logical inverse of contains. Only works on array column types. If an array of values is passed, the row must not match any of them to be returned in the response.", - "example": { - "arrayColumn": [ - "a", - "b" - ] - } - }, - "containsAny": { - "type": "object", - "description": "As with the contains search, only works on array column types and searches for any of the provided values when given an array.", - "example": { - "arrayColumn": [ - "a", - "b" - ] - } } } }, diff --git a/packages/server/specs/openapi.yaml b/packages/server/specs/openapi.yaml index 0ba9f6d983..edfb29f432 100644 --- a/packages/server/specs/openapi.yaml +++ b/packages/server/specs/openapi.yaml @@ -469,8 +469,19 @@ components: visible: false readonly: false query: - string: - column: value + logicalOperator: all + onEmptyFilter: none + groups: + - logicalOperator: any + filters: + - operator: string + field: name + value: John + - operator: range + field: age + value: + low: 18 + high: 100 primaryDisplay: name views: value: @@ -492,8 +503,19 @@ components: visible: false readonly: false query: - string: - column: value + logicalOperator: all + onEmptyFilter: none + groups: + - logicalOperator: any + filters: + - operator: string + field: name + value: John + - operator: range + field: age + value: + low: 18 + high: 100 primaryDisplay: name securitySchemes: ApiKeyAuth: @@ -1778,85 +1800,115 @@ components: description: A column used to display rows from this view - usually used when rendered in tables. query: + description: Search parameters for view type: object + required: [] properties: - allOr: - type: boolean - description: Specifies that a row should be returned if it satisfies any of the - specified options, rather than requiring it to fulfill all the - search parameters. This defaults to false, meaning AND logic - will be used. - string: - type: object - example: - columnName1: value - columnName2: value - description: A map of field name to the string to search for, this will look for - rows that have a value starting with the string value. - additionalProperties: - type: string - description: The value to search for in the column. - fuzzy: - type: object - description: Searches for a sub-string within a string column, e.g. searching - for 'dib' will match 'Budibase'. - range: - type: object - description: Searches within a range, the format of this must be in the format - of an object with a "low" and "high" property. - example: - columnName1: - low: 10 - high: 20 - equal: - type: object - description: Searches for rows that have a column value that is exactly the - value set. - notEqual: - type: object - description: Searches for any row which does not contain the specified column - value. - empty: - type: object - description: Searches for rows which do not contain the specified column. The - object should simply contain keys of the column names, these can - map to any value. - example: - columnName1: "" - notEmpty: - type: object - description: Searches for rows which have the specified column. - oneOf: - type: object - description: Searches for rows which have a column value that is any of the - specified values. The format of this must be columnName -> - [value1, value2]. - contains: - type: object - description: Searches for a value, or set of values in array column types (such - as a multi-select). If an array of search options is provided - then it must match all. - example: - arrayColumn: - - a - - b - notContains: - type: object - description: The logical inverse of contains. Only works on array column types. - If an array of values is passed, the row must not match any of - them to be returned in the response. - example: - arrayColumn: - - a - - b - containsAny: - type: object - description: As with the contains search, only works on array column types and - searches for any of the provided values when given an array. - example: - arrayColumn: - - a - - b + logicalOperator: + description: When using groups this defines whether all of the filters must + match, or only one of them. + type: string + enum: + - all + - any + onEmptyFilter: + description: If no filters match, should the view return all rows, or no rows. + type: string + enum: + - all + - none + groups: + description: A grouping of filters to be applied. + type: array + items: + type: object + properties: + logicalOperator: + description: When using groups this defines whether all of the filters must + match, or only one of them. + type: string + enum: + - all + - any + filters: + description: A list of filters to apply + type: array + items: + type: object + properties: + operator: + type: string + description: The type of search operation which is being performed. + enum: + - equal + - notEqual + - empty + - notEmpty + - fuzzy + - string + - contains + - notContains + - containsAny + - oneOf + - range + field: + type: string + description: The field in the view to perform the search on. + value: + description: The value to search for - the type will depend on the operator in + use. + oneOf: + - type: string + - type: number + - type: boolean + - type: object + - type: array + groups: + description: A grouping of filters to be applied. + type: array + items: + type: object + properties: + logicalOperator: + description: When using groups this defines whether all of the filters must + match, or only one of them. + type: string + enum: + - all + - any + filters: + description: A list of filters to apply + type: array + items: + type: object + properties: + operator: + type: string + description: The type of search operation which is being performed. + enum: + - equal + - notEqual + - empty + - notEmpty + - fuzzy + - string + - contains + - notContains + - containsAny + - oneOf + - range + field: + type: string + description: The field in the view to perform the search on. + value: + description: The value to search for - the type will depend on the operator in + use. + oneOf: + - type: string + - type: number + - type: boolean + - type: object + - type: array sort: type: object required: @@ -1958,85 +2010,115 @@ components: description: A column used to display rows from this view - usually used when rendered in tables. query: + description: Search parameters for view type: object + required: [] properties: - allOr: - type: boolean - description: Specifies that a row should be returned if it satisfies any of the - specified options, rather than requiring it to fulfill all - the search parameters. This defaults to false, meaning AND - logic will be used. - string: - type: object - example: - columnName1: value - columnName2: value - description: A map of field name to the string to search for, this will look for - rows that have a value starting with the string value. - additionalProperties: - type: string - description: The value to search for in the column. - fuzzy: - type: object - description: Searches for a sub-string within a string column, e.g. searching - for 'dib' will match 'Budibase'. - range: - type: object - description: Searches within a range, the format of this must be in the format - of an object with a "low" and "high" property. - example: - columnName1: - low: 10 - high: 20 - equal: - type: object - description: Searches for rows that have a column value that is exactly the - value set. - notEqual: - type: object - description: Searches for any row which does not contain the specified column - value. - empty: - type: object - description: Searches for rows which do not contain the specified column. The - object should simply contain keys of the column names, these - can map to any value. - example: - columnName1: "" - notEmpty: - type: object - description: Searches for rows which have the specified column. - oneOf: - type: object - description: Searches for rows which have a column value that is any of the - specified values. The format of this must be columnName -> - [value1, value2]. - contains: - type: object - description: Searches for a value, or set of values in array column types (such - as a multi-select). If an array of search options is - provided then it must match all. - example: - arrayColumn: - - a - - b - notContains: - type: object - description: The logical inverse of contains. Only works on array column types. - If an array of values is passed, the row must not match any - of them to be returned in the response. - example: - arrayColumn: - - a - - b - containsAny: - type: object - description: As with the contains search, only works on array column types and - searches for any of the provided values when given an array. - example: - arrayColumn: - - a - - b + logicalOperator: + description: When using groups this defines whether all of the filters must + match, or only one of them. + type: string + enum: + - all + - any + onEmptyFilter: + description: If no filters match, should the view return all rows, or no rows. + type: string + enum: + - all + - none + groups: + description: A grouping of filters to be applied. + type: array + items: + type: object + properties: + logicalOperator: + description: When using groups this defines whether all of the filters must + match, or only one of them. + type: string + enum: + - all + - any + filters: + description: A list of filters to apply + type: array + items: + type: object + properties: + operator: + type: string + description: The type of search operation which is being performed. + enum: + - equal + - notEqual + - empty + - notEmpty + - fuzzy + - string + - contains + - notContains + - containsAny + - oneOf + - range + field: + type: string + description: The field in the view to perform the search on. + value: + description: The value to search for - the type will depend on the operator in + use. + oneOf: + - type: string + - type: number + - type: boolean + - type: object + - type: array + groups: + description: A grouping of filters to be applied. + type: array + items: + type: object + properties: + logicalOperator: + description: When using groups this defines whether all of the filters must + match, or only one of them. + type: string + enum: + - all + - any + filters: + description: A list of filters to apply + type: array + items: + type: object + properties: + operator: + type: string + description: The type of search operation which is being performed. + enum: + - equal + - notEqual + - empty + - notEmpty + - fuzzy + - string + - contains + - notContains + - containsAny + - oneOf + - range + field: + type: string + description: The field in the view to perform the search on. + value: + description: The value to search for - the type will depend on the operator in + use. + oneOf: + - type: string + - type: number + - type: boolean + - type: object + - type: array sort: type: object required: @@ -2145,86 +2227,115 @@ components: description: A column used to display rows from this view - usually used when rendered in tables. query: + description: Search parameters for view type: object + required: [] properties: - allOr: - type: boolean - description: Specifies that a row should be returned if it satisfies any of the - specified options, rather than requiring it to fulfill all - the search parameters. This defaults to false, meaning AND - logic will be used. - string: - type: object - example: - columnName1: value - columnName2: value - description: A map of field name to the string to search for, this will look for - rows that have a value starting with the string value. - additionalProperties: - type: string - description: The value to search for in the column. - fuzzy: - type: object - description: Searches for a sub-string within a string column, e.g. searching - for 'dib' will match 'Budibase'. - range: - type: object - description: Searches within a range, the format of this must be in the format - of an object with a "low" and "high" property. - example: - columnName1: - low: 10 - high: 20 - equal: - type: object - description: Searches for rows that have a column value that is exactly the - value set. - notEqual: - type: object - description: Searches for any row which does not contain the specified column - value. - empty: - type: object - description: Searches for rows which do not contain the specified column. The - object should simply contain keys of the column names, - these can map to any value. - example: - columnName1: "" - notEmpty: - type: object - description: Searches for rows which have the specified column. - oneOf: - type: object - description: Searches for rows which have a column value that is any of the - specified values. The format of this must be columnName -> - [value1, value2]. - contains: - type: object - description: Searches for a value, or set of values in array column types (such - as a multi-select). If an array of search options is - provided then it must match all. - example: - arrayColumn: - - a - - b - notContains: - type: object - description: The logical inverse of contains. Only works on array column types. - If an array of values is passed, the row must not match - any of them to be returned in the response. - example: - arrayColumn: - - a - - b - containsAny: - type: object - description: As with the contains search, only works on array column types and - searches for any of the provided values when given an - array. - example: - arrayColumn: - - a - - b + logicalOperator: + description: When using groups this defines whether all of the filters must + match, or only one of them. + type: string + enum: + - all + - any + onEmptyFilter: + description: If no filters match, should the view return all rows, or no rows. + type: string + enum: + - all + - none + groups: + description: A grouping of filters to be applied. + type: array + items: + type: object + properties: + logicalOperator: + description: When using groups this defines whether all of the filters must + match, or only one of them. + type: string + enum: + - all + - any + filters: + description: A list of filters to apply + type: array + items: + type: object + properties: + operator: + type: string + description: The type of search operation which is being performed. + enum: + - equal + - notEqual + - empty + - notEmpty + - fuzzy + - string + - contains + - notContains + - containsAny + - oneOf + - range + field: + type: string + description: The field in the view to perform the search on. + value: + description: The value to search for - the type will depend on the operator in + use. + oneOf: + - type: string + - type: number + - type: boolean + - type: object + - type: array + groups: + description: A grouping of filters to be applied. + type: array + items: + type: object + properties: + logicalOperator: + description: When using groups this defines whether all of the filters must + match, or only one of them. + type: string + enum: + - all + - any + filters: + description: A list of filters to apply + type: array + items: + type: object + properties: + operator: + type: string + description: The type of search operation which is being performed. + enum: + - equal + - notEqual + - empty + - notEmpty + - fuzzy + - string + - contains + - notContains + - containsAny + - oneOf + - range + field: + type: string + description: The field in the view to perform the search on. + value: + description: The value to search for - the type will depend on the operator in + use. + oneOf: + - type: string + - type: number + - type: boolean + - type: object + - type: array sort: type: object required: diff --git a/packages/server/specs/resources/view.ts b/packages/server/specs/resources/view.ts index eda934d15d..aeb2b97aa9 100644 --- a/packages/server/specs/resources/view.ts +++ b/packages/server/specs/resources/view.ts @@ -1,7 +1,14 @@ import { object } from "./utils" import Resource from "./utils/Resource" -import { CalculationType, SortOrder, SortType } from "@budibase/types" -import { searchSchema } from "./misc" +import { + ArrayOperator, + BasicOperator, + CalculationType, + RangeOperator, + SortOrder, + SortType, +} from "@budibase/types" +import { cloneDeep } from "lodash" const view = { name: "peopleView", @@ -25,9 +32,17 @@ const view = { }, }, query: { - string: { - column: "value", - }, + logicalOperator: "all", + onEmptyFilter: "none", + groups: [ + { + logicalOperator: "any", + filters: [ + { operator: "string", field: "name", value: "John" }, + { operator: "range", field: "age", value: { low: 18, high: 100 } }, + ], + }, + ], }, primaryDisplay: "name", } @@ -68,6 +83,78 @@ const baseColumnDef = { }, } +const logicalOperator = { + description: + "When using groups this defines whether all of the filters must match, or only one of them.", + type: "string", + enum: ["all", "any"], +} + +const filterGroup = { + description: "A grouping of filters to be applied.", + type: "array", + items: { + type: "object", + properties: { + logicalOperator, + filters: { + description: "A list of filters to apply", + type: "array", + items: { + type: "object", + properties: { + operator: { + type: "string", + description: + "The type of search operation which is being performed.", + enum: [ + ...Object.values(BasicOperator), + ...Object.values(ArrayOperator), + ...Object.values(RangeOperator), + ], + }, + field: { + type: "string", + description: "The field in the view to perform the search on.", + }, + value: { + description: + "The value to search for - the type will depend on the operator in use.", + oneOf: [ + { type: "string" }, + { type: "number" }, + { type: "boolean" }, + { type: "object" }, + { type: "array" }, + ], + }, + }, + }, + }, + }, + }, +} + +// have to clone to avoid constantly recursive structure - we can't represent this easily +const layeredFilterGroup: any = cloneDeep(filterGroup) +layeredFilterGroup.items.properties.groups = filterGroup + +const viewQuerySchema = { + description: "Search parameters for view", + type: "object", + required: [], + properties: { + logicalOperator, + onEmptyFilter: { + description: + "If no filters match, should the view return all rows, or no rows.", + type: "string", + enum: ["all", "none"], + }, + groups: layeredFilterGroup, + }, +} + const viewSchema = { description: "The view to be created/updated.", type: "object", @@ -91,7 +178,7 @@ const viewSchema = { description: "A column used to display rows from this view - usually used when rendered in tables.", }, - query: searchSchema, + query: viewQuerySchema, sort: { type: "object", required: ["field"], diff --git a/packages/server/src/api/controllers/public/views.ts b/packages/server/src/api/controllers/public/views.ts index 51b18b4426..238b46e366 100644 --- a/packages/server/src/api/controllers/public/views.ts +++ b/packages/server/src/api/controllers/public/views.ts @@ -11,12 +11,10 @@ function fixView(view: ViewV2, params?: { viewId: string }) { if (params?.viewId) { view.id = params.viewId } - if (!view.version) { - view.version = 2 - } if (!view.query) { view.query = {} } + view.version = 2 return view } diff --git a/packages/server/src/api/routes/public/tests/Request.ts b/packages/server/src/api/routes/public/tests/Request.ts index 5b9a7210c9..56d93340f7 100644 --- a/packages/server/src/api/routes/public/tests/Request.ts +++ b/packages/server/src/api/routes/public/tests/Request.ts @@ -96,6 +96,19 @@ export class PublicRowAPI { this.request = request } + async create( + tableId: string, + row: Row, + expectations?: PublicAPIExpectations + ): Promise> { + return this.request.send( + "post", + `/tables/${tableId}/rows`, + row, + expectations + ) + } + async search( tableId: string, query: SearchFilters, diff --git a/packages/server/src/api/routes/public/tests/views.spec.ts b/packages/server/src/api/routes/public/tests/views.spec.ts index 5c6aee3af1..ef2074b1a3 100644 --- a/packages/server/src/api/routes/public/tests/views.spec.ts +++ b/packages/server/src/api/routes/public/tests/views.spec.ts @@ -65,4 +65,26 @@ describe("check public API security", () => { const result = await request.views.destroy(view.data.id, { status: 204 }) expect(result).toBeDefined() }) + + it("should be able to search rows through a view", async () => { + const row1 = await request.rows.create( + table._id!, + { name: "hello world" }, + { status: 200 } + ) + await request.rows.create(table._id!, { name: "foo bar" }, { status: 200 }) + const response = await request.views.create( + { + ...baseView(), + query: { + string: { + name: "hello", + }, + }, + }, + { status: 201 } + ) + const results = await request.rows.viewSearch(response.data.id, {}) + expect(results.data.length).toEqual(1) + }) }) diff --git a/packages/server/src/definitions/openapi.ts b/packages/server/src/definitions/openapi.ts index a0e18ea0f3..b82229130b 100644 --- a/packages/server/src/definitions/openapi.ts +++ b/packages/server/src/definitions/openapi.ts @@ -845,49 +845,90 @@ export interface components { type?: "calculation"; /** @description A column used to display rows from this view - usually used when rendered in tables. */ primaryDisplay?: string; + /** @description Search parameters for view */ query?: { - /** @description Specifies that a row should be returned if it satisfies any of the specified options, rather than requiring it to fulfill all the search parameters. This defaults to false, meaning AND logic will be used. */ - allOr?: boolean; /** - * @description A map of field name to the string to search for, this will look for rows that have a value starting with the string value. - * @example [object Object] + * @description When using groups this defines whether all of the filters must match, or only one of them. + * @enum {string} */ - string?: { [key: string]: string }; - /** @description Searches for a sub-string within a string column, e.g. searching for 'dib' will match 'Budibase'. */ - fuzzy?: { [key: string]: unknown }; + logicalOperator?: "all" | "any"; /** - * @description Searches within a range, the format of this must be in the format of an object with a "low" and "high" property. - * @example [object Object] + * @description If no filters match, should the view return all rows, or no rows. + * @enum {string} */ - range?: { [key: string]: unknown }; - /** @description Searches for rows that have a column value that is exactly the value set. */ - equal?: { [key: string]: unknown }; - /** @description Searches for any row which does not contain the specified column value. */ - notEqual?: { [key: string]: unknown }; - /** - * @description Searches for rows which do not contain the specified column. The object should simply contain keys of the column names, these can map to any value. - * @example [object Object] - */ - empty?: { [key: string]: unknown }; - /** @description Searches for rows which have the specified column. */ - notEmpty?: { [key: string]: unknown }; - /** @description Searches for rows which have a column value that is any of the specified values. The format of this must be columnName -> [value1, value2]. */ - oneOf?: { [key: string]: unknown }; - /** - * @description Searches for a value, or set of values in array column types (such as a multi-select). If an array of search options is provided then it must match all. - * @example [object Object] - */ - contains?: { [key: string]: unknown }; - /** - * @description The logical inverse of contains. Only works on array column types. If an array of values is passed, the row must not match any of them to be returned in the response. - * @example [object Object] - */ - notContains?: { [key: string]: unknown }; - /** - * @description As with the contains search, only works on array column types and searches for any of the provided values when given an array. - * @example [object Object] - */ - containsAny?: { [key: string]: unknown }; + onEmptyFilter?: "all" | "none"; + /** @description A grouping of filters to be applied. */ + groups?: { + /** + * @description When using groups this defines whether all of the filters must match, or only one of them. + * @enum {string} + */ + logicalOperator?: "all" | "any"; + /** @description A list of filters to apply */ + filters?: { + /** + * @description The type of search operation which is being performed. + * @enum {string} + */ + operator?: + | "equal" + | "notEqual" + | "empty" + | "notEmpty" + | "fuzzy" + | "string" + | "contains" + | "notContains" + | "containsAny" + | "oneOf" + | "range"; + /** @description The field in the view to perform the search on. */ + field?: string; + /** @description The value to search for - the type will depend on the operator in use. */ + value?: + | string + | number + | boolean + | { [key: string]: unknown } + | unknown[]; + }[]; + /** @description A grouping of filters to be applied. */ + groups?: { + /** + * @description When using groups this defines whether all of the filters must match, or only one of them. + * @enum {string} + */ + logicalOperator?: "all" | "any"; + /** @description A list of filters to apply */ + filters?: { + /** + * @description The type of search operation which is being performed. + * @enum {string} + */ + operator?: + | "equal" + | "notEqual" + | "empty" + | "notEmpty" + | "fuzzy" + | "string" + | "contains" + | "notContains" + | "containsAny" + | "oneOf" + | "range"; + /** @description The field in the view to perform the search on. */ + field?: string; + /** @description The value to search for - the type will depend on the operator in use. */ + value?: + | string + | number + | boolean + | { [key: string]: unknown } + | unknown[]; + }[]; + }[]; + }[]; }; sort?: { /** @description The field from the table/view schema to sort on. */ @@ -946,49 +987,90 @@ export interface components { type?: "calculation"; /** @description A column used to display rows from this view - usually used when rendered in tables. */ primaryDisplay?: string; + /** @description Search parameters for view */ query?: { - /** @description Specifies that a row should be returned if it satisfies any of the specified options, rather than requiring it to fulfill all the search parameters. This defaults to false, meaning AND logic will be used. */ - allOr?: boolean; /** - * @description A map of field name to the string to search for, this will look for rows that have a value starting with the string value. - * @example [object Object] + * @description When using groups this defines whether all of the filters must match, or only one of them. + * @enum {string} */ - string?: { [key: string]: string }; - /** @description Searches for a sub-string within a string column, e.g. searching for 'dib' will match 'Budibase'. */ - fuzzy?: { [key: string]: unknown }; + logicalOperator?: "all" | "any"; /** - * @description Searches within a range, the format of this must be in the format of an object with a "low" and "high" property. - * @example [object Object] + * @description If no filters match, should the view return all rows, or no rows. + * @enum {string} */ - range?: { [key: string]: unknown }; - /** @description Searches for rows that have a column value that is exactly the value set. */ - equal?: { [key: string]: unknown }; - /** @description Searches for any row which does not contain the specified column value. */ - notEqual?: { [key: string]: unknown }; - /** - * @description Searches for rows which do not contain the specified column. The object should simply contain keys of the column names, these can map to any value. - * @example [object Object] - */ - empty?: { [key: string]: unknown }; - /** @description Searches for rows which have the specified column. */ - notEmpty?: { [key: string]: unknown }; - /** @description Searches for rows which have a column value that is any of the specified values. The format of this must be columnName -> [value1, value2]. */ - oneOf?: { [key: string]: unknown }; - /** - * @description Searches for a value, or set of values in array column types (such as a multi-select). If an array of search options is provided then it must match all. - * @example [object Object] - */ - contains?: { [key: string]: unknown }; - /** - * @description The logical inverse of contains. Only works on array column types. If an array of values is passed, the row must not match any of them to be returned in the response. - * @example [object Object] - */ - notContains?: { [key: string]: unknown }; - /** - * @description As with the contains search, only works on array column types and searches for any of the provided values when given an array. - * @example [object Object] - */ - containsAny?: { [key: string]: unknown }; + onEmptyFilter?: "all" | "none"; + /** @description A grouping of filters to be applied. */ + groups?: { + /** + * @description When using groups this defines whether all of the filters must match, or only one of them. + * @enum {string} + */ + logicalOperator?: "all" | "any"; + /** @description A list of filters to apply */ + filters?: { + /** + * @description The type of search operation which is being performed. + * @enum {string} + */ + operator?: + | "equal" + | "notEqual" + | "empty" + | "notEmpty" + | "fuzzy" + | "string" + | "contains" + | "notContains" + | "containsAny" + | "oneOf" + | "range"; + /** @description The field in the view to perform the search on. */ + field?: string; + /** @description The value to search for - the type will depend on the operator in use. */ + value?: + | string + | number + | boolean + | { [key: string]: unknown } + | unknown[]; + }[]; + /** @description A grouping of filters to be applied. */ + groups?: { + /** + * @description When using groups this defines whether all of the filters must match, or only one of them. + * @enum {string} + */ + logicalOperator?: "all" | "any"; + /** @description A list of filters to apply */ + filters?: { + /** + * @description The type of search operation which is being performed. + * @enum {string} + */ + operator?: + | "equal" + | "notEqual" + | "empty" + | "notEmpty" + | "fuzzy" + | "string" + | "contains" + | "notContains" + | "containsAny" + | "oneOf" + | "range"; + /** @description The field in the view to perform the search on. */ + field?: string; + /** @description The value to search for - the type will depend on the operator in use. */ + value?: + | string + | number + | boolean + | { [key: string]: unknown } + | unknown[]; + }[]; + }[]; + }[]; }; sort?: { /** @description The field from the table/view schema to sort on. */ @@ -1049,49 +1131,90 @@ export interface components { type?: "calculation"; /** @description A column used to display rows from this view - usually used when rendered in tables. */ primaryDisplay?: string; + /** @description Search parameters for view */ query?: { - /** @description Specifies that a row should be returned if it satisfies any of the specified options, rather than requiring it to fulfill all the search parameters. This defaults to false, meaning AND logic will be used. */ - allOr?: boolean; /** - * @description A map of field name to the string to search for, this will look for rows that have a value starting with the string value. - * @example [object Object] + * @description When using groups this defines whether all of the filters must match, or only one of them. + * @enum {string} */ - string?: { [key: string]: string }; - /** @description Searches for a sub-string within a string column, e.g. searching for 'dib' will match 'Budibase'. */ - fuzzy?: { [key: string]: unknown }; + logicalOperator?: "all" | "any"; /** - * @description Searches within a range, the format of this must be in the format of an object with a "low" and "high" property. - * @example [object Object] + * @description If no filters match, should the view return all rows, or no rows. + * @enum {string} */ - range?: { [key: string]: unknown }; - /** @description Searches for rows that have a column value that is exactly the value set. */ - equal?: { [key: string]: unknown }; - /** @description Searches for any row which does not contain the specified column value. */ - notEqual?: { [key: string]: unknown }; - /** - * @description Searches for rows which do not contain the specified column. The object should simply contain keys of the column names, these can map to any value. - * @example [object Object] - */ - empty?: { [key: string]: unknown }; - /** @description Searches for rows which have the specified column. */ - notEmpty?: { [key: string]: unknown }; - /** @description Searches for rows which have a column value that is any of the specified values. The format of this must be columnName -> [value1, value2]. */ - oneOf?: { [key: string]: unknown }; - /** - * @description Searches for a value, or set of values in array column types (such as a multi-select). If an array of search options is provided then it must match all. - * @example [object Object] - */ - contains?: { [key: string]: unknown }; - /** - * @description The logical inverse of contains. Only works on array column types. If an array of values is passed, the row must not match any of them to be returned in the response. - * @example [object Object] - */ - notContains?: { [key: string]: unknown }; - /** - * @description As with the contains search, only works on array column types and searches for any of the provided values when given an array. - * @example [object Object] - */ - containsAny?: { [key: string]: unknown }; + onEmptyFilter?: "all" | "none"; + /** @description A grouping of filters to be applied. */ + groups?: { + /** + * @description When using groups this defines whether all of the filters must match, or only one of them. + * @enum {string} + */ + logicalOperator?: "all" | "any"; + /** @description A list of filters to apply */ + filters?: { + /** + * @description The type of search operation which is being performed. + * @enum {string} + */ + operator?: + | "equal" + | "notEqual" + | "empty" + | "notEmpty" + | "fuzzy" + | "string" + | "contains" + | "notContains" + | "containsAny" + | "oneOf" + | "range"; + /** @description The field in the view to perform the search on. */ + field?: string; + /** @description The value to search for - the type will depend on the operator in use. */ + value?: + | string + | number + | boolean + | { [key: string]: unknown } + | unknown[]; + }[]; + /** @description A grouping of filters to be applied. */ + groups?: { + /** + * @description When using groups this defines whether all of the filters must match, or only one of them. + * @enum {string} + */ + logicalOperator?: "all" | "any"; + /** @description A list of filters to apply */ + filters?: { + /** + * @description The type of search operation which is being performed. + * @enum {string} + */ + operator?: + | "equal" + | "notEqual" + | "empty" + | "notEmpty" + | "fuzzy" + | "string" + | "contains" + | "notContains" + | "containsAny" + | "oneOf" + | "range"; + /** @description The field in the view to perform the search on. */ + field?: string; + /** @description The value to search for - the type will depend on the operator in use. */ + value?: + | string + | number + | boolean + | { [key: string]: unknown } + | unknown[]; + }[]; + }[]; + }[]; }; sort?: { /** @description The field from the table/view schema to sort on. */ From cdf7cb9fab74f6a76d6bb3cb3492c2213fa74bf1 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Fri, 15 Nov 2024 17:54:14 +0000 Subject: [PATCH 14/31] Final update to view mapping and typing. --- .../server/src/api/controllers/public/rows.ts | 10 +-- .../src/api/controllers/public/views.ts | 86 +++++++++++-------- .../server/src/api/controllers/row/views.ts | 1 + packages/server/src/api/routes/public/rows.ts | 4 +- .../src/api/routes/public/tests/Request.ts | 19 ++-- .../src/api/routes/public/tests/views.spec.ts | 29 ++++--- .../server/src/api/routes/utils/validators.ts | 43 +++++++++- 7 files changed, 124 insertions(+), 68 deletions(-) diff --git a/packages/server/src/api/controllers/public/rows.ts b/packages/server/src/api/controllers/public/rows.ts index 634a41ed85..24ac18c7b1 100644 --- a/packages/server/src/api/controllers/public/rows.ts +++ b/packages/server/src/api/controllers/public/rows.ts @@ -47,12 +47,10 @@ export async function search(ctx: UserCtx, next: Next) { export async function viewSearch(ctx: UserCtx, next: Next) { ctx.request.body = getSearchParameters(ctx) - await rowController.views.searchView({ - ...ctx, - params: { - viewId: ctx.params.viewId, - }, - }) + ctx.params = { + viewId: ctx.params.viewId, + } + await rowController.views.searchView(ctx) await next() } diff --git a/packages/server/src/api/controllers/public/views.ts b/packages/server/src/api/controllers/public/views.ts index 238b46e366..5b08f39e36 100644 --- a/packages/server/src/api/controllers/public/views.ts +++ b/packages/server/src/api/controllers/public/views.ts @@ -1,72 +1,88 @@ import { search as stringSearch } from "./utils" import * as controller from "../view" -import { ViewV2, UserCtx } from "@budibase/types" +import { ViewV2, UserCtx, UISearchFilter, PublicAPIView } from "@budibase/types" import { Next } from "koa" import { merge } from "lodash" -function fixView(view: ViewV2, params?: { viewId: string }) { - if (!params || !view) { - return view +function viewRequest(view: PublicAPIView, params?: { viewId: string }) { + const viewV2: ViewV2 = view + if (!viewV2) { + return viewV2 } if (params?.viewId) { - view.id = params.viewId + viewV2.id = params.viewId } if (!view.query) { - view.query = {} + viewV2.query = {} + } else { + // public API only has one form of query + viewV2.queryUI = viewV2.query as UISearchFilter } - view.version = 2 - return view + viewV2.version = 2 + return viewV2 +} + +function viewResponse(view: ViewV2): PublicAPIView { + // remove our internal structure - always un-necessary + delete view.query + return { + ...view, + query: view.queryUI, + } +} + +function viewsResponse(views: ViewV2[]): PublicAPIView[] { + return views.map(viewResponse) } export async function search(ctx: UserCtx, next: Next) { const { name } = ctx.request.body await controller.v2.fetch(ctx) - ctx.body = stringSearch(ctx.body.data, name) + ctx.body.data = viewsResponse(stringSearch(ctx.body.data, name)) await next() } export async function create(ctx: UserCtx, next: Next) { - await controller.v2.create( - merge(ctx, { - request: { - body: fixView(ctx.request.body), - }, - }) - ) + ctx = merge(ctx, { + request: { + body: viewRequest(ctx.request.body), + }, + }) + await controller.v2.create(ctx) + ctx.body.data = viewResponse(ctx.body.data) await next() } export async function read(ctx: UserCtx, next: Next) { - await controller.v2.get( - merge(ctx, { - params: { - viewId: ctx.params.viewId, - }, - }) - ) + ctx = merge(ctx, { + params: { + viewId: ctx.params.viewId, + }, + }) + await controller.v2.get(ctx) + ctx.body.data = viewResponse(ctx.body.data) await next() } export async function update(ctx: UserCtx, next: Next) { const viewId = ctx.params.viewId - await controller.v2.update( - merge(ctx, { - request: { - body: { - data: fixView(ctx.request.body, { viewId }), - }, + ctx = merge(ctx, { + request: { + body: { + data: viewRequest(ctx.request.body, { viewId }), }, - params: { - viewId, - }, - }) - ) + }, + params: { + viewId, + }, + }) + await controller.v2.update(ctx) + ctx.body.data = viewResponse(ctx.body.data) await next() } export async function destroy(ctx: UserCtx, next: Next) { await controller.v2.remove(ctx) - ctx.body = ctx.table await next() } diff --git a/packages/server/src/api/controllers/row/views.ts b/packages/server/src/api/controllers/row/views.ts index b8d01424f2..02ac871de0 100644 --- a/packages/server/src/api/controllers/row/views.ts +++ b/packages/server/src/api/controllers/row/views.ts @@ -50,6 +50,7 @@ export async function searchView( result.rows.forEach(r => (r._viewId = view.id)) ctx.body = result } + function getSortOptions(request: SearchViewRowRequest, view: ViewV2) { if (request.sort) { return { diff --git a/packages/server/src/api/routes/public/rows.ts b/packages/server/src/api/routes/public/rows.ts index 2085c5cf0f..2fb81d4601 100644 --- a/packages/server/src/api/routes/public/rows.ts +++ b/packages/server/src/api/routes/public/rows.ts @@ -1,4 +1,4 @@ -import controller from "../../controllers/public/rows" +import controller, { viewSearch } from "../../controllers/public/rows" import Endpoint from "./utils/Endpoint" import { externalSearchValidator } from "../utils/validators" @@ -200,7 +200,7 @@ read.push( new Endpoint( "post", "/views/:viewId/rows/search", - controller.search + controller.viewSearch ).addMiddleware(externalSearchValidator()) ) diff --git a/packages/server/src/api/routes/public/tests/Request.ts b/packages/server/src/api/routes/public/tests/Request.ts index 56d93340f7..463d56f141 100644 --- a/packages/server/src/api/routes/public/tests/Request.ts +++ b/packages/server/src/api/routes/public/tests/Request.ts @@ -6,6 +6,7 @@ import { ViewV2Schema, ViewV2, ViewV2Type, + PublicAPIView, } from "@budibase/types" import { HttpMethod, MakeRequestResponse, generateMakeRequest } from "./utils" import TestConfiguration from "../../../../tests/utilities/TestConfiguration" @@ -148,17 +149,17 @@ export class PublicViewAPI { } async create( - view: Omit, + view: Omit, expectations?: PublicAPIExpectations - ): Promise> { + ): Promise> { return this.request.send("post", "/views", view, expectations) } async update( viewId: string, - view: Omit, + view: Omit, expectations?: PublicAPIExpectations - ): Promise> { + ): Promise> { return this.request.send("put", `/views/${viewId}`, view, expectations) } @@ -177,20 +178,14 @@ export class PublicViewAPI { async find( viewId: string, expectations?: PublicAPIExpectations - ): Promise> { + ): Promise> { return this.request.send("get", `/views/${viewId}`, undefined, expectations) } - async fetch( - expectations?: PublicAPIExpectations - ): Promise> { - return this.request.send("get", "/views", undefined, expectations) - } - async search( viewName: string, expectations?: PublicAPIExpectations - ): Promise> { + ): Promise> { return this.request.send( "post", "/views/search", diff --git a/packages/server/src/api/routes/public/tests/views.spec.ts b/packages/server/src/api/routes/public/tests/views.spec.ts index ef2074b1a3..1cbc383f7e 100644 --- a/packages/server/src/api/routes/public/tests/views.spec.ts +++ b/packages/server/src/api/routes/public/tests/views.spec.ts @@ -1,6 +1,6 @@ import * as setup from "../../tests/utilities" import { basicTable } from "../../../../tests/utilities/structures" -import { Table } from "@budibase/types" +import { BasicOperator, Table, UILogicalOperator } from "@budibase/types" import { PublicAPIRequest } from "./Request" import { generator } from "@budibase/backend-core/tests" @@ -34,14 +34,10 @@ describe("check public API security", () => { it("should be able to update a view", async () => { const view = await request.views.create(baseView(), { status: 201 }) - await request.views.update( - view.data.id, - { - ...view.data, - name: "new name", - }, - { status: 200 } - ) + const response = await request.views.update(view.data.id, { + ...view.data, + name: "new name", + }) }) it("should be able to search views", async () => { @@ -77,9 +73,18 @@ describe("check public API security", () => { { ...baseView(), query: { - string: { - name: "hello", - }, + logicalOperator: UILogicalOperator.ANY, + groups: [ + { + filters: [ + { + operator: BasicOperator.STRING, + field: "name", + value: "hello", + }, + ], + }, + ], }, }, { status: 201 } diff --git a/packages/server/src/api/routes/utils/validators.ts b/packages/server/src/api/routes/utils/validators.ts index f07f4ff29f..3bee4f88ce 100644 --- a/packages/server/src/api/routes/utils/validators.ts +++ b/packages/server/src/api/routes/utils/validators.ts @@ -12,6 +12,10 @@ import { ViewV2Type, SortOrder, SortType, + UILogicalOperator, + BasicOperator, + ArrayOperator, + RangeOperator, } from "@budibase/types" import Joi, { CustomValidator } from "joi" import { ValidSnippetNameRegex, helpers } from "@budibase/shared-core" @@ -69,6 +73,43 @@ export function tableValidator() { ) } +function searchUIFilterValidator() { + const logicalOperator = Joi.string().valid( + ...Object.values(UILogicalOperator) + ) + const operators = [ + ...Object.values(BasicOperator), + ...Object.values(ArrayOperator), + ...Object.values(RangeOperator), + ] + const filters = Joi.array().items( + Joi.object({ + operator: Joi.string() + .valid(...operators) + .required(), + field: Joi.string().required(), + // could do with better validation of value based on operator + value: Joi.any().required(), + }) + ) + return Joi.object({ + logicalOperator, + onEmptyFilter: Joi.string().valid(...Object.values(EmptyFilterOption)), + groups: Joi.array().items( + Joi.object({ + logicalOperator, + filters, + groups: Joi.array().items( + Joi.object({ + filters, + logicalOperator, + }) + ), + }) + ), + }) +} + export function viewValidator() { return auth.joiValidator.body( Joi.object({ @@ -78,7 +119,7 @@ export function viewValidator() { type: Joi.string().optional().valid(null, ViewV2Type.CALCULATION), primaryDisplay: OPTIONAL_STRING, schema: Joi.object().required(), - query: searchFiltersValidator().optional(), + query: searchUIFilterValidator().optional(), sort: Joi.object({ field: Joi.string().required(), order: Joi.string() From 3149f0dbd7d819063a84b095011d33f169c4f533 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Mon, 18 Nov 2024 13:26:16 +0000 Subject: [PATCH 15/31] Adding public API def. --- packages/types/src/documents/app/view.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/types/src/documents/app/view.ts b/packages/types/src/documents/app/view.ts index 1212031f24..1170284b15 100644 --- a/packages/types/src/documents/app/view.ts +++ b/packages/types/src/documents/app/view.ts @@ -101,6 +101,10 @@ export interface ViewV2 { schema?: ViewV2Schema } +export interface PublicAPIView extends Omit { + query?: UISearchFilter +} + export type ViewV2Schema = Record export type ViewSchema = ViewCountOrSumSchema | ViewStatisticsSchema From 67fedd986cd82453eab21553a35c4301cea1adc7 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Thu, 21 Nov 2024 12:18:20 +0000 Subject: [PATCH 16/31] Adding some validation for tenant lookup endpoint, fail with a better error if something goes wrong with email processing. --- packages/worker/package.json | 1 + .../src/api/controllers/global/users.ts | 5 + yarn.lock | 379 ++++++++---------- 3 files changed, 171 insertions(+), 214 deletions(-) diff --git a/packages/worker/package.json b/packages/worker/package.json index 85eae6c88a..5b6804d38f 100644 --- a/packages/worker/package.json +++ b/packages/worker/package.json @@ -52,6 +52,7 @@ "bull": "4.10.1", "dd-trace": "5.23.0", "dotenv": "8.6.0", + "email-validator": "^2.0.4", "global-agent": "3.0.0", "ical-generator": "4.1.0", "joi": "17.6.0", diff --git a/packages/worker/src/api/controllers/global/users.ts b/packages/worker/src/api/controllers/global/users.ts index f415698fc0..2479a50d9e 100644 --- a/packages/worker/src/api/controllers/global/users.ts +++ b/packages/worker/src/api/controllers/global/users.ts @@ -40,6 +40,7 @@ import { import { checkAnyUserExists } from "../../../utilities/users" import { isEmailConfigured } from "../../../utilities/email" import { BpmStatusKey, BpmStatusValue, utils } from "@budibase/shared-core" +import emailValidator from "email-validator" import crypto from "crypto" const MAX_USERS_UPLOAD_LIMIT = 1000 @@ -300,6 +301,10 @@ export const find = async (ctx: any) => { export const tenantUserLookup = async (ctx: any) => { const id = ctx.params.id + // is email, check its valid + if (id.includes("@") && !emailValidator.validate(id)) { + ctx.throw(400, `${id} is not a valid email address to lookup.`) + } const user = await userSdk.core.getFirstPlatformUser(id) if (user) { ctx.body = user diff --git a/yarn.lock b/yarn.lock index 28591e7be0..1f74b624ae 100644 --- a/yarn.lock +++ b/yarn.lock @@ -842,7 +842,7 @@ "@azure/abort-controller" "^2.0.0" tslib "^2.6.2" -"@azure/identity@4.2.1", "@azure/identity@^4.2.1": +"@azure/identity@^4.2.1": version "4.2.1" resolved "https://registry.yarnpkg.com/@azure/identity/-/identity-4.2.1.tgz#22b366201e989b7b41c0e1690e103bd579c31e4c" integrity sha512-U8hsyC9YPcEIzoaObJlRDvp7KiF0MGS7xcWbyJSVvXRkC/HXo1f0oYeBYmEvVgRfacw7GHf6D6yAoh9JHz6A5Q== @@ -2196,47 +2196,6 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@budibase/backend-core@3.2.7": - version "0.0.0" - dependencies: - "@budibase/nano" "10.1.5" - "@budibase/pouchdb-replication-stream" "1.2.11" - "@budibase/shared-core" "0.0.0" - "@budibase/types" "0.0.0" - "@techpass/passport-openidconnect" "0.3.3" - aws-cloudfront-sign "3.0.2" - aws-sdk "2.1692.0" - bcrypt "5.1.0" - bcryptjs "2.4.3" - bull "4.10.1" - correlation-id "4.0.0" - dd-trace "5.23.0" - dotenv "16.0.1" - google-auth-library "^8.0.1" - google-spreadsheet "npm:@budibase/google-spreadsheet@4.1.5" - ioredis "5.3.2" - joi "17.6.0" - jsonwebtoken "9.0.2" - knex "2.4.2" - koa-passport "^6.0.0" - koa-pino-logger "4.0.0" - lodash "4.17.21" - node-fetch "2.6.7" - passport-google-oauth "2.0.0" - passport-local "1.0.0" - passport-oauth2-refresh "^2.1.0" - pino "8.11.0" - pino-http "8.3.3" - posthog-node "4.0.1" - pouchdb "9.0.0" - pouchdb-find "9.0.0" - redlock "4.2.0" - rotating-file-stream "3.1.0" - sanitize-s3-objectkey "0.0.1" - semver "^7.5.4" - tar-fs "2.1.1" - uuid "^8.3.2" - "@budibase/handlebars-helpers@^0.13.2": version "0.13.2" resolved "https://registry.yarnpkg.com/@budibase/handlebars-helpers/-/handlebars-helpers-0.13.2.tgz#73ab51c464e91fd955b429017648e0257060db77" @@ -2279,47 +2238,6 @@ pouchdb-promise "^6.0.4" through2 "^2.0.0" -"@budibase/pro@npm:@budibase/pro@latest": - version "3.2.7" - resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-3.2.7.tgz#4dfc48f3e2ba3a3b235872e54c3de4de54ea7941" - integrity sha512-GRRaf1qSqQfoodjvKjBDvNOVHZrcSXF7so6Y9Xv/MiRTjDES5nmhIT5rL/PQ1+Mb+sPytYmMaJ2mlTtwqSQZgQ== - dependencies: - "@anthropic-ai/sdk" "^0.27.3" - "@budibase/backend-core" "3.2.7" - "@budibase/shared-core" "3.2.7" - "@budibase/string-templates" "3.2.7" - "@budibase/types" "3.2.7" - "@koa/router" "8.0.8" - bull "4.10.1" - dd-trace "5.2.0" - joi "17.6.0" - jsonwebtoken "9.0.2" - lru-cache "^7.14.1" - memorystream "^0.3.1" - node-fetch "2.6.7" - openai "4.59.0" - scim-patch "^0.8.1" - scim2-parse-filter "^0.2.8" - -"@budibase/shared-core@3.2.7": - version "0.0.0" - dependencies: - "@budibase/types" "0.0.0" - cron-validate "1.4.5" - -"@budibase/string-templates@3.2.7": - version "0.0.0" - dependencies: - "@budibase/handlebars-helpers" "^0.13.2" - dayjs "^1.10.8" - handlebars "^4.7.8" - lodash.clonedeep "^4.5.0" - -"@budibase/types@3.2.7": - version "0.0.0" - dependencies: - scim-patch "^0.8.1" - "@bull-board/api@5.10.2": version "5.10.2" resolved "https://registry.yarnpkg.com/@bull-board/api/-/api-5.10.2.tgz#ae8ff6918b23897bf879a6ead3683f964374c4b3" @@ -2477,13 +2395,6 @@ resolved "https://registry.yarnpkg.com/@dagrejs/graphlib/-/graphlib-2.2.4.tgz#d77bfa9ff49e2307c0c6e6b8b26b5dd3c05816c4" integrity sha512-mepCf/e9+SKYy1d02/UkvSy6+6MoyXhVxP8lLDfA7BPE1X1d4dR0sZznmbM8/XVJ1GPM+Svnx7Xj6ZweByWUkw== -"@datadog/native-appsec@7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@datadog/native-appsec/-/native-appsec-7.0.0.tgz#a380174dd49aef2d9bb613a0ec8ead6dc7822095" - integrity sha512-bywstWFW2hWxzPuS0+mFMVHHL0geulx5yQFtsjfszaH2LTAgk2D+Rt40MKbAoZ8q3tRw2dy6aYQ7svO3ca8jpA== - dependencies: - node-gyp-build "^3.9.0" - "@datadog/native-appsec@8.1.1": version "8.1.1" resolved "https://registry.yarnpkg.com/@datadog/native-appsec/-/native-appsec-8.1.1.tgz#76aa34697e6ecbd3d9ef7e6938d3cdcfa689b1f3" @@ -2491,14 +2402,6 @@ dependencies: node-gyp-build "^3.9.0" -"@datadog/native-iast-rewriter@2.2.2": - version "2.2.2" - resolved "https://registry.yarnpkg.com/@datadog/native-iast-rewriter/-/native-iast-rewriter-2.2.2.tgz#3f7feaf6be1af4c83ad063065b8ed509bbaf11cb" - integrity sha512-13ZBhJpjZ/tiV6rYfyAf/ITye9cyd3x12M/2NKhD4Ivev4N4uKBREAjpArOtzKtPXZ5b6oXwVV4ofT1SHoYyzA== - dependencies: - lru-cache "^7.14.0" - node-gyp-build "^4.5.0" - "@datadog/native-iast-rewriter@2.4.1": version "2.4.1" resolved "https://registry.yarnpkg.com/@datadog/native-iast-rewriter/-/native-iast-rewriter-2.4.1.tgz#e8211f78c818906513fb96a549374da0382c7623" @@ -2507,13 +2410,6 @@ lru-cache "^7.14.0" node-gyp-build "^4.5.0" -"@datadog/native-iast-taint-tracking@1.6.4": - version "1.6.4" - resolved "https://registry.yarnpkg.com/@datadog/native-iast-taint-tracking/-/native-iast-taint-tracking-1.6.4.tgz#16c21ad7c36a53420c0d3c5a3720731809cc7e98" - integrity sha512-Owxk7hQ4Dxwv4zJAoMjRga0IvE6lhvxnNc8pJCHsemCWBXchjr/9bqg05Zy5JnMbKUWn4XuZeJD6RFZpRa8bfw== - dependencies: - node-gyp-build "^3.9.0" - "@datadog/native-iast-taint-tracking@3.1.0": version "3.1.0" resolved "https://registry.yarnpkg.com/@datadog/native-iast-taint-tracking/-/native-iast-taint-tracking-3.1.0.tgz#7b2ed7f8fad212d65e5ab03bcdea8b42a3051b2e" @@ -2529,17 +2425,6 @@ node-addon-api "^6.1.0" node-gyp-build "^3.9.0" -"@datadog/pprof@5.0.0": - version "5.0.0" - resolved "https://registry.yarnpkg.com/@datadog/pprof/-/pprof-5.0.0.tgz#0c0aaf06def6d2bc4b2d353ec7b264dadbfbefab" - integrity sha512-vhNan4SBuNWLpexunDJQ+hNbRAgWdk2qy5Iyh7Nn94uSSHXigAJMAvu4jwMKKQKFfchtobOkWT8GQUWW3tgpFg== - dependencies: - delay "^5.0.0" - node-gyp-build "<4.0" - p-limit "^3.1.0" - pprof-format "^2.0.7" - source-map "^0.7.4" - "@datadog/pprof@5.3.0": version "5.3.0" resolved "https://registry.yarnpkg.com/@datadog/pprof/-/pprof-5.3.0.tgz#c2f58d328ecced7f99887f1a559d7fe3aecb9219" @@ -3470,18 +3355,6 @@ koa-compose "^4.1.0" path-to-regexp "^6.3.0" -"@koa/router@8.0.8": - version "8.0.8" - resolved "https://registry.yarnpkg.com/@koa/router/-/router-8.0.8.tgz#95f32d11373d03d89dcb63fabe9ac6f471095236" - integrity sha512-FnT93N4NUehnXr+juupDmG2yfi0JnWdCmNEuIXpCG4TtG+9xvtrLambBH3RclycopVUOEYAim2lydiNBI7IRVg== - dependencies: - debug "^4.1.1" - http-errors "^1.7.3" - koa-compose "^4.1.0" - methods "^1.1.2" - path-to-regexp "1.x" - urijs "^1.19.2" - "@lerna/child-process@7.4.2": version "7.4.2" resolved "https://registry.yarnpkg.com/@lerna/child-process/-/child-process-7.4.2.tgz#a2fd013ac2150dc288270d3e0d0b850c06bec511" @@ -3945,7 +3818,7 @@ resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.8.0.tgz#5aa7abb48f23f693068ed2999ae627d2f7d902ec" integrity sha512-I/s6F7yKUDdtMsoBWXJe8Qz40Tui5vsuKCWJEWVL+5q9sSWRzzx6v2KeNsOBEwd94j0eWkpWCH4yB6rZg9Mf0w== -"@opentelemetry/api@^1.0.0", "@opentelemetry/api@^1.0.1": +"@opentelemetry/api@^1.0.1": version "1.9.0" resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.9.0.tgz#d03eba68273dc0f7509e2a3d5cba21eae10379fe" integrity sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg== @@ -7354,7 +7227,23 @@ aws4@^1.8.0: resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.13.2.tgz#0aa167216965ac9474ccfa83892cfb6b3e1e52ef" integrity sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw== -axios@1.1.3, axios@1.7.7, axios@^0.21.1, axios@^1.0.0, axios@^1.1.3, axios@^1.4.0, axios@^1.6.2, axios@^1.6.8: +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: + 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@^1.0.0, axios@^1.1.3, axios@^1.4.0, axios@^1.6.2, axios@^1.6.8: version "1.7.7" resolved "https://registry.yarnpkg.com/axios/-/axios-1.7.7.tgz#2f554296f9892a72ac8d8e4c5b79c14a91d0a47f" integrity sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q== @@ -9295,48 +9184,11 @@ dayjs@^1.10.8: resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.13.tgz#92430b0139055c3ebb60150aa13e860a4b5a366c" integrity sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg== -dc-polyfill@^0.1.2, dc-polyfill@^0.1.4: +dc-polyfill@^0.1.4: version "0.1.6" resolved "https://registry.yarnpkg.com/dc-polyfill/-/dc-polyfill-0.1.6.tgz#c2940fa68ffb24a7bf127cc6cfdd15b39f0e7f02" integrity sha512-UV33cugmCC49a5uWAApM+6Ev9ZdvIUMTrtCO9fj96TPGOQiea54oeO3tiEVdVeo3J9N2UdJEmbS4zOkkEA35uQ== -dd-trace@5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/dd-trace/-/dd-trace-5.2.0.tgz#6ca2d76ece95f08d98468d7782c22f24192afa53" - integrity sha512-Z5ql3ZKzVW3DPstHPkTPcIPvKljHNtzTYY/WuZRlgT4XK7rMaN0j5nA8LlUh7m+tOPWs05IiKngbYVZjsqhRgA== - dependencies: - "@datadog/native-appsec" "7.0.0" - "@datadog/native-iast-rewriter" "2.2.2" - "@datadog/native-iast-taint-tracking" "1.6.4" - "@datadog/native-metrics" "^2.0.0" - "@datadog/pprof" "5.0.0" - "@datadog/sketches-js" "^2.1.0" - "@opentelemetry/api" "^1.0.0" - "@opentelemetry/core" "^1.14.0" - crypto-randomuuid "^1.0.0" - dc-polyfill "^0.1.2" - ignore "^5.2.4" - import-in-the-middle "^1.7.3" - int64-buffer "^0.1.9" - ipaddr.js "^2.1.0" - istanbul-lib-coverage "3.2.0" - jest-docblock "^29.7.0" - koalas "^1.0.2" - limiter "1.1.5" - lodash.sortby "^4.7.0" - lru-cache "^7.14.0" - methods "^1.1.2" - module-details-from-path "^1.0.3" - msgpack-lite "^0.1.26" - node-abort-controller "^3.1.1" - opentracing ">=0.12.1" - path-to-regexp "^0.1.2" - pprof-format "^2.0.7" - protobufjs "^7.2.5" - retry "^0.13.1" - semver "^7.5.4" - tlhunter-sorted-set "^0.1.0" - dd-trace@5.23.0: version "5.23.0" resolved "https://registry.yarnpkg.com/dd-trace/-/dd-trace-5.23.0.tgz#a0c11863406de440a6675648caf06e1d07d67ba8" @@ -10229,6 +10081,11 @@ elliptic@^6.5.3, elliptic@^6.5.5: minimalistic-assert "^1.0.1" minimalistic-crypto-utils "^1.0.1" +email-validator@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/email-validator/-/email-validator-2.0.4.tgz#b8dfaa5d0dae28f1b03c95881d904d4e40bfe7ed" + integrity sha512-gYCwo7kh5S3IDyZPLZf6hSS0MnZT8QmJFqYvbqlDZSbwdZlY6QZWxJ4i/6UhITOJ4XzyI647Bm2MXKCLqnJ4nQ== + emittery@^0.13.1: version "0.13.1" resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.13.1.tgz#c04b8c3457490e0847ae51fced3af52d338e3dad" @@ -11358,7 +11215,7 @@ 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.15.6: +follow-redirects@^1.14.0, follow-redirects@^1.15.0, follow-redirects@^1.15.6: version "1.15.9" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.9.tgz#a604fa10e443bf98ca94228d9eebcc2e8a2c8ee1" integrity sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ== @@ -12476,7 +12333,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== @@ -12709,7 +12571,7 @@ import-from@^3.0.0: dependencies: resolve-from "^5.0.0" -import-in-the-middle@1.11.2, import-in-the-middle@^1.7.3: +import-in-the-middle@1.11.2: version "1.11.2" resolved "https://registry.yarnpkg.com/import-in-the-middle/-/import-in-the-middle-1.11.2.tgz#dd848e72b63ca6cd7c34df8b8d97fc9baee6174f" integrity sha512-gK6Rr6EykBcc6cVWRSBR5TWf8nn6hZMYSRYqCcHa0l0d1fPK7JSYo6+Mlmck76jIX9aL/IZ71c06U2VpFwl1zA== @@ -12923,11 +12785,6 @@ ip@^2.0.0: resolved "https://registry.yarnpkg.com/ip/-/ip-2.0.0.tgz#4cf4ab182fee2314c75ede1276f8c80b479936da" integrity sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ== -ipaddr.js@^2.1.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-2.2.0.tgz#d33fa7bac284f4de7af949638c9d68157c6b92e8" - integrity sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA== - is-arguments@^1.0.4, is-arguments@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b" @@ -12976,6 +12833,11 @@ is-boolean-object@^1.1.0: call-bind "^1.0.2" has-tostringtag "^1.0.0" +is-buffer@^1.1.5: + version "1.1.6" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" + integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== + is-buffer@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.5.tgz#ebc252e400d22ff8d77fa09888821a24a658c191" @@ -13437,11 +13299,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" @@ -14333,7 +14190,14 @@ kill-port@^1.6.1: get-them-args "1.3.2" shell-exec "1.0.2" -kind-of@6.0.3, kind-of@^3.0.2, kind-of@^3.1.0, kind-of@^6.0.0, kind-of@^6.0.2, kind-of@^6.0.3: +kind-of@^3.0.2, kind-of@^3.1.0: + version "3.2.2" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" + integrity sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ== + dependencies: + is-buffer "^1.1.5" + +kind-of@^6.0.0, kind-of@^6.0.2, kind-of@^6.0.3: version "6.0.3" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== @@ -15902,7 +15766,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== @@ -16075,7 +15939,7 @@ node-abi@^3.3.0: dependencies: semver "^7.3.5" -node-abort-controller@^3.0.1, node-abort-controller@^3.1.1: +node-abort-controller@^3.0.1: version "3.1.1" resolved "https://registry.yarnpkg.com/node-abort-controller/-/node-abort-controller-3.1.1.tgz#a94377e964a9a37ac3976d848cb5c765833b8548" integrity sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ== @@ -16100,13 +15964,27 @@ node-domexception@1.0.0: resolved "https://registry.yarnpkg.com/node-domexception/-/node-domexception-1.0.0.tgz#6888db46a1f71c0b76b3f7555016b63fe64766e5" integrity sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ== -node-fetch@2.6.7, node-fetch@2.6.9, 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.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: + version "2.6.9" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.9.tgz#7c7f744b5cc6eb5fd404e0c7a9fec630a55657e6" + integrity sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg== + 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" @@ -17204,7 +17082,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== @@ -17259,14 +17145,7 @@ path-scurry@^1.11.1, path-scurry@^1.6.1: lru-cache "^10.2.0" minipass "^5.0.0 || ^6.0.2 || ^7.0.0" -path-to-regexp@1.x: - version "1.9.0" - resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.9.0.tgz#5dc0753acbf8521ca2e0f137b4578b917b10cf24" - integrity sha512-xIp7/apCFJuUHdDLWe8O1HIkb0kQrOMb/0u6FXQjemHn/ii5LrIzU6bdECnsiTF/GjZkMEKg1xdiZwNqDYlZ6g== - dependencies: - isarray "0.0.1" - -path-to-regexp@^0.1.10, path-to-regexp@^0.1.2: +path-to-regexp@^0.1.10: version "0.1.11" resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.11.tgz#a527e662c89efc4646dbfa8100bf3e847e495761" integrity sha512-c0t+KCuUkO/YDLPG4WWzEwx3J5F/GHXsD1h/SNZfySqAIKe/BaP95x8fWtOfRJokpS5yYHRJjMtYlXD8jxnpbw== @@ -18193,7 +18072,7 @@ pouchdb@9.0.0: uuid "8.3.2" vuvuzela "1.0.3" -pprof-format@^2.0.7, pprof-format@^2.1.0: +pprof-format@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/pprof-format/-/pprof-format-2.1.0.tgz#acc8d7773bcf4faf0a3d3df11bceefba7ac06664" integrity sha512-0+G5bHH0RNr8E5hoZo/zJYsL92MhkZjwrHp3O2IxmY8RJL9ooKeuZ8Tm0ZNBw5sGZ9TiM71sthTjWoR2Vf5/xw== @@ -18476,6 +18355,13 @@ 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: + version "1.11.0" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.11.0.tgz#56fed2560dcb74a9c374f8e6f6596b328b26699c" + integrity sha512-pjFdcBXT4g061k/SQkzNCRnav+1RdIOgrcX8hs5eL3CEQcFZP9qT8T1RWYxGKT11rH1DdIW+kJRfCYykBJuerQ== + dependencies: + punycode "^2.3.1" + psl@^1.1.33: version "1.9.0" resolved "https://registry.yarnpkg.com/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7" @@ -18521,6 +18407,11 @@ punycode@^2.1.0, punycode@^2.1.1, punycode@^2.3.0: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f" integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA== +punycode@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" + integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== + pupa@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/pupa/-/pupa-2.1.1.tgz#f5e8fd4afc2c5d97828faa523549ed8744a20d62" @@ -19479,6 +19370,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.4.1" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.4.1.tgz#44cc8988377f126304d3b3fc1010c733b929ef0f" + integrity sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg== + sax@>=0.6.0: version "1.2.4" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" @@ -19551,13 +19447,33 @@ 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.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@^7.6.0, semver@~2.3.1: +"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.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, semver@^7.6.0: + version "7.6.3" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143" + integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== + +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== + seq-queue@^0.0.5: version "0.0.5" resolved "https://registry.yarnpkg.com/seq-queue/-/seq-queue-0.0.5.tgz#d56812e1c017a6e4e7c3e3a37a1da6d78dd3c93e" @@ -20241,7 +20157,16 @@ string-range@~1.2, string-range@~1.2.1: resolved "https://registry.yarnpkg.com/string-range/-/string-range-1.2.2.tgz#a893ed347e72299bc83befbbf2a692a8d239d5dd" integrity sha512-tYft6IFi8SjplJpxCUxyqisD3b+R2CSkomrtJYCkvuf1KuCAWgz7YXt4O0jip7efpfCemwHEzTEAO8EuOYgh3w== -"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0": + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + 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-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -20333,7 +20258,7 @@ stringify-object@^3.2.1: is-obj "^1.0.1" is-regexp "^1.0.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -20347,6 +20272,13 @@ strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: dependencies: ansi-regex "^4.1.0" +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + strip-ansi@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.0.1.tgz#61740a08ce36b61e50e65653f07060d000975fb2" @@ -21085,7 +21017,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== @@ -21095,6 +21027,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" @@ -21570,14 +21510,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" @@ -21628,11 +21560,6 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" -urijs@^1.19.2: - version "1.19.11" - resolved "https://registry.yarnpkg.com/urijs/-/urijs-1.19.11.tgz#204b0d6b605ae80bea54bea39280cdb7c9f923cc" - integrity sha512-HXgFDgDommxn5/bIv0cnQZsPhHDA90NPHD6+c/v21U5+Sx5hoP8+dP9IZXBU1gIfvdRfhG8cel9QNPeionfcCQ== - url-parse-lax@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-3.0.0.tgz#16b5cafc07dbe3676c1b1999177823d6503acb0c" @@ -22137,7 +22064,7 @@ worker-farm@1.7.0: dependencies: errno "~0.1.7" -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -22155,6 +22082,15 @@ wrap-ansi@^5.1.0: string-width "^3.0.0" strip-ansi "^5.0.0" +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" @@ -22282,7 +22218,14 @@ 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.6.2, xml2js@^0.5.0: +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.6.2: version "0.6.2" resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.6.2.tgz#dd0b630083aa09c161e25a4d0901e2b2a929b499" integrity sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA== @@ -22290,6 +22233,14 @@ xml2js@0.1.x, xml2js@0.6.2, xml2js@^0.5.0: sax ">=0.6.0" xmlbuilder "~11.0.0" +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" + xmlbuilder@~11.0.0: version "11.0.1" resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz#be9bae1c8a046e76b31127726347d0ad7002beb3" From 37eb6f296731fe683ed733dd73ebcca5f31b663e Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Thu, 21 Nov 2024 13:13:50 +0000 Subject: [PATCH 17/31] Updating pro. --- packages/pro | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/pro b/packages/pro index 4facf6a44e..3b56ed03a5 160000 --- a/packages/pro +++ b/packages/pro @@ -1 +1 @@ -Subproject commit 4facf6a44ee52a405794845f71584168b9db652c +Subproject commit 3b56ed03a562b7caa8da8962243efe9050b78e9d From 34e792a8f325516d1e419b4afcb05227a4f193fa Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Thu, 21 Nov 2024 13:13:57 +0000 Subject: [PATCH 18/31] Updating yarn.lock. --- yarn.lock | 232 +++++++++++++++++++++++++----------------------------- 1 file changed, 106 insertions(+), 126 deletions(-) diff --git a/yarn.lock b/yarn.lock index 1f74b624ae..cb98346bfa 100644 --- a/yarn.lock +++ b/yarn.lock @@ -842,7 +842,7 @@ "@azure/abort-controller" "^2.0.0" tslib "^2.6.2" -"@azure/identity@^4.2.1": +"@azure/identity@4.2.1", "@azure/identity@^4.2.1": version "4.2.1" resolved "https://registry.yarnpkg.com/@azure/identity/-/identity-4.2.1.tgz#22b366201e989b7b41c0e1690e103bd579c31e4c" integrity sha512-U8hsyC9YPcEIzoaObJlRDvp7KiF0MGS7xcWbyJSVvXRkC/HXo1f0oYeBYmEvVgRfacw7GHf6D6yAoh9JHz6A5Q== @@ -2196,6 +2196,47 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== +"@budibase/backend-core@3.2.11": + version "0.0.0" + dependencies: + "@budibase/nano" "10.1.5" + "@budibase/pouchdb-replication-stream" "1.2.11" + "@budibase/shared-core" "0.0.0" + "@budibase/types" "0.0.0" + "@techpass/passport-openidconnect" "0.3.3" + aws-cloudfront-sign "3.0.2" + aws-sdk "2.1692.0" + bcrypt "5.1.0" + bcryptjs "2.4.3" + bull "4.10.1" + correlation-id "4.0.0" + dd-trace "5.23.0" + dotenv "16.0.1" + google-auth-library "^8.0.1" + google-spreadsheet "npm:@budibase/google-spreadsheet@4.1.5" + ioredis "5.3.2" + joi "17.6.0" + jsonwebtoken "9.0.2" + knex "2.4.2" + koa-passport "^6.0.0" + koa-pino-logger "4.0.0" + lodash "4.17.21" + node-fetch "2.6.7" + passport-google-oauth "2.0.0" + passport-local "1.0.0" + passport-oauth2-refresh "^2.1.0" + pino "8.11.0" + pino-http "8.3.3" + posthog-node "4.0.1" + pouchdb "9.0.0" + pouchdb-find "9.0.0" + redlock "4.2.0" + rotating-file-stream "3.1.0" + sanitize-s3-objectkey "0.0.1" + semver "^7.5.4" + tar-fs "2.1.1" + uuid "^8.3.2" + "@budibase/handlebars-helpers@^0.13.2": version "0.13.2" resolved "https://registry.yarnpkg.com/@budibase/handlebars-helpers/-/handlebars-helpers-0.13.2.tgz#73ab51c464e91fd955b429017648e0257060db77" @@ -2238,6 +2279,47 @@ pouchdb-promise "^6.0.4" through2 "^2.0.0" +"@budibase/pro@npm:@budibase/pro@latest": + version "3.2.11" + resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-3.2.11.tgz#40d4929b3958dacca3f4c207718a4647c08a6100" + integrity sha512-xE1tx/C2cnbyR4s/6XkkweoQw6CW5fsQt++gzrrML8abgsODru+tA7M2NbWlKsEnVWHDQRvUVcXm4wqDeNNZ9g== + dependencies: + "@anthropic-ai/sdk" "^0.27.3" + "@budibase/backend-core" "3.2.11" + "@budibase/shared-core" "3.2.11" + "@budibase/string-templates" "3.2.11" + "@budibase/types" "3.2.11" + "@koa/router" "13.1.0" + bull "4.10.1" + dd-trace "5.23.0" + joi "17.6.0" + jsonwebtoken "9.0.2" + lru-cache "^7.14.1" + memorystream "^0.3.1" + node-fetch "2.6.7" + openai "4.59.0" + scim-patch "^0.8.1" + scim2-parse-filter "^0.2.8" + +"@budibase/shared-core@3.2.11": + version "0.0.0" + dependencies: + "@budibase/types" "0.0.0" + cron-validate "1.4.5" + +"@budibase/string-templates@3.2.11": + version "0.0.0" + dependencies: + "@budibase/handlebars-helpers" "^0.13.2" + dayjs "^1.10.8" + handlebars "^4.7.8" + lodash.clonedeep "^4.5.0" + +"@budibase/types@3.2.11": + version "0.0.0" + dependencies: + scim-patch "^0.8.1" + "@bull-board/api@5.10.2": version "5.10.2" resolved "https://registry.yarnpkg.com/@bull-board/api/-/api-5.10.2.tgz#ae8ff6918b23897bf879a6ead3683f964374c4b3" @@ -7227,23 +7309,7 @@ aws4@^1.8.0: resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.13.2.tgz#0aa167216965ac9474ccfa83892cfb6b3e1e52ef" integrity sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw== -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: - 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@^1.0.0, axios@^1.1.3, axios@^1.4.0, axios@^1.6.2, axios@^1.6.8: +axios@1.1.3, axios@1.7.7, axios@^0.21.1, axios@^1.0.0, axios@^1.1.3, axios@^1.4.0, axios@^1.6.2, axios@^1.6.8: version "1.7.7" resolved "https://registry.yarnpkg.com/axios/-/axios-1.7.7.tgz#2f554296f9892a72ac8d8e4c5b79c14a91d0a47f" integrity sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q== @@ -11215,7 +11281,7 @@ 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.15.0, follow-redirects@^1.15.6: +follow-redirects@^1.15.6: version "1.15.9" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.9.tgz#a604fa10e443bf98ca94228d9eebcc2e8a2c8ee1" integrity sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ== @@ -12333,12 +12399,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== @@ -12833,11 +12894,6 @@ is-boolean-object@^1.1.0: call-bind "^1.0.2" has-tostringtag "^1.0.0" -is-buffer@^1.1.5: - version "1.1.6" - resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" - integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== - is-buffer@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.5.tgz#ebc252e400d22ff8d77fa09888821a24a658c191" @@ -13299,6 +13355,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" @@ -14190,14 +14251,7 @@ kill-port@^1.6.1: get-them-args "1.3.2" shell-exec "1.0.2" -kind-of@^3.0.2, kind-of@^3.1.0: - version "3.2.2" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" - integrity sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ== - dependencies: - is-buffer "^1.1.5" - -kind-of@^6.0.0, kind-of@^6.0.2, kind-of@^6.0.3: +kind-of@6.0.3, kind-of@^3.0.2, kind-of@^3.1.0, kind-of@^6.0.0, kind-of@^6.0.2, kind-of@^6.0.3: version "6.0.3" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== @@ -15766,7 +15820,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== @@ -15964,27 +16018,13 @@ node-domexception@1.0.0: resolved "https://registry.yarnpkg.com/node-domexception/-/node-domexception-1.0.0.tgz#6888db46a1f71c0b76b3f7555016b63fe64766e5" integrity sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ== -node-fetch@2.6.7, node-fetch@^2.6.0, node-fetch@^2.6.1, node-fetch@^2.6.7: +node-fetch@2.6.7, node-fetch@2.6.9, 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: - version "2.6.9" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.9.tgz#7c7f744b5cc6eb5fd404e0c7a9fec630a55657e6" - integrity sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg== - 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" @@ -17082,15 +17122,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== @@ -18355,13 +18387,6 @@ 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: - version "1.11.0" - resolved "https://registry.yarnpkg.com/psl/-/psl-1.11.0.tgz#56fed2560dcb74a9c374f8e6f6596b328b26699c" - integrity sha512-pjFdcBXT4g061k/SQkzNCRnav+1RdIOgrcX8hs5eL3CEQcFZP9qT8T1RWYxGKT11rH1DdIW+kJRfCYykBJuerQ== - dependencies: - punycode "^2.3.1" - psl@^1.1.33: version "1.9.0" resolved "https://registry.yarnpkg.com/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7" @@ -18407,11 +18432,6 @@ punycode@^2.1.0, punycode@^2.1.1, punycode@^2.3.0: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f" integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA== -punycode@^2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" - integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== - pupa@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/pupa/-/pupa-2.1.1.tgz#f5e8fd4afc2c5d97828faa523549ed8744a20d62" @@ -19370,11 +19390,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.4.1" - resolved "https://registry.yarnpkg.com/sax/-/sax-1.4.1.tgz#44cc8988377f126304d3b3fc1010c733b929ef0f" - integrity sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg== - sax@>=0.6.0: version "1.2.4" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" @@ -19447,33 +19462,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.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.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@^7.6.0, semver@~2.3.1: 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, semver@^7.6.0: - version "7.6.3" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143" - integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== - -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== - seq-queue@^0.0.5: version "0.0.5" resolved "https://registry.yarnpkg.com/seq-queue/-/seq-queue-0.0.5.tgz#d56812e1c017a6e4e7c3e3a37a1da6d78dd3c93e" @@ -21017,7 +21012,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== @@ -21027,14 +21022,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" @@ -21510,6 +21497,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" @@ -22218,14 +22213,7 @@ 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.6.2: +xml2js@0.1.x, xml2js@0.6.2, xml2js@^0.5.0: version "0.6.2" resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.6.2.tgz#dd0b630083aa09c161e25a4d0901e2b2a929b499" integrity sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA== @@ -22233,14 +22221,6 @@ xml2js@0.6.2: sax ">=0.6.0" xmlbuilder "~11.0.0" -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" - xmlbuilder@~11.0.0: version "11.0.1" resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz#be9bae1c8a046e76b31127726347d0ad7002beb3" From 2f6455b8076d1b084e3f77ecaf096edaa19448ac Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Thu, 21 Nov 2024 14:24:47 +0000 Subject: [PATCH 19/31] Yarn lock changes. --- yarn.lock | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/yarn.lock b/yarn.lock index cb98346bfa..0eb1759cdb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2279,7 +2279,7 @@ pouchdb-promise "^6.0.4" through2 "^2.0.0" -"@budibase/pro@npm:@budibase/pro@latest": +"@budibase/pro@0.0.0", "@budibase/pro@npm:@budibase/pro@latest": version "3.2.11" resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-3.2.11.tgz#40d4929b3958dacca3f4c207718a4647c08a6100" integrity sha512-xE1tx/C2cnbyR4s/6XkkweoQw6CW5fsQt++gzrrML8abgsODru+tA7M2NbWlKsEnVWHDQRvUVcXm4wqDeNNZ9g== @@ -4024,7 +4024,7 @@ dependencies: slash "^4.0.0" -"@rollup/plugin-commonjs@16.0.0", "@rollup/plugin-commonjs@^16.0.0": +"@rollup/plugin-commonjs@^16.0.0": version "16.0.0" resolved "https://registry.yarnpkg.com/@rollup/plugin-commonjs/-/plugin-commonjs-16.0.0.tgz#169004d56cd0f0a1d0f35915d31a036b0efe281f" integrity sha512-LuNyypCP3msCGVQJ7ki8PqYdpjfEkE/xtFa5DqlF+7IBD0JsfMZ87C58heSwIMint58sAUZbt3ITqOmdQv/dXw== @@ -4110,7 +4110,7 @@ is-module "^1.0.0" resolve "^1.22.1" -"@rollup/plugin-replace@^5.0.2", "@rollup/plugin-replace@^5.0.3": +"@rollup/plugin-replace@^5.0.3": version "5.0.7" resolved "https://registry.yarnpkg.com/@rollup/plugin-replace/-/plugin-replace-5.0.7.tgz#150c9ee9db8031d9e4580a61a0edeaaed3d37687" integrity sha512-PqxSfuorkHz/SPpyngLyg5GCEkOcee9M1bkxiVDr41Pd61mqP1PLOoDPbpl44SB2mQGKwV/In74gqQmGITOhEQ== @@ -12511,11 +12511,6 @@ husky@^8.0.3: resolved "https://registry.yarnpkg.com/husky/-/husky-8.0.3.tgz#4936d7212e46d1dea28fef29bb3a108872cd9184" integrity sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg== -husky@^9.1.4: - version "9.1.7" - resolved "https://registry.yarnpkg.com/husky/-/husky-9.1.7.tgz#d46a38035d101b46a70456a850ff4201344c0b2d" - integrity sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA== - ical-generator@4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/ical-generator/-/ical-generator-4.1.0.tgz#2a336c951864c5583a2aa715d16f2edcdfd2d90b" @@ -20883,11 +20878,6 @@ timekeeper@2.2.0: resolved "https://registry.yarnpkg.com/timekeeper/-/timekeeper-2.2.0.tgz#9645731fce9e3280a18614a57a9d1b72af3ca368" integrity sha512-W3AmPTJWZkRwu+iSNxPIsLZ2ByADsOLbbLxe46UJyWj3mlYLlwucKiq+/dPm0l9wTzqoF3/2PH0AGFCebjq23A== -timekeeper@^2.2.0: - version "2.3.1" - resolved "https://registry.yarnpkg.com/timekeeper/-/timekeeper-2.3.1.tgz#2deb6e0b95d93625fda84c18d47f84a99e4eba01" - integrity sha512-LeQRS7/4JcC0PgdSFnfUiStQEdiuySlCj/5SJ18D+T1n9BoY7PxKFfCwLulpHXoLUFr67HxBddQdEX47lDGx1g== - tiny-glob@^0.2.9: version "0.2.9" resolved "https://registry.yarnpkg.com/tiny-glob/-/tiny-glob-0.2.9.tgz#2212d441ac17928033b110f8b3640683129d31e2" @@ -21120,7 +21110,7 @@ tsconfig-paths@^3.10.1, tsconfig-paths@^3.15.0: minimist "^1.2.6" strip-bom "^3.0.0" -tsconfig-paths@^4.1.2, tsconfig-paths@^4.2.0: +tsconfig-paths@^4.1.2: version "4.2.0" resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz#ef78e19039133446d244beac0fd6a1632e2d107c" integrity sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg== From 28be70c23fbfaa4316c3fb2614d8a601f5be3999 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Thu, 21 Nov 2024 14:35:32 +0000 Subject: [PATCH 20/31] Updating yarn.lock. --- yarn.lock | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/yarn.lock b/yarn.lock index 0eb1759cdb..cb98346bfa 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2279,7 +2279,7 @@ pouchdb-promise "^6.0.4" through2 "^2.0.0" -"@budibase/pro@0.0.0", "@budibase/pro@npm:@budibase/pro@latest": +"@budibase/pro@npm:@budibase/pro@latest": version "3.2.11" resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-3.2.11.tgz#40d4929b3958dacca3f4c207718a4647c08a6100" integrity sha512-xE1tx/C2cnbyR4s/6XkkweoQw6CW5fsQt++gzrrML8abgsODru+tA7M2NbWlKsEnVWHDQRvUVcXm4wqDeNNZ9g== @@ -4024,7 +4024,7 @@ dependencies: slash "^4.0.0" -"@rollup/plugin-commonjs@^16.0.0": +"@rollup/plugin-commonjs@16.0.0", "@rollup/plugin-commonjs@^16.0.0": version "16.0.0" resolved "https://registry.yarnpkg.com/@rollup/plugin-commonjs/-/plugin-commonjs-16.0.0.tgz#169004d56cd0f0a1d0f35915d31a036b0efe281f" integrity sha512-LuNyypCP3msCGVQJ7ki8PqYdpjfEkE/xtFa5DqlF+7IBD0JsfMZ87C58heSwIMint58sAUZbt3ITqOmdQv/dXw== @@ -4110,7 +4110,7 @@ is-module "^1.0.0" resolve "^1.22.1" -"@rollup/plugin-replace@^5.0.3": +"@rollup/plugin-replace@^5.0.2", "@rollup/plugin-replace@^5.0.3": version "5.0.7" resolved "https://registry.yarnpkg.com/@rollup/plugin-replace/-/plugin-replace-5.0.7.tgz#150c9ee9db8031d9e4580a61a0edeaaed3d37687" integrity sha512-PqxSfuorkHz/SPpyngLyg5GCEkOcee9M1bkxiVDr41Pd61mqP1PLOoDPbpl44SB2mQGKwV/In74gqQmGITOhEQ== @@ -12511,6 +12511,11 @@ husky@^8.0.3: resolved "https://registry.yarnpkg.com/husky/-/husky-8.0.3.tgz#4936d7212e46d1dea28fef29bb3a108872cd9184" integrity sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg== +husky@^9.1.4: + version "9.1.7" + resolved "https://registry.yarnpkg.com/husky/-/husky-9.1.7.tgz#d46a38035d101b46a70456a850ff4201344c0b2d" + integrity sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA== + ical-generator@4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/ical-generator/-/ical-generator-4.1.0.tgz#2a336c951864c5583a2aa715d16f2edcdfd2d90b" @@ -20878,6 +20883,11 @@ timekeeper@2.2.0: resolved "https://registry.yarnpkg.com/timekeeper/-/timekeeper-2.2.0.tgz#9645731fce9e3280a18614a57a9d1b72af3ca368" integrity sha512-W3AmPTJWZkRwu+iSNxPIsLZ2ByADsOLbbLxe46UJyWj3mlYLlwucKiq+/dPm0l9wTzqoF3/2PH0AGFCebjq23A== +timekeeper@^2.2.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/timekeeper/-/timekeeper-2.3.1.tgz#2deb6e0b95d93625fda84c18d47f84a99e4eba01" + integrity sha512-LeQRS7/4JcC0PgdSFnfUiStQEdiuySlCj/5SJ18D+T1n9BoY7PxKFfCwLulpHXoLUFr67HxBddQdEX47lDGx1g== + tiny-glob@^0.2.9: version "0.2.9" resolved "https://registry.yarnpkg.com/tiny-glob/-/tiny-glob-0.2.9.tgz#2212d441ac17928033b110f8b3640683129d31e2" @@ -21110,7 +21120,7 @@ tsconfig-paths@^3.10.1, tsconfig-paths@^3.15.0: minimist "^1.2.6" strip-bom "^3.0.0" -tsconfig-paths@^4.1.2: +tsconfig-paths@^4.1.2, tsconfig-paths@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz#ef78e19039133446d244beac0fd6a1632e2d107c" integrity sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg== From 2e0bdc919a582689a41dbd3dff7aa67658bcd5e7 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Thu, 21 Nov 2024 14:53:39 +0000 Subject: [PATCH 21/31] Clean repo, yarn.lock with submodules. --- yarn.lock | 5 ----- 1 file changed, 5 deletions(-) diff --git a/yarn.lock b/yarn.lock index cb98346bfa..506cea0f03 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12511,11 +12511,6 @@ husky@^8.0.3: resolved "https://registry.yarnpkg.com/husky/-/husky-8.0.3.tgz#4936d7212e46d1dea28fef29bb3a108872cd9184" integrity sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg== -husky@^9.1.4: - version "9.1.7" - resolved "https://registry.yarnpkg.com/husky/-/husky-9.1.7.tgz#d46a38035d101b46a70456a850ff4201344c0b2d" - integrity sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA== - ical-generator@4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/ical-generator/-/ical-generator-4.1.0.tgz#2a336c951864c5583a2aa715d16f2edcdfd2d90b" From 4e2f0405db7c2f4bb38507e82fdbf50925ce94db Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Thu, 21 Nov 2024 16:36:31 +0000 Subject: [PATCH 22/31] Additional instrumentation for DD around CouchDB. --- .../backend-core/src/db/couch/DatabaseImpl.ts | 4 +- .../backend-core/src/db/instrumentation.ts | 127 ++++++++++++------ packages/types/src/documents/pouch.ts | 2 +- packages/types/src/sdk/db.ts | 4 +- 4 files changed, 92 insertions(+), 45 deletions(-) diff --git a/packages/backend-core/src/db/couch/DatabaseImpl.ts b/packages/backend-core/src/db/couch/DatabaseImpl.ts index 83b9b69d0b..371f3dc997 100644 --- a/packages/backend-core/src/db/couch/DatabaseImpl.ts +++ b/packages/backend-core/src/db/couch/DatabaseImpl.ts @@ -190,7 +190,7 @@ export class DatabaseImpl implements Database { } } - private async performCall(call: DBCallback): Promise { + private async performCall(call: DBCallback): Promise { const db = this.getDb() const fnc = await call(db) try { @@ -467,7 +467,7 @@ export class DatabaseImpl implements Database { } catch (err: any) { // didn't exist, don't worry if (err.statusCode === 404) { - return + return { ok: true } } else { throw new CouchDBError(err.message, err) } diff --git a/packages/backend-core/src/db/instrumentation.ts b/packages/backend-core/src/db/instrumentation.ts index e08bfc0362..4c12e20443 100644 --- a/packages/backend-core/src/db/instrumentation.ts +++ b/packages/backend-core/src/db/instrumentation.ts @@ -27,7 +27,7 @@ export class DDInstrumentedDatabase implements Database { exists(docId?: string): Promise { return tracer.trace("db.exists", span => { - span?.addTags({ db_name: this.name, doc_id: docId }) + span.addTags({ db_name: this.name, doc_id: docId }) if (docId) { return this.db.exists(docId) } @@ -37,15 +37,17 @@ export class DDInstrumentedDatabase implements Database { get(id?: string | undefined): Promise { return tracer.trace("db.get", span => { - span?.addTags({ db_name: this.name, doc_id: id }) + span.addTags({ db_name: this.name, doc_id: id }) return this.db.get(id) }) } tryGet(id?: string | undefined): Promise { - return tracer.trace("db.tryGet", span => { - span?.addTags({ db_name: this.name, doc_id: id }) - return this.db.tryGet(id) + return tracer.trace("db.tryGet", async span => { + span.addTags({ db_name: this.name, doc_id: id }) + const doc = await this.db.tryGet(id) + span.addTags({ doc_found: doc !== undefined }) + return doc }) } @@ -53,13 +55,15 @@ export class DDInstrumentedDatabase implements Database { ids: string[], opts?: { allowMissing?: boolean | undefined } | undefined ): Promise { - return tracer.trace("db.getMultiple", span => { - span?.addTags({ + return tracer.trace("db.getMultiple", async span => { + span.addTags({ db_name: this.name, num_docs: ids.length, allow_missing: opts?.allowMissing, }) - return this.db.getMultiple(ids, opts) + const docs = await this.db.getMultiple(ids, opts) + span.addTags({ num_docs_found: docs.length }) + return docs }) } @@ -69,12 +73,14 @@ export class DDInstrumentedDatabase implements Database { idOrDoc: string | Document, rev?: string ): Promise { - return tracer.trace("db.remove", span => { - span?.addTags({ db_name: this.name, doc_id: idOrDoc }) + return tracer.trace("db.remove", async span => { + span.addTags({ db_name: this.name, doc_id: idOrDoc, rev }) const isDocument = typeof idOrDoc === "object" const id = isDocument ? idOrDoc._id! : idOrDoc rev = isDocument ? idOrDoc._rev : rev - return this.db.remove(id, rev) + const resp = await this.db.remove(id, rev) + span.addTags({ ok: resp.ok }) + return resp }) } @@ -83,7 +89,11 @@ export class DDInstrumentedDatabase implements Database { opts?: { silenceErrors?: boolean } ): Promise { return tracer.trace("db.bulkRemove", span => { - span?.addTags({ db_name: this.name, num_docs: documents.length }) + span.addTags({ + db_name: this.name, + num_docs: documents.length, + silence_errors: opts?.silenceErrors, + }) return this.db.bulkRemove(documents, opts) }) } @@ -92,15 +102,21 @@ export class DDInstrumentedDatabase implements Database { document: AnyDocument, opts?: DatabasePutOpts | undefined ): Promise { - return tracer.trace("db.put", span => { - span?.addTags({ db_name: this.name, doc_id: document._id }) - return this.db.put(document, opts) + return tracer.trace("db.put", async span => { + span.addTags({ + db_name: this.name, + doc_id: document._id, + force: opts?.force, + }) + const resp = await this.db.put(document, opts) + span.addTags({ ok: resp.ok }) + return resp }) } bulkDocs(documents: AnyDocument[]): Promise { return tracer.trace("db.bulkDocs", span => { - span?.addTags({ db_name: this.name, num_docs: documents.length }) + span.addTags({ db_name: this.name, num_docs: documents.length }) return this.db.bulkDocs(documents) }) } @@ -108,9 +124,15 @@ export class DDInstrumentedDatabase implements Database { allDocs( params: DatabaseQueryOpts ): Promise> { - return tracer.trace("db.allDocs", span => { - span?.addTags({ db_name: this.name }) - return this.db.allDocs(params) + return tracer.trace("db.allDocs", async span => { + span.addTags({ db_name: this.name, ...params }) + const resp = await this.db.allDocs(params) + span.addTags({ + total_rows: resp.total_rows, + rows_length: resp.rows.length, + offset: resp.offset, + }) + return resp }) } @@ -118,57 +140,77 @@ export class DDInstrumentedDatabase implements Database { viewName: string, params: DatabaseQueryOpts ): Promise> { - return tracer.trace("db.query", span => { - span?.addTags({ db_name: this.name, view_name: viewName }) - return this.db.query(viewName, params) + return tracer.trace("db.query", async span => { + span.addTags({ db_name: this.name, view_name: viewName, ...params }) + const resp = await this.db.query(viewName, params) + span.addTags({ + total_rows: resp.total_rows, + rows_length: resp.rows.length, + offset: resp.offset, + }) + return resp }) } - destroy(): Promise { - return tracer.trace("db.destroy", span => { - span?.addTags({ db_name: this.name }) - return this.db.destroy() + destroy(): Promise { + return tracer.trace("db.destroy", async span => { + span.addTags({ db_name: this.name }) + const resp = await this.db.destroy() + span.addTags({ ok: resp.ok }) + return resp }) } - compact(): Promise { - return tracer.trace("db.compact", span => { - span?.addTags({ db_name: this.name }) - return this.db.compact() + compact(): Promise { + return tracer.trace("db.compact", async span => { + span.addTags({ db_name: this.name }) + const resp = await this.db.compact() + span.addTags({ ok: resp.ok }) + return resp }) } dump(stream: Writable, opts?: DatabaseDumpOpts | undefined): Promise { return tracer.trace("db.dump", span => { - span?.addTags({ db_name: this.name }) + span.addTags({ + db_name: this.name, + batch_limit: opts?.batch_limit, + batch_size: opts?.batch_size, + style: opts?.style, + timeout: opts?.timeout, + num_doc_ids: opts?.doc_ids?.length, + query_params: opts?.query_params, + view: opts?.view, + selector: opts?.selector, + }) return this.db.dump(stream, opts) }) } load(...args: any[]): Promise { return tracer.trace("db.load", span => { - span?.addTags({ db_name: this.name }) + span.addTags({ db_name: this.name, num_args: args.length }) return this.db.load(...args) }) } createIndex(...args: any[]): Promise { return tracer.trace("db.createIndex", span => { - span?.addTags({ db_name: this.name }) + span.addTags({ db_name: this.name, num_args: args.length }) return this.db.createIndex(...args) }) } deleteIndex(...args: any[]): Promise { return tracer.trace("db.deleteIndex", span => { - span?.addTags({ db_name: this.name }) + span.addTags({ db_name: this.name, num_args: args.length }) return this.db.deleteIndex(...args) }) } getIndexes(...args: any[]): Promise { return tracer.trace("db.getIndexes", span => { - span?.addTags({ db_name: this.name }) + span.addTags({ db_name: this.name, num_args: args.length }) return this.db.getIndexes(...args) }) } @@ -177,22 +219,27 @@ export class DDInstrumentedDatabase implements Database { sql: string, parameters?: SqlQueryBinding ): Promise { - return tracer.trace("db.sql", span => { - span?.addTags({ db_name: this.name }) - return this.db.sql(sql, parameters) + return tracer.trace("db.sql", async span => { + span.addTags({ db_name: this.name, num_bindings: parameters?.length }) + const resp = await this.db.sql(sql, parameters) + span.addTags({ num_rows: resp.length }) + return resp }) } sqlPurgeDocument(docIds: string[] | string): Promise { return tracer.trace("db.sqlPurgeDocument", span => { - span?.addTags({ db_name: this.name }) + span.addTags({ + db_name: this.name, + num_docs: Array.isArray(docIds) ? docIds.length : 1, + }) return this.db.sqlPurgeDocument(docIds) }) } sqlDiskCleanup(): Promise { return tracer.trace("db.sqlDiskCleanup", span => { - span?.addTags({ db_name: this.name }) + span.addTags({ db_name: this.name }) return this.db.sqlDiskCleanup() }) } diff --git a/packages/types/src/documents/pouch.ts b/packages/types/src/documents/pouch.ts index 6ff851a515..c2ac1599ee 100644 --- a/packages/types/src/documents/pouch.ts +++ b/packages/types/src/documents/pouch.ts @@ -8,7 +8,7 @@ export interface RowValue { export interface RowResponse { id: string key: string - error: string + error?: string value: T doc?: T } diff --git a/packages/types/src/sdk/db.ts b/packages/types/src/sdk/db.ts index 9797715329..9feecbdb2b 100644 --- a/packages/types/src/sdk/db.ts +++ b/packages/types/src/sdk/db.ts @@ -163,8 +163,8 @@ export interface Database { viewName: string, params: DatabaseQueryOpts ): Promise> - destroy(): Promise - compact(): Promise + destroy(): Promise + compact(): Promise // these are all PouchDB related functions that are rarely used - in future // should be replaced by better typed/non-pouch implemented methods dump(stream: Writable, opts?: DatabaseDumpOpts): Promise From 14bbd9a21cd87bb1bc123be3f608b16c15c1c95e Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Fri, 22 Nov 2024 14:51:53 +0000 Subject: [PATCH 23/31] Setting CORS headers to allow cross-origin on public API. --- packages/server/package.json | 8 +- .../server/src/api/routes/public/index.ts | 2 + .../src/api/routes/public/tests/Request.ts | 14 + .../src/api/routes/public/tests/cors.spec.ts | 21 ++ yarn.lock | 246 ++++++++++-------- 5 files changed, 182 insertions(+), 109 deletions(-) create mode 100644 packages/server/src/api/routes/public/tests/cors.spec.ts diff --git a/packages/server/package.json b/packages/server/package.json index 4e192ec286..bf13be59bf 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -63,6 +63,7 @@ "@bull-board/koa": "5.10.2", "@elastic/elasticsearch": "7.10.0", "@google-cloud/firestore": "7.8.0", + "@koa/cors": "5.0.0", "@koa/router": "13.1.0", "@socket.io/redis-adapter": "^8.2.1", "@types/xml2js": "^0.4.14", @@ -80,8 +81,8 @@ "cookies": "0.8.0", "csvtojson": "2.0.10", "curlconverter": "3.21.0", - "dd-trace": "5.23.0", "dayjs": "^1.10.8", + "dd-trace": "5.23.0", "dotenv": "8.2.0", "form-data": "4.0.0", "global-agent": "3.0.0", @@ -131,6 +132,7 @@ "xml2js": "0.6.2" }, "devDependencies": { + "@babel/core": "^7.22.5", "@babel/preset-env": "7.16.11", "@jest/types": "^29.6.3", "@swc/core": "1.3.71", @@ -140,6 +142,7 @@ "@types/jest": "29.5.5", "@types/koa": "2.13.4", "@types/koa-send": "^4.1.6", + "@types/koa__cors": "5.0.0", "@types/koa__router": "12.0.4", "@types/lodash": "4.14.200", "@types/mssql": "9.1.5", @@ -172,8 +175,7 @@ "tsconfig-paths": "4.0.0", "typescript": "5.5.2", "update-dotenv": "1.1.1", - "yargs": "13.2.4", - "@babel/core": "^7.22.5" + "yargs": "13.2.4" }, "nx": { "targets": { diff --git a/packages/server/src/api/routes/public/index.ts b/packages/server/src/api/routes/public/index.ts index bcec8f3166..531192811c 100644 --- a/packages/server/src/api/routes/public/index.ts +++ b/packages/server/src/api/routes/public/index.ts @@ -15,6 +15,7 @@ import mapperMiddleware from "./middleware/mapper" import env from "../../../environment" import { middleware, redis } from "@budibase/backend-core" import { SelectableDatabase } from "@budibase/backend-core/src/redis/utils" +import cors from "@koa/cors" // below imports don't have declaration files const Router = require("@koa/router") const { RateLimit, Stores } = require("koa2-ratelimit") @@ -82,6 +83,7 @@ const publicRouter = new Router({ if (limiter && !env.isDev()) { publicRouter.use(limiter) } +publicRouter.use(cors()) function addMiddleware( endpoints: any, diff --git a/packages/server/src/api/routes/public/tests/Request.ts b/packages/server/src/api/routes/public/tests/Request.ts index 463d56f141..3dfa11c0b3 100644 --- a/packages/server/src/api/routes/public/tests/Request.ts +++ b/packages/server/src/api/routes/public/tests/Request.ts @@ -18,6 +18,7 @@ type Response = { data: T } export interface PublicAPIExpectations { status?: number body?: Record + headers?: Record } export class PublicAPIRequest { @@ -71,6 +72,12 @@ export class PublicAPIRequest { if (expectations?.body) { expect(res.body).toEqual(expectations?.body) } + if (expectations?.headers) { + for (let [header, value] of Object.entries(expectations.headers)) { + const found = res.headers[header] + expect(found?.toLowerCase()).toEqual(value) + } + } return res.body } } @@ -88,6 +95,13 @@ export class PublicTableAPI { ): Promise> { return this.request.send("post", "/tables", table, expectations) } + + async search( + name: string, + expectations?: PublicAPIExpectations + ): Promise> { + return this.request.send("post", "/tables/search", { name }, expectations) + } } export class PublicRowAPI { diff --git a/packages/server/src/api/routes/public/tests/cors.spec.ts b/packages/server/src/api/routes/public/tests/cors.spec.ts new file mode 100644 index 0000000000..1a5895575d --- /dev/null +++ b/packages/server/src/api/routes/public/tests/cors.spec.ts @@ -0,0 +1,21 @@ +import * as setup from "../../tests/utilities" +import { PublicAPIRequest } from "./Request" + +describe("check public API security", () => { + const config = setup.getConfig() + let request: PublicAPIRequest + + beforeAll(async () => { + await config.init() + request = await PublicAPIRequest.init(config, await config.globalUser()) + }) + + it("should have Access-Control-Allow-Origin set to *", async () => { + await request.tables.search("", { + status: 200, + headers: { + "access-control-allow-origin": "*", + }, + }) + }) +}) diff --git a/yarn.lock b/yarn.lock index 506cea0f03..86facb7df9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -842,7 +842,7 @@ "@azure/abort-controller" "^2.0.0" tslib "^2.6.2" -"@azure/identity@4.2.1", "@azure/identity@^4.2.1": +"@azure/identity@^4.2.1": version "4.2.1" resolved "https://registry.yarnpkg.com/@azure/identity/-/identity-4.2.1.tgz#22b366201e989b7b41c0e1690e103bd579c31e4c" integrity sha512-U8hsyC9YPcEIzoaObJlRDvp7KiF0MGS7xcWbyJSVvXRkC/HXo1f0oYeBYmEvVgRfacw7GHf6D6yAoh9JHz6A5Q== @@ -2196,47 +2196,6 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@budibase/backend-core@3.2.11": - version "0.0.0" - dependencies: - "@budibase/nano" "10.1.5" - "@budibase/pouchdb-replication-stream" "1.2.11" - "@budibase/shared-core" "0.0.0" - "@budibase/types" "0.0.0" - "@techpass/passport-openidconnect" "0.3.3" - aws-cloudfront-sign "3.0.2" - aws-sdk "2.1692.0" - bcrypt "5.1.0" - bcryptjs "2.4.3" - bull "4.10.1" - correlation-id "4.0.0" - dd-trace "5.23.0" - dotenv "16.0.1" - google-auth-library "^8.0.1" - google-spreadsheet "npm:@budibase/google-spreadsheet@4.1.5" - ioredis "5.3.2" - joi "17.6.0" - jsonwebtoken "9.0.2" - knex "2.4.2" - koa-passport "^6.0.0" - koa-pino-logger "4.0.0" - lodash "4.17.21" - node-fetch "2.6.7" - passport-google-oauth "2.0.0" - passport-local "1.0.0" - passport-oauth2-refresh "^2.1.0" - pino "8.11.0" - pino-http "8.3.3" - posthog-node "4.0.1" - pouchdb "9.0.0" - pouchdb-find "9.0.0" - redlock "4.2.0" - rotating-file-stream "3.1.0" - sanitize-s3-objectkey "0.0.1" - semver "^7.5.4" - tar-fs "2.1.1" - uuid "^8.3.2" - "@budibase/handlebars-helpers@^0.13.2": version "0.13.2" resolved "https://registry.yarnpkg.com/@budibase/handlebars-helpers/-/handlebars-helpers-0.13.2.tgz#73ab51c464e91fd955b429017648e0257060db77" @@ -2279,47 +2238,6 @@ pouchdb-promise "^6.0.4" through2 "^2.0.0" -"@budibase/pro@npm:@budibase/pro@latest": - version "3.2.11" - resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-3.2.11.tgz#40d4929b3958dacca3f4c207718a4647c08a6100" - integrity sha512-xE1tx/C2cnbyR4s/6XkkweoQw6CW5fsQt++gzrrML8abgsODru+tA7M2NbWlKsEnVWHDQRvUVcXm4wqDeNNZ9g== - dependencies: - "@anthropic-ai/sdk" "^0.27.3" - "@budibase/backend-core" "3.2.11" - "@budibase/shared-core" "3.2.11" - "@budibase/string-templates" "3.2.11" - "@budibase/types" "3.2.11" - "@koa/router" "13.1.0" - bull "4.10.1" - dd-trace "5.23.0" - joi "17.6.0" - jsonwebtoken "9.0.2" - lru-cache "^7.14.1" - memorystream "^0.3.1" - node-fetch "2.6.7" - openai "4.59.0" - scim-patch "^0.8.1" - scim2-parse-filter "^0.2.8" - -"@budibase/shared-core@3.2.11": - version "0.0.0" - dependencies: - "@budibase/types" "0.0.0" - cron-validate "1.4.5" - -"@budibase/string-templates@3.2.11": - version "0.0.0" - dependencies: - "@budibase/handlebars-helpers" "^0.13.2" - dayjs "^1.10.8" - handlebars "^4.7.8" - lodash.clonedeep "^4.5.0" - -"@budibase/types@3.2.11": - version "0.0.0" - dependencies: - scim-patch "^0.8.1" - "@bull-board/api@5.10.2": version "5.10.2" resolved "https://registry.yarnpkg.com/@bull-board/api/-/api-5.10.2.tgz#ae8ff6918b23897bf879a6ead3683f964374c4b3" @@ -3428,6 +3346,13 @@ resolved "https://registry.yarnpkg.com/@jsep-plugin/regex/-/regex-1.0.4.tgz#cb2fc423220fa71c609323b9ba7f7d344a755fcc" integrity sha512-q7qL4Mgjs1vByCaTnDFcBnV9HS7GVPJX5vyVoCgZHNSC9rjwIlmbXG5sUuorR5ndfHAIlJ8pVStxvjXHbNvtUg== +"@koa/cors@5.0.0": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@koa/cors/-/cors-5.0.0.tgz#0029b5f057fa0d0ae0e37dd2c89ece315a0daffd" + integrity sha512-x/iUDjcS90W69PryLDIMgFyV21YLTnG9zOpPXS7Bkt2b8AsY3zZsIpOLBkYr9fBcF3HbkKaER5hOBZLfpLgYNw== + dependencies: + vary "^1.1.2" + "@koa/router@13.1.0": version "13.1.0" resolved "https://registry.yarnpkg.com/@koa/router/-/router-13.1.0.tgz#43f4c554444ea4f4a148a5735a9525c6d16fd1b5" @@ -5698,6 +5623,13 @@ "@types/koa-compose" "*" "@types/node" "*" +"@types/koa__cors@^5.0.0": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@types/koa__cors/-/koa__cors-5.0.0.tgz#74567a045b599266e2cd3940cef96cedecc2ef1f" + integrity sha512-LCk/n25Obq5qlernGOK/2LUwa/2YJb2lxHUkkvYFDOpLXlVI6tKcdfCHRBQnOY4LwH6el5WOLs6PD/a8Uzau6g== + dependencies: + "@types/koa" "*" + "@types/koa__router@12.0.4": version "12.0.4" resolved "https://registry.yarnpkg.com/@types/koa__router/-/koa__router-12.0.4.tgz#a1f9afec9dc7e7d9fa1252d1938c44b403e19a28" @@ -7309,7 +7241,23 @@ aws4@^1.8.0: resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.13.2.tgz#0aa167216965ac9474ccfa83892cfb6b3e1e52ef" integrity sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw== -axios@1.1.3, axios@1.7.7, axios@^0.21.1, axios@^1.0.0, axios@^1.1.3, axios@^1.4.0, axios@^1.6.2, axios@^1.6.8: +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: + 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@^1.0.0, axios@^1.1.3, axios@^1.4.0, axios@^1.6.2, axios@^1.6.8: version "1.7.7" resolved "https://registry.yarnpkg.com/axios/-/axios-1.7.7.tgz#2f554296f9892a72ac8d8e4c5b79c14a91d0a47f" integrity sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q== @@ -11281,7 +11229,7 @@ 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.15.6: +follow-redirects@^1.14.0, follow-redirects@^1.15.0, follow-redirects@^1.15.6: version "1.15.9" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.9.tgz#a604fa10e443bf98ca94228d9eebcc2e8a2c8ee1" integrity sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ== @@ -12399,7 +12347,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== @@ -12889,6 +12842,11 @@ is-boolean-object@^1.1.0: call-bind "^1.0.2" has-tostringtag "^1.0.0" +is-buffer@^1.1.5: + version "1.1.6" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" + integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== + is-buffer@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.5.tgz#ebc252e400d22ff8d77fa09888821a24a658c191" @@ -13350,11 +13308,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" @@ -14246,7 +14199,14 @@ kill-port@^1.6.1: get-them-args "1.3.2" shell-exec "1.0.2" -kind-of@6.0.3, kind-of@^3.0.2, kind-of@^3.1.0, kind-of@^6.0.0, kind-of@^6.0.2, kind-of@^6.0.3: +kind-of@^3.0.2, kind-of@^3.1.0: + version "3.2.2" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" + integrity sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ== + dependencies: + is-buffer "^1.1.5" + +kind-of@^6.0.0, kind-of@^6.0.2, kind-of@^6.0.3: version "6.0.3" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== @@ -15815,7 +15775,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== @@ -16013,13 +15973,27 @@ node-domexception@1.0.0: resolved "https://registry.yarnpkg.com/node-domexception/-/node-domexception-1.0.0.tgz#6888db46a1f71c0b76b3f7555016b63fe64766e5" integrity sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ== -node-fetch@2.6.7, node-fetch@2.6.9, 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.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: + version "2.6.9" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.9.tgz#7c7f744b5cc6eb5fd404e0c7a9fec630a55657e6" + integrity sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg== + 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" @@ -17117,7 +17091,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== @@ -18382,6 +18364,13 @@ 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: + version "1.13.0" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.13.0.tgz#8b2357f13ef3cf546af3f52de00543a94da86cfa" + integrity sha512-BFwmFXiJoFqlUpZ5Qssolv15DMyc84gTBds1BjsV1BfXEo1UyyD7GsmN67n7J77uRhoSNW1AXtXKPLcBFQn9Aw== + dependencies: + punycode "^2.3.1" + psl@^1.1.33: version "1.9.0" resolved "https://registry.yarnpkg.com/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7" @@ -18427,6 +18416,11 @@ punycode@^2.1.0, punycode@^2.1.1, punycode@^2.3.0: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f" integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA== +punycode@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" + integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== + pupa@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/pupa/-/pupa-2.1.1.tgz#f5e8fd4afc2c5d97828faa523549ed8744a20d62" @@ -19385,6 +19379,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.4.1" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.4.1.tgz#44cc8988377f126304d3b3fc1010c733b929ef0f" + integrity sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg== + sax@>=0.6.0: version "1.2.4" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" @@ -19457,13 +19456,33 @@ 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.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@^7.6.0, semver@~2.3.1: +"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.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, semver@^7.6.0: + version "7.6.3" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143" + integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== + +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== + seq-queue@^0.0.5: version "0.0.5" resolved "https://registry.yarnpkg.com/seq-queue/-/seq-queue-0.0.5.tgz#d56812e1c017a6e4e7c3e3a37a1da6d78dd3c93e" @@ -21007,7 +21026,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== @@ -21017,6 +21036,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" @@ -21492,14 +21519,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" @@ -22208,7 +22227,14 @@ 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.6.2, xml2js@^0.5.0: +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.6.2: version "0.6.2" resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.6.2.tgz#dd0b630083aa09c161e25a4d0901e2b2a929b499" integrity sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA== @@ -22216,6 +22242,14 @@ xml2js@0.1.x, xml2js@0.6.2, xml2js@^0.5.0: sax ">=0.6.0" xmlbuilder "~11.0.0" +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" + xmlbuilder@~11.0.0: version "11.0.1" resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz#be9bae1c8a046e76b31127726347d0ad7002beb3" From 4650537958437f72c149373a84a43dc95d1bc847 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Fri, 22 Nov 2024 15:08:07 +0000 Subject: [PATCH 24/31] yarn lock update. --- yarn.lock | 234 +++++++++++++++++++++++++----------------------------- 1 file changed, 107 insertions(+), 127 deletions(-) diff --git a/yarn.lock b/yarn.lock index 86facb7df9..51675e6104 100644 --- a/yarn.lock +++ b/yarn.lock @@ -842,7 +842,7 @@ "@azure/abort-controller" "^2.0.0" tslib "^2.6.2" -"@azure/identity@^4.2.1": +"@azure/identity@4.2.1", "@azure/identity@^4.2.1": version "4.2.1" resolved "https://registry.yarnpkg.com/@azure/identity/-/identity-4.2.1.tgz#22b366201e989b7b41c0e1690e103bd579c31e4c" integrity sha512-U8hsyC9YPcEIzoaObJlRDvp7KiF0MGS7xcWbyJSVvXRkC/HXo1f0oYeBYmEvVgRfacw7GHf6D6yAoh9JHz6A5Q== @@ -2196,6 +2196,47 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== +"@budibase/backend-core@3.2.11": + version "0.0.0" + dependencies: + "@budibase/nano" "10.1.5" + "@budibase/pouchdb-replication-stream" "1.2.11" + "@budibase/shared-core" "0.0.0" + "@budibase/types" "0.0.0" + "@techpass/passport-openidconnect" "0.3.3" + aws-cloudfront-sign "3.0.2" + aws-sdk "2.1692.0" + bcrypt "5.1.0" + bcryptjs "2.4.3" + bull "4.10.1" + correlation-id "4.0.0" + dd-trace "5.23.0" + dotenv "16.0.1" + google-auth-library "^8.0.1" + google-spreadsheet "npm:@budibase/google-spreadsheet@4.1.5" + ioredis "5.3.2" + joi "17.6.0" + jsonwebtoken "9.0.2" + knex "2.4.2" + koa-passport "^6.0.0" + koa-pino-logger "4.0.0" + lodash "4.17.21" + node-fetch "2.6.7" + passport-google-oauth "2.0.0" + passport-local "1.0.0" + passport-oauth2-refresh "^2.1.0" + pino "8.11.0" + pino-http "8.3.3" + posthog-node "4.0.1" + pouchdb "9.0.0" + pouchdb-find "9.0.0" + redlock "4.2.0" + rotating-file-stream "3.1.0" + sanitize-s3-objectkey "0.0.1" + semver "^7.5.4" + tar-fs "2.1.1" + uuid "^8.3.2" + "@budibase/handlebars-helpers@^0.13.2": version "0.13.2" resolved "https://registry.yarnpkg.com/@budibase/handlebars-helpers/-/handlebars-helpers-0.13.2.tgz#73ab51c464e91fd955b429017648e0257060db77" @@ -2238,6 +2279,47 @@ pouchdb-promise "^6.0.4" through2 "^2.0.0" +"@budibase/pro@npm:@budibase/pro@latest": + version "3.2.11" + resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-3.2.11.tgz#40d4929b3958dacca3f4c207718a4647c08a6100" + integrity sha512-xE1tx/C2cnbyR4s/6XkkweoQw6CW5fsQt++gzrrML8abgsODru+tA7M2NbWlKsEnVWHDQRvUVcXm4wqDeNNZ9g== + dependencies: + "@anthropic-ai/sdk" "^0.27.3" + "@budibase/backend-core" "3.2.11" + "@budibase/shared-core" "3.2.11" + "@budibase/string-templates" "3.2.11" + "@budibase/types" "3.2.11" + "@koa/router" "13.1.0" + bull "4.10.1" + dd-trace "5.23.0" + joi "17.6.0" + jsonwebtoken "9.0.2" + lru-cache "^7.14.1" + memorystream "^0.3.1" + node-fetch "2.6.7" + openai "4.59.0" + scim-patch "^0.8.1" + scim2-parse-filter "^0.2.8" + +"@budibase/shared-core@3.2.11": + version "0.0.0" + dependencies: + "@budibase/types" "0.0.0" + cron-validate "1.4.5" + +"@budibase/string-templates@3.2.11": + version "0.0.0" + dependencies: + "@budibase/handlebars-helpers" "^0.13.2" + dayjs "^1.10.8" + handlebars "^4.7.8" + lodash.clonedeep "^4.5.0" + +"@budibase/types@3.2.11": + version "0.0.0" + dependencies: + scim-patch "^0.8.1" + "@bull-board/api@5.10.2": version "5.10.2" resolved "https://registry.yarnpkg.com/@bull-board/api/-/api-5.10.2.tgz#ae8ff6918b23897bf879a6ead3683f964374c4b3" @@ -5623,7 +5705,7 @@ "@types/koa-compose" "*" "@types/node" "*" -"@types/koa__cors@^5.0.0": +"@types/koa__cors@5.0.0": version "5.0.0" resolved "https://registry.yarnpkg.com/@types/koa__cors/-/koa__cors-5.0.0.tgz#74567a045b599266e2cd3940cef96cedecc2ef1f" integrity sha512-LCk/n25Obq5qlernGOK/2LUwa/2YJb2lxHUkkvYFDOpLXlVI6tKcdfCHRBQnOY4LwH6el5WOLs6PD/a8Uzau6g== @@ -7241,23 +7323,7 @@ aws4@^1.8.0: resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.13.2.tgz#0aa167216965ac9474ccfa83892cfb6b3e1e52ef" integrity sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw== -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: - 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@^1.0.0, axios@^1.1.3, axios@^1.4.0, axios@^1.6.2, axios@^1.6.8: +axios@1.1.3, axios@1.7.7, axios@^0.21.1, axios@^1.0.0, axios@^1.1.3, axios@^1.4.0, axios@^1.6.2, axios@^1.6.8: version "1.7.7" resolved "https://registry.yarnpkg.com/axios/-/axios-1.7.7.tgz#2f554296f9892a72ac8d8e4c5b79c14a91d0a47f" integrity sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q== @@ -11229,7 +11295,7 @@ 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.15.0, follow-redirects@^1.15.6: +follow-redirects@^1.15.6: version "1.15.9" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.9.tgz#a604fa10e443bf98ca94228d9eebcc2e8a2c8ee1" integrity sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ== @@ -12347,12 +12413,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== @@ -12842,11 +12903,6 @@ is-boolean-object@^1.1.0: call-bind "^1.0.2" has-tostringtag "^1.0.0" -is-buffer@^1.1.5: - version "1.1.6" - resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" - integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== - is-buffer@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.5.tgz#ebc252e400d22ff8d77fa09888821a24a658c191" @@ -13308,6 +13364,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" @@ -14199,14 +14260,7 @@ kill-port@^1.6.1: get-them-args "1.3.2" shell-exec "1.0.2" -kind-of@^3.0.2, kind-of@^3.1.0: - version "3.2.2" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" - integrity sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ== - dependencies: - is-buffer "^1.1.5" - -kind-of@^6.0.0, kind-of@^6.0.2, kind-of@^6.0.3: +kind-of@6.0.3, kind-of@^3.0.2, kind-of@^3.1.0, kind-of@^6.0.0, kind-of@^6.0.2, kind-of@^6.0.3: version "6.0.3" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== @@ -15775,7 +15829,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== @@ -15973,27 +16027,13 @@ node-domexception@1.0.0: resolved "https://registry.yarnpkg.com/node-domexception/-/node-domexception-1.0.0.tgz#6888db46a1f71c0b76b3f7555016b63fe64766e5" integrity sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ== -node-fetch@2.6.7, node-fetch@^2.6.0, node-fetch@^2.6.1, node-fetch@^2.6.7: +node-fetch@2.6.7, node-fetch@2.6.9, 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: - version "2.6.9" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.9.tgz#7c7f744b5cc6eb5fd404e0c7a9fec630a55657e6" - integrity sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg== - 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" @@ -17091,15 +17131,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== @@ -18364,13 +18396,6 @@ 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: - version "1.13.0" - resolved "https://registry.yarnpkg.com/psl/-/psl-1.13.0.tgz#8b2357f13ef3cf546af3f52de00543a94da86cfa" - integrity sha512-BFwmFXiJoFqlUpZ5Qssolv15DMyc84gTBds1BjsV1BfXEo1UyyD7GsmN67n7J77uRhoSNW1AXtXKPLcBFQn9Aw== - dependencies: - punycode "^2.3.1" - psl@^1.1.33: version "1.9.0" resolved "https://registry.yarnpkg.com/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7" @@ -18416,11 +18441,6 @@ punycode@^2.1.0, punycode@^2.1.1, punycode@^2.3.0: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f" integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA== -punycode@^2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" - integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== - pupa@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/pupa/-/pupa-2.1.1.tgz#f5e8fd4afc2c5d97828faa523549ed8744a20d62" @@ -19379,11 +19399,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.4.1" - resolved "https://registry.yarnpkg.com/sax/-/sax-1.4.1.tgz#44cc8988377f126304d3b3fc1010c733b929ef0f" - integrity sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg== - sax@>=0.6.0: version "1.2.4" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" @@ -19456,33 +19471,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.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.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@^7.6.0, semver@~2.3.1: 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, semver@^7.6.0: - version "7.6.3" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143" - integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== - -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== - seq-queue@^0.0.5: version "0.0.5" resolved "https://registry.yarnpkg.com/seq-queue/-/seq-queue-0.0.5.tgz#d56812e1c017a6e4e7c3e3a37a1da6d78dd3c93e" @@ -21026,7 +21021,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== @@ -21036,14 +21031,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" @@ -21519,6 +21506,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" @@ -22227,14 +22222,7 @@ 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.6.2: +xml2js@0.1.x, xml2js@0.6.2, xml2js@^0.5.0: version "0.6.2" resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.6.2.tgz#dd0b630083aa09c161e25a4d0901e2b2a929b499" integrity sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA== @@ -22242,14 +22230,6 @@ xml2js@0.6.2: sax ">=0.6.0" xmlbuilder "~11.0.0" -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" - xmlbuilder@~11.0.0: version "11.0.1" resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz#be9bae1c8a046e76b31127726347d0ad7002beb3" From e3e4ea6319233e66347c43d46d93f21fb52c47aa Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Fri, 22 Nov 2024 15:35:05 +0000 Subject: [PATCH 25/31] Fixing view fetch test case. --- packages/server/src/api/routes/tests/viewV2.spec.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/server/src/api/routes/tests/viewV2.spec.ts b/packages/server/src/api/routes/tests/viewV2.spec.ts index f360fdb040..23ae7c79d3 100644 --- a/packages/server/src/api/routes/tests/viewV2.spec.ts +++ b/packages/server/src/api/routes/tests/viewV2.spec.ts @@ -2596,8 +2596,6 @@ if (descriptions.length) { let view: ViewV2, view2: ViewV2 let table: Table, table2: Table beforeEach(async () => { - // clean app to make sure fixed amount of views - await config.init() table = await config.api.table.save(saveTableRequest()) table2 = await config.api.table.save(saveTableRequest()) view = await config.api.viewV2.create({ @@ -2611,11 +2609,11 @@ if (descriptions.length) { schema: {}, }) }) + it("should be able to list views", async () => { const response = await config.api.viewV2.fetch({ status: 200, }) - expect(response.data.length).toEqual(2) expect(response.data.find(v => v.id === view.id)).toBeDefined() expect(response.data.find(v => v.id === view2.id)).toBeDefined() }) From e137205de00f1e605e8a845e064bfe10d538bddf Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Fri, 22 Nov 2024 16:42:09 +0000 Subject: [PATCH 26/31] PR comments. --- packages/backend-core/src/db/instrumentation.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/backend-core/src/db/instrumentation.ts b/packages/backend-core/src/db/instrumentation.ts index 4c12e20443..0c0056d6ed 100644 --- a/packages/backend-core/src/db/instrumentation.ts +++ b/packages/backend-core/src/db/instrumentation.ts @@ -179,9 +179,7 @@ export class DDInstrumentedDatabase implements Database { style: opts?.style, timeout: opts?.timeout, num_doc_ids: opts?.doc_ids?.length, - query_params: opts?.query_params, view: opts?.view, - selector: opts?.selector, }) return this.db.dump(stream, opts) }) From 16acf8751b8bc2fc9268231bcb53b577f42a6f7d Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Fri, 22 Nov 2024 16:45:43 +0000 Subject: [PATCH 27/31] PR comment. --- packages/server/src/api/controllers/public/rows.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/server/src/api/controllers/public/rows.ts b/packages/server/src/api/controllers/public/rows.ts index 24ac18c7b1..3c9cbf0ddd 100644 --- a/packages/server/src/api/controllers/public/rows.ts +++ b/packages/server/src/api/controllers/public/rows.ts @@ -22,7 +22,7 @@ export function fixRow(row: Row, params: any) { return row } -function getSearchParameters(ctx: UserCtx) { +function buildSearchRequestBody(ctx: UserCtx) { let { sort, paginate, bookmark, limit, query } = ctx.request.body // update the body to the correct format of the internal search if (!sort) { @@ -40,13 +40,13 @@ function getSearchParameters(ctx: UserCtx) { } export async function search(ctx: UserCtx, next: Next) { - ctx.request.body = getSearchParameters(ctx) + ctx.request.body = buildSearchRequestBody(ctx) await rowController.search(ctx) await next() } export async function viewSearch(ctx: UserCtx, next: Next) { - ctx.request.body = getSearchParameters(ctx) + ctx.request.body = buildSearchRequestBody(ctx) ctx.params = { viewId: ctx.params.viewId, } From 276493e33d791091b1212c768cbd08b353f39b6c Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Fri, 22 Nov 2024 16:49:57 +0000 Subject: [PATCH 28/31] Add required keys to the mappings. --- .../server/src/api/controllers/public/mapping/applications.ts | 3 ++- packages/server/src/api/controllers/public/mapping/queries.ts | 3 ++- packages/server/src/api/controllers/public/mapping/rows.ts | 3 ++- packages/server/src/api/controllers/public/mapping/tables.ts | 3 ++- packages/server/src/api/controllers/public/mapping/users.ts | 3 ++- packages/server/src/api/controllers/public/mapping/views.ts | 4 ++-- 6 files changed, 12 insertions(+), 7 deletions(-) diff --git a/packages/server/src/api/controllers/public/mapping/applications.ts b/packages/server/src/api/controllers/public/mapping/applications.ts index 0b729fc610..74c55e1c7b 100644 --- a/packages/server/src/api/controllers/public/mapping/applications.ts +++ b/packages/server/src/api/controllers/public/mapping/applications.ts @@ -1,6 +1,7 @@ import { Application } from "./types" +import { RequiredKeys } from "@budibase/types" -function application(body: any): Application { +function application(body: any): RequiredKeys { let app = body?.application ? body.application : body return { _id: app.appId, diff --git a/packages/server/src/api/controllers/public/mapping/queries.ts b/packages/server/src/api/controllers/public/mapping/queries.ts index 481b5f18c4..d0857453f6 100644 --- a/packages/server/src/api/controllers/public/mapping/queries.ts +++ b/packages/server/src/api/controllers/public/mapping/queries.ts @@ -1,6 +1,7 @@ import { Query, ExecuteQuery } from "./types" +import { RequiredKeys } from "@budibase/types" -function query(body: any): Query { +function query(body: any): RequiredKeys { return { _id: body._id, datasourceId: body.datasourceId, diff --git a/packages/server/src/api/controllers/public/mapping/rows.ts b/packages/server/src/api/controllers/public/mapping/rows.ts index c1cba43718..69f376bebf 100644 --- a/packages/server/src/api/controllers/public/mapping/rows.ts +++ b/packages/server/src/api/controllers/public/mapping/rows.ts @@ -1,6 +1,7 @@ import { Row, RowSearch } from "./types" +import { RequiredKeys } from "@budibase/types" -function row(body: any): Row { +function row(body: any): RequiredKeys { delete body._rev // have to input everything, since structure unknown return { diff --git a/packages/server/src/api/controllers/public/mapping/tables.ts b/packages/server/src/api/controllers/public/mapping/tables.ts index 72ed9f1a9a..857feb82ca 100644 --- a/packages/server/src/api/controllers/public/mapping/tables.ts +++ b/packages/server/src/api/controllers/public/mapping/tables.ts @@ -1,6 +1,7 @@ import { Table } from "./types" +import { RequiredKeys } from "@budibase/types" -function table(body: any): Table { +function table(body: any): RequiredKeys
{ return { _id: body._id, name: body.name, diff --git a/packages/server/src/api/controllers/public/mapping/users.ts b/packages/server/src/api/controllers/public/mapping/users.ts index 2a158bede9..232c81cec0 100644 --- a/packages/server/src/api/controllers/public/mapping/users.ts +++ b/packages/server/src/api/controllers/public/mapping/users.ts @@ -1,6 +1,7 @@ import { User } from "./types" +import { RequiredKeys } from "@budibase/types" -function user(body: any): User { +function user(body: any): RequiredKeys { return { _id: body._id, email: body.email, diff --git a/packages/server/src/api/controllers/public/mapping/views.ts b/packages/server/src/api/controllers/public/mapping/views.ts index 7eee164368..9ee1fe42d5 100644 --- a/packages/server/src/api/controllers/public/mapping/views.ts +++ b/packages/server/src/api/controllers/public/mapping/views.ts @@ -1,8 +1,8 @@ import { View } from "./types" -import { ViewV2, Ctx } from "@budibase/types" +import { ViewV2, Ctx, RequiredKeys } from "@budibase/types" import { dataFilters } from "@budibase/shared-core" -function view(body: ViewV2): View { +function view(body: ViewV2): RequiredKeys { return { id: body.id, tableId: body.tableId, From 43d30c440d9e5a0e8f98f4bb244d45d492144181 Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Mon, 25 Nov 2024 08:27:18 +0000 Subject: [PATCH 29/31] further security updates --- package.json | 5 +- packages/backend-core/package.json | 2 +- packages/pro | 2 +- packages/server/package.json | 6 +- packages/worker/package.json | 2 +- yarn.lock | 248 +++++++++++++++-------------- 6 files changed, 140 insertions(+), 125 deletions(-) diff --git a/package.json b/package.json index 860447fc57..e354f36d2a 100644 --- a/package.json +++ b/package.json @@ -25,11 +25,12 @@ "prettier": "2.8.8", "prettier-plugin-svelte": "^2.3.0", "proper-lockfile": "^4.1.2", - "svelte": "^4.2.10", + "svelte": "4.2.19", "svelte-eslint-parser": "^0.33.1", "typescript": "5.5.2", "typescript-eslint": "^7.3.1", - "yargs": "^17.7.2" + "yargs": "^17.7.2", + "cross-spawn": "7.0.6" }, "scripts": { "get-past-client-version": "node scripts/getPastClientVersion.js", diff --git a/packages/backend-core/package.json b/packages/backend-core/package.json index 8f91d1e55d..a4381b4200 100644 --- a/packages/backend-core/package.json +++ b/packages/backend-core/package.json @@ -40,7 +40,7 @@ "bcryptjs": "2.4.3", "bull": "4.10.1", "correlation-id": "4.0.0", - "dd-trace": "5.23.0", + "dd-trace": "5.26.0", "dotenv": "16.0.1", "google-auth-library": "^8.0.1", "google-spreadsheet": "npm:@budibase/google-spreadsheet@4.1.5", diff --git a/packages/pro b/packages/pro index 4facf6a44e..b803ac9238 160000 --- a/packages/pro +++ b/packages/pro @@ -1 +1 @@ -Subproject commit 4facf6a44ee52a405794845f71584168b9db652c +Subproject commit b803ac923852e3feafc5ffec200ee56594393ba8 diff --git a/packages/server/package.json b/packages/server/package.json index 4e192ec286..581df9ccc4 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -80,8 +80,8 @@ "cookies": "0.8.0", "csvtojson": "2.0.10", "curlconverter": "3.21.0", - "dd-trace": "5.23.0", "dayjs": "^1.10.8", + "dd-trace": "5.26.0", "dotenv": "8.2.0", "form-data": "4.0.0", "global-agent": "3.0.0", @@ -131,6 +131,7 @@ "xml2js": "0.6.2" }, "devDependencies": { + "@babel/core": "^7.22.5", "@babel/preset-env": "7.16.11", "@jest/types": "^29.6.3", "@swc/core": "1.3.71", @@ -172,8 +173,7 @@ "tsconfig-paths": "4.0.0", "typescript": "5.5.2", "update-dotenv": "1.1.1", - "yargs": "13.2.4", - "@babel/core": "^7.22.5" + "yargs": "13.2.4" }, "nx": { "targets": { diff --git a/packages/worker/package.json b/packages/worker/package.json index 85eae6c88a..bc2d7fdae6 100644 --- a/packages/worker/package.json +++ b/packages/worker/package.json @@ -50,7 +50,7 @@ "bcrypt": "5.1.0", "bcryptjs": "2.4.3", "bull": "4.10.1", - "dd-trace": "5.23.0", + "dd-trace": "5.26.0", "dotenv": "8.6.0", "global-agent": "3.0.0", "ical-generator": "4.1.0", diff --git a/yarn.lock b/yarn.lock index 28591e7be0..e67c82e5df 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2196,7 +2196,7 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@budibase/backend-core@3.2.7": +"@budibase/backend-core@3.2.11": version "0.0.0" dependencies: "@budibase/nano" "10.1.5" @@ -2210,7 +2210,7 @@ bcryptjs "2.4.3" bull "4.10.1" correlation-id "4.0.0" - dd-trace "5.23.0" + dd-trace "5.26.0" dotenv "16.0.1" google-auth-library "^8.0.1" google-spreadsheet "npm:@budibase/google-spreadsheet@4.1.5" @@ -2280,18 +2280,18 @@ through2 "^2.0.0" "@budibase/pro@npm:@budibase/pro@latest": - version "3.2.7" - resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-3.2.7.tgz#4dfc48f3e2ba3a3b235872e54c3de4de54ea7941" - integrity sha512-GRRaf1qSqQfoodjvKjBDvNOVHZrcSXF7so6Y9Xv/MiRTjDES5nmhIT5rL/PQ1+Mb+sPytYmMaJ2mlTtwqSQZgQ== + version "3.2.11" + resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-3.2.11.tgz#40d4929b3958dacca3f4c207718a4647c08a6100" + integrity sha512-xE1tx/C2cnbyR4s/6XkkweoQw6CW5fsQt++gzrrML8abgsODru+tA7M2NbWlKsEnVWHDQRvUVcXm4wqDeNNZ9g== dependencies: "@anthropic-ai/sdk" "^0.27.3" - "@budibase/backend-core" "3.2.7" - "@budibase/shared-core" "3.2.7" - "@budibase/string-templates" "3.2.7" - "@budibase/types" "3.2.7" - "@koa/router" "8.0.8" + "@budibase/backend-core" "3.2.11" + "@budibase/shared-core" "3.2.11" + "@budibase/string-templates" "3.2.11" + "@budibase/types" "3.2.11" + "@koa/router" "13.1.0" bull "4.10.1" - dd-trace "5.2.0" + dd-trace "5.23.0" joi "17.6.0" jsonwebtoken "9.0.2" lru-cache "^7.14.1" @@ -2301,13 +2301,13 @@ scim-patch "^0.8.1" scim2-parse-filter "^0.2.8" -"@budibase/shared-core@3.2.7": +"@budibase/shared-core@3.2.11": version "0.0.0" dependencies: "@budibase/types" "0.0.0" cron-validate "1.4.5" -"@budibase/string-templates@3.2.7": +"@budibase/string-templates@3.2.11": version "0.0.0" dependencies: "@budibase/handlebars-helpers" "^0.13.2" @@ -2315,7 +2315,7 @@ handlebars "^4.7.8" lodash.clonedeep "^4.5.0" -"@budibase/types@3.2.7": +"@budibase/types@3.2.11": version "0.0.0" dependencies: scim-patch "^0.8.1" @@ -2477,12 +2477,10 @@ resolved "https://registry.yarnpkg.com/@dagrejs/graphlib/-/graphlib-2.2.4.tgz#d77bfa9ff49e2307c0c6e6b8b26b5dd3c05816c4" integrity sha512-mepCf/e9+SKYy1d02/UkvSy6+6MoyXhVxP8lLDfA7BPE1X1d4dR0sZznmbM8/XVJ1GPM+Svnx7Xj6ZweByWUkw== -"@datadog/native-appsec@7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@datadog/native-appsec/-/native-appsec-7.0.0.tgz#a380174dd49aef2d9bb613a0ec8ead6dc7822095" - integrity sha512-bywstWFW2hWxzPuS0+mFMVHHL0geulx5yQFtsjfszaH2LTAgk2D+Rt40MKbAoZ8q3tRw2dy6aYQ7svO3ca8jpA== - dependencies: - node-gyp-build "^3.9.0" +"@datadog/libdatadog@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@datadog/libdatadog/-/libdatadog-0.2.2.tgz#ac02c76ac9a38250dca740727c7cdf00244ce3d3" + integrity sha512-rTWo96mEPTY5UbtGoFj8/wY0uKSViJhsPg/Z6aoFWBFXQ8b45Ix2e/yvf92AAwrhG+gPLTxEqTXh3kef2dP8Ow== "@datadog/native-appsec@8.1.1": version "8.1.1" @@ -2491,13 +2489,12 @@ dependencies: node-gyp-build "^3.9.0" -"@datadog/native-iast-rewriter@2.2.2": - version "2.2.2" - resolved "https://registry.yarnpkg.com/@datadog/native-iast-rewriter/-/native-iast-rewriter-2.2.2.tgz#3f7feaf6be1af4c83ad063065b8ed509bbaf11cb" - integrity sha512-13ZBhJpjZ/tiV6rYfyAf/ITye9cyd3x12M/2NKhD4Ivev4N4uKBREAjpArOtzKtPXZ5b6oXwVV4ofT1SHoYyzA== +"@datadog/native-appsec@8.3.0": + version "8.3.0" + resolved "https://registry.yarnpkg.com/@datadog/native-appsec/-/native-appsec-8.3.0.tgz#91afd89d18d386be4da8a1b0e04500f2f8b5eb66" + integrity sha512-RYHbSJ/MwJcJaLzaCaZvUyNLUKFbMshayIiv4ckpFpQJDiq1T8t9iM2k7008s75g1vRuXfsRNX7MaLn4aoFuWA== dependencies: - lru-cache "^7.14.0" - node-gyp-build "^4.5.0" + node-gyp-build "^3.9.0" "@datadog/native-iast-rewriter@2.4.1": version "2.4.1" @@ -2507,12 +2504,13 @@ lru-cache "^7.14.0" node-gyp-build "^4.5.0" -"@datadog/native-iast-taint-tracking@1.6.4": - version "1.6.4" - resolved "https://registry.yarnpkg.com/@datadog/native-iast-taint-tracking/-/native-iast-taint-tracking-1.6.4.tgz#16c21ad7c36a53420c0d3c5a3720731809cc7e98" - integrity sha512-Owxk7hQ4Dxwv4zJAoMjRga0IvE6lhvxnNc8pJCHsemCWBXchjr/9bqg05Zy5JnMbKUWn4XuZeJD6RFZpRa8bfw== +"@datadog/native-iast-rewriter@2.5.0": + version "2.5.0" + resolved "https://registry.yarnpkg.com/@datadog/native-iast-rewriter/-/native-iast-rewriter-2.5.0.tgz#b613defe86e78168f750d1f1662d4ffb3cf002e6" + integrity sha512-WRu34A3Wwp6oafX8KWNAbedtDaaJO+nzfYQht7pcJKjyC2ggfPeF7SoP+eDo9wTn4/nQwEOscSR4hkJqTRlpXQ== dependencies: - node-gyp-build "^3.9.0" + lru-cache "^7.14.0" + node-gyp-build "^4.5.0" "@datadog/native-iast-taint-tracking@3.1.0": version "3.1.0" @@ -2521,6 +2519,13 @@ dependencies: node-gyp-build "^3.9.0" +"@datadog/native-iast-taint-tracking@3.2.0": + version "3.2.0" + resolved "https://registry.yarnpkg.com/@datadog/native-iast-taint-tracking/-/native-iast-taint-tracking-3.2.0.tgz#9fb6823d82f934e12c06ea1baa7399ca80deb2ec" + integrity sha512-Mc6FzCoyvU5yXLMsMS9yKnEqJMWoImAukJXolNWCTm+JQYCMf2yMsJ8pBAm7KyZKliamM9rCn7h7Tr2H3lXwjA== + dependencies: + node-gyp-build "^3.9.0" + "@datadog/native-metrics@^2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/@datadog/native-metrics/-/native-metrics-2.0.0.tgz#65bf03313ee419956361e097551db36173e85712" @@ -2529,16 +2534,13 @@ node-addon-api "^6.1.0" node-gyp-build "^3.9.0" -"@datadog/pprof@5.0.0": - version "5.0.0" - resolved "https://registry.yarnpkg.com/@datadog/pprof/-/pprof-5.0.0.tgz#0c0aaf06def6d2bc4b2d353ec7b264dadbfbefab" - integrity sha512-vhNan4SBuNWLpexunDJQ+hNbRAgWdk2qy5Iyh7Nn94uSSHXigAJMAvu4jwMKKQKFfchtobOkWT8GQUWW3tgpFg== +"@datadog/native-metrics@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@datadog/native-metrics/-/native-metrics-3.0.1.tgz#dc276c93785c0377a048e316f23b7c8ff3acfa84" + integrity sha512-0GuMyYyXf+Qpb/F+Fcekz58f2mO37lit9U3jMbWY/m8kac44gCPABzL5q3gWbdH+hWgqYfQoEYsdNDGSrKfwoQ== dependencies: - delay "^5.0.0" - node-gyp-build "<4.0" - p-limit "^3.1.0" - pprof-format "^2.0.7" - source-map "^0.7.4" + node-addon-api "^6.1.0" + node-gyp-build "^3.9.0" "@datadog/pprof@5.3.0": version "5.3.0" @@ -2551,6 +2553,17 @@ pprof-format "^2.1.0" source-map "^0.7.4" +"@datadog/pprof@5.4.1": + version "5.4.1" + resolved "https://registry.yarnpkg.com/@datadog/pprof/-/pprof-5.4.1.tgz#08c9bcf5d8efb2eeafdfc9f5bb5402f79fb41266" + integrity sha512-IvpL96e/cuh8ugP5O8Czdup7XQOLHeIDgM5pac5W7Lc1YzGe5zTtebKFpitvb1CPw1YY+1qFx0pWGgKP2kOfHg== + dependencies: + delay "^5.0.0" + node-gyp-build "<4.0" + p-limit "^3.1.0" + pprof-format "^2.1.0" + source-map "^0.7.4" + "@datadog/sketches-js@^2.1.0": version "2.1.0" resolved "https://registry.yarnpkg.com/@datadog/sketches-js/-/sketches-js-2.1.0.tgz#8c7e8028a5fc22ad102fa542b0a446c956830455" @@ -2879,6 +2892,11 @@ wrap-ansi "^8.1.0" wrap-ansi-cjs "npm:wrap-ansi@^7.0.0" +"@isaacs/ttlcache@^1.4.1": + version "1.4.1" + resolved "https://registry.yarnpkg.com/@isaacs/ttlcache/-/ttlcache-1.4.1.tgz#21fb23db34e9b6220c6ba023a0118a2dd3461ea2" + integrity sha512-RQgQ4uQ+pLbqXfOmieB91ejmLwvSgv9nLx6sT6sD83s7umBypgg+OIBOBbEUiJXrfpnp9j0mRhYYdzp9uqq3lA== + "@istanbuljs/load-nyc-config@^1.0.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" @@ -3470,18 +3488,6 @@ koa-compose "^4.1.0" path-to-regexp "^6.3.0" -"@koa/router@8.0.8": - version "8.0.8" - resolved "https://registry.yarnpkg.com/@koa/router/-/router-8.0.8.tgz#95f32d11373d03d89dcb63fabe9ac6f471095236" - integrity sha512-FnT93N4NUehnXr+juupDmG2yfi0JnWdCmNEuIXpCG4TtG+9xvtrLambBH3RclycopVUOEYAim2lydiNBI7IRVg== - dependencies: - debug "^4.1.1" - http-errors "^1.7.3" - koa-compose "^4.1.0" - methods "^1.1.2" - path-to-regexp "1.x" - urijs "^1.19.2" - "@lerna/child-process@7.4.2": version "7.4.2" resolved "https://registry.yarnpkg.com/@lerna/child-process/-/child-process-7.4.2.tgz#a2fd013ac2150dc288270d3e0d0b850c06bec511" @@ -3945,7 +3951,7 @@ resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.8.0.tgz#5aa7abb48f23f693068ed2999ae627d2f7d902ec" integrity sha512-I/s6F7yKUDdtMsoBWXJe8Qz40Tui5vsuKCWJEWVL+5q9sSWRzzx6v2KeNsOBEwd94j0eWkpWCH4yB6rZg9Mf0w== -"@opentelemetry/api@^1.0.0", "@opentelemetry/api@^1.0.1": +"@opentelemetry/api@^1.0.1": version "1.9.0" resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.9.0.tgz#d03eba68273dc0f7509e2a3d5cba21eae10379fe" integrity sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg== @@ -8965,6 +8971,15 @@ cron-validate@1.4.5: dependencies: yup "0.32.9" +cross-spawn@7.0.6: + version "7.0.6" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" + integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + cross-spawn@^6.0.0: version "6.0.5" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" @@ -9295,48 +9310,11 @@ dayjs@^1.10.8: resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.13.tgz#92430b0139055c3ebb60150aa13e860a4b5a366c" integrity sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg== -dc-polyfill@^0.1.2, dc-polyfill@^0.1.4: +dc-polyfill@^0.1.4: version "0.1.6" resolved "https://registry.yarnpkg.com/dc-polyfill/-/dc-polyfill-0.1.6.tgz#c2940fa68ffb24a7bf127cc6cfdd15b39f0e7f02" integrity sha512-UV33cugmCC49a5uWAApM+6Ev9ZdvIUMTrtCO9fj96TPGOQiea54oeO3tiEVdVeo3J9N2UdJEmbS4zOkkEA35uQ== -dd-trace@5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/dd-trace/-/dd-trace-5.2.0.tgz#6ca2d76ece95f08d98468d7782c22f24192afa53" - integrity sha512-Z5ql3ZKzVW3DPstHPkTPcIPvKljHNtzTYY/WuZRlgT4XK7rMaN0j5nA8LlUh7m+tOPWs05IiKngbYVZjsqhRgA== - dependencies: - "@datadog/native-appsec" "7.0.0" - "@datadog/native-iast-rewriter" "2.2.2" - "@datadog/native-iast-taint-tracking" "1.6.4" - "@datadog/native-metrics" "^2.0.0" - "@datadog/pprof" "5.0.0" - "@datadog/sketches-js" "^2.1.0" - "@opentelemetry/api" "^1.0.0" - "@opentelemetry/core" "^1.14.0" - crypto-randomuuid "^1.0.0" - dc-polyfill "^0.1.2" - ignore "^5.2.4" - import-in-the-middle "^1.7.3" - int64-buffer "^0.1.9" - ipaddr.js "^2.1.0" - istanbul-lib-coverage "3.2.0" - jest-docblock "^29.7.0" - koalas "^1.0.2" - limiter "1.1.5" - lodash.sortby "^4.7.0" - lru-cache "^7.14.0" - methods "^1.1.2" - module-details-from-path "^1.0.3" - msgpack-lite "^0.1.26" - node-abort-controller "^3.1.1" - opentracing ">=0.12.1" - path-to-regexp "^0.1.2" - pprof-format "^2.0.7" - protobufjs "^7.2.5" - retry "^0.13.1" - semver "^7.5.4" - tlhunter-sorted-set "^0.1.0" - dd-trace@5.23.0: version "5.23.0" resolved "https://registry.yarnpkg.com/dd-trace/-/dd-trace-5.23.0.tgz#a0c11863406de440a6675648caf06e1d07d67ba8" @@ -9374,6 +9352,44 @@ dd-trace@5.23.0: shell-quote "^1.8.1" tlhunter-sorted-set "^0.1.0" +dd-trace@5.26.0: + version "5.26.0" + resolved "https://registry.yarnpkg.com/dd-trace/-/dd-trace-5.26.0.tgz#cc55061f66742bf01d0d7dc9f75c0e4937c82f40" + integrity sha512-AQ4usxrbAG41f7CKUUe7fayZgfrh24D0L0vNzcU2mMJOmqQ3bXeDz9uSHkF3aFY8Epcsegrep3ifjRC0/zOxTw== + dependencies: + "@datadog/libdatadog" "^0.2.2" + "@datadog/native-appsec" "8.3.0" + "@datadog/native-iast-rewriter" "2.5.0" + "@datadog/native-iast-taint-tracking" "3.2.0" + "@datadog/native-metrics" "^3.0.1" + "@datadog/pprof" "5.4.1" + "@datadog/sketches-js" "^2.1.0" + "@isaacs/ttlcache" "^1.4.1" + "@opentelemetry/api" ">=1.0.0 <1.9.0" + "@opentelemetry/core" "^1.14.0" + crypto-randomuuid "^1.0.0" + dc-polyfill "^0.1.4" + ignore "^5.2.4" + import-in-the-middle "1.11.2" + int64-buffer "^0.1.9" + istanbul-lib-coverage "3.2.0" + jest-docblock "^29.7.0" + koalas "^1.0.2" + limiter "1.1.5" + lodash.sortby "^4.7.0" + lru-cache "^7.14.0" + module-details-from-path "^1.0.3" + msgpack-lite "^0.1.26" + opentracing ">=0.12.1" + path-to-regexp "^0.1.10" + pprof-format "^2.1.0" + protobufjs "^7.2.5" + retry "^0.13.1" + rfdc "^1.3.1" + semver "^7.5.4" + shell-quote "^1.8.1" + tlhunter-sorted-set "^0.1.0" + debug@4, debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4, debug@~4.3.1, debug@~4.3.2, debug@~4.3.4: version "4.3.6" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.6.tgz#2ab2c38fbaffebf8aa95fdfe6d88438c7a13c52b" @@ -12588,11 +12604,6 @@ husky@^8.0.3: resolved "https://registry.yarnpkg.com/husky/-/husky-8.0.3.tgz#4936d7212e46d1dea28fef29bb3a108872cd9184" integrity sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg== -husky@^9.1.4: - version "9.1.7" - resolved "https://registry.yarnpkg.com/husky/-/husky-9.1.7.tgz#d46a38035d101b46a70456a850ff4201344c0b2d" - integrity sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA== - ical-generator@4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/ical-generator/-/ical-generator-4.1.0.tgz#2a336c951864c5583a2aa715d16f2edcdfd2d90b" @@ -12709,7 +12720,7 @@ import-from@^3.0.0: dependencies: resolve-from "^5.0.0" -import-in-the-middle@1.11.2, import-in-the-middle@^1.7.3: +import-in-the-middle@1.11.2: version "1.11.2" resolved "https://registry.yarnpkg.com/import-in-the-middle/-/import-in-the-middle-1.11.2.tgz#dd848e72b63ca6cd7c34df8b8d97fc9baee6174f" integrity sha512-gK6Rr6EykBcc6cVWRSBR5TWf8nn6hZMYSRYqCcHa0l0d1fPK7JSYo6+Mlmck76jIX9aL/IZ71c06U2VpFwl1zA== @@ -12923,11 +12934,6 @@ ip@^2.0.0: resolved "https://registry.yarnpkg.com/ip/-/ip-2.0.0.tgz#4cf4ab182fee2314c75ede1276f8c80b479936da" integrity sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ== -ipaddr.js@^2.1.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-2.2.0.tgz#d33fa7bac284f4de7af949638c9d68157c6b92e8" - integrity sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA== - is-arguments@^1.0.4, is-arguments@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b" @@ -16075,7 +16081,7 @@ node-abi@^3.3.0: dependencies: semver "^7.3.5" -node-abort-controller@^3.0.1, node-abort-controller@^3.1.1: +node-abort-controller@^3.0.1: version "3.1.1" resolved "https://registry.yarnpkg.com/node-abort-controller/-/node-abort-controller-3.1.1.tgz#a94377e964a9a37ac3976d848cb5c765833b8548" integrity sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ== @@ -17259,14 +17265,7 @@ path-scurry@^1.11.1, path-scurry@^1.6.1: lru-cache "^10.2.0" minipass "^5.0.0 || ^6.0.2 || ^7.0.0" -path-to-regexp@1.x: - version "1.9.0" - resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.9.0.tgz#5dc0753acbf8521ca2e0f137b4578b917b10cf24" - integrity sha512-xIp7/apCFJuUHdDLWe8O1HIkb0kQrOMb/0u6FXQjemHn/ii5LrIzU6bdECnsiTF/GjZkMEKg1xdiZwNqDYlZ6g== - dependencies: - isarray "0.0.1" - -path-to-regexp@^0.1.10, path-to-regexp@^0.1.2: +path-to-regexp@^0.1.10: version "0.1.11" resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.11.tgz#a527e662c89efc4646dbfa8100bf3e847e495761" integrity sha512-c0t+KCuUkO/YDLPG4WWzEwx3J5F/GHXsD1h/SNZfySqAIKe/BaP95x8fWtOfRJokpS5yYHRJjMtYlXD8jxnpbw== @@ -18193,7 +18192,7 @@ pouchdb@9.0.0: uuid "8.3.2" vuvuzela "1.0.3" -pprof-format@^2.0.7, pprof-format@^2.1.0: +pprof-format@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/pprof-format/-/pprof-format-2.1.0.tgz#acc8d7773bcf4faf0a3d3df11bceefba7ac06664" integrity sha512-0+G5bHH0RNr8E5hoZo/zJYsL92MhkZjwrHp3O2IxmY8RJL9ooKeuZ8Tm0ZNBw5sGZ9TiM71sthTjWoR2Vf5/xw== @@ -20595,6 +20594,26 @@ svelte-spa-router@^4.0.1: dependencies: regexparam "2.0.2" +svelte@4.2.19: + version "4.2.19" + resolved "https://registry.yarnpkg.com/svelte/-/svelte-4.2.19.tgz#4e6e84a8818e2cd04ae0255fcf395bc211e61d4c" + integrity sha512-IY1rnGr6izd10B0A8LqsBfmlT5OILVuZ7XsI0vdGPEvuonFV7NYEUK4dAkm9Zg2q0Um92kYjTpS1CAP3Nh/KWw== + dependencies: + "@ampproject/remapping" "^2.2.1" + "@jridgewell/sourcemap-codec" "^1.4.15" + "@jridgewell/trace-mapping" "^0.3.18" + "@types/estree" "^1.0.1" + acorn "^8.9.0" + aria-query "^5.3.0" + axobject-query "^4.0.0" + code-red "^1.0.3" + css-tree "^2.3.1" + estree-walker "^3.0.3" + is-reference "^3.0.1" + locate-character "^3.0.0" + magic-string "^0.30.4" + periscopic "^3.1.0" + svelte@^4.2.10: version "4.2.12" resolved "https://registry.yarnpkg.com/svelte/-/svelte-4.2.12.tgz#13d98d2274d24d3ad216c8fdc801511171c70bb1" @@ -21628,11 +21647,6 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" -urijs@^1.19.2: - version "1.19.11" - resolved "https://registry.yarnpkg.com/urijs/-/urijs-1.19.11.tgz#204b0d6b605ae80bea54bea39280cdb7c9f923cc" - integrity sha512-HXgFDgDommxn5/bIv0cnQZsPhHDA90NPHD6+c/v21U5+Sx5hoP8+dP9IZXBU1gIfvdRfhG8cel9QNPeionfcCQ== - url-parse-lax@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-3.0.0.tgz#16b5cafc07dbe3676c1b1999177823d6503acb0c" From ef3572edee43b100621a10f36f37c5caecf74e2f Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Mon, 25 Nov 2024 08:41:00 +0000 Subject: [PATCH 30/31] update pro ref --- packages/pro | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/pro b/packages/pro index b803ac9238..25dd40ee12 160000 --- a/packages/pro +++ b/packages/pro @@ -1 +1 @@ -Subproject commit b803ac923852e3feafc5ffec200ee56594393ba8 +Subproject commit 25dd40ee12b048307b558ebcedb36548d6e042cd From aa133e92534b76237b6243d531bca170d716f727 Mon Sep 17 00:00:00 2001 From: Budibase Staging Release Bot <> Date: Mon, 25 Nov 2024 09:37:43 +0000 Subject: [PATCH 31/31] Bump version to 3.2.12 --- lerna.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lerna.json b/lerna.json index a1006702cc..dc238bb392 100644 --- a/lerna.json +++ b/lerna.json @@ -1,6 +1,6 @@ { "$schema": "node_modules/lerna/schemas/lerna-schema.json", - "version": "3.2.11", + "version": "3.2.12", "npmClient": "yarn", "concurrency": 20, "command": {