More progress on modernising application tests.
This commit is contained in:
parent
b2c4f04aa6
commit
b9600d8330
|
@ -6,7 +6,7 @@ import { Plugin } from "@budibase/types"
|
||||||
|
|
||||||
// URLS
|
// URLS
|
||||||
|
|
||||||
export function enrichPluginURLs(plugins: Plugin[]) {
|
export function enrichPluginURLs(plugins: Plugin[]): Plugin[] {
|
||||||
if (!plugins || !plugins.length) {
|
if (!plugins || !plugins.length) {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,12 +3,12 @@ set -e
|
||||||
|
|
||||||
if [[ -n $CI ]]
|
if [[ -n $CI ]]
|
||||||
then
|
then
|
||||||
export NODE_OPTIONS="--max-old-space-size=4096 --no-node-snapshot"
|
export NODE_OPTIONS="--max-old-space-size=4096 --no-node-snapshot $NODE_OPTIONS"
|
||||||
echo "jest --coverage --maxWorkers=2 --forceExit --workerIdleMemoryLimit=2000MB --bail $@"
|
echo "jest --coverage --maxWorkers=2 --forceExit --workerIdleMemoryLimit=2000MB --bail $@"
|
||||||
jest --coverage --maxWorkers=2 --forceExit --workerIdleMemoryLimit=2000MB --bail $@
|
jest --coverage --maxWorkers=2 --forceExit --workerIdleMemoryLimit=2000MB --bail $@
|
||||||
else
|
else
|
||||||
# --maxWorkers performs better in development
|
# --maxWorkers performs better in development
|
||||||
export NODE_OPTIONS="--no-node-snapshot"
|
export NODE_OPTIONS="--no-node-snapshot $NODE_OPTIONS"
|
||||||
echo "jest --coverage --maxWorkers=2 --forceExit $@"
|
echo "jest --coverage --maxWorkers=2 --forceExit $@"
|
||||||
jest --coverage --maxWorkers=2 --forceExit $@
|
jest --coverage --maxWorkers=2 --forceExit $@
|
||||||
fi
|
fi
|
|
@ -48,6 +48,8 @@ import {
|
||||||
Screen,
|
Screen,
|
||||||
UserCtx,
|
UserCtx,
|
||||||
CreateAppRequest,
|
CreateAppRequest,
|
||||||
|
FetchAppDefinitionResponse,
|
||||||
|
type FetchAppPackageResponse,
|
||||||
} from "@budibase/types"
|
} from "@budibase/types"
|
||||||
import { BASE_LAYOUT_PROP_IDS } from "../../constants/layouts"
|
import { BASE_LAYOUT_PROP_IDS } from "../../constants/layouts"
|
||||||
import sdk from "../../sdk"
|
import sdk from "../../sdk"
|
||||||
|
@ -59,23 +61,23 @@ import * as appMigrations from "../../appMigrations"
|
||||||
async function getLayouts() {
|
async function getLayouts() {
|
||||||
const db = context.getAppDB()
|
const db = context.getAppDB()
|
||||||
return (
|
return (
|
||||||
await db.allDocs(
|
await db.allDocs<Layout>(
|
||||||
getLayoutParams(null, {
|
getLayoutParams(null, {
|
||||||
include_docs: true,
|
include_docs: true,
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
).rows.map((row: any) => row.doc)
|
).rows.map(row => row.doc!)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getScreens() {
|
async function getScreens() {
|
||||||
const db = context.getAppDB()
|
const db = context.getAppDB()
|
||||||
return (
|
return (
|
||||||
await db.allDocs(
|
await db.allDocs<Screen>(
|
||||||
getScreenParams(null, {
|
getScreenParams(null, {
|
||||||
include_docs: true,
|
include_docs: true,
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
).rows.map((row: any) => row.doc)
|
).rows.map(row => row.doc!)
|
||||||
}
|
}
|
||||||
|
|
||||||
function getUserRoleId(ctx: UserCtx) {
|
function getUserRoleId(ctx: UserCtx) {
|
||||||
|
@ -175,14 +177,16 @@ export const addSampleData = async (ctx: UserCtx) => {
|
||||||
ctx.status = 200
|
ctx.status = 200
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function fetch(ctx: UserCtx) {
|
export async function fetch(ctx: UserCtx<null, App[]>) {
|
||||||
ctx.body = await sdk.applications.fetch(
|
ctx.body = await sdk.applications.fetch(
|
||||||
ctx.query.status as AppStatus,
|
ctx.query.status as AppStatus,
|
||||||
ctx.user
|
ctx.user
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function fetchAppDefinition(ctx: UserCtx) {
|
export async function fetchAppDefinition(
|
||||||
|
ctx: UserCtx<null, FetchAppDefinitionResponse>
|
||||||
|
) {
|
||||||
const layouts = await getLayouts()
|
const layouts = await getLayouts()
|
||||||
const userRoleId = getUserRoleId(ctx)
|
const userRoleId = getUserRoleId(ctx)
|
||||||
const accessController = new roles.AccessController()
|
const accessController = new roles.AccessController()
|
||||||
|
@ -197,17 +201,19 @@ export async function fetchAppDefinition(ctx: UserCtx) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function fetchAppPackage(ctx: UserCtx) {
|
export async function fetchAppPackage(
|
||||||
|
ctx: UserCtx<null, FetchAppPackageResponse>
|
||||||
|
) {
|
||||||
const db = context.getAppDB()
|
const db = context.getAppDB()
|
||||||
const appId = context.getAppId()
|
const appId = context.getAppId()
|
||||||
let application = await db.get<any>(DocumentType.APP_METADATA)
|
let application = await db.get<App>(DocumentType.APP_METADATA)
|
||||||
const layouts = await getLayouts()
|
const layouts = await getLayouts()
|
||||||
let screens = await getScreens()
|
let screens = await getScreens()
|
||||||
const license = await licensing.cache.getCachedLicense()
|
const license = await licensing.cache.getCachedLicense()
|
||||||
|
|
||||||
// Enrich plugin URLs
|
// Enrich plugin URLs
|
||||||
application.usedPlugins = objectStore.enrichPluginURLs(
|
application.usedPlugins = objectStore.enrichPluginURLs(
|
||||||
application.usedPlugins
|
application.usedPlugins || []
|
||||||
)
|
)
|
||||||
|
|
||||||
// Only filter screens if the user is not a builder
|
// Only filter screens if the user is not a builder
|
||||||
|
@ -425,7 +431,9 @@ export async function create(ctx: UserCtx) {
|
||||||
|
|
||||||
// This endpoint currently operates as a PATCH rather than a PUT
|
// This endpoint currently operates as a PATCH rather than a PUT
|
||||||
// Thus name and url fields are handled only if present
|
// Thus name and url fields are handled only if present
|
||||||
export async function update(ctx: UserCtx) {
|
export async function update(
|
||||||
|
ctx: UserCtx<{ name?: string; url?: string }, App>
|
||||||
|
) {
|
||||||
const apps = (await dbCore.getAllApps({ dev: true })) as App[]
|
const apps = (await dbCore.getAllApps({ dev: true })) as App[]
|
||||||
// validation
|
// validation
|
||||||
const name = ctx.request.body.name,
|
const name = ctx.request.body.name,
|
||||||
|
@ -498,7 +506,7 @@ export async function revertClient(ctx: UserCtx) {
|
||||||
const revertedToVersion = application.revertableVersion
|
const revertedToVersion = application.revertableVersion
|
||||||
const appPackageUpdates = {
|
const appPackageUpdates = {
|
||||||
version: revertedToVersion,
|
version: revertedToVersion,
|
||||||
revertableVersion: null,
|
revertableVersion: undefined,
|
||||||
}
|
}
|
||||||
const app = await updateAppPackage(appPackageUpdates, ctx.params.appId)
|
const app = await updateAppPackage(appPackageUpdates, ctx.params.appId)
|
||||||
await events.app.versionReverted(app, currentVersion, revertedToVersion)
|
await events.app.versionReverted(app, currentVersion, revertedToVersion)
|
||||||
|
@ -618,12 +626,15 @@ export async function importToApp(ctx: UserCtx) {
|
||||||
ctx.body = { message: "app updated" }
|
ctx.body = { message: "app updated" }
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function updateAppPackage(appPackage: any, appId: any) {
|
export async function updateAppPackage(
|
||||||
|
appPackage: Partial<App>,
|
||||||
|
appId: string
|
||||||
|
) {
|
||||||
return context.doInAppContext(appId, async () => {
|
return context.doInAppContext(appId, async () => {
|
||||||
const db = context.getAppDB()
|
const db = context.getAppDB()
|
||||||
const application = await db.get<App>(DocumentType.APP_METADATA)
|
const application = await db.get<App>(DocumentType.APP_METADATA)
|
||||||
|
|
||||||
const newAppPackage = { ...application, ...appPackage }
|
const newAppPackage: App = { ...application, ...appPackage }
|
||||||
if (appPackage._rev !== application._rev) {
|
if (appPackage._rev !== application._rev) {
|
||||||
newAppPackage._rev = application._rev
|
newAppPackage._rev = application._rev
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,25 +11,27 @@ jest.mock("../../../utilities/redis", () => ({
|
||||||
checkDebounce: jest.fn(),
|
checkDebounce: jest.fn(),
|
||||||
shutdown: jest.fn(),
|
shutdown: jest.fn(),
|
||||||
}))
|
}))
|
||||||
import { clearAllApps, checkBuilderEndpoint } from "./utilities/TestFunctions"
|
import { checkBuilderEndpoint } from "./utilities/TestFunctions"
|
||||||
import * as setup from "./utilities"
|
import * as setup from "./utilities"
|
||||||
import { AppStatus } from "../../../db/utils"
|
import { AppStatus } from "../../../db/utils"
|
||||||
import { events, utils, context } from "@budibase/backend-core"
|
import { events, utils, context } from "@budibase/backend-core"
|
||||||
import env from "../../../environment"
|
import env from "../../../environment"
|
||||||
|
import type { App } from "@budibase/types"
|
||||||
|
|
||||||
jest.setTimeout(15000)
|
jest.setTimeout(150000000)
|
||||||
|
|
||||||
describe("/applications", () => {
|
describe("/applications", () => {
|
||||||
let request = setup.getRequest()
|
let request = setup.getRequest()
|
||||||
let config = setup.getConfig()
|
let config = setup.getConfig()
|
||||||
|
let app: App
|
||||||
|
|
||||||
afterAll(setup.afterAll)
|
afterAll(setup.afterAll)
|
||||||
|
beforeAll(async () => await config.init())
|
||||||
beforeAll(async () => {
|
|
||||||
await config.init()
|
|
||||||
})
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
|
app = await config.api.application.create({ name: utils.newid() })
|
||||||
|
const deployment = await config.api.application.publish(app.appId)
|
||||||
|
expect(deployment.status).toBe("SUCCESS")
|
||||||
jest.clearAllMocks()
|
jest.clearAllMocks()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -74,7 +76,7 @@ describe("/applications", () => {
|
||||||
|
|
||||||
it("migrates navigation settings from old apps", async () => {
|
it("migrates navigation settings from old apps", async () => {
|
||||||
const app = await config.api.application.create({
|
const app = await config.api.application.create({
|
||||||
name: "Old App",
|
name: utils.newid(),
|
||||||
useTemplate: "true",
|
useTemplate: "true",
|
||||||
templateFile: "src/api/routes/tests/data/old-app.txt",
|
templateFile: "src/api/routes/tests/data/old-app.txt",
|
||||||
})
|
})
|
||||||
|
@ -96,77 +98,45 @@ describe("/applications", () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("fetch", () => {
|
describe("fetch", () => {
|
||||||
beforeEach(async () => {
|
|
||||||
// Clean all apps but the onde from config
|
|
||||||
await clearAllApps(config.getTenantId(), [config.getAppId()!])
|
|
||||||
})
|
|
||||||
|
|
||||||
it("lists all applications", async () => {
|
it("lists all applications", async () => {
|
||||||
await config.createApp("app1")
|
|
||||||
await config.createApp("app2")
|
|
||||||
const apps = await config.api.application.fetch({ status: AppStatus.DEV })
|
const apps = await config.api.application.fetch({ status: AppStatus.DEV })
|
||||||
|
expect(apps.length).toBeGreaterThan(0)
|
||||||
// two created apps + the inited app
|
|
||||||
expect(apps.length).toBe(3)
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("fetchAppDefinition", () => {
|
describe("fetchAppDefinition", () => {
|
||||||
it("should be able to get an apps definition", async () => {
|
it("should be able to get an apps definition", async () => {
|
||||||
const res = await request
|
const res = await config.api.application.getDefinition(app.appId)
|
||||||
.get(`/api/applications/${config.getAppId()}/definition`)
|
expect(res.libraries.length).toEqual(1)
|
||||||
.set(config.defaultHeaders())
|
|
||||||
.expect("Content-Type", /json/)
|
|
||||||
.expect(200)
|
|
||||||
expect(res.body.libraries.length).toEqual(1)
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("fetchAppPackage", () => {
|
describe("fetchAppPackage", () => {
|
||||||
it("should be able to fetch the app package", async () => {
|
it("should be able to fetch the app package", async () => {
|
||||||
const res = await request
|
const res = await config.api.application.getAppPackage(app.appId)
|
||||||
.get(`/api/applications/${config.getAppId()}/appPackage`)
|
expect(res.application).toBeDefined()
|
||||||
.set(config.defaultHeaders())
|
expect(res.application.appId).toEqual(config.getAppId())
|
||||||
.expect("Content-Type", /json/)
|
|
||||||
.expect(200)
|
|
||||||
expect(res.body.application).toBeDefined()
|
|
||||||
expect(res.body.application.appId).toEqual(config.getAppId())
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("update", () => {
|
describe("update", () => {
|
||||||
it("should be able to update the app package", async () => {
|
it("should be able to update the app package", async () => {
|
||||||
const res = await request
|
const updatedApp = await config.api.application.update(app.appId, {
|
||||||
.put(`/api/applications/${config.getAppId()}`)
|
name: "TEST_APP",
|
||||||
.send({
|
})
|
||||||
name: "TEST_APP",
|
expect(updatedApp._rev).toBeDefined()
|
||||||
})
|
|
||||||
.set(config.defaultHeaders())
|
|
||||||
.expect("Content-Type", /json/)
|
|
||||||
.expect(200)
|
|
||||||
expect(res.body._rev).toBeDefined()
|
|
||||||
expect(events.app.updated).toBeCalledTimes(1)
|
expect(events.app.updated).toBeCalledTimes(1)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("publish", () => {
|
describe("publish", () => {
|
||||||
it("should publish app with dev app ID", async () => {
|
it("should publish app with dev app ID", async () => {
|
||||||
const appId = config.getAppId()
|
await config.api.application.publish(app.appId)
|
||||||
await request
|
|
||||||
.post(`/api/applications/${appId}/publish`)
|
|
||||||
.set(config.defaultHeaders())
|
|
||||||
.expect("Content-Type", /json/)
|
|
||||||
.expect(200)
|
|
||||||
expect(events.app.published).toBeCalledTimes(1)
|
expect(events.app.published).toBeCalledTimes(1)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should publish app with prod app ID", async () => {
|
it("should publish app with prod app ID", async () => {
|
||||||
const appId = config.getProdAppId()
|
await config.api.application.publish(app.appId.replace("_dev", ""))
|
||||||
await request
|
|
||||||
.post(`/api/applications/${appId}/publish`)
|
|
||||||
.set(config.defaultHeaders())
|
|
||||||
.expect("Content-Type", /json/)
|
|
||||||
.expect(200)
|
|
||||||
expect(events.app.published).toBeCalledTimes(1)
|
expect(events.app.published).toBeCalledTimes(1)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -222,33 +192,25 @@ describe("/applications", () => {
|
||||||
|
|
||||||
describe("sync", () => {
|
describe("sync", () => {
|
||||||
it("app should sync correctly", async () => {
|
it("app should sync correctly", async () => {
|
||||||
const res = await request
|
const { message } = await config.api.application.sync(app.appId)
|
||||||
.post(`/api/applications/${config.getAppId()}/sync`)
|
expect(message).toEqual("App sync completed successfully.")
|
||||||
.set(config.defaultHeaders())
|
|
||||||
.expect("Content-Type", /json/)
|
|
||||||
.expect(200)
|
|
||||||
expect(res.body.message).toEqual("App sync completed successfully.")
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it("app should not sync if production", async () => {
|
it("app should not sync if production", async () => {
|
||||||
const res = await request
|
const { message } = await config.api.application.sync(
|
||||||
.post(`/api/applications/app_123456/sync`)
|
app.appId.replace("_dev", ""),
|
||||||
.set(config.defaultHeaders())
|
{ statusCode: 400 }
|
||||||
.expect("Content-Type", /json/)
|
)
|
||||||
.expect(400)
|
|
||||||
expect(res.body.message).toEqual(
|
expect(message).toEqual(
|
||||||
"This action cannot be performed for production apps"
|
"This action cannot be performed for production apps"
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("app should not sync if sync is disabled", async () => {
|
it("app should not sync if sync is disabled", async () => {
|
||||||
env._set("DISABLE_AUTO_PROD_APP_SYNC", true)
|
env._set("DISABLE_AUTO_PROD_APP_SYNC", true)
|
||||||
const res = await request
|
const { message } = await config.api.application.sync(app.appId)
|
||||||
.post(`/api/applications/${config.getAppId()}/sync`)
|
expect(message).toEqual(
|
||||||
.set(config.defaultHeaders())
|
|
||||||
.expect("Content-Type", /json/)
|
|
||||||
.expect(200)
|
|
||||||
expect(res.body.message).toEqual(
|
|
||||||
"App sync disabled. You can reenable with the DISABLE_AUTO_PROD_APP_SYNC environment variable."
|
"App sync disabled. You can reenable with the DISABLE_AUTO_PROD_APP_SYNC environment variable."
|
||||||
)
|
)
|
||||||
env._set("DISABLE_AUTO_PROD_APP_SYNC", false)
|
env._set("DISABLE_AUTO_PROD_APP_SYNC", false)
|
||||||
|
@ -256,51 +218,26 @@ describe("/applications", () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("unpublish", () => {
|
describe("unpublish", () => {
|
||||||
beforeEach(async () => {
|
|
||||||
// We want to republish as the unpublish will delete the prod app
|
|
||||||
await config.publish()
|
|
||||||
})
|
|
||||||
|
|
||||||
it("should unpublish app with dev app ID", async () => {
|
it("should unpublish app with dev app ID", async () => {
|
||||||
const appId = config.getAppId()
|
await config.api.application.unpublish(app.appId)
|
||||||
await request
|
|
||||||
.post(`/api/applications/${appId}/unpublish`)
|
|
||||||
.set(config.defaultHeaders())
|
|
||||||
.expect(204)
|
|
||||||
expect(events.app.unpublished).toBeCalledTimes(1)
|
expect(events.app.unpublished).toBeCalledTimes(1)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should unpublish app with prod app ID", async () => {
|
it("should unpublish app with prod app ID", async () => {
|
||||||
const appId = config.getProdAppId()
|
await config.api.application.unpublish(app.appId.replace("_dev", ""))
|
||||||
await request
|
|
||||||
.post(`/api/applications/${appId}/unpublish`)
|
|
||||||
.set(config.defaultHeaders())
|
|
||||||
.expect(204)
|
|
||||||
expect(events.app.unpublished).toBeCalledTimes(1)
|
expect(events.app.unpublished).toBeCalledTimes(1)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("delete", () => {
|
describe("delete", () => {
|
||||||
it("should delete published app and dev apps with dev app ID", async () => {
|
it("should delete published app and dev apps with dev app ID", async () => {
|
||||||
await config.createApp("to-delete")
|
await config.api.application.delete(app.appId)
|
||||||
const appId = config.getAppId()
|
|
||||||
await request
|
|
||||||
.delete(`/api/applications/${appId}`)
|
|
||||||
.set(config.defaultHeaders())
|
|
||||||
.expect("Content-Type", /json/)
|
|
||||||
.expect(200)
|
|
||||||
expect(events.app.deleted).toBeCalledTimes(1)
|
expect(events.app.deleted).toBeCalledTimes(1)
|
||||||
expect(events.app.unpublished).toBeCalledTimes(1)
|
expect(events.app.unpublished).toBeCalledTimes(1)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should delete published app and dev app with prod app ID", async () => {
|
it("should delete published app and dev app with prod app ID", async () => {
|
||||||
await config.createApp("to-delete")
|
await config.api.application.delete(app.appId.replace("_dev", ""))
|
||||||
const appId = config.getProdAppId()
|
|
||||||
await request
|
|
||||||
.delete(`/api/applications/${appId}`)
|
|
||||||
.set(config.defaultHeaders())
|
|
||||||
.expect("Content-Type", /json/)
|
|
||||||
.expect(200)
|
|
||||||
expect(events.app.deleted).toBeCalledTimes(1)
|
expect(events.app.deleted).toBeCalledTimes(1)
|
||||||
expect(events.app.unpublished).toBeCalledTimes(1)
|
expect(events.app.unpublished).toBeCalledTimes(1)
|
||||||
})
|
})
|
||||||
|
@ -308,28 +245,18 @@ describe("/applications", () => {
|
||||||
|
|
||||||
describe("POST /api/applications/:appId/sync", () => {
|
describe("POST /api/applications/:appId/sync", () => {
|
||||||
it("should not sync automation logs", async () => {
|
it("should not sync automation logs", async () => {
|
||||||
// setup the apps
|
|
||||||
await config.createApp("testing-auto-logs")
|
|
||||||
const automation = await config.createAutomation()
|
const automation = await config.createAutomation()
|
||||||
await config.publish()
|
await context.doInAppContext(app.appId, () =>
|
||||||
await context.doInAppContext(config.getProdAppId(), () => {
|
config.createAutomationLog(automation)
|
||||||
return config.createAutomationLog(automation)
|
)
|
||||||
})
|
|
||||||
|
|
||||||
// do the sync
|
await config.api.application.sync(app.appId)
|
||||||
const appId = config.getAppId()
|
|
||||||
await request
|
|
||||||
.post(`/api/applications/${appId}/sync`)
|
|
||||||
.set(config.defaultHeaders())
|
|
||||||
.expect("Content-Type", /json/)
|
|
||||||
.expect(200)
|
|
||||||
|
|
||||||
// does exist in prod
|
// does exist in prod
|
||||||
const prodLogs = await config.getAutomationLogs()
|
const prodLogs = await config.getAutomationLogs()
|
||||||
expect(prodLogs.data.length).toBe(1)
|
expect(prodLogs.data.length).toBe(1)
|
||||||
|
|
||||||
// delete prod app so we revert to dev log search
|
await config.api.application.unpublish(app.appId)
|
||||||
await config.unpublish()
|
|
||||||
|
|
||||||
// doesn't exist in dev
|
// doesn't exist in dev
|
||||||
const devLogs = await config.getAutomationLogs()
|
const devLogs = await config.getAutomationLogs()
|
||||||
|
|
|
@ -1,9 +1,14 @@
|
||||||
import { Response } from "supertest"
|
import { Response } from "supertest"
|
||||||
import { App, type CreateAppRequest } from "@budibase/types"
|
import {
|
||||||
|
App,
|
||||||
|
type CreateAppRequest,
|
||||||
|
type FetchAppDefinitionResponse,
|
||||||
|
type FetchAppPackageResponse,
|
||||||
|
} from "@budibase/types"
|
||||||
import TestConfiguration from "../TestConfiguration"
|
import TestConfiguration from "../TestConfiguration"
|
||||||
import { TestAPI } from "./base"
|
import { TestAPI } from "./base"
|
||||||
import { AppStatus } from "../../../db/utils"
|
import { AppStatus } from "../../../db/utils"
|
||||||
import { dbObjectAsPojo } from "oracledb"
|
import { constants } from "@budibase/backend-core"
|
||||||
|
|
||||||
export class ApplicationAPI extends TestAPI {
|
export class ApplicationAPI extends TestAPI {
|
||||||
constructor(config: TestConfiguration) {
|
constructor(config: TestConfiguration) {
|
||||||
|
@ -27,12 +32,55 @@ export class ApplicationAPI extends TestAPI {
|
||||||
const result = await request
|
const result = await request
|
||||||
|
|
||||||
if (result.statusCode !== 200) {
|
if (result.statusCode !== 200) {
|
||||||
fail(JSON.stringify(result.body))
|
throw new Error(JSON.stringify(result.body))
|
||||||
}
|
}
|
||||||
|
|
||||||
return result.body as App
|
return result.body as App
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 publsih 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 }
|
||||||
|
}
|
||||||
|
|
||||||
|
unpublish = async (appId: string): Promise<void> => {
|
||||||
|
await this.request
|
||||||
|
.post(`/api/applications/${appId}/unpublish`)
|
||||||
|
.set(this.config.defaultHeaders())
|
||||||
|
.expect(204)
|
||||||
|
}
|
||||||
|
|
||||||
|
sync = async (
|
||||||
|
appId: string,
|
||||||
|
{ statusCode }: { statusCode: number } = { statusCode: 200 }
|
||||||
|
): 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
|
||||||
|
}
|
||||||
|
|
||||||
getRaw = async (appId: string): Promise<Response> => {
|
getRaw = async (appId: string): Promise<Response> => {
|
||||||
const result = await this.request
|
const result = await this.request
|
||||||
.get(`/api/applications/${appId}/appPackage`)
|
.get(`/api/applications/${appId}/appPackage`)
|
||||||
|
@ -47,6 +95,48 @@ export class ApplicationAPI extends TestAPI {
|
||||||
return result.body.application as App
|
return result.body.application as App
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getDefinition = async (
|
||||||
|
appId: string
|
||||||
|
): 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
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
update = async (
|
||||||
|
appId: string,
|
||||||
|
app: { name?: string; url?: string }
|
||||||
|
): 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
|
||||||
|
}
|
||||||
|
|
||||||
fetch = async ({ status }: { status?: AppStatus } = {}): Promise<App[]> => {
|
fetch = async ({ status }: { status?: AppStatus } = {}): Promise<App[]> => {
|
||||||
let query = []
|
let query = []
|
||||||
if (status) {
|
if (status) {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { User, Document } from "../"
|
import { User, Document, Layout, Screen, Plugin } from "../"
|
||||||
import { SocketSession } from "../../sdk"
|
import { SocketSession, PlanType } from "../../sdk"
|
||||||
|
|
||||||
export type AppMetadataErrors = { [key: string]: string[] }
|
export type AppMetadataErrors = { [key: string]: string[] }
|
||||||
|
|
||||||
|
@ -24,6 +24,8 @@ export interface App extends Document {
|
||||||
icon?: AppIcon
|
icon?: AppIcon
|
||||||
features?: AppFeatures
|
features?: AppFeatures
|
||||||
automations?: AutomationSettings
|
automations?: AutomationSettings
|
||||||
|
usedPlugins?: Plugin[]
|
||||||
|
upgradableVersion?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AppInstance {
|
export interface AppInstance {
|
||||||
|
@ -85,3 +87,18 @@ export interface CreateAppRequest {
|
||||||
encryptionPassword?: string
|
encryptionPassword?: string
|
||||||
templateString?: string
|
templateString?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface FetchAppDefinitionResponse {
|
||||||
|
layouts: Layout[]
|
||||||
|
screens: Screen[]
|
||||||
|
libraries: string[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FetchAppPackageResponse {
|
||||||
|
application: App
|
||||||
|
licenseType: PlanType
|
||||||
|
screens: Screen[]
|
||||||
|
layouts: Layout[]
|
||||||
|
clientLibPath: string
|
||||||
|
hasLock: boolean
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue