From 9b3ab049a4d46200ed357dbfc7d4282c2686334e Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Tue, 8 Oct 2024 11:50:16 +0100 Subject: [PATCH 1/9] wip --- .../src/api/routes/tests/viewV2.spec.ts | 30 +++++++++++++++++++ packages/server/src/sdk/app/views/index.ts | 3 +- packages/types/src/documents/app/row.ts | 20 +++++++++++++ 3 files changed, 52 insertions(+), 1 deletion(-) diff --git a/packages/server/src/api/routes/tests/viewV2.spec.ts b/packages/server/src/api/routes/tests/viewV2.spec.ts index 5dabae6a03..5b25b48656 100644 --- a/packages/server/src/api/routes/tests/viewV2.spec.ts +++ b/packages/server/src/api/routes/tests/viewV2.spec.ts @@ -26,6 +26,8 @@ import { BBReferenceFieldSubType, NumericCalculationFieldMetadata, ViewV2Schema, + canGroupBy, + ViewV2Type, } from "@budibase/types" import { generator, mocks } from "@budibase/backend-core/tests" import { DatabaseName, getDatasource } from "../../../integrations/tests/utils" @@ -721,6 +723,34 @@ describe.each([ }, }) }) + + it.only("cannot use complex types as group-by fields", async () => { + const complexTypes = Object.values(FieldType).filter( + type => !canGroupBy(type) + ) + for (const type of complexTypes) { + const field = { name: "field", type } as FieldSchema + const table = await config.api.table.save( + saveTableRequest({ schema: { field } }) + ) + await config.api.viewV2.create( + { + tableId: table._id!, + name: generator.guid(), + type: ViewV2Type.CALCULATION, + schema: { + field: { visible: true }, + }, + }, + { + status: 400, + body: { + message: "", + }, + } + ) + } + }) }) describe("update", () => { diff --git a/packages/server/src/sdk/app/views/index.ts b/packages/server/src/sdk/app/views/index.ts index 5445851d88..76b2f6e347 100644 --- a/packages/server/src/sdk/app/views/index.ts +++ b/packages/server/src/sdk/app/views/index.ts @@ -1,6 +1,7 @@ import { CalculationType, FieldType, + isNumeric, PermissionLevel, RelationSchemaField, RenameColumn, @@ -103,7 +104,7 @@ async function guardCalculationViewSchema( ) } - if (!isCount && !helpers.schema.isNumeric(targetSchema)) { + if (!isCount && !isNumeric(targetSchema.type)) { throw new HTTPError( `Calculation field "${name}" references field "${schema.field}" which is not a numeric field`, 400 diff --git a/packages/types/src/documents/app/row.ts b/packages/types/src/documents/app/row.ts index 42440b2988..673d481e3c 100644 --- a/packages/types/src/documents/app/row.ts +++ b/packages/types/src/documents/app/row.ts @@ -127,6 +127,26 @@ export const JsonTypes = [ FieldType.ARRAY, ] +export const NumericTypes = [FieldType.NUMBER, FieldType.BIGINT] + +export function isNumeric(type: FieldType) { + return NumericTypes.includes(type) +} + +export const GroupByTypes = [ + FieldType.STRING, + FieldType.LONGFORM, + FieldType.OPTIONS, + FieldType.NUMBER, + FieldType.BOOLEAN, + FieldType.DATETIME, + FieldType.BIGINT, +] + +export function canGroupBy(type: FieldType) { + return GroupByTypes.includes(type) +} + export interface RowAttachment { size: number name: string From 5c3adbed270a91fade0798024c18e0f9bbc19dd8 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Tue, 8 Oct 2024 15:34:34 +0100 Subject: [PATCH 2/9] Validate you can't group by complex fields. --- .../src/api/routes/tests/viewV2.spec.ts | 54 +++++++++---------- packages/server/src/sdk/app/views/index.ts | 8 +++ 2 files changed, 35 insertions(+), 27 deletions(-) diff --git a/packages/server/src/api/routes/tests/viewV2.spec.ts b/packages/server/src/api/routes/tests/viewV2.spec.ts index fa8b005ae9..c7c040717a 100644 --- a/packages/server/src/api/routes/tests/viewV2.spec.ts +++ b/packages/server/src/api/routes/tests/viewV2.spec.ts @@ -25,8 +25,8 @@ import { BBReferenceFieldSubType, NumericCalculationFieldMetadata, ViewV2Schema, - canGroupBy, ViewV2Type, + JsonTypes, } from "@budibase/types" import { generator, mocks } from "@budibase/backend-core/tests" import { DatabaseName, getDatasource } from "../../../integrations/tests/utils" @@ -738,33 +738,33 @@ describe.each([ }) }) - it.only("cannot use complex types as group-by fields", async () => { - const complexTypes = Object.values(FieldType).filter( - type => !canGroupBy(type) - ) - for (const type of complexTypes) { - const field = { name: "field", type } as FieldSchema - const table = await config.api.table.save( - saveTableRequest({ schema: { field } }) - ) - await config.api.viewV2.create( - { - tableId: table._id!, - name: generator.guid(), - type: ViewV2Type.CALCULATION, - schema: { - field: { visible: true }, + // We don't allow the creation of tables with most JsonTypes when using + // external datasources. + isInternal && + it("cannot use complex types as group-by fields", async () => { + for (const type of JsonTypes) { + const field = { name: "field", type } as FieldSchema + const table = await config.api.table.save( + saveTableRequest({ schema: { field } }) + ) + await config.api.viewV2.create( + { + tableId: table._id!, + name: generator.guid(), + type: ViewV2Type.CALCULATION, + schema: { + field: { visible: true }, + }, }, - }, - { - status: 400, - body: { - message: "", - }, - } - ) - } - }) + { + status: 400, + body: { + message: `Grouping by fields of type "${type}" is not supported`, + }, + } + ) + } + }) }) describe("update", () => { diff --git a/packages/server/src/sdk/app/views/index.ts b/packages/server/src/sdk/app/views/index.ts index 1d0e633332..44f6beedb1 100644 --- a/packages/server/src/sdk/app/views/index.ts +++ b/packages/server/src/sdk/app/views/index.ts @@ -1,5 +1,6 @@ import { CalculationType, + canGroupBy, FieldType, isNumeric, PermissionLevel, @@ -121,6 +122,13 @@ async function guardCalculationViewSchema( 400 ) } + + if (!canGroupBy(targetSchema.type)) { + throw new HTTPError( + `Grouping by fields of type "${targetSchema.type}" is not supported`, + 400 + ) + } } } From 5dc85230d9ee8f373bc11cc484592257c8f95206 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Tue, 8 Oct 2024 17:04:45 +0100 Subject: [PATCH 3/9] Add a test to make sure 'shadowing' a field works as expected in view calculations. --- .../src/api/routes/tests/viewV2.spec.ts | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/packages/server/src/api/routes/tests/viewV2.spec.ts b/packages/server/src/api/routes/tests/viewV2.spec.ts index c7c040717a..58176cb597 100644 --- a/packages/server/src/api/routes/tests/viewV2.spec.ts +++ b/packages/server/src/api/routes/tests/viewV2.spec.ts @@ -738,6 +738,41 @@ describe.each([ }) }) + !isLucene && + it("does not get confused when a calculation field shadows a basic one", async () => { + const table = await config.api.table.save( + saveTableRequest({ + schema: { + age: { + name: "age", + type: FieldType.NUMBER, + }, + }, + }) + ) + + await config.api.row.bulkImport(table._id!, { + rows: [{ age: 1 }, { age: 2 }, { age: 3 }], + }) + + const view = await config.api.viewV2.create({ + tableId: table._id!, + name: generator.guid(), + type: ViewV2Type.CALCULATION, + schema: { + age: { + visible: true, + calculationType: CalculationType.SUM, + field: "age", + }, + }, + }) + + const { rows } = await config.api.row.search(view.id) + expect(rows).toHaveLength(1) + expect(rows[0].age).toEqual(6) + }) + // We don't allow the creation of tables with most JsonTypes when using // external datasources. isInternal && From 0182d4a09f8b4657dae2731779fc595fdeea5567 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Tue, 8 Oct 2024 17:55:42 +0100 Subject: [PATCH 4/9] Make sure calculation views cannot be used to write or modify rows. --- .../src/api/controllers/row/external.ts | 6 ++ .../src/api/controllers/row/internal.ts | 7 +++ .../src/api/routes/tests/viewV2.spec.ts | 58 +++++++++++++++++++ packages/server/src/sdk/app/rows/external.ts | 5 ++ packages/server/src/sdk/app/rows/internal.ts | 7 ++- 5 files changed, 82 insertions(+), 1 deletion(-) diff --git a/packages/server/src/api/controllers/row/external.ts b/packages/server/src/api/controllers/row/external.ts index 18a9be5087..4fcd612ec3 100644 --- a/packages/server/src/api/controllers/row/external.ts +++ b/packages/server/src/api/controllers/row/external.ts @@ -27,6 +27,7 @@ import { } from "../../../utilities/rowProcessor" import { cloneDeep } from "lodash" import { generateIdForRow } from "./utils" +import { helpers } from "@budibase/shared-core" export async function handleRequest( operation: T, @@ -42,6 +43,11 @@ export async function handleRequest( export async function patch(ctx: UserCtx) { const source = await utils.getSource(ctx) + + if (sdk.views.isView(source) && helpers.views.isCalculationView(source)) { + ctx.throw(400, "Cannot update rows through a calculation view") + } + const table = await utils.getTableFromSource(source) const { _id, ...rowData } = ctx.request.body diff --git a/packages/server/src/api/controllers/row/internal.ts b/packages/server/src/api/controllers/row/internal.ts index bb9de6ce52..53b24d517c 100644 --- a/packages/server/src/api/controllers/row/internal.ts +++ b/packages/server/src/api/controllers/row/internal.ts @@ -22,13 +22,20 @@ import sdk from "../../../sdk" import { getLinkedTableIDs } from "../../../db/linkedRows/linkUtils" import { flatten } from "lodash" import { findRow } from "../../../sdk/app/rows/internal" +import { helpers } from "@budibase/shared-core" export async function patch(ctx: UserCtx) { const { tableId } = utils.getSourceId(ctx) const source = await utils.getSource(ctx) + + if (sdk.views.isView(source) && helpers.views.isCalculationView(source)) { + ctx.throw(400, "Cannot update rows through a calculation view") + } + const table = sdk.views.isView(source) ? await sdk.views.getTable(source.id) : source + const inputs = ctx.request.body const isUserTable = tableId === InternalTables.USER_METADATA let oldRow diff --git a/packages/server/src/api/routes/tests/viewV2.spec.ts b/packages/server/src/api/routes/tests/viewV2.spec.ts index 58176cb597..195481ae10 100644 --- a/packages/server/src/api/routes/tests/viewV2.spec.ts +++ b/packages/server/src/api/routes/tests/viewV2.spec.ts @@ -1978,6 +1978,30 @@ describe.each([ expect(newRow.one).toBeUndefined() expect(newRow.two).toEqual("bar") }) + + it("should not be possible to create a row in a calculation view", async () => { + const view = await config.api.viewV2.create({ + tableId: table._id!, + name: generator.guid(), + type: ViewV2Type.CALCULATION, + schema: { + id: { visible: true }, + one: { visible: true }, + }, + }) + + await config.api.row.save( + view.id, + { one: "foo" }, + { + status: 400, + body: { + message: "Cannot insert rows through a calculation view", + status: 400, + }, + } + ) + }) }) describe("patch", () => { @@ -2042,6 +2066,40 @@ describe.each([ expect(row.one).toEqual("foo") expect(row.two).toEqual("newBar") }) + + it("should not be possible to modify a row in a calculation view", async () => { + const view = await config.api.viewV2.create({ + tableId: table._id!, + name: generator.guid(), + type: ViewV2Type.CALCULATION, + schema: { + id: { visible: true }, + one: { visible: true }, + }, + }) + + const newRow = await config.api.row.save(table._id!, { + one: "foo", + two: "bar", + }) + + await config.api.row.patch( + view.id, + { + tableId: table._id!, + _id: newRow._id!, + _rev: newRow._rev!, + one: "newFoo", + two: "newBar", + }, + { + status: 400, + body: { + message: "Cannot update rows through a calculation view", + }, + } + ) + }) }) describe("destroy", () => { diff --git a/packages/server/src/sdk/app/rows/external.ts b/packages/server/src/sdk/app/rows/external.ts index 060ef3738a..f7cc38bf0a 100644 --- a/packages/server/src/sdk/app/rows/external.ts +++ b/packages/server/src/sdk/app/rows/external.ts @@ -15,6 +15,7 @@ import { } from "../../../utilities/rowProcessor" import cloneDeep from "lodash/fp/cloneDeep" import { tryExtractingTableAndViewId } from "./utils" +import { helpers } from "@budibase/shared-core" export async function getRow( sourceId: string | Table | ViewV2, @@ -54,6 +55,10 @@ export async function save( source = await sdk.tables.getTable(tableId) } + if (sdk.views.isView(source) && helpers.views.isCalculationView(source)) { + throw new HTTPError("Cannot insert rows through a calculation view", 400) + } + const row = await inputProcessing(userId, cloneDeep(source), inputs) const validateResult = await sdk.rows.utils.validate({ diff --git a/packages/server/src/sdk/app/rows/internal.ts b/packages/server/src/sdk/app/rows/internal.ts index 9306609132..bd03b77f94 100644 --- a/packages/server/src/sdk/app/rows/internal.ts +++ b/packages/server/src/sdk/app/rows/internal.ts @@ -1,4 +1,4 @@ -import { context, db } from "@budibase/backend-core" +import { context, db, HTTPError } from "@budibase/backend-core" import { Row, Table, ViewV2 } from "@budibase/types" import sdk from "../../../sdk" import { finaliseRow } from "../../../api/controllers/row/staticFormula" @@ -10,6 +10,7 @@ import * as linkRows from "../../../db/linkedRows" import { InternalTables } from "../../../db/utils" import { getFullUser } from "../../../utilities/users" import { getSource, tryExtractingTableAndViewId } from "./utils" +import { helpers } from "@budibase/shared-core" export async function save( tableOrViewId: string, @@ -29,6 +30,10 @@ export async function save( table = source } + if (sdk.views.isView(source) && helpers.views.isCalculationView(source)) { + throw new HTTPError("Cannot insert rows through a calculation view", 400) + } + if (!inputs._rev && !inputs._id) { inputs._id = db.generateRowID(inputs.tableId) } From 9d06c705ac47af317c3acb41d4b36a0236b9ee82 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 9 Oct 2024 12:47:43 +0200 Subject: [PATCH 5/9] Remove ContextUser usages in favor of just ids --- .../src/api/controllers/table/internal.ts | 4 ++-- .../api/controllers/table/tests/utils.spec.ts | 2 +- .../server/src/api/controllers/table/utils.ts | 20 +++++++++---------- .../src/sdk/app/tables/internal/index.ts | 5 ++--- 4 files changed, 14 insertions(+), 17 deletions(-) diff --git a/packages/server/src/api/controllers/table/internal.ts b/packages/server/src/api/controllers/table/internal.ts index 4286d51d3e..256c5b1f0a 100644 --- a/packages/server/src/api/controllers/table/internal.ts +++ b/packages/server/src/api/controllers/table/internal.ts @@ -33,7 +33,7 @@ export async function save( try { const { table } = await sdk.tables.internal.save(tableToSave, { - user: ctx.user, + userId: ctx.user._id, rowsToImport: rows, tableId: ctx.request.body._id, renaming, @@ -72,7 +72,7 @@ export async function bulkImport( await handleDataImport(table, { importRows: rows, identifierFields, - user: ctx.user, + userId: ctx.user._id, }) return table } diff --git a/packages/server/src/api/controllers/table/tests/utils.spec.ts b/packages/server/src/api/controllers/table/tests/utils.spec.ts index dad0146696..68b00d3268 100644 --- a/packages/server/src/api/controllers/table/tests/utils.spec.ts +++ b/packages/server/src/api/controllers/table/tests/utils.spec.ts @@ -41,7 +41,7 @@ describe("utils", () => { const data = [{ name: "Alice" }, { name: "Bob" }, { name: "Claire" }] - const result = await importToRows(data, table, config.user) + const result = await importToRows(data, table, config.user?._id) expect(result).toEqual([ expect.objectContaining({ autoId: 1, diff --git a/packages/server/src/api/controllers/table/utils.ts b/packages/server/src/api/controllers/table/utils.ts index d568e5f33e..106d0e23a6 100644 --- a/packages/server/src/api/controllers/table/utils.ts +++ b/packages/server/src/api/controllers/table/utils.ts @@ -18,7 +18,6 @@ import { quotas } from "@budibase/pro" import { events, context, features } from "@budibase/backend-core" import { AutoFieldSubType, - ContextUser, Datasource, Row, SourceName, @@ -122,7 +121,7 @@ export function makeSureTableUpToDate(table: Table, tableToSave: Table) { export async function importToRows( data: Row[], table: Table, - user?: ContextUser, + userId?: string, opts?: { keepCouchId: boolean } ) { const originalTable = table @@ -136,7 +135,7 @@ export async function importToRows( // We use a reference to table here and update it after input processing, // so that we can auto increment auto IDs in imported data properly - const processed = await inputProcessing(user?._id, table, row, { + const processed = await inputProcessing(userId, table, row, { noAutoRelationships: true, }) row = processed @@ -167,11 +166,10 @@ export async function importToRows( export async function handleDataImport( table: Table, - opts?: { identifierFields?: string[]; user?: ContextUser; importRows?: Row[] } + opts?: { identifierFields?: string[]; userId?: string; importRows?: Row[] } ) { const schema = table.schema const identifierFields = opts?.identifierFields || [] - const user = opts?.user const importRows = opts?.importRows if (!importRows || !isRows(importRows) || !isSchema(schema)) { @@ -181,7 +179,7 @@ export async function handleDataImport( const db = context.getAppDB() const data = parse(importRows, table) - const finalData = await importToRows(data, table, user, { + const finalData = await importToRows(data, table, opts?.userId, { keepCouchId: identifierFields.includes("_id"), }) @@ -282,22 +280,22 @@ export function checkStaticTables(table: Table) { class TableSaveFunctions { db: Database - user?: ContextUser + userId?: string oldTable?: Table importRows?: Row[] rows: Row[] constructor({ - user, + userId, oldTable, importRows, }: { - user?: ContextUser + userId?: string oldTable?: Table importRows?: Row[] }) { this.db = context.getAppDB() - this.user = user + this.userId = userId this.oldTable = oldTable this.importRows = importRows // any rows that need updated @@ -329,7 +327,7 @@ class TableSaveFunctions { table = await handleSearchIndexes(table) table = await handleDataImport(table, { importRows: this.importRows, - user: this.user, + userId: this.userId, }) if (await features.flags.isEnabled("SQS")) { await sdk.tables.sqs.addTable(table) diff --git a/packages/server/src/sdk/app/tables/internal/index.ts b/packages/server/src/sdk/app/tables/internal/index.ts index c0beed0db8..7c72834ee0 100644 --- a/packages/server/src/sdk/app/tables/internal/index.ts +++ b/packages/server/src/sdk/app/tables/internal/index.ts @@ -5,7 +5,6 @@ import { ViewStatisticsSchema, ViewV2, Row, - ContextUser, } from "@budibase/types" import { hasTypeChanged, @@ -27,7 +26,7 @@ import { quotas } from "@budibase/pro" export async function save( table: Table, opts?: { - user?: ContextUser + userId?: string tableId?: string rowsToImport?: Row[] renaming?: RenameColumn @@ -63,7 +62,7 @@ export async function save( // saving a table is a complex operation, involving many different steps, this // has been broken out into a utility to make it more obvious/easier to manipulate const tableSaveFunctions = new TableSaveFunctions({ - user: opts?.user, + userId: opts?.userId, oldTable, importRows: opts?.rowsToImport, }) From 15d124bfafbc8ad5de5d31f1bc73721d3d8fe0e8 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 9 Oct 2024 12:58:10 +0200 Subject: [PATCH 6/9] Move internal creation to sdk --- .../server/src/api/controllers/table/index.ts | 10 +++-- packages/server/src/sdk/app/tables/create.ts | 18 +++++++++ packages/server/src/sdk/app/tables/index.ts | 2 + .../src/sdk/app/tables/internal/index.ts | 39 ++++++++++++++++++- 4 files changed, 64 insertions(+), 5 deletions(-) create mode 100644 packages/server/src/sdk/app/tables/create.ts diff --git a/packages/server/src/api/controllers/table/index.ts b/packages/server/src/api/controllers/table/index.ts index 7bec1581b4..a1969f44fc 100644 --- a/packages/server/src/api/controllers/table/index.ts +++ b/packages/server/src/api/controllers/table/index.ts @@ -106,14 +106,18 @@ export async function save(ctx: UserCtx) { const isImport = table.rows const renaming = ctx.request.body._rename + const isCreate = !table._id + checkDefaultFields(table) - const api = pickApi({ table }) - let savedTable = await api.save(ctx, renaming) - if (!table._id) { + let savedTable: Table + if (isCreate) { + savedTable = await sdk.tables.create(table) savedTable = await sdk.tables.enrichViewSchemas(savedTable) await events.table.created(savedTable) } else { + const api = pickApi({ table }) + savedTable = await api.save(ctx, renaming) await events.table.updated(savedTable) } if (renaming) { diff --git a/packages/server/src/sdk/app/tables/create.ts b/packages/server/src/sdk/app/tables/create.ts new file mode 100644 index 0000000000..585623d0a4 --- /dev/null +++ b/packages/server/src/sdk/app/tables/create.ts @@ -0,0 +1,18 @@ +import { Row, Table } from "@budibase/types" + +// import * as external from "./external" +import * as internal from "./internal" +import { isExternal } from "./utils" + +export async function create( + table: Omit, + rows?: Row[], + userId?: string +): Promise { + if (isExternal({ table })) { + // const datasourceId = table.sourceId! + throw "not implemented" + // return await external.create(table, rows, userId) + } + return await internal.create(table, rows, userId) +} diff --git a/packages/server/src/sdk/app/tables/index.ts b/packages/server/src/sdk/app/tables/index.ts index fcf7051e7c..326b2e1456 100644 --- a/packages/server/src/sdk/app/tables/index.ts +++ b/packages/server/src/sdk/app/tables/index.ts @@ -1,5 +1,6 @@ import { populateExternalTableSchemas } from "./validation" import * as getters from "./getters" +import * as create from "./create" import * as updates from "./update" import * as utils from "./utils" import { migrate } from "./migration" @@ -7,6 +8,7 @@ import * as sqs from "./internal/sqs" export default { populateExternalTableSchemas, + ...create, ...updates, ...getters, ...utils, diff --git a/packages/server/src/sdk/app/tables/internal/index.ts b/packages/server/src/sdk/app/tables/internal/index.ts index 7c72834ee0..b2081fee96 100644 --- a/packages/server/src/sdk/app/tables/internal/index.ts +++ b/packages/server/src/sdk/app/tables/internal/index.ts @@ -5,6 +5,7 @@ import { ViewStatisticsSchema, ViewV2, Row, + TableSourceType, } from "@budibase/types" import { hasTypeChanged, @@ -15,14 +16,48 @@ import { EventType, updateLinks } from "../../../../db/linkedRows" import { cloneDeep } from "lodash/fp" import isEqual from "lodash/isEqual" import { runStaticFormulaChecks } from "../../../../api/controllers/table/bulkFormula" -import { context } from "@budibase/backend-core" +import { context, HTTPError } from "@budibase/backend-core" import { findDuplicateInternalColumns } from "@budibase/shared-core" import { getTable } from "../getters" import { checkAutoColumns } from "./utils" import * as viewsSdk from "../../views" -import { getRowParams } from "../../../../db/utils" +import { generateTableID, getRowParams } from "../../../../db/utils" import { quotas } from "@budibase/pro" +export async function create(table: Table, rows?: Row[], userId?: string) { + const tableId = generateTableID() + + let tableToSave: Table = { + _id: tableId, + ...table, + // Ensure these fields are populated, even if not sent in the request + type: table.type || "table", + sourceType: TableSourceType.INTERNAL, + } + + const isImport = !!rows + + if (!tableToSave.views) { + tableToSave.views = {} + } + + try { + const { table } = await save(tableToSave, { + userId, + rowsToImport: rows, + isImport, + }) + + return table + } catch (err: any) { + if (err instanceof Error) { + throw new HTTPError(err.message, 400) + } else { + throw new HTTPError(err.message || err, err.status || 500) + } + } +} + export async function save( table: Table, opts?: { From 4efe335b65f6354d1f2b6a2a62c4ad39a0babfe4 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 9 Oct 2024 13:20:02 +0200 Subject: [PATCH 7/9] Create external table in sdk --- packages/server/src/sdk/app/tables/create.ts | 11 +++--- .../src/sdk/app/tables/external/index.ts | 36 +++++++++++++++++-- .../src/sdk/app/tables/internal/index.ts | 6 +++- 3 files changed, 45 insertions(+), 8 deletions(-) diff --git a/packages/server/src/sdk/app/tables/create.ts b/packages/server/src/sdk/app/tables/create.ts index 585623d0a4..ed6d6baeb0 100644 --- a/packages/server/src/sdk/app/tables/create.ts +++ b/packages/server/src/sdk/app/tables/create.ts @@ -1,6 +1,6 @@ import { Row, Table } from "@budibase/types" -// import * as external from "./external" +import * as external from "./external" import * as internal from "./internal" import { isExternal } from "./utils" @@ -9,10 +9,11 @@ export async function create( rows?: Row[], userId?: string ): Promise
{ + let createdTable: Table if (isExternal({ table })) { - // const datasourceId = table.sourceId! - throw "not implemented" - // return await external.create(table, rows, userId) + createdTable = await external.create(table) + } else { + createdTable = await internal.create(table, rows, userId) } - return await internal.create(table, rows, userId) + return createdTable } diff --git a/packages/server/src/sdk/app/tables/external/index.ts b/packages/server/src/sdk/app/tables/external/index.ts index e374e70c87..941d193b94 100644 --- a/packages/server/src/sdk/app/tables/external/index.ts +++ b/packages/server/src/sdk/app/tables/external/index.ts @@ -8,8 +8,11 @@ import { ViewV2, AutoFieldSubType, } from "@budibase/types" -import { context } from "@budibase/backend-core" -import { buildExternalTableId } from "../../../../integrations/utils" +import { context, HTTPError } from "@budibase/backend-core" +import { + breakExternalTableId, + buildExternalTableId, +} from "../../../../integrations/utils" import { foreignKeyStructure, hasTypeChanged, @@ -86,6 +89,35 @@ function validate(table: Table, oldTable?: Table) { } } +function getDatasourceId(table: Table) { + if (!table) { + throw new Error("No table supplied") + } + if (table.sourceId) { + return table.sourceId + } + if (!table._id) { + throw new Error("No table ID supplied") + } + return breakExternalTableId(table._id).datasourceId +} + +export async function create(table: Omit) { + const datasourceId = getDatasourceId(table) + + const tableToCreate = { ...table, created: true } + try { + const result = await save(datasourceId!, tableToCreate) + return result.table + } catch (err: any) { + if (err instanceof Error) { + throw new HTTPError(err.message, 400) + } else { + throw new HTTPError(err?.message || err, err.status || 500) + } + } +} + export async function save( datasourceId: string, update: Table, diff --git a/packages/server/src/sdk/app/tables/internal/index.ts b/packages/server/src/sdk/app/tables/internal/index.ts index b2081fee96..fbcbed03dc 100644 --- a/packages/server/src/sdk/app/tables/internal/index.ts +++ b/packages/server/src/sdk/app/tables/internal/index.ts @@ -24,7 +24,11 @@ import * as viewsSdk from "../../views" import { generateTableID, getRowParams } from "../../../../db/utils" import { quotas } from "@budibase/pro" -export async function create(table: Table, rows?: Row[], userId?: string) { +export async function create( + table: Omit, + rows?: Row[], + userId?: string +) { const tableId = generateTableID() let tableToSave: Table = { From 80ae7cbe0ba1b4f892e5a7cc8521947a09e36d94 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 9 Oct 2024 13:21:22 +0200 Subject: [PATCH 8/9] Rename --- packages/server/src/api/controllers/table/external.ts | 2 +- packages/server/src/api/controllers/table/index.ts | 2 +- packages/server/src/api/controllers/table/internal.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/server/src/api/controllers/table/external.ts b/packages/server/src/api/controllers/table/external.ts index 5b15d3d9c7..6f09bf4a61 100644 --- a/packages/server/src/api/controllers/table/external.ts +++ b/packages/server/src/api/controllers/table/external.ts @@ -31,7 +31,7 @@ function getDatasourceId(table: Table) { return breakExternalTableId(table._id).datasourceId } -export async function save( +export async function updateTable( ctx: UserCtx, renaming?: RenameColumn ) { diff --git a/packages/server/src/api/controllers/table/index.ts b/packages/server/src/api/controllers/table/index.ts index a1969f44fc..0e44548d99 100644 --- a/packages/server/src/api/controllers/table/index.ts +++ b/packages/server/src/api/controllers/table/index.ts @@ -117,7 +117,7 @@ export async function save(ctx: UserCtx) { await events.table.created(savedTable) } else { const api = pickApi({ table }) - savedTable = await api.save(ctx, renaming) + savedTable = await api.updateTable(ctx, renaming) await events.table.updated(savedTable) } if (renaming) { diff --git a/packages/server/src/api/controllers/table/internal.ts b/packages/server/src/api/controllers/table/internal.ts index 256c5b1f0a..983f578bbc 100644 --- a/packages/server/src/api/controllers/table/internal.ts +++ b/packages/server/src/api/controllers/table/internal.ts @@ -12,7 +12,7 @@ import { } from "@budibase/types" import sdk from "../../../sdk" -export async function save( +export async function updateTable( ctx: UserCtx, renaming?: RenameColumn ) { From 3b01f404dd500033ca71a23dae04557ac87b2283 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 9 Oct 2024 13:49:24 +0200 Subject: [PATCH 9/9] Fix imports --- packages/server/src/api/controllers/table/index.ts | 6 +++--- packages/server/src/api/controllers/table/internal.ts | 3 --- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/packages/server/src/api/controllers/table/index.ts b/packages/server/src/api/controllers/table/index.ts index 0e44548d99..404f82e57c 100644 --- a/packages/server/src/api/controllers/table/index.ts +++ b/packages/server/src/api/controllers/table/index.ts @@ -102,8 +102,8 @@ export async function find(ctx: UserCtx) { export async function save(ctx: UserCtx) { const appId = ctx.appId - const table = ctx.request.body - const isImport = table.rows + const { rows, ...table } = ctx.request.body + const isImport = rows const renaming = ctx.request.body._rename const isCreate = !table._id @@ -112,7 +112,7 @@ export async function save(ctx: UserCtx) { let savedTable: Table if (isCreate) { - savedTable = await sdk.tables.create(table) + savedTable = await sdk.tables.create(table, rows, ctx.user._id) savedTable = await sdk.tables.enrichViewSchemas(savedTable) await events.table.created(savedTable) } else { diff --git a/packages/server/src/api/controllers/table/internal.ts b/packages/server/src/api/controllers/table/internal.ts index 983f578bbc..40ce5e279d 100644 --- a/packages/server/src/api/controllers/table/internal.ts +++ b/packages/server/src/api/controllers/table/internal.ts @@ -25,8 +25,6 @@ export async function updateTable( sourceType: rest.sourceType || TableSourceType.INTERNAL, } - const isImport = !!rows - if (!tableToSave.views) { tableToSave.views = {} } @@ -37,7 +35,6 @@ export async function updateTable( rowsToImport: rows, tableId: ctx.request.body._id, renaming, - isImport, }) return table