@budibase/worker 2.2.26 → 2.2.27-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.
Files changed (172) hide show
  1. package/Dockerfile +1 -1
  2. package/__mocks__/node-fetch.ts +23 -0
  3. package/coverage/clover.xml +1423 -1379
  4. package/coverage/coverage-final.json +83 -77
  5. package/coverage/lcov-report/index.html +140 -140
  6. package/coverage/lcov-report/src/api/controllers/global/auth.ts.html +52 -52
  7. package/coverage/lcov-report/src/api/controllers/global/configs.ts.html +167 -224
  8. package/coverage/lcov-report/src/api/controllers/global/email.ts.html +4 -4
  9. package/coverage/lcov-report/src/api/controllers/global/events.ts.html +136 -0
  10. package/coverage/lcov-report/src/api/controllers/global/index.html +83 -68
  11. package/coverage/lcov-report/src/api/controllers/global/license.ts.html +16 -16
  12. package/coverage/lcov-report/src/api/controllers/global/roles.ts.html +69 -69
  13. package/coverage/lcov-report/src/api/controllers/global/self.ts.html +20 -20
  14. package/coverage/lcov-report/src/api/controllers/global/templates.ts.html +54 -54
  15. package/coverage/lcov-report/src/api/controllers/global/users.ts.html +83 -44
  16. package/coverage/lcov-report/src/api/controllers/global/workspaces.ts.html +9 -9
  17. package/coverage/lcov-report/src/api/controllers/system/accounts.ts.html +5 -5
  18. package/coverage/lcov-report/src/api/controllers/system/environment.ts.html +4 -4
  19. package/coverage/lcov-report/src/api/controllers/system/index.html +11 -11
  20. package/coverage/lcov-report/src/api/controllers/system/migrations.ts.html +5 -5
  21. package/coverage/lcov-report/src/api/controllers/system/restore.ts.html +4 -4
  22. package/coverage/lcov-report/src/api/controllers/system/status.ts.html +5 -5
  23. package/coverage/lcov-report/src/api/controllers/system/tenants.ts.html +7 -7
  24. package/coverage/lcov-report/src/api/index.html +1 -1
  25. package/coverage/lcov-report/src/api/index.ts.html +42 -30
  26. package/coverage/lcov-report/src/api/routes/global/auth.ts.html +14 -14
  27. package/coverage/lcov-report/src/api/routes/global/configs.ts.html +25 -25
  28. package/coverage/lcov-report/src/api/routes/global/email.ts.html +13 -13
  29. package/coverage/lcov-report/src/api/routes/global/events.ts.html +109 -0
  30. package/coverage/lcov-report/src/api/routes/global/index.html +37 -22
  31. package/coverage/lcov-report/src/api/routes/global/license.ts.html +9 -9
  32. package/coverage/lcov-report/src/api/routes/global/roles.ts.html +10 -10
  33. package/coverage/lcov-report/src/api/routes/global/self.ts.html +11 -11
  34. package/coverage/lcov-report/src/api/routes/global/templates.ts.html +14 -14
  35. package/coverage/lcov-report/src/api/routes/global/users.ts.html +62 -20
  36. package/coverage/lcov-report/src/api/routes/global/workspaces.ts.html +12 -12
  37. package/coverage/lcov-report/src/api/routes/index.html +7 -7
  38. package/coverage/lcov-report/src/api/routes/index.ts.html +29 -23
  39. package/coverage/lcov-report/src/api/routes/system/accounts.ts.html +10 -10
  40. package/coverage/lcov-report/src/api/routes/system/environment.ts.html +9 -9
  41. package/coverage/lcov-report/src/api/routes/system/index.html +15 -15
  42. package/coverage/lcov-report/src/api/routes/system/migrations.ts.html +10 -10
  43. package/coverage/lcov-report/src/api/routes/system/restore.ts.html +9 -9
  44. package/coverage/lcov-report/src/api/routes/system/status.ts.html +9 -9
  45. package/coverage/lcov-report/src/api/routes/system/tenants.ts.html +10 -10
  46. package/coverage/lcov-report/src/api/routes/validation/index.html +3 -3
  47. package/coverage/lcov-report/src/api/routes/validation/index.ts.html +2 -2
  48. package/coverage/lcov-report/src/api/routes/validation/users.ts.html +14 -14
  49. package/coverage/lcov-report/src/constants/index.html +17 -17
  50. package/coverage/lcov-report/src/constants/index.ts.html +77 -53
  51. package/coverage/lcov-report/src/constants/templates/index.html +18 -18
  52. package/coverage/lcov-report/src/constants/templates/index.ts.html +42 -42
  53. package/coverage/lcov-report/src/db/index.html +17 -17
  54. package/coverage/lcov-report/src/db/index.ts.html +18 -18
  55. package/coverage/lcov-report/src/environment.ts.html +45 -30
  56. package/coverage/lcov-report/src/index.html +27 -27
  57. package/coverage/lcov-report/src/index.ts.html +55 -55
  58. package/coverage/lcov-report/src/middleware/cloudRestricted.ts.html +5 -5
  59. package/coverage/lcov-report/src/middleware/index.html +1 -1
  60. package/coverage/lcov-report/src/migrations/functions/globalInfoSyncUsers.ts.html +9 -9
  61. package/coverage/lcov-report/src/migrations/functions/index.html +11 -11
  62. package/coverage/lcov-report/src/migrations/index.html +15 -15
  63. package/coverage/lcov-report/src/migrations/index.ts.html +27 -27
  64. package/coverage/lcov-report/src/sdk/accounts/accounts.ts.html +13 -13
  65. package/coverage/lcov-report/src/sdk/accounts/index.html +13 -13
  66. package/coverage/lcov-report/src/sdk/accounts/index.ts.html +2 -2
  67. package/coverage/lcov-report/src/sdk/index.html +5 -5
  68. package/coverage/lcov-report/src/sdk/index.ts.html +6 -6
  69. package/coverage/lcov-report/src/sdk/users/events.ts.html +82 -58
  70. package/coverage/lcov-report/src/sdk/users/index.html +28 -28
  71. package/coverage/lcov-report/src/sdk/users/index.ts.html +2 -2
  72. package/coverage/lcov-report/src/sdk/users/users.ts.html +174 -120
  73. package/coverage/lcov-report/src/tests/TestConfiguration.ts.html +221 -107
  74. package/coverage/lcov-report/src/tests/api/accounts.ts.html +7 -7
  75. package/coverage/lcov-report/src/tests/api/auth.ts.html +25 -13
  76. package/coverage/lcov-report/src/tests/api/base.ts.html +5 -5
  77. package/coverage/lcov-report/src/tests/api/configs.ts.html +11 -11
  78. package/coverage/lcov-report/src/tests/api/email.ts.html +6 -6
  79. package/coverage/lcov-report/src/tests/api/environment.ts.html +6 -6
  80. package/coverage/lcov-report/src/tests/api/groups.ts.html +163 -0
  81. package/coverage/lcov-report/src/tests/api/index.html +91 -31
  82. package/coverage/lcov-report/src/tests/api/index.ts.html +62 -29
  83. package/coverage/lcov-report/src/tests/api/license.ts.html +136 -0
  84. package/coverage/lcov-report/src/tests/api/migrations.ts.html +8 -8
  85. package/coverage/lcov-report/src/tests/api/restore.ts.html +7 -7
  86. package/coverage/lcov-report/src/tests/api/roles.ts.html +181 -0
  87. package/coverage/lcov-report/src/tests/api/self.ts.html +7 -7
  88. package/coverage/lcov-report/src/tests/api/status.ts.html +6 -6
  89. package/coverage/lcov-report/src/tests/api/templates.ts.html +175 -0
  90. package/coverage/lcov-report/src/tests/api/tenants.ts.html +10 -10
  91. package/coverage/lcov-report/src/tests/api/users.ts.html +30 -27
  92. package/coverage/lcov-report/src/tests/controllers.ts.html +14 -14
  93. package/coverage/lcov-report/src/tests/index.html +34 -34
  94. package/coverage/lcov-report/src/tests/index.ts.html +19 -55
  95. package/coverage/lcov-report/src/tests/mocks/{email.js.html → email.ts.html} +5 -5
  96. package/coverage/lcov-report/src/tests/mocks/index.html +6 -6
  97. package/coverage/lcov-report/src/tests/mocks/index.ts.html +10 -10
  98. package/coverage/lcov-report/src/tests/structures/configs.ts.html +11 -11
  99. package/coverage/lcov-report/src/tests/structures/groups.ts.html +15 -15
  100. package/coverage/lcov-report/src/tests/structures/index.html +26 -26
  101. package/coverage/lcov-report/src/tests/structures/index.ts.html +16 -22
  102. package/coverage/lcov-report/src/tests/structures/users.ts.html +16 -16
  103. package/coverage/lcov-report/src/utilities/appService.ts.html +45 -18
  104. package/coverage/lcov-report/src/utilities/email.ts.html +28 -28
  105. package/coverage/lcov-report/src/utilities/fileSystem.ts.html +4 -4
  106. package/coverage/lcov-report/src/utilities/index.html +27 -27
  107. package/coverage/lcov-report/src/utilities/index.ts.html +2 -2
  108. package/coverage/lcov-report/src/utilities/redis.ts.html +20 -20
  109. package/coverage/lcov-report/src/utilities/templates.ts.html +6 -6
  110. package/coverage/lcov-report/src/utilities/users.ts.html +7 -7
  111. package/coverage/lcov.info +2826 -2609
  112. package/jest-testcontainers-config.js +8 -0
  113. package/jest.config.ts +10 -4
  114. package/package.json +11 -7
  115. package/scripts/dev/manage.js +2 -2
  116. package/src/api/controllers/global/auth.ts +1 -1
  117. package/src/api/controllers/global/configs.ts +112 -131
  118. package/src/api/controllers/global/events.ts +17 -0
  119. package/src/api/controllers/global/users.ts +14 -1
  120. package/src/api/index.ts +4 -0
  121. package/src/api/routes/global/auth.ts +1 -1
  122. package/src/api/routes/global/configs.ts +7 -7
  123. package/src/api/routes/global/email.ts +1 -1
  124. package/src/api/routes/global/events.ts +8 -0
  125. package/src/api/routes/global/license.ts +1 -1
  126. package/src/api/routes/global/roles.ts +1 -1
  127. package/src/api/routes/global/self.ts +1 -1
  128. package/src/api/routes/global/templates.ts +1 -1
  129. package/src/api/routes/global/tests/auth.spec.ts +30 -22
  130. package/src/api/routes/global/tests/configs.spec.ts +114 -94
  131. package/src/api/routes/global/tests/groups.spec.ts +58 -0
  132. package/src/api/routes/global/tests/license.spec.ts +0 -2
  133. package/src/api/routes/global/tests/roles.spec.ts +71 -5
  134. package/src/api/routes/global/tests/templates.spec.ts +81 -9
  135. package/src/api/routes/global/tests/users.spec.ts +35 -36
  136. package/src/api/routes/global/users.ts +15 -1
  137. package/src/api/routes/global/workspaces.ts +1 -1
  138. package/src/api/routes/index.ts +2 -0
  139. package/src/api/routes/system/accounts.ts +1 -1
  140. package/src/api/routes/system/environment.ts +1 -1
  141. package/src/api/routes/system/migrations.ts +1 -1
  142. package/src/api/routes/system/restore.ts +1 -1
  143. package/src/api/routes/system/status.ts +1 -1
  144. package/src/api/routes/system/tenants.ts +1 -1
  145. package/src/api/routes/system/tests/environment.spec.ts +1 -1
  146. package/src/constants/index.ts +13 -5
  147. package/src/db/index.ts +1 -1
  148. package/src/environment.ts +6 -1
  149. package/src/index.ts +5 -5
  150. package/src/middleware/cloudRestricted.ts +1 -1
  151. package/src/sdk/users/events.ts +8 -0
  152. package/src/sdk/users/users.ts +23 -5
  153. package/src/tests/TestConfiguration.ts +52 -14
  154. package/src/tests/api/auth.ts +7 -3
  155. package/src/tests/api/configs.ts +2 -2
  156. package/src/tests/api/groups.ts +26 -0
  157. package/src/tests/api/index.ts +12 -1
  158. package/src/tests/api/license.ts +17 -0
  159. package/src/tests/api/roles.ts +32 -0
  160. package/src/tests/api/templates.ts +30 -0
  161. package/src/tests/api/users.ts +4 -3
  162. package/src/tests/index.ts +6 -18
  163. package/src/tests/jestEnv.ts +11 -0
  164. package/src/tests/jestSetup.ts +8 -15
  165. package/src/tests/mocks/{email.js → email.ts} +1 -1
  166. package/src/tests/mocks/index.ts +4 -4
  167. package/src/tests/structures/configs.ts +2 -2
  168. package/src/tests/structures/groups.ts +1 -1
  169. package/src/tests/structures/index.ts +3 -5
  170. package/src/tests/structures/users.ts +1 -1
  171. package/src/utilities/appService.ts +12 -3
  172. package/tsconfig.json +2 -2
@@ -1,14 +1,9 @@
1
1
  import { InviteUsersResponse, User } from "@budibase/types"
2
2
 
3
3
  jest.mock("nodemailer")
4
- import {
5
- TestConfiguration,
6
- mocks,
7
- structures,
8
- TENANT_1,
9
- } from "../../../../tests"
4
+ import { TestConfiguration, mocks, structures } from "../../../../tests"
10
5
  const sendMailMock = mocks.email.mock()
11
- import { events, tenancy } from "@budibase/backend-core"
6
+ import { context, events, tenancy } from "@budibase/backend-core"
12
7
 
13
8
  describe("/api/global/users", () => {
14
9
  const config = new TestConfiguration()
@@ -27,16 +22,18 @@ describe("/api/global/users", () => {
27
22
 
28
23
  describe("invite", () => {
29
24
  it("should be able to generate an invitation", async () => {
30
- const email = structures.users.newEmail()
31
- const { code, res } = await config.api.users.sendUserInvite(
32
- sendMailMock,
33
- email
34
- )
35
-
36
- expect(res.body).toEqual({ message: "Invitation has been sent." })
37
- expect(sendMailMock).toHaveBeenCalled()
38
- expect(code).toBeDefined()
39
- expect(events.user.invited).toBeCalledTimes(1)
25
+ await context.doInTenant(config.tenant1User!.tenantId, async () => {
26
+ const email = structures.users.newEmail()
27
+ const { code, res } = await config.api.users.sendUserInvite(
28
+ sendMailMock,
29
+ email
30
+ )
31
+
32
+ expect(res.body).toEqual({ message: "Invitation has been sent." })
33
+ expect(sendMailMock).toHaveBeenCalled()
34
+ expect(code).toBeDefined()
35
+ expect(events.user.invited).toBeCalledTimes(1)
36
+ })
40
37
  })
41
38
 
42
39
  it("should not be able to generate an invitation for existing user", async () => {
@@ -53,20 +50,22 @@ describe("/api/global/users", () => {
53
50
  })
54
51
 
55
52
  it("should be able to create new user from invite", async () => {
56
- const email = structures.users.newEmail()
57
- const { code } = await config.api.users.sendUserInvite(
58
- sendMailMock,
59
- email
60
- )
61
-
62
- const res = await config.api.users.acceptInvite(code)
63
-
64
- expect(res.body._id).toBeDefined()
65
- const user = await config.getUser(email)
66
- expect(user).toBeDefined()
67
- expect(user._id).toEqual(res.body._id)
68
- expect(events.user.inviteAccepted).toBeCalledTimes(1)
69
- expect(events.user.inviteAccepted).toBeCalledWith(user)
53
+ await context.doInTenant(config.tenant1User!.tenantId, async () => {
54
+ const email = structures.users.newEmail()
55
+ const { code } = await config.api.users.sendUserInvite(
56
+ sendMailMock,
57
+ email
58
+ )
59
+
60
+ const res = await config.api.users.acceptInvite(code)
61
+
62
+ expect(res.body._id).toBeDefined()
63
+ const user = await config.getUser(email)
64
+ expect(user).toBeDefined()
65
+ expect(user._id).toEqual(res.body._id)
66
+ expect(events.user.inviteAccepted).toBeCalledTimes(1)
67
+ expect(events.user.inviteAccepted).toBeCalledWith(user)
68
+ })
70
69
  })
71
70
  })
72
71
 
@@ -116,9 +115,9 @@ describe("/api/global/users", () => {
116
115
 
117
116
  it("should ignore users existing in other tenants", async () => {
118
117
  const user = await config.createUser()
119
- jest.resetAllMocks()
118
+ jest.clearAllMocks()
120
119
 
121
- await tenancy.doInTenant(TENANT_1, async () => {
120
+ await tenancy.doInTenant(config.getTenantId(), async () => {
122
121
  const response = await config.api.users.bulkCreateUsers([user])
123
122
 
124
123
  expect(response.created?.successful.length).toBe(0)
@@ -229,9 +228,9 @@ describe("/api/global/users", () => {
229
228
 
230
229
  it("should not be able to create user that exists in other tenant", async () => {
231
230
  const user = await config.createUser()
232
- jest.resetAllMocks()
231
+ jest.clearAllMocks()
233
232
 
234
- await tenancy.doInTenant(TENANT_1, async () => {
233
+ await tenancy.doInTenant(config.getTenantId(), async () => {
235
234
  delete user._id
236
235
  const response = await config.api.users.saveUser(user, 400)
237
236
 
@@ -444,7 +443,7 @@ describe("/api/global/users", () => {
444
443
  })
445
444
 
446
445
  it("should not be able to update email address", async () => {
447
- const email = "email@test.com"
446
+ const email = structures.email()
448
447
  const user = await config.createUser(structures.users.user({ email }))
449
448
  user.email = "new@test.com"
450
449
 
@@ -38,6 +38,13 @@ function buildInviteMultipleValidation() {
38
38
  ))
39
39
  }
40
40
 
41
+ function buildInviteLookupValidation() {
42
+ // prettier-ignore
43
+ return auth.joiValidator.params(Joi.object({
44
+ code: Joi.string().required()
45
+ }).unknown(true))
46
+ }
47
+
41
48
  const createUserAdminOnly = (ctx: any, next: any) => {
42
49
  if (!ctx.request.body._id) {
43
50
  return auth.adminOnly(ctx, next)
@@ -51,6 +58,8 @@ function buildInviteAcceptValidation() {
51
58
  return auth.joiValidator.body(Joi.object({
52
59
  inviteCode: Joi.string().required(),
53
60
  password: Joi.string().required(),
61
+ firstName: Joi.string().required(),
62
+ lastName: Joi.string().optional(),
54
63
  }).required().unknown(true))
55
64
  }
56
65
 
@@ -91,6 +100,11 @@ router
91
100
  )
92
101
 
93
102
  // non-global endpoints
103
+ .get(
104
+ "/api/global/users/invite/:code",
105
+ buildInviteLookupValidation(),
106
+ controller.checkInvite
107
+ )
94
108
  .post(
95
109
  "/api/global/users/invite/accept",
96
110
  buildInviteAcceptValidation(),
@@ -113,4 +127,4 @@ router
113
127
  selfController.updateSelf
114
128
  )
115
129
 
116
- export = router
130
+ export default router
@@ -34,4 +34,4 @@ router
34
34
  .get("/api/global/workspaces", controller.fetch)
35
35
  .get("/api/global/workspaces/:id", controller.find)
36
36
 
37
- export = router
37
+ export default router
@@ -7,6 +7,7 @@ import templateRoutes from "./global/templates"
7
7
  import emailRoutes from "./global/email"
8
8
  import authRoutes from "./global/auth"
9
9
  import roleRoutes from "./global/roles"
10
+ import eventRoutes from "./global/events"
10
11
  import environmentRoutes from "./system/environment"
11
12
  import tenantsRoutes from "./system/tenants"
12
13
  import statusRoutes from "./system/status"
@@ -34,4 +35,5 @@ export const routes: Router[] = [
34
35
  migrationRoutes,
35
36
  accountRoutes,
36
37
  restoreRoutes,
38
+ eventRoutes,
37
39
  ]
@@ -16,4 +16,4 @@ router
16
16
  controller.destroy
17
17
  )
18
18
 
19
- export = router
19
+ export default router
@@ -5,4 +5,4 @@ const router: Router = new Router()
5
5
 
6
6
  router.get("/api/system/environment", controller.fetch)
7
7
 
8
- export = router
8
+ export default router
@@ -16,4 +16,4 @@ router
16
16
  migrationsController.fetchDefinitions
17
17
  )
18
18
 
19
- export = router
19
+ export default router
@@ -5,4 +5,4 @@ const router: Router = new Router()
5
5
 
6
6
  router.post("/api/system/restored", controller.systemRestored)
7
7
 
8
- export = router
8
+ export default router
@@ -5,4 +5,4 @@ const router: Router = new Router()
5
5
 
6
6
  router.get("/api/system/status", controller.fetch)
7
7
 
8
- export = router
8
+ export default router
@@ -10,4 +10,4 @@ router.delete(
10
10
  controller.delete
11
11
  )
12
12
 
13
- export = router
13
+ export default router
@@ -20,7 +20,7 @@ describe("/api/system/environment", () => {
20
20
  const env = await config.api.environment.getEnvironment()
21
21
  expect(env.body).toEqual({
22
22
  cloud: true,
23
- disableAccountPortal: false,
23
+ disableAccountPortal: 0,
24
24
  isDev: false,
25
25
  multiTenancy: true,
26
26
  })
@@ -27,6 +27,14 @@ export enum EmailTemplatePurpose {
27
27
  CUSTOM = "custom",
28
28
  }
29
29
 
30
+ export enum TemplateMetadataNames {
31
+ BASE = "Base format",
32
+ PASSWORD_RECOVERY = "Password recovery",
33
+ WELCOME = "User welcome",
34
+ INVITATION = "User invitation",
35
+ CUSTOM = "Custom",
36
+ }
37
+
30
38
  export enum InternalTemplateBinding {
31
39
  PLATFORM_URL = "platformUrl",
32
40
  COMPANY = "company",
@@ -93,7 +101,7 @@ export const TemplateBindings = {
93
101
  export const TemplateMetadata = {
94
102
  [TemplateType.EMAIL]: [
95
103
  {
96
- name: "Base format",
104
+ name: TemplateMetadataNames.BASE,
97
105
  description:
98
106
  "This is the base template, all others are based on it. The {{ body }} will be replaced with another email template.",
99
107
  category: "miscellaneous",
@@ -110,7 +118,7 @@ export const TemplateMetadata = {
110
118
  ],
111
119
  },
112
120
  {
113
- name: "Password recovery",
121
+ name: TemplateMetadataNames.PASSWORD_RECOVERY,
114
122
  description:
115
123
  "When a user requests a password reset they will receive an email built with this template.",
116
124
  category: "user management",
@@ -129,7 +137,7 @@ export const TemplateMetadata = {
129
137
  ],
130
138
  },
131
139
  {
132
- name: "User welcome",
140
+ name: TemplateMetadataNames.WELCOME,
133
141
  description:
134
142
  "When a new user is added they will be sent a welcome email using this template.",
135
143
  category: "user management",
@@ -137,7 +145,7 @@ export const TemplateMetadata = {
137
145
  bindings: [],
138
146
  },
139
147
  {
140
- name: "User invitation",
148
+ name: TemplateMetadataNames.INVITATION,
141
149
  description:
142
150
  "When inviting a user via the email on-boarding this template will be used.",
143
151
  category: "user management",
@@ -156,7 +164,7 @@ export const TemplateMetadata = {
156
164
  ],
157
165
  },
158
166
  {
159
- name: "Custom",
167
+ name: TemplateMetadataNames.CUSTOM,
160
168
  description:
161
169
  "A custom template, this is currently used for SMTP email actions in automations.",
162
170
  category: "automations",
package/src/db/index.ts CHANGED
@@ -1,4 +1,4 @@
1
- import core from "@budibase/backend-core"
1
+ import * as core from "@budibase/backend-core"
2
2
  import env from "../environment"
3
3
 
4
4
  export const init = () => {
@@ -91,6 +91,11 @@ for (let [key, value] of Object.entries(environment)) {
91
91
  // @ts-ignore
92
92
  environment[key] = 0
93
93
  }
94
+ // handle the edge case of "false" to disable an environment variable
95
+ if (value === "false") {
96
+ // @ts-ignore
97
+ environment[key] = 0
98
+ }
94
99
  }
95
100
 
96
- export = environment
101
+ export default environment
package/src/index.ts CHANGED
@@ -14,7 +14,7 @@ import { Event } from "@sentry/types/dist/event"
14
14
  import Application from "koa"
15
15
  import { bootstrap } from "global-agent"
16
16
  import * as db from "./db"
17
- import { auth, logging, events, pinoSettings } from "@budibase/backend-core"
17
+ import { auth, logging, events, middleware } from "@budibase/backend-core"
18
18
  db.init()
19
19
  import Koa from "koa"
20
20
  import koaBody from "koa-body"
@@ -24,7 +24,7 @@ import * as redis from "./utilities/redis"
24
24
  const Sentry = require("@sentry/node")
25
25
  const koaSession = require("koa-session")
26
26
  const logger = require("koa-pino-logger")
27
- const destroyable = require("server-destroy")
27
+ import destroyable from "server-destroy"
28
28
 
29
29
  // this will setup http and https proxies form env variables
30
30
  bootstrap()
@@ -36,7 +36,8 @@ app.keys = ["secret", "key"]
36
36
  // set up top level koa middleware
37
37
  app.use(koaBody({ multipart: true }))
38
38
  app.use(koaSession(app))
39
- app.use(logger(pinoSettings()))
39
+ app.use(middleware.logging)
40
+ app.use(logger(logging.pinoSettings()))
40
41
 
41
42
  // authentication
42
43
  app.use(auth.passport.initialize())
@@ -79,11 +80,10 @@ server.on("close", async () => {
79
80
 
80
81
  const shutdown = () => {
81
82
  server.close()
82
- // @ts-ignore
83
83
  server.destroy()
84
84
  }
85
85
 
86
- export = server.listen(parseInt(env.PORT || "4002"), async () => {
86
+ export default server.listen(parseInt(env.PORT || "4002"), async () => {
87
87
  console.log(`Worker running on ${JSON.stringify(server.address())}`)
88
88
  await redis.init()
89
89
  })
@@ -6,7 +6,7 @@ import { BBContext } from "@budibase/types"
6
6
  * This is a restricted endpoint in the cloud.
7
7
  * Ensure that the correct API key has been supplied.
8
8
  */
9
- export = async (ctx: BBContext, next: any) => {
9
+ export default async (ctx: BBContext, next: any) => {
10
10
  if (!env.SELF_HOSTED && !env.DISABLE_ACCOUNT_PORTAL) {
11
11
  const apiKey = ctx.request.headers[constants.Header.API_KEY]
12
12
  if (apiKey !== env.INTERNAL_API_KEY) {
@@ -73,6 +73,10 @@ export const handleSaveEvents = async (
73
73
  await events.user.permissionAdminRemoved(user)
74
74
  }
75
75
 
76
+ if (isOnboardingComplete(user, existingUser)) {
77
+ await events.user.onboardingComplete(user)
78
+ }
79
+
76
80
  if (
77
81
  !existingUser.forceResetPassword &&
78
82
  user.forceResetPassword &&
@@ -114,6 +118,10 @@ const isRemovingAdmin = (user: any, existingUser: any) => {
114
118
  return isRemovingPermission(user, existingUser, isAdmin)
115
119
  }
116
120
 
121
+ const isOnboardingComplete = (user: any, existingUser: any) => {
122
+ return !existingUser?.onboardedAt && typeof user.onboardedAt === "string"
123
+ }
124
+
117
125
  /**
118
126
  * Check if a permission is being added to a new or existing user.
119
127
  */
@@ -25,11 +25,13 @@ import {
25
25
  InviteUsersRequest,
26
26
  InviteUsersResponse,
27
27
  MigrationType,
28
+ PlatformUser,
28
29
  PlatformUserByEmail,
29
30
  RowResponse,
30
31
  SearchUsersRequest,
31
32
  User,
32
33
  ThirdPartyUser,
34
+ isUser,
33
35
  } from "@budibase/types"
34
36
  import { sendEmail } from "../../utilities/email"
35
37
  import { EmailTemplatePurpose } from "../../constants"
@@ -153,10 +155,26 @@ const buildUser = async (
153
155
  return fullUser
154
156
  }
155
157
 
158
+ // lookup, could be email or userId, either will return a doc
159
+ export const getPlatformUser = async (
160
+ identifier: string
161
+ ): Promise<PlatformUser | null> => {
162
+ // use the view here and allow to find anyone regardless of casing
163
+ // Use lowercase to ensure email login is case insensitive
164
+ const response = dbUtils.queryPlatformView(
165
+ ViewName.PLATFORM_USERS_LOWERCASE,
166
+ {
167
+ keys: [identifier.toLowerCase()],
168
+ include_docs: true,
169
+ }
170
+ ) as Promise<PlatformUser>
171
+ return response
172
+ }
173
+
156
174
  const validateUniqueUser = async (email: string, tenantId: string) => {
157
175
  // check budibase users in other tenants
158
176
  if (env.MULTI_TENANCY) {
159
- const tenantUser = await tenancy.getTenantUser(email)
177
+ const tenantUser = await getPlatformUser(email)
160
178
  if (tenantUser != null && tenantUser.tenantId !== tenantId) {
161
179
  throw `Unavailable`
162
180
  }
@@ -246,10 +264,10 @@ export const save = async (
246
264
  builtUser._rev = response.rev
247
265
 
248
266
  await eventHelpers.handleSaveEvents(builtUser, dbUser)
249
- await addTenant(tenantId, _id, email)
250
267
  await cache.user.invalidateUser(response.id)
268
+
251
269
  // let server know to sync user
252
- await apps.syncUserInApps(_id)
270
+ await apps.syncUserInApps(_id, dbUser)
253
271
 
254
272
  await Promise.all(groupPromises)
255
273
 
@@ -555,7 +573,7 @@ export const destroy = async (id: string, currentUser: any) => {
555
573
  await cache.user.invalidateUser(userId)
556
574
  await sessions.invalidateSessions(userId, { reason: "deletion" })
557
575
  // let server know to sync user
558
- await apps.syncUserInApps(userId)
576
+ await apps.syncUserInApps(userId, dbUser)
559
577
  }
560
578
 
561
579
  const bulkDeleteProcessing = async (dbUser: User) => {
@@ -565,7 +583,7 @@ const bulkDeleteProcessing = async (dbUser: User) => {
565
583
  await cache.user.invalidateUser(userId)
566
584
  await sessions.invalidateSessions(userId, { reason: "bulk-deletion" })
567
585
  // let server know to sync user
568
- await apps.syncUserInApps(userId)
586
+ await apps.syncUserInApps(userId, dbUser)
569
587
  }
570
588
 
571
589
  export const invite = async (
@@ -1,4 +1,12 @@
1
- import "./mocks"
1
+ import mocks from "./mocks"
2
+
3
+ // init the licensing mock
4
+ import * as pro from "@budibase/pro"
5
+ mocks.licenses.init(pro)
6
+
7
+ // use unlimited license by default
8
+ mocks.licenses.useUnlimited()
9
+
2
10
  import * as dbConfig from "../db"
3
11
  dbConfig.init()
4
12
  import env from "../environment"
@@ -12,22 +20,44 @@ import {
12
20
  auth,
13
21
  constants,
14
22
  env as coreEnv,
23
+ DEFAULT_TENANT_ID,
15
24
  } from "@budibase/backend-core"
16
- import structures, { TENANT_ID, TENANT_1, CSRF_TOKEN } from "./structures"
25
+ import structures, { TENANT_ID, CSRF_TOKEN } from "./structures"
17
26
  import { CreateUserResponse, User, AuthToken } from "@budibase/types"
18
27
  import API from "./api"
28
+ import sdk from "../sdk"
19
29
 
20
30
  enum Mode {
21
31
  CLOUD = "cloud",
22
32
  SELF = "self",
23
33
  }
24
34
 
35
+ async function retry<T extends (...arg0: any[]) => any>(
36
+ fn: T,
37
+ maxTry: number = 5,
38
+ retryCount = 1
39
+ ): Promise<Awaited<ReturnType<T>>> {
40
+ const currRetry = typeof retryCount === "number" ? retryCount : 1
41
+ try {
42
+ const result = await fn()
43
+ return result
44
+ } catch (e) {
45
+ console.log(`Retry ${currRetry} failed.`)
46
+ if (currRetry > maxTry) {
47
+ console.log(`All ${maxTry} retry attempts exhausted`)
48
+ throw e
49
+ }
50
+ return retry(fn, maxTry, currRetry + 1)
51
+ }
52
+ }
53
+
25
54
  class TestConfiguration {
26
55
  server: any
27
56
  request: any
28
57
  api: API
29
58
  defaultUser?: User
30
59
  tenant1User?: User
60
+ #tenantId?: string
31
61
 
32
62
  constructor(
33
63
  opts: { openServer: boolean; mode: Mode } = {
@@ -43,7 +73,7 @@ class TestConfiguration {
43
73
 
44
74
  if (opts.openServer) {
45
75
  env.PORT = "0" // random port
46
- this.server = require("../index")
76
+ this.server = require("../index").default
47
77
  // we need the request for logging in, involves cookies, hard to fake
48
78
  this.request = supertest(this.server)
49
79
  }
@@ -104,13 +134,21 @@ class TestConfiguration {
104
134
  // SETUP / TEARDOWN
105
135
 
106
136
  async beforeAll() {
107
- await this.createDefaultUser()
108
- await this.createSession(this.defaultUser!)
137
+ try {
138
+ this.#tenantId = structures.tenant.id()
109
139
 
110
- await tenancy.doInTenant(TENANT_1, async () => {
111
- await this.createTenant1User()
112
- await this.createSession(this.tenant1User!)
113
- })
140
+ // Running tests in parallel causes issues creating the globaldb twice. This ensures the db is properly created before starting
141
+ await retry(async () => await this.createDefaultUser())
142
+ await this.createSession(this.defaultUser!)
143
+
144
+ await tenancy.doInTenant(this.#tenantId, async () => {
145
+ await this.createTenant1User()
146
+ await this.createSession(this.tenant1User!)
147
+ })
148
+ } catch (e: any) {
149
+ console.log(e)
150
+ throw new Error(e.message)
151
+ }
114
152
  }
115
153
 
116
154
  async afterAll() {
@@ -125,6 +163,8 @@ class TestConfiguration {
125
163
  // create user / new tenant
126
164
  const res = await this.api.users.createAdminUser()
127
165
 
166
+ await sdk.users.addTenant(res.tenantId, res.userId, res.email)
167
+
128
168
  // return the created user
129
169
  const userRes = await this.api.users.getUser(res.userId, {
130
170
  headers: {
@@ -144,7 +184,7 @@ class TestConfiguration {
144
184
  try {
145
185
  return tenancy.getTenantId()
146
186
  } catch (e: any) {
147
- return TENANT_ID
187
+ return DEFAULT_TENANT_ID
148
188
  }
149
189
  }
150
190
 
@@ -195,7 +235,7 @@ class TestConfiguration {
195
235
  const tenantId = this.getTenantId()
196
236
  if (tenantId === TENANT_ID) {
197
237
  return this.authHeaders(this.defaultUser!)
198
- } else if (tenantId === TENANT_1) {
238
+ } else if (tenantId === this.getTenantId()) {
199
239
  return this.authHeaders(this.tenant1User!)
200
240
  } else {
201
241
  throw new Error("could not determine auth headers to use")
@@ -214,7 +254,6 @@ class TestConfiguration {
214
254
 
215
255
  async createDefaultUser() {
216
256
  const user = structures.users.adminUser({
217
- email: "test@test.com",
218
257
  password: "test",
219
258
  })
220
259
  this.defaultUser = await this.createUser(user)
@@ -222,7 +261,6 @@ class TestConfiguration {
222
261
 
223
262
  async createTenant1User() {
224
263
  const user = structures.users.adminUser({
225
- email: "tenant1@test.com",
226
264
  password: "test",
227
265
  })
228
266
  this.tenant1User = await this.createUser(user)
@@ -319,4 +357,4 @@ class TestConfiguration {
319
357
  }
320
358
  }
321
359
 
322
- export = TestConfiguration
360
+ export default TestConfiguration
@@ -1,3 +1,4 @@
1
+ import structures from "../structures"
1
2
  import TestConfiguration from "../TestConfiguration"
2
3
  import { TestAPI } from "./base"
3
4
 
@@ -24,14 +25,17 @@ export class AuthAPI extends TestAPI {
24
25
  .expect(200)
25
26
  }
26
27
 
27
- requestPasswordReset = async (sendMailMock: any) => {
28
+ requestPasswordReset = async (sendMailMock: any, userEmail: string) => {
28
29
  await this.config.saveSmtpConfig()
29
30
  await this.config.saveSettingsConfig()
30
- await this.config.createUser()
31
+ await this.config.createUser({
32
+ ...structures.users.user(),
33
+ email: userEmail,
34
+ })
31
35
  const res = await this.request
32
36
  .post(`/api/global/auth/${this.config.getTenantId()}/reset`)
33
37
  .send({
34
- email: "test@test.com",
38
+ email: userEmail,
35
39
  })
36
40
  .expect("Content-Type", /json/)
37
41
  .expect(200)
@@ -10,8 +10,8 @@ export class ConfigAPI extends TestAPI {
10
10
  return this.request
11
11
  .get(`/api/global/configs/checklist`)
12
12
  .set(this.config.defaultHeaders())
13
- .expect("Content-Type", /json/)
14
13
  .expect(200)
14
+ .expect("Content-Type", /json/)
15
15
  }
16
16
 
17
17
  saveConfig = (data: any) => {
@@ -19,8 +19,8 @@ export class ConfigAPI extends TestAPI {
19
19
  .post(`/api/global/configs`)
20
20
  .send(data)
21
21
  .set(this.config.defaultHeaders())
22
- .expect("Content-Type", /json/)
23
22
  .expect(200)
23
+ .expect("Content-Type", /json/)
24
24
  }
25
25
 
26
26
  OIDCCallback = (configId: string, preAuthRes: any) => {
@@ -0,0 +1,26 @@
1
+ import { UserGroup } from "@budibase/types"
2
+ import TestConfiguration from "../TestConfiguration"
3
+ import { TestAPI } from "./base"
4
+
5
+ export class GroupsAPI extends TestAPI {
6
+ constructor(config: TestConfiguration) {
7
+ super(config)
8
+ }
9
+
10
+ saveGroup = (group: UserGroup) => {
11
+ return this.request
12
+ .post(`/api/global/groups`)
13
+ .send(group)
14
+ .set(this.config.defaultHeaders())
15
+ .expect("Content-Type", /json/)
16
+ .expect(200)
17
+ }
18
+
19
+ deleteGroup = (id: string, rev: string) => {
20
+ return this.request
21
+ .delete(`/api/global/groups/${id}/${rev}`)
22
+ .set(this.config.defaultHeaders())
23
+ .expect("Content-Type", /json/)
24
+ .expect(200)
25
+ }
26
+ }