@budibase/server 2.3.18-alpha.2 → 2.3.18-alpha.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/__mocks__/node-fetch.ts +3 -0
- package/builder/assets/blankScreenPreview.72634dd1.png +0 -0
- package/builder/assets/{index.bbe4c16b.js → index.07aa249f.js} +430 -417
- package/builder/assets/index.dc0472d8.css +6 -0
- package/builder/assets/listScreenPreview.599c0aae.png +0 -0
- package/builder/index.html +2 -2
- package/dist/api/controllers/automation.js +11 -2
- package/dist/api/controllers/cloud.js +2 -2
- package/dist/api/controllers/row/ExternalRequest.js +49 -24
- package/dist/api/controllers/row/external.js +1 -1
- package/dist/api/controllers/row/internalSearch.js +6 -450
- package/dist/api/controllers/row/utils.js +1 -3
- package/dist/api/routes/automation.js +1 -1
- package/dist/api/routes/public/applications.js +7 -7
- package/dist/api/routes/public/queries.js +2 -2
- package/dist/api/routes/public/rows.js +5 -5
- package/dist/api/routes/public/tables.js +5 -5
- package/dist/api/routes/public/users.js +5 -5
- package/dist/app.js +2 -0
- package/dist/db/index.js +25 -2
- package/dist/db/utils.js +2 -5
- package/dist/db/views/staticViews.js +2 -1
- package/dist/integrations/base/sql.js +4 -8
- package/dist/integrations/googlesheets.js +17 -20
- package/dist/middleware/authorized.js +5 -3
- package/dist/middleware/builder.js +6 -3
- package/dist/migrations/functions/backfill/global/configs.js +10 -4
- package/dist/migrations/tests/helpers.js +1 -1
- package/dist/migrations/tests/structures.js +1 -1
- package/dist/package.json +9 -8
- package/dist/startup.js +3 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/jest.config.ts +1 -0
- package/package.json +10 -9
- package/scripts/test.sh +12 -0
- package/specs/{generate.js → generate.ts} +7 -9
- package/specs/openapi.json +24 -24
- package/specs/openapi.yaml +24 -24
- package/specs/{parameters.js → parameters.ts} +6 -6
- package/specs/resources/{application.js → application.ts} +4 -4
- package/specs/resources/{index.js → index.ts} +8 -8
- package/specs/resources/{misc.js → misc.ts} +3 -3
- package/specs/resources/{query.js → query.ts} +4 -4
- package/specs/resources/{row.js → row.ts} +3 -4
- package/specs/resources/{table.js → table.ts} +5 -5
- package/specs/resources/{user.js → user.ts} +3 -3
- package/specs/resources/utils/Resource.ts +39 -0
- package/specs/resources/utils/{index.js → index.ts} +1 -1
- package/specs/{security.js → security.ts} +1 -1
- package/src/api/controllers/automation.ts +13 -2
- package/src/api/controllers/cloud.ts +2 -2
- package/src/api/controllers/row/ExternalRequest.ts +95 -28
- package/src/api/controllers/row/external.ts +1 -1
- package/src/api/controllers/row/internalSearch.ts +11 -524
- package/src/api/controllers/row/utils.ts +1 -2
- package/src/api/routes/automation.ts +1 -1
- package/src/api/routes/public/applications.ts +7 -7
- package/src/api/routes/public/queries.ts +2 -2
- package/src/api/routes/public/rows.ts +5 -5
- package/src/api/routes/public/tables.ts +5 -5
- package/src/api/routes/public/tests/{compare.spec.js → compare.spec.ts} +44 -25
- package/src/api/routes/public/users.ts +5 -5
- package/src/api/routes/tests/{cloud.seq.spec.ts → cloud.spec.ts} +13 -20
- package/src/api/routes/tests/utilities/TestFunctions.ts +1 -2
- package/src/app.ts +2 -0
- package/src/db/index.ts +2 -2
- package/src/db/utils.ts +0 -4
- package/src/db/views/staticViews.ts +3 -3
- package/src/definitions/openapi.ts +449 -63
- package/src/integration-test/postgres.spec.ts +351 -81
- package/src/integrations/base/sql.ts +4 -8
- package/src/integrations/googlesheets.ts +21 -22
- package/src/integrations/tests/googlesheets.spec.ts +122 -0
- package/src/middleware/authorized.ts +6 -4
- package/src/middleware/builder.ts +8 -3
- package/src/migrations/functions/backfill/global/configs.ts +15 -9
- package/src/migrations/functions/tests/userEmailViewCasing.spec.js +3 -4
- package/src/migrations/tests/helpers.ts +2 -2
- package/src/migrations/tests/structures.ts +1 -0
- package/src/startup.ts +4 -1
- package/src/tests/jestEnv.ts +1 -0
- package/src/tests/utilities/TestConfiguration.ts +42 -30
- package/src/tests/utilities/structures.ts +0 -2
- package/builder/assets/index.7e76c039.css +0 -6
- package/dist/integrations/base/utils.js +0 -16
- package/specs/resources/utils/Resource.js +0 -26
- package/src/integrations/base/utils.ts +0 -12
|
@@ -11,9 +11,8 @@ import { OAuth2Client } from "google-auth-library"
|
|
|
11
11
|
import { buildExternalTableId } from "./utils"
|
|
12
12
|
import { DataSourceOperation, FieldTypes } from "../constants"
|
|
13
13
|
import { GoogleSpreadsheet } from "google-spreadsheet"
|
|
14
|
-
import
|
|
15
|
-
import {
|
|
16
|
-
const fetch = require("node-fetch")
|
|
14
|
+
import fetch from "node-fetch"
|
|
15
|
+
import { configs, HTTPError } from "@budibase/backend-core"
|
|
17
16
|
|
|
18
17
|
interface GoogleSheetsConfig {
|
|
19
18
|
spreadsheetId: string
|
|
@@ -112,7 +111,7 @@ const SCHEMA: Integration = {
|
|
|
112
111
|
|
|
113
112
|
class GoogleSheetsIntegration implements DatasourcePlus {
|
|
114
113
|
private readonly config: GoogleSheetsConfig
|
|
115
|
-
private client:
|
|
114
|
+
private client: GoogleSpreadsheet
|
|
116
115
|
public tables: Record<string, Table> = {}
|
|
117
116
|
public schemaErrors: Record<string, string> = {}
|
|
118
117
|
|
|
@@ -173,16 +172,9 @@ class GoogleSheetsIntegration implements DatasourcePlus {
|
|
|
173
172
|
async connect() {
|
|
174
173
|
try {
|
|
175
174
|
// Initialise oAuth client
|
|
176
|
-
|
|
177
|
-
let googleConfig = await dbCore.getScopedConfig(db, {
|
|
178
|
-
type: constants.Config.GOOGLE,
|
|
179
|
-
})
|
|
180
|
-
|
|
175
|
+
let googleConfig = await configs.getGoogleDatasourceConfig()
|
|
181
176
|
if (!googleConfig) {
|
|
182
|
-
|
|
183
|
-
clientID: env.GOOGLE_CLIENT_ID,
|
|
184
|
-
clientSecret: env.GOOGLE_CLIENT_SECRET,
|
|
185
|
-
}
|
|
177
|
+
throw new HTTPError("Google config not found", 400)
|
|
186
178
|
}
|
|
187
179
|
|
|
188
180
|
const oauthClient = new OAuth2Client({
|
|
@@ -211,7 +203,7 @@ class GoogleSheetsIntegration implements DatasourcePlus {
|
|
|
211
203
|
|
|
212
204
|
async buildSchema(datasourceId: string) {
|
|
213
205
|
await this.connect()
|
|
214
|
-
const sheets =
|
|
206
|
+
const sheets = this.client.sheetsByIndex
|
|
215
207
|
const tables: Record<string, Table> = {}
|
|
216
208
|
for (let sheet of sheets) {
|
|
217
209
|
// must fetch rows to determine schema
|
|
@@ -294,7 +286,7 @@ class GoogleSheetsIntegration implements DatasourcePlus {
|
|
|
294
286
|
async updateTable(table?: any) {
|
|
295
287
|
try {
|
|
296
288
|
await this.connect()
|
|
297
|
-
const sheet =
|
|
289
|
+
const sheet = this.client.sheetsByTitle[table.name]
|
|
298
290
|
await sheet.loadHeaderRow()
|
|
299
291
|
|
|
300
292
|
if (table._rename) {
|
|
@@ -308,10 +300,17 @@ class GoogleSheetsIntegration implements DatasourcePlus {
|
|
|
308
300
|
}
|
|
309
301
|
await sheet.setHeaderRow(headers)
|
|
310
302
|
} else {
|
|
311
|
-
|
|
303
|
+
const updatedHeaderValues = [...sheet.headerValues]
|
|
304
|
+
|
|
305
|
+
const newField = Object.keys(table.schema).find(
|
|
312
306
|
key => !sheet.headerValues.includes(key)
|
|
313
307
|
)
|
|
314
|
-
|
|
308
|
+
|
|
309
|
+
if (newField) {
|
|
310
|
+
updatedHeaderValues.push(newField)
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
await sheet.setHeaderRow(updatedHeaderValues)
|
|
315
314
|
}
|
|
316
315
|
} catch (err) {
|
|
317
316
|
console.error("Error updating table in google sheets", err)
|
|
@@ -322,7 +321,7 @@ class GoogleSheetsIntegration implements DatasourcePlus {
|
|
|
322
321
|
async deleteTable(sheet: any) {
|
|
323
322
|
try {
|
|
324
323
|
await this.connect()
|
|
325
|
-
const sheetToDelete =
|
|
324
|
+
const sheetToDelete = this.client.sheetsByTitle[sheet]
|
|
326
325
|
return await sheetToDelete.delete()
|
|
327
326
|
} catch (err) {
|
|
328
327
|
console.error("Error deleting table in google sheets", err)
|
|
@@ -333,7 +332,7 @@ class GoogleSheetsIntegration implements DatasourcePlus {
|
|
|
333
332
|
async create(query: { sheet: string; row: any }) {
|
|
334
333
|
try {
|
|
335
334
|
await this.connect()
|
|
336
|
-
const sheet =
|
|
335
|
+
const sheet = this.client.sheetsByTitle[query.sheet]
|
|
337
336
|
const rowToInsert =
|
|
338
337
|
typeof query.row === "string" ? JSON.parse(query.row) : query.row
|
|
339
338
|
const row = await sheet.addRow(rowToInsert)
|
|
@@ -349,7 +348,7 @@ class GoogleSheetsIntegration implements DatasourcePlus {
|
|
|
349
348
|
async read(query: { sheet: string }) {
|
|
350
349
|
try {
|
|
351
350
|
await this.connect()
|
|
352
|
-
const sheet =
|
|
351
|
+
const sheet = this.client.sheetsByTitle[query.sheet]
|
|
353
352
|
const rows = await sheet.getRows()
|
|
354
353
|
const headerValues = sheet.headerValues
|
|
355
354
|
const response = []
|
|
@@ -368,7 +367,7 @@ class GoogleSheetsIntegration implements DatasourcePlus {
|
|
|
368
367
|
async update(query: { sheet: string; rowIndex: number; row: any }) {
|
|
369
368
|
try {
|
|
370
369
|
await this.connect()
|
|
371
|
-
const sheet =
|
|
370
|
+
const sheet = this.client.sheetsByTitle[query.sheet]
|
|
372
371
|
const rows = await sheet.getRows()
|
|
373
372
|
const row = rows[query.rowIndex]
|
|
374
373
|
if (row) {
|
|
@@ -392,7 +391,7 @@ class GoogleSheetsIntegration implements DatasourcePlus {
|
|
|
392
391
|
|
|
393
392
|
async delete(query: { sheet: string; rowIndex: number }) {
|
|
394
393
|
await this.connect()
|
|
395
|
-
const sheet =
|
|
394
|
+
const sheet = this.client.sheetsByTitle[query.sheet]
|
|
396
395
|
const rows = await sheet.getRows()
|
|
397
396
|
const row = rows[query.rowIndex]
|
|
398
397
|
if (row) {
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import type { GoogleSpreadsheetWorksheet } from "google-spreadsheet"
|
|
2
|
+
|
|
3
|
+
jest.mock("google-auth-library")
|
|
4
|
+
const { OAuth2Client } = require("google-auth-library")
|
|
5
|
+
|
|
6
|
+
const setCredentialsMock = jest.fn()
|
|
7
|
+
const getAccessTokenMock = jest.fn()
|
|
8
|
+
|
|
9
|
+
OAuth2Client.mockImplementation(() => {
|
|
10
|
+
return {
|
|
11
|
+
setCredentials: setCredentialsMock,
|
|
12
|
+
getAccessToken: getAccessTokenMock,
|
|
13
|
+
}
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
jest.mock("google-spreadsheet")
|
|
17
|
+
const { GoogleSpreadsheet } = require("google-spreadsheet")
|
|
18
|
+
|
|
19
|
+
const sheetsByTitle: { [title: string]: GoogleSpreadsheetWorksheet } = {}
|
|
20
|
+
|
|
21
|
+
GoogleSpreadsheet.mockImplementation(() => {
|
|
22
|
+
return {
|
|
23
|
+
useOAuth2Client: jest.fn(),
|
|
24
|
+
loadInfo: jest.fn(),
|
|
25
|
+
sheetsByTitle,
|
|
26
|
+
}
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
import { structures } from "@budibase/backend-core/tests"
|
|
30
|
+
import TestConfiguration from "../../tests/utilities/TestConfiguration"
|
|
31
|
+
import GoogleSheetsIntegration from "../googlesheets"
|
|
32
|
+
import { FieldType, Table, TableSchema } from "../../../../types/src/documents"
|
|
33
|
+
|
|
34
|
+
describe("Google Sheets Integration", () => {
|
|
35
|
+
let integration: any,
|
|
36
|
+
config = new TestConfiguration()
|
|
37
|
+
|
|
38
|
+
beforeEach(async () => {
|
|
39
|
+
integration = new GoogleSheetsIntegration.integration({
|
|
40
|
+
spreadsheetId: "randomId",
|
|
41
|
+
auth: {
|
|
42
|
+
appId: "appId",
|
|
43
|
+
accessToken: "accessToken",
|
|
44
|
+
refreshToken: "refreshToken",
|
|
45
|
+
},
|
|
46
|
+
})
|
|
47
|
+
await config.init()
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
function createBasicTable(name: string, columns: string[]): Table {
|
|
51
|
+
return {
|
|
52
|
+
name,
|
|
53
|
+
schema: {
|
|
54
|
+
...columns.reduce((p, c) => {
|
|
55
|
+
p[c] = {
|
|
56
|
+
name: c,
|
|
57
|
+
type: FieldType.STRING,
|
|
58
|
+
constraints: {
|
|
59
|
+
type: "string",
|
|
60
|
+
},
|
|
61
|
+
}
|
|
62
|
+
return p
|
|
63
|
+
}, {} as TableSchema),
|
|
64
|
+
},
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function createSheet({
|
|
69
|
+
headerValues,
|
|
70
|
+
}: {
|
|
71
|
+
headerValues: string[]
|
|
72
|
+
}): GoogleSpreadsheetWorksheet {
|
|
73
|
+
return {
|
|
74
|
+
// to ignore the unmapped fields
|
|
75
|
+
...({} as any),
|
|
76
|
+
loadHeaderRow: jest.fn(),
|
|
77
|
+
headerValues,
|
|
78
|
+
setHeaderRow: jest.fn(),
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
describe("update table", () => {
|
|
83
|
+
test("adding a new field will be adding a new header row", async () => {
|
|
84
|
+
await config.doInContext(structures.uuid(), async () => {
|
|
85
|
+
const tableColumns = ["name", "description", "new field"]
|
|
86
|
+
const table = createBasicTable(structures.uuid(), tableColumns)
|
|
87
|
+
|
|
88
|
+
const sheet = createSheet({ headerValues: ["name", "description"] })
|
|
89
|
+
sheetsByTitle[table.name] = sheet
|
|
90
|
+
await integration.updateTable(table)
|
|
91
|
+
|
|
92
|
+
expect(sheet.loadHeaderRow).toBeCalledTimes(1)
|
|
93
|
+
expect(sheet.setHeaderRow).toBeCalledTimes(1)
|
|
94
|
+
expect(sheet.setHeaderRow).toBeCalledWith(tableColumns)
|
|
95
|
+
})
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
test("removing an existing field will not remove the data from the spreadsheet", async () => {
|
|
99
|
+
await config.doInContext(structures.uuid(), async () => {
|
|
100
|
+
const tableColumns = ["name"]
|
|
101
|
+
const table = createBasicTable(structures.uuid(), tableColumns)
|
|
102
|
+
|
|
103
|
+
const sheet = createSheet({
|
|
104
|
+
headerValues: ["name", "description", "location"],
|
|
105
|
+
})
|
|
106
|
+
sheetsByTitle[table.name] = sheet
|
|
107
|
+
await integration.updateTable(table)
|
|
108
|
+
|
|
109
|
+
expect(sheet.loadHeaderRow).toBeCalledTimes(1)
|
|
110
|
+
expect(sheet.setHeaderRow).toBeCalledTimes(1)
|
|
111
|
+
expect(sheet.setHeaderRow).toBeCalledWith([
|
|
112
|
+
"name",
|
|
113
|
+
"description",
|
|
114
|
+
"location",
|
|
115
|
+
])
|
|
116
|
+
|
|
117
|
+
// No undefineds are sent
|
|
118
|
+
expect((sheet.setHeaderRow as any).mock.calls[0][0]).toHaveLength(3)
|
|
119
|
+
})
|
|
120
|
+
})
|
|
121
|
+
})
|
|
122
|
+
})
|
|
@@ -79,10 +79,6 @@ export default (
|
|
|
79
79
|
return ctx.throw(403, "No user info found")
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
-
// check general builder stuff, this middleware is a good way
|
|
83
|
-
// to find API endpoints which are builder focused
|
|
84
|
-
await builderMiddleware(ctx, permType)
|
|
85
|
-
|
|
86
82
|
// get the resource roles
|
|
87
83
|
let resourceRoles: any = []
|
|
88
84
|
let otherLevelRoles: any = []
|
|
@@ -112,6 +108,12 @@ export default (
|
|
|
112
108
|
return ctx.throw(403, "Session not authenticated")
|
|
113
109
|
}
|
|
114
110
|
|
|
111
|
+
// check general builder stuff, this middleware is a good way
|
|
112
|
+
// to find API endpoints which are builder focused
|
|
113
|
+
if (permType === permissions.PermissionType.BUILDER) {
|
|
114
|
+
await builderMiddleware(ctx)
|
|
115
|
+
}
|
|
116
|
+
|
|
115
117
|
try {
|
|
116
118
|
// check authorized
|
|
117
119
|
await checkAuthorized(ctx, resourceRoles, permType, permLevel)
|
|
@@ -64,13 +64,18 @@ async function updateAppUpdatedAt(ctx: BBContext) {
|
|
|
64
64
|
})
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
export default async function builder(ctx: BBContext
|
|
67
|
+
export default async function builder(ctx: BBContext) {
|
|
68
68
|
const appId = ctx.appId
|
|
69
69
|
// this only functions within an app context
|
|
70
70
|
if (!appId) {
|
|
71
71
|
return
|
|
72
72
|
}
|
|
73
|
-
|
|
73
|
+
|
|
74
|
+
// check authenticated
|
|
75
|
+
if (!ctx.isAuthenticated) {
|
|
76
|
+
return ctx.throw(403, "Session not authenticated")
|
|
77
|
+
}
|
|
78
|
+
|
|
74
79
|
const referer = ctx.headers["referer"]
|
|
75
80
|
|
|
76
81
|
const overviewPath = "/builder/portal/overview/"
|
|
@@ -82,7 +87,7 @@ export default async function builder(ctx: BBContext, permType: string) {
|
|
|
82
87
|
const hasAppId = !referer ? false : referer.includes(appId)
|
|
83
88
|
const editingApp = referer ? hasAppId : false
|
|
84
89
|
// check this is a builder call and editing
|
|
85
|
-
if (!
|
|
90
|
+
if (!editingApp) {
|
|
86
91
|
return
|
|
87
92
|
}
|
|
88
93
|
// check locks
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
events,
|
|
3
|
+
DocumentType,
|
|
4
|
+
SEPARATOR,
|
|
5
|
+
UNICODE_MAX,
|
|
6
|
+
} from "@budibase/backend-core"
|
|
2
7
|
import {
|
|
3
8
|
Config,
|
|
4
9
|
isSMTPConfig,
|
|
@@ -9,15 +14,16 @@ import {
|
|
|
9
14
|
} from "@budibase/types"
|
|
10
15
|
import env from "./../../../../environment"
|
|
11
16
|
|
|
17
|
+
export const getConfigParams = () => {
|
|
18
|
+
return {
|
|
19
|
+
include_docs: true,
|
|
20
|
+
startkey: `${DocumentType.CONFIG}${SEPARATOR}`,
|
|
21
|
+
endkey: `${DocumentType.CONFIG}${SEPARATOR}${UNICODE_MAX}`,
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
12
25
|
const getConfigs = async (globalDb: any): Promise<Config[]> => {
|
|
13
|
-
const response = await globalDb.allDocs(
|
|
14
|
-
dbUtils.getConfigParams(
|
|
15
|
-
{},
|
|
16
|
-
{
|
|
17
|
-
include_docs: true,
|
|
18
|
-
}
|
|
19
|
-
)
|
|
20
|
-
)
|
|
26
|
+
const response = await globalDb.allDocs(getConfigParams())
|
|
21
27
|
return response.rows.map((row: any) => row.doc)
|
|
22
28
|
}
|
|
23
29
|
|
|
@@ -8,9 +8,8 @@ jest.mock("@budibase/backend-core", () => {
|
|
|
8
8
|
}
|
|
9
9
|
}
|
|
10
10
|
})
|
|
11
|
-
const {
|
|
11
|
+
const { context, db: dbCore } = require("@budibase/backend-core")
|
|
12
12
|
const TestConfig = require("../../../tests/utilities/TestConfiguration")
|
|
13
|
-
const { TENANT_ID } = require("../../../tests/utilities/structures")
|
|
14
13
|
|
|
15
14
|
// mock email view creation
|
|
16
15
|
|
|
@@ -26,8 +25,8 @@ describe("run", () => {
|
|
|
26
25
|
afterAll(config.end)
|
|
27
26
|
|
|
28
27
|
it("runs successfully", async () => {
|
|
29
|
-
await
|
|
30
|
-
const globalDb =
|
|
28
|
+
await config.doInTenant(async () => {
|
|
29
|
+
const globalDb = context.getGlobalDB()
|
|
31
30
|
await migration.run(globalDb)
|
|
32
31
|
expect(dbCore.createNewUserEmailView).toHaveBeenCalledTimes(1)
|
|
33
32
|
})
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Mimic configs test configuration from worker, creation configs directly in database
|
|
2
2
|
|
|
3
3
|
import * as structures from "./structures"
|
|
4
|
-
import {
|
|
4
|
+
import { configs } from "@budibase/backend-core"
|
|
5
5
|
import { Config } from "@budibase/types"
|
|
6
6
|
|
|
7
7
|
export const saveSettingsConfig = async (globalDb: any) => {
|
|
@@ -25,7 +25,7 @@ export const saveSmtpConfig = async (globalDb: any) => {
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
const saveConfig = async (config: Config, globalDb: any) => {
|
|
28
|
-
config._id =
|
|
28
|
+
config._id = configs.generateConfigID(config.type)
|
|
29
29
|
|
|
30
30
|
let response
|
|
31
31
|
try {
|
package/src/startup.ts
CHANGED
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
generateApiKey,
|
|
6
6
|
getChecklist,
|
|
7
7
|
} from "./utilities/workerRequests"
|
|
8
|
-
import { installation, tenancy, logging } from "@budibase/backend-core"
|
|
8
|
+
import { installation, tenancy, logging, events } from "@budibase/backend-core"
|
|
9
9
|
import fs from "fs"
|
|
10
10
|
import { watch } from "./watch"
|
|
11
11
|
import * as automations from "./automations"
|
|
@@ -124,6 +124,9 @@ export async function startup(app?: any, server?: any) {
|
|
|
124
124
|
// get the references to the queue promises, don't await as
|
|
125
125
|
// they will never end, unless the processing stops
|
|
126
126
|
let queuePromises = []
|
|
127
|
+
// configure events to use the pro audit log write
|
|
128
|
+
// can't integrate directly into backend-core due to cyclic issues
|
|
129
|
+
queuePromises.push(events.processors.init(pro.sdk.auditLogs.write))
|
|
127
130
|
queuePromises.push(automations.init())
|
|
128
131
|
queuePromises.push(initPro())
|
|
129
132
|
if (app) {
|
package/src/tests/jestEnv.ts
CHANGED
|
@@ -21,7 +21,6 @@ import {
|
|
|
21
21
|
basicScreen,
|
|
22
22
|
basicLayout,
|
|
23
23
|
basicWebhook,
|
|
24
|
-
TENANT_ID,
|
|
25
24
|
} from "./structures"
|
|
26
25
|
import {
|
|
27
26
|
constants,
|
|
@@ -41,8 +40,8 @@ import { generateUserMetadataID } from "../../db/utils"
|
|
|
41
40
|
import { startup } from "../../startup"
|
|
42
41
|
import supertest from "supertest"
|
|
43
42
|
import {
|
|
43
|
+
App,
|
|
44
44
|
AuthToken,
|
|
45
|
-
Database,
|
|
46
45
|
Datasource,
|
|
47
46
|
Row,
|
|
48
47
|
SourceName,
|
|
@@ -63,7 +62,7 @@ class TestConfiguration {
|
|
|
63
62
|
started: boolean
|
|
64
63
|
appId: string | null
|
|
65
64
|
allApps: any[]
|
|
66
|
-
app
|
|
65
|
+
app?: App
|
|
67
66
|
prodApp: any
|
|
68
67
|
prodAppId: any
|
|
69
68
|
user: any
|
|
@@ -73,7 +72,7 @@ class TestConfiguration {
|
|
|
73
72
|
linkedTable: any
|
|
74
73
|
automation: any
|
|
75
74
|
datasource: any
|
|
76
|
-
tenantId
|
|
75
|
+
tenantId?: string
|
|
77
76
|
defaultUserValues: DefaultUserValues
|
|
78
77
|
|
|
79
78
|
constructor(openServer = true) {
|
|
@@ -89,7 +88,6 @@ class TestConfiguration {
|
|
|
89
88
|
}
|
|
90
89
|
this.appId = null
|
|
91
90
|
this.allApps = []
|
|
92
|
-
this.tenantId = null
|
|
93
91
|
this.defaultUserValues = this.populateDefaultUserValues()
|
|
94
92
|
}
|
|
95
93
|
|
|
@@ -154,19 +152,10 @@ class TestConfiguration {
|
|
|
154
152
|
|
|
155
153
|
// use a new id as the name to avoid name collisions
|
|
156
154
|
async init(appName = newid()) {
|
|
157
|
-
this.defaultUserValues = this.populateDefaultUserValues()
|
|
158
|
-
if (context.isMultiTenant()) {
|
|
159
|
-
this.tenantId = structures.tenant.id()
|
|
160
|
-
}
|
|
161
|
-
|
|
162
155
|
if (!this.started) {
|
|
163
156
|
await startup()
|
|
164
157
|
}
|
|
165
|
-
|
|
166
|
-
this.globalUserId = this.user._id
|
|
167
|
-
this.userMetadataId = generateUserMetadataID(this.globalUserId)
|
|
168
|
-
|
|
169
|
-
return this.createApp(appName)
|
|
158
|
+
return this.newTenant(appName)
|
|
170
159
|
}
|
|
171
160
|
|
|
172
161
|
end() {
|
|
@@ -182,24 +171,22 @@ class TestConfiguration {
|
|
|
182
171
|
}
|
|
183
172
|
|
|
184
173
|
// MODES
|
|
185
|
-
|
|
174
|
+
setMultiTenancy = (value: boolean) => {
|
|
186
175
|
env._set("MULTI_TENANCY", value)
|
|
187
176
|
coreEnv._set("MULTI_TENANCY", value)
|
|
188
177
|
}
|
|
189
178
|
|
|
190
|
-
|
|
179
|
+
setSelfHosted = (value: boolean) => {
|
|
191
180
|
env._set("SELF_HOSTED", value)
|
|
192
181
|
coreEnv._set("SELF_HOSTED", value)
|
|
193
182
|
}
|
|
194
183
|
|
|
195
184
|
modeCloud = () => {
|
|
196
|
-
this
|
|
197
|
-
this.#setMultiTenancy(true)
|
|
185
|
+
this.setSelfHosted(false)
|
|
198
186
|
}
|
|
199
187
|
|
|
200
188
|
modeSelf = () => {
|
|
201
|
-
this
|
|
202
|
-
this.#setMultiTenancy(false)
|
|
189
|
+
this.setSelfHosted(true)
|
|
203
190
|
}
|
|
204
191
|
|
|
205
192
|
// UTILS
|
|
@@ -354,6 +341,8 @@ class TestConfiguration {
|
|
|
354
341
|
})
|
|
355
342
|
}
|
|
356
343
|
|
|
344
|
+
// HEADERS
|
|
345
|
+
|
|
357
346
|
defaultHeaders(extras = {}) {
|
|
358
347
|
const tenantId = this.getTenantId()
|
|
359
348
|
const authObj: AuthToken = {
|
|
@@ -374,6 +363,7 @@ class TestConfiguration {
|
|
|
374
363
|
`${constants.Cookie.CurrentApp}=${appToken}`,
|
|
375
364
|
],
|
|
376
365
|
[constants.Header.CSRF_TOKEN]: this.defaultUserValues.csrfToken,
|
|
366
|
+
Host: this.tenantHost(),
|
|
377
367
|
...extras,
|
|
378
368
|
}
|
|
379
369
|
|
|
@@ -383,10 +373,6 @@ class TestConfiguration {
|
|
|
383
373
|
return headers
|
|
384
374
|
}
|
|
385
375
|
|
|
386
|
-
getTenantId() {
|
|
387
|
-
return this.tenantId || TENANT_ID
|
|
388
|
-
}
|
|
389
|
-
|
|
390
376
|
publicHeaders({ prodApp = true } = {}) {
|
|
391
377
|
const appId = prodApp ? this.prodAppId : this.appId
|
|
392
378
|
|
|
@@ -397,9 +383,7 @@ class TestConfiguration {
|
|
|
397
383
|
headers[constants.Header.APP_ID] = appId
|
|
398
384
|
}
|
|
399
385
|
|
|
400
|
-
|
|
401
|
-
headers[constants.Header.TENANT_ID] = this.tenantId
|
|
402
|
-
}
|
|
386
|
+
headers[constants.Header.TENANT_ID] = this.getTenantId()
|
|
403
387
|
|
|
404
388
|
return headers
|
|
405
389
|
}
|
|
@@ -413,6 +397,34 @@ class TestConfiguration {
|
|
|
413
397
|
return this.login({ email, roleId, builder, prodApp })
|
|
414
398
|
}
|
|
415
399
|
|
|
400
|
+
// TENANCY
|
|
401
|
+
|
|
402
|
+
tenantHost() {
|
|
403
|
+
const tenantId = this.getTenantId()
|
|
404
|
+
const platformHost = new URL(coreEnv.PLATFORM_URL).host.split(":")[0]
|
|
405
|
+
return `${tenantId}.${platformHost}`
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
getTenantId() {
|
|
409
|
+
if (!this.tenantId) {
|
|
410
|
+
throw new Error("no test tenant id - init has not been called")
|
|
411
|
+
}
|
|
412
|
+
return this.tenantId
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
async newTenant(appName = newid()): Promise<App> {
|
|
416
|
+
this.defaultUserValues = this.populateDefaultUserValues()
|
|
417
|
+
this.tenantId = structures.tenant.id()
|
|
418
|
+
this.user = await this.globalUser()
|
|
419
|
+
this.globalUserId = this.user._id
|
|
420
|
+
this.userMetadataId = generateUserMetadataID(this.globalUserId)
|
|
421
|
+
return this.createApp(appName)
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
doInTenant(task: any) {
|
|
425
|
+
return context.doInTenant(this.getTenantId(), task)
|
|
426
|
+
}
|
|
427
|
+
|
|
416
428
|
// API
|
|
417
429
|
|
|
418
430
|
async generateApiKey(userId = this.defaultUserValues.globalUserId) {
|
|
@@ -432,7 +444,7 @@ class TestConfiguration {
|
|
|
432
444
|
}
|
|
433
445
|
|
|
434
446
|
// APP
|
|
435
|
-
async createApp(appName: string) {
|
|
447
|
+
async createApp(appName: string): Promise<App> {
|
|
436
448
|
// create dev app
|
|
437
449
|
// clear any old app
|
|
438
450
|
this.appId = null
|
|
@@ -442,7 +454,7 @@ class TestConfiguration {
|
|
|
442
454
|
null,
|
|
443
455
|
controllers.app.create
|
|
444
456
|
)
|
|
445
|
-
this.appId = this.app
|
|
457
|
+
this.appId = this.app?.appId!
|
|
446
458
|
})
|
|
447
459
|
return await context.doInAppContext(this.appId, async () => {
|
|
448
460
|
// create production app
|