Updating test cases, fixes based on running through view/row API.

This commit is contained in:
mike12345567 2023-08-08 13:19:22 +01:00
parent 2011e1693e
commit 0abd1deb34
8 changed files with 87 additions and 146 deletions

View File

@ -15,6 +15,7 @@ import {
UserCtx, UserCtx,
} from "@budibase/types" } from "@budibase/types"
import sdk from "../../../sdk" import sdk from "../../../sdk"
import * as utils from "./utils"
export async function handleRequest( export async function handleRequest(
operation: Operation, operation: Operation,
@ -43,7 +44,7 @@ export async function handleRequest(
} }
export async function patch(ctx: UserCtx<PatchRowRequest, PatchRowResponse>) { export async function patch(ctx: UserCtx<PatchRowRequest, PatchRowResponse>) {
const tableId = ctx.params.tableId const tableId = utils.getTableId(ctx)
const { _id, ...rowData } = ctx.request.body const { _id, ...rowData } = ctx.request.body
const validateResult = await sdk.rows.utils.validate({ const validateResult = await sdk.rows.utils.validate({
@ -70,7 +71,7 @@ export async function patch(ctx: UserCtx<PatchRowRequest, PatchRowResponse>) {
export async function save(ctx: UserCtx) { export async function save(ctx: UserCtx) {
const inputs = ctx.request.body const inputs = ctx.request.body
const tableId = ctx.params.tableId const tableId = utils.getTableId(ctx)
const validateResult = await sdk.rows.utils.validate({ const validateResult = await sdk.rows.utils.validate({
row: inputs, row: inputs,
tableId, tableId,
@ -98,12 +99,12 @@ export async function save(ctx: UserCtx) {
export async function find(ctx: UserCtx) { export async function find(ctx: UserCtx) {
const id = ctx.params.rowId const id = ctx.params.rowId
const tableId = ctx.params.tableId const tableId = utils.getTableId(ctx)
return sdk.rows.external.getRow(tableId, id) return sdk.rows.external.getRow(tableId, id)
} }
export async function destroy(ctx: UserCtx) { export async function destroy(ctx: UserCtx) {
const tableId = ctx.params.tableId const tableId = utils.getTableId(ctx)
const _id = ctx.request.body._id const _id = ctx.request.body._id
const { row } = (await handleRequest(Operation.DELETE, tableId, { const { row } = (await handleRequest(Operation.DELETE, tableId, {
id: breakRowIdField(_id), id: breakRowIdField(_id),
@ -114,7 +115,7 @@ export async function destroy(ctx: UserCtx) {
export async function bulkDestroy(ctx: UserCtx) { export async function bulkDestroy(ctx: UserCtx) {
const { rows } = ctx.request.body const { rows } = ctx.request.body
const tableId = ctx.params.tableId const tableId = utils.getTableId(ctx)
let promises: Promise<Row[] | { row: Row; table: Table }>[] = [] let promises: Promise<Row[] | { row: Row; table: Table }>[] = []
for (let row of rows) { for (let row of rows) {
promises.push( promises.push(
@ -130,7 +131,7 @@ export async function bulkDestroy(ctx: UserCtx) {
export async function fetchEnrichedRow(ctx: UserCtx) { export async function fetchEnrichedRow(ctx: UserCtx) {
const id = ctx.params.rowId const id = ctx.params.rowId
const tableId = ctx.params.tableId const tableId = utils.getTableId(ctx)
const { datasourceId, tableName } = breakExternalTableId(tableId) const { datasourceId, tableName } = breakExternalTableId(tableId)
const datasource: Datasource = await sdk.datasources.get(datasourceId!) const datasource: Datasource = await sdk.datasources.get(datasourceId!)
if (!tableName) { if (!tableName) {

View File

@ -13,7 +13,7 @@ import {
import { FieldTypes } from "../../../constants" import { FieldTypes } from "../../../constants"
import * as utils from "./utils" import * as utils from "./utils"
import { cloneDeep } from "lodash/fp" import { cloneDeep } from "lodash/fp"
import { context, db as dbCore } from "@budibase/backend-core" import { context } from "@budibase/backend-core"
import { finaliseRow, updateRelatedFormula } from "./staticFormula" import { finaliseRow, updateRelatedFormula } from "./staticFormula"
import { import {
UserCtx, UserCtx,
@ -26,8 +26,8 @@ import {
import sdk from "../../../sdk" import sdk from "../../../sdk"
export async function patch(ctx: UserCtx<PatchRowRequest, PatchRowResponse>) { export async function patch(ctx: UserCtx<PatchRowRequest, PatchRowResponse>) {
const tableId = utils.getTableId(ctx)
const inputs = ctx.request.body const inputs = ctx.request.body
const tableId = inputs.tableId
const isUserTable = tableId === InternalTables.USER_METADATA const isUserTable = tableId === InternalTables.USER_METADATA
let oldRow let oldRow
const dbTable = await sdk.tables.getTable(tableId) const dbTable = await sdk.tables.getTable(tableId)
@ -94,7 +94,8 @@ export async function patch(ctx: UserCtx<PatchRowRequest, PatchRowResponse>) {
export async function save(ctx: UserCtx) { export async function save(ctx: UserCtx) {
let inputs = ctx.request.body let inputs = ctx.request.body
inputs.tableId = ctx.params.tableId const tableId = utils.getTableId(ctx)
inputs.tableId = tableId
if (!inputs._rev && !inputs._id) { if (!inputs._rev && !inputs._id) {
inputs._id = generateRowID(inputs.tableId) inputs._id = generateRowID(inputs.tableId)
@ -132,20 +133,22 @@ export async function save(ctx: UserCtx) {
} }
export async function find(ctx: UserCtx) { export async function find(ctx: UserCtx) {
const db = dbCore.getDB(ctx.appId) const tableId = utils.getTableId(ctx),
const table = await sdk.tables.getTable(ctx.params.tableId) rowId = ctx.params.rowId
let row = await utils.findRow(ctx, ctx.params.tableId, ctx.params.rowId) const table = await sdk.tables.getTable(tableId)
let row = await utils.findRow(ctx, tableId, rowId)
row = await outputProcessing(table, row) row = await outputProcessing(table, row)
return row return row
} }
export async function destroy(ctx: UserCtx) { export async function destroy(ctx: UserCtx) {
const db = context.getAppDB() const db = context.getAppDB()
const tableId = utils.getTableId(ctx)
const { _id } = ctx.request.body const { _id } = ctx.request.body
let row = await db.get<Row>(_id) let row = await db.get<Row>(_id)
let _rev = ctx.request.body._rev || row._rev let _rev = ctx.request.body._rev || row._rev
if (row.tableId !== ctx.params.tableId) { if (row.tableId !== tableId) {
throw "Supplied tableId doesn't match the row's tableId" throw "Supplied tableId doesn't match the row's tableId"
} }
const table = await sdk.tables.getTable(row.tableId) const table = await sdk.tables.getTable(row.tableId)
@ -163,7 +166,7 @@ export async function destroy(ctx: UserCtx) {
await updateRelatedFormula(table, row) await updateRelatedFormula(table, row)
let response let response
if (ctx.params.tableId === InternalTables.USER_METADATA) { if (tableId === InternalTables.USER_METADATA) {
ctx.params = { ctx.params = {
id: _id, id: _id,
} }
@ -176,7 +179,7 @@ export async function destroy(ctx: UserCtx) {
} }
export async function bulkDestroy(ctx: UserCtx) { export async function bulkDestroy(ctx: UserCtx) {
const tableId = ctx.params.tableId const tableId = utils.getTableId(ctx)
const table = await sdk.tables.getTable(tableId) const table = await sdk.tables.getTable(tableId)
let { rows } = ctx.request.body let { rows } = ctx.request.body
@ -216,7 +219,7 @@ export async function bulkDestroy(ctx: UserCtx) {
export async function fetchEnrichedRow(ctx: UserCtx) { export async function fetchEnrichedRow(ctx: UserCtx) {
const db = context.getAppDB() const db = context.getAppDB()
const tableId = ctx.params.tableId const tableId = utils.getTableId(ctx)
const rowId = ctx.params.rowId const rowId = ctx.params.rowId
// need table to work out where links go in row // need table to work out where links go in row
let [table, row] = await Promise.all([ let [table, row] = await Promise.all([

View File

@ -45,15 +45,19 @@ export async function findRow(ctx: UserCtx, tableId: string, rowId: string) {
} }
export function getTableId(ctx: Ctx) { export function getTableId(ctx: Ctx) {
if (ctx.request.body?.tableId) { // top priority, use the URL first
return ctx.request.body.tableId
}
if (ctx.params?.sourceId) { if (ctx.params?.sourceId) {
return ctx.params.sourceId return ctx.params.sourceId
} }
// check body for a table ID
if (ctx.request.body?.tableId) {
return ctx.request.body.tableId
}
// now check for old way of specifying table ID
if (ctx.params?.tableId) { if (ctx.params?.tableId) {
return ctx.params.tableId return ctx.params.tableId
} }
// now check if a specific view name
if (ctx.params?.viewName) { if (ctx.params?.viewName) {
return ctx.params.viewName return ctx.params.viewName
} }

View File

@ -16,15 +16,12 @@ import {
FieldType, FieldType,
SortType, SortType,
SortOrder, SortOrder,
DeleteRow,
} from "@budibase/types" } from "@budibase/types"
import { import {
expectAnyInternalColsAttributes, expectAnyInternalColsAttributes,
generator, generator,
structures, structures,
} from "@budibase/backend-core/tests" } from "@budibase/backend-core/tests"
import trimViewRowInfoMiddleware from "../../../middleware/trimViewRowInfo"
import router from "../row"
describe("/rows", () => { describe("/rows", () => {
let request = setup.getRequest() let request = setup.getRequest()
@ -393,18 +390,6 @@ describe("/rows", () => {
expect(saved.arrayFieldArrayStrKnown).toEqual(["One"]) expect(saved.arrayFieldArrayStrKnown).toEqual(["One"])
expect(saved.optsFieldStrKnown).toEqual("Alpha") expect(saved.optsFieldStrKnown).toEqual("Alpha")
}) })
it("should throw an error when creating a table row with view id data", async () => {
const res = await request
.post(`/api/${row.tableId}/rows`)
.send({ ...row, _viewId: generator.guid() })
.set(config.defaultHeaders())
.expect("Content-Type", /json/)
.expect(400)
expect(res.body.message).toEqual(
"Table row endpoints cannot contain view info"
)
})
}) })
describe("patch", () => { describe("patch", () => {
@ -454,25 +439,6 @@ describe("/rows", () => {
await assertRowUsage(rowUsage) await assertRowUsage(rowUsage)
await assertQueryUsage(queryUsage) await assertQueryUsage(queryUsage)
}) })
it("should throw an error when creating a table row with view id data", async () => {
const existing = await config.createRow()
const res = await config.api.row.patch(
table._id!,
{
...existing,
_id: existing._id!,
_rev: existing._rev!,
tableId: table._id!,
_viewId: generator.guid(),
},
{ expectStatus: 400 }
)
expect(res.body.message).toEqual(
"Table row endpoints cannot contain view info"
)
})
}) })
describe("destroy", () => { describe("destroy", () => {
@ -741,7 +707,7 @@ describe("/rows", () => {
}) })
// the environment needs configured for this // the environment needs configured for this
await setup.switchToSelfHosted(async () => { await setup.switchToSelfHosted(async () => {
context.doInAppContext(config.getAppId(), async () => { return context.doInAppContext(config.getAppId(), async () => {
const enriched = await outputProcessing(table, [row]) const enriched = await outputProcessing(table, [row])
expect((enriched as Row[])[0].attachment[0].url).toBe( expect((enriched as Row[])[0].attachment[0].url).toBe(
`/files/signed/prod-budi-app-assets/${config.getProdAppId()}/attachments/${attachmentId}` `/files/signed/prod-budi-app-assets/${config.getProdAppId()}/attachments/${attachmentId}`
@ -847,7 +813,7 @@ describe("/rows", () => {
}) })
const data = randomRowData() const data = randomRowData()
const newRow = await config.api.viewV2.row.create(view.id, { const newRow = await config.api.row.save(view.id, {
tableId: config.table!._id, tableId: config.table!._id,
_viewId: view.id, _viewId: view.id,
...data, ...data,
@ -869,16 +835,6 @@ describe("/rows", () => {
expect(row.body.age).toBeUndefined() expect(row.body.age).toBeUndefined()
expect(row.body.jobTitle).toBeUndefined() expect(row.body.jobTitle).toBeUndefined()
}) })
it("should setup the trimViewRowInfo middleware", async () => {
const route = router.stack.find(
r =>
r.methods.includes("POST") &&
r.path === "/api/v2/views/:viewId/rows"
)
expect(route).toBeDefined()
expect(route?.stack).toContainEqual(trimViewRowInfoMiddleware)
})
}) })
describe("patch", () => { describe("patch", () => {
@ -893,13 +849,13 @@ describe("/rows", () => {
}, },
}) })
const newRow = await config.api.viewV2.row.create(view.id, { const newRow = await config.api.row.save(view.id, {
tableId, tableId,
_viewId: view.id, _viewId: view.id,
...randomRowData(), ...randomRowData(),
}) })
const newData = randomRowData() const newData = randomRowData()
await config.api.viewV2.row.update(view.id, newRow._id!, { await config.api.row.patch(view.id, {
tableId, tableId,
_viewId: view.id, _viewId: view.id,
_id: newRow._id!, _id: newRow._id!,
@ -922,16 +878,6 @@ describe("/rows", () => {
expect(row.body.age).toBeUndefined() expect(row.body.age).toBeUndefined()
expect(row.body.jobTitle).toBeUndefined() expect(row.body.jobTitle).toBeUndefined()
}) })
it("should setup the trimViewRowInfo middleware", async () => {
const route = router.stack.find(
r =>
r.methods.includes("PATCH") &&
r.path === "/api/v2/views/:viewId/rows/:rowId"
)
expect(route).toBeDefined()
expect(route?.stack).toContainEqual(trimViewRowInfoMiddleware)
})
}) })
describe("destroy", () => { describe("destroy", () => {
@ -950,10 +896,7 @@ describe("/rows", () => {
const rowUsage = await getRowUsage() const rowUsage = await getRowUsage()
const queryUsage = await getQueryUsage() const queryUsage = await getQueryUsage()
const body: DeleteRow = { await config.api.row.delete(view.id, [createdRow])
_id: createdRow._id!,
}
await config.api.viewV2.row.delete(view.id, body)
await assertRowUsage(rowUsage - 1) await assertRowUsage(rowUsage - 1)
await assertQueryUsage(queryUsage + 1) await assertQueryUsage(queryUsage + 1)
@ -982,9 +925,7 @@ describe("/rows", () => {
const rowUsage = await getRowUsage() const rowUsage = await getRowUsage()
const queryUsage = await getQueryUsage() const queryUsage = await getQueryUsage()
await config.api.viewV2.row.delete(view.id, { await config.api.row.delete(view.id, [rows[0], rows[2]])
rows: [rows[0], rows[2]],
})
await assertRowUsage(rowUsage - 2) await assertRowUsage(rowUsage - 2)
await assertQueryUsage(queryUsage + 1) await assertQueryUsage(queryUsage + 1)

View File

@ -3,26 +3,35 @@ import * as utils from "../db/utils"
import sdk from "../sdk" import sdk from "../sdk"
import { db } from "@budibase/backend-core" import { db } from "@budibase/backend-core"
import { Next } from "koa" import { Next } from "koa"
import { getTableId } from "../api/controllers/row/utils"
export default async (ctx: Ctx<Row>, next: Next) => { export default async (ctx: Ctx<Row>, next: Next) => {
const { body } = ctx.request const { body } = ctx.request
const { _viewId: viewId } = body let { _viewId: viewId } = body
const possibleViewId = ctx.params.tableId const possibleViewId = getTableId(ctx)
if (utils.isViewID(possibleViewId)) {
viewId = possibleViewId
}
// nothing to do, it is not a view (just a table ID) // nothing to do, it is not a view (just a table ID)
if (!viewId || !utils.isViewID(possibleViewId)) { if (!viewId) {
return next() return next()
} }
const { tableId } = utils.extractViewInfoFromID(possibleViewId) const { tableId } = utils.extractViewInfoFromID(viewId)
// don't need to trim delete requests
if (ctx.method.toLowerCase() !== "delete") {
const { _viewId, ...trimmedView } = await trimViewFields( const { _viewId, ...trimmedView } = await trimViewFields(
viewId, viewId,
tableId, tableId,
body body
) )
ctx.request.body = trimmedView ctx.request.body = trimmedView
ctx.params.tableId = tableId }
ctx.params.sourceId = tableId
return next() return next()
} }

View File

@ -1,4 +1,4 @@
import { PatchRowRequest } from "@budibase/types" import { PatchRowRequest, SaveRowRequest, Row } from "@budibase/types"
import TestConfiguration from "../TestConfiguration" import TestConfiguration from "../TestConfiguration"
import { TestAPI } from "./base" import { TestAPI } from "./base"
@ -8,12 +8,12 @@ export class RowAPI extends TestAPI {
} }
get = async ( get = async (
tableId: string, sourceId: string,
rowId: string, rowId: string,
{ expectStatus } = { expectStatus: 200 } { expectStatus } = { expectStatus: 200 }
) => { ) => {
const request = this.request const request = this.request
.get(`/api/${tableId}/rows/${rowId}`) .get(`/api/${sourceId}/rows/${rowId}`)
.set(this.config.defaultHeaders()) .set(this.config.defaultHeaders())
.expect(expectStatus) .expect(expectStatus)
if (expectStatus !== 404) { if (expectStatus !== 404) {
@ -22,16 +22,43 @@ export class RowAPI extends TestAPI {
return request return request
} }
save = async (
sourceId: string,
row: SaveRowRequest,
{ expectStatus } = { expectStatus: 200 }
): Promise<Row> => {
const resp = await this.request
.post(`/api/${sourceId}/rows`)
.send(row)
.set(this.config.defaultHeaders())
.expect("Content-Type", /json/)
.expect(expectStatus)
return resp.body as Row
}
patch = async ( patch = async (
tableId: string, sourceId: string,
row: PatchRowRequest, row: PatchRowRequest,
{ expectStatus } = { expectStatus: 200 } { expectStatus } = { expectStatus: 200 }
) => { ) => {
return this.request return this.request
.patch(`/api/${tableId}/rows`) .patch(`/api/${sourceId}/rows`)
.send(row) .send(row)
.set(this.config.defaultHeaders()) .set(this.config.defaultHeaders())
.expect("Content-Type", /json/) .expect("Content-Type", /json/)
.expect(expectStatus) .expect(expectStatus)
} }
delete = async (
sourceId: string,
rows: Row[],
{ expectStatus } = { expectStatus: 200 }
) => {
return this.request
.delete(`/api/${sourceId}/rows`)
.send({ rows })
.set(this.config.defaultHeaders())
.expect("Content-Type", /json/)
.expect(expectStatus)
}
} }

View File

@ -1,10 +1,6 @@
import { import {
CreateViewRequest, CreateViewRequest,
UpdateViewRequest, UpdateViewRequest,
DeleteRowRequest,
PatchRowRequest,
PatchRowResponse,
Row,
ViewV2, ViewV2,
SearchViewRowRequest, SearchViewRowRequest,
} from "@budibase/types" } from "@budibase/types"
@ -90,46 +86,4 @@ export class ViewV2API extends TestAPI {
.expect("Content-Type", /json/) .expect("Content-Type", /json/)
.expect(expectStatus) .expect(expectStatus)
} }
row = {
create: async (
viewId: string,
row: Row,
{ expectStatus } = { expectStatus: 200 }
): Promise<Row> => {
const result = await this.request
.post(`/api/v2/views/${viewId}/rows`)
.send(row)
.set(this.config.defaultHeaders())
.expect("Content-Type", /json/)
.expect(expectStatus)
return result.body as Row
},
update: async (
viewId: string,
rowId: string,
row: PatchRowRequest,
{ expectStatus } = { expectStatus: 200 }
): Promise<PatchRowResponse> => {
const result = await this.request
.patch(`/api/v2/views/${viewId}/rows/${rowId}`)
.send(row)
.set(this.config.defaultHeaders())
.expect("Content-Type", /json/)
.expect(expectStatus)
return result.body as PatchRowResponse
},
delete: async (
viewId: string,
body: DeleteRowRequest,
{ expectStatus } = { expectStatus: 200 }
): Promise<any> => {
const result = await this.request
.delete(`/api/v2/views/${viewId}/rows`)
.send(body)
.set(this.config.defaultHeaders())
.expect(expectStatus)
return result.body
},
}
} }

View File

@ -1,6 +1,8 @@
import { SearchParams } from "../../../sdk" import { SearchParams } from "../../../sdk"
import { Row } from "../../../documents" import { Row } from "../../../documents"
export interface SaveRowRequest extends Row {}
export interface PatchRowRequest extends Row { export interface PatchRowRequest extends Row {
_id: string _id: string
_rev: string _rev: string