Migrate ApplicationAPI
This commit is contained in:
parent
3e76511ffd
commit
7a48fd85ac
|
@ -184,7 +184,7 @@ describe("/applications", () => {
|
||||||
it("app should not sync if production", async () => {
|
it("app should not sync if production", async () => {
|
||||||
const { message } = await config.api.application.sync(
|
const { message } = await config.api.application.sync(
|
||||||
app.appId.replace("_dev", ""),
|
app.appId.replace("_dev", ""),
|
||||||
{ statusCode: 400 }
|
{ status: 400 }
|
||||||
)
|
)
|
||||||
|
|
||||||
expect(message).toEqual(
|
expect(message).toEqual(
|
||||||
|
|
|
@ -30,9 +30,9 @@ describe("migrations", () => {
|
||||||
|
|
||||||
const appId = config.getAppId()
|
const appId = config.getAppId()
|
||||||
|
|
||||||
const response = await config.api.application.getRaw(appId)
|
await config.api.application.get(appId, {
|
||||||
|
headersNotPresent: [Header.MIGRATING_APP],
|
||||||
expect(response.headers[Header.MIGRATING_APP]).toBeUndefined()
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it("accessing an app that has pending migrations will attach the migrating header", async () => {
|
it("accessing an app that has pending migrations will attach the migrating header", async () => {
|
||||||
|
@ -46,8 +46,10 @@ describe("migrations", () => {
|
||||||
func: async () => {},
|
func: async () => {},
|
||||||
})
|
})
|
||||||
|
|
||||||
const response = await config.api.application.getRaw(appId)
|
await config.api.application.get(appId, {
|
||||||
|
headers: {
|
||||||
expect(response.headers[Header.MIGRATING_APP]).toEqual(appId)
|
[Header.MIGRATING_APP]: appId,
|
||||||
|
},
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
import { Response } from "supertest"
|
|
||||||
import {
|
import {
|
||||||
App,
|
App,
|
||||||
|
PublishResponse,
|
||||||
type CreateAppRequest,
|
type CreateAppRequest,
|
||||||
type FetchAppDefinitionResponse,
|
type FetchAppDefinitionResponse,
|
||||||
type FetchAppPackageResponse,
|
type FetchAppPackageResponse,
|
||||||
} from "@budibase/types"
|
} from "@budibase/types"
|
||||||
import TestConfiguration from "../TestConfiguration"
|
import TestConfiguration from "../TestConfiguration"
|
||||||
import { TestAPI } from "./base"
|
import { Expectations, TestAPI } from "./base"
|
||||||
import { AppStatus } from "../../../db/utils"
|
import { AppStatus } from "../../../db/utils"
|
||||||
import { constants } from "@budibase/backend-core"
|
import { constants } from "@budibase/backend-core"
|
||||||
|
|
||||||
|
@ -15,179 +15,124 @@ export class ApplicationAPI extends TestAPI {
|
||||||
super(config)
|
super(config)
|
||||||
}
|
}
|
||||||
|
|
||||||
create = async (app: CreateAppRequest): Promise<App> => {
|
create = async (
|
||||||
const request = this.request
|
app: CreateAppRequest,
|
||||||
.post("/api/applications")
|
expectations?: Expectations
|
||||||
.set(this.config.defaultHeaders())
|
): Promise<App> => {
|
||||||
.expect("Content-Type", /json/)
|
const files = app.templateFile ? { templateFile: app.templateFile } : {}
|
||||||
|
delete app.templateFile
|
||||||
for (const key of Object.keys(app)) {
|
return await this._post<App>("/api/applications", {
|
||||||
request.field(key, (app as any)[key])
|
fields: app,
|
||||||
|
files,
|
||||||
|
expectations,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if (app.templateFile) {
|
delete = async (
|
||||||
request.attach("templateFile", app.templateFile)
|
appId: string,
|
||||||
|
expectations?: Expectations
|
||||||
|
): Promise<void> => {
|
||||||
|
await this._delete(`/api/applications/${appId}`, { expectations })
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await request
|
publish = async (appId: string): Promise<PublishResponse> => {
|
||||||
|
return await this._post<PublishResponse>(
|
||||||
if (result.statusCode !== 200) {
|
`/api/applications/${appId}/publish`,
|
||||||
throw new Error(JSON.stringify(result.body))
|
{
|
||||||
}
|
|
||||||
|
|
||||||
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 publish endpoint does take an :appId parameter, it doesn't
|
// While the publish endpoint does take an :appId parameter, it doesn't
|
||||||
// use it. It uses the appId from the context.
|
// use it. It uses the appId from the context.
|
||||||
let headers = {
|
headers: {
|
||||||
...this.config.defaultHeaders(),
|
|
||||||
[constants.Header.APP_ID]: appId,
|
[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> => {
|
unpublish = async (appId: string): Promise<void> => {
|
||||||
await this.request
|
await this._post(`/api/applications/${appId}/unpublish`, {
|
||||||
.post(`/api/applications/${appId}/unpublish`)
|
expectations: { status: 204 },
|
||||||
.set(this.config.defaultHeaders())
|
})
|
||||||
.expect(204)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sync = async (
|
sync = async (
|
||||||
appId: string,
|
appId: string,
|
||||||
{ statusCode }: { statusCode: number } = { statusCode: 200 }
|
expectations?: Expectations
|
||||||
): Promise<{ message: string }> => {
|
): Promise<{ message: string }> => {
|
||||||
const result = await this.request
|
return await this._post<{ message: string }>(
|
||||||
.post(`/api/applications/${appId}/sync`)
|
`/api/applications/${appId}/sync`,
|
||||||
.set(this.config.defaultHeaders())
|
{ expectations }
|
||||||
.expect("Content-Type", /json/)
|
)
|
||||||
.expect(statusCode)
|
|
||||||
return result.body
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getRaw = async (appId: string): Promise<Response> => {
|
get = async (appId: string, expectations?: Expectations): Promise<App> => {
|
||||||
// While the appPackage endpoint does take an :appId parameter, it doesn't
|
return await this._get<App>(`/api/applications/${appId}`, {
|
||||||
// use it. It uses the appId from the context.
|
// While the get endpoint does take an :appId parameter, it doesn't use
|
||||||
let headers = {
|
// it. It uses the appId from the context.
|
||||||
...this.config.defaultHeaders(),
|
headers: {
|
||||||
[constants.Header.APP_ID]: appId,
|
[constants.Header.APP_ID]: appId,
|
||||||
}
|
},
|
||||||
const result = await this.request
|
expectations,
|
||||||
.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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getDefinition = async (
|
getDefinition = async (
|
||||||
appId: string
|
appId: string,
|
||||||
|
expectations?: Expectations
|
||||||
): Promise<FetchAppDefinitionResponse> => {
|
): Promise<FetchAppDefinitionResponse> => {
|
||||||
const result = await this.request
|
return await this._get<FetchAppDefinitionResponse>(
|
||||||
.get(`/api/applications/${appId}/definition`)
|
`/api/applications/${appId}/definition`,
|
||||||
.set(this.config.defaultHeaders())
|
{ expectations }
|
||||||
.expect("Content-Type", /json/)
|
)
|
||||||
.expect(200)
|
|
||||||
return result.body as FetchAppDefinitionResponse
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getAppPackage = async (appId: string): Promise<FetchAppPackageResponse> => {
|
getAppPackage = async (
|
||||||
const result = await this.request
|
appId: string,
|
||||||
.get(`/api/applications/${appId}/appPackage`)
|
expectations?: Expectations
|
||||||
.set(this.config.defaultHeaders())
|
): Promise<FetchAppPackageResponse> => {
|
||||||
.expect("Content-Type", /json/)
|
return await this._get<FetchAppPackageResponse>(
|
||||||
.expect(200)
|
`/api/applications/${appId}/appPackage`,
|
||||||
return result.body
|
{ expectations }
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
update = async (
|
update = async (
|
||||||
appId: string,
|
appId: string,
|
||||||
app: { name?: string; url?: string }
|
app: { name?: string; url?: string },
|
||||||
|
expectations?: Expectations
|
||||||
): Promise<App> => {
|
): Promise<App> => {
|
||||||
const request = this.request
|
return await this._put<App>(`/api/applications/${appId}`, {
|
||||||
.put(`/api/applications/${appId}`)
|
fields: app,
|
||||||
.set(this.config.defaultHeaders())
|
expectations,
|
||||||
.expect("Content-Type", /json/)
|
})
|
||||||
|
|
||||||
for (const key of Object.keys(app)) {
|
|
||||||
request.field(key, (app as any)[key])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await request
|
updateClient = async (
|
||||||
|
appId: string,
|
||||||
if (result.statusCode !== 200) {
|
expectations?: Expectations
|
||||||
throw new Error(JSON.stringify(result.body))
|
): Promise<void> => {
|
||||||
}
|
await this._post(`/api/applications/${appId}/client/update`, {
|
||||||
|
|
||||||
return result.body as App
|
|
||||||
}
|
|
||||||
|
|
||||||
updateClient = async (appId: string): Promise<void> => {
|
|
||||||
// While the updateClient endpoint does take an :appId parameter, it doesn't
|
// While the updateClient endpoint does take an :appId parameter, it doesn't
|
||||||
// use it. It uses the appId from the context.
|
// use it. It uses the appId from the context.
|
||||||
let headers = {
|
headers: {
|
||||||
...this.config.defaultHeaders(),
|
|
||||||
[constants.Header.APP_ID]: appId,
|
[constants.Header.APP_ID]: appId,
|
||||||
}
|
},
|
||||||
const response = await this.request
|
expectations,
|
||||||
.post(`/api/applications/${appId}/client/update`)
|
})
|
||||||
.set(headers)
|
|
||||||
.expect("Content-Type", /json/)
|
|
||||||
|
|
||||||
if (response.statusCode !== 200) {
|
|
||||||
throw new Error(JSON.stringify(response.body))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
revertClient = async (appId: string): Promise<void> => {
|
revertClient = async (appId: string): Promise<void> => {
|
||||||
|
await this._post(`/api/applications/${appId}/client/revert`, {
|
||||||
// While the revertClient endpoint does take an :appId parameter, it doesn't
|
// While the revertClient endpoint does take an :appId parameter, it doesn't
|
||||||
// use it. It uses the appId from the context.
|
// use it. It uses the appId from the context.
|
||||||
let headers = {
|
headers: {
|
||||||
...this.config.defaultHeaders(),
|
|
||||||
[constants.Header.APP_ID]: appId,
|
[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))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fetch = async ({ status }: { status?: AppStatus } = {}): Promise<App[]> => {
|
fetch = async ({ status }: { status?: AppStatus } = {}): Promise<App[]> => {
|
||||||
let query = []
|
return await this._get<App[]>("/api/applications", {
|
||||||
if (status) {
|
query: { 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[]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import exp from "constants"
|
||||||
import TestConfiguration from "../TestConfiguration"
|
import TestConfiguration from "../TestConfiguration"
|
||||||
import { SuperTest, Test } from "supertest"
|
import { SuperTest, Test } from "supertest"
|
||||||
|
|
||||||
|
@ -11,10 +12,13 @@ export interface TestAPIOpts {
|
||||||
export interface Expectations {
|
export interface Expectations {
|
||||||
status?: number
|
status?: number
|
||||||
contentType?: string | RegExp
|
contentType?: string | RegExp
|
||||||
|
headers?: Record<string, string | RegExp>
|
||||||
|
headersNotPresent?: string[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RequestOpts {
|
export interface RequestOpts {
|
||||||
headers?: Headers
|
headers?: Headers
|
||||||
|
query?: Record<string, string | undefined>
|
||||||
body?: Record<string, any>
|
body?: Record<string, any>
|
||||||
fields?: Record<string, any>
|
fields?: Record<string, any>
|
||||||
files?: Record<string, any>
|
files?: Record<string, any>
|
||||||
|
@ -30,11 +34,8 @@ export abstract class TestAPI {
|
||||||
this.request = config.request!
|
this.request = config.request!
|
||||||
}
|
}
|
||||||
|
|
||||||
protected _get = async <T>(
|
protected _get = async <T>(url: string, opts?: RequestOpts): Promise<T> => {
|
||||||
url: string,
|
return await this._request<T>("get", url, opts)
|
||||||
expectations?: Expectations
|
|
||||||
): Promise<T> => {
|
|
||||||
return await this._request<T>("get", url, { expectations })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected _post = async <T>(url: string, opts?: RequestOpts): Promise<T> => {
|
protected _post = async <T>(url: string, opts?: RequestOpts): Promise<T> => {
|
||||||
|
@ -61,9 +62,26 @@ export abstract class TestAPI {
|
||||||
url: string,
|
url: string,
|
||||||
opts?: RequestOpts
|
opts?: RequestOpts
|
||||||
): Promise<T> => {
|
): Promise<T> => {
|
||||||
const { headers = {}, body, fields, files, expectations } = opts || {}
|
const {
|
||||||
|
headers = {},
|
||||||
|
query = {},
|
||||||
|
body,
|
||||||
|
fields,
|
||||||
|
files,
|
||||||
|
expectations,
|
||||||
|
} = opts || {}
|
||||||
const { status = 200, contentType = /json/ } = expectations || {}
|
const { status = 200, contentType = /json/ } = expectations || {}
|
||||||
|
|
||||||
|
let queryParams = []
|
||||||
|
for (const [key, value] of Object.entries(query)) {
|
||||||
|
if (value) {
|
||||||
|
queryParams.push(`${key}=${value}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (queryParams.length) {
|
||||||
|
url += `?${queryParams.join("&")}`
|
||||||
|
}
|
||||||
|
|
||||||
let request = this.request[method](url).set(this.config.defaultHeaders())
|
let request = this.request[method](url).set(this.config.defaultHeaders())
|
||||||
if (headers) {
|
if (headers) {
|
||||||
request = request.set(headers)
|
request = request.set(headers)
|
||||||
|
@ -81,13 +99,22 @@ export abstract class TestAPI {
|
||||||
request = request.attach(key, value)
|
request = request.attach(key, value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (contentType) {
|
if (contentType && status !== 204) {
|
||||||
if (contentType instanceof RegExp) {
|
if (contentType instanceof RegExp) {
|
||||||
request = request.expect("Content-Type", contentType)
|
request = request.expect("Content-Type", contentType)
|
||||||
} else {
|
} else {
|
||||||
request = request.expect("Content-Type", contentType)
|
request = request.expect("Content-Type", contentType)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (expectations?.headers) {
|
||||||
|
for (const [key, value] of Object.entries(expectations.headers)) {
|
||||||
|
if (value instanceof RegExp) {
|
||||||
|
request = request.expect(key, value)
|
||||||
|
} else {
|
||||||
|
request = request.expect(key, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const response = await request
|
const response = await request
|
||||||
|
|
||||||
|
@ -99,6 +126,16 @@ export abstract class TestAPI {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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]}"`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return response.body as T
|
return response.body as T
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,11 +19,11 @@ export class TableAPI extends TestAPI {
|
||||||
}
|
}
|
||||||
|
|
||||||
fetch = async (expectations?: Expectations): Promise<Table[]> => {
|
fetch = async (expectations?: Expectations): Promise<Table[]> => {
|
||||||
return await this._get<Table[]>("/api/tables", expectations)
|
return await this._get<Table[]>("/api/tables", { expectations })
|
||||||
}
|
}
|
||||||
|
|
||||||
get = async (tableId: string, expectations: Expectations): Promise<Table> => {
|
get = async (tableId: string, expectations: Expectations): Promise<Table> => {
|
||||||
return await this._get<Table>(`/api/tables/${tableId}`, expectations)
|
return await this._get<Table>(`/api/tables/${tableId}`, { expectations })
|
||||||
}
|
}
|
||||||
|
|
||||||
migrate = async (
|
migrate = async (
|
||||||
|
|
|
@ -27,3 +27,9 @@ export interface FetchAppPackageResponse {
|
||||||
clientLibPath: string
|
clientLibPath: string
|
||||||
hasLock: boolean
|
hasLock: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface PublishResponse {
|
||||||
|
_id: string
|
||||||
|
status: string
|
||||||
|
appUrl: string
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue