Merge pull request #13157 from Budibase/test-api-unification
Unifying how we send requests to the server in packages/server tests.
This commit is contained in:
commit
7bbda49c69
|
@ -1,5 +1,6 @@
|
|||
import { APIError } from "@budibase/types"
|
||||
import * as errors from "../errors"
|
||||
import environment from "../environment"
|
||||
|
||||
export async function errorHandling(ctx: any, next: any) {
|
||||
try {
|
||||
|
@ -14,15 +15,19 @@ export async function errorHandling(ctx: any, next: any) {
|
|||
console.error(err)
|
||||
}
|
||||
|
||||
const error = errors.getPublicError(err)
|
||||
const body: APIError = {
|
||||
let error: APIError = {
|
||||
message: err.message,
|
||||
status: status,
|
||||
validationErrors: err.validation,
|
||||
error,
|
||||
error: errors.getPublicError(err),
|
||||
}
|
||||
|
||||
ctx.body = body
|
||||
if (environment.isTest() && ctx.headers["x-budibase-include-stacktrace"]) {
|
||||
// @ts-ignore
|
||||
error.stack = err.stack
|
||||
}
|
||||
|
||||
ctx.body = error
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,10 @@ import {
|
|||
GetResourcePermsResponse,
|
||||
ResourcePermissionInfo,
|
||||
GetDependantResourcesResponse,
|
||||
AddPermissionResponse,
|
||||
AddPermissionRequest,
|
||||
RemovePermissionRequest,
|
||||
RemovePermissionResponse,
|
||||
} from "@budibase/types"
|
||||
import { getRoleParams } from "../../db/utils"
|
||||
import {
|
||||
|
@ -16,9 +20,9 @@ import {
|
|||
import { removeFromArray } from "../../utilities"
|
||||
import sdk from "../../sdk"
|
||||
|
||||
const PermissionUpdateType = {
|
||||
REMOVE: "remove",
|
||||
ADD: "add",
|
||||
const enum PermissionUpdateType {
|
||||
REMOVE = "remove",
|
||||
ADD = "add",
|
||||
}
|
||||
|
||||
const SUPPORTED_LEVELS = CURRENTLY_SUPPORTED_LEVELS
|
||||
|
@ -39,7 +43,7 @@ async function updatePermissionOnRole(
|
|||
resourceId,
|
||||
level,
|
||||
}: { roleId: string; resourceId: string; level: PermissionLevel },
|
||||
updateType: string
|
||||
updateType: PermissionUpdateType
|
||||
) {
|
||||
const allowedAction = await sdk.permissions.resourceActionAllowed({
|
||||
resourceId,
|
||||
|
@ -107,11 +111,15 @@ async function updatePermissionOnRole(
|
|||
}
|
||||
|
||||
const response = await db.bulkDocs(docUpdates)
|
||||
return response.map((resp: any) => {
|
||||
return response.map(resp => {
|
||||
const version = docUpdates.find(role => role._id === resp.id)?.version
|
||||
resp._id = roles.getExternalRoleID(resp.id, version)
|
||||
delete resp.id
|
||||
return resp
|
||||
const _id = roles.getExternalRoleID(resp.id, version)
|
||||
return {
|
||||
_id,
|
||||
rev: resp.rev,
|
||||
error: resp.error,
|
||||
reason: resp.reason,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -189,13 +197,14 @@ export async function getDependantResources(
|
|||
}
|
||||
}
|
||||
|
||||
export async function addPermission(ctx: UserCtx) {
|
||||
ctx.body = await updatePermissionOnRole(ctx.params, PermissionUpdateType.ADD)
|
||||
export async function addPermission(ctx: UserCtx<void, AddPermissionResponse>) {
|
||||
const params: AddPermissionRequest = ctx.params
|
||||
ctx.body = await updatePermissionOnRole(params, PermissionUpdateType.ADD)
|
||||
}
|
||||
|
||||
export async function removePermission(ctx: UserCtx) {
|
||||
ctx.body = await updatePermissionOnRole(
|
||||
ctx.params,
|
||||
PermissionUpdateType.REMOVE
|
||||
)
|
||||
export async function removePermission(
|
||||
ctx: UserCtx<void, RemovePermissionResponse>
|
||||
) {
|
||||
const params: RemovePermissionRequest = ctx.params
|
||||
ctx.body = await updatePermissionOnRole(params, PermissionUpdateType.REMOVE)
|
||||
}
|
||||
|
|
|
@ -17,10 +17,12 @@ import {
|
|||
QueryPreview,
|
||||
QuerySchema,
|
||||
FieldType,
|
||||
type ExecuteQueryRequest,
|
||||
type ExecuteQueryResponse,
|
||||
type Row,
|
||||
ExecuteQueryRequest,
|
||||
ExecuteQueryResponse,
|
||||
Row,
|
||||
QueryParameter,
|
||||
PreviewQueryRequest,
|
||||
PreviewQueryResponse,
|
||||
} from "@budibase/types"
|
||||
import { ValidQueryNameRegex, utils as JsonUtils } from "@budibase/shared-core"
|
||||
|
||||
|
@ -134,14 +136,16 @@ function enrichParameters(
|
|||
return requestParameters
|
||||
}
|
||||
|
||||
export async function preview(ctx: UserCtx) {
|
||||
export async function preview(
|
||||
ctx: UserCtx<PreviewQueryRequest, PreviewQueryResponse>
|
||||
) {
|
||||
const { datasource, envVars } = await sdk.datasources.getWithEnvVars(
|
||||
ctx.request.body.datasourceId
|
||||
)
|
||||
const query: QueryPreview = ctx.request.body
|
||||
// preview may not have a queryId as it hasn't been saved, but if it does
|
||||
// this stops dynamic variables from calling the same query
|
||||
const { fields, parameters, queryVerb, transformer, queryId, schema } = query
|
||||
const { fields, parameters, queryVerb, transformer, queryId, schema } =
|
||||
ctx.request.body
|
||||
|
||||
let existingSchema = schema
|
||||
if (queryId && !existingSchema) {
|
||||
|
@ -266,9 +270,7 @@ export async function preview(ctx: UserCtx) {
|
|||
},
|
||||
}
|
||||
|
||||
const { rows, keys, info, extra } = (await Runner.run(
|
||||
inputs
|
||||
)) as QueryResponse
|
||||
const { rows, keys, info, extra } = await Runner.run<QueryResponse>(inputs)
|
||||
const { previewSchema, nestedSchemaFields } = getSchemaFields(rows, keys)
|
||||
|
||||
// if existing schema, update to include any previous schema keys
|
||||
|
@ -281,7 +283,7 @@ export async function preview(ctx: UserCtx) {
|
|||
}
|
||||
// remove configuration before sending event
|
||||
delete datasource.config
|
||||
await events.query.previewed(datasource, query)
|
||||
await events.query.previewed(datasource, ctx.request.body)
|
||||
ctx.body = {
|
||||
rows,
|
||||
nestedSchemaFields,
|
||||
|
@ -295,7 +297,10 @@ export async function preview(ctx: UserCtx) {
|
|||
}
|
||||
|
||||
async function execute(
|
||||
ctx: UserCtx<ExecuteQueryRequest, ExecuteQueryResponse | Row[]>,
|
||||
ctx: UserCtx<
|
||||
ExecuteQueryRequest,
|
||||
ExecuteQueryResponse | Record<string, any>[]
|
||||
>,
|
||||
opts: any = { rowsOnly: false, isAutomation: false }
|
||||
) {
|
||||
const db = context.getAppDB()
|
||||
|
@ -350,18 +355,23 @@ async function execute(
|
|||
}
|
||||
}
|
||||
|
||||
export async function executeV1(ctx: UserCtx) {
|
||||
export async function executeV1(
|
||||
ctx: UserCtx<ExecuteQueryRequest, Record<string, any>[]>
|
||||
) {
|
||||
return execute(ctx, { rowsOnly: true, isAutomation: false })
|
||||
}
|
||||
|
||||
export async function executeV2(
|
||||
ctx: UserCtx,
|
||||
ctx: UserCtx<
|
||||
ExecuteQueryRequest,
|
||||
ExecuteQueryResponse | Record<string, any>[]
|
||||
>,
|
||||
{ isAutomation }: { isAutomation?: boolean } = {}
|
||||
) {
|
||||
return execute(ctx, { rowsOnly: false, isAutomation })
|
||||
}
|
||||
|
||||
const removeDynamicVariables = async (queryId: any) => {
|
||||
const removeDynamicVariables = async (queryId: string) => {
|
||||
const db = context.getAppDB()
|
||||
const query = await db.get<Query>(queryId)
|
||||
const datasource = await sdk.datasources.get(query.datasourceId)
|
||||
|
@ -384,7 +394,7 @@ const removeDynamicVariables = async (queryId: any) => {
|
|||
|
||||
export async function destroy(ctx: UserCtx) {
|
||||
const db = context.getAppDB()
|
||||
const queryId = ctx.params.queryId
|
||||
const queryId = ctx.params.queryId as string
|
||||
await removeDynamicVariables(queryId)
|
||||
const query = await db.get<Query>(queryId)
|
||||
const datasource = await sdk.datasources.get(query.datasourceId)
|
||||
|
|
|
@ -211,7 +211,7 @@ export async function validate(ctx: Ctx<Row, ValidateResponse>) {
|
|||
}
|
||||
}
|
||||
|
||||
export async function fetchEnrichedRow(ctx: any) {
|
||||
export async function fetchEnrichedRow(ctx: UserCtx<void, Row>) {
|
||||
const tableId = utils.getTableId(ctx)
|
||||
ctx.body = await pickApi(tableId).fetchEnrichedRow(ctx)
|
||||
}
|
||||
|
|
|
@ -184,7 +184,7 @@ describe("/applications", () => {
|
|||
it("app should not sync if production", async () => {
|
||||
const { message } = await config.api.application.sync(
|
||||
app.appId.replace("_dev", ""),
|
||||
{ statusCode: 400 }
|
||||
{ status: 400 }
|
||||
)
|
||||
|
||||
expect(message).toEqual(
|
||||
|
|
|
@ -29,7 +29,7 @@ describe("/api/applications/:appId/sync", () => {
|
|||
let resp = (await config.api.attachment.process(
|
||||
"ohno.exe",
|
||||
Buffer.from([0]),
|
||||
{ expectStatus: 400 }
|
||||
{ status: 400 }
|
||||
)) as unknown as APIError
|
||||
expect(resp.message).toContain("invalid extension")
|
||||
})
|
||||
|
@ -40,7 +40,7 @@ describe("/api/applications/:appId/sync", () => {
|
|||
let resp = (await config.api.attachment.process(
|
||||
"OHNO.EXE",
|
||||
Buffer.from([0]),
|
||||
{ expectStatus: 400 }
|
||||
{ status: 400 }
|
||||
)) as unknown as APIError
|
||||
expect(resp.message).toContain("invalid extension")
|
||||
})
|
||||
|
@ -51,7 +51,7 @@ describe("/api/applications/:appId/sync", () => {
|
|||
undefined as any,
|
||||
undefined as any,
|
||||
{
|
||||
expectStatus: 400,
|
||||
status: 400,
|
||||
}
|
||||
)) as unknown as APIError
|
||||
expect(resp.message).toContain("No file provided")
|
||||
|
|
|
@ -19,11 +19,8 @@ describe("/backups", () => {
|
|||
|
||||
describe("/api/backups/export", () => {
|
||||
it("should be able to export app", async () => {
|
||||
const { body, headers } = await config.api.backup.exportBasicBackup(
|
||||
config.getAppId()!
|
||||
)
|
||||
const body = await config.api.backup.exportBasicBackup(config.getAppId()!)
|
||||
expect(body instanceof Buffer).toBe(true)
|
||||
expect(headers["content-type"]).toEqual("application/gzip")
|
||||
expect(events.app.exported).toBeCalledTimes(1)
|
||||
})
|
||||
|
||||
|
@ -38,15 +35,13 @@ describe("/backups", () => {
|
|||
it("should infer the app name from the app", async () => {
|
||||
tk.freeze(mocks.date.MOCK_DATE)
|
||||
|
||||
const { headers } = await config.api.backup.exportBasicBackup(
|
||||
config.getAppId()!
|
||||
)
|
||||
|
||||
expect(headers["content-disposition"]).toEqual(
|
||||
`attachment; filename="${
|
||||
config.getApp().name
|
||||
}-export-${mocks.date.MOCK_DATE.getTime()}.tar.gz"`
|
||||
)
|
||||
await config.api.backup.exportBasicBackup(config.getAppId()!, {
|
||||
headers: {
|
||||
"content-disposition": `attachment; filename="${
|
||||
config.getApp().name
|
||||
}-export-${mocks.date.MOCK_DATE.getTime()}.tar.gz"`,
|
||||
},
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
|
|
|
@ -45,7 +45,7 @@ describe("/permission", () => {
|
|||
table = (await config.createTable()) as typeof table
|
||||
row = await config.createRow()
|
||||
view = await config.api.viewV2.create({ tableId: table._id })
|
||||
perms = await config.api.permission.set({
|
||||
perms = await config.api.permission.add({
|
||||
roleId: STD_ROLE_ID,
|
||||
resourceId: table._id,
|
||||
level: PermissionLevel.READ,
|
||||
|
@ -88,13 +88,13 @@ describe("/permission", () => {
|
|||
})
|
||||
|
||||
it("should get resource permissions with multiple roles", async () => {
|
||||
perms = await config.api.permission.set({
|
||||
perms = await config.api.permission.add({
|
||||
roleId: HIGHER_ROLE_ID,
|
||||
resourceId: table._id,
|
||||
level: PermissionLevel.WRITE,
|
||||
})
|
||||
const res = await config.api.permission.get(table._id)
|
||||
expect(res.body).toEqual({
|
||||
expect(res).toEqual({
|
||||
permissions: {
|
||||
read: { permissionType: "EXPLICIT", role: STD_ROLE_ID },
|
||||
write: { permissionType: "EXPLICIT", role: HIGHER_ROLE_ID },
|
||||
|
@ -117,16 +117,19 @@ describe("/permission", () => {
|
|||
level: PermissionLevel.READ,
|
||||
})
|
||||
|
||||
const response = await config.api.permission.set(
|
||||
await config.api.permission.add(
|
||||
{
|
||||
roleId: STD_ROLE_ID,
|
||||
resourceId: table._id,
|
||||
level: PermissionLevel.EXECUTE,
|
||||
},
|
||||
{ expectStatus: 403 }
|
||||
)
|
||||
expect(response.message).toEqual(
|
||||
"You are not allowed to 'read' the resource type 'datasource'"
|
||||
{
|
||||
status: 403,
|
||||
body: {
|
||||
message:
|
||||
"You are not allowed to 'read' the resource type 'datasource'",
|
||||
},
|
||||
}
|
||||
)
|
||||
})
|
||||
})
|
||||
|
@ -138,9 +141,9 @@ describe("/permission", () => {
|
|||
resourceId: table._id,
|
||||
level: PermissionLevel.READ,
|
||||
})
|
||||
expect(res.body[0]._id).toEqual(STD_ROLE_ID)
|
||||
expect(res[0]._id).toEqual(STD_ROLE_ID)
|
||||
const permsRes = await config.api.permission.get(table._id)
|
||||
expect(permsRes.body[STD_ROLE_ID]).toBeUndefined()
|
||||
expect(permsRes.permissions[STD_ROLE_ID]).toBeUndefined()
|
||||
})
|
||||
|
||||
it("throw forbidden if the action is not allowed for the resource", async () => {
|
||||
|
@ -156,10 +159,13 @@ describe("/permission", () => {
|
|||
resourceId: table._id,
|
||||
level: PermissionLevel.EXECUTE,
|
||||
},
|
||||
{ expectStatus: 403 }
|
||||
)
|
||||
expect(response.body.message).toEqual(
|
||||
"You are not allowed to 'read' the resource type 'datasource'"
|
||||
{
|
||||
status: 403,
|
||||
body: {
|
||||
message:
|
||||
"You are not allowed to 'read' the resource type 'datasource'",
|
||||
},
|
||||
}
|
||||
)
|
||||
})
|
||||
})
|
||||
|
@ -181,10 +187,8 @@ describe("/permission", () => {
|
|||
// replicate changes before checking permissions
|
||||
await config.publish()
|
||||
|
||||
const res = await config.api.viewV2.search(view.id, undefined, {
|
||||
usePublicUser: true,
|
||||
})
|
||||
expect(res.body.rows[0]._id).toEqual(row._id)
|
||||
const res = await config.api.viewV2.publicSearch(view.id)
|
||||
expect(res.rows[0]._id).toEqual(row._id)
|
||||
})
|
||||
|
||||
it("should not be able to access the view data when the table is not public and there are no view permissions overrides", async () => {
|
||||
|
@ -196,14 +200,11 @@ describe("/permission", () => {
|
|||
// replicate changes before checking permissions
|
||||
await config.publish()
|
||||
|
||||
await config.api.viewV2.search(view.id, undefined, {
|
||||
expectStatus: 403,
|
||||
usePublicUser: true,
|
||||
})
|
||||
await config.api.viewV2.publicSearch(view.id, undefined, { status: 403 })
|
||||
})
|
||||
|
||||
it("should ignore the view permissions if the flag is not on", async () => {
|
||||
await config.api.permission.set({
|
||||
await config.api.permission.add({
|
||||
roleId: STD_ROLE_ID,
|
||||
resourceId: view.id,
|
||||
level: PermissionLevel.READ,
|
||||
|
@ -216,15 +217,14 @@ describe("/permission", () => {
|
|||
// replicate changes before checking permissions
|
||||
await config.publish()
|
||||
|
||||
await config.api.viewV2.search(view.id, undefined, {
|
||||
expectStatus: 403,
|
||||
usePublicUser: true,
|
||||
await config.api.viewV2.publicSearch(view.id, undefined, {
|
||||
status: 403,
|
||||
})
|
||||
})
|
||||
|
||||
it("should use the view permissions if the flag is on", async () => {
|
||||
mocks.licenses.useViewPermissions()
|
||||
await config.api.permission.set({
|
||||
await config.api.permission.add({
|
||||
roleId: STD_ROLE_ID,
|
||||
resourceId: view.id,
|
||||
level: PermissionLevel.READ,
|
||||
|
@ -237,10 +237,8 @@ describe("/permission", () => {
|
|||
// replicate changes before checking permissions
|
||||
await config.publish()
|
||||
|
||||
const res = await config.api.viewV2.search(view.id, undefined, {
|
||||
usePublicUser: true,
|
||||
})
|
||||
expect(res.body.rows[0]._id).toEqual(row._id)
|
||||
const res = await config.api.viewV2.publicSearch(view.id)
|
||||
expect(res.rows[0]._id).toEqual(row._id)
|
||||
})
|
||||
|
||||
it("shouldn't allow writing from a public user", async () => {
|
||||
|
@ -277,7 +275,7 @@ describe("/permission", () => {
|
|||
|
||||
const res = await config.api.permission.get(legacyView.name)
|
||||
|
||||
expect(res.body).toEqual({
|
||||
expect(res).toEqual({
|
||||
permissions: {
|
||||
read: {
|
||||
permissionType: "BASE",
|
||||
|
|
|
@ -397,15 +397,16 @@ describe("/queries", () => {
|
|||
})
|
||||
|
||||
it("should fail with invalid integration type", async () => {
|
||||
const response = await config.api.datasource.create(
|
||||
{
|
||||
...basicDatasource().datasource,
|
||||
source: "INVALID_INTEGRATION" as SourceName,
|
||||
const datasource: Datasource = {
|
||||
...basicDatasource().datasource,
|
||||
source: "INVALID_INTEGRATION" as SourceName,
|
||||
}
|
||||
await config.api.datasource.create(datasource, {
|
||||
status: 500,
|
||||
body: {
|
||||
message: "No datasource implementation found.",
|
||||
},
|
||||
{ expectStatus: 500, rawResponse: true }
|
||||
)
|
||||
|
||||
expect(response.body.message).toBe("No datasource implementation found.")
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
|
|
|
@ -93,7 +93,7 @@ describe("/roles", () => {
|
|||
|
||||
it("should be able to get the role with a permission added", async () => {
|
||||
const table = await config.createTable()
|
||||
await config.api.permission.set({
|
||||
await config.api.permission.add({
|
||||
roleId: BUILTIN_ROLE_IDS.POWER,
|
||||
resourceId: table._id,
|
||||
level: PermissionLevel.READ,
|
||||
|
|
|
@ -7,6 +7,7 @@ import { context, InternalTable, roles, tenancy } from "@budibase/backend-core"
|
|||
import { quotas } from "@budibase/pro"
|
||||
import {
|
||||
AutoFieldSubType,
|
||||
DeleteRow,
|
||||
FieldSchema,
|
||||
FieldType,
|
||||
FieldTypeSubtypes,
|
||||
|
@ -106,9 +107,6 @@ describe.each([
|
|||
mocks.licenses.useCloudFree()
|
||||
})
|
||||
|
||||
const loadRow = (id: string, tbl_Id: string, status = 200) =>
|
||||
config.api.row.get(tbl_Id, id, { expectStatus: status })
|
||||
|
||||
const getRowUsage = async () => {
|
||||
const { total } = await config.doInContext(undefined, () =>
|
||||
quotas.getCurrentUsageValues(QuotaUsageType.STATIC, StaticQuotaName.ROWS)
|
||||
|
@ -235,7 +233,7 @@ describe.each([
|
|||
|
||||
const res = await config.api.row.get(tableId, existing._id!)
|
||||
|
||||
expect(res.body).toEqual({
|
||||
expect(res).toEqual({
|
||||
...existing,
|
||||
...defaultRowFields,
|
||||
})
|
||||
|
@ -265,7 +263,7 @@ describe.each([
|
|||
await config.createRow()
|
||||
|
||||
await config.api.row.get(tableId, "1234567", {
|
||||
expectStatus: 404,
|
||||
status: 404,
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -395,7 +393,7 @@ describe.each([
|
|||
const createdRow = await config.createRow(row)
|
||||
const id = createdRow._id!
|
||||
|
||||
const saved = (await loadRow(id, table._id!)).body
|
||||
const saved = await config.api.row.get(table._id!, id)
|
||||
|
||||
expect(saved.stringUndefined).toBe(undefined)
|
||||
expect(saved.stringNull).toBe(null)
|
||||
|
@ -476,8 +474,8 @@ describe.each([
|
|||
)
|
||||
|
||||
const row = await config.api.row.get(table._id!, createRowResponse._id!)
|
||||
expect(row.body.Story).toBeUndefined()
|
||||
expect(row.body).toEqual({
|
||||
expect(row.Story).toBeUndefined()
|
||||
expect(row).toEqual({
|
||||
...defaultRowFields,
|
||||
OrderID: 1111,
|
||||
Country: "Aussy",
|
||||
|
@ -524,10 +522,10 @@ describe.each([
|
|||
expect(row.name).toEqual("Updated Name")
|
||||
expect(row.description).toEqual(existing.description)
|
||||
|
||||
const savedRow = await loadRow(row._id!, table._id!)
|
||||
const savedRow = await config.api.row.get(table._id!, row._id!)
|
||||
|
||||
expect(savedRow.body.description).toEqual(existing.description)
|
||||
expect(savedRow.body.name).toEqual("Updated Name")
|
||||
expect(savedRow.description).toEqual(existing.description)
|
||||
expect(savedRow.name).toEqual("Updated Name")
|
||||
await assertRowUsage(rowUsage)
|
||||
})
|
||||
|
||||
|
@ -543,7 +541,7 @@ describe.each([
|
|||
tableId: table._id!,
|
||||
name: 1,
|
||||
},
|
||||
{ expectStatus: 400 }
|
||||
{ status: 400 }
|
||||
)
|
||||
|
||||
await assertRowUsage(rowUsage)
|
||||
|
@ -582,8 +580,8 @@ describe.each([
|
|||
})
|
||||
|
||||
let getResp = await config.api.row.get(table._id!, row._id!)
|
||||
expect(getResp.body.user1[0]._id).toEqual(user1._id)
|
||||
expect(getResp.body.user2[0]._id).toEqual(user2._id)
|
||||
expect(getResp.user1[0]._id).toEqual(user1._id)
|
||||
expect(getResp.user2[0]._id).toEqual(user2._id)
|
||||
|
||||
let patchResp = await config.api.row.patch(table._id!, {
|
||||
_id: row._id!,
|
||||
|
@ -595,8 +593,8 @@ describe.each([
|
|||
expect(patchResp.user2[0]._id).toEqual(user2._id)
|
||||
|
||||
getResp = await config.api.row.get(table._id!, row._id!)
|
||||
expect(getResp.body.user1[0]._id).toEqual(user2._id)
|
||||
expect(getResp.body.user2[0]._id).toEqual(user2._id)
|
||||
expect(getResp.user1[0]._id).toEqual(user2._id)
|
||||
expect(getResp.user2[0]._id).toEqual(user2._id)
|
||||
})
|
||||
|
||||
it("should be able to update relationships when both columns are same name", async () => {
|
||||
|
@ -609,7 +607,7 @@ describe.each([
|
|||
description: "test",
|
||||
relationship: [row._id],
|
||||
})
|
||||
row = (await config.api.row.get(table._id!, row._id!)).body
|
||||
row = await config.api.row.get(table._id!, row._id!)
|
||||
expect(row.relationship.length).toBe(1)
|
||||
const resp = await config.api.row.patch(table._id!, {
|
||||
_id: row._id!,
|
||||
|
@ -632,8 +630,10 @@ describe.each([
|
|||
const createdRow = await config.createRow()
|
||||
const rowUsage = await getRowUsage()
|
||||
|
||||
const res = await config.api.row.delete(table._id!, [createdRow])
|
||||
expect(res.body[0]._id).toEqual(createdRow._id)
|
||||
const res = await config.api.row.bulkDelete(table._id!, {
|
||||
rows: [createdRow],
|
||||
})
|
||||
expect(res[0]._id).toEqual(createdRow._id)
|
||||
await assertRowUsage(rowUsage - 1)
|
||||
})
|
||||
})
|
||||
|
@ -682,10 +682,12 @@ describe.each([
|
|||
const row2 = await config.createRow()
|
||||
const rowUsage = await getRowUsage()
|
||||
|
||||
const res = await config.api.row.delete(table._id!, [row1, row2])
|
||||
const res = await config.api.row.bulkDelete(table._id!, {
|
||||
rows: [row1, row2],
|
||||
})
|
||||
|
||||
expect(res.body.length).toEqual(2)
|
||||
await loadRow(row1._id!, table._id!, 404)
|
||||
expect(res.length).toEqual(2)
|
||||
await config.api.row.get(table._id!, row1._id!, { status: 404 })
|
||||
await assertRowUsage(rowUsage - 2)
|
||||
})
|
||||
|
||||
|
@ -697,14 +699,12 @@ describe.each([
|
|||
])
|
||||
const rowUsage = await getRowUsage()
|
||||
|
||||
const res = await config.api.row.delete(table._id!, [
|
||||
row1,
|
||||
row2._id,
|
||||
{ _id: row3._id },
|
||||
])
|
||||
const res = await config.api.row.bulkDelete(table._id!, {
|
||||
rows: [row1, row2._id!, { _id: row3._id }],
|
||||
})
|
||||
|
||||
expect(res.body.length).toEqual(3)
|
||||
await loadRow(row1._id!, table._id!, 404)
|
||||
expect(res.length).toEqual(3)
|
||||
await config.api.row.get(table._id!, row1._id!, { status: 404 })
|
||||
await assertRowUsage(rowUsage - 3)
|
||||
})
|
||||
|
||||
|
@ -712,34 +712,36 @@ describe.each([
|
|||
const row1 = await config.createRow()
|
||||
const rowUsage = await getRowUsage()
|
||||
|
||||
const res = await config.api.row.delete(table._id!, row1)
|
||||
const res = await config.api.row.delete(table._id!, row1 as DeleteRow)
|
||||
|
||||
expect(res.body.id).toEqual(row1._id)
|
||||
await loadRow(row1._id!, table._id!, 404)
|
||||
expect(res.id).toEqual(row1._id)
|
||||
await config.api.row.get(table._id!, row1._id!, { status: 404 })
|
||||
await assertRowUsage(rowUsage - 1)
|
||||
})
|
||||
|
||||
it("Should ignore malformed/invalid delete requests", async () => {
|
||||
const rowUsage = await getRowUsage()
|
||||
|
||||
const res = await config.api.row.delete(
|
||||
table._id!,
|
||||
{ not: "valid" },
|
||||
{ expectStatus: 400 }
|
||||
)
|
||||
expect(res.body.message).toEqual("Invalid delete rows request")
|
||||
|
||||
const res2 = await config.api.row.delete(
|
||||
table._id!,
|
||||
{ rows: 123 },
|
||||
{ expectStatus: 400 }
|
||||
)
|
||||
expect(res2.body.message).toEqual("Invalid delete rows request")
|
||||
|
||||
const res3 = await config.api.row.delete(table._id!, "invalid", {
|
||||
expectStatus: 400,
|
||||
await config.api.row.delete(table._id!, { not: "valid" } as any, {
|
||||
status: 400,
|
||||
body: {
|
||||
message: "Invalid delete rows request",
|
||||
},
|
||||
})
|
||||
|
||||
await config.api.row.delete(table._id!, { rows: 123 } as any, {
|
||||
status: 400,
|
||||
body: {
|
||||
message: "Invalid delete rows request",
|
||||
},
|
||||
})
|
||||
|
||||
await config.api.row.delete(table._id!, "invalid" as any, {
|
||||
status: 400,
|
||||
body: {
|
||||
message: "Invalid delete rows request",
|
||||
},
|
||||
})
|
||||
expect(res3.body.message).toEqual("Invalid delete rows request")
|
||||
|
||||
await assertRowUsage(rowUsage)
|
||||
})
|
||||
|
@ -757,16 +759,16 @@ describe.each([
|
|||
const row = await config.createRow()
|
||||
const rowUsage = await getRowUsage()
|
||||
|
||||
const res = await config.api.legacyView.get(table._id!)
|
||||
expect(res.body.length).toEqual(1)
|
||||
expect(res.body[0]._id).toEqual(row._id)
|
||||
const rows = await config.api.legacyView.get(table._id!)
|
||||
expect(rows.length).toEqual(1)
|
||||
expect(rows[0]._id).toEqual(row._id)
|
||||
await assertRowUsage(rowUsage)
|
||||
})
|
||||
|
||||
it("should throw an error if view doesn't exist", async () => {
|
||||
const rowUsage = await getRowUsage()
|
||||
|
||||
await config.api.legacyView.get("derp", { expectStatus: 404 })
|
||||
await config.api.legacyView.get("derp", { status: 404 })
|
||||
|
||||
await assertRowUsage(rowUsage)
|
||||
})
|
||||
|
@ -781,9 +783,9 @@ describe.each([
|
|||
const row = await config.createRow()
|
||||
const rowUsage = await getRowUsage()
|
||||
|
||||
const res = await config.api.legacyView.get(view.name)
|
||||
expect(res.body.length).toEqual(1)
|
||||
expect(res.body[0]._id).toEqual(row._id)
|
||||
const rows = await config.api.legacyView.get(view.name)
|
||||
expect(rows.length).toEqual(1)
|
||||
expect(rows[0]._id).toEqual(row._id)
|
||||
|
||||
await assertRowUsage(rowUsage)
|
||||
})
|
||||
|
@ -841,8 +843,8 @@ describe.each([
|
|||
linkedTable._id!,
|
||||
secondRow._id!
|
||||
)
|
||||
expect(resBasic.body.link.length).toBe(1)
|
||||
expect(resBasic.body.link[0]).toEqual({
|
||||
expect(resBasic.link.length).toBe(1)
|
||||
expect(resBasic.link[0]).toEqual({
|
||||
_id: firstRow._id,
|
||||
primaryDisplay: firstRow.name,
|
||||
})
|
||||
|
@ -852,10 +854,10 @@ describe.each([
|
|||
linkedTable._id!,
|
||||
secondRow._id!
|
||||
)
|
||||
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")
|
||||
expect(resEnriched.link.length).toBe(1)
|
||||
expect(resEnriched.link[0]._id).toBe(firstRow._id)
|
||||
expect(resEnriched.link[0].name).toBe("Test Contact")
|
||||
expect(resEnriched.link[0].description).toBe("original description")
|
||||
await assertRowUsage(rowUsage)
|
||||
})
|
||||
})
|
||||
|
@ -903,7 +905,7 @@ describe.each([
|
|||
const res = await config.api.row.exportRows(table._id!, {
|
||||
rows: [existing._id!],
|
||||
})
|
||||
const results = JSON.parse(res.text)
|
||||
const results = JSON.parse(res)
|
||||
expect(results.length).toEqual(1)
|
||||
const row = results[0]
|
||||
|
||||
|
@ -922,7 +924,7 @@ describe.each([
|
|||
rows: [existing._id!],
|
||||
columns: ["_id"],
|
||||
})
|
||||
const results = JSON.parse(res.text)
|
||||
const results = JSON.parse(res)
|
||||
expect(results.length).toEqual(1)
|
||||
const row = results[0]
|
||||
|
||||
|
@ -1000,7 +1002,7 @@ describe.each([
|
|||
})
|
||||
|
||||
const row = await config.api.row.get(table._id!, newRow._id!)
|
||||
expect(row.body).toEqual({
|
||||
expect(row).toEqual({
|
||||
name: data.name,
|
||||
surname: data.surname,
|
||||
address: data.address,
|
||||
|
@ -1010,9 +1012,9 @@ describe.each([
|
|||
id: newRow.id,
|
||||
...defaultRowFields,
|
||||
})
|
||||
expect(row.body._viewId).toBeUndefined()
|
||||
expect(row.body.age).toBeUndefined()
|
||||
expect(row.body.jobTitle).toBeUndefined()
|
||||
expect(row._viewId).toBeUndefined()
|
||||
expect(row.age).toBeUndefined()
|
||||
expect(row.jobTitle).toBeUndefined()
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -1042,7 +1044,7 @@ describe.each([
|
|||
})
|
||||
|
||||
const row = await config.api.row.get(tableId, newRow._id!)
|
||||
expect(row.body).toEqual({
|
||||
expect(row).toEqual({
|
||||
...newRow,
|
||||
name: newData.name,
|
||||
address: newData.address,
|
||||
|
@ -1051,9 +1053,9 @@ describe.each([
|
|||
id: newRow.id,
|
||||
...defaultRowFields,
|
||||
})
|
||||
expect(row.body._viewId).toBeUndefined()
|
||||
expect(row.body.age).toBeUndefined()
|
||||
expect(row.body.jobTitle).toBeUndefined()
|
||||
expect(row._viewId).toBeUndefined()
|
||||
expect(row.age).toBeUndefined()
|
||||
expect(row.jobTitle).toBeUndefined()
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -1071,12 +1073,12 @@ describe.each([
|
|||
const createdRow = await config.createRow()
|
||||
const rowUsage = await getRowUsage()
|
||||
|
||||
await config.api.row.delete(view.id, [createdRow])
|
||||
await config.api.row.bulkDelete(view.id, { rows: [createdRow] })
|
||||
|
||||
await assertRowUsage(rowUsage - 1)
|
||||
|
||||
await config.api.row.get(tableId, createdRow._id!, {
|
||||
expectStatus: 404,
|
||||
status: 404,
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -1097,17 +1099,17 @@ describe.each([
|
|||
])
|
||||
const rowUsage = await getRowUsage()
|
||||
|
||||
await config.api.row.delete(view.id, [rows[0], rows[2]])
|
||||
await config.api.row.bulkDelete(view.id, { rows: [rows[0], rows[2]] })
|
||||
|
||||
await assertRowUsage(rowUsage - 2)
|
||||
|
||||
await config.api.row.get(tableId, rows[0]._id!, {
|
||||
expectStatus: 404,
|
||||
status: 404,
|
||||
})
|
||||
await config.api.row.get(tableId, rows[2]._id!, {
|
||||
expectStatus: 404,
|
||||
status: 404,
|
||||
})
|
||||
await config.api.row.get(tableId, rows[1]._id!, { expectStatus: 200 })
|
||||
await config.api.row.get(tableId, rows[1]._id!, { status: 200 })
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -1154,8 +1156,8 @@ describe.each([
|
|||
const createViewResponse = await config.createView()
|
||||
const response = await config.api.viewV2.search(createViewResponse.id)
|
||||
|
||||
expect(response.body.rows).toHaveLength(10)
|
||||
expect(response.body).toEqual({
|
||||
expect(response.rows).toHaveLength(10)
|
||||
expect(response).toEqual({
|
||||
rows: expect.arrayContaining(
|
||||
rows.map(r => ({
|
||||
_viewId: createViewResponse.id,
|
||||
|
@ -1206,8 +1208,8 @@ describe.each([
|
|||
|
||||
const response = await config.api.viewV2.search(createViewResponse.id)
|
||||
|
||||
expect(response.body.rows).toHaveLength(5)
|
||||
expect(response.body).toEqual({
|
||||
expect(response.rows).toHaveLength(5)
|
||||
expect(response).toEqual({
|
||||
rows: expect.arrayContaining(
|
||||
expectedRows.map(r => ({
|
||||
_viewId: createViewResponse.id,
|
||||
|
@ -1328,8 +1330,8 @@ describe.each([
|
|||
createViewResponse.id
|
||||
)
|
||||
|
||||
expect(response.body.rows).toHaveLength(4)
|
||||
expect(response.body.rows).toEqual(
|
||||
expect(response.rows).toHaveLength(4)
|
||||
expect(response.rows).toEqual(
|
||||
expected.map(name => expect.objectContaining({ name }))
|
||||
)
|
||||
}
|
||||
|
@ -1357,8 +1359,8 @@ describe.each([
|
|||
}
|
||||
)
|
||||
|
||||
expect(response.body.rows).toHaveLength(4)
|
||||
expect(response.body.rows).toEqual(
|
||||
expect(response.rows).toHaveLength(4)
|
||||
expect(response.rows).toEqual(
|
||||
expected.map(name => expect.objectContaining({ name }))
|
||||
)
|
||||
}
|
||||
|
@ -1382,8 +1384,8 @@ describe.each([
|
|||
})
|
||||
const response = await config.api.viewV2.search(view.id)
|
||||
|
||||
expect(response.body.rows).toHaveLength(10)
|
||||
expect(response.body.rows).toEqual(
|
||||
expect(response.rows).toHaveLength(10)
|
||||
expect(response.rows).toEqual(
|
||||
expect.arrayContaining(
|
||||
rows.map(r => ({
|
||||
...(isInternal
|
||||
|
@ -1402,7 +1404,7 @@ describe.each([
|
|||
const createViewResponse = await config.createView()
|
||||
const response = await config.api.viewV2.search(createViewResponse.id)
|
||||
|
||||
expect(response.body.rows).toHaveLength(0)
|
||||
expect(response.rows).toHaveLength(0)
|
||||
})
|
||||
|
||||
it("respects the limit parameter", async () => {
|
||||
|
@ -1417,7 +1419,7 @@ describe.each([
|
|||
query: {},
|
||||
})
|
||||
|
||||
expect(response.body.rows).toHaveLength(limit)
|
||||
expect(response.rows).toHaveLength(limit)
|
||||
})
|
||||
|
||||
it("can handle pagination", async () => {
|
||||
|
@ -1426,7 +1428,7 @@ describe.each([
|
|||
|
||||
const createViewResponse = await config.createView()
|
||||
const allRows = (await config.api.viewV2.search(createViewResponse.id))
|
||||
.body.rows
|
||||
.rows
|
||||
|
||||
const firstPageResponse = await config.api.viewV2.search(
|
||||
createViewResponse.id,
|
||||
|
@ -1436,7 +1438,7 @@ describe.each([
|
|||
query: {},
|
||||
}
|
||||
)
|
||||
expect(firstPageResponse.body).toEqual({
|
||||
expect(firstPageResponse).toEqual({
|
||||
rows: expect.arrayContaining(allRows.slice(0, 4)),
|
||||
totalRows: isInternal ? 10 : undefined,
|
||||
hasNextPage: true,
|
||||
|
@ -1448,12 +1450,12 @@ describe.each([
|
|||
{
|
||||
paginate: true,
|
||||
limit: 4,
|
||||
bookmark: firstPageResponse.body.bookmark,
|
||||
bookmark: firstPageResponse.bookmark,
|
||||
|
||||
query: {},
|
||||
}
|
||||
)
|
||||
expect(secondPageResponse.body).toEqual({
|
||||
expect(secondPageResponse).toEqual({
|
||||
rows: expect.arrayContaining(allRows.slice(4, 8)),
|
||||
totalRows: isInternal ? 10 : undefined,
|
||||
hasNextPage: true,
|
||||
|
@ -1465,11 +1467,11 @@ describe.each([
|
|||
{
|
||||
paginate: true,
|
||||
limit: 4,
|
||||
bookmark: secondPageResponse.body.bookmark,
|
||||
bookmark: secondPageResponse.bookmark,
|
||||
query: {},
|
||||
}
|
||||
)
|
||||
expect(lastPageResponse.body).toEqual({
|
||||
expect(lastPageResponse).toEqual({
|
||||
rows: expect.arrayContaining(allRows.slice(8)),
|
||||
totalRows: isInternal ? 10 : undefined,
|
||||
hasNextPage: false,
|
||||
|
@ -1489,7 +1491,7 @@ describe.each([
|
|||
email: "joe@joe.com",
|
||||
roles: {},
|
||||
},
|
||||
{ expectStatus: 400 }
|
||||
{ status: 400 }
|
||||
)
|
||||
expect(response.message).toBe("Cannot create new user entry.")
|
||||
})
|
||||
|
@ -1516,58 +1518,52 @@ describe.each([
|
|||
|
||||
it("does not allow public users to fetch by default", async () => {
|
||||
await config.publish()
|
||||
await config.api.viewV2.search(viewId, undefined, {
|
||||
expectStatus: 403,
|
||||
usePublicUser: true,
|
||||
await config.api.viewV2.publicSearch(viewId, undefined, {
|
||||
status: 403,
|
||||
})
|
||||
})
|
||||
|
||||
it("allow public users to fetch when permissions are explicit", async () => {
|
||||
await config.api.permission.set({
|
||||
await config.api.permission.add({
|
||||
roleId: roles.BUILTIN_ROLE_IDS.PUBLIC,
|
||||
level: PermissionLevel.READ,
|
||||
resourceId: viewId,
|
||||
})
|
||||
await config.publish()
|
||||
|
||||
const response = await config.api.viewV2.search(viewId, undefined, {
|
||||
usePublicUser: true,
|
||||
})
|
||||
const response = await config.api.viewV2.publicSearch(viewId)
|
||||
|
||||
expect(response.body.rows).toHaveLength(10)
|
||||
expect(response.rows).toHaveLength(10)
|
||||
})
|
||||
|
||||
it("allow public users to fetch when permissions are inherited", async () => {
|
||||
await config.api.permission.set({
|
||||
await config.api.permission.add({
|
||||
roleId: roles.BUILTIN_ROLE_IDS.PUBLIC,
|
||||
level: PermissionLevel.READ,
|
||||
resourceId: tableId,
|
||||
})
|
||||
await config.publish()
|
||||
|
||||
const response = await config.api.viewV2.search(viewId, undefined, {
|
||||
usePublicUser: true,
|
||||
})
|
||||
const response = await config.api.viewV2.publicSearch(viewId)
|
||||
|
||||
expect(response.body.rows).toHaveLength(10)
|
||||
expect(response.rows).toHaveLength(10)
|
||||
})
|
||||
|
||||
it("respects inherited permissions, not allowing not public views from public tables", async () => {
|
||||
await config.api.permission.set({
|
||||
await config.api.permission.add({
|
||||
roleId: roles.BUILTIN_ROLE_IDS.PUBLIC,
|
||||
level: PermissionLevel.READ,
|
||||
resourceId: tableId,
|
||||
})
|
||||
await config.api.permission.set({
|
||||
await config.api.permission.add({
|
||||
roleId: roles.BUILTIN_ROLE_IDS.POWER,
|
||||
level: PermissionLevel.READ,
|
||||
resourceId: viewId,
|
||||
})
|
||||
await config.publish()
|
||||
|
||||
await config.api.viewV2.search(viewId, undefined, {
|
||||
usePublicUser: true,
|
||||
expectStatus: 403,
|
||||
await config.api.viewV2.publicSearch(viewId, undefined, {
|
||||
status: 403,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@ -1754,7 +1750,7 @@ describe.each([
|
|||
}
|
||||
const row = await config.api.row.save(tableId, rowData)
|
||||
|
||||
const { body: retrieved } = await config.api.row.get(tableId, row._id!)
|
||||
const retrieved = await config.api.row.get(tableId, row._id!)
|
||||
expect(retrieved).toEqual({
|
||||
name: rowData.name,
|
||||
description: rowData.description,
|
||||
|
@ -1781,7 +1777,7 @@ describe.each([
|
|||
}
|
||||
const row = await config.api.row.save(tableId, rowData)
|
||||
|
||||
const { body: retrieved } = await config.api.row.get(tableId, row._id!)
|
||||
const retrieved = await config.api.row.get(tableId, row._id!)
|
||||
expect(retrieved).toEqual({
|
||||
name: rowData.name,
|
||||
description: rowData.description,
|
||||
|
|
|
@ -663,8 +663,7 @@ describe("/tables", () => {
|
|||
expect(migratedTable.schema["user column"]).toBeDefined()
|
||||
expect(migratedTable.schema["user relationship"]).not.toBeDefined()
|
||||
|
||||
const resp = await config.api.row.get(table._id!, testRow._id!)
|
||||
const migratedRow = resp.body as Row
|
||||
const migratedRow = await config.api.row.get(table._id!, testRow._id!)
|
||||
|
||||
expect(migratedRow["user column"]).toBeDefined()
|
||||
expect(migratedRow["user relationship"]).not.toBeDefined()
|
||||
|
@ -716,15 +715,13 @@ describe("/tables", () => {
|
|||
expect(migratedTable.schema["user column"]).toBeDefined()
|
||||
expect(migratedTable.schema["user relationship"]).not.toBeDefined()
|
||||
|
||||
const row1Migrated = (await config.api.row.get(table._id!, row1._id!))
|
||||
.body as Row
|
||||
const row1Migrated = await config.api.row.get(table._id!, row1._id!)
|
||||
expect(row1Migrated["user relationship"]).not.toBeDefined()
|
||||
expect(row1Migrated["user column"].map((r: Row) => r._id)).toEqual(
|
||||
expect.arrayContaining([users[0]._id, users[1]._id])
|
||||
)
|
||||
|
||||
const row2Migrated = (await config.api.row.get(table._id!, row2._id!))
|
||||
.body as Row
|
||||
const row2Migrated = await config.api.row.get(table._id!, row2._id!)
|
||||
expect(row2Migrated["user relationship"]).not.toBeDefined()
|
||||
expect(row2Migrated["user column"].map((r: Row) => r._id)).toEqual(
|
||||
expect.arrayContaining([users[1]._id, users[2]._id])
|
||||
|
@ -773,15 +770,13 @@ describe("/tables", () => {
|
|||
expect(migratedTable.schema["user column"]).toBeDefined()
|
||||
expect(migratedTable.schema["user relationship"]).not.toBeDefined()
|
||||
|
||||
const row1Migrated = (await config.api.row.get(table._id!, row1._id!))
|
||||
.body as Row
|
||||
const row1Migrated = await config.api.row.get(table._id!, row1._id!)
|
||||
expect(row1Migrated["user relationship"]).not.toBeDefined()
|
||||
expect(row1Migrated["user column"].map((r: Row) => r._id)).toEqual(
|
||||
expect.arrayContaining([users[0]._id, users[1]._id])
|
||||
)
|
||||
|
||||
const row2Migrated = (await config.api.row.get(table._id!, row2._id!))
|
||||
.body as Row
|
||||
const row2Migrated = await config.api.row.get(table._id!, row2._id!)
|
||||
expect(row2Migrated["user relationship"]).not.toBeDefined()
|
||||
expect(row2Migrated["user column"].map((r: Row) => r._id)).toEqual([
|
||||
users[2]._id,
|
||||
|
@ -831,7 +826,7 @@ describe("/tables", () => {
|
|||
subtype: FieldSubtype.USERS,
|
||||
},
|
||||
},
|
||||
{ expectStatus: 400 }
|
||||
{ status: 400 }
|
||||
)
|
||||
})
|
||||
|
||||
|
@ -846,7 +841,7 @@ describe("/tables", () => {
|
|||
subtype: FieldSubtype.USERS,
|
||||
},
|
||||
},
|
||||
{ expectStatus: 400 }
|
||||
{ status: 400 }
|
||||
)
|
||||
})
|
||||
|
||||
|
@ -861,7 +856,7 @@ describe("/tables", () => {
|
|||
subtype: FieldSubtype.USERS,
|
||||
},
|
||||
},
|
||||
{ expectStatus: 400 }
|
||||
{ status: 400 }
|
||||
)
|
||||
})
|
||||
|
||||
|
@ -880,7 +875,7 @@ describe("/tables", () => {
|
|||
subtype: FieldSubtype.USERS,
|
||||
},
|
||||
},
|
||||
{ expectStatus: 400 }
|
||||
{ status: 400 }
|
||||
)
|
||||
})
|
||||
})
|
||||
|
|
|
@ -90,7 +90,7 @@ describe("/users", () => {
|
|||
})
|
||||
await config.api.user.update(
|
||||
{ ...user, roleId: roles.BUILTIN_ROLE_IDS.POWER },
|
||||
{ expectStatus: 409 }
|
||||
{ status: 409 }
|
||||
)
|
||||
})
|
||||
})
|
||||
|
|
|
@ -177,7 +177,7 @@ describe.each([
|
|||
}
|
||||
|
||||
await config.api.viewV2.create(newView, {
|
||||
expectStatus: 201,
|
||||
status: 201,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@ -275,7 +275,7 @@ describe.each([
|
|||
const tableId = table._id!
|
||||
await config.api.viewV2.update(
|
||||
{ ...view, id: generator.guid() },
|
||||
{ expectStatus: 404 }
|
||||
{ status: 404 }
|
||||
)
|
||||
|
||||
expect(await config.api.table.get(tableId)).toEqual(
|
||||
|
@ -304,7 +304,7 @@ describe.each([
|
|||
},
|
||||
],
|
||||
},
|
||||
{ expectStatus: 404 }
|
||||
{ status: 404 }
|
||||
)
|
||||
|
||||
expect(await config.api.table.get(tableId)).toEqual(
|
||||
|
@ -326,12 +326,10 @@ describe.each([
|
|||
...viewV1,
|
||||
},
|
||||
{
|
||||
expectStatus: 400,
|
||||
handleResponse: r => {
|
||||
expect(r.body).toEqual({
|
||||
message: "Only views V2 can be updated",
|
||||
status: 400,
|
||||
})
|
||||
status: 400,
|
||||
body: {
|
||||
message: "Only views V2 can be updated",
|
||||
status: 400,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
@ -403,7 +401,7 @@ describe.each([
|
|||
} as Record<string, FieldSchema>,
|
||||
},
|
||||
{
|
||||
expectStatus: 200,
|
||||
status: 200,
|
||||
}
|
||||
)
|
||||
})
|
||||
|
|
|
@ -30,9 +30,9 @@ describe("migrations", () => {
|
|||
|
||||
const appId = config.getAppId()
|
||||
|
||||
const response = await config.api.application.getRaw(appId)
|
||||
|
||||
expect(response.headers[Header.MIGRATING_APP]).toBeUndefined()
|
||||
await config.api.application.get(appId, {
|
||||
headersNotPresent: [Header.MIGRATING_APP],
|
||||
})
|
||||
})
|
||||
|
||||
it("accessing an app that has pending migrations will attach the migrating header", async () => {
|
||||
|
@ -46,8 +46,10 @@ describe("migrations", () => {
|
|||
func: async () => {},
|
||||
})
|
||||
|
||||
const response = await config.api.application.getRaw(appId)
|
||||
|
||||
expect(response.headers[Header.MIGRATING_APP]).toEqual(appId)
|
||||
await config.api.application.get(appId, {
|
||||
headers: {
|
||||
[Header.MIGRATING_APP]: appId,
|
||||
},
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -24,7 +24,7 @@ describe("test the create row action", () => {
|
|||
expect(res.id).toBeDefined()
|
||||
expect(res.revision).toBeDefined()
|
||||
expect(res.success).toEqual(true)
|
||||
const gottenRow = await config.getRow(table._id, res.id)
|
||||
const gottenRow = await config.api.row.get(table._id, res.id)
|
||||
expect(gottenRow.name).toEqual("test")
|
||||
expect(gottenRow.description).toEqual("test")
|
||||
})
|
||||
|
|
|
@ -36,7 +36,7 @@ describe("test the update row action", () => {
|
|||
it("should be able to run the action", async () => {
|
||||
const res = await setup.runStep(setup.actions.UPDATE_ROW.stepId, inputs)
|
||||
expect(res.success).toEqual(true)
|
||||
const updatedRow = await config.getRow(table._id!, res.id)
|
||||
const updatedRow = await config.api.row.get(table._id!, res.id)
|
||||
expect(updatedRow.name).toEqual("Updated name")
|
||||
expect(updatedRow.description).not.toEqual("")
|
||||
})
|
||||
|
@ -87,8 +87,8 @@ describe("test the update row action", () => {
|
|||
})
|
||||
|
||||
let getResp = await config.api.row.get(table._id!, row._id!)
|
||||
expect(getResp.body.user1[0]._id).toEqual(user1._id)
|
||||
expect(getResp.body.user2[0]._id).toEqual(user2._id)
|
||||
expect(getResp.user1[0]._id).toEqual(user1._id)
|
||||
expect(getResp.user2[0]._id).toEqual(user2._id)
|
||||
|
||||
let stepResp = await setup.runStep(setup.actions.UPDATE_ROW.stepId, {
|
||||
rowId: row._id,
|
||||
|
@ -103,8 +103,8 @@ describe("test the update row action", () => {
|
|||
expect(stepResp.success).toEqual(true)
|
||||
|
||||
getResp = await config.api.row.get(table._id!, row._id!)
|
||||
expect(getResp.body.user1[0]._id).toEqual(user2._id)
|
||||
expect(getResp.body.user2[0]._id).toEqual(user2._id)
|
||||
expect(getResp.user1[0]._id).toEqual(user2._id)
|
||||
expect(getResp.user2[0]._id).toEqual(user2._id)
|
||||
})
|
||||
|
||||
it("should overwrite links if those links are not set and we ask it do", async () => {
|
||||
|
@ -140,8 +140,8 @@ describe("test the update row action", () => {
|
|||
})
|
||||
|
||||
let getResp = await config.api.row.get(table._id!, row._id!)
|
||||
expect(getResp.body.user1[0]._id).toEqual(user1._id)
|
||||
expect(getResp.body.user2[0]._id).toEqual(user2._id)
|
||||
expect(getResp.user1[0]._id).toEqual(user1._id)
|
||||
expect(getResp.user2[0]._id).toEqual(user2._id)
|
||||
|
||||
let stepResp = await setup.runStep(setup.actions.UPDATE_ROW.stepId, {
|
||||
rowId: row._id,
|
||||
|
@ -163,7 +163,7 @@ describe("test the update row action", () => {
|
|||
expect(stepResp.success).toEqual(true)
|
||||
|
||||
getResp = await config.api.row.get(table._id!, row._id!)
|
||||
expect(getResp.body.user1[0]._id).toEqual(user2._id)
|
||||
expect(getResp.body.user2).toBeUndefined()
|
||||
expect(getResp.user1[0]._id).toEqual(user2._id)
|
||||
expect(getResp.user2).toBeUndefined()
|
||||
})
|
||||
})
|
||||
|
|
|
@ -100,7 +100,7 @@ describe("test the link controller", () => {
|
|||
const { _id } = await config.createRow(
|
||||
basicLinkedRow(t1._id!, row._id!, linkField)
|
||||
)
|
||||
return config.getRow(t1._id!, _id!)
|
||||
return config.api.row.get(t1._id!, _id!)
|
||||
}
|
||||
|
||||
it("should be able to confirm if two table schemas are equal", async () => {
|
||||
|
|
|
@ -398,7 +398,7 @@ describe("postgres integrations", () => {
|
|||
expect(res.status).toBe(200)
|
||||
expect(res.body).toEqual(updatedRow)
|
||||
|
||||
const persistedRow = await config.getRow(
|
||||
const persistedRow = await config.api.row.get(
|
||||
primaryPostgresTable._id!,
|
||||
row.id
|
||||
)
|
||||
|
@ -1040,28 +1040,37 @@ describe("postgres integrations", () => {
|
|||
|
||||
describe("POST /api/datasources/verify", () => {
|
||||
it("should be able to verify the connection", async () => {
|
||||
const response = await config.api.datasource.verify({
|
||||
datasource: await databaseTestProviders.postgres.datasource(),
|
||||
})
|
||||
expect(response.status).toBe(200)
|
||||
expect(response.body.connected).toBe(true)
|
||||
await config.api.datasource.verify(
|
||||
{
|
||||
datasource: await databaseTestProviders.postgres.datasource(),
|
||||
},
|
||||
{
|
||||
body: {
|
||||
connected: true,
|
||||
},
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
it("should state an invalid datasource cannot connect", async () => {
|
||||
const dbConfig = await databaseTestProviders.postgres.datasource()
|
||||
const response = await config.api.datasource.verify({
|
||||
datasource: {
|
||||
...dbConfig,
|
||||
config: {
|
||||
...dbConfig.config,
|
||||
password: "wrongpassword",
|
||||
await config.api.datasource.verify(
|
||||
{
|
||||
datasource: {
|
||||
...dbConfig,
|
||||
config: {
|
||||
...dbConfig.config,
|
||||
password: "wrongpassword",
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
expect(response.status).toBe(200)
|
||||
expect(response.body.connected).toBe(false)
|
||||
expect(response.body.error).toBeDefined()
|
||||
{
|
||||
body: {
|
||||
connected: false,
|
||||
error: 'password authentication failed for user "postgres"',
|
||||
},
|
||||
}
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
|
|
|
@ -98,7 +98,10 @@ describe("sdk >> rows >> internal", () => {
|
|||
},
|
||||
})
|
||||
|
||||
const persistedRow = await config.getRow(table._id!, response.row._id!)
|
||||
const persistedRow = await config.api.row.get(
|
||||
table._id!,
|
||||
response.row._id!
|
||||
)
|
||||
expect(persistedRow).toEqual({
|
||||
...row,
|
||||
type: "row",
|
||||
|
@ -157,7 +160,10 @@ describe("sdk >> rows >> internal", () => {
|
|||
},
|
||||
})
|
||||
|
||||
const persistedRow = await config.getRow(table._id!, response.row._id!)
|
||||
const persistedRow = await config.api.row.get(
|
||||
table._id!,
|
||||
response.row._id!
|
||||
)
|
||||
expect(persistedRow).toEqual({
|
||||
...row,
|
||||
type: "row",
|
||||
|
|
|
@ -712,11 +712,6 @@ export default class TestConfiguration {
|
|||
return this.api.row.save(tableId, config)
|
||||
}
|
||||
|
||||
async getRow(tableId: string, rowId: string): Promise<Row> {
|
||||
const res = await this.api.row.get(tableId, rowId)
|
||||
return res.body
|
||||
}
|
||||
|
||||
async getRows(tableId: string) {
|
||||
if (!tableId && this.table) {
|
||||
tableId = this.table._id!
|
||||
|
|
|
@ -1,193 +1,133 @@
|
|||
import { Response } from "supertest"
|
||||
import {
|
||||
App,
|
||||
PublishResponse,
|
||||
type CreateAppRequest,
|
||||
type FetchAppDefinitionResponse,
|
||||
type FetchAppPackageResponse,
|
||||
} from "@budibase/types"
|
||||
import TestConfiguration from "../TestConfiguration"
|
||||
import { TestAPI } from "./base"
|
||||
import { Expectations, TestAPI } from "./base"
|
||||
import { AppStatus } from "../../../db/utils"
|
||||
import { constants } from "@budibase/backend-core"
|
||||
|
||||
export class ApplicationAPI extends TestAPI {
|
||||
constructor(config: TestConfiguration) {
|
||||
super(config)
|
||||
create = async (
|
||||
app: CreateAppRequest,
|
||||
expectations?: Expectations
|
||||
): Promise<App> => {
|
||||
const files = app.templateFile ? { templateFile: app.templateFile } : {}
|
||||
delete app.templateFile
|
||||
return await this._post<App>("/api/applications", {
|
||||
fields: app,
|
||||
files,
|
||||
expectations,
|
||||
})
|
||||
}
|
||||
|
||||
create = async (app: CreateAppRequest): Promise<App> => {
|
||||
const request = this.request
|
||||
.post("/api/applications")
|
||||
.set(this.config.defaultHeaders())
|
||||
.expect("Content-Type", /json/)
|
||||
|
||||
for (const key of Object.keys(app)) {
|
||||
request.field(key, (app as any)[key])
|
||||
}
|
||||
|
||||
if (app.templateFile) {
|
||||
request.attach("templateFile", app.templateFile)
|
||||
}
|
||||
|
||||
const result = await request
|
||||
|
||||
if (result.statusCode !== 200) {
|
||||
throw new Error(JSON.stringify(result.body))
|
||||
}
|
||||
|
||||
return result.body as App
|
||||
delete = async (
|
||||
appId: string,
|
||||
expectations?: Expectations
|
||||
): Promise<void> => {
|
||||
await this._delete(`/api/applications/${appId}`, { expectations })
|
||||
}
|
||||
|
||||
delete = async (appId: string): Promise<void> => {
|
||||
await this.request
|
||||
.delete(`/api/applications/${appId}`)
|
||||
.set(this.config.defaultHeaders())
|
||||
.expect(200)
|
||||
}
|
||||
|
||||
publish = async (
|
||||
appId: string
|
||||
): Promise<{ _id: string; status: string; appUrl: string }> => {
|
||||
// While the publish endpoint does take an :appId parameter, it doesn't
|
||||
// use it. It uses the appId from the context.
|
||||
let headers = {
|
||||
...this.config.defaultHeaders(),
|
||||
[constants.Header.APP_ID]: appId,
|
||||
}
|
||||
const result = await this.request
|
||||
.post(`/api/applications/${appId}/publish`)
|
||||
.set(headers)
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(200)
|
||||
return result.body as { _id: string; status: string; appUrl: string }
|
||||
publish = async (appId: string): Promise<PublishResponse> => {
|
||||
return await this._post<PublishResponse>(
|
||||
`/api/applications/${appId}/publish`,
|
||||
{
|
||||
// While the publish endpoint does take an :appId parameter, it doesn't
|
||||
// use it. It uses the appId from the context.
|
||||
headers: {
|
||||
[constants.Header.APP_ID]: appId,
|
||||
},
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
unpublish = async (appId: string): Promise<void> => {
|
||||
await this.request
|
||||
.post(`/api/applications/${appId}/unpublish`)
|
||||
.set(this.config.defaultHeaders())
|
||||
.expect(204)
|
||||
await this._post(`/api/applications/${appId}/unpublish`, {
|
||||
expectations: { status: 204 },
|
||||
})
|
||||
}
|
||||
|
||||
sync = async (
|
||||
appId: string,
|
||||
{ statusCode }: { statusCode: number } = { statusCode: 200 }
|
||||
expectations?: Expectations
|
||||
): Promise<{ message: string }> => {
|
||||
const result = await this.request
|
||||
.post(`/api/applications/${appId}/sync`)
|
||||
.set(this.config.defaultHeaders())
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(statusCode)
|
||||
return result.body
|
||||
return await this._post<{ message: string }>(
|
||||
`/api/applications/${appId}/sync`,
|
||||
{ expectations }
|
||||
)
|
||||
}
|
||||
|
||||
getRaw = async (appId: string): Promise<Response> => {
|
||||
// While the appPackage endpoint does take an :appId parameter, it doesn't
|
||||
// use it. It uses the appId from the context.
|
||||
let headers = {
|
||||
...this.config.defaultHeaders(),
|
||||
[constants.Header.APP_ID]: appId,
|
||||
}
|
||||
const result = await this.request
|
||||
.get(`/api/applications/${appId}/appPackage`)
|
||||
.set(headers)
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(200)
|
||||
return result
|
||||
}
|
||||
|
||||
get = async (appId: string): Promise<App> => {
|
||||
const result = await this.getRaw(appId)
|
||||
return result.body.application as App
|
||||
get = async (appId: string, expectations?: Expectations): Promise<App> => {
|
||||
return await this._get<App>(`/api/applications/${appId}`, {
|
||||
// While the get endpoint does take an :appId parameter, it doesn't use
|
||||
// it. It uses the appId from the context.
|
||||
headers: {
|
||||
[constants.Header.APP_ID]: appId,
|
||||
},
|
||||
expectations,
|
||||
})
|
||||
}
|
||||
|
||||
getDefinition = async (
|
||||
appId: string
|
||||
appId: string,
|
||||
expectations?: Expectations
|
||||
): Promise<FetchAppDefinitionResponse> => {
|
||||
const result = await this.request
|
||||
.get(`/api/applications/${appId}/definition`)
|
||||
.set(this.config.defaultHeaders())
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(200)
|
||||
return result.body as FetchAppDefinitionResponse
|
||||
return await this._get<FetchAppDefinitionResponse>(
|
||||
`/api/applications/${appId}/definition`,
|
||||
{ expectations }
|
||||
)
|
||||
}
|
||||
|
||||
getAppPackage = async (appId: string): Promise<FetchAppPackageResponse> => {
|
||||
const result = await this.request
|
||||
.get(`/api/applications/${appId}/appPackage`)
|
||||
.set(this.config.defaultHeaders())
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(200)
|
||||
return result.body
|
||||
getAppPackage = async (
|
||||
appId: string,
|
||||
expectations?: Expectations
|
||||
): Promise<FetchAppPackageResponse> => {
|
||||
return await this._get<FetchAppPackageResponse>(
|
||||
`/api/applications/${appId}/appPackage`,
|
||||
{ expectations }
|
||||
)
|
||||
}
|
||||
|
||||
update = async (
|
||||
appId: string,
|
||||
app: { name?: string; url?: string }
|
||||
app: { name?: string; url?: string },
|
||||
expectations?: Expectations
|
||||
): Promise<App> => {
|
||||
const request = this.request
|
||||
.put(`/api/applications/${appId}`)
|
||||
.set(this.config.defaultHeaders())
|
||||
.expect("Content-Type", /json/)
|
||||
|
||||
for (const key of Object.keys(app)) {
|
||||
request.field(key, (app as any)[key])
|
||||
}
|
||||
|
||||
const result = await request
|
||||
|
||||
if (result.statusCode !== 200) {
|
||||
throw new Error(JSON.stringify(result.body))
|
||||
}
|
||||
|
||||
return result.body as App
|
||||
return await this._put<App>(`/api/applications/${appId}`, {
|
||||
fields: app,
|
||||
expectations,
|
||||
})
|
||||
}
|
||||
|
||||
updateClient = async (appId: string): Promise<void> => {
|
||||
// While the updateClient endpoint does take an :appId parameter, it doesn't
|
||||
// use it. It uses the appId from the context.
|
||||
let headers = {
|
||||
...this.config.defaultHeaders(),
|
||||
[constants.Header.APP_ID]: appId,
|
||||
}
|
||||
const response = await this.request
|
||||
.post(`/api/applications/${appId}/client/update`)
|
||||
.set(headers)
|
||||
.expect("Content-Type", /json/)
|
||||
|
||||
if (response.statusCode !== 200) {
|
||||
throw new Error(JSON.stringify(response.body))
|
||||
}
|
||||
updateClient = async (
|
||||
appId: string,
|
||||
expectations?: Expectations
|
||||
): Promise<void> => {
|
||||
await this._post(`/api/applications/${appId}/client/update`, {
|
||||
// While the updateClient endpoint does take an :appId parameter, it doesn't
|
||||
// use it. It uses the appId from the context.
|
||||
headers: {
|
||||
[constants.Header.APP_ID]: appId,
|
||||
},
|
||||
expectations,
|
||||
})
|
||||
}
|
||||
|
||||
revertClient = async (appId: string): Promise<void> => {
|
||||
// While the revertClient endpoint does take an :appId parameter, it doesn't
|
||||
// use it. It uses the appId from the context.
|
||||
let headers = {
|
||||
...this.config.defaultHeaders(),
|
||||
[constants.Header.APP_ID]: appId,
|
||||
}
|
||||
const response = await this.request
|
||||
.post(`/api/applications/${appId}/client/revert`)
|
||||
.set(headers)
|
||||
.expect("Content-Type", /json/)
|
||||
|
||||
if (response.statusCode !== 200) {
|
||||
throw new Error(JSON.stringify(response.body))
|
||||
}
|
||||
await this._post(`/api/applications/${appId}/client/revert`, {
|
||||
// While the revertClient endpoint does take an :appId parameter, it doesn't
|
||||
// use it. It uses the appId from the context.
|
||||
headers: {
|
||||
[constants.Header.APP_ID]: appId,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
fetch = async ({ status }: { status?: AppStatus } = {}): Promise<App[]> => {
|
||||
let query = []
|
||||
if (status) {
|
||||
query.push(`status=${status}`)
|
||||
}
|
||||
|
||||
const result = await this.request
|
||||
.get(`/api/applications${query.length ? `?${query.join("&")}` : ""}`)
|
||||
.set(this.config.defaultHeaders())
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(200)
|
||||
return result.body as App[]
|
||||
return await this._get<App[]>("/api/applications", {
|
||||
query: { status },
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,35 +1,16 @@
|
|||
import {
|
||||
APIError,
|
||||
Datasource,
|
||||
ProcessAttachmentResponse,
|
||||
} from "@budibase/types"
|
||||
import TestConfiguration from "../TestConfiguration"
|
||||
import { TestAPI } from "./base"
|
||||
import { ProcessAttachmentResponse } from "@budibase/types"
|
||||
import { Expectations, TestAPI } from "./base"
|
||||
import fs from "fs"
|
||||
|
||||
export class AttachmentAPI extends TestAPI {
|
||||
constructor(config: TestConfiguration) {
|
||||
super(config)
|
||||
}
|
||||
|
||||
process = async (
|
||||
name: string,
|
||||
file: Buffer | fs.ReadStream | string,
|
||||
{ expectStatus } = { expectStatus: 200 }
|
||||
expectations?: Expectations
|
||||
): Promise<ProcessAttachmentResponse> => {
|
||||
const result = await this.request
|
||||
.post(`/api/attachments/process`)
|
||||
.attach("file", file, name)
|
||||
.set(this.config.defaultHeaders())
|
||||
|
||||
if (result.statusCode !== expectStatus) {
|
||||
throw new Error(
|
||||
`Expected status ${expectStatus} but got ${
|
||||
result.statusCode
|
||||
}, body: ${JSON.stringify(result.body)}`
|
||||
)
|
||||
}
|
||||
|
||||
return result.body
|
||||
return await this._post(`/api/attachments/process`, {
|
||||
files: { file: { name, file } },
|
||||
expectations,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,42 +2,38 @@ import {
|
|||
CreateAppBackupResponse,
|
||||
ImportAppBackupResponse,
|
||||
} from "@budibase/types"
|
||||
import TestConfiguration from "../TestConfiguration"
|
||||
import { TestAPI } from "./base"
|
||||
import { Expectations, TestAPI } from "./base"
|
||||
|
||||
export class BackupAPI extends TestAPI {
|
||||
constructor(config: TestConfiguration) {
|
||||
super(config)
|
||||
}
|
||||
|
||||
exportBasicBackup = async (appId: string) => {
|
||||
const result = await this.request
|
||||
.post(`/api/backups/export?appId=${appId}`)
|
||||
.set(this.config.defaultHeaders())
|
||||
.expect("Content-Type", /application\/gzip/)
|
||||
.expect(200)
|
||||
return {
|
||||
body: result.body as Buffer,
|
||||
headers: result.headers,
|
||||
exportBasicBackup = async (appId: string, expectations?: Expectations) => {
|
||||
const exp = {
|
||||
...expectations,
|
||||
headers: {
|
||||
...expectations?.headers,
|
||||
"Content-Type": "application/gzip",
|
||||
},
|
||||
}
|
||||
return await this._post<Buffer>(`/api/backups/export`, {
|
||||
query: { appId },
|
||||
expectations: exp,
|
||||
})
|
||||
}
|
||||
|
||||
createBackup = async (appId: string) => {
|
||||
const result = await this.request
|
||||
.post(`/api/apps/${appId}/backups`)
|
||||
.set(this.config.defaultHeaders())
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(200)
|
||||
return result.body as CreateAppBackupResponse
|
||||
createBackup = async (appId: string, expectations?: Expectations) => {
|
||||
return await this._post<CreateAppBackupResponse>(
|
||||
`/api/apps/${appId}/backups`,
|
||||
{ expectations }
|
||||
)
|
||||
}
|
||||
|
||||
waitForBackupToComplete = async (appId: string, backupId: string) => {
|
||||
for (let i = 0; i < 10; i++) {
|
||||
await new Promise(resolve => setTimeout(resolve, 1000))
|
||||
const result = await this.request
|
||||
.get(`/api/apps/${appId}/backups/${backupId}/file`)
|
||||
.set(this.config.defaultHeaders())
|
||||
if (result.status === 200) {
|
||||
const response = await this._requestRaw(
|
||||
"get",
|
||||
`/api/apps/${appId}/backups/${backupId}/file`
|
||||
)
|
||||
if (response.status === 200) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@ -46,13 +42,12 @@ export class BackupAPI extends TestAPI {
|
|||
|
||||
importBackup = async (
|
||||
appId: string,
|
||||
backupId: string
|
||||
backupId: string,
|
||||
expectations?: Expectations
|
||||
): Promise<ImportAppBackupResponse> => {
|
||||
const result = await this.request
|
||||
.post(`/api/apps/${appId}/backups/${backupId}/import`)
|
||||
.set(this.config.defaultHeaders())
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(200)
|
||||
return result.body as ImportAppBackupResponse
|
||||
return await this._post<ImportAppBackupResponse>(
|
||||
`/api/apps/${appId}/backups/${backupId}/import`,
|
||||
{ expectations }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,17 +1,196 @@
|
|||
import TestConfiguration from "../TestConfiguration"
|
||||
import { SuperTest, Test } from "supertest"
|
||||
import { SuperTest, Test, Response } from "supertest"
|
||||
import { ReadStream } from "fs"
|
||||
|
||||
export interface TestAPIOpts {
|
||||
headers?: any
|
||||
type Headers = Record<string, string | string[] | undefined>
|
||||
type Method = "get" | "post" | "put" | "patch" | "delete"
|
||||
|
||||
export interface AttachedFile {
|
||||
name: string
|
||||
file: Buffer | ReadStream | string
|
||||
}
|
||||
|
||||
function isAttachedFile(file: any): file is AttachedFile {
|
||||
if (file === undefined) {
|
||||
return false
|
||||
}
|
||||
const attachedFile = file as AttachedFile
|
||||
return (
|
||||
Object.hasOwnProperty.call(attachedFile, "file") &&
|
||||
Object.hasOwnProperty.call(attachedFile, "name")
|
||||
)
|
||||
}
|
||||
|
||||
export interface Expectations {
|
||||
status?: number
|
||||
headers?: Record<string, string | RegExp>
|
||||
headersNotPresent?: string[]
|
||||
body?: Record<string, any>
|
||||
}
|
||||
|
||||
export interface RequestOpts {
|
||||
headers?: Headers
|
||||
query?: Record<string, string | undefined>
|
||||
body?: Record<string, any>
|
||||
fields?: Record<string, any>
|
||||
files?: Record<
|
||||
string,
|
||||
Buffer | ReadStream | string | AttachedFile | undefined
|
||||
>
|
||||
expectations?: Expectations
|
||||
publicUser?: boolean
|
||||
}
|
||||
|
||||
export abstract class TestAPI {
|
||||
config: TestConfiguration
|
||||
request: SuperTest<Test>
|
||||
|
||||
protected constructor(config: TestConfiguration) {
|
||||
constructor(config: TestConfiguration) {
|
||||
this.config = config
|
||||
this.request = config.request!
|
||||
}
|
||||
|
||||
protected _get = async <T>(url: string, opts?: RequestOpts): Promise<T> => {
|
||||
return await this._request<T>("get", url, opts)
|
||||
}
|
||||
|
||||
protected _post = async <T>(url: string, opts?: RequestOpts): Promise<T> => {
|
||||
return await this._request<T>("post", url, opts)
|
||||
}
|
||||
|
||||
protected _put = async <T>(url: string, opts?: RequestOpts): Promise<T> => {
|
||||
return await this._request<T>("put", url, opts)
|
||||
}
|
||||
|
||||
protected _patch = async <T>(url: string, opts?: RequestOpts): Promise<T> => {
|
||||
return await this._request<T>("patch", url, opts)
|
||||
}
|
||||
|
||||
protected _delete = async <T>(
|
||||
url: string,
|
||||
opts?: RequestOpts
|
||||
): Promise<T> => {
|
||||
return await this._request<T>("delete", url, opts)
|
||||
}
|
||||
|
||||
protected _requestRaw = async (
|
||||
method: "get" | "post" | "put" | "patch" | "delete",
|
||||
url: string,
|
||||
opts?: RequestOpts
|
||||
): Promise<Response> => {
|
||||
const {
|
||||
headers = {},
|
||||
query = {},
|
||||
body,
|
||||
fields = {},
|
||||
files = {},
|
||||
expectations,
|
||||
publicUser = false,
|
||||
} = opts || {}
|
||||
const { status = 200 } = expectations || {}
|
||||
const expectHeaders = expectations?.headers || {}
|
||||
|
||||
if (status !== 204 && !expectHeaders["Content-Type"]) {
|
||||
expectHeaders["Content-Type"] = /^application\/json/
|
||||
}
|
||||
|
||||
let queryParams = []
|
||||
for (const [key, value] of Object.entries(query)) {
|
||||
if (value) {
|
||||
queryParams.push(`${key}=${value}`)
|
||||
}
|
||||
}
|
||||
if (queryParams.length) {
|
||||
url += `?${queryParams.join("&")}`
|
||||
}
|
||||
|
||||
const headersFn = publicUser
|
||||
? this.config.publicHeaders.bind(this.config)
|
||||
: this.config.defaultHeaders.bind(this.config)
|
||||
let request = this.request[method](url).set(
|
||||
headersFn({
|
||||
"x-budibase-include-stacktrace": "true",
|
||||
})
|
||||
)
|
||||
if (headers) {
|
||||
request = request.set(headers)
|
||||
}
|
||||
if (body) {
|
||||
request = request.send(body)
|
||||
}
|
||||
for (const [key, value] of Object.entries(fields)) {
|
||||
request = request.field(key, value)
|
||||
}
|
||||
|
||||
for (const [key, value] of Object.entries(files)) {
|
||||
if (isAttachedFile(value)) {
|
||||
request = request.attach(key, value.file, value.name)
|
||||
} else {
|
||||
request = request.attach(key, value as any)
|
||||
}
|
||||
}
|
||||
if (expectations?.headers) {
|
||||
for (const [key, value] of Object.entries(expectations.headers)) {
|
||||
if (value === undefined) {
|
||||
throw new Error(
|
||||
`Got an undefined expected value for header "${key}", if you want to check for the absence of a header, use headersNotPresent`
|
||||
)
|
||||
}
|
||||
request = request.expect(key, value as any)
|
||||
}
|
||||
}
|
||||
|
||||
return await request
|
||||
}
|
||||
|
||||
protected _request = async <T>(
|
||||
method: Method,
|
||||
url: string,
|
||||
opts?: RequestOpts
|
||||
): Promise<T> => {
|
||||
const { expectations } = opts || {}
|
||||
const { status = 200 } = expectations || {}
|
||||
|
||||
const response = await this._requestRaw(method, url, opts)
|
||||
|
||||
if (response.status !== status) {
|
||||
let message = `Expected status ${status} but got ${response.status}`
|
||||
|
||||
const stack = response.body.stack
|
||||
delete response.body.stack
|
||||
|
||||
if (response.body) {
|
||||
message += `\n\nBody:`
|
||||
const body = JSON.stringify(response.body, null, 2)
|
||||
for (const line of body.split("\n")) {
|
||||
message += `\n⏐ ${line}`
|
||||
}
|
||||
}
|
||||
|
||||
if (stack) {
|
||||
message += `\n\nStack from request handler:`
|
||||
for (const line of stack.split("\n")) {
|
||||
message += `\n⏐ ${line}`
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error(message)
|
||||
}
|
||||
|
||||
if (expectations?.headersNotPresent) {
|
||||
for (const header of expectations.headersNotPresent) {
|
||||
if (response.headers[header]) {
|
||||
throw new Error(
|
||||
`Expected header ${header} not to be present, found value "${response.headers[header]}"`
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (expectations?.body) {
|
||||
expect(response.body).toMatchObject(expectations.body)
|
||||
}
|
||||
|
||||
return response.body
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,63 +1,48 @@
|
|||
import {
|
||||
CreateDatasourceRequest,
|
||||
Datasource,
|
||||
VerifyDatasourceRequest,
|
||||
CreateDatasourceResponse,
|
||||
UpdateDatasourceResponse,
|
||||
UpdateDatasourceRequest,
|
||||
} from "@budibase/types"
|
||||
import TestConfiguration from "../TestConfiguration"
|
||||
import { TestAPI } from "./base"
|
||||
import supertest from "supertest"
|
||||
import { Expectations, TestAPI } from "./base"
|
||||
|
||||
export class DatasourceAPI extends TestAPI {
|
||||
constructor(config: TestConfiguration) {
|
||||
super(config)
|
||||
}
|
||||
|
||||
create = async <B extends boolean = false>(
|
||||
create = async (
|
||||
config: Datasource,
|
||||
{
|
||||
expectStatus,
|
||||
rawResponse,
|
||||
}: { expectStatus?: number; rawResponse?: B } = {}
|
||||
): Promise<B extends false ? Datasource : supertest.Response> => {
|
||||
const body: CreateDatasourceRequest = {
|
||||
datasource: config,
|
||||
tablesFilter: [],
|
||||
}
|
||||
const result = await this.request
|
||||
.post(`/api/datasources`)
|
||||
.send(body)
|
||||
.set(this.config.defaultHeaders())
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(expectStatus || 200)
|
||||
if (rawResponse) {
|
||||
return result as any
|
||||
}
|
||||
return result.body.datasource
|
||||
expectations?: Expectations
|
||||
): Promise<Datasource> => {
|
||||
const response = await this._post<CreateDatasourceResponse>(
|
||||
`/api/datasources`,
|
||||
{
|
||||
body: {
|
||||
datasource: config,
|
||||
tablesFilter: [],
|
||||
},
|
||||
expectations,
|
||||
}
|
||||
)
|
||||
return response.datasource
|
||||
}
|
||||
|
||||
update = async (
|
||||
datasource: Datasource,
|
||||
{ expectStatus } = { expectStatus: 200 }
|
||||
datasource: UpdateDatasourceRequest,
|
||||
expectations?: Expectations
|
||||
): Promise<Datasource> => {
|
||||
const result = await this.request
|
||||
.put(`/api/datasources/${datasource._id}`)
|
||||
.send(datasource)
|
||||
.set(this.config.defaultHeaders())
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(expectStatus)
|
||||
return result.body.datasource as Datasource
|
||||
const response = await this._put<UpdateDatasourceResponse>(
|
||||
`/api/datasources/${datasource._id}`,
|
||||
{ body: datasource, expectations }
|
||||
)
|
||||
return response.datasource
|
||||
}
|
||||
|
||||
verify = async (
|
||||
data: VerifyDatasourceRequest,
|
||||
{ expectStatus } = { expectStatus: 200 }
|
||||
expectations?: Expectations
|
||||
) => {
|
||||
const result = await this.request
|
||||
.post(`/api/datasources/verify`)
|
||||
.send(data)
|
||||
.set(this.config.defaultHeaders())
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(expectStatus)
|
||||
return result
|
||||
return await this._post(`/api/datasources/verify`, {
|
||||
body: data,
|
||||
expectations,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,16 +1,8 @@
|
|||
import TestConfiguration from "../TestConfiguration"
|
||||
import { TestAPI } from "./base"
|
||||
import { Expectations, TestAPI } from "./base"
|
||||
import { Row } from "@budibase/types"
|
||||
|
||||
export class LegacyViewAPI extends TestAPI {
|
||||
constructor(config: TestConfiguration) {
|
||||
super(config)
|
||||
}
|
||||
|
||||
get = async (id: string, { expectStatus } = { expectStatus: 200 }) => {
|
||||
return await this.request
|
||||
.get(`/api/views/${id}`)
|
||||
.set(this.config.defaultHeaders())
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(expectStatus)
|
||||
get = async (id: string, expectations?: Expectations) => {
|
||||
return await this._get<Row[]>(`/api/views/${id}`, { expectations })
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,52 +1,39 @@
|
|||
import { AnyDocument, PermissionLevel } from "@budibase/types"
|
||||
import TestConfiguration from "../TestConfiguration"
|
||||
import { TestAPI } from "./base"
|
||||
import {
|
||||
AddPermissionRequest,
|
||||
AddPermissionResponse,
|
||||
GetResourcePermsResponse,
|
||||
RemovePermissionRequest,
|
||||
RemovePermissionResponse,
|
||||
} from "@budibase/types"
|
||||
import { Expectations, TestAPI } from "./base"
|
||||
|
||||
export class PermissionAPI extends TestAPI {
|
||||
constructor(config: TestConfiguration) {
|
||||
super(config)
|
||||
get = async (resourceId: string, expectations?: Expectations) => {
|
||||
return await this._get<GetResourcePermsResponse>(
|
||||
`/api/permission/${resourceId}`,
|
||||
{ expectations }
|
||||
)
|
||||
}
|
||||
|
||||
get = async (
|
||||
resourceId: string,
|
||||
{ expectStatus } = { expectStatus: 200 }
|
||||
) => {
|
||||
return this.request
|
||||
.get(`/api/permission/${resourceId}`)
|
||||
.set(this.config.defaultHeaders())
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(expectStatus)
|
||||
}
|
||||
|
||||
set = async (
|
||||
{
|
||||
roleId,
|
||||
resourceId,
|
||||
level,
|
||||
}: { roleId: string; resourceId: string; level: PermissionLevel },
|
||||
{ expectStatus } = { expectStatus: 200 }
|
||||
): Promise<any> => {
|
||||
const res = await this.request
|
||||
.post(`/api/permission/${roleId}/${resourceId}/${level}`)
|
||||
.set(this.config.defaultHeaders())
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(expectStatus)
|
||||
return res.body
|
||||
add = async (
|
||||
request: AddPermissionRequest,
|
||||
expectations?: Expectations
|
||||
): Promise<AddPermissionResponse> => {
|
||||
const { roleId, resourceId, level } = request
|
||||
return await this._post<AddPermissionResponse>(
|
||||
`/api/permission/${roleId}/${resourceId}/${level}`,
|
||||
{ expectations }
|
||||
)
|
||||
}
|
||||
|
||||
revoke = async (
|
||||
{
|
||||
roleId,
|
||||
resourceId,
|
||||
level,
|
||||
}: { roleId: string; resourceId: string; level: PermissionLevel },
|
||||
{ expectStatus } = { expectStatus: 200 }
|
||||
request: RemovePermissionRequest,
|
||||
expectations?: Expectations
|
||||
) => {
|
||||
const res = await this.request
|
||||
.delete(`/api/permission/${roleId}/${resourceId}/${level}`)
|
||||
.set(this.config.defaultHeaders())
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(expectStatus)
|
||||
return res
|
||||
const { roleId, resourceId, level } = request
|
||||
return await this._delete<RemovePermissionResponse>(
|
||||
`/api/permission/${roleId}/${resourceId}/${level}`,
|
||||
{ expectations }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,60 +1,32 @@
|
|||
import TestConfiguration from "../TestConfiguration"
|
||||
import {
|
||||
Query,
|
||||
QueryPreview,
|
||||
type ExecuteQueryRequest,
|
||||
type ExecuteQueryResponse,
|
||||
ExecuteQueryRequest,
|
||||
ExecuteQueryResponse,
|
||||
PreviewQueryRequest,
|
||||
PreviewQueryResponse,
|
||||
} from "@budibase/types"
|
||||
import { TestAPI } from "./base"
|
||||
|
||||
export class QueryAPI extends TestAPI {
|
||||
constructor(config: TestConfiguration) {
|
||||
super(config)
|
||||
}
|
||||
|
||||
create = async (body: Query): Promise<Query> => {
|
||||
const res = await this.request
|
||||
.post(`/api/queries`)
|
||||
.set(this.config.defaultHeaders())
|
||||
.send(body)
|
||||
.expect("Content-Type", /json/)
|
||||
|
||||
if (res.status !== 200) {
|
||||
throw new Error(JSON.stringify(res.body))
|
||||
}
|
||||
|
||||
return res.body as Query
|
||||
return await this._post<Query>(`/api/queries`, { body })
|
||||
}
|
||||
|
||||
execute = async (
|
||||
queryId: string,
|
||||
body?: ExecuteQueryRequest
|
||||
): Promise<ExecuteQueryResponse> => {
|
||||
const res = await this.request
|
||||
.post(`/api/v2/queries/${queryId}`)
|
||||
.set(this.config.defaultHeaders())
|
||||
.send(body)
|
||||
.expect("Content-Type", /json/)
|
||||
|
||||
if (res.status !== 200) {
|
||||
throw new Error(JSON.stringify(res.body))
|
||||
}
|
||||
|
||||
return res.body
|
||||
return await this._post<ExecuteQueryResponse>(
|
||||
`/api/v2/queries/${queryId}`,
|
||||
{
|
||||
body,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
previewQuery = async (queryPreview: QueryPreview) => {
|
||||
const res = await this.request
|
||||
.post(`/api/queries/preview`)
|
||||
.send(queryPreview)
|
||||
.set(this.config.defaultHeaders())
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(200)
|
||||
|
||||
if (res.status !== 200) {
|
||||
throw new Error(JSON.stringify(res.body))
|
||||
}
|
||||
|
||||
return res.body
|
||||
previewQuery = async (queryPreview: PreviewQueryRequest) => {
|
||||
return await this._post<PreviewQueryResponse>(`/api/queries/preview`, {
|
||||
body: queryPreview,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,162 +8,140 @@ import {
|
|||
BulkImportResponse,
|
||||
SearchRowResponse,
|
||||
SearchParams,
|
||||
DeleteRowRequest,
|
||||
DeleteRows,
|
||||
DeleteRow,
|
||||
ExportRowsResponse,
|
||||
} from "@budibase/types"
|
||||
import TestConfiguration from "../TestConfiguration"
|
||||
import { TestAPI } from "./base"
|
||||
import { Expectations, TestAPI } from "./base"
|
||||
|
||||
export class RowAPI extends TestAPI {
|
||||
constructor(config: TestConfiguration) {
|
||||
super(config)
|
||||
}
|
||||
|
||||
get = async (
|
||||
sourceId: string,
|
||||
rowId: string,
|
||||
{ expectStatus } = { expectStatus: 200 }
|
||||
expectations?: Expectations
|
||||
) => {
|
||||
const request = this.request
|
||||
.get(`/api/${sourceId}/rows/${rowId}`)
|
||||
.set(this.config.defaultHeaders())
|
||||
.expect(expectStatus)
|
||||
if (expectStatus !== 404) {
|
||||
request.expect("Content-Type", /json/)
|
||||
}
|
||||
return request
|
||||
return await this._get<Row>(`/api/${sourceId}/rows/${rowId}`, {
|
||||
expectations,
|
||||
})
|
||||
}
|
||||
|
||||
getEnriched = async (
|
||||
sourceId: string,
|
||||
rowId: string,
|
||||
{ expectStatus } = { expectStatus: 200 }
|
||||
expectations?: Expectations
|
||||
) => {
|
||||
const request = this.request
|
||||
.get(`/api/${sourceId}/${rowId}/enrich`)
|
||||
.set(this.config.defaultHeaders())
|
||||
.expect(expectStatus)
|
||||
if (expectStatus !== 404) {
|
||||
request.expect("Content-Type", /json/)
|
||||
}
|
||||
return request
|
||||
return await this._get<Row>(`/api/${sourceId}/${rowId}/enrich`, {
|
||||
expectations,
|
||||
})
|
||||
}
|
||||
|
||||
save = async (
|
||||
tableId: string,
|
||||
row: SaveRowRequest,
|
||||
{ expectStatus } = { expectStatus: 200 }
|
||||
expectations?: Expectations
|
||||
): Promise<Row> => {
|
||||
const resp = await this.request
|
||||
.post(`/api/${tableId}/rows`)
|
||||
.send(row)
|
||||
.set(this.config.defaultHeaders())
|
||||
.expect("Content-Type", /json/)
|
||||
if (resp.status !== expectStatus) {
|
||||
throw new Error(
|
||||
`Expected status ${expectStatus} but got ${
|
||||
resp.status
|
||||
}, body: ${JSON.stringify(resp.body)}`
|
||||
)
|
||||
}
|
||||
return resp.body as Row
|
||||
return await this._post<Row>(`/api/${tableId}/rows`, {
|
||||
body: row,
|
||||
expectations,
|
||||
})
|
||||
}
|
||||
|
||||
validate = async (
|
||||
sourceId: string,
|
||||
row: SaveRowRequest,
|
||||
{ expectStatus } = { expectStatus: 200 }
|
||||
expectations?: Expectations
|
||||
): Promise<ValidateResponse> => {
|
||||
const resp = await this.request
|
||||
.post(`/api/${sourceId}/rows/validate`)
|
||||
.send(row)
|
||||
.set(this.config.defaultHeaders())
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(expectStatus)
|
||||
return resp.body as ValidateResponse
|
||||
return await this._post<ValidateResponse>(
|
||||
`/api/${sourceId}/rows/validate`,
|
||||
{
|
||||
body: row,
|
||||
expectations,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
patch = async (
|
||||
sourceId: string,
|
||||
row: PatchRowRequest,
|
||||
{ expectStatus } = { expectStatus: 200 }
|
||||
expectations?: Expectations
|
||||
): Promise<Row> => {
|
||||
let resp = await this.request
|
||||
.patch(`/api/${sourceId}/rows`)
|
||||
.send(row)
|
||||
.set(this.config.defaultHeaders())
|
||||
.expect("Content-Type", /json/)
|
||||
if (resp.status !== expectStatus) {
|
||||
throw new Error(
|
||||
`Expected status ${expectStatus} but got ${
|
||||
resp.status
|
||||
}, body: ${JSON.stringify(resp.body)}`
|
||||
)
|
||||
}
|
||||
return resp.body as Row
|
||||
return await this._patch<Row>(`/api/${sourceId}/rows`, {
|
||||
body: row,
|
||||
expectations,
|
||||
})
|
||||
}
|
||||
|
||||
delete = async (
|
||||
sourceId: string,
|
||||
rows: Row | string | (Row | string)[],
|
||||
{ expectStatus } = { expectStatus: 200 }
|
||||
row: DeleteRow,
|
||||
expectations?: Expectations
|
||||
) => {
|
||||
return this.request
|
||||
.delete(`/api/${sourceId}/rows`)
|
||||
.send(Array.isArray(rows) ? { rows } : rows)
|
||||
.set(this.config.defaultHeaders())
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(expectStatus)
|
||||
return await this._delete<Row>(`/api/${sourceId}/rows`, {
|
||||
body: row,
|
||||
expectations,
|
||||
})
|
||||
}
|
||||
|
||||
bulkDelete = async (
|
||||
sourceId: string,
|
||||
body: DeleteRows,
|
||||
expectations?: Expectations
|
||||
) => {
|
||||
return await this._delete<Row[]>(`/api/${sourceId}/rows`, {
|
||||
body,
|
||||
expectations,
|
||||
})
|
||||
}
|
||||
|
||||
fetch = async (
|
||||
sourceId: string,
|
||||
{ expectStatus } = { expectStatus: 200 }
|
||||
expectations?: Expectations
|
||||
): Promise<Row[]> => {
|
||||
const request = this.request
|
||||
.get(`/api/${sourceId}/rows`)
|
||||
.set(this.config.defaultHeaders())
|
||||
.expect(expectStatus)
|
||||
|
||||
return (await request).body
|
||||
return await this._get<Row[]>(`/api/${sourceId}/rows`, {
|
||||
expectations,
|
||||
})
|
||||
}
|
||||
|
||||
exportRows = async (
|
||||
tableId: string,
|
||||
body: ExportRowsRequest,
|
||||
{ expectStatus } = { expectStatus: 200 }
|
||||
expectations?: Expectations
|
||||
) => {
|
||||
const request = this.request
|
||||
.post(`/api/${tableId}/rows/exportRows?format=json`)
|
||||
.set(this.config.defaultHeaders())
|
||||
.send(body)
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(expectStatus)
|
||||
return request
|
||||
const response = await this._requestRaw(
|
||||
"post",
|
||||
`/api/${tableId}/rows/exportRows`,
|
||||
{
|
||||
body,
|
||||
query: { format: "json" },
|
||||
expectations,
|
||||
}
|
||||
)
|
||||
return response.text
|
||||
}
|
||||
|
||||
bulkImport = async (
|
||||
tableId: string,
|
||||
body: BulkImportRequest,
|
||||
{ expectStatus } = { expectStatus: 200 }
|
||||
expectations?: Expectations
|
||||
): Promise<BulkImportResponse> => {
|
||||
let request = this.request
|
||||
.post(`/api/tables/${tableId}/import`)
|
||||
.send(body)
|
||||
.set(this.config.defaultHeaders())
|
||||
.expect(expectStatus)
|
||||
return (await request).body
|
||||
return await this._post<BulkImportResponse>(
|
||||
`/api/tables/${tableId}/import`,
|
||||
{
|
||||
body,
|
||||
expectations,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
search = async (
|
||||
sourceId: string,
|
||||
params?: SearchParams,
|
||||
{ expectStatus } = { expectStatus: 200 }
|
||||
expectations?: Expectations
|
||||
): Promise<SearchRowResponse> => {
|
||||
const request = this.request
|
||||
.post(`/api/${sourceId}/search`)
|
||||
.send(params)
|
||||
.set(this.config.defaultHeaders())
|
||||
.expect(expectStatus)
|
||||
|
||||
return (await request).body
|
||||
return await this._post<SearchRowResponse>(`/api/${sourceId}/search`, {
|
||||
body: params,
|
||||
expectations,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,18 +1,8 @@
|
|||
import TestConfiguration from "../TestConfiguration"
|
||||
import { Screen } from "@budibase/types"
|
||||
import { TestAPI } from "./base"
|
||||
import { Expectations, TestAPI } from "./base"
|
||||
|
||||
export class ScreenAPI extends TestAPI {
|
||||
constructor(config: TestConfiguration) {
|
||||
super(config)
|
||||
}
|
||||
|
||||
list = async (): Promise<Screen[]> => {
|
||||
const res = await this.request
|
||||
.get(`/api/screens`)
|
||||
.set(this.config.defaultHeaders())
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(200)
|
||||
return res.body as Screen[]
|
||||
list = async (expectations?: Expectations): Promise<Screen[]> => {
|
||||
return await this._get<Screen[]>(`/api/screens`, { expectations })
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,74 +5,38 @@ import {
|
|||
SaveTableResponse,
|
||||
Table,
|
||||
} from "@budibase/types"
|
||||
import TestConfiguration from "../TestConfiguration"
|
||||
import { TestAPI } from "./base"
|
||||
import { Expectations, TestAPI } from "./base"
|
||||
|
||||
export class TableAPI extends TestAPI {
|
||||
constructor(config: TestConfiguration) {
|
||||
super(config)
|
||||
}
|
||||
|
||||
save = async (
|
||||
data: SaveTableRequest,
|
||||
{ expectStatus } = { expectStatus: 200 }
|
||||
expectations?: Expectations
|
||||
): Promise<SaveTableResponse> => {
|
||||
const res = await this.request
|
||||
.post(`/api/tables`)
|
||||
.send(data)
|
||||
.set(this.config.defaultHeaders())
|
||||
.expect("Content-Type", /json/)
|
||||
|
||||
if (res.status !== expectStatus) {
|
||||
throw new Error(
|
||||
`Expected status ${expectStatus} but got ${
|
||||
res.status
|
||||
} with body ${JSON.stringify(res.body)}`
|
||||
)
|
||||
}
|
||||
|
||||
return res.body
|
||||
return await this._post<SaveTableResponse>("/api/tables", {
|
||||
body: data,
|
||||
expectations,
|
||||
})
|
||||
}
|
||||
|
||||
fetch = async (
|
||||
{ expectStatus } = { expectStatus: 200 }
|
||||
): Promise<Table[]> => {
|
||||
const res = await this.request
|
||||
.get(`/api/tables`)
|
||||
.set(this.config.defaultHeaders())
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(expectStatus)
|
||||
return res.body
|
||||
fetch = async (expectations?: Expectations): Promise<Table[]> => {
|
||||
return await this._get<Table[]>("/api/tables", { expectations })
|
||||
}
|
||||
|
||||
get = async (
|
||||
tableId: string,
|
||||
{ expectStatus } = { expectStatus: 200 }
|
||||
expectations?: Expectations
|
||||
): Promise<Table> => {
|
||||
const res = await this.request
|
||||
.get(`/api/tables/${tableId}`)
|
||||
.set(this.config.defaultHeaders())
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(expectStatus)
|
||||
return res.body
|
||||
return await this._get<Table>(`/api/tables/${tableId}`, { expectations })
|
||||
}
|
||||
|
||||
migrate = async (
|
||||
tableId: string,
|
||||
data: MigrateRequest,
|
||||
{ expectStatus } = { expectStatus: 200 }
|
||||
expectations?: Expectations
|
||||
): Promise<MigrateResponse> => {
|
||||
const res = await this.request
|
||||
.post(`/api/tables/${tableId}/migrate`)
|
||||
.send(data)
|
||||
.set(this.config.defaultHeaders())
|
||||
if (res.status !== expectStatus) {
|
||||
throw new Error(
|
||||
`Expected status ${expectStatus} but got ${
|
||||
res.status
|
||||
} with body ${JSON.stringify(res.body)}`
|
||||
)
|
||||
}
|
||||
return res.body
|
||||
return await this._post<MigrateResponse>(`/api/tables/${tableId}/migrate`, {
|
||||
body: data,
|
||||
expectations,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,154 +4,79 @@ import {
|
|||
Flags,
|
||||
UserMetadata,
|
||||
} from "@budibase/types"
|
||||
import TestConfiguration from "../TestConfiguration"
|
||||
import { TestAPI } from "./base"
|
||||
import { Expectations, TestAPI } from "./base"
|
||||
import { DocumentInsertResponse } from "@budibase/nano"
|
||||
|
||||
export class UserAPI extends TestAPI {
|
||||
constructor(config: TestConfiguration) {
|
||||
super(config)
|
||||
}
|
||||
|
||||
fetch = async (
|
||||
{ expectStatus } = { expectStatus: 200 }
|
||||
expectations?: Expectations
|
||||
): Promise<FetchUserMetadataResponse> => {
|
||||
const res = await this.request
|
||||
.get(`/api/users/metadata`)
|
||||
.set(this.config.defaultHeaders())
|
||||
.expect("Content-Type", /json/)
|
||||
|
||||
if (res.status !== expectStatus) {
|
||||
throw new Error(
|
||||
`Expected status ${expectStatus} but got ${
|
||||
res.status
|
||||
} with body ${JSON.stringify(res.body)}`
|
||||
)
|
||||
}
|
||||
|
||||
return res.body
|
||||
return await this._get<FetchUserMetadataResponse>("/api/users/metadata", {
|
||||
expectations,
|
||||
})
|
||||
}
|
||||
|
||||
find = async (
|
||||
id: string,
|
||||
{ expectStatus } = { expectStatus: 200 }
|
||||
expectations?: Expectations
|
||||
): Promise<FindUserMetadataResponse> => {
|
||||
const res = await this.request
|
||||
.get(`/api/users/metadata/${id}`)
|
||||
.set(this.config.defaultHeaders())
|
||||
.expect("Content-Type", /json/)
|
||||
|
||||
if (res.status !== expectStatus) {
|
||||
throw new Error(
|
||||
`Expected status ${expectStatus} but got ${
|
||||
res.status
|
||||
} with body ${JSON.stringify(res.body)}`
|
||||
)
|
||||
}
|
||||
|
||||
return res.body
|
||||
return await this._get<FindUserMetadataResponse>(
|
||||
`/api/users/metadata/${id}`,
|
||||
{
|
||||
expectations,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
update = async (
|
||||
user: UserMetadata,
|
||||
{ expectStatus } = { expectStatus: 200 }
|
||||
expectations?: Expectations
|
||||
): Promise<DocumentInsertResponse> => {
|
||||
const res = await this.request
|
||||
.put(`/api/users/metadata`)
|
||||
.set(this.config.defaultHeaders())
|
||||
.send(user)
|
||||
.expect("Content-Type", /json/)
|
||||
|
||||
if (res.status !== expectStatus) {
|
||||
throw new Error(
|
||||
`Expected status ${expectStatus} but got ${
|
||||
res.status
|
||||
} with body ${JSON.stringify(res.body)}`
|
||||
)
|
||||
}
|
||||
|
||||
return res.body as DocumentInsertResponse
|
||||
return await this._put<DocumentInsertResponse>("/api/users/metadata", {
|
||||
body: user,
|
||||
expectations,
|
||||
})
|
||||
}
|
||||
|
||||
updateSelf = async (
|
||||
user: UserMetadata,
|
||||
{ expectStatus } = { expectStatus: 200 }
|
||||
expectations?: Expectations
|
||||
): Promise<DocumentInsertResponse> => {
|
||||
const res = await this.request
|
||||
.post(`/api/users/metadata/self`)
|
||||
.set(this.config.defaultHeaders())
|
||||
.send(user)
|
||||
.expect("Content-Type", /json/)
|
||||
|
||||
if (res.status !== expectStatus) {
|
||||
throw new Error(
|
||||
`Expected status ${expectStatus} but got ${
|
||||
res.status
|
||||
} with body ${JSON.stringify(res.body)}`
|
||||
)
|
||||
}
|
||||
|
||||
return res.body as DocumentInsertResponse
|
||||
return await this._post<DocumentInsertResponse>(
|
||||
"/api/users/metadata/self",
|
||||
{
|
||||
body: user,
|
||||
expectations,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
destroy = async (
|
||||
id: string,
|
||||
{ expectStatus } = { expectStatus: 200 }
|
||||
expectations?: Expectations
|
||||
): Promise<{ message: string }> => {
|
||||
const res = await this.request
|
||||
.delete(`/api/users/metadata/${id}`)
|
||||
.set(this.config.defaultHeaders())
|
||||
.expect("Content-Type", /json/)
|
||||
|
||||
if (res.status !== expectStatus) {
|
||||
throw new Error(
|
||||
`Expected status ${expectStatus} but got ${
|
||||
res.status
|
||||
} with body ${JSON.stringify(res.body)}`
|
||||
)
|
||||
}
|
||||
|
||||
return res.body as { message: string }
|
||||
return await this._delete<{ message: string }>(
|
||||
`/api/users/metadata/${id}`,
|
||||
{
|
||||
expectations,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
setFlag = async (
|
||||
flag: string,
|
||||
value: any,
|
||||
{ expectStatus } = { expectStatus: 200 }
|
||||
expectations?: Expectations
|
||||
): Promise<{ message: string }> => {
|
||||
const res = await this.request
|
||||
.post(`/api/users/flags`)
|
||||
.set(this.config.defaultHeaders())
|
||||
.send({ flag, value })
|
||||
.expect("Content-Type", /json/)
|
||||
|
||||
if (res.status !== expectStatus) {
|
||||
throw new Error(
|
||||
`Expected status ${expectStatus} but got ${
|
||||
res.status
|
||||
} with body ${JSON.stringify(res.body)}`
|
||||
)
|
||||
}
|
||||
|
||||
return res.body as { message: string }
|
||||
return await this._post<{ message: string }>(`/api/users/flags`, {
|
||||
body: { flag, value },
|
||||
expectations,
|
||||
})
|
||||
}
|
||||
|
||||
getFlags = async (
|
||||
{ expectStatus } = { expectStatus: 200 }
|
||||
): Promise<Flags> => {
|
||||
const res = await this.request
|
||||
.get(`/api/users/flags`)
|
||||
.set(this.config.defaultHeaders())
|
||||
.expect("Content-Type", /json/)
|
||||
|
||||
if (res.status !== expectStatus) {
|
||||
throw new Error(
|
||||
`Expected status ${expectStatus} but got ${
|
||||
res.status
|
||||
} with body ${JSON.stringify(res.body)}`
|
||||
)
|
||||
}
|
||||
|
||||
return res.body as Flags
|
||||
getFlags = async (expectations?: Expectations): Promise<Flags> => {
|
||||
return await this._get<Flags>(`/api/users/flags`, {
|
||||
expectations,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,21 +3,16 @@ import {
|
|||
UpdateViewRequest,
|
||||
ViewV2,
|
||||
SearchViewRowRequest,
|
||||
PaginatedSearchRowResponse,
|
||||
} from "@budibase/types"
|
||||
import TestConfiguration from "../TestConfiguration"
|
||||
import { TestAPI } from "./base"
|
||||
import { Expectations, TestAPI } from "./base"
|
||||
import { generator } from "@budibase/backend-core/tests"
|
||||
import { Response } from "superagent"
|
||||
import sdk from "../../../sdk"
|
||||
|
||||
export class ViewV2API extends TestAPI {
|
||||
constructor(config: TestConfiguration) {
|
||||
super(config)
|
||||
}
|
||||
|
||||
create = async (
|
||||
viewData?: Partial<CreateViewRequest>,
|
||||
{ expectStatus } = { expectStatus: 201 }
|
||||
expectations?: Expectations
|
||||
): Promise<ViewV2> => {
|
||||
let tableId = viewData?.tableId
|
||||
if (!tableId && !this.config.table) {
|
||||
|
@ -30,43 +25,36 @@ export class ViewV2API extends TestAPI {
|
|||
name: generator.guid(),
|
||||
...viewData,
|
||||
}
|
||||
const result = await this.request
|
||||
.post(`/api/v2/views`)
|
||||
.send(view)
|
||||
.set(this.config.defaultHeaders())
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(expectStatus)
|
||||
return result.body.data as ViewV2
|
||||
|
||||
const exp: Expectations = {
|
||||
status: 201,
|
||||
...expectations,
|
||||
}
|
||||
|
||||
const resp = await this._post<{ data: ViewV2 }>("/api/v2/views", {
|
||||
body: view,
|
||||
expectations: exp,
|
||||
})
|
||||
return resp.data
|
||||
}
|
||||
|
||||
update = async (
|
||||
view: UpdateViewRequest,
|
||||
{
|
||||
expectStatus,
|
||||
handleResponse,
|
||||
}: {
|
||||
expectStatus: number
|
||||
handleResponse?: (response: Response) => void
|
||||
} = { expectStatus: 200 }
|
||||
expectations?: Expectations
|
||||
): 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
|
||||
const resp = await this._put<{ data: ViewV2 }>(`/api/v2/views/${view.id}`, {
|
||||
body: view,
|
||||
expectations,
|
||||
})
|
||||
return resp.data
|
||||
}
|
||||
|
||||
delete = async (viewId: string, { expectStatus } = { expectStatus: 204 }) => {
|
||||
return this.request
|
||||
.delete(`/api/v2/views/${viewId}`)
|
||||
.set(this.config.defaultHeaders())
|
||||
.expect(expectStatus)
|
||||
delete = async (viewId: string, expectations?: Expectations) => {
|
||||
const exp = {
|
||||
status: 204,
|
||||
...expectations,
|
||||
}
|
||||
return await this._delete(`/api/v2/views/${viewId}`, { expectations: exp })
|
||||
}
|
||||
|
||||
get = async (viewId: string) => {
|
||||
|
@ -78,17 +66,29 @@ export class ViewV2API extends TestAPI {
|
|||
search = async (
|
||||
viewId: string,
|
||||
params?: SearchViewRowRequest,
|
||||
{ expectStatus = 200, usePublicUser = false } = {}
|
||||
expectations?: Expectations
|
||||
) => {
|
||||
return this.request
|
||||
.post(`/api/v2/views/${viewId}/search`)
|
||||
.send(params)
|
||||
.set(
|
||||
usePublicUser
|
||||
? this.config.publicHeaders()
|
||||
: this.config.defaultHeaders()
|
||||
)
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(expectStatus)
|
||||
return await this._post<PaginatedSearchRowResponse>(
|
||||
`/api/v2/views/${viewId}/search`,
|
||||
{
|
||||
body: params,
|
||||
expectations,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
publicSearch = async (
|
||||
viewId: string,
|
||||
params?: SearchViewRowRequest,
|
||||
expectations?: Expectations
|
||||
) => {
|
||||
return await this._post<PaginatedSearchRowResponse>(
|
||||
`/api/v2/views/${viewId}/search`,
|
||||
{
|
||||
body: params,
|
||||
expectations,
|
||||
publicUser: true,
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { PlanType } from "../../../sdk"
|
||||
import { PermissionLevel, PlanType } from "../../../sdk"
|
||||
|
||||
export interface ResourcePermissionInfo {
|
||||
role: string
|
||||
|
@ -14,3 +14,21 @@ export interface GetResourcePermsResponse {
|
|||
export interface GetDependantResourcesResponse {
|
||||
resourceByType?: Record<string, number>
|
||||
}
|
||||
|
||||
export interface AddedPermission {
|
||||
_id?: string
|
||||
rev?: string
|
||||
error?: string
|
||||
reason?: string
|
||||
}
|
||||
|
||||
export type AddPermissionResponse = AddedPermission[]
|
||||
|
||||
export interface AddPermissionRequest {
|
||||
roleId: string
|
||||
resourceId: string
|
||||
level: PermissionLevel
|
||||
}
|
||||
|
||||
export interface RemovePermissionRequest extends AddPermissionRequest {}
|
||||
export interface RemovePermissionResponse extends AddPermissionResponse {}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { SearchFilters, SearchParams } from "../../../sdk"
|
||||
import { Row } from "../../../documents"
|
||||
import { SortOrder } from "../../../api"
|
||||
import { PaginationResponse, SortOrder } from "../../../api"
|
||||
import { ReadStream } from "fs"
|
||||
|
||||
export interface SaveRowRequest extends Row {}
|
||||
|
@ -31,6 +31,10 @@ export interface SearchRowResponse {
|
|||
rows: any[]
|
||||
}
|
||||
|
||||
export interface PaginatedSearchRowResponse
|
||||
extends SearchRowResponse,
|
||||
PaginationResponse {}
|
||||
|
||||
export interface ExportRowsRequest {
|
||||
rows: string[]
|
||||
columns?: string[]
|
||||
|
|
|
@ -27,3 +27,9 @@ export interface FetchAppPackageResponse {
|
|||
clientLibPath: string
|
||||
hasLock: boolean
|
||||
}
|
||||
|
||||
export interface PublishResponse {
|
||||
_id: string
|
||||
status: string
|
||||
appUrl: string
|
||||
}
|
||||
|
|
|
@ -13,3 +13,4 @@ export * from "./searchFilter"
|
|||
export * from "./cookies"
|
||||
export * from "./automation"
|
||||
export * from "./layout"
|
||||
export * from "./query"
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
import { QueryPreview, QuerySchema } from "../../documents"
|
||||
|
||||
export interface PreviewQueryRequest extends QueryPreview {}
|
||||
|
||||
export interface PreviewQueryResponse {
|
||||
rows: any[]
|
||||
nestedSchemaFields: { [key: string]: { [key: string]: string | QuerySchema } }
|
||||
schema: { [key: string]: string | QuerySchema }
|
||||
info: any
|
||||
extra: any
|
||||
}
|
||||
|
||||
export interface ExecuteQueryRequest {
|
||||
parameters?: { [key: string]: string }
|
||||
pagination?: any
|
||||
}
|
||||
|
||||
export interface ExecuteQueryResponse {
|
||||
data: Record<string, any>[]
|
||||
}
|
|
@ -62,22 +62,6 @@ export interface PaginationValues {
|
|||
limit: number | null
|
||||
}
|
||||
|
||||
export interface PreviewQueryRequest extends Omit<Query, "parameters"> {
|
||||
parameters: {}
|
||||
flags?: {
|
||||
urlName?: boolean
|
||||
}
|
||||
}
|
||||
|
||||
export interface ExecuteQueryRequest {
|
||||
parameters?: { [key: string]: string }
|
||||
pagination?: any
|
||||
}
|
||||
|
||||
export interface ExecuteQueryResponse {
|
||||
data: Row[]
|
||||
}
|
||||
|
||||
export enum HttpMethod {
|
||||
GET = "GET",
|
||||
POST = "POST",
|
||||
|
|
Loading…
Reference in New Issue