From 3335c86a84802fe027becb7557d098317cbb170c Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Mon, 31 Jul 2023 16:14:14 +0200 Subject: [PATCH] Delete row from view --- .../server/src/api/controllers/row/index.ts | 4 +- packages/server/src/api/routes/row.ts | 42 +++++++++++++++++++ .../server/src/api/routes/tests/row.spec.ts | 33 ++++++++++++++- .../server/src/tests/utilities/api/row.ts | 15 +++++-- .../server/src/tests/utilities/api/viewV2.ts | 13 ++++++ 5 files changed, 100 insertions(+), 7 deletions(-) diff --git a/packages/server/src/api/controllers/row/index.ts b/packages/server/src/api/controllers/row/index.ts index 1bb9569c49..d177fdc8a9 100644 --- a/packages/server/src/api/controllers/row/index.ts +++ b/packages/server/src/api/controllers/row/index.ts @@ -166,13 +166,13 @@ async function deleteRow(ctx: UserCtx) { const appId = ctx.appId const tableId = utils.getTableId(ctx) - let resp = await quotas.addQuery(() => pickApi(tableId).destroy(ctx), { + const resp = await quotas.addQuery(() => pickApi(tableId).destroy(ctx), { datasourceId: tableId, }) await quotas.removeRow() ctx.eventEmitter && ctx.eventEmitter.emitRow(`row:delete`, appId, resp.row) - gridSocket?.emitRowDeletion(ctx, resp.row._id) + gridSocket?.emitRowDeletion(ctx, resp.row._id!) return resp } diff --git a/packages/server/src/api/routes/row.ts b/packages/server/src/api/routes/row.ts index ba3b2404cc..b79347a871 100644 --- a/packages/server/src/api/routes/row.ts +++ b/packages/server/src/api/routes/row.ts @@ -6,6 +6,7 @@ import { permissions } from "@budibase/backend-core" import { internalSearchValidator } from "./utils/validators" import noViewData from "../../middleware/noViewData" import trimViewRowInfo from "../../middleware/trimViewRowInfo" +import * as utils from "../../db/utils" const { PermissionType, PermissionLevel } = permissions const router: Router = new Router() @@ -305,6 +306,14 @@ router trimViewRowInfo, rowController.save ) + /** + * @api {patch} /api/v2/views/:viewId/rows/:rowId Updates a row + * @apiName Update a row + * @apiGroup rows + * @apiPermission table write access + * @apiDescription This endpoint is identical to the row creation endpoint but instead it will + * error if an _id isn't provided, it will only function for existing rows. + */ .patch( "/api/v2/views/:viewId/rows/:rowId", paramResource("viewId"), @@ -312,5 +321,38 @@ router trimViewRowInfo, rowController.patch ) + /** + * @api {delete} /api/v2/views/:viewId/rows Delete rows for a view + * @apiName Delete rows for a view + * @apiGroup rows + * @apiPermission table write access + * @apiDescription This endpoint can delete a single row, or delete them in a bulk + * fashion. + * + * @apiParam {string} tableId The ID of the table the row is to be deleted from. + * + * @apiParam (Body) {object[]} [rows] If bulk deletion is desired then provide the rows in this + * key of the request body that are to be deleted. + * @apiParam (Body) {string} [_id] If deleting a single row then provide its ID in this field. + * @apiParam (Body) {string} [_rev] If deleting a single row from an internal table then provide its + * revision here. + * + * @apiSuccess {object[]|object} body If deleting bulk then the response body will be an array + * of the deleted rows, if deleting a single row then the body will contain a "row" property which + * is the deleted row. + */ + .delete( + "/api/v2/views/:viewId/rows", + paramResource("viewId"), + authorized(PermissionType.VIEW, PermissionLevel.WRITE), + // This is required as the implementation relies on the table id + (ctx, next) => { + ctx.params.tableId = utils.extractViewInfoFromID( + ctx.params.viewId + ).tableId + next() + }, + rowController.destroy + ) export default router diff --git a/packages/server/src/api/routes/tests/row.spec.ts b/packages/server/src/api/routes/tests/row.spec.ts index 125edd668a..b7df9777d4 100644 --- a/packages/server/src/api/routes/tests/row.spec.ts +++ b/packages/server/src/api/routes/tests/row.spec.ts @@ -16,6 +16,7 @@ import { FieldType, SortType, SortOrder, + DeleteRow, } from "@budibase/types" import { expectAnyInternalColsAttributes, @@ -1146,7 +1147,7 @@ describe("/rows", () => { describe("patch", () => { it("should update only the view fields for a row", async () => { const table = await config.createTable(userTable()) - const tableId = config.table!._id! + const tableId = table._id! const view = await config.api.viewV2.create({ tableId, columns: { @@ -1195,5 +1196,35 @@ describe("/rows", () => { expect(route?.stack).toContainEqual(trimViewRowInfoMiddleware) }) }) + + describe("destroy", () => { + it("should be able to delete a row", async () => { + const table = await config.createTable(userTable()) + const tableId = table._id! + const view = await config.api.viewV2.create({ + tableId, + columns: { + name: { visible: true }, + address: { visible: true }, + }, + }) + + const createdRow = await config.createRow() + const rowUsage = await getRowUsage() + const queryUsage = await getQueryUsage() + + const body: DeleteRow = { + _id: createdRow._id!, + } + await config.api.viewV2.row.delete(view.id, body) + + await assertRowUsage(rowUsage - 1) + await assertQueryUsage(queryUsage + 1) + + await config.api.row.get(tableId, createdRow._id!, { + expectStatus: 404, + }) + }) + }) }) }) diff --git a/packages/server/src/tests/utilities/api/row.ts b/packages/server/src/tests/utilities/api/row.ts index f4c80fa4c2..c7c72368f5 100644 --- a/packages/server/src/tests/utilities/api/row.ts +++ b/packages/server/src/tests/utilities/api/row.ts @@ -7,12 +7,19 @@ export class RowAPI extends TestAPI { super(config) } - get = async (tableId: string, rowId: string) => { - return await this.request + get = async ( + tableId: string, + rowId: string, + { expectStatus } = { expectStatus: 200 } + ) => { + const request = this.request .get(`/api/${tableId}/rows/${rowId}`) .set(this.config.defaultHeaders()) - .expect("Content-Type", /json/) - .expect(200) + .expect(expectStatus) + if (expectStatus !== 404) { + request.expect("Content-Type", /json/) + } + return request } patch = async ( diff --git a/packages/server/src/tests/utilities/api/viewV2.ts b/packages/server/src/tests/utilities/api/viewV2.ts index 1df056c630..a3e58bc537 100644 --- a/packages/server/src/tests/utilities/api/viewV2.ts +++ b/packages/server/src/tests/utilities/api/viewV2.ts @@ -1,5 +1,6 @@ import { CreateViewRequest, + DeleteRowRequest, PatchRowRequest, PatchRowResponse, Row, @@ -130,5 +131,17 @@ export class ViewV2API extends TestAPI { .expect(expectStatus) return result.body as PatchRowResponse }, + delete: async ( + viewId: string, + body: DeleteRowRequest, + { expectStatus } = { expectStatus: 200 } + ): Promise => { + const result = await this.request + .delete(`/api/v2/views/${viewId}/rows`) + .send(body) + .set(this.config.defaultHeaders()) + .expect(expectStatus) + return result.body + }, } }