@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.
Files changed (156) hide show
  1. package/builder/assets/index.7f9a008b.css +6 -0
  2. package/builder/assets/index.f02eef32.js +1817 -0
  3. package/builder/index.html +2 -2
  4. package/dist/api/controllers/automation.js +7 -13
  5. package/dist/api/controllers/plugin/index.js +37 -6
  6. package/dist/api/controllers/table/utils.js +1 -2
  7. package/dist/api/controllers/user.js +83 -1
  8. package/dist/api/routes/index.js +0 -2
  9. package/dist/api/routes/user.js +1 -0
  10. package/dist/app.js +13 -4
  11. package/dist/automations/actions.js +6 -32
  12. package/dist/automations/index.js +2 -3
  13. package/dist/automations/steps/bash.js +6 -6
  14. package/dist/automations/steps/createRow.js +11 -11
  15. package/dist/automations/steps/delay.js +3 -3
  16. package/dist/automations/steps/deleteRow.js +8 -8
  17. package/dist/automations/steps/discord.js +8 -8
  18. package/dist/automations/steps/executeQuery.js +9 -9
  19. package/dist/automations/steps/executeScript.js +6 -6
  20. package/dist/automations/steps/filter.js +6 -6
  21. package/dist/automations/steps/integromat.js +10 -10
  22. package/dist/automations/steps/loop.js +9 -9
  23. package/dist/automations/steps/outgoingWebhook.js +10 -10
  24. package/dist/automations/steps/queryRows.js +14 -14
  25. package/dist/automations/steps/sendSmtpEmail.js +9 -9
  26. package/dist/automations/steps/serverLog.js +4 -4
  27. package/dist/automations/steps/slack.js +6 -6
  28. package/dist/automations/steps/updateRow.js +11 -11
  29. package/dist/automations/steps/zapier.js +9 -9
  30. package/dist/automations/triggerInfo/app.js +5 -5
  31. package/dist/automations/triggerInfo/cron.js +4 -4
  32. package/dist/automations/triggerInfo/rowDeleted.js +5 -5
  33. package/dist/automations/triggerInfo/rowSaved.js +7 -7
  34. package/dist/automations/triggerInfo/rowUpdated.js +7 -7
  35. package/dist/automations/triggerInfo/webhook.js +6 -6
  36. package/dist/elasticApm.js +14 -0
  37. package/dist/environment.js +1 -0
  38. package/dist/events/index.js +0 -3
  39. package/dist/integrations/index.js +3 -3
  40. package/dist/integrations/microsoftSqlServer.js +2 -5
  41. package/dist/integrations/mysql.js +3 -5
  42. package/dist/integrations/postgres.js +5 -7
  43. package/dist/integrations/redis.js +0 -7
  44. package/dist/integrations/rest.js +0 -4
  45. package/dist/migrations/functions/usageQuotas/syncApps.js +1 -1
  46. package/dist/migrations/functions/usageQuotas/syncRows.js +2 -1
  47. package/dist/package.json +15 -15
  48. package/dist/sdk/app/applications/sync.js +23 -117
  49. package/dist/sdk/index.js +0 -2
  50. package/dist/sdk/users/utils.js +4 -21
  51. package/dist/startup.js +28 -31
  52. package/dist/threads/automation.js +5 -16
  53. package/dist/tsconfig.build.tsbuildinfo +1 -1
  54. package/dist/utilities/fileSystem/plugin.js +23 -33
  55. package/dist/utilities/global.js +12 -17
  56. package/dist/watch.js +2 -2
  57. package/dist/websocket.js +22 -0
  58. package/jest.config.ts +3 -3
  59. package/nodemon.json +3 -7
  60. package/package.json +16 -16
  61. package/scripts/dev/manage.js +0 -2
  62. package/scripts/integrations/mssql/data/entrypoint.sh +0 -1
  63. package/scripts/integrations/mssql/data/setup.sql +17 -17
  64. package/scripts/integrations/mysql/init.sql +1 -1
  65. package/scripts/integrations/postgres/init.sql +0 -1
  66. package/scripts/likeCypress.ts +35 -0
  67. package/src/api/controllers/automation.ts +6 -12
  68. package/src/api/controllers/plugin/index.ts +45 -8
  69. package/src/api/controllers/row/internal.ts +10 -9
  70. package/src/api/controllers/row/utils.ts +2 -2
  71. package/src/api/controllers/table/utils.ts +1 -2
  72. package/src/api/controllers/user.ts +96 -10
  73. package/src/api/routes/index.ts +0 -2
  74. package/src/api/routes/tests/automation.spec.js +4 -7
  75. package/src/api/routes/tests/user.spec.js +37 -48
  76. package/src/api/routes/user.ts +5 -0
  77. package/src/app.ts +15 -4
  78. package/src/automations/actions.ts +24 -56
  79. package/src/automations/index.ts +1 -1
  80. package/src/automations/steps/bash.ts +7 -10
  81. package/src/automations/steps/createRow.ts +12 -15
  82. package/src/automations/steps/delay.ts +4 -6
  83. package/src/automations/steps/deleteRow.ts +9 -12
  84. package/src/automations/steps/discord.ts +8 -10
  85. package/src/automations/steps/executeQuery.ts +10 -13
  86. package/src/automations/steps/executeScript.ts +7 -10
  87. package/src/automations/steps/filter.ts +6 -8
  88. package/src/automations/steps/integromat.ts +10 -12
  89. package/src/automations/steps/loop.ts +10 -16
  90. package/src/automations/steps/outgoingWebhook.ts +11 -14
  91. package/src/automations/steps/queryRows.ts +15 -18
  92. package/src/automations/steps/sendSmtpEmail.ts +9 -11
  93. package/src/automations/steps/serverLog.ts +4 -6
  94. package/src/automations/steps/slack.ts +6 -8
  95. package/src/automations/steps/updateRow.ts +12 -15
  96. package/src/automations/steps/zapier.ts +9 -11
  97. package/src/automations/tests/utilities/index.ts +2 -2
  98. package/src/automations/triggerInfo/app.ts +5 -8
  99. package/src/automations/triggerInfo/cron.ts +4 -7
  100. package/src/automations/triggerInfo/rowDeleted.ts +5 -8
  101. package/src/automations/triggerInfo/rowSaved.ts +7 -10
  102. package/src/automations/triggerInfo/rowUpdated.ts +7 -10
  103. package/src/automations/triggerInfo/webhook.ts +6 -9
  104. package/src/elasticApm.ts +10 -0
  105. package/src/environment.ts +1 -0
  106. package/src/events/index.ts +0 -1
  107. package/src/integrations/index.ts +3 -3
  108. package/src/integrations/microsoftSqlServer.ts +2 -5
  109. package/src/integrations/mysql.ts +3 -5
  110. package/src/integrations/postgres.ts +5 -7
  111. package/src/integrations/redis.ts +0 -8
  112. package/src/integrations/rest.ts +0 -3
  113. package/src/migrations/functions/usageQuotas/syncApps.ts +1 -1
  114. package/src/migrations/functions/usageQuotas/syncRows.ts +3 -2
  115. package/src/migrations/functions/usageQuotas/tests/syncRows.spec.ts +2 -2
  116. package/src/sdk/app/applications/sync.ts +22 -129
  117. package/src/sdk/index.ts +0 -2
  118. package/src/sdk/users/tests/utils.spec.ts +32 -1
  119. package/src/sdk/users/utils.ts +5 -23
  120. package/src/startup.ts +34 -36
  121. package/src/tests/jestEnv.ts +1 -0
  122. package/src/tests/jestSetup.ts +1 -0
  123. package/src/tests/logging.ts +34 -0
  124. package/src/tests/utilities/TestConfiguration.ts +0 -28
  125. package/src/tests/utilities/structures.ts +17 -25
  126. package/src/threads/automation.ts +6 -18
  127. package/src/utilities/fileSystem/plugin.ts +4 -13
  128. package/src/utilities/global.ts +16 -21
  129. package/src/watch.ts +2 -2
  130. package/src/websocket.ts +26 -0
  131. package/tsconfig.json +7 -1
  132. package/builder/assets/index.5c1a6913.js +0 -1776
  133. package/builder/assets/index.c0265b74.css +0 -6
  134. package/dist/api/controllers/ops.js +0 -40
  135. package/dist/api/routes/ops.js +0 -52
  136. package/dist/events/docUpdates/index.js +0 -17
  137. package/dist/events/docUpdates/processors.js +0 -18
  138. package/dist/events/docUpdates/syncUsers.js +0 -49
  139. package/dist/sdk/plugins/index.js +0 -27
  140. package/dist/sdk/plugins/plugins.js +0 -53
  141. package/dist/websockets/client.js +0 -14
  142. package/dist/websockets/grid.js +0 -60
  143. package/dist/websockets/index.js +0 -17
  144. package/dist/websockets/websocket.js +0 -78
  145. package/src/api/controllers/ops.ts +0 -32
  146. package/src/api/routes/ops.ts +0 -30
  147. package/src/events/docUpdates/index.ts +0 -1
  148. package/src/events/docUpdates/processors.ts +0 -14
  149. package/src/events/docUpdates/syncUsers.ts +0 -35
  150. package/src/sdk/app/applications/tests/sync.spec.ts +0 -137
  151. package/src/sdk/plugins/index.ts +0 -5
  152. package/src/sdk/plugins/plugins.ts +0 -41
  153. package/src/websockets/client.ts +0 -11
  154. package/src/websockets/grid.ts +0 -55
  155. package/src/websockets/index.ts +0 -14
  156. 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: UserCtx, db: Database, tableId: string) {
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: UserCtx) {
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: UserCtx) {
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: UserCtx) {
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: UserCtx) {
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: UserCtx) {
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: UserCtx) {
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: UserCtx) {
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: UserCtx) {
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 { UserCtx } from "@budibase/types"
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: UserCtx, tableId: string, rowId: string) {
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
- (schema.type === FieldTypes.OPTIONS ||
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 { context } from "@budibase/backend-core"
6
- import { UserCtx } from "@budibase/types"
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 fetchMetadata(ctx: UserCtx) {
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: UserCtx) {
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: UserCtx) {
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: UserCtx) {
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: UserCtx) {
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: UserCtx) {
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: UserCtx) {
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()
@@ -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, BUILTIN_ACTION_DEFINITIONS } = require("../../../automations")
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(async () => {
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(BUILTIN_ACTION_DEFINITIONS).length
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
- "Must supply a 'flag' field in request body."
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
- "Must supply a 'flag' field in request body."
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
  })
@@ -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.correlation)
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(app, server)
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 BUILTIN_ACTION_DEFINITIONS: Record<string, AutomationStepSchema> =
49
- {
50
- SEND_EMAIL_SMTP: sendSmtpEmail.definition,
51
- CREATE_ROW: createRow.definition,
52
- UPDATE_ROW: updateRow.definition,
53
- DELETE_ROW: deleteRow.definition,
54
- OUTGOING_WEBHOOK: outgoingWebhook.definition,
55
- EXECUTE_SCRIPT: executeScript.definition,
56
- EXECUTE_QUERY: executeQuery.definition,
57
- SERVER_LOG: serverLog.definition,
58
- DELAY: delay.definition,
59
- FILTER: filter.definition,
60
- QUERY_ROWS: queryRow.definition,
61
- LOOP: loop.definition,
62
- // these used to be lowercase step IDs, maintain for backwards compat
63
- discord: discord.definition,
64
- slack: slack.definition,
65
- zapier: zapier.definition,
66
- integromat: integromat.definition,
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
- BUILTIN_ACTION_DEFINITIONS["EXECUTE_BASH"] = bash.definition
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(stepId: string) {
97
- if (ACTION_IMPLS[stepId] != null) {
98
- return ACTION_IMPLS[stepId]
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
  }
@@ -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 { BUILTIN_ACTION_DEFINITIONS, getActionDefinitions } from "./actions"
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
- AutomationStepType,
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: AutomationStepType.ACTION,
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: AutomationIOType.STRING,
28
- customType: AutomationCustomIOType.CODE,
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: AutomationIOType.STRING,
34
+ type: "string",
38
35
  description: "Standard output of your bash command or script",
39
36
  },
40
37
  success: {
41
- type: AutomationIOType.BOOLEAN,
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