Merge pull request #11349 from Budibase/BUDI-7189/update_view_endpoint

Add endpoint to update views 2.0
This commit is contained in:
Adria Navarro 2023-07-25 19:04:36 +01:00 committed by GitHub
commit 1512ecd939
6 changed files with 194 additions and 3 deletions

View File

@ -1,5 +1,10 @@
import sdk from "../../../sdk"
import { CreateViewRequest, Ctx, ViewResponse } from "@budibase/types"
import {
CreateViewRequest,
Ctx,
UpdateViewRequest,
ViewResponse,
} from "@budibase/types"
export async function create(ctx: Ctx<CreateViewRequest, ViewResponse>) {
const view = ctx.request.body
@ -12,6 +17,25 @@ export async function create(ctx: Ctx<CreateViewRequest, ViewResponse>) {
}
}
export async function update(ctx: Ctx<UpdateViewRequest, ViewResponse>) {
const view = ctx.request.body
if (view.version !== 2) {
ctx.throw(400, "Only views V2 can be updated")
}
if (ctx.params.viewId !== view.id) {
ctx.throw(400, "View id does not match between the body and the uri path")
}
const { tableId } = view
const result = await sdk.views.update(tableId, view)
ctx.body = {
data: result,
}
}
export async function remove(ctx: Ctx) {
const { viewId } = ctx.params

View File

@ -86,6 +86,124 @@ describe("/v2/views", () => {
})
})
describe("update", () => {
let view: ViewV2
beforeEach(async () => {
await config.createTable(priceTable())
view = await config.api.viewV2.create({ name: "View A" })
})
it("can update an existing view data", async () => {
const tableId = config.table!._id!
await config.api.viewV2.update({
...view,
query: { equal: { newField: "thatValue" } },
})
expect(await config.api.table.get(tableId)).toEqual({
...config.table,
views: {
[view.name]: {
...view,
query: { equal: { newField: "thatValue" } },
schema: expect.anything(),
},
},
_rev: expect.any(String),
updatedAt: expect.any(String),
})
})
it("can update an existing view name", async () => {
const tableId = config.table!._id!
await config.api.viewV2.update({ ...view, name: "View B" })
expect(await config.api.table.get(tableId)).toEqual(
expect.objectContaining({
views: {
"View B": { ...view, name: "View B", schema: expect.anything() },
},
})
)
})
it("cannot update an unexisting views nor edit ids", async () => {
const tableId = config.table!._id!
await config.api.viewV2.update(
{ ...view, id: generator.guid() },
{ expectStatus: 404 }
)
expect(await config.api.table.get(tableId)).toEqual(
expect.objectContaining({
views: {
[view.name]: {
...view,
schema: expect.anything(),
},
},
})
)
})
it("cannot update views with the wrong tableId", async () => {
const tableId = config.table!._id!
await config.api.viewV2.update(
{
...view,
tableId: generator.guid(),
query: { equal: { newField: "thatValue" } },
},
{ expectStatus: 404 }
)
expect(await config.api.table.get(tableId)).toEqual(
expect.objectContaining({
views: {
[view.name]: {
...view,
schema: expect.anything(),
},
},
})
)
})
it("cannot update views v1", async () => {
const viewV1 = await config.createView()
await config.api.viewV2.update(
{
...viewV1,
},
{
expectStatus: 400,
handleResponse: r => {
expect(r.body).toEqual({
message: "Only views V2 can be updated",
status: 400,
})
},
}
)
})
it("cannot update the a view with unmatching ids between url and body", async () => {
const anotherView = await config.api.viewV2.create()
const result = await config
.request!.put(`/api/v2/views/${anotherView.id}`)
.send(view)
.set(config.defaultHeaders())
.expect("Content-Type", /json/)
.expect(400)
expect(result.body).toEqual({
message: "View id does not match between the body and the uri path",
status: 400,
})
})
})
describe("delete", () => {
let view: ViewV2

View File

@ -13,6 +13,11 @@ router
authorized(permissions.BUILDER),
viewController.v2.create
)
.put(
`/api/v2/views/:viewId`,
authorized(permissions.BUILDER),
viewController.v2.update
)
.delete(
`/api/v2/views/:viewId`,
authorized(permissions.BUILDER),

View File

@ -33,6 +33,24 @@ export async function create(
return view
}
export async function update(tableId: string, view: ViewV2): Promise<ViewV2> {
const db = context.getAppDB()
const table = await sdk.tables.getTable(tableId)
table.views ??= {}
const existingView = Object.values(table.views).find(
v => isV2(v) && v.id === view.id
)
if (!existingView) {
throw new HTTPError(`View ${view.id} not found in table ${tableId}`, 404)
}
delete table.views[existingView.name]
table.views[view.name] = view
await db.put(table)
return view
}
export function isV2(view: View | ViewV2): view is ViewV2 {
return (view as ViewV2).version === 2
}

View File

@ -1,7 +1,8 @@
import { SortOrder, SortType, ViewV2 } from "@budibase/types"
import { CreateViewRequest, SortOrder, SortType, ViewV2 } from "@budibase/types"
import TestConfiguration from "../TestConfiguration"
import { TestAPI } from "./base"
import { generator } from "@budibase/backend-core/tests"
import { Response } from "superagent"
export class ViewV2API extends TestAPI {
constructor(config: TestConfiguration) {
@ -9,7 +10,7 @@ export class ViewV2API extends TestAPI {
}
create = async (
viewData?: Partial<ViewV2>,
viewData?: Partial<CreateViewRequest>,
{ expectStatus } = { expectStatus: 201 }
): Promise<ViewV2> => {
let tableId = viewData?.tableId
@ -31,6 +32,29 @@ export class ViewV2API extends TestAPI {
return result.body.data as ViewV2
}
update = async (
view: ViewV2,
{
expectStatus,
handleResponse,
}: {
expectStatus: number
handleResponse?: (response: Response) => void
} = { expectStatus: 200 }
): Promise<ViewV2> => {
const result = await this.request
.put(`/api/v2/views/${view.id}`)
.send(view)
.set(this.config.defaultHeaders())
.expect("Content-Type", /json/)
.expect(expectStatus)
if (handleResponse) {
handleResponse(result)
}
return result.body.data as ViewV2
}
delete = async (viewId: string, { expectStatus } = { expectStatus: 204 }) => {
return this.request
.delete(`/api/v2/views/${viewId}`)

View File

@ -5,3 +5,5 @@ export interface ViewResponse {
}
export type CreateViewRequest = Omit<ViewV2, "version" | "id">
export type UpdateViewRequest = ViewV2