@budibase/worker 2.3.17-alpha.3 → 2.3.17-alpha.5

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 (43) hide show
  1. package/package.json +8 -7
  2. package/scripts/dev/manage.js +1 -0
  3. package/src/api/controllers/global/auth.ts +77 -70
  4. package/src/api/controllers/global/self.ts +28 -44
  5. package/src/api/controllers/global/users.ts +65 -25
  6. package/src/api/controllers/system/accounts.ts +7 -5
  7. package/src/api/controllers/system/tenants.ts +4 -8
  8. package/src/api/index.ts +4 -20
  9. package/src/api/routes/global/auth.ts +10 -7
  10. package/src/api/routes/global/tests/auth.spec.ts +234 -33
  11. package/src/api/routes/global/tests/configs.spec.ts +93 -113
  12. package/src/api/routes/global/tests/roles.spec.ts +3 -2
  13. package/src/api/routes/global/tests/self.spec.ts +3 -4
  14. package/src/api/routes/global/tests/users.spec.ts +44 -46
  15. package/src/api/routes/system/tenants.ts +1 -1
  16. package/src/api/routes/system/tests/accounts.spec.ts +4 -4
  17. package/src/api/routes/system/tests/migrations.spec.ts +2 -2
  18. package/src/api/routes/system/tests/restore.spec.ts +2 -2
  19. package/src/api/routes/system/tests/status.spec.ts +5 -5
  20. package/src/environment.ts +15 -1
  21. package/src/index.ts +6 -0
  22. package/src/middleware/tests/tenancy.spec.ts +4 -4
  23. package/src/migrations/functions/globalInfoSyncUsers.ts +4 -3
  24. package/src/sdk/accounts/index.ts +2 -1
  25. package/src/sdk/accounts/{accounts.ts → metadata.ts} +0 -1
  26. package/src/sdk/auth/auth.ts +86 -0
  27. package/src/sdk/auth/index.ts +1 -0
  28. package/src/sdk/tenants/index.ts +1 -0
  29. package/src/sdk/tenants/tenants.ts +76 -0
  30. package/src/sdk/users/events.ts +4 -0
  31. package/src/sdk/users/index.ts +1 -0
  32. package/src/sdk/users/tests/users.spec.ts +52 -0
  33. package/src/sdk/users/users.ts +53 -46
  34. package/src/tests/TestConfiguration.ts +38 -89
  35. package/src/tests/api/auth.ts +42 -18
  36. package/src/tests/api/base.ts +2 -1
  37. package/src/tests/api/restore.ts +1 -0
  38. package/src/tests/jestEnv.ts +2 -1
  39. package/src/tests/jestSetup.ts +2 -5
  40. package/src/tests/logging.ts +34 -0
  41. package/src/tests/structures/index.ts +0 -2
  42. package/src/utilities/email.ts +3 -3
  43. package/src/tests/structures/users.ts +0 -37
@@ -15,61 +15,29 @@ const supertest = require("supertest")
15
15
  import { Config } from "../constants"
16
16
  import {
17
17
  users,
18
- tenancy,
18
+ context,
19
19
  sessions,
20
20
  auth,
21
21
  constants,
22
22
  env as coreEnv,
23
- DEFAULT_TENANT_ID,
24
23
  } from "@budibase/backend-core"
25
- import structures, { TENANT_ID, CSRF_TOKEN } from "./structures"
26
- import { CreateUserResponse, User, AuthToken } from "@budibase/types"
24
+ import structures, { CSRF_TOKEN } from "./structures"
25
+ import { SaveUserResponse, User, AuthToken } from "@budibase/types"
27
26
  import API from "./api"
28
- import sdk from "../sdk"
29
-
30
- enum Mode {
31
- CLOUD = "cloud",
32
- SELF = "self",
33
- }
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
27
 
54
28
  class TestConfiguration {
55
29
  server: any
56
30
  request: any
57
31
  api: API
58
- defaultUser?: User
59
- tenant1User?: User
60
- #tenantId?: string
61
-
62
- constructor(
63
- opts: { openServer: boolean; mode: Mode } = {
64
- openServer: true,
65
- mode: Mode.CLOUD,
66
- }
67
- ) {
68
- if (opts.mode === Mode.CLOUD) {
69
- this.modeCloud()
70
- } else if (opts.mode === Mode.SELF) {
71
- this.modeSelf()
72
- }
32
+ tenantId: string
33
+ user?: User
34
+ userPassword = "test"
35
+
36
+ constructor(opts: { openServer: boolean } = { openServer: true }) {
37
+ // default to cloud hosting
38
+ this.cloudHosted()
39
+
40
+ this.tenantId = structures.tenant.id()
73
41
 
74
42
  if (opts.openServer) {
75
43
  env.PORT = "0" // random port
@@ -85,26 +53,19 @@ class TestConfiguration {
85
53
  return this.request
86
54
  }
87
55
 
88
- // MODES
89
-
90
- setMultiTenancy = (value: boolean) => {
91
- env._set("MULTI_TENANCY", value)
92
- coreEnv._set("MULTI_TENANCY", value)
93
- }
56
+ // HOSTING
94
57
 
95
58
  setSelfHosted = (value: boolean) => {
96
59
  env._set("SELF_HOSTED", value)
97
60
  coreEnv._set("SELF_HOSTED", value)
98
61
  }
99
62
 
100
- modeCloud = () => {
63
+ cloudHosted = () => {
101
64
  this.setSelfHosted(false)
102
- this.setMultiTenancy(true)
103
65
  }
104
66
 
105
- modeSelf = () => {
67
+ selfHosted = () => {
106
68
  this.setSelfHosted(true)
107
- this.setMultiTenancy(false)
108
69
  }
109
70
 
110
71
  // UTILS
@@ -125,7 +86,7 @@ class TestConfiguration {
125
86
  if (params) {
126
87
  request.params = params
127
88
  }
128
- await tenancy.doInTenant(this.getTenantId(), () => {
89
+ await context.doInTenant(this.getTenantId(), () => {
129
90
  return controlFunc(request)
130
91
  })
131
92
  return request.body
@@ -135,18 +96,10 @@ class TestConfiguration {
135
96
 
136
97
  async beforeAll() {
137
98
  try {
138
- this.#tenantId = structures.tenant.id()
139
-
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
- })
99
+ await this.createDefaultUser()
100
+ await this.createSession(this.user!)
148
101
  } catch (e: any) {
149
- console.log(e)
102
+ console.error(e)
150
103
  throw new Error(e.message)
151
104
  }
152
105
  }
@@ -159,12 +112,16 @@ class TestConfiguration {
159
112
 
160
113
  // TENANCY
161
114
 
115
+ doInTenant(task: any) {
116
+ return context.doInTenant(this.tenantId, () => {
117
+ return task()
118
+ })
119
+ }
120
+
162
121
  createTenant = async (): Promise<User> => {
163
122
  // create user / new tenant
164
123
  const res = await this.api.users.createAdminUser()
165
124
 
166
- await sdk.users.addTenant(res.tenantId, res.userId, res.email)
167
-
168
125
  // return the created user
169
126
  const userRes = await this.api.users.getUser(res.userId, {
170
127
  headers: {
@@ -182,9 +139,9 @@ class TestConfiguration {
182
139
 
183
140
  getTenantId() {
184
141
  try {
185
- return tenancy.getTenantId()
186
- } catch (e: any) {
187
- return DEFAULT_TENANT_ID
142
+ return context.getTenantId()
143
+ } catch (e) {
144
+ return this.tenantId!
188
145
  }
189
146
  }
190
147
 
@@ -232,14 +189,11 @@ class TestConfiguration {
232
189
  }
233
190
 
234
191
  defaultHeaders() {
235
- const tenantId = this.getTenantId()
236
- if (tenantId === TENANT_ID) {
237
- return this.authHeaders(this.defaultUser!)
238
- } else if (tenantId === this.getTenantId()) {
239
- return this.authHeaders(this.tenant1User!)
240
- } else {
241
- throw new Error("could not determine auth headers to use")
242
- }
192
+ return this.authHeaders(this.user!)
193
+ }
194
+
195
+ tenantIdHeaders() {
196
+ return { [constants.Header.TENANT_ID]: this.tenantId }
243
197
  }
244
198
 
245
199
  internalAPIHeaders() {
@@ -254,20 +208,15 @@ class TestConfiguration {
254
208
 
255
209
  async createDefaultUser() {
256
210
  const user = structures.users.adminUser({
257
- password: "test",
211
+ password: this.userPassword,
258
212
  })
259
- this.defaultUser = await this.createUser(user)
260
- }
261
-
262
- async createTenant1User() {
263
- const user = structures.users.adminUser({
264
- password: "test",
213
+ await context.doInTenant(this.tenantId!, async () => {
214
+ this.user = await this.createUser(user)
265
215
  })
266
- this.tenant1User = await this.createUser(user)
267
216
  }
268
217
 
269
218
  async getUser(email: string): Promise<User> {
270
- return tenancy.doInTenant(this.getTenantId(), () => {
219
+ return context.doInTenant(this.getTenantId(), () => {
271
220
  return users.getGlobalUserByEmail(email)
272
221
  })
273
222
  }
@@ -277,7 +226,7 @@ class TestConfiguration {
277
226
  user = structures.users.user()
278
227
  }
279
228
  const response = await this._req(user, null, controllers.users.save)
280
- const body = response as CreateUserResponse
229
+ const body = response as SaveUserResponse
281
230
  return this.getUser(body.email)
282
231
  }
283
232
 
@@ -1,21 +1,39 @@
1
- import structures from "../structures"
2
1
  import TestConfiguration from "../TestConfiguration"
3
- import { TestAPI } from "./base"
2
+ import { TestAPI, TestAPIOpts } from "./base"
4
3
 
5
4
  export class AuthAPI extends TestAPI {
6
5
  constructor(config: TestConfiguration) {
7
6
  super(config)
8
7
  }
9
8
 
10
- updatePassword = (code: string) => {
9
+ updatePassword = (
10
+ resetCode: string,
11
+ password: string,
12
+ opts?: TestAPIOpts
13
+ ) => {
11
14
  return this.request
12
15
  .post(`/api/global/auth/${this.config.getTenantId()}/reset/update`)
13
16
  .send({
14
- password: "newpassword",
15
- resetCode: code,
17
+ password,
18
+ resetCode,
16
19
  })
17
20
  .expect("Content-Type", /json/)
18
- .expect(200)
21
+ .expect(opts?.status ? opts.status : 200)
22
+ }
23
+
24
+ login = (
25
+ tenantId: string,
26
+ email: string,
27
+ password: string,
28
+ opts?: TestAPIOpts
29
+ ) => {
30
+ return this.request
31
+ .post(`/api/global/auth/${tenantId}/login`)
32
+ .send({
33
+ username: email,
34
+ password: password,
35
+ })
36
+ .expect(opts?.status ? opts.status : 200)
19
37
  }
20
38
 
21
39
  logout = () => {
@@ -25,25 +43,31 @@ export class AuthAPI extends TestAPI {
25
43
  .expect(200)
26
44
  }
27
45
 
28
- requestPasswordReset = async (sendMailMock: any, userEmail: string) => {
46
+ requestPasswordReset = async (
47
+ sendMailMock: any,
48
+ email: string,
49
+ opts?: TestAPIOpts
50
+ ) => {
29
51
  await this.config.saveSmtpConfig()
30
52
  await this.config.saveSettingsConfig()
31
- await this.config.createUser({
32
- ...structures.users.user(),
33
- email: userEmail,
34
- })
53
+
35
54
  const res = await this.request
36
55
  .post(`/api/global/auth/${this.config.getTenantId()}/reset`)
37
56
  .send({
38
- email: userEmail,
57
+ email: email,
39
58
  })
40
59
  .expect("Content-Type", /json/)
41
- .expect(200)
42
- const emailCall = sendMailMock.mock.calls[0][0]
43
- const parts = emailCall.html.split(
44
- `http://localhost:10000/builder/auth/reset?code=`
45
- )
46
- const code = parts[1].split('"')[0].split("&")[0]
60
+ .expect(opts?.status ? opts.status : 200)
61
+
62
+ let code: string | undefined
63
+ if (res.status === 200) {
64
+ const emailCall = sendMailMock.mock.calls[0][0]
65
+ const parts = emailCall.html.split(
66
+ `http://localhost:10000/builder/auth/reset?code=`
67
+ )
68
+ code = parts[1].split('"')[0].split("&")[0]
69
+ }
70
+
47
71
  return { code, res }
48
72
  }
49
73
  }
@@ -1,4 +1,5 @@
1
1
  import TestConfiguration from "../TestConfiguration"
2
+ import { SuperTest, Test } from "supertest"
2
3
 
3
4
  export interface TestAPIOpts {
4
5
  headers?: any
@@ -7,7 +8,7 @@ export interface TestAPIOpts {
7
8
 
8
9
  export abstract class TestAPI {
9
10
  config: TestConfiguration
10
- request: any
11
+ request: SuperTest<Test>
11
12
 
12
13
  protected constructor(config: TestConfiguration) {
13
14
  this.config = config
@@ -9,6 +9,7 @@ export class RestoreAPI extends TestAPI {
9
9
  restored = (opts?: TestAPIOpts) => {
10
10
  return this.request
11
11
  .post(`/api/system/restored`)
12
+ .set(this.config.tenantIdHeaders())
12
13
  .expect(opts?.status ? opts.status : 200)
13
14
  }
14
15
  }
@@ -1,7 +1,8 @@
1
1
  process.env.SELF_HOSTED = "0"
2
2
  process.env.NODE_ENV = "jest"
3
3
  process.env.JWT_SECRET = "test-jwtsecret"
4
- process.env.LOG_LEVEL = "silent"
4
+ process.env.LOG_LEVEL = process.env.LOG_LEVEL || "error"
5
+ process.env.ENABLE_4XX_HTTP_LOGGING = "0"
5
6
  process.env.MULTI_TENANCY = "1"
6
7
  process.env.MINIO_URL = "http://localhost"
7
8
  process.env.MINIO_ACCESS_KEY = "test"
@@ -1,5 +1,6 @@
1
- import { mocks, testContainerUtils } from "@budibase/backend-core/tests"
1
+ import "./logging"
2
2
 
3
+ import { mocks, testContainerUtils } from "@budibase/backend-core/tests"
3
4
  import env from "../environment"
4
5
  import { env as coreEnv } from "@budibase/backend-core"
5
6
 
@@ -11,10 +12,6 @@ mocks.fetch.enable()
11
12
  const tk = require("timekeeper")
12
13
  tk.freeze(mocks.date.MOCK_DATE)
13
14
 
14
- if (!process.env.DEBUG) {
15
- global.console.log = jest.fn() // console.log are ignored in tests
16
- }
17
-
18
15
  if (!process.env.CI) {
19
16
  // set a longer timeout in dev for debugging
20
17
  // 100 seconds
@@ -0,0 +1,34 @@
1
+ export enum LogLevel {
2
+ TRACE = "trace",
3
+ DEBUG = "debug",
4
+ INFO = "info",
5
+ WARN = "warn",
6
+ ERROR = "error",
7
+ }
8
+
9
+ const LOG_INDEX: { [key in LogLevel]: number } = {
10
+ [LogLevel.TRACE]: 1,
11
+ [LogLevel.DEBUG]: 2,
12
+ [LogLevel.INFO]: 3,
13
+ [LogLevel.WARN]: 4,
14
+ [LogLevel.ERROR]: 5,
15
+ }
16
+
17
+ const setIndex = LOG_INDEX[process.env.LOG_LEVEL as LogLevel]
18
+
19
+ if (setIndex > LOG_INDEX.trace) {
20
+ global.console.trace = jest.fn()
21
+ }
22
+
23
+ if (setIndex > LOG_INDEX.debug) {
24
+ global.console.debug = jest.fn()
25
+ }
26
+
27
+ if (setIndex > LOG_INDEX.info) {
28
+ global.console.info = jest.fn()
29
+ global.console.log = jest.fn()
30
+ }
31
+
32
+ if (setIndex > LOG_INDEX.warn) {
33
+ global.console.warn = jest.fn()
34
+ }
@@ -1,6 +1,5 @@
1
1
  import { structures } from "@budibase/backend-core/tests"
2
2
  import * as configs from "./configs"
3
- import * as users from "./users"
4
3
  import * as groups from "./groups"
5
4
  import { v4 as uuid } from "uuid"
6
5
 
@@ -11,7 +10,6 @@ const pkg = {
11
10
  ...structures,
12
11
  uuid,
13
12
  configs,
14
- users,
15
13
  TENANT_ID,
16
14
  CSRF_TOKEN,
17
15
  groups,
@@ -26,7 +26,7 @@ type SendEmailOpts = {
26
26
  automation?: boolean
27
27
  }
28
28
 
29
- const TEST_MODE = false
29
+ const TEST_MODE = env.ENABLE_EMAIL_TEST_MODE && env.isDev()
30
30
  const TYPE = TemplateType.EMAIL
31
31
 
32
32
  const FULL_EMAIL_PURPOSES = [
@@ -62,8 +62,8 @@ function createSMTPTransport(config: any) {
62
62
  host: "smtp.ethereal.email",
63
63
  secure: false,
64
64
  auth: {
65
- user: "don.bahringer@ethereal.email",
66
- pass: "yCKSH8rWyUPbnhGYk9",
65
+ user: "wyatt.zulauf29@ethereal.email",
66
+ pass: "tEwDtHBWWxusVWAPfa",
67
67
  },
68
68
  }
69
69
  }
@@ -1,37 +0,0 @@
1
- export const email = "test@test.com"
2
- import { AdminUser, BuilderUser, User } from "@budibase/types"
3
- import { v4 as uuid } from "uuid"
4
-
5
- export const newEmail = () => {
6
- return `${uuid()}@test.com`
7
- }
8
-
9
- export const user = (userProps?: any): User => {
10
- return {
11
- email: newEmail(),
12
- password: "test",
13
- roles: { app_test: "admin" },
14
- ...userProps,
15
- }
16
- }
17
-
18
- export const adminUser = (userProps?: any): AdminUser => {
19
- return {
20
- ...user(userProps),
21
- admin: {
22
- global: true,
23
- },
24
- builder: {
25
- global: true,
26
- },
27
- }
28
- }
29
-
30
- export const builderUser = (userProps?: any): BuilderUser => {
31
- return {
32
- ...user(userProps),
33
- builder: {
34
- global: true,
35
- },
36
- }
37
- }