2023-02-03 12:28:27 +01:00
|
|
|
import { generator, mocks, structures } from "@budibase/backend-core/tests"
|
2022-12-12 23:02:32 +01:00
|
|
|
|
|
|
|
// init the licensing mock
|
|
|
|
import * as pro from "@budibase/pro"
|
2022-11-28 20:12:23 +01:00
|
|
|
import { init as dbInit } from "../../db"
|
|
|
|
import env from "../../environment"
|
|
|
|
import {
|
2021-03-03 19:41:49 +01:00
|
|
|
basicAutomation,
|
2023-10-26 14:19:09 +02:00
|
|
|
basicAutomationResults,
|
2021-03-04 11:05:50 +01:00
|
|
|
basicDatasource,
|
2023-10-26 14:19:09 +02:00
|
|
|
basicLayout,
|
2021-03-04 11:05:50 +01:00
|
|
|
basicQuery,
|
2023-10-26 14:19:09 +02:00
|
|
|
basicRole,
|
|
|
|
basicRow,
|
2021-03-08 15:49:19 +01:00
|
|
|
basicScreen,
|
2023-10-26 14:19:09 +02:00
|
|
|
basicTable,
|
2021-03-08 16:57:19 +01:00
|
|
|
basicWebhook,
|
2022-11-28 20:12:23 +01:00
|
|
|
} from "./structures"
|
|
|
|
import {
|
2022-11-21 19:33:34 +01:00
|
|
|
cache,
|
2023-10-26 14:19:09 +02:00
|
|
|
constants,
|
2022-11-21 19:33:34 +01:00
|
|
|
context,
|
2022-11-28 20:12:23 +01:00
|
|
|
db as dbCore,
|
2022-11-21 19:33:34 +01:00
|
|
|
encryption,
|
2023-09-06 01:49:54 +02:00
|
|
|
env as coreEnv,
|
2023-10-26 14:19:09 +02:00
|
|
|
roles,
|
|
|
|
sessions,
|
|
|
|
tenancy,
|
2024-07-11 15:27:48 +02:00
|
|
|
utils,
|
2022-11-28 20:12:23 +01:00
|
|
|
} from "@budibase/backend-core"
|
2024-01-26 11:07:06 +01:00
|
|
|
import {
|
|
|
|
app as appController,
|
|
|
|
deploy as deployController,
|
|
|
|
role as roleController,
|
|
|
|
automation as automationController,
|
|
|
|
webhook as webhookController,
|
|
|
|
query as queryController,
|
|
|
|
screen as screenController,
|
|
|
|
layout as layoutController,
|
|
|
|
view as viewController,
|
|
|
|
} from "./controllers"
|
|
|
|
|
2022-11-28 20:12:23 +01:00
|
|
|
import { cleanup } from "../../utilities/fileSystem"
|
|
|
|
import { generateUserMetadataID } from "../../db/utils"
|
|
|
|
import { startup } from "../../startup"
|
2023-01-16 16:35:41 +01:00
|
|
|
import supertest from "supertest"
|
2023-02-06 10:12:13 +01:00
|
|
|
import {
|
2023-02-23 14:43:01 +01:00
|
|
|
App,
|
2023-02-06 10:12:13 +01:00
|
|
|
AuthToken,
|
2023-10-26 14:19:09 +02:00
|
|
|
Automation,
|
|
|
|
CreateViewRequest,
|
2023-02-06 10:12:13 +01:00
|
|
|
Datasource,
|
2023-10-26 14:19:09 +02:00
|
|
|
FieldType,
|
|
|
|
INTERNAL_TABLE_SOURCE_ID,
|
2024-02-28 13:13:13 +01:00
|
|
|
Layout,
|
|
|
|
Query,
|
2023-10-26 14:19:09 +02:00
|
|
|
RelationshipFieldMetadata,
|
|
|
|
RelationshipType,
|
2023-02-06 10:12:13 +01:00
|
|
|
Row,
|
2024-02-28 13:13:13 +01:00
|
|
|
Screen,
|
2024-03-28 18:57:37 +01:00
|
|
|
RowSearchParams,
|
2023-02-06 10:12:13 +01:00
|
|
|
SourceName,
|
|
|
|
Table,
|
2023-10-26 14:19:09 +02:00
|
|
|
TableSourceType,
|
|
|
|
User,
|
2024-02-28 12:46:58 +01:00
|
|
|
UserCtx,
|
2023-09-08 10:43:19 +02:00
|
|
|
View,
|
2024-02-28 13:13:13 +01:00
|
|
|
Webhook,
|
2024-01-29 23:25:12 +01:00
|
|
|
WithRequired,
|
2025-02-25 19:23:29 +01:00
|
|
|
DevInfo,
|
2023-02-06 10:12:13 +01:00
|
|
|
} from "@budibase/types"
|
2021-03-03 18:52:41 +01:00
|
|
|
|
2023-07-18 12:56:24 +02:00
|
|
|
import API from "./api"
|
2023-12-05 17:28:19 +01:00
|
|
|
import jwt, { Secret } from "jsonwebtoken"
|
2024-02-28 11:08:42 +01:00
|
|
|
import { Server } from "http"
|
2023-07-18 12:56:24 +02:00
|
|
|
|
2024-07-11 15:27:48 +02:00
|
|
|
const newid = utils.newid
|
|
|
|
|
2023-10-26 14:19:09 +02:00
|
|
|
mocks.licenses.init(pro)
|
|
|
|
|
|
|
|
// use unlimited license by default
|
|
|
|
mocks.licenses.useUnlimited()
|
|
|
|
|
|
|
|
dbInit()
|
2023-07-18 12:56:24 +02:00
|
|
|
|
2024-05-02 17:26:09 +02:00
|
|
|
export interface CreateAppRequest {
|
|
|
|
appName: string
|
|
|
|
url?: string
|
|
|
|
snippets?: any[]
|
|
|
|
}
|
|
|
|
|
2024-02-09 15:27:48 +01:00
|
|
|
export interface TableToBuild extends Omit<Table, "sourceId" | "sourceType"> {
|
2023-10-25 20:00:25 +02:00
|
|
|
sourceId?: string
|
2023-10-26 14:19:09 +02:00
|
|
|
sourceType?: TableSourceType
|
2023-10-25 20:00:25 +02:00
|
|
|
}
|
|
|
|
|
2024-02-12 12:34:39 +01:00
|
|
|
export default class TestConfiguration {
|
2024-02-28 11:08:42 +01:00
|
|
|
server?: Server
|
|
|
|
request?: supertest.SuperTest<supertest.Test>
|
2022-11-28 20:12:23 +01:00
|
|
|
started: boolean
|
2024-02-28 11:08:42 +01:00
|
|
|
appId?: string
|
2024-02-28 12:46:58 +01:00
|
|
|
allApps: App[]
|
2023-02-23 14:43:01 +01:00
|
|
|
app?: App
|
2024-02-28 11:08:42 +01:00
|
|
|
prodApp?: App
|
|
|
|
prodAppId?: string
|
|
|
|
user?: User
|
|
|
|
userMetadataId?: string
|
2023-07-18 10:14:13 +02:00
|
|
|
table?: Table
|
2024-02-28 12:46:58 +01:00
|
|
|
automation?: Automation
|
2023-08-30 17:42:26 +02:00
|
|
|
datasource?: Datasource
|
2023-02-23 14:43:01 +01:00
|
|
|
tenantId?: string
|
2023-07-18 12:56:24 +02:00
|
|
|
api: API
|
2024-02-21 10:12:06 +01:00
|
|
|
csrfToken?: string
|
2024-10-17 18:15:41 +02:00
|
|
|
temporaryHeaders?: Record<string, string | string[]>
|
2022-11-28 20:12:23 +01:00
|
|
|
|
2021-03-11 19:29:48 +01:00
|
|
|
constructor(openServer = true) {
|
|
|
|
if (openServer) {
|
2021-06-25 17:14:06 +02:00
|
|
|
// use a random port because it doesn't matter
|
2022-11-28 20:12:23 +01:00
|
|
|
env.PORT = "0"
|
2023-08-17 17:39:25 +02:00
|
|
|
this.server = require("../../app").getServer()
|
2021-03-11 19:29:48 +01:00
|
|
|
// we need the request for logging in, involves cookies, hard to fake
|
|
|
|
this.request = supertest(this.server)
|
2022-10-27 16:15:08 +02:00
|
|
|
this.started = true
|
|
|
|
} else {
|
|
|
|
this.started = false
|
2021-03-11 19:29:48 +01:00
|
|
|
}
|
2024-02-28 11:08:42 +01:00
|
|
|
this.appId = undefined
|
2021-03-11 13:09:47 +01:00
|
|
|
this.allApps = []
|
2023-07-18 12:56:24 +02:00
|
|
|
|
|
|
|
this.api = new API(this)
|
2023-01-25 18:11:37 +01:00
|
|
|
}
|
|
|
|
|
2021-03-05 13:11:44 +01:00
|
|
|
getRequest() {
|
|
|
|
return this.request
|
|
|
|
}
|
|
|
|
|
2022-02-25 16:55:19 +01:00
|
|
|
getApp() {
|
2024-02-28 12:46:58 +01:00
|
|
|
if (!this.app) {
|
|
|
|
throw new Error("app has not been initialised, call config.init() first")
|
|
|
|
}
|
2022-02-25 16:55:19 +01:00
|
|
|
return this.app
|
|
|
|
}
|
|
|
|
|
2022-08-10 12:01:54 +02:00
|
|
|
getProdApp() {
|
2024-02-28 12:46:58 +01:00
|
|
|
if (!this.prodApp) {
|
|
|
|
throw new Error(
|
|
|
|
"prodApp has not been initialised, call config.init() first"
|
|
|
|
)
|
|
|
|
}
|
2022-08-10 12:01:54 +02:00
|
|
|
return this.prodApp
|
|
|
|
}
|
|
|
|
|
2021-03-05 13:11:44 +01:00
|
|
|
getAppId() {
|
2023-12-04 09:23:01 +01:00
|
|
|
if (!this.appId) {
|
2024-02-28 12:46:58 +01:00
|
|
|
throw new Error(
|
|
|
|
"appId has not been initialised, call config.init() first"
|
|
|
|
)
|
2023-12-04 09:23:01 +01:00
|
|
|
}
|
2021-03-05 13:11:44 +01:00
|
|
|
return this.appId
|
|
|
|
}
|
|
|
|
|
2022-04-04 16:59:00 +02:00
|
|
|
getProdAppId() {
|
2024-02-28 11:08:42 +01:00
|
|
|
if (!this.prodAppId) {
|
|
|
|
throw new Error(
|
|
|
|
"prodAppId has not been initialised, call config.init() first"
|
|
|
|
)
|
|
|
|
}
|
2022-04-04 16:59:00 +02:00
|
|
|
return this.prodAppId
|
|
|
|
}
|
|
|
|
|
2024-02-28 11:08:42 +01:00
|
|
|
getUser(): User {
|
|
|
|
if (!this.user) {
|
|
|
|
throw new Error("User has not been initialised, call config.init() first")
|
|
|
|
}
|
|
|
|
return this.user
|
|
|
|
}
|
|
|
|
|
2022-06-17 13:00:42 +02:00
|
|
|
getUserDetails() {
|
2024-02-28 11:08:42 +01:00
|
|
|
const user = this.getUser()
|
2022-06-17 13:00:42 +02:00
|
|
|
return {
|
2024-02-28 11:08:42 +01:00
|
|
|
globalId: user._id!,
|
|
|
|
email: user.email,
|
|
|
|
firstName: user.firstName,
|
|
|
|
lastName: user.lastName,
|
2024-02-21 10:36:17 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-02-28 12:46:58 +01:00
|
|
|
getAutomation() {
|
|
|
|
if (!this.automation) {
|
|
|
|
throw new Error(
|
|
|
|
"automation has not been initialised, call config.init() first"
|
|
|
|
)
|
|
|
|
}
|
|
|
|
return this.automation
|
|
|
|
}
|
|
|
|
|
2024-02-28 13:13:13 +01:00
|
|
|
getDatasource() {
|
|
|
|
if (!this.datasource) {
|
|
|
|
throw new Error(
|
|
|
|
"datasource has not been initialised, call config.init() first"
|
|
|
|
)
|
2022-06-17 13:00:42 +02:00
|
|
|
}
|
2024-02-28 13:13:13 +01:00
|
|
|
return this.datasource
|
2022-06-17 13:00:42 +02:00
|
|
|
}
|
|
|
|
|
2023-07-13 12:17:24 +02:00
|
|
|
async doInContext<T>(
|
2024-02-28 11:08:42 +01:00
|
|
|
appId: string | undefined,
|
2023-07-13 12:17:24 +02:00
|
|
|
task: () => Promise<T>
|
|
|
|
): Promise<T> {
|
2023-01-25 18:11:37 +01:00
|
|
|
const tenant = this.getTenantId()
|
|
|
|
return tenancy.doInTenant(tenant, () => {
|
2024-02-28 11:08:42 +01:00
|
|
|
if (!appId) {
|
|
|
|
appId = this.appId
|
|
|
|
}
|
|
|
|
|
2022-04-21 15:56:14 +02:00
|
|
|
// check if already in a context
|
2024-02-28 11:08:42 +01:00
|
|
|
if (context.getAppId() == null && appId) {
|
2022-04-21 15:56:14 +02:00
|
|
|
return context.doInAppContext(appId, async () => {
|
|
|
|
return task()
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
return task()
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2022-04-06 17:57:56 +02:00
|
|
|
// SETUP / TEARDOWN
|
|
|
|
|
|
|
|
// use a new id as the name to avoid name collisions
|
2024-05-07 10:13:43 +02:00
|
|
|
async init(appName = newid()) {
|
2022-10-27 16:15:08 +02:00
|
|
|
if (!this.started) {
|
|
|
|
await startup()
|
|
|
|
}
|
2024-05-07 10:13:43 +02:00
|
|
|
return this.newTenant(appName)
|
2022-04-06 17:57:56 +02:00
|
|
|
}
|
|
|
|
|
2023-02-07 13:45:41 +01:00
|
|
|
end() {
|
2022-04-06 17:57:56 +02:00
|
|
|
if (!this) {
|
|
|
|
return
|
|
|
|
}
|
2024-11-06 18:18:34 +01:00
|
|
|
|
2024-11-06 23:08:37 +01:00
|
|
|
if (this.server) {
|
|
|
|
this.server.close()
|
|
|
|
} else {
|
|
|
|
require("../../app").getServer().close()
|
|
|
|
}
|
2023-02-07 13:45:41 +01:00
|
|
|
if (this.allApps) {
|
|
|
|
cleanup(this.allApps.map(app => app.appId))
|
|
|
|
}
|
2022-04-06 17:57:56 +02:00
|
|
|
}
|
|
|
|
|
2025-02-25 19:23:29 +01:00
|
|
|
async withUser<T>(user: User, f: () => Promise<T>): Promise<T> {
|
2024-03-04 17:42:41 +01:00
|
|
|
const oldUser = this.user
|
|
|
|
this.user = user
|
|
|
|
try {
|
2024-03-05 11:05:05 +01:00
|
|
|
return await f()
|
2024-03-04 17:42:41 +01:00
|
|
|
} finally {
|
|
|
|
this.user = oldUser
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-01-28 18:43:03 +01:00
|
|
|
async withApp<R>(app: App | string, f: () => Promise<R>) {
|
2024-05-29 18:07:29 +02:00
|
|
|
const oldAppId = this.appId
|
|
|
|
this.appId = typeof app === "string" ? app : app.appId
|
2025-02-17 18:39:38 +01:00
|
|
|
return await context.doInAppContext(this.appId, async () => {
|
|
|
|
try {
|
|
|
|
return await f()
|
|
|
|
} finally {
|
|
|
|
this.appId = oldAppId
|
|
|
|
}
|
|
|
|
})
|
2024-05-29 18:07:29 +02:00
|
|
|
}
|
|
|
|
|
2025-01-28 18:43:03 +01:00
|
|
|
async withProdApp<R>(f: () => Promise<R>) {
|
|
|
|
return await this.withApp(this.getProdAppId(), f)
|
|
|
|
}
|
|
|
|
|
2022-04-06 17:57:56 +02:00
|
|
|
// UTILS
|
|
|
|
|
2024-02-28 13:13:13 +01:00
|
|
|
_req<Req extends Record<string, any> | void, Res>(
|
2024-02-28 12:46:58 +01:00
|
|
|
handler: (ctx: UserCtx<Req, Res>) => Promise<void>,
|
2024-02-28 11:08:42 +01:00
|
|
|
body?: Req,
|
2024-02-28 13:13:13 +01:00
|
|
|
params?: Record<string, string | undefined>
|
2024-02-28 12:46:58 +01:00
|
|
|
): Promise<Res> {
|
2022-07-13 14:22:21 +02:00
|
|
|
// create a fake request ctx
|
2022-11-28 20:12:23 +01:00
|
|
|
const request: any = {}
|
2022-08-10 12:01:54 +02:00
|
|
|
const appId = this.appId
|
2022-07-13 14:22:21 +02:00
|
|
|
request.appId = appId
|
2021-03-03 18:52:41 +01:00
|
|
|
// fake cookies, we don't need them
|
|
|
|
request.cookies = { set: () => {}, get: () => {} }
|
2023-01-25 18:11:37 +01:00
|
|
|
request.user = { appId, tenantId: this.getTenantId() }
|
2021-04-26 15:14:51 +02:00
|
|
|
request.query = {}
|
2021-03-03 18:52:41 +01:00
|
|
|
request.request = {
|
2022-07-13 14:22:21 +02:00
|
|
|
body,
|
2021-03-03 18:52:41 +01:00
|
|
|
}
|
2022-07-13 14:22:21 +02:00
|
|
|
if (params) {
|
|
|
|
request.params = params
|
|
|
|
}
|
2024-01-24 15:11:24 +01:00
|
|
|
request.throw = (status: number, message: string) => {
|
|
|
|
throw new Error(`Error ${status} - ${message}`)
|
|
|
|
}
|
2022-07-13 14:22:21 +02:00
|
|
|
return this.doInContext(appId, async () => {
|
2024-02-28 11:08:42 +01:00
|
|
|
await handler(request)
|
2022-01-28 16:43:51 +01:00
|
|
|
return request.body
|
2022-04-21 15:56:14 +02:00
|
|
|
})
|
2021-03-03 18:52:41 +01:00
|
|
|
}
|
|
|
|
|
2022-04-06 17:57:56 +02:00
|
|
|
// USER / AUTH
|
2024-02-28 11:08:42 +01:00
|
|
|
async globalUser(config: Partial<User> = {}): Promise<User> {
|
2024-02-21 10:12:06 +01:00
|
|
|
const {
|
2024-02-28 11:08:42 +01:00
|
|
|
_id = `us_${newid()}`,
|
2024-02-21 10:12:06 +01:00
|
|
|
firstName = generator.first(),
|
|
|
|
lastName = generator.last(),
|
2024-02-28 11:08:42 +01:00
|
|
|
builder = { global: true },
|
|
|
|
admin = { global: false },
|
2024-02-21 12:30:22 +01:00
|
|
|
email = generator.email({ domain: "example.com" }),
|
2024-02-28 12:16:26 +01:00
|
|
|
tenantId = this.getTenantId(),
|
|
|
|
roles = {},
|
2024-02-21 10:12:06 +01:00
|
|
|
} = config
|
|
|
|
|
2023-02-13 12:57:30 +01:00
|
|
|
const db = tenancy.getTenantDB(this.getTenantId())
|
2024-02-28 12:16:26 +01:00
|
|
|
let existing: Partial<User> = {}
|
2023-02-13 12:57:30 +01:00
|
|
|
try {
|
2024-02-28 11:08:42 +01:00
|
|
|
existing = await db.get<User>(_id)
|
2023-02-13 12:57:30 +01:00
|
|
|
} catch (err) {
|
2024-02-28 12:16:26 +01:00
|
|
|
// ignore
|
2023-02-13 12:57:30 +01:00
|
|
|
}
|
2023-10-19 18:28:55 +02:00
|
|
|
const user: User = {
|
2024-02-28 12:16:26 +01:00
|
|
|
_id,
|
2023-02-13 12:57:30 +01:00
|
|
|
...existing,
|
2024-02-28 12:16:26 +01:00
|
|
|
...config,
|
2024-03-05 15:37:06 +01:00
|
|
|
_rev: existing._rev,
|
2024-02-28 12:16:26 +01:00
|
|
|
email,
|
|
|
|
roles,
|
|
|
|
tenantId,
|
2023-02-13 12:57:30 +01:00
|
|
|
firstName,
|
|
|
|
lastName,
|
2024-02-28 12:16:26 +01:00
|
|
|
builder,
|
|
|
|
admin,
|
2023-02-13 12:57:30 +01:00
|
|
|
}
|
2024-02-28 11:08:42 +01:00
|
|
|
await sessions.createASession(_id, {
|
2024-03-05 15:37:06 +01:00
|
|
|
sessionId: this.sessionIdForUser(_id),
|
2023-02-13 12:57:30 +01:00
|
|
|
tenantId: this.getTenantId(),
|
2024-02-21 10:12:06 +01:00
|
|
|
csrfToken: this.csrfToken,
|
2024-09-12 10:38:11 +02:00
|
|
|
email,
|
2022-01-25 23:54:50 +01:00
|
|
|
})
|
2023-02-13 12:57:30 +01:00
|
|
|
const resp = await db.put(user)
|
2024-03-05 15:37:06 +01:00
|
|
|
await cache.user.invalidateUser(_id)
|
2023-02-13 12:57:30 +01:00
|
|
|
return {
|
|
|
|
_rev: resp.rev,
|
|
|
|
...user,
|
|
|
|
}
|
2021-04-23 19:07:39 +02:00
|
|
|
}
|
|
|
|
|
2024-02-28 11:08:42 +01:00
|
|
|
async createUser(user: Partial<User> = {}): Promise<User> {
|
2024-03-05 15:37:06 +01:00
|
|
|
return await this.globalUser(user)
|
2021-03-03 18:52:41 +01:00
|
|
|
}
|
|
|
|
|
2023-09-06 01:49:54 +02:00
|
|
|
async createGroup(roleId: string = roles.BUILTIN_ROLE_IDS.BASIC) {
|
2023-04-12 20:59:05 +02:00
|
|
|
return context.doInTenant(this.tenantId!, async () => {
|
|
|
|
const baseGroup = structures.userGroups.userGroup()
|
|
|
|
baseGroup.roles = {
|
2024-02-28 11:08:42 +01:00
|
|
|
[this.getProdAppId()]: roleId,
|
2023-04-12 20:59:05 +02:00
|
|
|
}
|
|
|
|
const { id, rev } = await pro.sdk.groups.save(baseGroup)
|
|
|
|
return {
|
|
|
|
_id: id,
|
|
|
|
_rev: rev,
|
|
|
|
...baseGroup,
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
async addUserToGroup(groupId: string, userId: string) {
|
|
|
|
return context.doInTenant(this.tenantId!, async () => {
|
|
|
|
await pro.sdk.groups.addUsers(groupId, [userId])
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
async removeUserFromGroup(groupId: string, userId: string) {
|
|
|
|
return context.doInTenant(this.tenantId!, async () => {
|
|
|
|
await pro.sdk.groups.removeUsers(groupId, [userId])
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2024-03-05 15:37:06 +01:00
|
|
|
sessionIdForUser(userId: string): string {
|
|
|
|
return `sessionid-${userId}`
|
|
|
|
}
|
|
|
|
|
2024-02-28 11:08:42 +01:00
|
|
|
async login({
|
|
|
|
roleId,
|
|
|
|
userId,
|
|
|
|
builder,
|
|
|
|
prodApp,
|
|
|
|
}: {
|
2024-02-28 13:13:13 +01:00
|
|
|
roleId?: string
|
2024-02-28 11:08:42 +01:00
|
|
|
userId: string
|
|
|
|
builder: boolean
|
|
|
|
prodApp: boolean
|
|
|
|
}) {
|
|
|
|
const appId = prodApp ? this.getProdAppId() : this.getAppId()
|
2022-04-06 17:57:56 +02:00
|
|
|
return context.doInAppContext(appId, async () => {
|
|
|
|
userId = !userId ? `us_uuid1` : userId
|
|
|
|
if (!this.request) {
|
|
|
|
throw "Server has not been opened, cannot login."
|
|
|
|
}
|
|
|
|
// make sure the user exists in the global DB
|
2022-11-21 19:33:34 +01:00
|
|
|
if (roleId !== roles.BUILTIN_ROLE_IDS.PUBLIC) {
|
2024-09-12 10:38:11 +02:00
|
|
|
const user = await this.globalUser({
|
2024-02-28 11:08:42 +01:00
|
|
|
_id: userId,
|
|
|
|
builder: { global: builder },
|
2024-02-28 13:13:13 +01:00
|
|
|
roles: { [appId]: roleId || roles.BUILTIN_ROLE_IDS.BASIC },
|
2022-04-06 17:57:56 +02:00
|
|
|
})
|
2024-09-12 10:38:11 +02:00
|
|
|
await sessions.createASession(userId, {
|
|
|
|
sessionId: this.sessionIdForUser(userId),
|
|
|
|
tenantId: this.getTenantId(),
|
|
|
|
email: user.email,
|
|
|
|
})
|
2022-04-06 17:57:56 +02:00
|
|
|
}
|
|
|
|
// have to fake this
|
2022-11-21 19:33:34 +01:00
|
|
|
const authObj = {
|
2022-04-06 17:57:56 +02:00
|
|
|
userId,
|
2024-03-05 15:37:06 +01:00
|
|
|
sessionId: this.sessionIdForUser(userId),
|
2023-01-25 18:11:37 +01:00
|
|
|
tenantId: this.getTenantId(),
|
2022-04-06 17:57:56 +02:00
|
|
|
}
|
2023-12-05 17:28:19 +01:00
|
|
|
const authToken = jwt.sign(authObj, coreEnv.JWT_SECRET as Secret)
|
2022-04-06 17:57:56 +02:00
|
|
|
|
|
|
|
// returning necessary request headers
|
2022-11-21 19:33:34 +01:00
|
|
|
await cache.user.invalidateUser(userId)
|
2022-04-06 17:57:56 +02:00
|
|
|
return {
|
|
|
|
Accept: "application/json",
|
2023-03-30 14:11:42 +02:00
|
|
|
Cookie: [`${constants.Cookie.Auth}=${authToken}`],
|
2022-11-21 19:33:34 +01:00
|
|
|
[constants.Header.APP_ID]: appId,
|
2024-10-25 11:41:20 +02:00
|
|
|
...this.temporaryHeaders,
|
2022-04-06 17:57:56 +02:00
|
|
|
}
|
|
|
|
})
|
2021-03-04 13:32:31 +01:00
|
|
|
}
|
|
|
|
|
2023-02-23 14:43:01 +01:00
|
|
|
// HEADERS
|
|
|
|
|
2024-10-14 19:57:46 +02:00
|
|
|
// sets the role for the headers, for the period of a callback
|
2024-10-17 16:17:36 +02:00
|
|
|
async loginAsRole(roleId: string, cb: () => Promise<unknown>) {
|
2024-10-14 19:57:46 +02:00
|
|
|
const roleUser = await this.createUser({
|
|
|
|
roles: {
|
2024-10-17 17:58:51 +02:00
|
|
|
[this.getProdAppId()]: roleId,
|
2024-10-14 19:57:46 +02:00
|
|
|
},
|
|
|
|
builder: { global: false },
|
|
|
|
admin: { global: false },
|
|
|
|
})
|
|
|
|
await this.login({
|
|
|
|
roleId,
|
|
|
|
userId: roleUser._id!,
|
|
|
|
builder: false,
|
|
|
|
prodApp: true,
|
|
|
|
})
|
2024-10-17 17:58:51 +02:00
|
|
|
await this.withUser(roleUser, async () => {
|
|
|
|
await cb()
|
|
|
|
})
|
2024-10-14 19:57:46 +02:00
|
|
|
}
|
|
|
|
|
2024-10-17 18:15:41 +02:00
|
|
|
async withHeaders(
|
|
|
|
headers: Record<string, string | string[]>,
|
|
|
|
cb: () => Promise<unknown>
|
|
|
|
) {
|
|
|
|
this.temporaryHeaders = headers
|
|
|
|
try {
|
|
|
|
await cb()
|
|
|
|
} finally {
|
|
|
|
this.temporaryHeaders = undefined
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-02-25 19:23:29 +01:00
|
|
|
defaultHeaders(
|
|
|
|
extras: Record<string, string | string[]> = {},
|
|
|
|
prodApp = false
|
|
|
|
) {
|
2023-01-25 18:11:37 +01:00
|
|
|
const tenantId = this.getTenantId()
|
2024-02-28 11:08:42 +01:00
|
|
|
const user = this.getUser()
|
2023-01-25 18:11:37 +01:00
|
|
|
const authObj: AuthToken = {
|
2024-02-28 11:08:42 +01:00
|
|
|
userId: user._id!,
|
2024-03-05 15:37:06 +01:00
|
|
|
sessionId: this.sessionIdForUser(user._id!),
|
2023-01-25 18:11:37 +01:00
|
|
|
tenantId,
|
2021-04-13 19:12:35 +02:00
|
|
|
}
|
2023-12-05 17:28:19 +01:00
|
|
|
const authToken = jwt.sign(authObj, coreEnv.JWT_SECRET as Secret)
|
2023-03-30 14:11:42 +02:00
|
|
|
|
2022-11-28 20:12:23 +01:00
|
|
|
const headers: any = {
|
2021-03-04 11:40:27 +01:00
|
|
|
Accept: "application/json",
|
2023-03-30 14:11:42 +02:00
|
|
|
Cookie: [`${constants.Cookie.Auth}=${authToken}`],
|
2024-02-21 10:12:06 +01:00
|
|
|
[constants.Header.CSRF_TOKEN]: this.csrfToken,
|
2023-02-23 14:43:01 +01:00
|
|
|
Host: this.tenantHost(),
|
2022-02-25 00:21:10 +01:00
|
|
|
...extras,
|
2021-03-04 11:40:27 +01:00
|
|
|
}
|
2023-01-19 18:23:48 +01:00
|
|
|
|
2023-05-23 16:55:25 +02:00
|
|
|
if (prodApp) {
|
|
|
|
headers[constants.Header.APP_ID] = this.prodAppId
|
|
|
|
} else if (this.appId) {
|
2022-11-21 19:33:34 +01:00
|
|
|
headers[constants.Header.APP_ID] = this.appId
|
2021-03-04 11:40:27 +01:00
|
|
|
}
|
2024-10-17 18:15:41 +02:00
|
|
|
return {
|
|
|
|
...headers,
|
|
|
|
...this.temporaryHeaders,
|
|
|
|
}
|
2021-03-04 11:40:27 +01:00
|
|
|
}
|
|
|
|
|
2025-02-25 19:23:29 +01:00
|
|
|
publicHeaders({
|
|
|
|
prodApp = true,
|
|
|
|
extras = {},
|
|
|
|
}: { prodApp?: boolean; extras?: Record<string, string | string[]> } = {}) {
|
2021-10-25 17:59:09 +02:00
|
|
|
const appId = prodApp ? this.prodAppId : this.appId
|
|
|
|
|
2025-02-25 19:23:29 +01:00
|
|
|
const headers: Record<string, string> = {
|
2021-03-04 11:40:27 +01:00
|
|
|
Accept: "application/json",
|
2024-08-05 16:45:49 +02:00
|
|
|
Cookie: "",
|
2021-03-04 11:40:27 +01:00
|
|
|
}
|
2021-10-26 17:21:26 +02:00
|
|
|
if (appId) {
|
2022-11-21 19:33:34 +01:00
|
|
|
headers[constants.Header.APP_ID] = appId
|
2021-03-04 11:40:27 +01:00
|
|
|
}
|
2023-01-26 16:16:42 +01:00
|
|
|
|
2023-02-23 14:43:01 +01:00
|
|
|
headers[constants.Header.TENANT_ID] = this.getTenantId()
|
2023-01-26 16:16:42 +01:00
|
|
|
|
2024-10-17 18:15:41 +02:00
|
|
|
return {
|
|
|
|
...headers,
|
|
|
|
...this.temporaryHeaders,
|
2025-02-25 19:23:29 +01:00
|
|
|
...extras,
|
2024-10-17 18:15:41 +02:00
|
|
|
}
|
2021-03-04 11:40:27 +01:00
|
|
|
}
|
|
|
|
|
2023-09-27 16:46:04 +02:00
|
|
|
async basicRoleHeaders() {
|
|
|
|
return await this.roleHeaders({
|
2024-02-21 12:30:22 +01:00
|
|
|
email: generator.email({ domain: "example.com" }),
|
2023-09-27 16:46:04 +02:00
|
|
|
builder: false,
|
|
|
|
prodApp: true,
|
|
|
|
roleId: roles.BUILTIN_ROLE_IDS.BASIC,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2021-04-23 19:07:39 +02:00
|
|
|
async roleHeaders({
|
2024-02-21 12:30:22 +01:00
|
|
|
email = generator.email({ domain: "example.com" }),
|
2022-11-21 19:33:34 +01:00
|
|
|
roleId = roles.BUILTIN_ROLE_IDS.ADMIN,
|
2021-04-23 19:07:39 +02:00
|
|
|
builder = false,
|
2021-10-25 17:59:09 +02:00
|
|
|
prodApp = true,
|
|
|
|
} = {}) {
|
2024-02-28 11:08:42 +01:00
|
|
|
return this.login({ userId: email, roleId, builder, prodApp })
|
2021-03-08 15:49:19 +01:00
|
|
|
}
|
|
|
|
|
2024-10-25 11:41:20 +02:00
|
|
|
browserUserAgent() {
|
|
|
|
return "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36"
|
|
|
|
}
|
|
|
|
|
2023-02-23 14:43:01 +01:00
|
|
|
// TENANCY
|
|
|
|
|
|
|
|
tenantHost() {
|
|
|
|
const tenantId = this.getTenantId()
|
|
|
|
const platformHost = new URL(coreEnv.PLATFORM_URL).host.split(":")[0]
|
|
|
|
return `${tenantId}.${platformHost}`
|
|
|
|
}
|
|
|
|
|
|
|
|
getTenantId() {
|
|
|
|
if (!this.tenantId) {
|
|
|
|
throw new Error("no test tenant id - init has not been called")
|
|
|
|
}
|
|
|
|
return this.tenantId
|
|
|
|
}
|
|
|
|
|
2024-05-07 10:13:43 +02:00
|
|
|
async newTenant(appName = newid()): Promise<App> {
|
2024-02-21 10:57:49 +01:00
|
|
|
this.csrfToken = generator.hash()
|
|
|
|
|
2023-02-23 14:43:01 +01:00
|
|
|
this.tenantId = structures.tenant.id()
|
|
|
|
this.user = await this.globalUser()
|
2024-02-28 11:08:42 +01:00
|
|
|
this.userMetadataId = generateUserMetadataID(this.user._id!)
|
2024-02-21 10:12:06 +01:00
|
|
|
|
2024-05-07 10:13:43 +02:00
|
|
|
return this.createApp(appName)
|
2023-02-23 14:43:01 +01:00
|
|
|
}
|
|
|
|
|
2024-02-28 13:19:08 +01:00
|
|
|
doInTenant<T>(task: () => T) {
|
2023-02-23 14:43:01 +01:00
|
|
|
return context.doInTenant(this.getTenantId(), task)
|
|
|
|
}
|
|
|
|
|
2022-04-06 17:57:56 +02:00
|
|
|
// API
|
|
|
|
|
2024-02-28 11:08:42 +01:00
|
|
|
async generateApiKey(userId?: string) {
|
|
|
|
const user = this.getUser()
|
|
|
|
if (!userId) {
|
|
|
|
userId = user._id!
|
|
|
|
}
|
2023-02-13 12:57:30 +01:00
|
|
|
const db = tenancy.getTenantDB(this.getTenantId())
|
|
|
|
const id = dbCore.generateDevInfoID(userId)
|
2025-02-25 19:23:29 +01:00
|
|
|
const devInfo = await db.tryGet<DevInfo>(id)
|
|
|
|
if (devInfo && devInfo.apiKey) {
|
|
|
|
return devInfo.apiKey
|
2023-02-13 12:57:30 +01:00
|
|
|
}
|
2025-02-25 19:23:29 +01:00
|
|
|
|
|
|
|
const apiKey = encryption.encrypt(
|
2023-02-13 12:57:30 +01:00
|
|
|
`${this.getTenantId()}${dbCore.SEPARATOR}${newid()}`
|
|
|
|
)
|
2025-02-25 19:23:29 +01:00
|
|
|
const newDevInfo: DevInfo = { _id: id, userId, apiKey }
|
|
|
|
await db.put(newDevInfo)
|
|
|
|
return apiKey
|
2022-04-06 17:57:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// APP
|
2024-05-07 10:13:43 +02:00
|
|
|
async createApp(appName: string, url?: string): Promise<App> {
|
2024-02-28 11:08:42 +01:00
|
|
|
this.appId = undefined
|
2024-02-28 12:46:58 +01:00
|
|
|
this.app = await context.doInTenant(
|
|
|
|
this.tenantId!,
|
|
|
|
async () =>
|
|
|
|
(await this._req(appController.create, {
|
|
|
|
name: appName,
|
2024-03-04 16:11:26 +01:00
|
|
|
url,
|
2024-02-28 12:46:58 +01:00
|
|
|
})) as App
|
|
|
|
)
|
|
|
|
this.appId = this.app.appId
|
2024-05-02 17:26:09 +02:00
|
|
|
|
2024-02-28 11:08:42 +01:00
|
|
|
return await context.doInAppContext(this.app.appId!, async () => {
|
2023-01-13 20:53:46 +01:00
|
|
|
// create production app
|
|
|
|
this.prodApp = await this.publish()
|
2021-10-25 17:59:09 +02:00
|
|
|
|
2023-01-13 20:53:46 +01:00
|
|
|
this.allApps.push(this.prodApp)
|
2024-02-28 12:46:58 +01:00
|
|
|
this.allApps.push(this.app!)
|
2021-10-25 17:59:09 +02:00
|
|
|
|
2023-11-08 17:17:24 +01:00
|
|
|
return this.app!
|
2023-01-13 20:53:46 +01:00
|
|
|
})
|
2021-03-03 18:52:41 +01:00
|
|
|
}
|
|
|
|
|
fixes for google sheets, admin checklist, and deleting an app from API (#8846)
* fixes for google sheets, admin checklist, and deleting an app from API
* code review
* splitting unpublish endpoint, moving deploy endpoint to applications controller. Still to do public API work and move deployment controller into application controller
* updating REST method for unpublish in API test
* unpublish and publish endpoint on public API, delete endpoint unpublishes and deletes app
* removing skip_setup from prodAppDb call
* removing commented code
* unit tests and open API spec updates
* unpublish, publish unit tests - delete still in progress
* remove line updating app name in API test
* unit tests
* v2.1.46
* Update pro version to 2.1.46
* v2.2.0
* Update pro version to 2.2.0
* Fix for budibase plugin skeleton, which utilises the old import style.
* Fix side nav styles
* v2.2.1
* Update pro version to 2.2.1
* using dist folder to allow importing constants for openAPI specs
* v2.2.2
* Update pro version to 2.2.2
* Fix for user enrichment call (updating to @budibase/nano fork) (#9038)
* Fix for #9029 - this should fix the issue users have been experiencing with user enrichment calls in apps, essentially it utilises a fork of the nano library we use to interact with CouchDB, which has been updated to use a POST request rather than a GET request as it supports a larger set of data being sent as query parameters.
* Incrementing Nano version to attempt to fix yarn registry issues.
* v2.2.3
* Update pro version to 2.2.3
* Fix SQL table `_id` filtering (#9030)
* Re-add support for filtering on _id using external SQL tables and fix filter key prefixes not working with _id field
* Remove like operator from internal tables and only allow basic operators on SQL table _id column
* Update data section filtering to respect new rules
* Update automation section filtering to respect new rules
* Update dynamic filter component to respect new rules
* v2.2.4
* Update pro version to 2.2.4
* lock changes (#9047)
* v2.2.5
* Update pro version to 2.2.5
* Make looping arrow point in right direction (#9053)
* v2.2.6
* Update pro version to 2.2.6
* Types/attaching license to account (#9065)
* adding license type to account
* removing planDuration
* v2.2.7
* Update pro version to 2.2.7
* Environment variable type coercion fix (#9074)
* Environment variable type coercion fix
* Update .gitignore
* v2.2.8
* Update pro version to 2.2.8
* tests passing
* all tests passing, updates to public API response
* update unpublish call to return 204, openAPI spec and unit
* fixing API tests
Co-authored-by: Budibase Release Bot <>
Co-authored-by: mike12345567 <me@michaeldrury.co.uk>
Co-authored-by: Andrew Kingston <andrew@kingston.dev>
Co-authored-by: melohagan <101575380+melohagan@users.noreply.github.com>
Co-authored-by: Rory Powell <rory.codes@gmail.com>
2022-12-19 14:18:00 +01:00
|
|
|
async publish() {
|
2024-02-28 11:08:42 +01:00
|
|
|
await this._req(deployController.publishApp)
|
2022-11-28 20:12:23 +01:00
|
|
|
// @ts-ignore
|
2022-01-28 01:05:39 +01:00
|
|
|
const prodAppId = this.getAppId().replace("_dev", "")
|
2022-07-13 14:22:21 +02:00
|
|
|
this.prodAppId = prodAppId
|
2022-08-10 12:01:54 +02:00
|
|
|
|
2022-01-28 16:43:51 +01:00
|
|
|
return context.doInAppContext(prodAppId, async () => {
|
2022-08-10 12:01:54 +02:00
|
|
|
const db = context.getProdAppDB()
|
2023-11-08 17:17:24 +01:00
|
|
|
return await db.get<App>(dbCore.DocumentType.APP_METADATA)
|
2022-01-28 16:43:51 +01:00
|
|
|
})
|
2021-10-25 17:59:09 +02:00
|
|
|
}
|
|
|
|
|
fixes for google sheets, admin checklist, and deleting an app from API (#8846)
* fixes for google sheets, admin checklist, and deleting an app from API
* code review
* splitting unpublish endpoint, moving deploy endpoint to applications controller. Still to do public API work and move deployment controller into application controller
* updating REST method for unpublish in API test
* unpublish and publish endpoint on public API, delete endpoint unpublishes and deletes app
* removing skip_setup from prodAppDb call
* removing commented code
* unit tests and open API spec updates
* unpublish, publish unit tests - delete still in progress
* remove line updating app name in API test
* unit tests
* v2.1.46
* Update pro version to 2.1.46
* v2.2.0
* Update pro version to 2.2.0
* Fix for budibase plugin skeleton, which utilises the old import style.
* Fix side nav styles
* v2.2.1
* Update pro version to 2.2.1
* using dist folder to allow importing constants for openAPI specs
* v2.2.2
* Update pro version to 2.2.2
* Fix for user enrichment call (updating to @budibase/nano fork) (#9038)
* Fix for #9029 - this should fix the issue users have been experiencing with user enrichment calls in apps, essentially it utilises a fork of the nano library we use to interact with CouchDB, which has been updated to use a POST request rather than a GET request as it supports a larger set of data being sent as query parameters.
* Incrementing Nano version to attempt to fix yarn registry issues.
* v2.2.3
* Update pro version to 2.2.3
* Fix SQL table `_id` filtering (#9030)
* Re-add support for filtering on _id using external SQL tables and fix filter key prefixes not working with _id field
* Remove like operator from internal tables and only allow basic operators on SQL table _id column
* Update data section filtering to respect new rules
* Update automation section filtering to respect new rules
* Update dynamic filter component to respect new rules
* v2.2.4
* Update pro version to 2.2.4
* lock changes (#9047)
* v2.2.5
* Update pro version to 2.2.5
* Make looping arrow point in right direction (#9053)
* v2.2.6
* Update pro version to 2.2.6
* Types/attaching license to account (#9065)
* adding license type to account
* removing planDuration
* v2.2.7
* Update pro version to 2.2.7
* Environment variable type coercion fix (#9074)
* Environment variable type coercion fix
* Update .gitignore
* v2.2.8
* Update pro version to 2.2.8
* tests passing
* all tests passing, updates to public API response
* update unpublish call to return 204, openAPI spec and unit
* fixing API tests
Co-authored-by: Budibase Release Bot <>
Co-authored-by: mike12345567 <me@michaeldrury.co.uk>
Co-authored-by: Andrew Kingston <andrew@kingston.dev>
Co-authored-by: melohagan <101575380+melohagan@users.noreply.github.com>
Co-authored-by: Rory Powell <rory.codes@gmail.com>
2022-12-19 14:18:00 +01:00
|
|
|
async unpublish() {
|
2024-12-02 16:26:12 +01:00
|
|
|
const response = await this._req(appController.unpublish, undefined, {
|
2024-02-28 11:08:42 +01:00
|
|
|
appId: this.appId,
|
|
|
|
})
|
|
|
|
this.prodAppId = undefined
|
|
|
|
this.prodApp = undefined
|
fixes for google sheets, admin checklist, and deleting an app from API (#8846)
* fixes for google sheets, admin checklist, and deleting an app from API
* code review
* splitting unpublish endpoint, moving deploy endpoint to applications controller. Still to do public API work and move deployment controller into application controller
* updating REST method for unpublish in API test
* unpublish and publish endpoint on public API, delete endpoint unpublishes and deletes app
* removing skip_setup from prodAppDb call
* removing commented code
* unit tests and open API spec updates
* unpublish, publish unit tests - delete still in progress
* remove line updating app name in API test
* unit tests
* v2.1.46
* Update pro version to 2.1.46
* v2.2.0
* Update pro version to 2.2.0
* Fix for budibase plugin skeleton, which utilises the old import style.
* Fix side nav styles
* v2.2.1
* Update pro version to 2.2.1
* using dist folder to allow importing constants for openAPI specs
* v2.2.2
* Update pro version to 2.2.2
* Fix for user enrichment call (updating to @budibase/nano fork) (#9038)
* Fix for #9029 - this should fix the issue users have been experiencing with user enrichment calls in apps, essentially it utilises a fork of the nano library we use to interact with CouchDB, which has been updated to use a POST request rather than a GET request as it supports a larger set of data being sent as query parameters.
* Incrementing Nano version to attempt to fix yarn registry issues.
* v2.2.3
* Update pro version to 2.2.3
* Fix SQL table `_id` filtering (#9030)
* Re-add support for filtering on _id using external SQL tables and fix filter key prefixes not working with _id field
* Remove like operator from internal tables and only allow basic operators on SQL table _id column
* Update data section filtering to respect new rules
* Update automation section filtering to respect new rules
* Update dynamic filter component to respect new rules
* v2.2.4
* Update pro version to 2.2.4
* lock changes (#9047)
* v2.2.5
* Update pro version to 2.2.5
* Make looping arrow point in right direction (#9053)
* v2.2.6
* Update pro version to 2.2.6
* Types/attaching license to account (#9065)
* adding license type to account
* removing planDuration
* v2.2.7
* Update pro version to 2.2.7
* Environment variable type coercion fix (#9074)
* Environment variable type coercion fix
* Update .gitignore
* v2.2.8
* Update pro version to 2.2.8
* tests passing
* all tests passing, updates to public API response
* update unpublish call to return 204, openAPI spec and unit
* fixing API tests
Co-authored-by: Budibase Release Bot <>
Co-authored-by: mike12345567 <me@michaeldrury.co.uk>
Co-authored-by: Andrew Kingston <andrew@kingston.dev>
Co-authored-by: melohagan <101575380+melohagan@users.noreply.github.com>
Co-authored-by: Rory Powell <rory.codes@gmail.com>
2022-12-19 14:18:00 +01:00
|
|
|
return response
|
|
|
|
}
|
|
|
|
|
2022-04-06 17:57:56 +02:00
|
|
|
// TABLE
|
|
|
|
|
2024-01-26 10:57:35 +01:00
|
|
|
async upsertTable(
|
2023-10-25 20:00:25 +02:00
|
|
|
config?: TableToBuild,
|
2023-07-18 14:34:23 +02:00
|
|
|
{ skipReassigning } = { skipReassigning: false }
|
|
|
|
): Promise<Table> {
|
2021-03-03 18:52:41 +01:00
|
|
|
config = config || basicTable()
|
2024-01-26 10:57:05 +01:00
|
|
|
const response = await this.api.table.save({
|
|
|
|
...config,
|
|
|
|
sourceType: config.sourceType || TableSourceType.INTERNAL,
|
|
|
|
sourceId: config.sourceId || INTERNAL_TABLE_SOURCE_ID,
|
|
|
|
})
|
2023-07-18 14:34:23 +02:00
|
|
|
if (!skipReassigning) {
|
|
|
|
this.table = response
|
|
|
|
}
|
|
|
|
return response
|
2021-03-03 18:52:41 +01:00
|
|
|
}
|
|
|
|
|
2023-10-25 20:00:25 +02:00
|
|
|
async createTable(
|
|
|
|
config?: TableToBuild,
|
|
|
|
options = { skipReassigning: false }
|
|
|
|
) {
|
2021-03-03 18:52:41 +01:00
|
|
|
if (config != null && config._id) {
|
|
|
|
delete config._id
|
|
|
|
}
|
2023-08-30 17:42:26 +02:00
|
|
|
config = config || basicTable()
|
2023-10-25 20:00:25 +02:00
|
|
|
if (!config.sourceId) {
|
|
|
|
config.sourceId = INTERNAL_TABLE_SOURCE_ID
|
2023-08-30 17:42:26 +02:00
|
|
|
}
|
2024-01-26 10:57:35 +01:00
|
|
|
return this.upsertTable(config, options)
|
2023-10-26 18:27:54 +02:00
|
|
|
}
|
2023-08-30 17:42:26 +02:00
|
|
|
|
2023-10-26 18:27:54 +02:00
|
|
|
async createExternalTable(
|
|
|
|
config?: TableToBuild,
|
|
|
|
options = { skipReassigning: false }
|
2024-01-26 10:57:05 +01:00
|
|
|
) {
|
2023-10-26 18:27:54 +02:00
|
|
|
if (config != null && config._id) {
|
|
|
|
delete config._id
|
|
|
|
}
|
|
|
|
config = config || basicTable()
|
|
|
|
if (this.datasource?._id) {
|
|
|
|
config.sourceId = this.datasource._id
|
|
|
|
config.sourceType = TableSourceType.EXTERNAL
|
|
|
|
}
|
2024-01-26 10:57:35 +01:00
|
|
|
return this.upsertTable(config, options)
|
2021-03-03 18:52:41 +01:00
|
|
|
}
|
|
|
|
|
2023-09-13 12:23:59 +02:00
|
|
|
async getTable(tableId?: string) {
|
|
|
|
tableId = tableId || this.table!._id!
|
2024-01-26 10:58:46 +01:00
|
|
|
return this.api.table.get(tableId)
|
2023-09-13 12:23:59 +02:00
|
|
|
}
|
|
|
|
|
2023-09-08 15:54:27 +02:00
|
|
|
async createLinkedTable(
|
|
|
|
relationshipType = RelationshipType.ONE_TO_MANY,
|
2023-09-13 14:09:48 +02:00
|
|
|
links: any = ["link"],
|
2023-10-25 20:00:25 +02:00
|
|
|
config?: TableToBuild
|
2023-09-08 15:54:27 +02:00
|
|
|
) {
|
2021-03-04 14:07:33 +01:00
|
|
|
if (!this.table) {
|
|
|
|
throw "Must have created a table first."
|
|
|
|
}
|
2023-09-08 15:54:27 +02:00
|
|
|
const tableConfig = config || basicTable()
|
2023-10-25 20:00:25 +02:00
|
|
|
if (!tableConfig.sourceId) {
|
|
|
|
tableConfig.sourceId = INTERNAL_TABLE_SOURCE_ID
|
|
|
|
}
|
2021-03-04 14:07:33 +01:00
|
|
|
tableConfig.primaryDisplay = "name"
|
2021-03-16 19:13:00 +01:00
|
|
|
for (let link of links) {
|
|
|
|
tableConfig.schema[link] = {
|
2023-09-08 15:54:27 +02:00
|
|
|
type: FieldType.LINK,
|
2021-03-16 19:13:00 +01:00
|
|
|
fieldName: link,
|
2023-10-05 16:14:30 +02:00
|
|
|
tableId: this.table._id!,
|
2021-03-16 19:13:00 +01:00
|
|
|
name: link,
|
2023-09-08 15:54:27 +02:00
|
|
|
relationshipType,
|
2023-10-11 12:55:23 +02:00
|
|
|
} as RelationshipFieldMetadata
|
2023-09-08 15:54:27 +02:00
|
|
|
}
|
|
|
|
|
2023-10-26 17:32:34 +02:00
|
|
|
if (this.datasource?._id) {
|
2023-09-08 15:54:27 +02:00
|
|
|
tableConfig.sourceId = this.datasource._id
|
2023-10-26 17:32:34 +02:00
|
|
|
tableConfig.sourceType = TableSourceType.EXTERNAL
|
2021-03-03 18:52:41 +01:00
|
|
|
}
|
2023-09-08 15:54:27 +02:00
|
|
|
|
2023-10-26 14:19:09 +02:00
|
|
|
return await this.createTable(tableConfig)
|
2021-03-03 18:52:41 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
async createAttachmentTable() {
|
2022-11-28 20:12:23 +01:00
|
|
|
const table: any = basicTable()
|
2021-03-03 18:52:41 +01:00
|
|
|
table.schema.attachment = {
|
|
|
|
type: "attachment",
|
|
|
|
}
|
|
|
|
return this.createTable(table)
|
|
|
|
}
|
|
|
|
|
2022-04-06 17:57:56 +02:00
|
|
|
// ROW
|
|
|
|
|
2023-01-19 12:00:51 +01:00
|
|
|
async createRow(config?: Row): Promise<Row> {
|
2021-03-03 18:52:41 +01:00
|
|
|
if (!this.table) {
|
|
|
|
throw "Test requires table to be configured."
|
|
|
|
}
|
2024-01-26 11:00:03 +01:00
|
|
|
const tableId = (config && config.tableId) || this.table._id!
|
2023-07-18 10:14:13 +02:00
|
|
|
config = config || basicRow(tableId!)
|
2024-01-26 11:00:03 +01:00
|
|
|
return this.api.row.save(tableId, config)
|
2021-03-11 19:29:48 +01:00
|
|
|
}
|
|
|
|
|
2022-11-28 20:12:23 +01:00
|
|
|
async getRows(tableId: string) {
|
2021-03-15 17:36:38 +01:00
|
|
|
if (!tableId && this.table) {
|
2023-07-18 10:14:13 +02:00
|
|
|
tableId = this.table._id!
|
2021-03-15 17:36:38 +01:00
|
|
|
}
|
2024-01-26 11:01:16 +01:00
|
|
|
return this.api.row.fetch(tableId)
|
2021-03-15 17:36:38 +01:00
|
|
|
}
|
|
|
|
|
2024-03-28 18:57:37 +01:00
|
|
|
async searchRows(tableId: string, searchParams?: RowSearchParams) {
|
2023-03-21 18:27:31 +01:00
|
|
|
if (!tableId && this.table) {
|
2023-07-18 10:14:13 +02:00
|
|
|
tableId = this.table._id!
|
2023-03-21 18:27:31 +01:00
|
|
|
}
|
2024-01-26 11:24:32 +01:00
|
|
|
return this.api.row.search(tableId, searchParams)
|
2023-03-21 18:27:31 +01:00
|
|
|
}
|
|
|
|
|
2022-04-06 17:57:56 +02:00
|
|
|
// ROLE
|
|
|
|
|
2022-11-28 20:12:23 +01:00
|
|
|
async createRole(config?: any) {
|
2024-02-28 11:08:42 +01:00
|
|
|
return this._req(roleController.save, config || basicRole())
|
2021-03-03 18:52:41 +01:00
|
|
|
}
|
|
|
|
|
2022-04-06 17:57:56 +02:00
|
|
|
// VIEW
|
|
|
|
|
2023-09-08 10:43:19 +02:00
|
|
|
async createLegacyView(config?: View) {
|
|
|
|
if (!this.table && !config) {
|
2021-03-03 18:52:41 +01:00
|
|
|
throw "Test requires table to be configured."
|
|
|
|
}
|
|
|
|
const view = config || {
|
2023-09-08 10:43:19 +02:00
|
|
|
tableId: this.table!._id,
|
2023-09-12 20:17:21 +02:00
|
|
|
name: generator.guid(),
|
2021-03-03 18:52:41 +01:00
|
|
|
}
|
2024-02-28 11:08:42 +01:00
|
|
|
return this._req(viewController.v1.save, view)
|
2021-03-03 18:52:41 +01:00
|
|
|
}
|
|
|
|
|
2023-09-12 19:39:40 +02:00
|
|
|
async createView(
|
|
|
|
config?: Omit<CreateViewRequest, "tableId" | "name"> & {
|
|
|
|
name?: string
|
|
|
|
tableId?: string
|
|
|
|
}
|
|
|
|
) {
|
|
|
|
if (!this.table && !config?.tableId) {
|
|
|
|
throw "Test requires table to be configured."
|
|
|
|
}
|
|
|
|
|
|
|
|
const view: CreateViewRequest = {
|
|
|
|
...config,
|
|
|
|
tableId: config?.tableId || this.table!._id!,
|
|
|
|
name: config?.name || generator.word(),
|
|
|
|
}
|
|
|
|
|
|
|
|
return await this.api.viewV2.create(view)
|
|
|
|
}
|
|
|
|
|
2022-04-06 17:57:56 +02:00
|
|
|
// AUTOMATION
|
|
|
|
|
2024-02-28 12:46:58 +01:00
|
|
|
async createAutomation(config?: Automation) {
|
2021-03-03 19:41:49 +01:00
|
|
|
config = config || basicAutomation()
|
|
|
|
if (config._rev) {
|
|
|
|
delete config._rev
|
|
|
|
}
|
2024-02-28 12:46:58 +01:00
|
|
|
const res = await this._req(automationController.create, config)
|
|
|
|
this.automation = res.automation
|
2021-03-03 19:41:49 +01:00
|
|
|
return this.automation
|
|
|
|
}
|
|
|
|
|
|
|
|
async getAllAutomations() {
|
2024-02-28 11:08:42 +01:00
|
|
|
return this._req(automationController.fetch)
|
2021-03-03 19:41:49 +01:00
|
|
|
}
|
|
|
|
|
2024-02-28 13:13:13 +01:00
|
|
|
async deleteAutomation(automation?: Automation) {
|
2021-03-03 19:41:49 +01:00
|
|
|
automation = automation || this.automation
|
|
|
|
if (!automation) {
|
|
|
|
return
|
|
|
|
}
|
2024-02-28 12:16:26 +01:00
|
|
|
return this._req(automationController.destroy, undefined, {
|
2024-02-28 11:08:42 +01:00
|
|
|
id: automation._id,
|
|
|
|
rev: automation._rev,
|
|
|
|
})
|
2021-03-03 19:41:49 +01:00
|
|
|
}
|
|
|
|
|
2024-02-28 13:13:13 +01:00
|
|
|
async createWebhook(config?: Webhook) {
|
2022-04-06 17:57:56 +02:00
|
|
|
if (!this.automation) {
|
|
|
|
throw "Must create an automation before creating webhook."
|
|
|
|
}
|
2024-02-28 12:46:58 +01:00
|
|
|
config = config || basicWebhook(this.automation._id!)
|
2024-01-26 11:07:06 +01:00
|
|
|
|
2024-02-28 11:08:42 +01:00
|
|
|
return (await this._req(webhookController.save, config)).webhook
|
2022-04-06 17:57:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// DATASOURCE
|
|
|
|
|
2023-01-20 11:29:11 +01:00
|
|
|
async createDatasource(config?: {
|
|
|
|
datasource: Datasource
|
2024-01-29 23:25:12 +01:00
|
|
|
}): Promise<WithRequired<Datasource, "_id">> {
|
2021-03-04 11:05:50 +01:00
|
|
|
config = config || basicDatasource()
|
2024-01-26 11:11:54 +01:00
|
|
|
const response = await this.api.datasource.create(config.datasource)
|
|
|
|
this.datasource = response
|
2024-01-29 22:57:20 +01:00
|
|
|
return { ...this.datasource, _id: this.datasource!._id! }
|
2021-03-04 11:05:50 +01:00
|
|
|
}
|
|
|
|
|
2024-01-29 23:25:12 +01:00
|
|
|
async updateDatasource(
|
|
|
|
datasource: Datasource
|
|
|
|
): Promise<WithRequired<Datasource, "_id">> {
|
2024-01-26 11:11:54 +01:00
|
|
|
const response = await this.api.datasource.update(datasource)
|
|
|
|
this.datasource = response
|
2024-01-29 23:25:12 +01:00
|
|
|
return { ...this.datasource, _id: this.datasource!._id! }
|
2022-01-04 19:23:45 +01:00
|
|
|
}
|
|
|
|
|
2024-02-28 13:13:13 +01:00
|
|
|
async restDatasource(cfg?: Record<string, any>) {
|
2022-01-11 11:35:53 +01:00
|
|
|
return this.createDatasource({
|
2023-01-20 11:29:11 +01:00
|
|
|
datasource: {
|
|
|
|
...basicDatasource().datasource,
|
|
|
|
source: SourceName.REST,
|
|
|
|
config: cfg || {},
|
|
|
|
},
|
2022-01-11 11:35:53 +01:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
async dynamicVariableDatasource() {
|
|
|
|
let datasource = await this.restDatasource()
|
2024-01-29 23:25:12 +01:00
|
|
|
|
2022-01-11 11:35:53 +01:00
|
|
|
const basedOnQuery = await this.createQuery({
|
2023-01-18 11:45:42 +01:00
|
|
|
...basicQuery(datasource._id!),
|
2022-01-11 11:35:53 +01:00
|
|
|
fields: {
|
|
|
|
path: "www.google.com",
|
|
|
|
},
|
|
|
|
})
|
|
|
|
datasource = await this.updateDatasource({
|
|
|
|
...datasource,
|
|
|
|
config: {
|
|
|
|
dynamicVariables: [
|
|
|
|
{
|
|
|
|
queryId: basedOnQuery._id,
|
|
|
|
name: "variable3",
|
|
|
|
value: "{{ data.0.[value] }}",
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
})
|
|
|
|
return { datasource, query: basedOnQuery }
|
|
|
|
}
|
|
|
|
|
2023-06-26 19:52:15 +02:00
|
|
|
// AUTOMATION LOG
|
|
|
|
|
2023-10-30 18:41:08 +01:00
|
|
|
async createAutomationLog(automation: Automation, appId?: string) {
|
|
|
|
appId = appId || this.getProdAppId()
|
|
|
|
return await context.doInAppContext(appId!, async () => {
|
2023-06-26 19:52:15 +02:00
|
|
|
return await pro.sdk.automations.logs.storeLog(
|
|
|
|
automation,
|
2023-06-27 11:01:06 +02:00
|
|
|
basicAutomationResults(automation._id!)
|
2023-06-26 19:52:15 +02:00
|
|
|
)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
async getAutomationLogs() {
|
2023-12-04 09:23:01 +01:00
|
|
|
return context.doInAppContext(this.getAppId(), async () => {
|
2023-06-27 11:01:06 +02:00
|
|
|
const now = new Date()
|
|
|
|
return await pro.sdk.automations.logs.logSearch({
|
|
|
|
startDate: new Date(now.getTime() - 100000).toISOString(),
|
|
|
|
})
|
2023-06-26 19:52:15 +02:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2022-04-06 17:57:56 +02:00
|
|
|
// QUERY
|
|
|
|
|
2024-02-28 13:13:13 +01:00
|
|
|
async createQuery(config?: Query) {
|
|
|
|
return this._req(
|
|
|
|
queryController.save,
|
|
|
|
config || basicQuery(this.getDatasource()._id!)
|
|
|
|
)
|
2021-03-04 11:05:50 +01:00
|
|
|
}
|
|
|
|
|
2022-04-06 17:57:56 +02:00
|
|
|
// SCREEN
|
|
|
|
|
2024-02-28 13:13:13 +01:00
|
|
|
async createScreen(config?: Screen) {
|
2021-03-08 15:49:19 +01:00
|
|
|
config = config || basicScreen()
|
2024-02-28 11:08:42 +01:00
|
|
|
return this._req(screenController.save, config)
|
2021-03-08 15:49:19 +01:00
|
|
|
}
|
|
|
|
|
2022-04-06 17:57:56 +02:00
|
|
|
// LAYOUT
|
2021-03-08 16:57:19 +01:00
|
|
|
|
2024-02-28 13:13:13 +01:00
|
|
|
async createLayout(config?: Layout) {
|
2021-03-09 12:56:32 +01:00
|
|
|
config = config || basicLayout()
|
2024-02-28 11:08:42 +01:00
|
|
|
return await this._req(layoutController.save, config)
|
2021-03-09 12:56:32 +01:00
|
|
|
}
|
2021-03-03 18:52:41 +01:00
|
|
|
}
|
2024-02-12 12:50:23 +01:00
|
|
|
|
|
|
|
module.exports = TestConfiguration
|