@budibase/server 2.5.6-alpha.8 → 2.5.6
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/builder/assets/index.56bca6f6.js +1817 -0
- package/builder/assets/index.7f9a008b.css +6 -0
- package/builder/index.html +2 -2
- package/dist/api/controllers/automation.js +7 -13
- package/dist/api/controllers/plugin/index.js +37 -6
- package/dist/api/controllers/table/utils.js +1 -2
- package/dist/api/controllers/user.js +83 -1
- package/dist/api/routes/index.js +0 -2
- package/dist/api/routes/user.js +1 -0
- package/dist/app.js +13 -4
- package/dist/automations/actions.js +6 -32
- package/dist/automations/index.js +2 -3
- package/dist/automations/steps/bash.js +6 -6
- package/dist/automations/steps/createRow.js +11 -11
- package/dist/automations/steps/delay.js +3 -3
- package/dist/automations/steps/deleteRow.js +8 -8
- package/dist/automations/steps/discord.js +8 -8
- package/dist/automations/steps/executeQuery.js +9 -9
- package/dist/automations/steps/executeScript.js +6 -6
- package/dist/automations/steps/filter.js +6 -6
- package/dist/automations/steps/integromat.js +10 -10
- package/dist/automations/steps/loop.js +9 -9
- package/dist/automations/steps/outgoingWebhook.js +10 -10
- package/dist/automations/steps/queryRows.js +14 -14
- package/dist/automations/steps/sendSmtpEmail.js +9 -9
- package/dist/automations/steps/serverLog.js +4 -4
- package/dist/automations/steps/slack.js +6 -6
- package/dist/automations/steps/updateRow.js +11 -11
- package/dist/automations/steps/zapier.js +9 -9
- package/dist/automations/triggerInfo/app.js +5 -5
- package/dist/automations/triggerInfo/cron.js +4 -4
- package/dist/automations/triggerInfo/rowDeleted.js +5 -5
- package/dist/automations/triggerInfo/rowSaved.js +7 -7
- package/dist/automations/triggerInfo/rowUpdated.js +7 -7
- package/dist/automations/triggerInfo/webhook.js +6 -6
- package/dist/elasticApm.js +14 -0
- package/dist/environment.js +1 -0
- package/dist/events/index.js +0 -3
- package/dist/integrations/index.js +3 -3
- package/dist/integrations/microsoftSqlServer.js +2 -5
- package/dist/integrations/mysql.js +3 -5
- package/dist/integrations/postgres.js +5 -7
- package/dist/integrations/redis.js +0 -7
- package/dist/integrations/rest.js +0 -4
- package/dist/migrations/functions/usageQuotas/syncApps.js +1 -1
- package/dist/migrations/functions/usageQuotas/syncRows.js +2 -1
- package/dist/package.json +15 -15
- package/dist/sdk/app/applications/sync.js +23 -117
- package/dist/sdk/index.js +0 -2
- package/dist/sdk/users/utils.js +4 -21
- package/dist/startup.js +28 -31
- package/dist/threads/automation.js +5 -16
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/dist/utilities/fileSystem/plugin.js +23 -33
- package/dist/utilities/global.js +12 -17
- package/dist/watch.js +2 -2
- package/dist/websocket.js +22 -0
- package/jest.config.ts +3 -3
- package/nodemon.json +3 -7
- package/package.json +16 -16
- package/scripts/dev/manage.js +0 -2
- package/scripts/integrations/mssql/data/entrypoint.sh +0 -1
- package/scripts/integrations/mssql/data/setup.sql +17 -17
- package/scripts/integrations/mysql/init.sql +1 -1
- package/scripts/integrations/postgres/init.sql +0 -1
- package/scripts/likeCypress.ts +35 -0
- package/src/api/controllers/automation.ts +6 -12
- package/src/api/controllers/plugin/index.ts +45 -8
- package/src/api/controllers/row/internal.ts +10 -9
- package/src/api/controllers/row/utils.ts +2 -2
- package/src/api/controllers/table/utils.ts +1 -2
- package/src/api/controllers/user.ts +96 -10
- package/src/api/routes/index.ts +0 -2
- package/src/api/routes/tests/automation.spec.js +4 -7
- package/src/api/routes/tests/user.spec.js +37 -48
- package/src/api/routes/user.ts +5 -0
- package/src/app.ts +15 -4
- package/src/automations/actions.ts +24 -56
- package/src/automations/index.ts +1 -1
- package/src/automations/steps/bash.ts +7 -10
- package/src/automations/steps/createRow.ts +12 -15
- package/src/automations/steps/delay.ts +4 -6
- package/src/automations/steps/deleteRow.ts +9 -12
- package/src/automations/steps/discord.ts +8 -10
- package/src/automations/steps/executeQuery.ts +10 -13
- package/src/automations/steps/executeScript.ts +7 -10
- package/src/automations/steps/filter.ts +6 -8
- package/src/automations/steps/integromat.ts +10 -12
- package/src/automations/steps/loop.ts +10 -16
- package/src/automations/steps/outgoingWebhook.ts +11 -14
- package/src/automations/steps/queryRows.ts +15 -18
- package/src/automations/steps/sendSmtpEmail.ts +9 -11
- package/src/automations/steps/serverLog.ts +4 -6
- package/src/automations/steps/slack.ts +6 -8
- package/src/automations/steps/updateRow.ts +12 -15
- package/src/automations/steps/zapier.ts +9 -11
- package/src/automations/tests/utilities/index.ts +2 -2
- package/src/automations/triggerInfo/app.ts +5 -8
- package/src/automations/triggerInfo/cron.ts +4 -7
- package/src/automations/triggerInfo/rowDeleted.ts +5 -8
- package/src/automations/triggerInfo/rowSaved.ts +7 -10
- package/src/automations/triggerInfo/rowUpdated.ts +7 -10
- package/src/automations/triggerInfo/webhook.ts +6 -9
- package/src/elasticApm.ts +10 -0
- package/src/environment.ts +1 -0
- package/src/events/index.ts +0 -1
- package/src/integrations/index.ts +3 -3
- package/src/integrations/microsoftSqlServer.ts +2 -5
- package/src/integrations/mysql.ts +3 -5
- package/src/integrations/postgres.ts +5 -7
- package/src/integrations/redis.ts +0 -8
- package/src/integrations/rest.ts +0 -3
- package/src/migrations/functions/usageQuotas/syncApps.ts +1 -1
- package/src/migrations/functions/usageQuotas/syncRows.ts +3 -2
- package/src/migrations/functions/usageQuotas/tests/syncRows.spec.ts +2 -2
- package/src/sdk/app/applications/sync.ts +22 -129
- package/src/sdk/index.ts +0 -2
- package/src/sdk/users/tests/utils.spec.ts +32 -1
- package/src/sdk/users/utils.ts +5 -23
- package/src/startup.ts +34 -36
- package/src/tests/jestEnv.ts +1 -0
- package/src/tests/jestSetup.ts +1 -0
- package/src/tests/logging.ts +34 -0
- package/src/tests/utilities/TestConfiguration.ts +0 -28
- package/src/tests/utilities/structures.ts +17 -25
- package/src/threads/automation.ts +6 -18
- package/src/utilities/fileSystem/plugin.ts +4 -13
- package/src/utilities/global.ts +16 -21
- package/src/watch.ts +2 -2
- package/src/websocket.ts +26 -0
- package/tsconfig.json +7 -1
- package/builder/assets/index.5c1a6913.js +0 -1776
- package/builder/assets/index.c0265b74.css +0 -6
- package/dist/api/controllers/ops.js +0 -40
- package/dist/api/routes/ops.js +0 -52
- package/dist/events/docUpdates/index.js +0 -17
- package/dist/events/docUpdates/processors.js +0 -18
- package/dist/events/docUpdates/syncUsers.js +0 -49
- package/dist/sdk/plugins/index.js +0 -27
- package/dist/sdk/plugins/plugins.js +0 -53
- package/dist/websockets/client.js +0 -14
- package/dist/websockets/grid.js +0 -60
- package/dist/websockets/index.js +0 -17
- package/dist/websockets/websocket.js +0 -78
- package/src/api/controllers/ops.ts +0 -32
- package/src/api/routes/ops.ts +0 -30
- package/src/events/docUpdates/index.ts +0 -1
- package/src/events/docUpdates/processors.ts +0 -14
- package/src/events/docUpdates/syncUsers.ts +0 -35
- package/src/sdk/app/applications/tests/sync.spec.ts +0 -137
- package/src/sdk/plugins/index.ts +0 -5
- package/src/sdk/plugins/plugins.ts +0 -41
- package/src/websockets/client.ts +0 -11
- package/src/websockets/grid.ts +0 -55
- package/src/websockets/index.ts +0 -14
- package/src/websockets/websocket.ts +0 -83
|
@@ -6,7 +6,6 @@ interface RedisConfig {
|
|
|
6
6
|
port: number
|
|
7
7
|
username: string
|
|
8
8
|
password?: string
|
|
9
|
-
db?: number
|
|
10
9
|
}
|
|
11
10
|
|
|
12
11
|
const SCHEMA: Integration = {
|
|
@@ -33,12 +32,6 @@ const SCHEMA: Integration = {
|
|
|
33
32
|
type: "password",
|
|
34
33
|
required: false,
|
|
35
34
|
},
|
|
36
|
-
db: {
|
|
37
|
-
type: "number",
|
|
38
|
-
required: false,
|
|
39
|
-
display: "DB",
|
|
40
|
-
default: 0,
|
|
41
|
-
},
|
|
42
35
|
},
|
|
43
36
|
query: {
|
|
44
37
|
create: {
|
|
@@ -95,7 +88,6 @@ class RedisIntegration {
|
|
|
95
88
|
port: this.config.port,
|
|
96
89
|
username: this.config.username,
|
|
97
90
|
password: this.config.password,
|
|
98
|
-
db: this.config.db,
|
|
99
91
|
})
|
|
100
92
|
}
|
|
101
93
|
|
package/src/integrations/rest.ts
CHANGED
|
@@ -151,9 +151,6 @@ class RestIntegration implements IntegrationBase {
|
|
|
151
151
|
data = data[keys[0]]
|
|
152
152
|
}
|
|
153
153
|
raw = rawXml
|
|
154
|
-
} else if (contentType.includes("application/pdf")) {
|
|
155
|
-
data = await response.arrayBuffer() // Save PDF as ArrayBuffer
|
|
156
|
-
raw = Buffer.from(data)
|
|
157
154
|
} else {
|
|
158
155
|
data = await response.text()
|
|
159
156
|
raw = data
|
|
@@ -9,6 +9,6 @@ export const run = async () => {
|
|
|
9
9
|
|
|
10
10
|
// sync app count
|
|
11
11
|
const tenantId = tenancy.getTenantId()
|
|
12
|
-
console.log(`Syncing app count: ${appCount}`)
|
|
12
|
+
console.log(`[Tenant: ${tenantId}] Syncing app count: ${appCount}`)
|
|
13
13
|
await quotas.setUsage(appCount, StaticQuotaName.APPS, QuotaUsageType.STATIC)
|
|
14
14
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { db as dbCore } from "@budibase/backend-core"
|
|
1
|
+
import { tenancy, db as dbCore } from "@budibase/backend-core"
|
|
2
2
|
import { getUniqueRows } from "../../../utilities/usageQuota/rows"
|
|
3
3
|
import { quotas } from "@budibase/pro"
|
|
4
4
|
import { StaticQuotaName, QuotaUsageType, App } from "@budibase/types"
|
|
@@ -18,7 +18,8 @@ export const run = async () => {
|
|
|
18
18
|
})
|
|
19
19
|
|
|
20
20
|
// sync row count
|
|
21
|
-
|
|
21
|
+
const tenantId = tenancy.getTenantId()
|
|
22
|
+
console.log(`[Tenant: ${tenantId}] Syncing row count: ${rowCount}`)
|
|
22
23
|
await quotas.setUsagePerApp(
|
|
23
24
|
counts,
|
|
24
25
|
StaticQuotaName.ROWS,
|
|
@@ -24,7 +24,7 @@ describe("syncRows", () => {
|
|
|
24
24
|
|
|
25
25
|
// app 1
|
|
26
26
|
const app1 = config.app
|
|
27
|
-
await context.doInAppContext(app1
|
|
27
|
+
await context.doInAppContext(app1.appId, async () => {
|
|
28
28
|
await config.createTable()
|
|
29
29
|
await config.createRow()
|
|
30
30
|
})
|
|
@@ -43,7 +43,7 @@ describe("syncRows", () => {
|
|
|
43
43
|
usageDoc = await quotas.getQuotaUsage()
|
|
44
44
|
expect(usageDoc.usageQuota.rows).toEqual(3)
|
|
45
45
|
expect(
|
|
46
|
-
usageDoc.apps?.[dbCore.getProdAppID(app1
|
|
46
|
+
usageDoc.apps?.[dbCore.getProdAppID(app1.appId)].usageQuota.rows
|
|
47
47
|
).toEqual(1)
|
|
48
48
|
expect(
|
|
49
49
|
usageDoc.apps?.[dbCore.getProdAppID(app2.appId)].usageQuota.rows
|
|
@@ -1,117 +1,6 @@
|
|
|
1
1
|
import env from "../../../environment"
|
|
2
|
-
import {
|
|
3
|
-
db as dbCore,
|
|
4
|
-
context,
|
|
5
|
-
docUpdates,
|
|
6
|
-
constants,
|
|
7
|
-
logging,
|
|
8
|
-
roles,
|
|
9
|
-
} from "@budibase/backend-core"
|
|
10
|
-
import { User, ContextUser, UserGroup } from "@budibase/types"
|
|
11
|
-
import { sdk as proSdk } from "@budibase/pro"
|
|
2
|
+
import { db as dbCore, context } from "@budibase/backend-core"
|
|
12
3
|
import sdk from "../../"
|
|
13
|
-
import { getGlobalUsers, processUser } from "../../../utilities/global"
|
|
14
|
-
import { generateUserMetadataID, InternalTables } from "../../../db/utils"
|
|
15
|
-
|
|
16
|
-
type DeletedUser = { _id: string; deleted: boolean }
|
|
17
|
-
|
|
18
|
-
async function syncUsersToApp(
|
|
19
|
-
appId: string,
|
|
20
|
-
users: (User | DeletedUser)[],
|
|
21
|
-
groups: UserGroup[]
|
|
22
|
-
) {
|
|
23
|
-
if (!(await dbCore.dbExists(appId))) {
|
|
24
|
-
return
|
|
25
|
-
}
|
|
26
|
-
await context.doInAppContext(appId, async () => {
|
|
27
|
-
const db = context.getAppDB()
|
|
28
|
-
for (let user of users) {
|
|
29
|
-
let ctxUser = user as ContextUser
|
|
30
|
-
let deletedUser = false
|
|
31
|
-
const metadataId = generateUserMetadataID(user._id!)
|
|
32
|
-
if ((user as DeletedUser).deleted) {
|
|
33
|
-
deletedUser = true
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// make sure role is correct
|
|
37
|
-
if (!deletedUser) {
|
|
38
|
-
ctxUser = await processUser(ctxUser, { appId, groups })
|
|
39
|
-
}
|
|
40
|
-
let roleId = ctxUser.roleId
|
|
41
|
-
if (roleId === roles.BUILTIN_ROLE_IDS.PUBLIC) {
|
|
42
|
-
roleId = undefined
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
let metadata
|
|
46
|
-
try {
|
|
47
|
-
metadata = await db.get(metadataId)
|
|
48
|
-
} catch (err: any) {
|
|
49
|
-
if (err.status !== 404) {
|
|
50
|
-
throw err
|
|
51
|
-
}
|
|
52
|
-
// no metadata and user is to be deleted, can skip
|
|
53
|
-
// no role - user isn't in app anyway
|
|
54
|
-
if (!roleId) {
|
|
55
|
-
continue
|
|
56
|
-
} else if (!deletedUser) {
|
|
57
|
-
// doesn't exist yet, creating it
|
|
58
|
-
metadata = {
|
|
59
|
-
tableId: InternalTables.USER_METADATA,
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// the user doesn't exist, or doesn't have a role anymore
|
|
65
|
-
// get rid of their metadata
|
|
66
|
-
if (deletedUser || !roleId) {
|
|
67
|
-
await db.remove(metadata)
|
|
68
|
-
continue
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// assign the roleId for the metadata doc
|
|
72
|
-
if (roleId) {
|
|
73
|
-
metadata.roleId = roleId
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
let combined = sdk.users.combineMetadataAndUser(ctxUser, metadata)
|
|
77
|
-
// if no combined returned, there are no updates to make
|
|
78
|
-
if (combined) {
|
|
79
|
-
await db.put(combined)
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
})
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
export async function syncUsersToAllApps(userIds: string[]) {
|
|
86
|
-
// list of users, if one has been deleted it will be undefined in array
|
|
87
|
-
const users = (await getGlobalUsers(userIds, {
|
|
88
|
-
noProcessing: true,
|
|
89
|
-
})) as User[]
|
|
90
|
-
const groups = await proSdk.groups.fetch()
|
|
91
|
-
const finalUsers: (User | DeletedUser)[] = []
|
|
92
|
-
for (let userId of userIds) {
|
|
93
|
-
const user = users.find(user => user._id === userId)
|
|
94
|
-
if (!user) {
|
|
95
|
-
finalUsers.push({ _id: userId, deleted: true })
|
|
96
|
-
} else {
|
|
97
|
-
finalUsers.push(user)
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
const devAppIds = await dbCore.getDevAppIDs()
|
|
101
|
-
let promises = []
|
|
102
|
-
for (let devAppId of devAppIds) {
|
|
103
|
-
const prodAppId = dbCore.getProdAppID(devAppId)
|
|
104
|
-
for (let appId of [prodAppId, devAppId]) {
|
|
105
|
-
promises.push(syncUsersToApp(appId, finalUsers, groups))
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
const resp = await Promise.allSettled(promises)
|
|
109
|
-
const failed = resp.filter(promise => promise.status === "rejected")
|
|
110
|
-
if (failed.length > 0) {
|
|
111
|
-
const reasons = failed.map(fail => (fail as PromiseRejectedResult).reason)
|
|
112
|
-
logging.logAlert("Failed to sync users to apps", reasons)
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
4
|
|
|
116
5
|
export async function syncApp(
|
|
117
6
|
appId: string,
|
|
@@ -134,28 +23,32 @@ export async function syncApp(
|
|
|
134
23
|
// specific case, want to make sure setup is skipped
|
|
135
24
|
const prodDb = context.getProdAppDB({ skip_setup: true })
|
|
136
25
|
const exists = await prodDb.exists()
|
|
26
|
+
if (!exists) {
|
|
27
|
+
// the database doesn't exist. Don't replicate
|
|
28
|
+
return {
|
|
29
|
+
message: "App sync not required, app not deployed.",
|
|
30
|
+
}
|
|
31
|
+
}
|
|
137
32
|
|
|
33
|
+
const replication = new dbCore.Replication({
|
|
34
|
+
source: prodAppId,
|
|
35
|
+
target: appId,
|
|
36
|
+
})
|
|
138
37
|
let error
|
|
139
|
-
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
try {
|
|
145
|
-
const replOpts = replication.appReplicateOpts()
|
|
146
|
-
if (opts?.automationOnly) {
|
|
147
|
-
replOpts.filter = (doc: any) =>
|
|
148
|
-
doc._id.startsWith(dbCore.DocumentType.AUTOMATION)
|
|
149
|
-
}
|
|
150
|
-
await replication.replicate(replOpts)
|
|
151
|
-
} catch (err) {
|
|
152
|
-
error = err
|
|
153
|
-
} finally {
|
|
154
|
-
await replication.close()
|
|
38
|
+
try {
|
|
39
|
+
const replOpts = replication.appReplicateOpts()
|
|
40
|
+
if (opts?.automationOnly) {
|
|
41
|
+
replOpts.filter = (doc: any) =>
|
|
42
|
+
doc._id.startsWith(dbCore.DocumentType.AUTOMATION)
|
|
155
43
|
}
|
|
44
|
+
await replication.replicate(replOpts)
|
|
45
|
+
} catch (err) {
|
|
46
|
+
error = err
|
|
47
|
+
} finally {
|
|
48
|
+
await replication.close()
|
|
156
49
|
}
|
|
157
50
|
|
|
158
|
-
// sync the users
|
|
51
|
+
// sync the users
|
|
159
52
|
await sdk.users.syncGlobalUsers()
|
|
160
53
|
|
|
161
54
|
if (error) {
|
package/src/sdk/index.ts
CHANGED
|
@@ -6,7 +6,6 @@ import { default as datasources } from "./app/datasources"
|
|
|
6
6
|
import { default as queries } from "./app/queries"
|
|
7
7
|
import { default as rows } from "./app/rows"
|
|
8
8
|
import { default as users } from "./users"
|
|
9
|
-
import { default as plugins } from "./plugins"
|
|
10
9
|
|
|
11
10
|
const sdk = {
|
|
12
11
|
backups,
|
|
@@ -17,7 +16,6 @@ const sdk = {
|
|
|
17
16
|
users,
|
|
18
17
|
datasources,
|
|
19
18
|
queries,
|
|
20
|
-
plugins,
|
|
21
19
|
}
|
|
22
20
|
|
|
23
21
|
// default export for TS
|
|
@@ -121,7 +121,38 @@ describe("syncGlobalUsers", () => {
|
|
|
121
121
|
await syncGlobalUsers()
|
|
122
122
|
|
|
123
123
|
const metadata = await rawUserMetadata()
|
|
124
|
-
expect(metadata).toHaveLength(
|
|
124
|
+
expect(metadata).toHaveLength(1)
|
|
125
|
+
})
|
|
126
|
+
})
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
it("app users are removed when app is removed from user group", async () => {
|
|
130
|
+
await config.doInTenant(async () => {
|
|
131
|
+
const group = await proSdk.groups.save(structures.userGroups.userGroup())
|
|
132
|
+
const user1 = await config.createUser({ admin: false, builder: false })
|
|
133
|
+
const user2 = await config.createUser({ admin: false, builder: false })
|
|
134
|
+
await proSdk.groups.updateGroupApps(group.id, {
|
|
135
|
+
appsToAdd: [
|
|
136
|
+
{ appId: config.prodAppId!, roleId: roles.BUILTIN_ROLE_IDS.BASIC },
|
|
137
|
+
],
|
|
138
|
+
})
|
|
139
|
+
await proSdk.groups.addUsers(group.id, [user1._id, user2._id])
|
|
140
|
+
|
|
141
|
+
await config.doInContext(config.appId, async () => {
|
|
142
|
+
await syncGlobalUsers()
|
|
143
|
+
expect(await rawUserMetadata()).toHaveLength(3)
|
|
144
|
+
|
|
145
|
+
await proSdk.groups.removeUsers(group.id, [user1._id])
|
|
146
|
+
await syncGlobalUsers()
|
|
147
|
+
|
|
148
|
+
const metadata = await rawUserMetadata()
|
|
149
|
+
expect(metadata).toHaveLength(2)
|
|
150
|
+
|
|
151
|
+
expect(metadata).not.toContainEqual(
|
|
152
|
+
expect.objectContaining({
|
|
153
|
+
_id: db.generateUserMetadataID(user1._id),
|
|
154
|
+
})
|
|
155
|
+
)
|
|
125
156
|
})
|
|
126
157
|
})
|
|
127
158
|
})
|
package/src/sdk/users/utils.ts
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import { getGlobalUsers } from "../../utilities/global"
|
|
2
2
|
import { context, roles as rolesCore } from "@budibase/backend-core"
|
|
3
3
|
import {
|
|
4
|
-
getGlobalIDFromUserMetadataID,
|
|
5
4
|
generateUserMetadataID,
|
|
6
5
|
getUserMetadataParams,
|
|
7
6
|
InternalTables,
|
|
8
7
|
} from "../../db/utils"
|
|
9
8
|
import { isEqual } from "lodash"
|
|
10
|
-
import { ContextUser, UserMetadata
|
|
9
|
+
import { ContextUser, UserMetadata } from "@budibase/types"
|
|
11
10
|
|
|
12
11
|
export function combineMetadataAndUser(
|
|
13
12
|
user: ContextUser,
|
|
@@ -38,10 +37,6 @@ export function combineMetadataAndUser(
|
|
|
38
37
|
if (found) {
|
|
39
38
|
newDoc._rev = found._rev
|
|
40
39
|
}
|
|
41
|
-
// clear fields that shouldn't be in metadata
|
|
42
|
-
delete newDoc.password
|
|
43
|
-
delete newDoc.forceResetPassword
|
|
44
|
-
delete newDoc.roles
|
|
45
40
|
if (found == null || !isEqual(newDoc, found)) {
|
|
46
41
|
return {
|
|
47
42
|
...found,
|
|
@@ -65,9 +60,10 @@ export async function rawUserMetadata() {
|
|
|
65
60
|
export async function syncGlobalUsers() {
|
|
66
61
|
// sync user metadata
|
|
67
62
|
const db = context.getAppDB()
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
63
|
+
const [users, metadata] = await Promise.all([
|
|
64
|
+
getGlobalUsers(),
|
|
65
|
+
rawUserMetadata(),
|
|
66
|
+
])
|
|
71
67
|
const toWrite = []
|
|
72
68
|
for (let user of users) {
|
|
73
69
|
const combined = combineMetadataAndUser(user, metadata)
|
|
@@ -75,19 +71,5 @@ export async function syncGlobalUsers() {
|
|
|
75
71
|
toWrite.push(combined)
|
|
76
72
|
}
|
|
77
73
|
}
|
|
78
|
-
let foundEmails: string[] = []
|
|
79
|
-
for (let data of metadata) {
|
|
80
|
-
if (!data._id) {
|
|
81
|
-
continue
|
|
82
|
-
}
|
|
83
|
-
const alreadyExisting = data.email && foundEmails.indexOf(data.email) !== -1
|
|
84
|
-
const globalId = getGlobalIDFromUserMetadataID(data._id)
|
|
85
|
-
if (!users.find(user => user._id === globalId) || alreadyExisting) {
|
|
86
|
-
toWrite.push({ ...data, _deleted: true })
|
|
87
|
-
}
|
|
88
|
-
if (data.email) {
|
|
89
|
-
foundEmails.push(data.email)
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
74
|
await db.bulkDocs(toWrite)
|
|
93
75
|
}
|
package/src/startup.ts
CHANGED
|
@@ -10,16 +10,19 @@ import fs from "fs"
|
|
|
10
10
|
import { watch } from "./watch"
|
|
11
11
|
import * as automations from "./automations"
|
|
12
12
|
import * as fileSystem from "./utilities/fileSystem"
|
|
13
|
-
import
|
|
13
|
+
import eventEmitter from "./events"
|
|
14
14
|
import * as migrations from "./migrations"
|
|
15
15
|
import * as bullboard from "./automations/bullboard"
|
|
16
16
|
import * as pro from "@budibase/pro"
|
|
17
17
|
import * as api from "./api"
|
|
18
18
|
import sdk from "./sdk"
|
|
19
|
+
const pino = require("koa-pino-logger")
|
|
19
20
|
|
|
20
21
|
let STARTUP_RAN = false
|
|
21
22
|
|
|
22
23
|
async function initRoutes(app: any) {
|
|
24
|
+
app.use(pino(logging.pinoSettings()))
|
|
25
|
+
|
|
23
26
|
if (!env.isTest()) {
|
|
24
27
|
const plugin = await bullboard.init()
|
|
25
28
|
app.use(plugin)
|
|
@@ -45,10 +48,8 @@ async function initPro() {
|
|
|
45
48
|
}
|
|
46
49
|
|
|
47
50
|
function shutdown(server?: any) {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
server.destroy()
|
|
51
|
-
}
|
|
51
|
+
server.close()
|
|
52
|
+
server.destroy()
|
|
52
53
|
}
|
|
53
54
|
|
|
54
55
|
export async function startup(app?: any, server?: any) {
|
|
@@ -63,7 +64,6 @@ export async function startup(app?: any, server?: any) {
|
|
|
63
64
|
eventEmitter.emitPort(env.PORT)
|
|
64
65
|
fileSystem.init()
|
|
65
66
|
await redis.init()
|
|
66
|
-
eventInit()
|
|
67
67
|
|
|
68
68
|
// run migrations on startup if not done via http
|
|
69
69
|
// not recommended in a clustered environment
|
|
@@ -72,39 +72,11 @@ export async function startup(app?: any, server?: any) {
|
|
|
72
72
|
await migrations.migrate()
|
|
73
73
|
} catch (e) {
|
|
74
74
|
logging.logAlert("Error performing migrations. Exiting.", e)
|
|
75
|
-
shutdown(
|
|
75
|
+
shutdown()
|
|
76
76
|
}
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
-
// monitor plugin directory if required
|
|
80
|
-
if (
|
|
81
|
-
env.SELF_HOSTED &&
|
|
82
|
-
!env.MULTI_TENANCY &&
|
|
83
|
-
env.PLUGINS_DIR &&
|
|
84
|
-
fs.existsSync(env.PLUGINS_DIR)
|
|
85
|
-
) {
|
|
86
|
-
watch()
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// check for version updates
|
|
90
|
-
await installation.checkInstallVersion()
|
|
91
|
-
|
|
92
|
-
// get the references to the queue promises, don't await as
|
|
93
|
-
// they will never end, unless the processing stops
|
|
94
|
-
let queuePromises = []
|
|
95
|
-
// configure events to use the pro audit log write
|
|
96
|
-
// can't integrate directly into backend-core due to cyclic issues
|
|
97
|
-
queuePromises.push(events.processors.init(pro.sdk.auditLogs.write))
|
|
98
|
-
queuePromises.push(automations.init())
|
|
99
|
-
queuePromises.push(initPro())
|
|
100
|
-
if (app) {
|
|
101
|
-
// bring routes online as final step once everything ready
|
|
102
|
-
await initRoutes(app)
|
|
103
|
-
}
|
|
104
|
-
|
|
105
79
|
// check and create admin user if required
|
|
106
|
-
// this must be run after the api has been initialised due to
|
|
107
|
-
// the app user sync
|
|
108
80
|
if (
|
|
109
81
|
env.SELF_HOSTED &&
|
|
110
82
|
!env.MULTI_TENANCY &&
|
|
@@ -131,8 +103,34 @@ export async function startup(app?: any, server?: any) {
|
|
|
131
103
|
)
|
|
132
104
|
} catch (e) {
|
|
133
105
|
logging.logAlert("Error creating initial admin user. Exiting.", e)
|
|
134
|
-
shutdown(
|
|
106
|
+
shutdown()
|
|
135
107
|
}
|
|
136
108
|
}
|
|
137
109
|
}
|
|
110
|
+
|
|
111
|
+
// monitor plugin directory if required
|
|
112
|
+
if (
|
|
113
|
+
env.SELF_HOSTED &&
|
|
114
|
+
!env.MULTI_TENANCY &&
|
|
115
|
+
env.PLUGINS_DIR &&
|
|
116
|
+
fs.existsSync(env.PLUGINS_DIR)
|
|
117
|
+
) {
|
|
118
|
+
watch()
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// check for version updates
|
|
122
|
+
await installation.checkInstallVersion()
|
|
123
|
+
|
|
124
|
+
// get the references to the queue promises, don't await as
|
|
125
|
+
// they will never end, unless the processing stops
|
|
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))
|
|
130
|
+
queuePromises.push(automations.init())
|
|
131
|
+
queuePromises.push(initPro())
|
|
132
|
+
if (app) {
|
|
133
|
+
// bring routes online as final step once everything ready
|
|
134
|
+
await initRoutes(app)
|
|
135
|
+
}
|
|
138
136
|
}
|
package/src/tests/jestEnv.ts
CHANGED
|
@@ -6,6 +6,7 @@ process.env.MULTI_TENANCY = "1"
|
|
|
6
6
|
// @ts-ignore
|
|
7
7
|
process.env.BUDIBASE_DIR = tmpdir("budibase-unittests")
|
|
8
8
|
process.env.LOG_LEVEL = process.env.LOG_LEVEL || "error"
|
|
9
|
+
process.env.ENABLE_4XX_HTTP_LOGGING = "0"
|
|
9
10
|
process.env.MOCK_REDIS = "1"
|
|
10
11
|
process.env.PLATFORM_URL = "http://localhost:10000"
|
|
11
12
|
process.env.REDIS_PASSWORD = "budibase"
|
package/src/tests/jestSetup.ts
CHANGED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export enum LogLevel {
|
|
2
|
+
TRACE = "trace",
|
|
3
|
+
DEBUG = "debug",
|
|
4
|
+
INFO = "info",
|
|
5
|
+
WARN = "warn",
|
|
6
|
+
ERROR = "error",
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const LOG_INDEX: { [key in LogLevel]: number } = {
|
|
10
|
+
[LogLevel.TRACE]: 1,
|
|
11
|
+
[LogLevel.DEBUG]: 2,
|
|
12
|
+
[LogLevel.INFO]: 3,
|
|
13
|
+
[LogLevel.WARN]: 4,
|
|
14
|
+
[LogLevel.ERROR]: 5,
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const setIndex = LOG_INDEX[process.env.LOG_LEVEL as LogLevel]
|
|
18
|
+
|
|
19
|
+
if (setIndex > LOG_INDEX.trace) {
|
|
20
|
+
global.console.trace = jest.fn()
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (setIndex > LOG_INDEX.debug) {
|
|
24
|
+
global.console.debug = jest.fn()
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (setIndex > LOG_INDEX.info) {
|
|
28
|
+
global.console.info = jest.fn()
|
|
29
|
+
global.console.log = jest.fn()
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (setIndex > LOG_INDEX.warn) {
|
|
33
|
+
global.console.warn = jest.fn()
|
|
34
|
+
}
|
|
@@ -49,7 +49,6 @@ import {
|
|
|
49
49
|
SearchFilters,
|
|
50
50
|
UserRoles,
|
|
51
51
|
} from "@budibase/types"
|
|
52
|
-
import { BUILTIN_ROLE_IDS } from "@budibase/backend-core/src/security/roles"
|
|
53
52
|
|
|
54
53
|
type DefaultUserValues = {
|
|
55
54
|
globalUserId: string
|
|
@@ -307,33 +306,6 @@ class TestConfiguration {
|
|
|
307
306
|
}
|
|
308
307
|
}
|
|
309
308
|
|
|
310
|
-
async createGroup(roleId: string = BUILTIN_ROLE_IDS.BASIC) {
|
|
311
|
-
return context.doInTenant(this.tenantId!, async () => {
|
|
312
|
-
const baseGroup = structures.userGroups.userGroup()
|
|
313
|
-
baseGroup.roles = {
|
|
314
|
-
[this.prodAppId]: roleId,
|
|
315
|
-
}
|
|
316
|
-
const { id, rev } = await pro.sdk.groups.save(baseGroup)
|
|
317
|
-
return {
|
|
318
|
-
_id: id,
|
|
319
|
-
_rev: rev,
|
|
320
|
-
...baseGroup,
|
|
321
|
-
}
|
|
322
|
-
})
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
async addUserToGroup(groupId: string, userId: string) {
|
|
326
|
-
return context.doInTenant(this.tenantId!, async () => {
|
|
327
|
-
await pro.sdk.groups.addUsers(groupId, [userId])
|
|
328
|
-
})
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
async removeUserFromGroup(groupId: string, userId: string) {
|
|
332
|
-
return context.doInTenant(this.tenantId!, async () => {
|
|
333
|
-
await pro.sdk.groups.removeUsers(groupId, [userId])
|
|
334
|
-
})
|
|
335
|
-
}
|
|
336
|
-
|
|
337
309
|
async login({ roleId, userId, builder, prodApp = false }: any = {}) {
|
|
338
310
|
const appId = prodApp ? this.prodAppId : this.appId
|
|
339
311
|
return context.doInAppContext(appId, async () => {
|
|
@@ -1,22 +1,18 @@
|
|
|
1
|
-
import { permissions, roles
|
|
1
|
+
import { permissions, roles } from "@budibase/backend-core"
|
|
2
2
|
import { createHomeScreen } from "../../constants/screens"
|
|
3
3
|
import { EMPTY_LAYOUT } from "../../constants/layouts"
|
|
4
4
|
import { cloneDeep } from "lodash/fp"
|
|
5
|
-
import {
|
|
6
|
-
BUILTIN_ACTION_DEFINITIONS,
|
|
7
|
-
TRIGGER_DEFINITIONS,
|
|
8
|
-
} from "../../automations"
|
|
5
|
+
import { ACTION_DEFINITIONS, TRIGGER_DEFINITIONS } from "../../automations"
|
|
9
6
|
import {
|
|
10
7
|
Automation,
|
|
11
8
|
AutomationActionStepId,
|
|
12
|
-
AutomationStep,
|
|
13
|
-
AutomationStepType,
|
|
14
|
-
AutomationTrigger,
|
|
15
9
|
AutomationTriggerStepId,
|
|
16
10
|
Datasource,
|
|
17
11
|
SourceName,
|
|
18
12
|
} from "@budibase/types"
|
|
19
13
|
|
|
14
|
+
const { v4: uuidv4 } = require("uuid")
|
|
15
|
+
|
|
20
16
|
export function basicTable() {
|
|
21
17
|
return {
|
|
22
18
|
name: "TestTable",
|
|
@@ -75,19 +71,19 @@ export function view(tableId: string) {
|
|
|
75
71
|
}
|
|
76
72
|
|
|
77
73
|
export function automationStep(
|
|
78
|
-
actionDefinition =
|
|
79
|
-
)
|
|
74
|
+
actionDefinition = ACTION_DEFINITIONS.CREATE_ROW
|
|
75
|
+
) {
|
|
80
76
|
return {
|
|
81
|
-
id:
|
|
77
|
+
id: uuidv4(),
|
|
82
78
|
...actionDefinition,
|
|
83
79
|
}
|
|
84
80
|
}
|
|
85
81
|
|
|
86
82
|
export function automationTrigger(
|
|
87
83
|
triggerDefinition = TRIGGER_DEFINITIONS.ROW_SAVED
|
|
88
|
-
)
|
|
84
|
+
) {
|
|
89
85
|
return {
|
|
90
|
-
id:
|
|
86
|
+
id: uuidv4(),
|
|
91
87
|
...triggerDefinition,
|
|
92
88
|
}
|
|
93
89
|
}
|
|
@@ -110,7 +106,7 @@ export function newAutomation({ steps, trigger }: any = {}) {
|
|
|
110
106
|
return automation
|
|
111
107
|
}
|
|
112
108
|
|
|
113
|
-
export function basicAutomation(appId?: string)
|
|
109
|
+
export function basicAutomation(appId?: string) {
|
|
114
110
|
return {
|
|
115
111
|
name: "My Automation",
|
|
116
112
|
screenId: "kasdkfldsafkl",
|
|
@@ -123,22 +119,18 @@ export function basicAutomation(appId?: string): Automation {
|
|
|
123
119
|
tagline: "test",
|
|
124
120
|
icon: "test",
|
|
125
121
|
description: "test",
|
|
126
|
-
type:
|
|
122
|
+
type: "trigger",
|
|
127
123
|
id: "test",
|
|
128
124
|
inputs: {},
|
|
129
125
|
schema: {
|
|
130
|
-
inputs: {
|
|
131
|
-
|
|
132
|
-
},
|
|
133
|
-
outputs: {
|
|
134
|
-
properties: {},
|
|
135
|
-
},
|
|
126
|
+
inputs: {},
|
|
127
|
+
outputs: {},
|
|
136
128
|
},
|
|
137
129
|
},
|
|
138
130
|
steps: [],
|
|
139
131
|
},
|
|
140
132
|
type: "automation",
|
|
141
|
-
appId
|
|
133
|
+
appId,
|
|
142
134
|
}
|
|
143
135
|
}
|
|
144
136
|
|
|
@@ -162,7 +154,7 @@ export function loopAutomation(tableId: string, loopOpts?: any): Automation {
|
|
|
162
154
|
inputs: {
|
|
163
155
|
tableId,
|
|
164
156
|
},
|
|
165
|
-
schema:
|
|
157
|
+
schema: ACTION_DEFINITIONS.QUERY_ROWS.schema,
|
|
166
158
|
},
|
|
167
159
|
{
|
|
168
160
|
id: "c",
|
|
@@ -171,7 +163,7 @@ export function loopAutomation(tableId: string, loopOpts?: any): Automation {
|
|
|
171
163
|
internal: true,
|
|
172
164
|
inputs: loopOpts,
|
|
173
165
|
blockToLoop: "d",
|
|
174
|
-
schema:
|
|
166
|
+
schema: ACTION_DEFINITIONS.LOOP.schema,
|
|
175
167
|
},
|
|
176
168
|
{
|
|
177
169
|
id: "d",
|
|
@@ -181,7 +173,7 @@ export function loopAutomation(tableId: string, loopOpts?: any): Automation {
|
|
|
181
173
|
inputs: {
|
|
182
174
|
text: "log statement",
|
|
183
175
|
},
|
|
184
|
-
schema:
|
|
176
|
+
schema: ACTION_DEFINITIONS.SERVER_LOG.schema,
|
|
185
177
|
},
|
|
186
178
|
],
|
|
187
179
|
trigger: {
|