@budibase/server 2.5.9 → 2.5.10-alpha.1
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.24635afb.js +1794 -0
- package/builder/assets/index.4eae16b2.css +6 -0
- package/builder/index.html +2 -2
- package/dist/api/controllers/application.js +3 -4
- package/dist/api/controllers/automation.js +13 -7
- package/dist/api/controllers/datasource.js +1 -1
- package/dist/api/controllers/dev.js +1 -1
- package/dist/api/controllers/ops.js +40 -0
- package/dist/api/controllers/plugin/index.js +6 -37
- package/dist/api/controllers/query/index.js +2 -2
- package/dist/api/controllers/row/ExternalRequest.js +21 -14
- package/dist/api/controllers/row/internal.js +5 -2
- package/dist/api/controllers/row/utils.js +2 -2
- package/dist/api/controllers/table/index.js +2 -2
- package/dist/api/controllers/table/utils.js +9 -3
- package/dist/api/controllers/user.js +1 -83
- package/dist/api/controllers/view/exporters.js +3 -1
- package/dist/api/index.js +1 -2
- package/dist/api/routes/index.js +2 -2
- package/dist/api/routes/{cloud.js → ops.js} +19 -6
- package/dist/api/routes/user.js +0 -1
- package/dist/app.js +4 -13
- package/dist/automations/actions.js +32 -6
- package/dist/automations/index.js +3 -2
- 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/db/utils.js +3 -2
- package/dist/environment.js +0 -1
- package/dist/events/docUpdates/index.js +17 -0
- package/dist/events/docUpdates/processors.js +18 -0
- package/dist/events/docUpdates/syncUsers.js +49 -0
- package/dist/events/index.js +3 -0
- package/dist/integrations/base/sqlTable.js +9 -2
- package/dist/integrations/index.js +3 -3
- package/dist/integrations/microsoftSqlServer.js +5 -2
- package/dist/integrations/mysql.js +5 -3
- package/dist/integrations/postgres.js +7 -5
- package/dist/integrations/redis.js +7 -0
- package/dist/integrations/rest.js +4 -0
- package/dist/migrations/functions/syncQuotas.js +2 -0
- package/dist/migrations/functions/usageQuotas/syncApps.js +1 -2
- package/dist/migrations/functions/usageQuotas/syncRows.js +1 -2
- package/dist/migrations/functions/usageQuotas/syncUsers.js +21 -0
- package/dist/sdk/app/applications/sync.js +117 -23
- package/dist/sdk/app/backups/exports.js +14 -38
- package/dist/sdk/index.js +2 -0
- package/dist/sdk/plugins/index.js +27 -0
- package/dist/sdk/plugins/plugins.js +53 -0
- package/dist/sdk/users/utils.js +21 -4
- package/dist/startup.js +31 -28
- package/dist/threads/automation.js +16 -5
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/dist/utilities/csv.js +33 -0
- package/dist/utilities/fileSystem/plugin.js +33 -23
- package/dist/utilities/global.js +17 -12
- package/dist/utilities/rowProcessor/utils.js +4 -5
- package/dist/utilities/schema.js +5 -1
- package/dist/watch.js +2 -2
- package/dist/websockets/client.js +14 -0
- package/dist/websockets/grid.js +60 -0
- package/dist/websockets/index.js +17 -0
- package/dist/websockets/websocket.js +78 -0
- package/package.json +16 -16
- package/scripts/dev/manage.js +2 -0
- package/scripts/integrations/mssql/data/entrypoint.sh +1 -0
- package/scripts/integrations/mssql/data/setup.sql +17 -17
- package/scripts/integrations/mysql/init.sql +1 -1
- package/scripts/integrations/postgres/init.sql +1 -0
- package/src/api/controllers/application.ts +4 -4
- package/src/api/controllers/automation.ts +12 -6
- package/src/api/controllers/datasource.ts +15 -5
- package/src/api/controllers/dev.ts +2 -2
- package/src/api/controllers/ops.ts +32 -0
- package/src/api/controllers/plugin/index.ts +8 -45
- package/src/api/controllers/query/index.ts +2 -2
- package/src/api/controllers/row/ExternalRequest.ts +21 -12
- package/src/api/controllers/row/internal.ts +13 -11
- package/src/api/controllers/row/utils.ts +4 -4
- package/src/api/controllers/table/index.ts +2 -2
- package/src/api/controllers/table/utils.ts +10 -3
- package/src/api/controllers/user.ts +10 -96
- package/src/api/controllers/view/exporters.ts +3 -1
- package/src/api/index.ts +2 -4
- package/src/api/routes/index.ts +2 -2
- package/src/api/routes/ops.ts +30 -0
- package/src/api/routes/tests/automation.spec.js +7 -4
- package/src/api/routes/tests/user.spec.js +48 -37
- package/src/api/routes/user.ts +0 -5
- package/src/app.ts +4 -15
- package/src/automations/actions.ts +56 -24
- package/src/automations/index.ts +1 -1
- package/src/automations/steps/bash.ts +10 -7
- package/src/automations/steps/createRow.ts +15 -12
- package/src/automations/steps/delay.ts +6 -4
- package/src/automations/steps/deleteRow.ts +12 -9
- package/src/automations/steps/discord.ts +10 -8
- package/src/automations/steps/executeQuery.ts +13 -10
- package/src/automations/steps/executeScript.ts +10 -7
- package/src/automations/steps/filter.ts +8 -6
- package/src/automations/steps/integromat.ts +12 -10
- package/src/automations/steps/loop.ts +16 -10
- package/src/automations/steps/outgoingWebhook.ts +14 -11
- package/src/automations/steps/queryRows.ts +18 -15
- package/src/automations/steps/sendSmtpEmail.ts +11 -9
- package/src/automations/steps/serverLog.ts +6 -4
- package/src/automations/steps/slack.ts +8 -6
- package/src/automations/steps/updateRow.ts +15 -12
- package/src/automations/steps/zapier.ts +11 -9
- package/src/automations/tests/utilities/index.ts +2 -2
- package/src/automations/triggerInfo/app.ts +8 -5
- package/src/automations/triggerInfo/cron.ts +7 -4
- package/src/automations/triggerInfo/rowDeleted.ts +8 -5
- package/src/automations/triggerInfo/rowSaved.ts +10 -7
- package/src/automations/triggerInfo/rowUpdated.ts +10 -7
- package/src/automations/triggerInfo/webhook.ts +9 -6
- package/src/db/utils.ts +1 -0
- package/src/environment.ts +0 -1
- package/src/events/docUpdates/index.ts +1 -0
- package/src/events/docUpdates/processors.ts +14 -0
- package/src/events/docUpdates/syncUsers.ts +35 -0
- package/src/events/index.ts +1 -0
- package/src/integration-test/postgres.spec.ts +3 -1
- package/src/integrations/base/sqlTable.ts +9 -2
- package/src/integrations/index.ts +3 -3
- package/src/integrations/microsoftSqlServer.ts +5 -2
- package/src/integrations/mysql.ts +5 -3
- package/src/integrations/postgres.ts +7 -5
- package/src/integrations/redis.ts +8 -0
- package/src/integrations/rest.ts +3 -0
- package/src/migrations/functions/syncQuotas.ts +2 -0
- package/src/migrations/functions/usageQuotas/syncApps.ts +2 -3
- package/src/migrations/functions/usageQuotas/syncRows.ts +2 -3
- package/src/migrations/functions/usageQuotas/syncUsers.ts +9 -0
- package/src/migrations/functions/usageQuotas/tests/syncRows.spec.ts +2 -2
- package/src/migrations/functions/usageQuotas/tests/syncUsers.spec.ts +26 -0
- package/src/migrations/index.ts +1 -0
- package/src/sdk/app/applications/sync.ts +129 -22
- package/src/sdk/app/applications/tests/sync.spec.ts +137 -0
- package/src/sdk/app/backups/exports.ts +17 -41
- package/src/sdk/index.ts +2 -0
- package/src/sdk/plugins/index.ts +5 -0
- package/src/sdk/plugins/plugins.ts +41 -0
- package/src/sdk/users/tests/utils.spec.ts +1 -32
- package/src/sdk/users/utils.ts +23 -5
- package/src/startup.ts +36 -34
- package/src/tests/jestEnv.ts +0 -1
- package/src/tests/jestSetup.ts +0 -1
- package/src/tests/utilities/TestConfiguration.ts +28 -0
- package/src/tests/utilities/structures.ts +25 -17
- package/src/threads/automation.ts +18 -6
- package/src/utilities/csv.ts +22 -0
- package/src/utilities/fileSystem/plugin.ts +13 -4
- package/src/utilities/global.ts +21 -16
- package/src/utilities/rowProcessor/utils.ts +9 -10
- package/src/utilities/schema.ts +8 -0
- package/src/utilities/tests/csv.spec.ts +33 -0
- package/src/watch.ts +2 -2
- package/src/websockets/client.ts +11 -0
- package/src/websockets/grid.ts +55 -0
- package/src/websockets/index.ts +14 -0
- package/src/websockets/websocket.ts +83 -0
- package/tsconfig.build.json +3 -5
- package/tsconfig.json +2 -1
- package/builder/assets/index.0b358332.js +0 -1817
- package/builder/assets/index.7f9a008b.css +0 -6
- package/dist/api/controllers/cloud.js +0 -130
- package/dist/elasticApm.js +0 -14
- package/dist/package.json +0 -180
- package/dist/websocket.js +0 -22
- package/scripts/likeCypress.ts +0 -35
- package/src/api/controllers/cloud.ts +0 -119
- package/src/api/routes/cloud.ts +0 -18
- package/src/api/routes/tests/cloud.spec.ts +0 -54
- package/src/elasticApm.ts +0 -10
- package/src/migrations/functions/tests/syncQuotas.spec.js +0 -26
- package/src/tests/logging.ts +0 -34
- package/src/websocket.ts +0 -26
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { FileType, Plugin, PluginSource, PluginType } from "@budibase/types"
|
|
2
|
+
import {
|
|
3
|
+
db as dbCore,
|
|
4
|
+
objectStore,
|
|
5
|
+
plugins as pluginCore,
|
|
6
|
+
tenancy,
|
|
7
|
+
} from "@budibase/backend-core"
|
|
8
|
+
import { fileUpload } from "../../api/controllers/plugin/file"
|
|
9
|
+
import env from "../../environment"
|
|
10
|
+
import { clientAppSocket } from "../../websockets"
|
|
11
|
+
import { sdk as pro } from "@budibase/pro"
|
|
12
|
+
|
|
13
|
+
export async function fetch(type?: PluginType) {
|
|
14
|
+
const db = tenancy.getGlobalDB()
|
|
15
|
+
const response = await db.allDocs(
|
|
16
|
+
dbCore.getPluginParams(null, {
|
|
17
|
+
include_docs: true,
|
|
18
|
+
})
|
|
19
|
+
)
|
|
20
|
+
let plugins = response.rows.map((row: any) => row.doc) as Plugin[]
|
|
21
|
+
plugins = objectStore.enrichPluginURLs(plugins)
|
|
22
|
+
if (type) {
|
|
23
|
+
return plugins.filter((plugin: Plugin) => plugin.schema?.type === type)
|
|
24
|
+
} else {
|
|
25
|
+
return plugins
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export async function processUploaded(plugin: FileType, source?: PluginSource) {
|
|
30
|
+
const { metadata, directory } = await fileUpload(plugin)
|
|
31
|
+
pluginCore.validate(metadata?.schema)
|
|
32
|
+
|
|
33
|
+
// Only allow components in cloud
|
|
34
|
+
if (!env.SELF_HOSTED && metadata?.schema?.type !== PluginType.COMPONENT) {
|
|
35
|
+
throw new Error("Only component plugins are supported outside of self-host")
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const doc = await pro.plugins.storePlugin(metadata, directory, source)
|
|
39
|
+
clientAppSocket.emit("plugin-update", { name: doc.name, hash: doc.hash })
|
|
40
|
+
return doc
|
|
41
|
+
}
|
|
@@ -121,38 +121,7 @@ describe("syncGlobalUsers", () => {
|
|
|
121
121
|
await syncGlobalUsers()
|
|
122
122
|
|
|
123
123
|
const metadata = await rawUserMetadata()
|
|
124
|
-
expect(metadata).toHaveLength(
|
|
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
|
-
)
|
|
124
|
+
expect(metadata).toHaveLength(0)
|
|
156
125
|
})
|
|
157
126
|
})
|
|
158
127
|
})
|
package/src/sdk/users/utils.ts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { getGlobalUsers } from "../../utilities/global"
|
|
2
2
|
import { context, roles as rolesCore } from "@budibase/backend-core"
|
|
3
3
|
import {
|
|
4
|
+
getGlobalIDFromUserMetadataID,
|
|
4
5
|
generateUserMetadataID,
|
|
5
6
|
getUserMetadataParams,
|
|
6
7
|
InternalTables,
|
|
7
8
|
} from "../../db/utils"
|
|
8
9
|
import { isEqual } from "lodash"
|
|
9
|
-
import { ContextUser, UserMetadata } from "@budibase/types"
|
|
10
|
+
import { ContextUser, UserMetadata, User } from "@budibase/types"
|
|
10
11
|
|
|
11
12
|
export function combineMetadataAndUser(
|
|
12
13
|
user: ContextUser,
|
|
@@ -37,6 +38,10 @@ export function combineMetadataAndUser(
|
|
|
37
38
|
if (found) {
|
|
38
39
|
newDoc._rev = found._rev
|
|
39
40
|
}
|
|
41
|
+
// clear fields that shouldn't be in metadata
|
|
42
|
+
delete newDoc.password
|
|
43
|
+
delete newDoc.forceResetPassword
|
|
44
|
+
delete newDoc.roles
|
|
40
45
|
if (found == null || !isEqual(newDoc, found)) {
|
|
41
46
|
return {
|
|
42
47
|
...found,
|
|
@@ -60,10 +65,9 @@ export async function rawUserMetadata() {
|
|
|
60
65
|
export async function syncGlobalUsers() {
|
|
61
66
|
// sync user metadata
|
|
62
67
|
const db = context.getAppDB()
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
])
|
|
68
|
+
const resp = await Promise.all([getGlobalUsers(), rawUserMetadata()])
|
|
69
|
+
const users = resp[0] as User[]
|
|
70
|
+
const metadata = resp[1] as UserMetadata[]
|
|
67
71
|
const toWrite = []
|
|
68
72
|
for (let user of users) {
|
|
69
73
|
const combined = combineMetadataAndUser(user, metadata)
|
|
@@ -71,5 +75,19 @@ export async function syncGlobalUsers() {
|
|
|
71
75
|
toWrite.push(combined)
|
|
72
76
|
}
|
|
73
77
|
}
|
|
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
|
+
}
|
|
74
92
|
await db.bulkDocs(toWrite)
|
|
75
93
|
}
|
package/src/startup.ts
CHANGED
|
@@ -10,19 +10,16 @@ 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 eventEmitter from "./events"
|
|
13
|
+
import { default as eventEmitter, init as eventInit } 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")
|
|
20
19
|
|
|
21
20
|
let STARTUP_RAN = false
|
|
22
21
|
|
|
23
22
|
async function initRoutes(app: any) {
|
|
24
|
-
app.use(pino(logging.pinoSettings()))
|
|
25
|
-
|
|
26
23
|
if (!env.isTest()) {
|
|
27
24
|
const plugin = await bullboard.init()
|
|
28
25
|
app.use(plugin)
|
|
@@ -48,8 +45,10 @@ async function initPro() {
|
|
|
48
45
|
}
|
|
49
46
|
|
|
50
47
|
function shutdown(server?: any) {
|
|
51
|
-
server
|
|
52
|
-
|
|
48
|
+
if (server) {
|
|
49
|
+
server.close()
|
|
50
|
+
server.destroy()
|
|
51
|
+
}
|
|
53
52
|
}
|
|
54
53
|
|
|
55
54
|
export async function startup(app?: any, server?: any) {
|
|
@@ -64,6 +63,7 @@ export async function startup(app?: any, server?: any) {
|
|
|
64
63
|
eventEmitter.emitPort(env.PORT)
|
|
65
64
|
fileSystem.init()
|
|
66
65
|
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,11 +72,39 @@ 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(server)
|
|
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
|
+
|
|
79
105
|
// 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
|
|
80
108
|
if (
|
|
81
109
|
env.SELF_HOSTED &&
|
|
82
110
|
!env.MULTI_TENANCY &&
|
|
@@ -103,34 +131,8 @@ export async function startup(app?: any, server?: any) {
|
|
|
103
131
|
)
|
|
104
132
|
} catch (e) {
|
|
105
133
|
logging.logAlert("Error creating initial admin user. Exiting.", e)
|
|
106
|
-
shutdown()
|
|
134
|
+
shutdown(server)
|
|
107
135
|
}
|
|
108
136
|
}
|
|
109
137
|
}
|
|
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
|
-
}
|
|
136
138
|
}
|
package/src/tests/jestEnv.ts
CHANGED
|
@@ -6,7 +6,6 @@ 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"
|
|
10
9
|
process.env.MOCK_REDIS = "1"
|
|
11
10
|
process.env.PLATFORM_URL = "http://localhost:10000"
|
|
12
11
|
process.env.REDIS_PASSWORD = "budibase"
|
package/src/tests/jestSetup.ts
CHANGED
|
@@ -49,6 +49,7 @@ import {
|
|
|
49
49
|
SearchFilters,
|
|
50
50
|
UserRoles,
|
|
51
51
|
} from "@budibase/types"
|
|
52
|
+
import { BUILTIN_ROLE_IDS } from "@budibase/backend-core/src/security/roles"
|
|
52
53
|
|
|
53
54
|
type DefaultUserValues = {
|
|
54
55
|
globalUserId: string
|
|
@@ -306,6 +307,33 @@ class TestConfiguration {
|
|
|
306
307
|
}
|
|
307
308
|
}
|
|
308
309
|
|
|
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
|
+
|
|
309
337
|
async login({ roleId, userId, builder, prodApp = false }: any = {}) {
|
|
310
338
|
const appId = prodApp ? this.prodAppId : this.appId
|
|
311
339
|
return context.doInAppContext(appId, async () => {
|
|
@@ -1,18 +1,22 @@
|
|
|
1
|
-
import { permissions, roles } from "@budibase/backend-core"
|
|
1
|
+
import { permissions, roles, utils } 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 {
|
|
5
|
+
import {
|
|
6
|
+
BUILTIN_ACTION_DEFINITIONS,
|
|
7
|
+
TRIGGER_DEFINITIONS,
|
|
8
|
+
} from "../../automations"
|
|
6
9
|
import {
|
|
7
10
|
Automation,
|
|
8
11
|
AutomationActionStepId,
|
|
12
|
+
AutomationStep,
|
|
13
|
+
AutomationStepType,
|
|
14
|
+
AutomationTrigger,
|
|
9
15
|
AutomationTriggerStepId,
|
|
10
16
|
Datasource,
|
|
11
17
|
SourceName,
|
|
12
18
|
} from "@budibase/types"
|
|
13
19
|
|
|
14
|
-
const { v4: uuidv4 } = require("uuid")
|
|
15
|
-
|
|
16
20
|
export function basicTable() {
|
|
17
21
|
return {
|
|
18
22
|
name: "TestTable",
|
|
@@ -71,19 +75,19 @@ export function view(tableId: string) {
|
|
|
71
75
|
}
|
|
72
76
|
|
|
73
77
|
export function automationStep(
|
|
74
|
-
actionDefinition =
|
|
75
|
-
) {
|
|
78
|
+
actionDefinition = BUILTIN_ACTION_DEFINITIONS.CREATE_ROW
|
|
79
|
+
): AutomationStep {
|
|
76
80
|
return {
|
|
77
|
-
id:
|
|
81
|
+
id: utils.newid(),
|
|
78
82
|
...actionDefinition,
|
|
79
83
|
}
|
|
80
84
|
}
|
|
81
85
|
|
|
82
86
|
export function automationTrigger(
|
|
83
87
|
triggerDefinition = TRIGGER_DEFINITIONS.ROW_SAVED
|
|
84
|
-
) {
|
|
88
|
+
): AutomationTrigger {
|
|
85
89
|
return {
|
|
86
|
-
id:
|
|
90
|
+
id: utils.newid(),
|
|
87
91
|
...triggerDefinition,
|
|
88
92
|
}
|
|
89
93
|
}
|
|
@@ -106,7 +110,7 @@ export function newAutomation({ steps, trigger }: any = {}) {
|
|
|
106
110
|
return automation
|
|
107
111
|
}
|
|
108
112
|
|
|
109
|
-
export function basicAutomation(appId?: string) {
|
|
113
|
+
export function basicAutomation(appId?: string): Automation {
|
|
110
114
|
return {
|
|
111
115
|
name: "My Automation",
|
|
112
116
|
screenId: "kasdkfldsafkl",
|
|
@@ -119,18 +123,22 @@ export function basicAutomation(appId?: string) {
|
|
|
119
123
|
tagline: "test",
|
|
120
124
|
icon: "test",
|
|
121
125
|
description: "test",
|
|
122
|
-
type:
|
|
126
|
+
type: AutomationStepType.TRIGGER,
|
|
123
127
|
id: "test",
|
|
124
128
|
inputs: {},
|
|
125
129
|
schema: {
|
|
126
|
-
inputs: {
|
|
127
|
-
|
|
130
|
+
inputs: {
|
|
131
|
+
properties: {},
|
|
132
|
+
},
|
|
133
|
+
outputs: {
|
|
134
|
+
properties: {},
|
|
135
|
+
},
|
|
128
136
|
},
|
|
129
137
|
},
|
|
130
138
|
steps: [],
|
|
131
139
|
},
|
|
132
140
|
type: "automation",
|
|
133
|
-
appId
|
|
141
|
+
appId: appId!,
|
|
134
142
|
}
|
|
135
143
|
}
|
|
136
144
|
|
|
@@ -154,7 +162,7 @@ export function loopAutomation(tableId: string, loopOpts?: any): Automation {
|
|
|
154
162
|
inputs: {
|
|
155
163
|
tableId,
|
|
156
164
|
},
|
|
157
|
-
schema:
|
|
165
|
+
schema: BUILTIN_ACTION_DEFINITIONS.QUERY_ROWS.schema,
|
|
158
166
|
},
|
|
159
167
|
{
|
|
160
168
|
id: "c",
|
|
@@ -163,7 +171,7 @@ export function loopAutomation(tableId: string, loopOpts?: any): Automation {
|
|
|
163
171
|
internal: true,
|
|
164
172
|
inputs: loopOpts,
|
|
165
173
|
blockToLoop: "d",
|
|
166
|
-
schema:
|
|
174
|
+
schema: BUILTIN_ACTION_DEFINITIONS.LOOP.schema,
|
|
167
175
|
},
|
|
168
176
|
{
|
|
169
177
|
id: "d",
|
|
@@ -173,7 +181,7 @@ export function loopAutomation(tableId: string, loopOpts?: any): Automation {
|
|
|
173
181
|
inputs: {
|
|
174
182
|
text: "log statement",
|
|
175
183
|
},
|
|
176
|
-
schema:
|
|
184
|
+
schema: BUILTIN_ACTION_DEFINITIONS.SERVER_LOG.schema,
|
|
177
185
|
},
|
|
178
186
|
],
|
|
179
187
|
trigger: {
|
|
@@ -27,15 +27,15 @@ import { processObject } from "@budibase/string-templates"
|
|
|
27
27
|
import { cloneDeep } from "lodash/fp"
|
|
28
28
|
import * as sdkUtils from "../sdk/utils"
|
|
29
29
|
import env from "../environment"
|
|
30
|
-
const FILTER_STEP_ID = actions.
|
|
31
|
-
const LOOP_STEP_ID = actions.
|
|
30
|
+
const FILTER_STEP_ID = actions.BUILTIN_ACTION_DEFINITIONS.FILTER.stepId
|
|
31
|
+
const LOOP_STEP_ID = actions.BUILTIN_ACTION_DEFINITIONS.LOOP.stepId
|
|
32
32
|
const CRON_STEP_ID = triggerDefs.CRON.stepId
|
|
33
33
|
const STOPPED_STATUS = { success: true, status: AutomationStatus.STOPPED }
|
|
34
34
|
|
|
35
35
|
function getLoopIterations(loopStep: LoopStep, input: LoopInput) {
|
|
36
36
|
const binding = automationUtils.typecastForLooping(loopStep, input)
|
|
37
|
-
if (!
|
|
38
|
-
return
|
|
37
|
+
if (!binding) {
|
|
38
|
+
return 0
|
|
39
39
|
}
|
|
40
40
|
if (Array.isArray(binding)) {
|
|
41
41
|
return binding.length
|
|
@@ -43,7 +43,7 @@ function getLoopIterations(loopStep: LoopStep, input: LoopInput) {
|
|
|
43
43
|
if (typeof binding === "string") {
|
|
44
44
|
return automationUtils.stringSplit(binding).length
|
|
45
45
|
}
|
|
46
|
-
return
|
|
46
|
+
return 0
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
/**
|
|
@@ -423,13 +423,25 @@ class Orchestrator {
|
|
|
423
423
|
}
|
|
424
424
|
}
|
|
425
425
|
|
|
426
|
+
if (loopStep && iterations === 0) {
|
|
427
|
+
loopStep = undefined
|
|
428
|
+
this.executionOutput.steps.splice(loopStepNumber + 1, 0, {
|
|
429
|
+
id: step.id,
|
|
430
|
+
stepId: step.stepId,
|
|
431
|
+
outputs: { status: AutomationStatus.NO_ITERATIONS, success: true },
|
|
432
|
+
inputs: {},
|
|
433
|
+
})
|
|
434
|
+
|
|
435
|
+
this._context.steps.splice(loopStepNumber, 1)
|
|
436
|
+
iterations = 1
|
|
437
|
+
}
|
|
438
|
+
|
|
426
439
|
// Delete the step after the loop step as it's irrelevant, since information is included
|
|
427
440
|
// in the loop step
|
|
428
441
|
if (wasLoopStep && !loopStep) {
|
|
429
442
|
this._context.steps.splice(loopStepNumber + 1, 1)
|
|
430
443
|
wasLoopStep = false
|
|
431
444
|
}
|
|
432
|
-
|
|
433
445
|
if (loopSteps && loopSteps.length) {
|
|
434
446
|
let tempOutput = {
|
|
435
447
|
success: true,
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import csv from "csvtojson"
|
|
2
|
+
|
|
3
|
+
export async function jsonFromCsvString(csvString: string) {
|
|
4
|
+
const castedWithEmptyValues = await csv({ ignoreEmpty: true }).fromString(
|
|
5
|
+
csvString
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
// By default the csvtojson library casts empty values as empty strings. This is causing issues on conversion.
|
|
9
|
+
// ignoreEmpty will remove the key completly if empty, so creating this empty object will ensure we return the values with the keys but empty values
|
|
10
|
+
const result = await csv({ ignoreEmpty: false }).fromString(csvString)
|
|
11
|
+
result.forEach((r, i) => {
|
|
12
|
+
for (const [key] of Object.entries(r).filter(
|
|
13
|
+
([key, value]) => value === ""
|
|
14
|
+
)) {
|
|
15
|
+
if (castedWithEmptyValues[i][key] === undefined) {
|
|
16
|
+
r[key] = null
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
return result
|
|
22
|
+
}
|
|
@@ -5,6 +5,7 @@ import { join } from "path"
|
|
|
5
5
|
import { objectStore } from "@budibase/backend-core"
|
|
6
6
|
|
|
7
7
|
const DATASOURCE_PATH = join(budibaseTempDir(), "datasource")
|
|
8
|
+
const AUTOMATION_PATH = join(budibaseTempDir(), "automation")
|
|
8
9
|
|
|
9
10
|
export const getPluginMetadata = async (path: string) => {
|
|
10
11
|
let metadata: any = {}
|
|
@@ -33,12 +34,12 @@ export const getPluginMetadata = async (path: string) => {
|
|
|
33
34
|
return { metadata, directory: path }
|
|
34
35
|
}
|
|
35
36
|
|
|
36
|
-
|
|
37
|
+
async function getPluginImpl(path: string, plugin: Plugin) {
|
|
37
38
|
const hash = plugin.schema?.hash
|
|
38
|
-
if (!fs.existsSync(
|
|
39
|
-
fs.mkdirSync(
|
|
39
|
+
if (!fs.existsSync(path)) {
|
|
40
|
+
fs.mkdirSync(path)
|
|
40
41
|
}
|
|
41
|
-
const filename = join(
|
|
42
|
+
const filename = join(path, plugin.name)
|
|
42
43
|
const metadataName = `${filename}.bbmetadata`
|
|
43
44
|
if (fs.existsSync(filename)) {
|
|
44
45
|
const currentHash = fs.readFileSync(metadataName, "utf8")
|
|
@@ -62,3 +63,11 @@ export const getDatasourcePlugin = async (plugin: Plugin) => {
|
|
|
62
63
|
|
|
63
64
|
return require(filename)
|
|
64
65
|
}
|
|
66
|
+
|
|
67
|
+
export const getDatasourcePlugin = async (plugin: Plugin) => {
|
|
68
|
+
return getPluginImpl(DATASOURCE_PATH, plugin)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export const getAutomationPlugin = async (plugin: Plugin) => {
|
|
72
|
+
return getPluginImpl(AUTOMATION_PATH, plugin)
|
|
73
|
+
}
|
package/src/utilities/global.ts
CHANGED
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
import env from "../environment"
|
|
10
10
|
import { groups } from "@budibase/pro"
|
|
11
11
|
import { UserCtx, ContextUser, User, UserGroup } from "@budibase/types"
|
|
12
|
+
import { global } from "yargs"
|
|
12
13
|
|
|
13
14
|
export function updateAppRole(
|
|
14
15
|
user: ContextUser,
|
|
@@ -16,7 +17,7 @@ export function updateAppRole(
|
|
|
16
17
|
) {
|
|
17
18
|
appId = appId || context.getAppId()
|
|
18
19
|
|
|
19
|
-
if (!user || !user.roles) {
|
|
20
|
+
if (!user || (!user.roles && !user.userGroups)) {
|
|
20
21
|
return user
|
|
21
22
|
}
|
|
22
23
|
// if in an multi-tenancy environment make sure roles are never updated
|
|
@@ -27,7 +28,7 @@ export function updateAppRole(
|
|
|
27
28
|
return user
|
|
28
29
|
}
|
|
29
30
|
// always use the deployed app
|
|
30
|
-
if (appId) {
|
|
31
|
+
if (appId && user.roles) {
|
|
31
32
|
user.roleId = user.roles[dbCore.getProdAppID(appId)]
|
|
32
33
|
}
|
|
33
34
|
// if a role wasn't found then either set as admin (builder) or public (everyone else)
|
|
@@ -60,7 +61,7 @@ async function checkGroupRoles(
|
|
|
60
61
|
return user
|
|
61
62
|
}
|
|
62
63
|
|
|
63
|
-
async function processUser(
|
|
64
|
+
export async function processUser(
|
|
64
65
|
user: ContextUser,
|
|
65
66
|
opts: { appId?: string; groups?: UserGroup[] } = {}
|
|
66
67
|
) {
|
|
@@ -94,16 +95,15 @@ export async function getGlobalUser(userId: string) {
|
|
|
94
95
|
return processUser(user, { appId })
|
|
95
96
|
}
|
|
96
97
|
|
|
97
|
-
export async function getGlobalUsers(
|
|
98
|
+
export async function getGlobalUsers(
|
|
99
|
+
userIds?: string[],
|
|
100
|
+
opts?: { noProcessing?: boolean }
|
|
101
|
+
) {
|
|
98
102
|
const appId = context.getAppId()
|
|
99
103
|
const db = tenancy.getGlobalDB()
|
|
100
|
-
const allGroups = await groups.fetch()
|
|
101
104
|
let globalUsers
|
|
102
|
-
if (
|
|
103
|
-
|
|
104
|
-
getGlobalIDFromUserMetadataID(user._id!)
|
|
105
|
-
)
|
|
106
|
-
globalUsers = (await db.allDocs(getMultiIDParams(globalIds))).rows.map(
|
|
105
|
+
if (userIds) {
|
|
106
|
+
globalUsers = (await db.allDocs(getMultiIDParams(userIds))).rows.map(
|
|
107
107
|
row => row.doc
|
|
108
108
|
)
|
|
109
109
|
} else {
|
|
@@ -126,15 +126,20 @@ export async function getGlobalUsers(users?: ContextUser[]) {
|
|
|
126
126
|
return globalUsers
|
|
127
127
|
}
|
|
128
128
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
129
|
+
if (opts?.noProcessing) {
|
|
130
|
+
return globalUsers
|
|
131
|
+
} else {
|
|
132
|
+
// pass in the groups, meaning we don't actually need to retrieve them for
|
|
133
|
+
// each user individually
|
|
134
|
+
const allGroups = await groups.fetch()
|
|
135
|
+
return Promise.all(
|
|
136
|
+
globalUsers.map(user => processUser(user, { groups: allGroups }))
|
|
137
|
+
)
|
|
138
|
+
}
|
|
134
139
|
}
|
|
135
140
|
|
|
136
141
|
export async function getGlobalUsersFromMetadata(users: ContextUser[]) {
|
|
137
|
-
const globalUsers = await getGlobalUsers(users)
|
|
142
|
+
const globalUsers = await getGlobalUsers(users.map(user => user._id!))
|
|
138
143
|
return users.map(user => {
|
|
139
144
|
const globalUser = globalUsers.find(
|
|
140
145
|
globalUser => globalUser && user._id?.includes(globalUser._id)
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
|
-
FieldTypes,
|
|
3
|
-
FormulaTypes,
|
|
4
2
|
AutoFieldDefaultNames,
|
|
5
3
|
AutoFieldSubTypes,
|
|
4
|
+
FieldTypes,
|
|
5
|
+
FormulaTypes,
|
|
6
6
|
} from "../../constants"
|
|
7
7
|
import { processStringSync } from "@budibase/string-templates"
|
|
8
|
-
import { FieldSchema,
|
|
8
|
+
import { FieldSchema, FieldType, Row, Table } from "@budibase/types"
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* If the subtype has been lost for any reason this works out what
|
|
@@ -50,6 +50,7 @@ export function processFormulas(
|
|
|
50
50
|
const isStatic = schema.formulaType === FormulaTypes.STATIC
|
|
51
51
|
if (
|
|
52
52
|
schema.type !== FieldTypes.FORMULA ||
|
|
53
|
+
schema.formula == null ||
|
|
53
54
|
(dynamic && isStatic) ||
|
|
54
55
|
(!dynamic && !isStatic)
|
|
55
56
|
) {
|
|
@@ -57,13 +58,11 @@ export function processFormulas(
|
|
|
57
58
|
}
|
|
58
59
|
// iterate through rows and process formula
|
|
59
60
|
for (let i = 0; i < rowArray.length; i++) {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
[column]: processStringSync(schema.formula, context),
|
|
66
|
-
}
|
|
61
|
+
let row = rowArray[i]
|
|
62
|
+
let context = contextRows ? contextRows[i] : row
|
|
63
|
+
rowArray[i] = {
|
|
64
|
+
...row,
|
|
65
|
+
[column]: processStringSync(schema.formula, context),
|
|
67
66
|
}
|
|
68
67
|
}
|
|
69
68
|
}
|