budibase/packages/server/src/api/routes/tests/row.spec.js

488 lines
16 KiB
JavaScript
Raw Normal View History

2021-02-16 17:46:18 +01:00
const { outputProcessing } = require("../../../utilities/rowProcessor")
const setup = require("./utilities")
const { basicRow } = setup.structures
const { doInAppContext } = require("@budibase/backend-core/context")
const { doInTenant } = require("@budibase/backend-core/tenancy")
2022-05-11 12:32:53 +02:00
const { quotas, QuotaUsageType, StaticQuotaName, MonthlyQuotaName } = require("@budibase/pro")
const {getAllCurrentUsageValue} = require("../../../../../../../budibase-pro/packages/pro/src/db/quotas")
2020-04-24 19:02:51 +02:00
// mock the fetch for the search system
jest.mock("node-fetch")
describe("/rows", () => {
let request = setup.getRequest()
let config = setup.getConfig()
let table
let row
2020-04-22 17:35:20 +02:00
afterAll(setup.afterAll)
2020-04-22 17:35:20 +02:00
2020-05-27 18:23:01 +02:00
beforeEach(async () => {
await config.init()
2021-03-04 14:07:33 +01:00
table = await config.createTable()
row = basicRow(table._id)
2020-05-27 18:23:01 +02:00
})
2020-04-24 19:02:51 +02:00
const loadRow = async (id, status = 200) =>
2020-09-10 10:36:14 +02:00
await request
.get(`/api/${table._id}/rows/${id}`)
2021-03-04 14:07:33 +01:00
.set(config.defaultHeaders())
2020-09-10 10:36:14 +02:00
.expect('Content-Type', /json/)
.expect(status)
2020-09-10 10:36:14 +02:00
2022-05-11 12:32:53 +02:00
const getRowUsage = async () => {
return config.doInContext(null, () => quotas.getCurrentUsageValue(QuotaUsageType.STATIC, StaticQuotaName.ROWS))
}
const getQueryUsage = async () => {
return config.doInContext(null, () => quotas.getAllCurrentUsageValue(QuotaUsageType.MONTHLY, MonthlyQuotaName.QUERIES))
2022-05-11 12:32:53 +02:00
}
const assertRowUsage = async (expected) => {
const usage = await getRowUsage()
expect(usage).toBe(expected)
}
const assertQueryUsage = async (expected) => {
const usage = await getQueryUsage()
expect(usage).toBe(expected)
}
2020-09-10 10:36:14 +02:00
describe("save, load, update", () => {
it("returns a success message when the row is created", async () => {
2022-05-11 12:32:53 +02:00
const rowUsage = await getRowUsage()
const queryUsage = await getQueryUsage()
2021-03-04 14:07:33 +01:00
const res = await request
.post(`/api/${row.tableId}/rows`)
.send(row)
.set(config.defaultHeaders())
.expect('Content-Type', /json/)
.expect(200)
expect(res.res.statusMessage).toEqual(`${table.name} saved successfully`)
2020-05-14 16:12:30 +02:00
expect(res.body.name).toEqual("Test Contact")
expect(res.body._rev).toBeDefined()
2022-05-11 12:32:53 +02:00
await assertRowUsage(rowUsage + 1)
await assertQueryUsage(queryUsage + 1)
2020-04-22 17:35:20 +02:00
})
it("updates a row successfully", async () => {
2021-03-04 14:07:33 +01:00
const existing = await config.createRow()
2022-05-11 12:32:53 +02:00
const rowUsage = await getRowUsage()
const queryUsage = await getQueryUsage()
2020-04-24 19:02:51 +02:00
2020-04-22 17:35:20 +02:00
const res = await request
.post(`/api/${table._id}/rows`)
2020-04-24 19:02:51 +02:00
.send({
2020-05-14 16:12:30 +02:00
_id: existing._id,
_rev: existing._rev,
tableId: table._id,
2020-04-24 19:02:51 +02:00
name: "Updated Name",
})
2021-03-04 14:07:33 +01:00
.set(config.defaultHeaders())
2020-04-22 17:35:20 +02:00
.expect('Content-Type', /json/)
.expect(200)
expect(res.res.statusMessage).toEqual(`${table.name} updated successfully.`)
2020-05-14 16:12:30 +02:00
expect(res.body.name).toEqual("Updated Name")
2022-05-11 12:32:53 +02:00
await assertRowUsage(rowUsage)
await assertQueryUsage(queryUsage + 1)
2020-04-22 17:35:20 +02:00
})
it("should load a row", async () => {
2021-03-04 14:07:33 +01:00
const existing = await config.createRow()
2022-05-11 12:32:53 +02:00
const queryUsage = await getQueryUsage()
2020-04-24 19:02:51 +02:00
2020-04-22 17:35:20 +02:00
const res = await request
.get(`/api/${table._id}/rows/${existing._id}`)
2021-03-04 14:07:33 +01:00
.set(config.defaultHeaders())
2020-04-22 17:35:20 +02:00
.expect('Content-Type', /json/)
.expect(200)
2020-04-24 19:02:51 +02:00
expect(res.body).toEqual({
...row,
2020-05-14 16:12:30 +02:00
_id: existing._id,
_rev: existing._rev,
type: "row",
2020-04-24 19:02:51 +02:00
})
2022-05-11 12:32:53 +02:00
await assertQueryUsage(queryUsage + 1)
2020-04-22 17:35:20 +02:00
})
it("should list all rows for given tableId", async () => {
const newRow = {
tableId: table._id,
2020-04-24 19:02:51 +02:00
name: "Second Contact",
status: "new"
}
2021-03-04 14:07:33 +01:00
await config.createRow()
await config.createRow(newRow)
2022-05-11 12:32:53 +02:00
const queryUsage = await getQueryUsage()
2020-04-22 17:35:20 +02:00
const res = await request
.get(`/api/${table._id}/rows`)
2021-03-04 14:07:33 +01:00
.set(config.defaultHeaders())
2020-04-22 17:35:20 +02:00
.expect('Content-Type', /json/)
.expect(200)
2020-05-14 16:12:30 +02:00
expect(res.body.length).toBe(2)
expect(res.body.find(r => r.name === newRow.name)).toBeDefined()
expect(res.body.find(r => r.name === row.name)).toBeDefined()
2022-05-11 12:32:53 +02:00
await assertQueryUsage(queryUsage + 1)
2020-04-22 17:35:20 +02:00
})
it("load should return 404 when row does not exist", async () => {
2021-03-04 14:07:33 +01:00
await config.createRow()
2022-05-11 12:32:53 +02:00
const queryUsage = await getQueryUsage()
2020-05-27 18:23:01 +02:00
await request
.get(`/api/${table._id}/rows/not-a-valid-id`)
2021-03-04 14:07:33 +01:00
.set(config.defaultHeaders())
2020-06-01 23:25:44 +02:00
.expect('Content-Type', /json/)
2020-04-22 17:35:20 +02:00
.expect(404)
2022-05-11 12:32:53 +02:00
await assertQueryUsage(queryUsage) // no change
2020-04-22 17:35:20 +02:00
})
it("row values are coerced", async () => {
const str = {type:"string", constraints: { type: "string", presence: false }}
const attachment = {type:"attachment", constraints: { type: "array", presence: false }}
const bool = {type:"boolean", constraints: { type: "boolean", presence: false }}
const number = {type:"number", constraints: { type: "number", presence: false }}
const datetime = {type:"datetime", constraints: { type: "string", presence: false, datetime: {earliest:"", latest: ""} }}
2021-03-04 14:07:33 +01:00
table = await config.createTable({
name: "TestTable2",
type: "table",
key: "name",
schema: {
name: str,
stringUndefined: str,
stringNull: str,
stringString: str,
numberEmptyString: number,
numberNull: number,
numberUndefined: number,
numberString: number,
2021-06-07 15:08:49 +02:00
numberNumber: number,
datetimeEmptyString: datetime,
datetimeNull: datetime,
datetimeUndefined: datetime,
datetimeString: datetime,
datetimeDate: datetime,
boolNull: bool,
boolEmpty: bool,
boolUndefined: bool,
boolString: bool,
boolBool: bool,
attachmentNull : attachment,
attachmentUndefined : attachment,
attachmentEmpty : attachment,
},
})
row = {
name: "Test Row",
stringUndefined: undefined,
stringNull: null,
stringString: "i am a string",
numberEmptyString: "",
numberNull: null,
numberUndefined: undefined,
numberString: "123",
numberNumber: 123,
datetimeEmptyString: "",
datetimeNull: null,
datetimeUndefined: undefined,
datetimeString: "1984-04-20T00:00:00.000Z",
datetimeDate: new Date("1984-04-20"),
boolNull: null,
boolEmpty: "",
boolUndefined: undefined,
boolString: "true",
boolBool: true,
tableId: table._id,
attachmentNull : null,
attachmentUndefined : undefined,
attachmentEmpty : "",
}
2021-03-04 14:07:33 +01:00
const id = (await config.createRow(row))._id
const saved = (await loadRow(id)).body
expect(saved.stringUndefined).toBe(undefined)
expect(saved.stringNull).toBe("")
expect(saved.stringString).toBe("i am a string")
expect(saved.numberEmptyString).toBe(null)
expect(saved.numberNull).toBe(null)
expect(saved.numberUndefined).toBe(undefined)
expect(saved.numberString).toBe(123)
expect(saved.numberNumber).toBe(123)
expect(saved.datetimeEmptyString).toBe(null)
expect(saved.datetimeNull).toBe(null)
expect(saved.datetimeUndefined).toBe(undefined)
expect(saved.datetimeString).toBe(new Date(row.datetimeString).toISOString())
expect(saved.datetimeDate).toBe(row.datetimeDate.toISOString())
expect(saved.boolNull).toBe(null)
expect(saved.boolEmpty).toBe(null)
expect(saved.boolUndefined).toBe(undefined)
expect(saved.boolString).toBe(true)
expect(saved.boolBool).toBe(true)
expect(saved.attachmentNull).toEqual([])
expect(saved.attachmentUndefined).toBe(undefined)
expect(saved.attachmentEmpty).toEqual([])
})
2020-04-22 17:35:20 +02:00
})
2020-05-28 16:39:29 +02:00
2020-09-10 10:36:14 +02:00
describe("patch", () => {
it("should update only the fields that are supplied", async () => {
2021-03-04 14:07:33 +01:00
const existing = await config.createRow()
2020-09-10 10:36:14 +02:00
2022-05-11 12:32:53 +02:00
const rowUsage = await getRowUsage()
const queryUsage = await getQueryUsage()
2020-09-10 10:36:14 +02:00
const res = await request
.patch(`/api/${table._id}/rows`)
2020-09-10 10:36:14 +02:00
.send({
_id: existing._id,
_rev: existing._rev,
tableId: table._id,
2020-09-10 10:36:14 +02:00
name: "Updated Name",
})
2021-03-04 14:07:33 +01:00
.set(config.defaultHeaders())
2020-09-10 10:36:14 +02:00
.expect('Content-Type', /json/)
.expect(200)
expect(res.res.statusMessage).toEqual(`${table.name} updated successfully.`)
2020-09-10 10:36:14 +02:00
expect(res.body.name).toEqual("Updated Name")
2020-09-11 10:29:23 +02:00
expect(res.body.description).toEqual(existing.description)
2020-09-10 10:36:14 +02:00
const savedRow = await loadRow(res.body._id)
2020-09-10 10:36:14 +02:00
expect(savedRow.body.description).toEqual(existing.description)
expect(savedRow.body.name).toEqual("Updated Name")
2022-05-11 12:32:53 +02:00
await assertRowUsage(rowUsage)
await assertQueryUsage(queryUsage + 2) // account for the second load
})
it("should throw an error when given improper types", async () => {
const existing = await config.createRow()
2022-05-11 12:32:53 +02:00
const rowUsage = await getRowUsage()
const queryUsage = await getQueryUsage()
await request
.patch(`/api/${table._id}/rows`)
.send({
_id: existing._id,
_rev: existing._rev,
tableId: table._id,
name: 1,
})
.set(config.defaultHeaders())
.expect(400)
2022-05-11 12:32:53 +02:00
await assertRowUsage(rowUsage)
await assertQueryUsage(queryUsage)
})
})
describe("destroy", () => {
it("should be able to delete a row", async () => {
const createdRow = await config.createRow(row)
2022-05-11 12:32:53 +02:00
const rowUsage = await getRowUsage()
const queryUsage = await getQueryUsage()
const res = await request
2021-06-17 17:52:52 +02:00
.delete(`/api/${table._id}/rows`)
.send({
rows: [
createdRow
]
})
.set(config.defaultHeaders())
.expect('Content-Type', /json/)
.expect(200)
2021-06-17 17:52:52 +02:00
expect(res.body[0]._id).toEqual(createdRow._id)
2022-05-11 12:32:53 +02:00
await assertRowUsage(rowUsage -1)
await assertQueryUsage(queryUsage +1)
2020-09-10 10:36:14 +02:00
})
})
2020-05-28 16:39:29 +02:00
describe("validate", () => {
it("should return no errors on valid row", async () => {
2022-05-11 12:32:53 +02:00
const rowUsage = await getRowUsage()
const queryUsage = await getQueryUsage()
const res = await request
.post(`/api/${table._id}/rows/validate`)
2020-05-28 16:39:29 +02:00
.send({ name: "ivan" })
2021-03-04 14:07:33 +01:00
.set(config.defaultHeaders())
2020-05-28 16:39:29 +02:00
.expect('Content-Type', /json/)
.expect(200)
expect(res.body.valid).toBe(true)
expect(Object.keys(res.body.errors)).toEqual([])
2022-05-11 12:32:53 +02:00
await assertRowUsage(rowUsage)
await assertQueryUsage(queryUsage)
2020-05-28 16:39:29 +02:00
})
it("should errors on invalid row", async () => {
2022-05-11 12:32:53 +02:00
const rowUsage = await getRowUsage()
const queryUsage = await getQueryUsage()
const res = await request
.post(`/api/${table._id}/rows/validate`)
2020-05-28 16:39:29 +02:00
.send({ name: 1 })
2021-03-04 14:07:33 +01:00
.set(config.defaultHeaders())
2020-05-28 16:39:29 +02:00
.expect('Content-Type', /json/)
.expect(200)
expect(res.body.valid).toBe(false)
expect(Object.keys(res.body.errors)).toEqual(["name"])
2022-05-11 12:32:53 +02:00
await assertRowUsage(rowUsage)
await assertQueryUsage(queryUsage)
2020-05-28 16:39:29 +02:00
})
})
describe("bulkDelete", () => {
it("should be able to delete a bulk set of rows", async () => {
const row1 = await config.createRow()
const row2 = await config.createRow()
2022-05-11 12:32:53 +02:00
const rowUsage = await getRowUsage()
const queryUsage = await getQueryUsage()
const res = await request
2021-06-17 17:52:52 +02:00
.delete(`/api/${table._id}/rows`)
.send({
rows: [
row1,
row2,
]
})
.set(config.defaultHeaders())
.expect('Content-Type', /json/)
.expect(200)
2022-05-11 12:32:53 +02:00
expect(res.body.length).toEqual(2)
await loadRow(row1._id, 404)
2022-05-11 12:32:53 +02:00
await assertRowUsage(rowUsage - 2)
await assertQueryUsage(queryUsage +1)
})
})
describe("fetchView", () => {
it("should be able to fetch tables contents via 'view'", async () => {
const row = await config.createRow()
2022-05-11 12:32:53 +02:00
const rowUsage = await getRowUsage()
const queryUsage = await getQueryUsage()
const res = await request
.get(`/api/views/${table._id}`)
.set(config.defaultHeaders())
.expect('Content-Type', /json/)
.expect(200)
expect(res.body.length).toEqual(1)
expect(res.body[0]._id).toEqual(row._id)
2022-05-11 12:32:53 +02:00
await assertRowUsage(rowUsage)
await assertQueryUsage(queryUsage +1)
})
it("should throw an error if view doesn't exist", async () => {
2022-05-11 12:32:53 +02:00
const rowUsage = await getRowUsage()
const queryUsage = await getQueryUsage()
await request
.get(`/api/views/derp`)
.set(config.defaultHeaders())
2021-09-30 13:55:21 +02:00
.expect(404)
2022-05-11 12:32:53 +02:00
await assertRowUsage(rowUsage)
await assertQueryUsage(queryUsage)
})
it("should be able to run on a view", async () => {
const view = await config.createView()
const row = await config.createRow()
2022-05-11 12:32:53 +02:00
const rowUsage = await getRowUsage()
const queryUsage = await getQueryUsage()
const res = await request
.get(`/api/views/${view.name}`)
.set(config.defaultHeaders())
.expect('Content-Type', /json/)
.expect(200)
expect(res.body.length).toEqual(1)
expect(res.body[0]._id).toEqual(row._id)
2022-05-11 12:32:53 +02:00
await assertRowUsage(rowUsage)
await assertQueryUsage(queryUsage + 1)
})
})
describe("fetchEnrichedRows", () => {
it("should allow enriching some linked rows", async () => {
const { table, firstRow, secondRow } = await doInTenant(setup.structures.TENANT_ID, async () => {
const table = await config.createLinkedTable()
const firstRow = await config.createRow({
name: "Test Contact",
description: "original description",
tableId: table._id
})
const secondRow = await config.createRow({
name: "Test 2",
description: "og desc",
link: [{_id: firstRow._id}],
tableId: table._id,
})
return { table, firstRow, secondRow }
2021-03-04 14:07:33 +01:00
})
2022-05-11 12:32:53 +02:00
const rowUsage = await getRowUsage()
const queryUsage = await getQueryUsage()
// test basic enrichment
const resBasic = await request
.get(`/api/${table._id}/rows/${secondRow._id}`)
.set(config.defaultHeaders())
.expect('Content-Type', /json/)
.expect(200)
expect(resBasic.body.link[0]._id).toBe(firstRow._id)
expect(resBasic.body.link[0].primaryDisplay).toBe("Test Contact")
// test full enrichment
const resEnriched = await request
.get(`/api/${table._id}/${secondRow._id}/enrich`)
.set(config.defaultHeaders())
.expect('Content-Type', /json/)
.expect(200)
expect(resEnriched.body.link.length).toBe(1)
expect(resEnriched.body.link[0]._id).toBe(firstRow._id)
expect(resEnriched.body.link[0].name).toBe("Test Contact")
expect(resEnriched.body.link[0].description).toBe("original description")
2022-05-11 12:32:53 +02:00
await assertRowUsage(rowUsage)
await assertQueryUsage(queryUsage +2)
})
})
describe("attachments", () => {
it("should allow enriching attachment rows", async () => {
const table = await config.createAttachmentTable()
const row = await config.createRow({
name: "test",
description: "test",
attachment: [{
key: `${config.getAppId()}/attachments/test/thing.csv`,
}],
tableId: table._id,
})
// the environment needs configured for this
await setup.switchToSelfHosted(async () => {
doInAppContext(config.getAppId(), async () => {
const enriched = await outputProcessing(table, [row])
expect(enriched[0].attachment[0].url).toBe(
`/prod-budi-app-assets/${config.getAppId()}/attachments/test/thing.csv`
)
})
})
2021-03-04 14:07:33 +01:00
})
})
2021-09-30 13:55:21 +02:00
})