diff --git a/packages/server/src/api/controllers/view/viewsV2.ts b/packages/server/src/api/controllers/view/viewsV2.ts index ae324c0391..3a8b0980b4 100644 --- a/packages/server/src/api/controllers/view/viewsV2.ts +++ b/packages/server/src/api/controllers/view/viewsV2.ts @@ -15,27 +15,19 @@ export async function find(ctx: Ctx) { } export async function create(ctx: Ctx) { + const { tableId } = ctx.params const view = ctx.request.body - const result = await sdk.views.create(view) + const result = await sdk.views.create(tableId, view) ctx.status = 201 ctx.body = { - data: { - ...view, - ...result, - }, + data: result, } } export async function remove(ctx: Ctx) { - const { viewId } = ctx.params - const doc = await sdk.views.get(viewId) - if (!doc) { - ctx.throw(404) - } + const { tableId, viewId } = ctx.params - const { _rev } = doc - - await sdk.views.remove(viewId, _rev!) + await sdk.views.remove(tableId, viewId) ctx.status = 204 } diff --git a/packages/server/src/api/routes/tests/viewV2.spec.ts b/packages/server/src/api/routes/tests/viewV2.spec.ts index d40bd82bcf..a83795cb9a 100644 --- a/packages/server/src/api/routes/tests/viewV2.spec.ts +++ b/packages/server/src/api/routes/tests/viewV2.spec.ts @@ -1,5 +1,12 @@ import * as setup from "./utilities" -import { FieldType, SortOrder, SortType, Table, ViewV2 } from "@budibase/types" +import { + CreateViewRequest, + FieldType, + SortOrder, + SortType, + Table, + ViewV2, +} from "@budibase/types" import { generator, structures } from "@budibase/backend-core/tests" function priceTable(): Table { @@ -26,7 +33,7 @@ function priceTable(): Table { describe("/v2/views", () => { const config = setup.getConfig() - const viewFilters: Omit = { + const viewFilters: Omit = { query: { allOr: false, equal: { field: "value" } }, sort: { field: "fieldToSort", @@ -46,20 +53,20 @@ describe("/v2/views", () => { describe("getView", () => { let view: ViewV2 beforeAll(async () => { - view = await config.api.viewV2.create({ + view = await config.api.viewV2.create(config.table?._id, { query: { allOr: false, notEqual: { field: "value" } }, }) }) it("can fetch the expected view", async () => { - const res = await config.api.viewV2.get(view._id!) + const res = await config.api.viewV2.get(view.id) expect(res.status).toBe(200) expect(res.body).toEqual({ data: { ...view, _id: view._id, - _rev: view._rev, + _rev: expect.any(String), createdAt: expect.any(String), updatedAt: expect.any(String), }, @@ -75,32 +82,38 @@ describe("/v2/views", () => { describe("create", () => { it("persist the view when the view is successfully created", async () => { - const newView: ViewV2 = { + const newView: CreateViewRequest = { name: generator.name(), tableId: config.table!._id!, } - const res = await config.api.viewV2.create(newView) + const res = await config.api.viewV2.create(config.table?._id, newView) expect(res).toEqual({ ...newView, + id: expect.any(String), + version: 2, _id: expect.any(String), - _rev: expect.any(String), + createdAt: expect.any(String), + updatedAt: expect.any(String), }) }) it("can persist views with queries", async () => { - const newView: ViewV2 = { + const newView: CreateViewRequest = { name: generator.name(), tableId: config.table!._id!, ...viewFilters, } - const res = await config.api.viewV2.create(newView) + const res = await config.api.viewV2.create(config.table!._id!, newView) expect(res).toEqual({ ...newView, ...viewFilters, + id: expect.any(String), + version: 2, _id: expect.any(String), - _rev: expect.any(String), + createdAt: expect.any(String), + updatedAt: expect.any(String), }) }) }) @@ -114,11 +127,11 @@ describe("/v2/views", () => { }) it("can delete an existing view", async () => { - await config.api.viewV2.get(view._id!, { expectStatus: 200 }) + await config.api.viewV2.get(view.id, { expectStatus: 200 }) - await config.api.viewV2.delete(view._id!) + await config.api.viewV2.delete(config.table?._id!, view.id) - await config.api.viewV2.get(view._id!, { expectStatus: 404 }) + await config.api.viewV2.get(view.id, { expectStatus: 404 }) }) }) }) diff --git a/packages/server/src/api/routes/view.ts b/packages/server/src/api/routes/view.ts index 8850b0882b..54ba54c431 100644 --- a/packages/server/src/api/routes/view.ts +++ b/packages/server/src/api/routes/view.ts @@ -14,12 +14,12 @@ router viewController.v2.find ) .post( - "/api/v2/views", + "/api/v2/views/:tableId", authorized(permissions.BUILDER), viewController.v2.create ) .delete( - `/api/v2/views/:viewId`, + `/api/v2/views/:tableId/:viewId`, authorized(permissions.BUILDER), viewController.v2.remove ) diff --git a/packages/server/src/sdk/app/views/index.ts b/packages/server/src/sdk/app/views/index.ts index 542c8df82c..3d7e21d448 100644 --- a/packages/server/src/sdk/app/views/index.ts +++ b/packages/server/src/sdk/app/views/index.ts @@ -1,6 +1,8 @@ -import { context } from "@budibase/backend-core" +import { HTTPError, context } from "@budibase/backend-core" import { ViewV2 } from "@budibase/types" import * as utils from "../../../db/utils" +import sdk from "../../../sdk" +import { utils as coreUtils } from "@budibase/backend-core" export async function get(viewId: string): Promise { const db = context.getAppDB() @@ -16,24 +18,45 @@ export async function get(viewId: string): Promise { } } -export async function create(view: ViewV2): Promise { - const db = context.getAppDB() - - const response = await db.put( - { - _id: utils.generateViewID(view.tableId), - ...view, - }, - {} - ) - return { - ...view, - _id: response.id, - _rev: response.rev, +export async function create( + tableId: string, + viewRequest: Omit +): Promise { + const view: ViewV2 = { + ...viewRequest, + id: coreUtils.newid(), + version: 2, } + view._id = view.id + + const db = context.getAppDB() + + await db.put(view, {}) + + const table = await sdk.tables.getTable(tableId) + table.views ??= {} + + // @ts-ignore: TODO + table.views[view.name] = view + await db.put(table) + return view } -export async function remove(viewId: string, rev: string): Promise { - const db = context.getAppDB() - await db.remove(viewId, rev) +function isV2(view: object): view is ViewV2 { + return (view as ViewV2).version === 2 +} + +export async function remove(tableId: string, viewId: string): Promise { + const db = context.getAppDB() + + const doc = await sdk.views.get(viewId) + await db.remove(viewId, doc!._rev) + + const table = await sdk.tables.getTable(tableId) + const view = Object.values(table.views!).find(v => isV2(v) && v.id === viewId) + if (!view) { + throw new HTTPError(`View ${viewId} not found in table ${tableId}`, 404) + } + delete table.views![view?.name] + await db.put(table) } diff --git a/packages/server/src/tests/utilities/api/viewV2.ts b/packages/server/src/tests/utilities/api/viewV2.ts index e261b55166..c2d2a79292 100644 --- a/packages/server/src/tests/utilities/api/viewV2.ts +++ b/packages/server/src/tests/utilities/api/viewV2.ts @@ -10,19 +10,21 @@ export class ViewV2API extends TestAPI { } create = async ( + tableId?: string, viewData?: Partial, { expectStatus } = { expectStatus: 201 } - ) => { - if (!this.config.table) { + ): Promise => { + if (!tableId && !this.config.table) { throw "Test requires table to be configured." } + tableId = this.config.table!._id! const view = { - tableId: this.config.table._id, + tableId, name: generator.guid(), ...viewData, } const result = await this.request - .post(`/api/v2/views`) + .post(`/api/v2/views/${tableId}`) .send(view) .set(this.config.defaultHeaders()) .expect("Content-Type", /json/) @@ -41,9 +43,13 @@ export class ViewV2API extends TestAPI { .expect(expectStatus) } - delete = async (viewId: string, { expectStatus } = { expectStatus: 204 }) => { + delete = async ( + tableId: string, + viewId: string, + { expectStatus } = { expectStatus: 204 } + ) => { return this.request - .delete(`/api/v2/views/${viewId}`) + .delete(`/api/v2/views/${tableId}/${viewId}`) .set(this.config.defaultHeaders()) .expect(expectStatus) } diff --git a/packages/types/src/api/web/app/view.ts b/packages/types/src/api/web/app/view.ts index e30d35d8ab..fe95a83483 100644 --- a/packages/types/src/api/web/app/view.ts +++ b/packages/types/src/api/web/app/view.ts @@ -6,5 +6,5 @@ export interface ViewResponse { export type CreateViewRequest = Omit< ViewV2, - "_id" | "_rev" | "createdAt" | "updatedAt" + "_id" | "_rev" | "createdAt" | "updatedAt" | "version" | "id" > diff --git a/packages/types/src/documents/app/view.ts b/packages/types/src/documents/app/view.ts index a4662e3c29..a60fb2a798 100644 --- a/packages/types/src/documents/app/view.ts +++ b/packages/types/src/documents/app/view.ts @@ -15,6 +15,8 @@ export interface View { } export interface ViewV2 extends Document { + version: 2 + id: string name: string tableId: string query?: SearchFilters