@budibase/server 2.5.6-alpha.9 → 2.5.7
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.7f9a008b.css +6 -0
- package/builder/assets/index.f02eef32.js +1817 -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
|
@@ -30,6 +30,7 @@ import { finaliseRow, updateRelatedFormula } from "./staticFormula"
|
|
|
30
30
|
import { csv, json, jsonWithSchema, Format } from "../view/exporters"
|
|
31
31
|
import { apiFileReturn } from "../../../utilities/fileSystem"
|
|
32
32
|
import {
|
|
33
|
+
Ctx,
|
|
33
34
|
UserCtx,
|
|
34
35
|
Database,
|
|
35
36
|
LinkDocumentValue,
|
|
@@ -71,7 +72,7 @@ async function getView(db: Database, viewName: string) {
|
|
|
71
72
|
return viewInfo
|
|
72
73
|
}
|
|
73
74
|
|
|
74
|
-
async function getRawTableData(ctx:
|
|
75
|
+
async function getRawTableData(ctx: Ctx, db: Database, tableId: string) {
|
|
75
76
|
let rows
|
|
76
77
|
if (tableId === InternalTables.USER_METADATA) {
|
|
77
78
|
await userController.fetchMetadata(ctx)
|
|
@@ -187,7 +188,7 @@ export async function save(ctx: UserCtx) {
|
|
|
187
188
|
})
|
|
188
189
|
}
|
|
189
190
|
|
|
190
|
-
export async function fetchView(ctx:
|
|
191
|
+
export async function fetchView(ctx: Ctx) {
|
|
191
192
|
const viewName = decodeURIComponent(ctx.params.viewName)
|
|
192
193
|
|
|
193
194
|
// if this is a table view being looked for just transfer to that
|
|
@@ -254,7 +255,7 @@ export async function fetchView(ctx: UserCtx) {
|
|
|
254
255
|
return rows
|
|
255
256
|
}
|
|
256
257
|
|
|
257
|
-
export async function fetch(ctx:
|
|
258
|
+
export async function fetch(ctx: Ctx) {
|
|
258
259
|
const db = context.getAppDB()
|
|
259
260
|
|
|
260
261
|
const tableId = ctx.params.tableId
|
|
@@ -263,7 +264,7 @@ export async function fetch(ctx: UserCtx) {
|
|
|
263
264
|
return outputProcessing(table, rows)
|
|
264
265
|
}
|
|
265
266
|
|
|
266
|
-
export async function find(ctx:
|
|
267
|
+
export async function find(ctx: Ctx) {
|
|
267
268
|
const db = dbCore.getDB(ctx.appId)
|
|
268
269
|
const table = await db.get(ctx.params.tableId)
|
|
269
270
|
let row = await utils.findRow(ctx, ctx.params.tableId, ctx.params.rowId)
|
|
@@ -271,7 +272,7 @@ export async function find(ctx: UserCtx) {
|
|
|
271
272
|
return row
|
|
272
273
|
}
|
|
273
274
|
|
|
274
|
-
export async function destroy(ctx:
|
|
275
|
+
export async function destroy(ctx: Ctx) {
|
|
275
276
|
const db = context.getAppDB()
|
|
276
277
|
const { _id } = ctx.request.body
|
|
277
278
|
let row = await db.get(_id)
|
|
@@ -307,7 +308,7 @@ export async function destroy(ctx: UserCtx) {
|
|
|
307
308
|
return { response, row }
|
|
308
309
|
}
|
|
309
310
|
|
|
310
|
-
export async function bulkDestroy(ctx:
|
|
311
|
+
export async function bulkDestroy(ctx: Ctx) {
|
|
311
312
|
const db = context.getAppDB()
|
|
312
313
|
const tableId = ctx.params.tableId
|
|
313
314
|
const table = await db.get(tableId)
|
|
@@ -346,7 +347,7 @@ export async function bulkDestroy(ctx: UserCtx) {
|
|
|
346
347
|
return { response: { ok: true }, rows: processedRows }
|
|
347
348
|
}
|
|
348
349
|
|
|
349
|
-
export async function search(ctx:
|
|
350
|
+
export async function search(ctx: Ctx) {
|
|
350
351
|
// Fetch the whole table when running in cypress, as search doesn't work
|
|
351
352
|
if (!env.COUCH_DB_URL && env.isCypress()) {
|
|
352
353
|
return { rows: await fetch(ctx) }
|
|
@@ -386,7 +387,7 @@ export async function search(ctx: UserCtx) {
|
|
|
386
387
|
return response
|
|
387
388
|
}
|
|
388
389
|
|
|
389
|
-
export async function exportRows(ctx:
|
|
390
|
+
export async function exportRows(ctx: Ctx) {
|
|
390
391
|
const db = context.getAppDB()
|
|
391
392
|
const table = await db.get(ctx.params.tableId)
|
|
392
393
|
const rowIds = ctx.request.body.rows
|
|
@@ -438,7 +439,7 @@ export async function exportRows(ctx: UserCtx) {
|
|
|
438
439
|
}
|
|
439
440
|
}
|
|
440
441
|
|
|
441
|
-
export async function fetchEnrichedRow(ctx:
|
|
442
|
+
export async function fetchEnrichedRow(ctx: Ctx) {
|
|
442
443
|
const db = context.getAppDB()
|
|
443
444
|
const tableId = ctx.params.tableId
|
|
444
445
|
const rowId = ctx.params.rowId
|
|
@@ -5,7 +5,7 @@ import { context } from "@budibase/backend-core"
|
|
|
5
5
|
import { makeExternalQuery } from "../../../integrations/base/query"
|
|
6
6
|
import { Row, Table } from "@budibase/types"
|
|
7
7
|
import { Format } from "../view/exporters"
|
|
8
|
-
import {
|
|
8
|
+
import { Ctx } from "@budibase/types"
|
|
9
9
|
import sdk from "../../../sdk"
|
|
10
10
|
const validateJs = require("validate.js")
|
|
11
11
|
const { cloneDeep } = require("lodash/fp")
|
|
@@ -26,7 +26,7 @@ export async function getDatasourceAndQuery(json: any) {
|
|
|
26
26
|
return makeExternalQuery(datasource, json)
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
export async function findRow(ctx:
|
|
29
|
+
export async function findRow(ctx: Ctx, tableId: string, rowId: string) {
|
|
30
30
|
const db = context.getAppDB()
|
|
31
31
|
let row
|
|
32
32
|
// TODO remove special user case in future
|
|
@@ -124,8 +124,7 @@ export function importToRows(
|
|
|
124
124
|
for (const [fieldName, schema] of Object.entries(table.schema)) {
|
|
125
125
|
// check whether the options need to be updated for inclusion as part of the data import
|
|
126
126
|
if (
|
|
127
|
-
|
|
128
|
-
schema.type === FieldTypes.ARRAY) &&
|
|
127
|
+
schema.type === FieldTypes.OPTIONS &&
|
|
129
128
|
row[fieldName] &&
|
|
130
129
|
(!schema.constraints!.inclusion ||
|
|
131
130
|
schema.constraints!.inclusion.indexOf(row[fieldName]) === -1)
|
|
@@ -1,12 +1,98 @@
|
|
|
1
1
|
import { generateUserMetadataID, generateUserFlagID } from "../../db/utils"
|
|
2
2
|
import { InternalTables } from "../../db/utils"
|
|
3
|
-
import { getGlobalUsers } from "../../utilities/global"
|
|
3
|
+
import { getGlobalUsers, getRawGlobalUser } from "../../utilities/global"
|
|
4
4
|
import { getFullUser } from "../../utilities/users"
|
|
5
|
-
import {
|
|
6
|
-
|
|
5
|
+
import {
|
|
6
|
+
context,
|
|
7
|
+
roles as rolesCore,
|
|
8
|
+
db as dbCore,
|
|
9
|
+
} from "@budibase/backend-core"
|
|
10
|
+
import { BBContext, Ctx, SyncUserRequest, User } from "@budibase/types"
|
|
7
11
|
import sdk from "../../sdk"
|
|
8
12
|
|
|
9
|
-
export async function
|
|
13
|
+
export async function syncUser(ctx: Ctx<SyncUserRequest>) {
|
|
14
|
+
let deleting = false,
|
|
15
|
+
user: User | any
|
|
16
|
+
const userId = ctx.params.id
|
|
17
|
+
|
|
18
|
+
const previousUser = ctx.request.body?.previousUser
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
user = (await getRawGlobalUser(userId)) as User
|
|
22
|
+
} catch (err: any) {
|
|
23
|
+
if (err && err.status === 404) {
|
|
24
|
+
user = {}
|
|
25
|
+
deleting = true
|
|
26
|
+
} else {
|
|
27
|
+
throw err
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
let previousApps = previousUser
|
|
32
|
+
? Object.keys(previousUser.roles).map(appId => appId)
|
|
33
|
+
: []
|
|
34
|
+
|
|
35
|
+
const roles = deleting ? {} : user.roles
|
|
36
|
+
// remove props which aren't useful to metadata
|
|
37
|
+
delete user.password
|
|
38
|
+
delete user.forceResetPassword
|
|
39
|
+
delete user.roles
|
|
40
|
+
// run through all production appIDs in the users roles
|
|
41
|
+
let prodAppIds
|
|
42
|
+
// if they are a builder then get all production app IDs
|
|
43
|
+
if ((user.builder && user.builder.global) || deleting) {
|
|
44
|
+
prodAppIds = await dbCore.getProdAppIDs()
|
|
45
|
+
} else {
|
|
46
|
+
prodAppIds = Object.entries(roles)
|
|
47
|
+
.filter(entry => entry[1] !== rolesCore.BUILTIN_ROLE_IDS.PUBLIC)
|
|
48
|
+
.map(([appId]) => appId)
|
|
49
|
+
}
|
|
50
|
+
for (let prodAppId of new Set([...prodAppIds, ...previousApps])) {
|
|
51
|
+
const roleId = roles[prodAppId]
|
|
52
|
+
const deleteFromApp = !roleId
|
|
53
|
+
const devAppId = dbCore.getDevelopmentAppID(prodAppId)
|
|
54
|
+
for (let appId of [prodAppId, devAppId]) {
|
|
55
|
+
if (!(await dbCore.dbExists(appId))) {
|
|
56
|
+
continue
|
|
57
|
+
}
|
|
58
|
+
await context.doInAppContext(appId, async () => {
|
|
59
|
+
const db = context.getAppDB()
|
|
60
|
+
const metadataId = generateUserMetadataID(userId)
|
|
61
|
+
let metadata
|
|
62
|
+
try {
|
|
63
|
+
metadata = await db.get(metadataId)
|
|
64
|
+
} catch (err) {
|
|
65
|
+
if (deleteFromApp) {
|
|
66
|
+
return
|
|
67
|
+
}
|
|
68
|
+
metadata = {
|
|
69
|
+
tableId: InternalTables.USER_METADATA,
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (deleteFromApp) {
|
|
74
|
+
await db.remove(metadata)
|
|
75
|
+
return
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// assign the roleId for the metadata doc
|
|
79
|
+
if (roleId) {
|
|
80
|
+
metadata.roleId = roleId
|
|
81
|
+
}
|
|
82
|
+
let combined = sdk.users.combineMetadataAndUser(user, metadata)
|
|
83
|
+
// if its null then there was no updates required
|
|
84
|
+
if (combined) {
|
|
85
|
+
await db.put(combined)
|
|
86
|
+
}
|
|
87
|
+
})
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
ctx.body = {
|
|
91
|
+
message: "User synced.",
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export async function fetchMetadata(ctx: BBContext) {
|
|
10
96
|
const global = await getGlobalUsers()
|
|
11
97
|
const metadata = await sdk.users.rawUserMetadata()
|
|
12
98
|
const users = []
|
|
@@ -25,7 +111,7 @@ export async function fetchMetadata(ctx: UserCtx) {
|
|
|
25
111
|
ctx.body = users
|
|
26
112
|
}
|
|
27
113
|
|
|
28
|
-
export async function updateSelfMetadata(ctx:
|
|
114
|
+
export async function updateSelfMetadata(ctx: BBContext) {
|
|
29
115
|
// overwrite the ID with current users
|
|
30
116
|
ctx.request.body._id = ctx.user?._id
|
|
31
117
|
// make sure no stale rev
|
|
@@ -35,7 +121,7 @@ export async function updateSelfMetadata(ctx: UserCtx) {
|
|
|
35
121
|
await updateMetadata(ctx)
|
|
36
122
|
}
|
|
37
123
|
|
|
38
|
-
export async function updateMetadata(ctx:
|
|
124
|
+
export async function updateMetadata(ctx: BBContext) {
|
|
39
125
|
const db = context.getAppDB()
|
|
40
126
|
const user = ctx.request.body
|
|
41
127
|
// this isn't applicable to the user
|
|
@@ -47,7 +133,7 @@ export async function updateMetadata(ctx: UserCtx) {
|
|
|
47
133
|
ctx.body = await db.put(metadata)
|
|
48
134
|
}
|
|
49
135
|
|
|
50
|
-
export async function destroyMetadata(ctx:
|
|
136
|
+
export async function destroyMetadata(ctx: BBContext) {
|
|
51
137
|
const db = context.getAppDB()
|
|
52
138
|
try {
|
|
53
139
|
const dbUser = await db.get(ctx.params.id)
|
|
@@ -60,11 +146,11 @@ export async function destroyMetadata(ctx: UserCtx) {
|
|
|
60
146
|
}
|
|
61
147
|
}
|
|
62
148
|
|
|
63
|
-
export async function findMetadata(ctx:
|
|
149
|
+
export async function findMetadata(ctx: BBContext) {
|
|
64
150
|
ctx.body = await getFullUser(ctx, ctx.params.id)
|
|
65
151
|
}
|
|
66
152
|
|
|
67
|
-
export async function setFlag(ctx:
|
|
153
|
+
export async function setFlag(ctx: BBContext) {
|
|
68
154
|
const userId = ctx.user?._id
|
|
69
155
|
const { flag, value } = ctx.request.body
|
|
70
156
|
if (!flag) {
|
|
@@ -83,7 +169,7 @@ export async function setFlag(ctx: UserCtx) {
|
|
|
83
169
|
ctx.body = { message: "Flag set successfully" }
|
|
84
170
|
}
|
|
85
171
|
|
|
86
|
-
export async function getFlags(ctx:
|
|
172
|
+
export async function getFlags(ctx: BBContext) {
|
|
87
173
|
const userId = ctx.user?._id
|
|
88
174
|
const docId = generateUserFlagID(userId!)
|
|
89
175
|
const db = context.getAppDB()
|
package/src/api/routes/index.ts
CHANGED
|
@@ -25,7 +25,6 @@ import devRoutes from "./dev"
|
|
|
25
25
|
import cloudRoutes from "./cloud"
|
|
26
26
|
import migrationRoutes from "./migrations"
|
|
27
27
|
import pluginRoutes from "./plugin"
|
|
28
|
-
import opsRoutes from "./ops"
|
|
29
28
|
import Router from "@koa/router"
|
|
30
29
|
import { api as pro } from "@budibase/pro"
|
|
31
30
|
|
|
@@ -64,7 +63,6 @@ export const mainRoutes: Router[] = [
|
|
|
64
63
|
rowRoutes,
|
|
65
64
|
migrationRoutes,
|
|
66
65
|
pluginRoutes,
|
|
67
|
-
opsRoutes,
|
|
68
66
|
scheduleRoutes,
|
|
69
67
|
environmentVariableRoutes,
|
|
70
68
|
// these need to be handled last as they still use /api/:tableId
|
|
@@ -7,7 +7,7 @@ const {
|
|
|
7
7
|
const setup = require("./utilities")
|
|
8
8
|
const { basicAutomation, newAutomation, automationTrigger, automationStep } = setup.structures
|
|
9
9
|
const MAX_RETRIES = 4
|
|
10
|
-
const { TRIGGER_DEFINITIONS,
|
|
10
|
+
const { TRIGGER_DEFINITIONS, ACTION_DEFINITIONS } = require("../../../automations")
|
|
11
11
|
const { events } = require("@budibase/backend-core")
|
|
12
12
|
|
|
13
13
|
|
|
@@ -19,14 +19,11 @@ describe("/automations", () => {
|
|
|
19
19
|
|
|
20
20
|
afterAll(setup.afterAll)
|
|
21
21
|
|
|
22
|
-
beforeAll
|
|
22
|
+
// For some reason this cannot be a beforeAll or the test "tests the automation successfully" fail
|
|
23
|
+
beforeEach(async () => {
|
|
23
24
|
await config.init()
|
|
24
25
|
})
|
|
25
26
|
|
|
26
|
-
beforeEach(() => {
|
|
27
|
-
events.automation.deleted.mockClear()
|
|
28
|
-
})
|
|
29
|
-
|
|
30
27
|
describe("get definitions", () => {
|
|
31
28
|
it("returns a list of definitions for actions", async () => {
|
|
32
29
|
const res = await request
|
|
@@ -55,7 +52,7 @@ describe("/automations", () => {
|
|
|
55
52
|
.expect('Content-Type', /json/)
|
|
56
53
|
.expect(200)
|
|
57
54
|
|
|
58
|
-
let definitionsLength = Object.keys(
|
|
55
|
+
let definitionsLength = Object.keys(ACTION_DEFINITIONS).length
|
|
59
56
|
definitionsLength-- // OUTGOING_WEBHOOK is deprecated
|
|
60
57
|
|
|
61
58
|
expect(Object.keys(res.body.action).length).toBeGreaterThanOrEqual(definitionsLength)
|
|
@@ -57,7 +57,6 @@ describe("/users", () => {
|
|
|
57
57
|
it("should be able to update the user", async () => {
|
|
58
58
|
const user = await config.createUser({ id: `us_update${utils.newid()}` })
|
|
59
59
|
user.roleId = BUILTIN_ROLE_IDS.BASIC
|
|
60
|
-
delete user._rev
|
|
61
60
|
const res = await request
|
|
62
61
|
.put(`/api/users/metadata`)
|
|
63
62
|
.set(config.defaultHeaders())
|
|
@@ -66,46 +65,6 @@ describe("/users", () => {
|
|
|
66
65
|
.expect("Content-Type", /json/)
|
|
67
66
|
expect(res.body.ok).toEqual(true)
|
|
68
67
|
})
|
|
69
|
-
|
|
70
|
-
it("should be able to update the user multiple times", async () => {
|
|
71
|
-
const user = await config.createUser()
|
|
72
|
-
delete user._rev
|
|
73
|
-
|
|
74
|
-
const res1 = await request
|
|
75
|
-
.put(`/api/users/metadata`)
|
|
76
|
-
.set(config.defaultHeaders())
|
|
77
|
-
.send({ ...user, roleId: BUILTIN_ROLE_IDS.BASIC })
|
|
78
|
-
.expect(200)
|
|
79
|
-
.expect("Content-Type", /json/)
|
|
80
|
-
|
|
81
|
-
const res = await request
|
|
82
|
-
.put(`/api/users/metadata`)
|
|
83
|
-
.set(config.defaultHeaders())
|
|
84
|
-
.send({ ...user, _rev: res1.body.rev, roleId: BUILTIN_ROLE_IDS.POWER })
|
|
85
|
-
.expect(200)
|
|
86
|
-
.expect("Content-Type", /json/)
|
|
87
|
-
|
|
88
|
-
expect(res.body.ok).toEqual(true)
|
|
89
|
-
})
|
|
90
|
-
|
|
91
|
-
it("should require the _rev field for multiple updates", async () => {
|
|
92
|
-
const user = await config.createUser()
|
|
93
|
-
delete user._rev
|
|
94
|
-
|
|
95
|
-
await request
|
|
96
|
-
.put(`/api/users/metadata`)
|
|
97
|
-
.set(config.defaultHeaders())
|
|
98
|
-
.send({ ...user, roleId: BUILTIN_ROLE_IDS.BASIC })
|
|
99
|
-
.expect(200)
|
|
100
|
-
.expect("Content-Type", /json/)
|
|
101
|
-
|
|
102
|
-
await request
|
|
103
|
-
.put(`/api/users/metadata`)
|
|
104
|
-
.set(config.defaultHeaders())
|
|
105
|
-
.send({ ...user, roleId: BUILTIN_ROLE_IDS.POWER })
|
|
106
|
-
.expect(409)
|
|
107
|
-
.expect("Content-Type", /json/)
|
|
108
|
-
})
|
|
109
68
|
})
|
|
110
69
|
|
|
111
70
|
describe("destroy", () => {
|
|
@@ -133,7 +92,6 @@ describe("/users", () => {
|
|
|
133
92
|
expect(res.body.tableId).toBeDefined()
|
|
134
93
|
})
|
|
135
94
|
})
|
|
136
|
-
|
|
137
95
|
describe("setFlag", () => {
|
|
138
96
|
it("should throw an error if a flag is not provided", async () => {
|
|
139
97
|
await config.createUser()
|
|
@@ -143,9 +101,8 @@ describe("/users", () => {
|
|
|
143
101
|
.send({ value: "test" })
|
|
144
102
|
.expect(400)
|
|
145
103
|
.expect("Content-Type", /json/)
|
|
146
|
-
expect(res.body.message).toEqual(
|
|
147
|
-
|
|
148
|
-
)
|
|
104
|
+
expect(res.body.message).toEqual("Must supply a 'flag' field in request body.")
|
|
105
|
+
|
|
149
106
|
})
|
|
150
107
|
|
|
151
108
|
it("should be able to set a flag on the user", async () => {
|
|
@@ -189,9 +146,8 @@ describe("/users", () => {
|
|
|
189
146
|
.send({ value: "test" })
|
|
190
147
|
.expect(400)
|
|
191
148
|
.expect("Content-Type", /json/)
|
|
192
|
-
expect(res.body.message).toEqual(
|
|
193
|
-
|
|
194
|
-
)
|
|
149
|
+
expect(res.body.message).toEqual("Must supply a 'flag' field in request body.")
|
|
150
|
+
|
|
195
151
|
})
|
|
196
152
|
|
|
197
153
|
it("should be able to set a flag on the user", async () => {
|
|
@@ -205,4 +161,37 @@ describe("/users", () => {
|
|
|
205
161
|
expect(res.body.message).toEqual("Flag set successfully")
|
|
206
162
|
})
|
|
207
163
|
})
|
|
164
|
+
|
|
165
|
+
describe("syncUser", () => {
|
|
166
|
+
it("should sync the user", async () => {
|
|
167
|
+
let user = await config.createUser()
|
|
168
|
+
await config.createApp('New App')
|
|
169
|
+
let res = await request
|
|
170
|
+
.post(`/api/users/metadata/sync/${user._id}`)
|
|
171
|
+
.set(config.defaultHeaders())
|
|
172
|
+
.expect(200)
|
|
173
|
+
.expect("Content-Type", /json/)
|
|
174
|
+
expect(res.body.message).toEqual('User synced.')
|
|
175
|
+
})
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
it("should sync the user when a previous user is specified", async () => {
|
|
179
|
+
const app1 = await config.createApp('App 1')
|
|
180
|
+
const app2 = await config.createApp('App 2')
|
|
181
|
+
|
|
182
|
+
let user = await config.createUser({
|
|
183
|
+
builder: false,
|
|
184
|
+
admin: true,
|
|
185
|
+
roles: { [app1.appId]: 'ADMIN' }
|
|
186
|
+
})
|
|
187
|
+
let res = await request
|
|
188
|
+
.post(`/api/users/metadata/sync/${user._id}`)
|
|
189
|
+
.set(config.defaultHeaders())
|
|
190
|
+
.send({ previousUser: { ...user, roles: { ...user.roles, [app2.appId]: 'BASIC' } } })
|
|
191
|
+
.expect(200)
|
|
192
|
+
.expect("Content-Type", /json/)
|
|
193
|
+
|
|
194
|
+
expect(res.body.message).toEqual('User synced.')
|
|
195
|
+
})
|
|
196
|
+
})
|
|
208
197
|
})
|
package/src/api/routes/user.ts
CHANGED
|
@@ -32,6 +32,11 @@ router
|
|
|
32
32
|
authorized(PermissionType.USER, PermissionLevel.WRITE),
|
|
33
33
|
controller.destroyMetadata
|
|
34
34
|
)
|
|
35
|
+
.post(
|
|
36
|
+
"/api/users/metadata/sync/:id",
|
|
37
|
+
authorized(PermissionType.USER, PermissionLevel.WRITE),
|
|
38
|
+
controller.syncUser
|
|
39
|
+
)
|
|
35
40
|
.post(
|
|
36
41
|
"/api/users/flags",
|
|
37
42
|
authorized(PermissionType.USER, PermissionLevel.WRITE),
|
package/src/app.ts
CHANGED
|
@@ -2,9 +2,21 @@ if (process.env.DD_APM_ENABLED) {
|
|
|
2
2
|
require("./ddApm")
|
|
3
3
|
}
|
|
4
4
|
|
|
5
|
+
if (process.env.ELASTIC_APM_ENABLED) {
|
|
6
|
+
require("./elasticApm")
|
|
7
|
+
}
|
|
8
|
+
|
|
5
9
|
// need to load environment first
|
|
6
10
|
import env from "./environment"
|
|
7
11
|
|
|
12
|
+
// enable APM if configured
|
|
13
|
+
if (process.env.ELASTIC_APM_ENABLED) {
|
|
14
|
+
const apm = require("elastic-apm-node").start({
|
|
15
|
+
serviceName: process.env.SERVICE,
|
|
16
|
+
environment: process.env.BUDIBASE_ENVIRONMENT,
|
|
17
|
+
})
|
|
18
|
+
}
|
|
19
|
+
|
|
8
20
|
import { ExtendableContext } from "koa"
|
|
9
21
|
import * as db from "./db"
|
|
10
22
|
db.init()
|
|
@@ -15,8 +27,8 @@ import * as api from "./api"
|
|
|
15
27
|
import * as automations from "./automations"
|
|
16
28
|
import { Thread } from "./threads"
|
|
17
29
|
import * as redis from "./utilities/redis"
|
|
18
|
-
import { initialise as initialiseWebsockets } from "./websockets"
|
|
19
30
|
import { events, logging, middleware, timers } from "@budibase/backend-core"
|
|
31
|
+
import { initialise as initialiseWebsockets } from "./websocket"
|
|
20
32
|
import { startup } from "./startup"
|
|
21
33
|
const Sentry = require("@sentry/node")
|
|
22
34
|
const destroyable = require("server-destroy")
|
|
@@ -41,8 +53,7 @@ app.use(
|
|
|
41
53
|
})
|
|
42
54
|
)
|
|
43
55
|
|
|
44
|
-
app.use(middleware.
|
|
45
|
-
app.use(middleware.pino)
|
|
56
|
+
app.use(middleware.logging)
|
|
46
57
|
app.use(userAgent)
|
|
47
58
|
|
|
48
59
|
if (env.isProd()) {
|
|
@@ -61,7 +72,7 @@ if (env.isProd()) {
|
|
|
61
72
|
|
|
62
73
|
const server = http.createServer(app.callback())
|
|
63
74
|
destroyable(server)
|
|
64
|
-
initialiseWebsockets(
|
|
75
|
+
initialiseWebsockets(server)
|
|
65
76
|
|
|
66
77
|
let shuttingDown = false,
|
|
67
78
|
errCode = 0
|
|
@@ -15,14 +15,7 @@ import * as delay from "./steps/delay"
|
|
|
15
15
|
import * as queryRow from "./steps/queryRows"
|
|
16
16
|
import * as loop from "./steps/loop"
|
|
17
17
|
import env from "../environment"
|
|
18
|
-
import {
|
|
19
|
-
AutomationStepSchema,
|
|
20
|
-
AutomationStepInput,
|
|
21
|
-
PluginType,
|
|
22
|
-
AutomationStep,
|
|
23
|
-
} from "@budibase/types"
|
|
24
|
-
import sdk from "../sdk"
|
|
25
|
-
import { getAutomationPlugin } from "../utilities/fileSystem"
|
|
18
|
+
import { AutomationStepSchema, AutomationStepInput } from "@budibase/types"
|
|
26
19
|
|
|
27
20
|
const ACTION_IMPLS: Record<
|
|
28
21
|
string,
|
|
@@ -45,26 +38,25 @@ const ACTION_IMPLS: Record<
|
|
|
45
38
|
zapier: zapier.run,
|
|
46
39
|
integromat: integromat.run,
|
|
47
40
|
}
|
|
48
|
-
export const
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
}
|
|
41
|
+
export const ACTION_DEFINITIONS: Record<string, AutomationStepSchema> = {
|
|
42
|
+
SEND_EMAIL_SMTP: sendSmtpEmail.definition,
|
|
43
|
+
CREATE_ROW: createRow.definition,
|
|
44
|
+
UPDATE_ROW: updateRow.definition,
|
|
45
|
+
DELETE_ROW: deleteRow.definition,
|
|
46
|
+
OUTGOING_WEBHOOK: outgoingWebhook.definition,
|
|
47
|
+
EXECUTE_SCRIPT: executeScript.definition,
|
|
48
|
+
EXECUTE_QUERY: executeQuery.definition,
|
|
49
|
+
SERVER_LOG: serverLog.definition,
|
|
50
|
+
DELAY: delay.definition,
|
|
51
|
+
FILTER: filter.definition,
|
|
52
|
+
QUERY_ROWS: queryRow.definition,
|
|
53
|
+
LOOP: loop.definition,
|
|
54
|
+
// these used to be lowercase step IDs, maintain for backwards compat
|
|
55
|
+
discord: discord.definition,
|
|
56
|
+
slack: slack.definition,
|
|
57
|
+
zapier: zapier.definition,
|
|
58
|
+
integromat: integromat.definition,
|
|
59
|
+
}
|
|
68
60
|
|
|
69
61
|
// don't add the bash script/definitions unless in self host
|
|
70
62
|
// the fact this isn't included in any definitions means it cannot be
|
|
@@ -74,36 +66,12 @@ if (env.SELF_HOSTED) {
|
|
|
74
66
|
// @ts-ignore
|
|
75
67
|
ACTION_IMPLS["EXECUTE_BASH"] = bash.run
|
|
76
68
|
// @ts-ignore
|
|
77
|
-
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
export async function getActionDefinitions() {
|
|
81
|
-
const actionDefinitions = BUILTIN_ACTION_DEFINITIONS
|
|
82
|
-
if (env.SELF_HOSTED) {
|
|
83
|
-
const plugins = await sdk.plugins.fetch(PluginType.AUTOMATION)
|
|
84
|
-
for (let plugin of plugins) {
|
|
85
|
-
const schema = plugin.schema.schema as AutomationStep
|
|
86
|
-
actionDefinitions[schema.stepId] = {
|
|
87
|
-
...schema,
|
|
88
|
-
custom: true,
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
return actionDefinitions
|
|
69
|
+
ACTION_DEFINITIONS["EXECUTE_BASH"] = bash.definition
|
|
93
70
|
}
|
|
94
71
|
|
|
95
72
|
/* istanbul ignore next */
|
|
96
|
-
export async function getAction(
|
|
97
|
-
if (ACTION_IMPLS[
|
|
98
|
-
return ACTION_IMPLS[
|
|
99
|
-
}
|
|
100
|
-
// must be a plugin
|
|
101
|
-
if (env.SELF_HOSTED) {
|
|
102
|
-
const plugins = await sdk.plugins.fetch(PluginType.AUTOMATION)
|
|
103
|
-
const found = plugins.find(plugin => plugin.schema.schema.stepId === stepId)
|
|
104
|
-
if (!found) {
|
|
105
|
-
throw new Error(`Unable to find action implementation for "${stepId}"`)
|
|
106
|
-
}
|
|
107
|
-
return (await getAutomationPlugin(found)).action
|
|
73
|
+
export async function getAction(actionName: string) {
|
|
74
|
+
if (ACTION_IMPLS[actionName] != null) {
|
|
75
|
+
return ACTION_IMPLS[actionName]
|
|
108
76
|
}
|
|
109
77
|
}
|
package/src/automations/index.ts
CHANGED
|
@@ -6,7 +6,7 @@ import BullQueue from "bull"
|
|
|
6
6
|
export { automationQueue } from "./bullboard"
|
|
7
7
|
export { shutdown } from "./bullboard"
|
|
8
8
|
export { TRIGGER_DEFINITIONS } from "./triggers"
|
|
9
|
-
export {
|
|
9
|
+
export { ACTION_DEFINITIONS } from "./actions"
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* This module is built purely to kick off the worker farm and manage the inputs/outputs
|
|
@@ -4,11 +4,8 @@ import * as automationUtils from "../automationUtils"
|
|
|
4
4
|
import environment from "../../environment"
|
|
5
5
|
import {
|
|
6
6
|
AutomationActionStepId,
|
|
7
|
-
AutomationCustomIOType,
|
|
8
|
-
AutomationIOType,
|
|
9
|
-
AutomationStepInput,
|
|
10
7
|
AutomationStepSchema,
|
|
11
|
-
|
|
8
|
+
AutomationStepInput,
|
|
12
9
|
} from "@budibase/types"
|
|
13
10
|
|
|
14
11
|
export const definition: AutomationStepSchema = {
|
|
@@ -16,7 +13,7 @@ export const definition: AutomationStepSchema = {
|
|
|
16
13
|
tagline: "Execute a bash command",
|
|
17
14
|
icon: "JourneyEvent",
|
|
18
15
|
description: "Run a bash script",
|
|
19
|
-
type:
|
|
16
|
+
type: "ACTION",
|
|
20
17
|
internal: true,
|
|
21
18
|
stepId: AutomationActionStepId.EXECUTE_BASH,
|
|
22
19
|
inputs: {},
|
|
@@ -24,8 +21,8 @@ export const definition: AutomationStepSchema = {
|
|
|
24
21
|
inputs: {
|
|
25
22
|
properties: {
|
|
26
23
|
code: {
|
|
27
|
-
type:
|
|
28
|
-
customType:
|
|
24
|
+
type: "string",
|
|
25
|
+
customType: "code",
|
|
29
26
|
title: "Code",
|
|
30
27
|
},
|
|
31
28
|
},
|
|
@@ -34,16 +31,16 @@ export const definition: AutomationStepSchema = {
|
|
|
34
31
|
outputs: {
|
|
35
32
|
properties: {
|
|
36
33
|
stdout: {
|
|
37
|
-
type:
|
|
34
|
+
type: "string",
|
|
38
35
|
description: "Standard output of your bash command or script",
|
|
39
36
|
},
|
|
40
37
|
success: {
|
|
41
|
-
type:
|
|
38
|
+
type: "boolean",
|
|
42
39
|
description: "Whether the command was successful",
|
|
43
40
|
},
|
|
44
41
|
},
|
|
45
|
-
required: ["stdout"],
|
|
46
42
|
},
|
|
43
|
+
required: ["stdout"],
|
|
47
44
|
},
|
|
48
45
|
}
|
|
49
46
|
|