@budibase/worker 2.2.26 → 2.2.27-alpha.0

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 (166) hide show
  1. package/Dockerfile +1 -1
  2. package/coverage/clover.xml +1393 -1364
  3. package/coverage/coverage-final.json +83 -77
  4. package/coverage/lcov-report/index.html +133 -133
  5. package/coverage/lcov-report/src/api/controllers/global/auth.ts.html +52 -52
  6. package/coverage/lcov-report/src/api/controllers/global/configs.ts.html +161 -218
  7. package/coverage/lcov-report/src/api/controllers/global/email.ts.html +4 -4
  8. package/coverage/lcov-report/src/api/controllers/global/events.ts.html +136 -0
  9. package/coverage/lcov-report/src/api/controllers/global/index.html +85 -70
  10. package/coverage/lcov-report/src/api/controllers/global/license.ts.html +16 -16
  11. package/coverage/lcov-report/src/api/controllers/global/roles.ts.html +77 -77
  12. package/coverage/lcov-report/src/api/controllers/global/self.ts.html +20 -20
  13. package/coverage/lcov-report/src/api/controllers/global/templates.ts.html +54 -54
  14. package/coverage/lcov-report/src/api/controllers/global/users.ts.html +82 -43
  15. package/coverage/lcov-report/src/api/controllers/global/workspaces.ts.html +9 -9
  16. package/coverage/lcov-report/src/api/controllers/system/accounts.ts.html +5 -5
  17. package/coverage/lcov-report/src/api/controllers/system/environment.ts.html +4 -4
  18. package/coverage/lcov-report/src/api/controllers/system/index.html +11 -11
  19. package/coverage/lcov-report/src/api/controllers/system/migrations.ts.html +5 -5
  20. package/coverage/lcov-report/src/api/controllers/system/restore.ts.html +4 -4
  21. package/coverage/lcov-report/src/api/controllers/system/status.ts.html +5 -5
  22. package/coverage/lcov-report/src/api/controllers/system/tenants.ts.html +7 -7
  23. package/coverage/lcov-report/src/api/index.html +1 -1
  24. package/coverage/lcov-report/src/api/index.ts.html +37 -25
  25. package/coverage/lcov-report/src/api/routes/global/auth.ts.html +14 -14
  26. package/coverage/lcov-report/src/api/routes/global/configs.ts.html +25 -25
  27. package/coverage/lcov-report/src/api/routes/global/email.ts.html +13 -13
  28. package/coverage/lcov-report/src/api/routes/global/events.ts.html +109 -0
  29. package/coverage/lcov-report/src/api/routes/global/index.html +37 -22
  30. package/coverage/lcov-report/src/api/routes/global/license.ts.html +9 -9
  31. package/coverage/lcov-report/src/api/routes/global/roles.ts.html +10 -10
  32. package/coverage/lcov-report/src/api/routes/global/self.ts.html +11 -11
  33. package/coverage/lcov-report/src/api/routes/global/templates.ts.html +14 -14
  34. package/coverage/lcov-report/src/api/routes/global/users.ts.html +62 -20
  35. package/coverage/lcov-report/src/api/routes/global/workspaces.ts.html +12 -12
  36. package/coverage/lcov-report/src/api/routes/index.html +7 -7
  37. package/coverage/lcov-report/src/api/routes/index.ts.html +29 -23
  38. package/coverage/lcov-report/src/api/routes/system/accounts.ts.html +10 -10
  39. package/coverage/lcov-report/src/api/routes/system/environment.ts.html +9 -9
  40. package/coverage/lcov-report/src/api/routes/system/index.html +15 -15
  41. package/coverage/lcov-report/src/api/routes/system/migrations.ts.html +10 -10
  42. package/coverage/lcov-report/src/api/routes/system/restore.ts.html +9 -9
  43. package/coverage/lcov-report/src/api/routes/system/status.ts.html +9 -9
  44. package/coverage/lcov-report/src/api/routes/system/tenants.ts.html +10 -10
  45. package/coverage/lcov-report/src/api/routes/validation/index.html +3 -3
  46. package/coverage/lcov-report/src/api/routes/validation/index.ts.html +2 -2
  47. package/coverage/lcov-report/src/api/routes/validation/users.ts.html +14 -14
  48. package/coverage/lcov-report/src/constants/index.html +17 -17
  49. package/coverage/lcov-report/src/constants/index.ts.html +77 -53
  50. package/coverage/lcov-report/src/constants/templates/index.html +19 -19
  51. package/coverage/lcov-report/src/constants/templates/index.ts.html +42 -42
  52. package/coverage/lcov-report/src/db/index.html +3 -3
  53. package/coverage/lcov-report/src/db/index.ts.html +10 -10
  54. package/coverage/lcov-report/src/environment.ts.html +44 -29
  55. package/coverage/lcov-report/src/index.html +27 -27
  56. package/coverage/lcov-report/src/index.ts.html +55 -55
  57. package/coverage/lcov-report/src/middleware/cloudRestricted.ts.html +5 -5
  58. package/coverage/lcov-report/src/middleware/index.html +1 -1
  59. package/coverage/lcov-report/src/migrations/functions/globalInfoSyncUsers.ts.html +9 -9
  60. package/coverage/lcov-report/src/migrations/functions/index.html +11 -11
  61. package/coverage/lcov-report/src/migrations/index.html +15 -15
  62. package/coverage/lcov-report/src/migrations/index.ts.html +27 -27
  63. package/coverage/lcov-report/src/sdk/accounts/accounts.ts.html +13 -13
  64. package/coverage/lcov-report/src/sdk/accounts/index.html +13 -13
  65. package/coverage/lcov-report/src/sdk/accounts/index.ts.html +2 -2
  66. package/coverage/lcov-report/src/sdk/index.html +5 -5
  67. package/coverage/lcov-report/src/sdk/index.ts.html +6 -6
  68. package/coverage/lcov-report/src/sdk/users/events.ts.html +82 -58
  69. package/coverage/lcov-report/src/sdk/users/index.html +29 -29
  70. package/coverage/lcov-report/src/sdk/users/index.ts.html +2 -2
  71. package/coverage/lcov-report/src/sdk/users/users.ts.html +176 -119
  72. package/coverage/lcov-report/src/tests/TestConfiguration.ts.html +119 -95
  73. package/coverage/lcov-report/src/tests/api/accounts.ts.html +7 -7
  74. package/coverage/lcov-report/src/tests/api/auth.ts.html +8 -8
  75. package/coverage/lcov-report/src/tests/api/base.ts.html +5 -5
  76. package/coverage/lcov-report/src/tests/api/configs.ts.html +9 -9
  77. package/coverage/lcov-report/src/tests/api/email.ts.html +6 -6
  78. package/coverage/lcov-report/src/tests/api/environment.ts.html +6 -6
  79. package/coverage/lcov-report/src/tests/api/groups.ts.html +163 -0
  80. package/coverage/lcov-report/src/tests/api/index.html +91 -31
  81. package/coverage/lcov-report/src/tests/api/index.ts.html +62 -29
  82. package/coverage/lcov-report/src/tests/api/license.ts.html +136 -0
  83. package/coverage/lcov-report/src/tests/api/migrations.ts.html +8 -8
  84. package/coverage/lcov-report/src/tests/api/restore.ts.html +7 -7
  85. package/coverage/lcov-report/src/tests/api/roles.ts.html +181 -0
  86. package/coverage/lcov-report/src/tests/api/self.ts.html +7 -7
  87. package/coverage/lcov-report/src/tests/api/status.ts.html +6 -6
  88. package/coverage/lcov-report/src/tests/api/templates.ts.html +175 -0
  89. package/coverage/lcov-report/src/tests/api/tenants.ts.html +10 -10
  90. package/coverage/lcov-report/src/tests/api/users.ts.html +28 -25
  91. package/coverage/lcov-report/src/tests/controllers.ts.html +14 -14
  92. package/coverage/lcov-report/src/tests/index.html +34 -34
  93. package/coverage/lcov-report/src/tests/index.ts.html +19 -55
  94. package/coverage/lcov-report/src/tests/mocks/{email.js.html → email.ts.html} +5 -5
  95. package/coverage/lcov-report/src/tests/mocks/index.html +6 -6
  96. package/coverage/lcov-report/src/tests/mocks/index.ts.html +10 -10
  97. package/coverage/lcov-report/src/tests/structures/configs.ts.html +11 -11
  98. package/coverage/lcov-report/src/tests/structures/groups.ts.html +15 -15
  99. package/coverage/lcov-report/src/tests/structures/index.html +25 -25
  100. package/coverage/lcov-report/src/tests/structures/index.ts.html +17 -17
  101. package/coverage/lcov-report/src/tests/structures/users.ts.html +16 -16
  102. package/coverage/lcov-report/src/utilities/appService.ts.html +45 -18
  103. package/coverage/lcov-report/src/utilities/email.ts.html +28 -28
  104. package/coverage/lcov-report/src/utilities/fileSystem.ts.html +4 -4
  105. package/coverage/lcov-report/src/utilities/index.html +27 -27
  106. package/coverage/lcov-report/src/utilities/index.ts.html +2 -2
  107. package/coverage/lcov-report/src/utilities/redis.ts.html +20 -20
  108. package/coverage/lcov-report/src/utilities/templates.ts.html +6 -6
  109. package/coverage/lcov-report/src/utilities/users.ts.html +3 -3
  110. package/coverage/lcov.info +2777 -2582
  111. package/jest.config.ts +7 -2
  112. package/package.json +10 -7
  113. package/scripts/dev/manage.js +2 -2
  114. package/src/api/controllers/global/auth.ts +1 -1
  115. package/src/api/controllers/global/configs.ts +112 -131
  116. package/src/api/controllers/global/events.ts +17 -0
  117. package/src/api/controllers/global/users.ts +14 -1
  118. package/src/api/index.ts +4 -0
  119. package/src/api/routes/global/auth.ts +1 -1
  120. package/src/api/routes/global/configs.ts +7 -7
  121. package/src/api/routes/global/email.ts +1 -1
  122. package/src/api/routes/global/events.ts +8 -0
  123. package/src/api/routes/global/license.ts +1 -1
  124. package/src/api/routes/global/roles.ts +1 -1
  125. package/src/api/routes/global/self.ts +1 -1
  126. package/src/api/routes/global/templates.ts +1 -1
  127. package/src/api/routes/global/tests/groups.spec.ts +58 -0
  128. package/src/api/routes/global/tests/license.spec.ts +0 -2
  129. package/src/api/routes/global/tests/roles.spec.ts +66 -5
  130. package/src/api/routes/global/tests/templates.spec.ts +81 -9
  131. package/src/api/routes/global/tests/users.spec.ts +2 -2
  132. package/src/api/routes/global/users.ts +15 -1
  133. package/src/api/routes/global/workspaces.ts +1 -1
  134. package/src/api/routes/index.ts +2 -0
  135. package/src/api/routes/system/accounts.ts +1 -1
  136. package/src/api/routes/system/environment.ts +1 -1
  137. package/src/api/routes/system/migrations.ts +1 -1
  138. package/src/api/routes/system/restore.ts +1 -1
  139. package/src/api/routes/system/status.ts +1 -1
  140. package/src/api/routes/system/tenants.ts +1 -1
  141. package/src/api/routes/system/tests/environment.spec.ts +1 -1
  142. package/src/constants/index.ts +13 -5
  143. package/src/db/index.ts +1 -1
  144. package/src/environment.ts +6 -1
  145. package/src/index.ts +5 -5
  146. package/src/middleware/cloudRestricted.ts +1 -1
  147. package/src/sdk/users/events.ts +8 -0
  148. package/src/sdk/users/users.ts +23 -4
  149. package/src/tests/TestConfiguration.ts +11 -3
  150. package/src/tests/api/groups.ts +26 -0
  151. package/src/tests/api/index.ts +12 -1
  152. package/src/tests/api/license.ts +17 -0
  153. package/src/tests/api/roles.ts +32 -0
  154. package/src/tests/api/templates.ts +30 -0
  155. package/src/tests/api/users.ts +2 -1
  156. package/src/tests/index.ts +6 -18
  157. package/src/tests/jestEnv.ts +11 -0
  158. package/src/tests/jestSetup.ts +3 -15
  159. package/src/tests/mocks/{email.js → email.ts} +1 -1
  160. package/src/tests/mocks/index.ts +4 -4
  161. package/src/tests/structures/configs.ts +2 -2
  162. package/src/tests/structures/groups.ts +1 -1
  163. package/src/tests/structures/index.ts +4 -4
  164. package/src/tests/structures/users.ts +1 -1
  165. package/src/utilities/appService.ts +12 -3
  166. package/tsconfig.json +2 -2
@@ -1,7 +1,5 @@
1
1
  import { TestConfiguration } from "../../../../tests"
2
2
 
3
- // TODO
4
-
5
3
  describe("/api/global/license", () => {
6
4
  const config = new TestConfiguration()
7
5
 
@@ -1,11 +1,47 @@
1
- import { TestConfiguration } from "../../../../tests"
1
+ import { structures, TestConfiguration } from "../../../../tests"
2
+ import { context, db, permissions, roles } from "@budibase/backend-core"
3
+ import { Mock } from "jest-mock"
2
4
 
3
- // TODO
5
+ jest.mock("@budibase/backend-core", () => {
6
+ const core = jest.requireActual("@budibase/backend-core")
7
+ return {
8
+ ...core,
9
+ db: {
10
+ ...core.db,
11
+ },
12
+ context: {
13
+ ...core.context,
14
+ getAppDB: jest.fn(),
15
+ },
16
+ }
17
+ })
18
+
19
+ const appDb = db.getDB("app_test")
20
+ const mockAppDB = context.getAppDB as Mock
21
+ mockAppDB.mockReturnValue(appDb)
22
+
23
+ async function addAppMetadata() {
24
+ await appDb.put({
25
+ _id: "app_metadata",
26
+ appId: "app_test",
27
+ name: "New App",
28
+ version: "version",
29
+ url: "url",
30
+ })
31
+ }
4
32
 
5
33
  describe("/api/global/roles", () => {
6
34
  const config = new TestConfiguration()
35
+ const role = new roles.Role(
36
+ db.generateRoleID("newRole"),
37
+ roles.BUILTIN_ROLE_IDS.BASIC,
38
+ permissions.BuiltinPermissionID.READ_ONLY
39
+ )
7
40
 
8
41
  beforeAll(async () => {
42
+ console.debug(role)
43
+ appDb.put(role)
44
+ await addAppMetadata()
9
45
  await config.beforeAll()
10
46
  })
11
47
 
@@ -18,10 +54,35 @@ describe("/api/global/roles", () => {
18
54
  })
19
55
 
20
56
  describe("GET /api/global/roles", () => {
21
- it("retrieves roles", () => {})
57
+ it("retrieves roles", async () => {
58
+ const res = await config.api.roles.get()
59
+ expect(res.body).toBeDefined()
60
+ expect(res.body["app_test"].roles.length).toEqual(5)
61
+ expect(res.body["app_test"].roles.map((r: any) => r._id)).toContain(
62
+ role._id
63
+ )
64
+ })
22
65
  })
23
66
 
24
- describe("GET /api/global/roles/:appId", () => {})
67
+ describe("GET api/global/roles/:appId", () => {
68
+ it("finds a role by appId", async () => {
69
+ const res = await config.api.roles.find("app_test")
70
+ expect(res.body).toBeDefined()
71
+ expect(res.body.name).toEqual("New App")
72
+ })
73
+ })
25
74
 
26
- describe("DELETE /api/global/roles/:appId", () => {})
75
+ describe("DELETE /api/global/roles/:appId", () => {
76
+ it("removes an app role", async () => {
77
+ let user = structures.users.user()
78
+ user.roles = {
79
+ app_test: "role1",
80
+ }
81
+ const userResponse = await config.createUser(user)
82
+ const res = await config.api.roles.remove("app_test")
83
+ const updatedUser = await config.api.users.getUser(userResponse._id!)
84
+ expect(updatedUser.body.roles).not.toHaveProperty("app_test")
85
+ expect(res.body.message).toEqual("App role removed from all users")
86
+ })
87
+ })
27
88
  })
@@ -1,3 +1,8 @@
1
+ import {
2
+ EmailTemplatePurpose,
3
+ TemplateMetadata,
4
+ TemplateType,
5
+ } from "../../../../constants"
1
6
  import { TestConfiguration } from "../../../../tests"
2
7
 
3
8
  // TODO
@@ -18,18 +23,85 @@ describe("/api/global/template", () => {
18
23
  })
19
24
 
20
25
  describe("GET /api/global/template/definitions", () => {
21
- it("retrieves definitions", () => {})
22
- })
23
-
24
- describe("POST /api/global/template", () => {})
26
+ describe("retrieves definitions", () => {
27
+ it("checks description definitions", async () => {
28
+ let result = await config.api.templates.definitions()
25
29
 
26
- describe("GET /api/global/template", () => {})
30
+ expect(result.body.info[EmailTemplatePurpose.BASE].description).toEqual(
31
+ TemplateMetadata[TemplateType.EMAIL][0].description
32
+ )
33
+ expect(
34
+ result.body.info[EmailTemplatePurpose.PASSWORD_RECOVERY].description
35
+ ).toEqual(TemplateMetadata[TemplateType.EMAIL][1].description)
36
+ expect(
37
+ result.body.info[EmailTemplatePurpose.WELCOME].description
38
+ ).toEqual(TemplateMetadata[TemplateType.EMAIL][2].description)
39
+ expect(
40
+ result.body.info[EmailTemplatePurpose.INVITATION].description
41
+ ).toEqual(TemplateMetadata[TemplateType.EMAIL][3].description)
42
+ expect(
43
+ result.body.info[EmailTemplatePurpose.CUSTOM].description
44
+ ).toEqual(TemplateMetadata[TemplateType.EMAIL][4].description)
45
+ })
27
46
 
28
- describe("GET /api/global/template/:type", () => {})
47
+ it("checks description bindings", async () => {
48
+ let result = await config.api.templates.definitions()
29
49
 
30
- describe("GET /api/global/template/:ownerId", () => {})
50
+ expect(result.body.bindings[EmailTemplatePurpose.BASE]).toEqual(
51
+ TemplateMetadata[TemplateType.EMAIL][0].bindings
52
+ )
53
+ expect(
54
+ result.body.bindings[EmailTemplatePurpose.PASSWORD_RECOVERY]
55
+ ).toEqual(TemplateMetadata[TemplateType.EMAIL][1].bindings)
56
+ expect(result.body.bindings[EmailTemplatePurpose.WELCOME]).toEqual(
57
+ TemplateMetadata[TemplateType.EMAIL][2].bindings
58
+ )
59
+ expect(result.body.bindings[EmailTemplatePurpose.INVITATION]).toEqual(
60
+ TemplateMetadata[TemplateType.EMAIL][3].bindings
61
+ )
62
+ expect(result.body.bindings[EmailTemplatePurpose.CUSTOM]).toEqual(
63
+ TemplateMetadata[TemplateType.EMAIL][4].bindings
64
+ )
65
+ })
66
+ })
67
+ })
31
68
 
32
- describe("GET /api/global/template/:id", () => {})
69
+ describe("POST /api/global/template", () => {
70
+ it("adds a new template", async () => {
71
+ let purpose = "base"
72
+ let contents = "Test contents"
73
+ let updatedTemplate = {
74
+ contents: contents,
75
+ purpose: purpose,
76
+ type: "email",
77
+ }
78
+ await config.api.templates.saveTemplate(updatedTemplate)
79
+ let res = await config.api.templates.getTemplate()
80
+ let newTemplate = res.body.find((t: any) => (t.purpose = purpose))
81
+ expect(newTemplate.contents).toEqual(contents)
82
+ })
83
+ })
33
84
 
34
- describe("DELETE /api/global/template/:id/:rev", () => {})
85
+ describe("GET /api/global/template", () => {
86
+ it("fetches templates", async () => {
87
+ let res = await config.api.templates.getTemplate()
88
+ expect(
89
+ res.body.find((t: any) => t.purpose === EmailTemplatePurpose.BASE)
90
+ ).toBeDefined()
91
+ expect(
92
+ res.body.find((t: any) => t.purpose === EmailTemplatePurpose.CUSTOM)
93
+ ).toBeDefined()
94
+ expect(
95
+ res.body.find((t: any) => t.purpose === EmailTemplatePurpose.INVITATION)
96
+ ).toBeDefined()
97
+ expect(
98
+ res.body.find(
99
+ (t: any) => t.purpose === EmailTemplatePurpose.PASSWORD_RECOVERY
100
+ )
101
+ ).toBeDefined()
102
+ expect(
103
+ res.body.find((t: any) => t.purpose === EmailTemplatePurpose.WELCOME)
104
+ ).toBeDefined()
105
+ })
106
+ })
35
107
  })
@@ -116,7 +116,7 @@ describe("/api/global/users", () => {
116
116
 
117
117
  it("should ignore users existing in other tenants", async () => {
118
118
  const user = await config.createUser()
119
- jest.resetAllMocks()
119
+ jest.clearAllMocks()
120
120
 
121
121
  await tenancy.doInTenant(TENANT_1, async () => {
122
122
  const response = await config.api.users.bulkCreateUsers([user])
@@ -229,7 +229,7 @@ describe("/api/global/users", () => {
229
229
 
230
230
  it("should not be able to create user that exists in other tenant", async () => {
231
231
  const user = await config.createUser()
232
- jest.resetAllMocks()
232
+ jest.clearAllMocks()
233
233
 
234
234
  await tenancy.doInTenant(TENANT_1, async () => {
235
235
  delete user._id
@@ -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
  }
@@ -248,8 +266,9 @@ export const save = async (
248
266
  await eventHelpers.handleSaveEvents(builtUser, dbUser)
249
267
  await addTenant(tenantId, _id, email)
250
268
  await cache.user.invalidateUser(response.id)
269
+
251
270
  // let server know to sync user
252
- await apps.syncUserInApps(_id)
271
+ await apps.syncUserInApps(_id, dbUser)
253
272
 
254
273
  await Promise.all(groupPromises)
255
274
 
@@ -555,7 +574,7 @@ export const destroy = async (id: string, currentUser: any) => {
555
574
  await cache.user.invalidateUser(userId)
556
575
  await sessions.invalidateSessions(userId, { reason: "deletion" })
557
576
  // let server know to sync user
558
- await apps.syncUserInApps(userId)
577
+ await apps.syncUserInApps(userId, dbUser)
559
578
  }
560
579
 
561
580
  const bulkDeleteProcessing = async (dbUser: User) => {
@@ -565,7 +584,7 @@ const bulkDeleteProcessing = async (dbUser: User) => {
565
584
  await cache.user.invalidateUser(userId)
566
585
  await sessions.invalidateSessions(userId, { reason: "bulk-deletion" })
567
586
  // let server know to sync user
568
- await apps.syncUserInApps(userId)
587
+ await apps.syncUserInApps(userId, dbUser)
569
588
  }
570
589
 
571
590
  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"
@@ -43,7 +51,7 @@ class TestConfiguration {
43
51
 
44
52
  if (opts.openServer) {
45
53
  env.PORT = "0" // random port
46
- this.server = require("../index")
54
+ this.server = require("../index").default
47
55
  // we need the request for logging in, involves cookies, hard to fake
48
56
  this.request = supertest(this.server)
49
57
  }
@@ -319,4 +327,4 @@ class TestConfiguration {
319
327
  }
320
328
  }
321
329
 
322
- export = TestConfiguration
330
+ export default TestConfiguration
@@ -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
+ }
@@ -10,7 +10,10 @@ import { MigrationAPI } from "./migrations"
10
10
  import { StatusAPI } from "./status"
11
11
  import { RestoreAPI } from "./restore"
12
12
  import { TenantAPI } from "./tenants"
13
-
13
+ import { GroupsAPI } from "./groups"
14
+ import { RolesAPI } from "./roles"
15
+ import { TemplatesAPI } from "./templates"
16
+ import { LicenseAPI } from "./license"
14
17
  export default class API {
15
18
  accounts: AccountAPI
16
19
  auth: AuthAPI
@@ -23,6 +26,10 @@ export default class API {
23
26
  status: StatusAPI
24
27
  restore: RestoreAPI
25
28
  tenants: TenantAPI
29
+ groups: GroupsAPI
30
+ roles: RolesAPI
31
+ templates: TemplatesAPI
32
+ license: LicenseAPI
26
33
 
27
34
  constructor(config: TestConfiguration) {
28
35
  this.accounts = new AccountAPI(config)
@@ -36,5 +43,9 @@ export default class API {
36
43
  this.status = new StatusAPI(config)
37
44
  this.restore = new RestoreAPI(config)
38
45
  this.tenants = new TenantAPI(config)
46
+ this.groups = new GroupsAPI(config)
47
+ this.roles = new RolesAPI(config)
48
+ this.templates = new TemplatesAPI(config)
49
+ this.license = new LicenseAPI(config)
39
50
  }
40
51
  }
@@ -0,0 +1,17 @@
1
+ import TestConfiguration from "../TestConfiguration"
2
+ import { TestAPI } from "./base"
3
+
4
+ export class LicenseAPI extends TestAPI {
5
+ constructor(config: TestConfiguration) {
6
+ super(config)
7
+ }
8
+
9
+ activate = async (licenseKey: string) => {
10
+ return this.request
11
+ .post("/api/global/license/activate")
12
+ .send({ licenseKey: licenseKey })
13
+ .set(this.config.defaultHeaders())
14
+ .expect("Content-Type", /json/)
15
+ .expect(200)
16
+ }
17
+ }