diff --git a/packages/server/src/api/controllers/row/external.ts b/packages/server/src/api/controllers/row/external.ts index 4fcd612ec3..082d07283b 100644 --- a/packages/server/src/api/controllers/row/external.ts +++ b/packages/server/src/api/controllers/row/external.ts @@ -28,6 +28,7 @@ import { import { cloneDeep } from "lodash" import { generateIdForRow } from "./utils" import { helpers } from "@budibase/shared-core" +import { HTTPError } from "@budibase/backend-core" export async function handleRequest( operation: T, @@ -102,6 +103,11 @@ export async function patch(ctx: UserCtx) { export async function destroy(ctx: UserCtx) { const source = await utils.getSource(ctx) + + if (sdk.views.isView(source) && helpers.views.isCalculationView(source)) { + throw new HTTPError("Cannot delete rows through a calculation view", 400) + } + const _id = ctx.request.body._id const { row } = await handleRequest(Operation.DELETE, source, { id: breakRowIdField(_id), diff --git a/packages/server/src/api/controllers/row/internal.ts b/packages/server/src/api/controllers/row/internal.ts index 53b24d517c..e193f2e968 100644 --- a/packages/server/src/api/controllers/row/internal.ts +++ b/packages/server/src/api/controllers/row/internal.ts @@ -8,7 +8,7 @@ import { } from "../../../utilities/rowProcessor" import * as utils from "./utils" import { cloneDeep } from "lodash/fp" -import { context } from "@budibase/backend-core" +import { context, HTTPError } from "@budibase/backend-core" import { finaliseRow, updateRelatedFormula } from "./staticFormula" import { FieldType, @@ -16,6 +16,7 @@ import { PatchRowRequest, PatchRowResponse, Row, + Table, UserCtx, } from "@budibase/types" import sdk from "../../../sdk" @@ -97,15 +98,26 @@ export async function patch(ctx: UserCtx) { export async function destroy(ctx: UserCtx) { const db = context.getAppDB() - const { tableId } = utils.getSourceId(ctx) + const source = await utils.getSource(ctx) + + if (sdk.views.isView(source) && helpers.views.isCalculationView(source)) { + throw new HTTPError("Cannot delete rows through a calculation view", 400) + } + + let table: Table + if (sdk.views.isView(source)) { + table = await sdk.views.getTable(source.id) + } else { + table = source + } + const { _id } = ctx.request.body let row = await db.get(_id) let _rev = ctx.request.body._rev || row._rev - if (row.tableId !== tableId) { + if (row.tableId !== table._id) { throw "Supplied tableId doesn't match the row's tableId" } - const table = await sdk.tables.getTable(tableId) // update the row to include full relationships before deleting them row = await outputProcessing(table, row, { squash: false, @@ -115,7 +127,7 @@ export async function destroy(ctx: UserCtx) { await linkRows.updateLinks({ eventType: linkRows.EventType.ROW_DELETE, row, - tableId, + tableId: table._id!, }) // remove any attachments that were on the row from object storage await AttachmentCleanup.rowDelete(table, [row]) @@ -123,7 +135,7 @@ export async function destroy(ctx: UserCtx) { await updateRelatedFormula(table, row) let response - if (tableId === InternalTables.USER_METADATA) { + if (table._id === InternalTables.USER_METADATA) { ctx.params = { id: _id, } diff --git a/packages/server/src/api/routes/tests/viewV2.spec.ts b/packages/server/src/api/routes/tests/viewV2.spec.ts index 195481ae10..d268063b99 100644 --- a/packages/server/src/api/routes/tests/viewV2.spec.ts +++ b/packages/server/src/api/routes/tests/viewV2.spec.ts @@ -2148,6 +2148,32 @@ describe.each([ }) await config.api.row.get(table._id!, rows[1]._id!, { status: 200 }) }) + + it("should not be possible to delete a row in a calculation view", async () => { + const row = await config.api.row.save(table._id!, {}) + + 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.delete( + view.id, + { _id: row._id! }, + { + status: 400, + body: { + message: "Cannot delete rows through a calculation view", + status: 400, + }, + } + ) + }) }) describe("read", () => {