@budibase/worker 1.0.199 → 1.0.200-alpha.2

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 (40) hide show
  1. package/Dockerfile +3 -0
  2. package/nodemon.json +1 -1
  3. package/package.json +56 -46
  4. package/scripts/dev/manage.js +2 -0
  5. package/scripts/jestSetup.js +9 -0
  6. package/scripts/load/users.js +4 -4
  7. package/src/api/controllers/global/auth.ts +21 -14
  8. package/src/api/controllers/global/configs.js +126 -2
  9. package/src/api/controllers/global/roles.js +3 -3
  10. package/src/api/controllers/global/self.js +12 -3
  11. package/src/api/controllers/global/users.ts +48 -88
  12. package/src/api/routes/global/self.js +2 -2
  13. package/src/api/routes/global/users.js +3 -3
  14. package/src/api/routes/tests/auth.spec.js +38 -16
  15. package/src/api/routes/tests/configs.spec.js +223 -7
  16. package/src/api/routes/tests/email.spec.js +8 -7
  17. package/src/api/routes/tests/realEmail.spec.js +5 -5
  18. package/src/api/routes/tests/self.spec.js +58 -0
  19. package/src/api/routes/tests/users.spec.js +299 -13
  20. package/src/api/routes/validation/index.ts +1 -0
  21. package/src/api/{utilities/validation.js → routes/validation/users.ts} +4 -4
  22. package/src/index.ts +2 -0
  23. package/src/sdk/index.ts +1 -0
  24. package/src/sdk/users/events.ts +161 -0
  25. package/src/sdk/users/index.ts +1 -0
  26. package/src/sdk/users/users.ts +201 -0
  27. package/src/{api/routes/tests/utilities → tests}/TestConfiguration.js +68 -111
  28. package/src/tests/controllers.js +7 -0
  29. package/src/tests/index.js +12 -0
  30. package/src/tests/mocks/email.js +10 -0
  31. package/src/tests/mocks/index.js +5 -0
  32. package/src/tests/structures/configs.js +76 -0
  33. package/src/tests/structures/index.js +12 -0
  34. package/src/tests/structures/users.ts +28 -0
  35. package/tsconfig.build.json +25 -0
  36. package/tsconfig.json +21 -15
  37. package/src/api/routes/tests/utilities/controllers.js +0 -7
  38. package/src/api/routes/tests/utilities/index.js +0 -41
  39. package/src/api/routes/tests/utilities/structures.js +0 -2
  40. package/src/api/utilities/index.js +0 -33
@@ -1,35 +1,22 @@
1
- const {
2
- getGlobalUserParams,
3
- StaticDatabases,
4
- } = require("@budibase/backend-core/db")
5
- const { getGlobalUserByEmail } = require("@budibase/backend-core/utils")
6
- const { user: userCache } = require("@budibase/backend-core/cache")
7
- const { invalidateSessions } = require("@budibase/backend-core/sessions")
8
- const accounts = require("@budibase/backend-core/accounts")
9
- const {
10
- getGlobalDB,
11
- doWithGlobalDB,
12
- getTenantId,
13
- getTenantUser,
14
- doesTenantExist,
15
- doInTenant,
16
- } = require("@budibase/backend-core/tenancy")
17
- const { removeUserFromInfoDB } = require("@budibase/backend-core/deprovision")
18
- const { errors } = require("@budibase/backend-core")
19
- const { CacheKeys, bustCache } = require("@budibase/backend-core/cache")
20
- import env from "../../../environment"
21
- import { syncUserInApps } from "../../../utilities/appService"
22
- import { quotas, users } from "@budibase/pro"
23
- import { allUsers, getUser } from "../../utilities"
24
1
  import { EmailTemplatePurpose } from "../../../constants"
25
2
  import { checkInviteCode } from "../../../utilities/redis"
26
3
  import { sendEmail } from "../../../utilities/email"
4
+ import { users } from "../../../sdk"
5
+ import env from "../../../environment"
6
+ import { User, CloudAccount } from "@budibase/types"
7
+ import {
8
+ events,
9
+ errors,
10
+ accounts,
11
+ db as dbUtils,
12
+ users as usersCore,
13
+ tenancy,
14
+ cache,
15
+ } from "@budibase/backend-core"
27
16
 
28
17
  export const save = async (ctx: any) => {
29
18
  try {
30
- const user: any = await users.save(ctx.request.body, getTenantId())
31
- // let server know to sync user
32
- await syncUserInApps(user._id)
19
+ const user = await users.save(ctx.request.body)
33
20
  ctx.body = user
34
21
  } catch (err: any) {
35
22
  ctx.throw(err.status || 400, err)
@@ -42,38 +29,22 @@ const parseBooleanParam = (param: any) => {
42
29
 
43
30
  export const adminUser = async (ctx: any) => {
44
31
  const { email, password, tenantId } = ctx.request.body
45
- await doInTenant(tenantId, async () => {
32
+ await tenancy.doInTenant(tenantId, async () => {
46
33
  // account portal sends a pre-hashed password - honour param to prevent double hashing
47
34
  const hashPassword = parseBooleanParam(ctx.request.query.hashPassword)
48
35
  // account portal sends no password for SSO users
49
36
  const requirePassword = parseBooleanParam(ctx.request.query.requirePassword)
50
37
 
51
- if (await doesTenantExist(tenantId)) {
38
+ if (await tenancy.doesTenantExist(tenantId)) {
52
39
  ctx.throw(403, "Organisation already exists.")
53
40
  }
54
41
 
55
- const response = await doWithGlobalDB(tenantId, async (db: any) => {
56
- const response = await db.allDocs(
57
- getGlobalUserParams(null, {
42
+ const response = await tenancy.doWithGlobalDB(tenantId, async (db: any) => {
43
+ return db.allDocs(
44
+ dbUtils.getGlobalUserParams(null, {
58
45
  include_docs: true,
59
46
  })
60
47
  )
61
- // write usage quotas for cloud
62
- if (!env.SELF_HOSTED) {
63
- // could be a scenario where it exists, make sure its clean
64
- try {
65
- const usageQuota = await db.get(
66
- StaticDatabases.GLOBAL.docs.usageQuota
67
- )
68
- if (usageQuota) {
69
- await db.remove(usageQuota._id, usageQuota._rev)
70
- }
71
- } catch (err) {
72
- // don't worry about errors
73
- }
74
- await db.put(quotas.generateNewQuotaUsage())
75
- }
76
- return response
77
48
  })
78
49
 
79
50
  if (response.rows.some((row: any) => row.doc.admin)) {
@@ -83,7 +54,7 @@ export const adminUser = async (ctx: any) => {
83
54
  )
84
55
  }
85
56
 
86
- const user = {
57
+ const user: User = {
87
58
  email: email,
88
59
  password: password,
89
60
  createdAt: Date.now(),
@@ -97,13 +68,19 @@ export const adminUser = async (ctx: any) => {
97
68
  tenantId,
98
69
  }
99
70
  try {
100
- const finalUser = await users.save(
101
- user,
102
- tenantId,
71
+ const finalUser = await users.save(user, {
103
72
  hashPassword,
104
- requirePassword
105
- )
106
- await bustCache(CacheKeys.CHECKLIST)
73
+ requirePassword,
74
+ })
75
+ await cache.bustCache(cache.CacheKeys.CHECKLIST)
76
+
77
+ // events
78
+ let account: CloudAccount | undefined
79
+ if (!env.SELF_HOSTED && !env.DISABLE_ACCOUNT_PORTAL) {
80
+ account = await accounts.getAccountByTenantId(tenantId)
81
+ }
82
+ await events.identification.identifyTenantGroup(tenantId, account)
83
+
107
84
  ctx.body = finalUser
108
85
  } catch (err: any) {
109
86
  ctx.throw(err.status || 400, err)
@@ -112,37 +89,16 @@ export const adminUser = async (ctx: any) => {
112
89
  }
113
90
 
114
91
  export const destroy = async (ctx: any) => {
115
- const db = getGlobalDB()
116
- const dbUser = await db.get(ctx.params.id)
117
-
118
- if (!env.SELF_HOSTED && !env.DISABLE_ACCOUNT_PORTAL) {
119
- // root account holder can't be deleted from inside budibase
120
- const email = dbUser.email
121
- const account = await accounts.getAccount(email)
122
- if (account) {
123
- if (email === ctx.user.email) {
124
- ctx.throw(400, 'Please visit "Account" to delete this user')
125
- } else {
126
- ctx.throw(400, "Account holder cannot be deleted")
127
- }
128
- }
129
- }
130
-
131
- await removeUserFromInfoDB(dbUser)
132
- await db.remove(dbUser._id, dbUser._rev)
133
- await quotas.removeUser(dbUser)
134
- await userCache.invalidateUser(dbUser._id)
135
- await invalidateSessions(dbUser._id)
136
- // let server know to sync user
137
- await syncUserInApps(dbUser._id)
92
+ const id = ctx.params.id
93
+ await users.destroy(id, ctx.user)
138
94
  ctx.body = {
139
- message: `User ${ctx.params.id} deleted.`,
95
+ message: `User ${id} deleted.`,
140
96
  }
141
97
  }
142
98
 
143
99
  // called internally by app server user fetch
144
100
  export const fetch = async (ctx: any) => {
145
- const all = await allUsers()
101
+ const all = await users.allUsers()
146
102
  // user hashed password shouldn't ever be returned
147
103
  for (let user of all) {
148
104
  if (user) {
@@ -154,12 +110,12 @@ export const fetch = async (ctx: any) => {
154
110
 
155
111
  // called internally by app server user find
156
112
  export const find = async (ctx: any) => {
157
- ctx.body = await getUser(ctx.params.id)
113
+ ctx.body = await users.getUser(ctx.params.id)
158
114
  }
159
115
 
160
116
  export const tenantUserLookup = async (ctx: any) => {
161
117
  const id = ctx.params.id
162
- const user = await getTenantUser(id)
118
+ const user = await tenancy.getTenantUser(id)
163
119
  if (user) {
164
120
  ctx.body = user
165
121
  } else {
@@ -169,14 +125,14 @@ export const tenantUserLookup = async (ctx: any) => {
169
125
 
170
126
  export const invite = async (ctx: any) => {
171
127
  let { email, userInfo } = ctx.request.body
172
- const existing = await getGlobalUserByEmail(email)
128
+ const existing = await usersCore.getGlobalUserByEmail(email)
173
129
  if (existing) {
174
130
  ctx.throw(400, "Email address already in use.")
175
131
  }
176
132
  if (!userInfo) {
177
133
  userInfo = {}
178
134
  }
179
- userInfo.tenantId = getTenantId()
135
+ userInfo.tenantId = tenancy.getTenantId()
180
136
  const opts: any = {
181
137
  subject: "{{ company }} platform invitation",
182
138
  info: userInfo,
@@ -185,6 +141,7 @@ export const invite = async (ctx: any) => {
185
141
  ctx.body = {
186
142
  message: "Invitation has been sent.",
187
143
  }
144
+ await events.user.invited()
188
145
  }
189
146
 
190
147
  export const inviteAccept = async (ctx: any) => {
@@ -192,16 +149,19 @@ export const inviteAccept = async (ctx: any) => {
192
149
  try {
193
150
  // info is an extension of the user object that was stored by global
194
151
  const { email, info }: any = await checkInviteCode(inviteCode)
195
- ctx.body = await users.save(
196
- {
152
+ ctx.body = await tenancy.doInTenant(info.tenantId, async () => {
153
+ const saved = await users.save({
197
154
  firstName,
198
155
  lastName,
199
156
  password,
200
157
  email,
201
158
  ...info,
202
- },
203
- info.tenantId
204
- )
159
+ })
160
+ const db = tenancy.getGlobalDB()
161
+ const user = await db.get(saved._id)
162
+ await events.user.inviteAccepted(user)
163
+ return saved
164
+ })
205
165
  } catch (err: any) {
206
166
  if (err.code === errors.codes.USAGE_LIMIT_EXCEEDED) {
207
167
  // explicitly re-throw limit exceeded errors
@@ -1,7 +1,7 @@
1
1
  const Router = require("@koa/router")
2
2
  const controller = require("../../controllers/global/self")
3
3
  const builderOnly = require("../../../middleware/builderOnly")
4
- const { buildUserSaveValidation } = require("../../utilities/validation")
4
+ const { users } = require("../validation")
5
5
 
6
6
  const router = Router()
7
7
 
@@ -11,7 +11,7 @@ router
11
11
  .get("/api/global/self", controller.getSelf)
12
12
  .post(
13
13
  "/api/global/self",
14
- buildUserSaveValidation(true),
14
+ users.buildUserSaveValidation(true),
15
15
  controller.updateSelf
16
16
  )
17
17
 
@@ -4,7 +4,7 @@ const joiValidator = require("../../../middleware/joi-validator")
4
4
  const adminOnly = require("../../../middleware/adminOnly")
5
5
  const Joi = require("joi")
6
6
  const cloudRestricted = require("../../../middleware/cloudRestricted")
7
- const { buildUserSaveValidation } = require("../../utilities/validation")
7
+ const { users } = require("../validation")
8
8
  const selfController = require("../../controllers/global/self")
9
9
  const builderOrAdmin = require("../../../middleware/builderOrAdmin")
10
10
 
@@ -42,7 +42,7 @@ router
42
42
  .post(
43
43
  "/api/global/users",
44
44
  adminOnly,
45
- buildUserSaveValidation(),
45
+ users.buildUserSaveValidation(),
46
46
  controller.save
47
47
  )
48
48
  .get("/api/global/users", builderOrAdmin, controller.fetch)
@@ -73,7 +73,7 @@ router
73
73
  .get("/api/global/users/self", selfController.getSelf)
74
74
  .post(
75
75
  "/api/global/users/self",
76
- buildUserSaveValidation(true),
76
+ users.buildUserSaveValidation(true),
77
77
  selfController.updateSelf
78
78
  )
79
79
 
@@ -1,26 +1,25 @@
1
1
  jest.mock("nodemailer")
2
- const setup = require("./utilities")
3
- const sendMailMock = setup.emailMock()
2
+ const { config, request, mocks, structures } = require("../../../tests")
3
+ const sendMailMock = mocks.email.mock()
4
+ const { events } = require("@budibase/backend-core")
4
5
 
5
- const TENANT_ID = "default"
6
+ const TENANT_ID = structures.TENANT_ID
6
7
 
7
8
  describe("/api/global/auth", () => {
8
- let request = setup.getRequest()
9
- let config = setup.getConfig()
10
- let code
11
9
 
12
10
  beforeAll(async () => {
13
- await config.init()
11
+ await config.beforeAll()
14
12
  })
15
13
 
16
- afterAll(setup.afterAll)
14
+ afterAll(async () => {
15
+ await config.afterAll()
16
+ })
17
17
 
18
18
  afterEach(() => {
19
19
  jest.clearAllMocks()
20
20
  })
21
21
 
22
- it("should be able to generate password reset email", async () => {
23
- // initially configure settings
22
+ const requestPasswordReset = async () => {
24
23
  await config.saveSmtpConfig()
25
24
  await config.saveSettingsConfig()
26
25
  await config.createUser()
@@ -31,16 +30,37 @@ describe("/api/global/auth", () => {
31
30
  })
32
31
  .expect("Content-Type", /json/)
33
32
  .expect(200)
34
- expect(res.body).toEqual({ message: "Please check your email for a reset link." })
35
- expect(sendMailMock).toHaveBeenCalled()
36
33
  const emailCall = sendMailMock.mock.calls[0][0]
37
- // after this URL there should be a code
38
34
  const parts = emailCall.html.split(`http://localhost:10000/builder/auth/reset?code=`)
39
- code = parts[1].split("\"")[0].split("&")[0]
35
+ const code = parts[1].split("\"")[0].split("&")[0]
36
+ return { code, res }
37
+ }
38
+
39
+ it("should logout", async () => {
40
+ await request
41
+ .post("/api/global/auth/logout")
42
+ .set(config.defaultHeaders())
43
+ .expect(200)
44
+ expect(events.auth.logout).toBeCalledTimes(1)
45
+ })
46
+
47
+ it("should be able to generate password reset email", async () => {
48
+ const { res, code } = await requestPasswordReset()
49
+ const user = await config.getUser("test@test.com")
50
+
51
+ expect(res.body).toEqual({ message: "Please check your email for a reset link." })
52
+ expect(sendMailMock).toHaveBeenCalled()
53
+
40
54
  expect(code).toBeDefined()
55
+ expect(events.user.passwordResetRequested).toBeCalledTimes(1)
56
+ expect(events.user.passwordResetRequested).toBeCalledWith(user)
41
57
  })
42
58
 
43
59
  it("should allow resetting user password with code", async () => {
60
+ const { code } = await requestPasswordReset()
61
+ const user = await config.getUser("test@test.com")
62
+ delete user.password
63
+
44
64
  const res = await request
45
65
  .post(`/api/global/auth/${TENANT_ID}/reset/update`)
46
66
  .send({
@@ -50,14 +70,16 @@ describe("/api/global/auth", () => {
50
70
  .expect("Content-Type", /json/)
51
71
  .expect(200)
52
72
  expect(res.body).toEqual({ message: "password reset successfully." })
73
+ expect(events.user.passwordReset).toBeCalledTimes(1)
74
+ expect(events.user.passwordReset).toBeCalledWith(user)
53
75
  })
54
76
 
55
77
  describe("oidc", () => {
56
78
  const auth = require("@budibase/backend-core/auth")
57
79
 
58
80
  // mock the oidc strategy implementation and return value
59
- strategyFactory = jest.fn()
60
- mockStrategyReturn = jest.fn()
81
+ let strategyFactory = jest.fn()
82
+ let mockStrategyReturn = jest.fn()
61
83
  strategyFactory.mockReturnValue(mockStrategyReturn)
62
84
  auth.oidc.strategyFactory = strategyFactory
63
85
 
@@ -1,17 +1,233 @@
1
1
  // mock the email system
2
2
  jest.mock("nodemailer")
3
- const setup = require("./utilities")
4
- setup.emailMock()
3
+ const { config, structures, mocks, request } = require("../../../tests")
4
+ mocks.email.mock()
5
+ const { Configs } = require("@budibase/backend-core/constants")
6
+ const { events } = require("@budibase/backend-core")
5
7
 
6
- describe("/api/global/configs/checklist", () => {
7
- let request = setup.getRequest()
8
- let config = setup.getConfig()
8
+ describe("configs", () => {
9
9
 
10
10
  beforeAll(async () => {
11
- await config.init()
11
+ await config.beforeAll()
12
12
  })
13
13
 
14
- afterAll(setup.afterAll)
14
+ beforeEach(() => {
15
+ jest.clearAllMocks()
16
+ })
17
+
18
+ afterAll(async () => {
19
+ await config.afterAll()
20
+ })
21
+
22
+ describe("post /api/global/configs", () => {
23
+
24
+ const saveConfig = async (conf, _id, _rev) => {
25
+ const data = {
26
+ ...conf,
27
+ _id,
28
+ _rev
29
+ }
30
+
31
+ const res = await request
32
+ .post(`/api/global/configs`)
33
+ .send(data)
34
+ .set(config.defaultHeaders())
35
+ .expect("Content-Type", /json/)
36
+ .expect(200)
37
+
38
+ return {
39
+ ...data,
40
+ ...res.body
41
+ }
42
+ }
43
+
44
+ describe("google", () => {
45
+ const saveGoogleConfig = async (conf, _id, _rev) => {
46
+ const googleConfig = structures.configs.google(conf)
47
+ return saveConfig(googleConfig, _id, _rev)
48
+ }
49
+
50
+ describe("create", () => {
51
+ it ("should create activated google config", async () => {
52
+ await saveGoogleConfig()
53
+ expect(events.auth.SSOCreated).toBeCalledTimes(1)
54
+ expect(events.auth.SSOCreated).toBeCalledWith(Configs.GOOGLE)
55
+ expect(events.auth.SSODeactivated).not.toBeCalled()
56
+ expect(events.auth.SSOActivated).toBeCalledTimes(1)
57
+ expect(events.auth.SSOActivated).toBeCalledWith(Configs.GOOGLE)
58
+ await config.deleteConfig(Configs.GOOGLE)
59
+ })
60
+
61
+ it ("should create deactivated google config", async () => {
62
+ await saveGoogleConfig({ activated: false })
63
+ expect(events.auth.SSOCreated).toBeCalledTimes(1)
64
+ expect(events.auth.SSOCreated).toBeCalledWith(Configs.GOOGLE)
65
+ expect(events.auth.SSOActivated).not.toBeCalled()
66
+ expect(events.auth.SSODeactivated).not.toBeCalled()
67
+ await config.deleteConfig(Configs.GOOGLE)
68
+ })
69
+ })
70
+
71
+ describe("update", () => {
72
+ it ("should update google config to deactivated", async () => {
73
+ const googleConf = await saveGoogleConfig()
74
+ jest.clearAllMocks()
75
+ await saveGoogleConfig({ ...googleConf.config, activated: false }, googleConf._id, googleConf._rev)
76
+ expect(events.auth.SSOUpdated).toBeCalledTimes(1)
77
+ expect(events.auth.SSOUpdated).toBeCalledWith(Configs.GOOGLE)
78
+ expect(events.auth.SSOActivated).not.toBeCalled()
79
+ expect(events.auth.SSODeactivated).toBeCalledTimes(1)
80
+ expect(events.auth.SSODeactivated).toBeCalledWith(Configs.GOOGLE)
81
+ await config.deleteConfig(Configs.GOOGLE)
82
+ })
83
+
84
+ it ("should update google config to activated", async () => {
85
+ const googleConf = await saveGoogleConfig({ activated: false })
86
+ jest.clearAllMocks()
87
+ await saveGoogleConfig({ ...googleConf.config, activated: true}, googleConf._id, googleConf._rev)
88
+ expect(events.auth.SSOUpdated).toBeCalledTimes(1)
89
+ expect(events.auth.SSOUpdated).toBeCalledWith(Configs.GOOGLE)
90
+ expect(events.auth.SSODeactivated).not.toBeCalled()
91
+ expect(events.auth.SSOActivated).toBeCalledTimes(1)
92
+ expect(events.auth.SSOActivated).toBeCalledWith(Configs.GOOGLE)
93
+ await config.deleteConfig(Configs.GOOGLE)
94
+ })
95
+ })
96
+ })
97
+
98
+ describe("oidc", () => {
99
+ const saveOIDCConfig = async (conf, _id, _rev) => {
100
+ const oidcConfig = structures.configs.oidc(conf)
101
+ return saveConfig(oidcConfig, _id, _rev)
102
+ }
103
+
104
+ describe("create", () => {
105
+ it ("should create activated OIDC config", async () => {
106
+ await saveOIDCConfig()
107
+ expect(events.auth.SSOCreated).toBeCalledTimes(1)
108
+ expect(events.auth.SSOCreated).toBeCalledWith(Configs.OIDC)
109
+ expect(events.auth.SSODeactivated).not.toBeCalled()
110
+ expect(events.auth.SSOActivated).toBeCalledTimes(1)
111
+ expect(events.auth.SSOActivated).toBeCalledWith(Configs.OIDC)
112
+ await config.deleteConfig(Configs.OIDC)
113
+ })
114
+
115
+ it ("should create deactivated OIDC config", async () => {
116
+ await saveOIDCConfig({ activated: false })
117
+ expect(events.auth.SSOCreated).toBeCalledTimes(1)
118
+ expect(events.auth.SSOCreated).toBeCalledWith(Configs.OIDC)
119
+ expect(events.auth.SSOActivated).not.toBeCalled()
120
+ expect(events.auth.SSODeactivated).not.toBeCalled()
121
+ await config.deleteConfig(Configs.OIDC)
122
+ })
123
+ })
124
+
125
+ describe("update", () => {
126
+ it ("should update OIDC config to deactivated", async () => {
127
+ const oidcConf = await saveOIDCConfig()
128
+ jest.clearAllMocks()
129
+ await saveOIDCConfig({ ...oidcConf.config.configs[0], activated: false }, oidcConf._id, oidcConf._rev)
130
+ expect(events.auth.SSOUpdated).toBeCalledTimes(1)
131
+ expect(events.auth.SSOUpdated).toBeCalledWith(Configs.OIDC)
132
+ expect(events.auth.SSOActivated).not.toBeCalled()
133
+ expect(events.auth.SSODeactivated).toBeCalledTimes(1)
134
+ expect(events.auth.SSODeactivated).toBeCalledWith(Configs.OIDC)
135
+ await config.deleteConfig(Configs.OIDC)
136
+ })
137
+
138
+ it ("should update OIDC config to activated", async () => {
139
+ const oidcConf = await saveOIDCConfig({ activated: false })
140
+ jest.clearAllMocks()
141
+ await saveOIDCConfig({ ...oidcConf.config.configs[0], activated: true}, oidcConf._id, oidcConf._rev)
142
+ expect(events.auth.SSOUpdated).toBeCalledTimes(1)
143
+ expect(events.auth.SSOUpdated).toBeCalledWith(Configs.OIDC)
144
+ expect(events.auth.SSODeactivated).not.toBeCalled()
145
+ expect(events.auth.SSOActivated).toBeCalledTimes(1)
146
+ expect(events.auth.SSOActivated).toBeCalledWith(Configs.OIDC)
147
+ await config.deleteConfig(Configs.OIDC)
148
+ })
149
+ })
150
+
151
+ })
152
+
153
+ describe("smtp", () => {
154
+ const saveSMTPConfig = async (conf, _id, _rev) => {
155
+ const smtpConfig = structures.configs.smtp(conf)
156
+ return saveConfig(smtpConfig, _id, _rev)
157
+ }
158
+
159
+ describe("create", () => {
160
+ it ("should create SMTP config", async () => {
161
+ await config.deleteConfig(Configs.SMTP)
162
+ await saveSMTPConfig()
163
+ expect(events.email.SMTPUpdated).not.toBeCalled()
164
+ expect(events.email.SMTPCreated).toBeCalledTimes(1)
165
+ await config.deleteConfig(Configs.SMTP)
166
+ })
167
+ })
168
+
169
+ describe("update", () => {
170
+ it ("should update SMTP config", async () => {
171
+ const smtpConf = await saveSMTPConfig()
172
+ jest.clearAllMocks()
173
+ await saveSMTPConfig(smtpConf.config, smtpConf._id, smtpConf._rev)
174
+ expect(events.email.SMTPCreated).not.toBeCalled()
175
+ expect(events.email.SMTPUpdated).toBeCalledTimes(1)
176
+ await config.deleteConfig(Configs.SMTP)
177
+ })
178
+ })
179
+ })
180
+
181
+ describe("settings", () => {
182
+ const saveSettingsConfig = async (conf, _id, _rev) => {
183
+ const settingsConfig = structures.configs.settings(conf)
184
+ return saveConfig(settingsConfig, _id, _rev)
185
+ }
186
+
187
+ describe("create", () => {
188
+ it ("should create settings config with default settings", async () => {
189
+ await config.deleteConfig(Configs.SETTINGS)
190
+
191
+ await saveSettingsConfig()
192
+
193
+ expect(events.org.nameUpdated).not.toBeCalled()
194
+ expect(events.org.logoUpdated).not.toBeCalled()
195
+ expect(events.org.platformURLUpdated).not.toBeCalled()
196
+ })
197
+
198
+ it ("should create settings config with non-default settings", async () => {
199
+ await config.deleteConfig(Configs.SETTINGS)
200
+ const conf = {
201
+ company: "acme",
202
+ logoUrl: "http://example.com",
203
+ platformUrl: "http://example.com"
204
+ }
205
+
206
+ await saveSettingsConfig(conf)
207
+
208
+ expect(events.org.nameUpdated).toBeCalledTimes(1)
209
+ expect(events.org.logoUpdated).toBeCalledTimes(1)
210
+ expect(events.org.platformURLUpdated).toBeCalledTimes(1)
211
+ })
212
+ })
213
+
214
+ describe("update", () => {
215
+ it ("should update settings config", async () => {
216
+ await config.deleteConfig(Configs.SETTINGS)
217
+ const settingsConfig = await saveSettingsConfig()
218
+ settingsConfig.config.company = "acme"
219
+ settingsConfig.config.logoUrl = "http://example.com"
220
+ settingsConfig.config.platformUrl = "http://example.com"
221
+
222
+ await saveSettingsConfig(settingsConfig.config, settingsConfig._id, settingsConfig._rev)
223
+
224
+ expect(events.org.nameUpdated).toBeCalledTimes(1)
225
+ expect(events.org.logoUpdated).toBeCalledTimes(1)
226
+ expect(events.org.platformURLUpdated).toBeCalledTimes(1)
227
+ })
228
+ })
229
+ })
230
+ })
15
231
 
16
232
  it("should return the correct checklist status based on the state of the budibase installation", async () => {
17
233
  await config.saveSmtpConfig()
@@ -1,19 +1,20 @@
1
1
  jest.mock("nodemailer")
2
- const setup = require("./utilities")
3
- const sendMailMock = setup.emailMock()
2
+ const { config, mocks, structures, request } = require("../../../tests")
3
+ const sendMailMock = mocks.email.mock()
4
4
 
5
5
  const { EmailTemplatePurpose } = require("../../../constants")
6
- const { TENANT_ID } = require("./utilities/structures")
6
+
7
+ const TENANT_ID = structures.TENANT_ID
7
8
 
8
9
  describe("/api/global/email", () => {
9
- let request = setup.getRequest()
10
- let config = setup.getConfig()
11
10
 
12
11
  beforeAll(async () => {
13
- await config.init()
12
+ await config.beforeAll()
14
13
  })
15
14
 
16
- afterAll(setup.afterAll)
15
+ afterAll(async () => {
16
+ await config.afterAll()
17
+ })
17
18
 
18
19
  it("should be able to send an email (with mocking)", async () => {
19
20
  // initially configure settings
@@ -1,4 +1,4 @@
1
- const setup = require("./utilities")
1
+ const { config, request } = require("../../../tests")
2
2
  const { EmailTemplatePurpose } = require("../../../constants")
3
3
  const nodemailer = require("nodemailer")
4
4
  const fetch = require("node-fetch")
@@ -7,14 +7,14 @@ const fetch = require("node-fetch")
7
7
  jest.setTimeout(30000)
8
8
 
9
9
  describe("/api/global/email", () => {
10
- let request = setup.getRequest()
11
- let config = setup.getConfig()
12
10
 
13
11
  beforeAll(async () => {
14
- await config.init()
12
+ await config.beforeAll()
15
13
  })
16
14
 
17
- afterAll(setup.afterAll)
15
+ afterAll(async () => {
16
+ await config.afterAll()
17
+ })
18
18
 
19
19
  async function sendRealEmail(purpose) {
20
20
  let response, text