@budibase/worker 2.4.44-alpha.3 → 2.4.44-alpha.31

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.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@budibase/worker",
3
3
  "email": "hi@budibase.com",
4
- "version": "2.4.44-alpha.3",
4
+ "version": "2.4.44-alpha.31",
5
5
  "description": "Budibase background service",
6
6
  "main": "src/index.ts",
7
7
  "repository": {
@@ -22,6 +22,7 @@
22
22
  "build:docker": "docker build . -t worker-service --label version=$BUDIBASE_RELEASE_VERSION",
23
23
  "dev:stack:init": "node ./scripts/dev/manage.js init",
24
24
  "dev:builder": "npm run dev:stack:init && nodemon",
25
+ "dev:built": "yarn run dev:stack:init && yarn run run:docker",
25
26
  "test": "bash scripts/test.sh",
26
27
  "test:watch": "jest --watch",
27
28
  "env:multi:enable": "node scripts/multiTenancy.js enable",
@@ -36,10 +37,10 @@
36
37
  "author": "Budibase",
37
38
  "license": "GPL-3.0",
38
39
  "dependencies": {
39
- "@budibase/backend-core": "2.4.44-alpha.3",
40
- "@budibase/pro": "2.4.44-alpha.2",
41
- "@budibase/string-templates": "2.4.44-alpha.3",
42
- "@budibase/types": "2.4.44-alpha.3",
40
+ "@budibase/backend-core": "2.4.44-alpha.31",
41
+ "@budibase/pro": "2.4.44-alpha.27",
42
+ "@budibase/string-templates": "2.4.44-alpha.31",
43
+ "@budibase/types": "2.4.44-alpha.31",
43
44
  "@koa/router": "8.0.8",
44
45
  "@sentry/node": "6.17.7",
45
46
  "@techpass/passport-openidconnect": "0.3.2",
@@ -56,7 +57,6 @@
56
57
  "koa-body": "4.2.0",
57
58
  "koa-compress": "4.0.1",
58
59
  "koa-passport": "4.1.4",
59
- "koa-pino-logger": "3.0.0",
60
60
  "koa-send": "5.0.1",
61
61
  "koa-session": "5.13.1",
62
62
  "koa-static": "5.0.0",
@@ -66,7 +66,6 @@
66
66
  "passport-google-oauth": "2.0.0",
67
67
  "passport-jwt": "4.0.0",
68
68
  "passport-local": "1.0.0",
69
- "pino-pretty": "4.8.0",
70
69
  "pouchdb": "7.3.0",
71
70
  "pouchdb-all-dbs": "1.1.1",
72
71
  "server-destroy": "1.0.1"
@@ -103,5 +102,5 @@
103
102
  "typescript": "4.7.3",
104
103
  "update-dotenv": "1.1.1"
105
104
  },
106
- "gitHead": "e6449a30351b2c3ffe9fe3d8b5e5fa02861b3ae3"
105
+ "gitHead": "e868f91a5b9bdd08b460c3355eebd5a9ddc1341c"
107
106
  }
@@ -30,6 +30,7 @@ async function init() {
30
30
  DEPLOYMENT_ENVIRONMENT: "development",
31
31
  TENANT_FEATURE_FLAGS: "*:LICENSING,*:USER_GROUPS,*:ONBOARDING_TOUR",
32
32
  ENABLE_EMAIL_TEST_MODE: 1,
33
+ HTTP_LOGGING: 0,
33
34
  }
34
35
  let envFile = ""
35
36
  Object.keys(envFileJson).forEach(key => {
@@ -2,6 +2,7 @@ import tk from "timekeeper"
2
2
  import _ from "lodash"
3
3
  import { mocks, structures } from "@budibase/backend-core/tests"
4
4
  import {
5
+ ScimCreateUserRequest,
5
6
  ScimGroupResponse,
6
7
  ScimUpdateRequest,
7
8
  ScimUserResponse,
@@ -176,7 +177,9 @@ describe("scim", () => {
176
177
  const response = await getScimUsers({
177
178
  params: {
178
179
  filter: encodeURI(
179
- `emails[type eq "work"].value eq "${userToFetch?.emails[0].value}"`
180
+ `emails[type eq "work"].value eq "${
181
+ userToFetch?.emails![0].value
182
+ }"`
180
183
  ),
181
184
  },
182
185
  })
@@ -252,6 +255,45 @@ describe("scim", () => {
252
255
  )
253
256
  })
254
257
 
258
+ it("a new user can minim information", async () => {
259
+ const userData = {
260
+ externalId: structures.uuid(),
261
+ email: structures.generator.email(),
262
+ username: structures.generator.name(),
263
+ firstName: undefined,
264
+ lastName: undefined,
265
+ }
266
+ const body = structures.scim.createUserRequest(userData)
267
+
268
+ const response = await postScimUser({ body })
269
+
270
+ const expectedScimUser = {
271
+ schemas: ["urn:ietf:params:scim:schemas:core:2.0:User"],
272
+ id: expect.any(String),
273
+ externalId: userData.externalId,
274
+ meta: {
275
+ resourceType: "User",
276
+ created: mocks.date.MOCK_DATE.toISOString(),
277
+ lastModified: mocks.date.MOCK_DATE.toISOString(),
278
+ },
279
+ userName: userData.username,
280
+ active: true,
281
+ emails: [
282
+ {
283
+ value: userData.email,
284
+ type: "work",
285
+ primary: true,
286
+ },
287
+ ],
288
+ }
289
+ expect(response).toEqual(expectedScimUser)
290
+
291
+ const persistedUsers = await config.api.scimUsersAPI.get()
292
+ expect(persistedUsers.Resources).toEqual(
293
+ expect.arrayContaining([expectedScimUser])
294
+ )
295
+ })
296
+
255
297
  it("an event is dispatched", async () => {
256
298
  const body = structures.scim.createUserRequest()
257
299
 
@@ -259,6 +301,70 @@ describe("scim", () => {
259
301
 
260
302
  expect(events.user.created).toBeCalledTimes(1)
261
303
  })
304
+
305
+ it("if the username is an email, the user name will be used as email", async () => {
306
+ const email = structures.generator.email()
307
+
308
+ const body: ScimCreateUserRequest = structures.scim.createUserRequest(
309
+ { username: email }
310
+ )
311
+ delete body.emails
312
+
313
+ await postScimUser({ body })
314
+
315
+ const user = await config.getUser(email)
316
+ expect(user).toBeDefined()
317
+ expect(user.email).toEqual(email)
318
+ })
319
+
320
+ it("if multiple emails are provided, the first primary one is used as email", async () => {
321
+ const email = structures.generator.email()
322
+
323
+ const body: ScimCreateUserRequest = {
324
+ ...structures.scim.createUserRequest(),
325
+ emails: [
326
+ {
327
+ primary: false,
328
+ type: "work",
329
+ value: structures.generator.email(),
330
+ },
331
+ {
332
+ primary: true,
333
+ type: "work",
334
+ value: email,
335
+ },
336
+ {
337
+ primary: true,
338
+ type: "work",
339
+ value: structures.generator.email(),
340
+ },
341
+ ],
342
+ }
343
+
344
+ await postScimUser({ body })
345
+
346
+ const user = await config.getUser(email)
347
+ expect(user).toBeDefined()
348
+ expect(user.email).toEqual(email)
349
+ })
350
+
351
+ it("if no email is provided and the user name is not an email, an exception is thrown", async () => {
352
+ const body: ScimCreateUserRequest = structures.scim.createUserRequest(
353
+ { username: structures.generator.name() }
354
+ )
355
+ delete body.emails
356
+
357
+ await postScimUser({ body }, { expect: 500 })
358
+ })
359
+ })
360
+
361
+ it("creating an existing user name returns a conflict", async () => {
362
+ const body = structures.scim.createUserRequest()
363
+
364
+ await postScimUser({ body })
365
+
366
+ const res = await postScimUser({ body }, { expect: 409 })
367
+ expect((res as any).message).toBe("Email already in use")
262
368
  })
263
369
  })
264
370
 
@@ -331,7 +437,7 @@ describe("scim", () => {
331
437
  name: {
332
438
  ...user.name,
333
439
  familyName: newFamilyName,
334
- formatted: `${user.name.givenName} ${newFamilyName}`,
440
+ formatted: `${user.name!.givenName} ${newFamilyName}`,
335
441
  },
336
442
  }
337
443
  expect(response).toEqual(expectedScimUser)
@@ -341,72 +447,40 @@ describe("scim", () => {
341
447
  })
342
448
 
343
449
  it.each([false, "false", "False"])(
344
- "can deactive an active user (sending %s)",
450
+ "deactivating an active user (sending %s) will delete it",
345
451
  async activeValue => {
346
452
  const body: ScimUpdateRequest = {
347
453
  schemas: ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
348
454
  Operations: [{ op: "Replace", path: "active", value: activeValue }],
349
455
  }
350
456
 
351
- const response = await patchScimUser({ id: user.id, body })
352
-
353
- const expectedScimUser: ScimUserResponse = {
354
- ...user,
355
- active: false,
356
- }
357
- expect(response).toEqual(expectedScimUser)
358
-
359
- const persistedUser = await config.api.scimUsersAPI.find(user.id)
360
- expect(persistedUser).toEqual(expectedScimUser)
361
- }
362
- )
363
-
364
- it.each([true, "true", "True"])(
365
- "can activate an inactive user (sending %s)",
366
- async activeValue => {
367
- // Deactivate user
368
- await patchScimUser({
369
- id: user.id,
370
- body: {
371
- schemas: ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
372
- Operations: [{ op: "Replace", path: "active", value: true }],
373
- },
374
- })
375
-
376
- const body: ScimUpdateRequest = {
377
- schemas: ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
378
- Operations: [{ op: "Replace", path: "active", value: activeValue }],
379
- }
380
-
381
- const response = await patchScimUser({ id: user.id, body })
382
-
383
- const expectedScimUser: ScimUserResponse = {
384
- ...user,
385
- active: true,
386
- }
387
- expect(response).toEqual(expectedScimUser)
457
+ await patchScimUser(
458
+ { id: user.id, body },
459
+ { expect: 204, skipContentTypeCheck: true }
460
+ )
388
461
 
389
- const persistedUser = await config.api.scimUsersAPI.find(user.id)
390
- expect(persistedUser).toEqual(expectedScimUser)
462
+ await config.api.scimUsersAPI.find(user.id, { expect: 404 })
391
463
  }
392
464
  )
393
465
 
394
466
  it("supports updating unmapped fields", async () => {
467
+ const value = structures.generator.letter()
395
468
  const body: ScimUpdateRequest = {
396
469
  schemas: ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
397
470
  Operations: [
398
471
  {
399
472
  op: "Add",
400
473
  path: "preferredLanguage",
401
- value: structures.generator.letter(),
474
+ value,
402
475
  },
403
476
  ],
404
477
  }
405
478
 
406
479
  const response = await patchScimUser({ id: user.id, body })
407
480
 
408
- const expectedScimUser: ScimUserResponse = {
481
+ const expectedScimUser = {
409
482
  ...user,
483
+ preferredLanguage: value,
410
484
  }
411
485
  expect(response).toEqual(expectedScimUser)
412
486
 
@@ -48,7 +48,7 @@ describe("/api/global/users", () => {
48
48
  400
49
49
  )
50
50
 
51
- expect(res.body.message).toBe("Unavailable")
51
+ expect(res.body.message).toBe(`Unavailable`)
52
52
  expect(sendMailMock).toHaveBeenCalledTimes(0)
53
53
  expect(code).toBeUndefined()
54
54
  expect(events.user.invited).toBeCalledTimes(0)
@@ -225,7 +225,9 @@ describe("/api/global/users", () => {
225
225
 
226
226
  const response = await config.api.users.saveUser(user, 400)
227
227
 
228
- expect(response.body.message).toBe(`Unavailable`)
228
+ expect(response.body.message).toBe(
229
+ `Email already in use: '${user.email}'`
230
+ )
229
231
  expect(events.user.created).toBeCalledTimes(0)
230
232
  })
231
233
 
@@ -237,7 +239,9 @@ describe("/api/global/users", () => {
237
239
  delete user._id
238
240
  const response = await config.api.users.saveUser(user, 400)
239
241
 
240
- expect(response.body.message).toBe(`Unavailable`)
242
+ expect(response.body.message).toBe(
243
+ `Email already in use: '${user.email}'`
244
+ )
241
245
  expect(events.user.created).toBeCalledTimes(0)
242
246
  })
243
247
  })
@@ -249,7 +253,9 @@ describe("/api/global/users", () => {
249
253
 
250
254
  const response = await config.api.users.saveUser(user, 400)
251
255
 
252
- expect(response.body.message).toBe(`Unavailable`)
256
+ expect(response.body.message).toBe(
257
+ `Email already in use: '${user.email}'`
258
+ )
253
259
  expect(events.user.created).toBeCalledTimes(0)
254
260
  })
255
261
 
@@ -47,7 +47,6 @@ const environment = {
47
47
  // flags
48
48
  NODE_ENV: process.env.NODE_ENV,
49
49
  SELF_HOSTED: !!parseInt(process.env.SELF_HOSTED || ""),
50
- LOG_LEVEL: process.env.LOG_LEVEL,
51
50
  MULTI_TENANCY: process.env.MULTI_TENANCY,
52
51
  DISABLE_ACCOUNT_PORTAL: process.env.DISABLE_ACCOUNT_PORTAL,
53
52
  SMTP_FALLBACK_ENABLED: process.env.SMTP_FALLBACK_ENABLED,
package/src/index.ts CHANGED
@@ -2,10 +2,6 @@ if (process.env.DD_APM_ENABLED) {
2
2
  require("./ddApm")
3
3
  }
4
4
 
5
- if (process.env.ELASTIC_APM_ENABLED) {
6
- require("./elasticApm")
7
- }
8
-
9
5
  // need to load environment first
10
6
  import env from "./environment"
11
7
  import { Scope } from "@sentry/node"
@@ -31,7 +27,6 @@ import api from "./api"
31
27
  import * as redis from "./utilities/redis"
32
28
  const Sentry = require("@sentry/node")
33
29
  const koaSession = require("koa-session")
34
- const logger = require("koa-pino-logger")
35
30
  const { userAgent } = require("koa-useragent")
36
31
 
37
32
  import destroyable from "server-destroy"
@@ -60,8 +55,8 @@ app.use(handleScimBody)
60
55
  app.use(koaBody({ multipart: true }))
61
56
 
62
57
  app.use(koaSession(app))
63
- app.use(middleware.logging)
64
- app.use(logger(logging.pinoSettings()))
58
+ app.use(middleware.correlation)
59
+ app.use(middleware.pino)
65
60
  app.use(userAgent)
66
61
 
67
62
  // authentication
@@ -16,6 +16,7 @@ import {
16
16
  ViewName,
17
17
  env as coreEnv,
18
18
  context,
19
+ EmailUnavailableError,
19
20
  } from "@budibase/backend-core"
20
21
  import {
21
22
  AccountMetadata,
@@ -158,7 +159,7 @@ const validateUniqueUser = async (email: string, tenantId: string) => {
158
159
  if (env.MULTI_TENANCY) {
159
160
  const tenantUser = await getPlatformUser(email)
160
161
  if (tenantUser != null && tenantUser.tenantId !== tenantId) {
161
- throw `Unavailable`
162
+ throw new EmailUnavailableError(email)
162
163
  }
163
164
  }
164
165
 
@@ -166,7 +167,7 @@ const validateUniqueUser = async (email: string, tenantId: string) => {
166
167
  if (!env.SELF_HOSTED && !env.DISABLE_ACCOUNT_PORTAL) {
167
168
  const account = await accounts.getAccount(email)
168
169
  if (account && account.verified && account.tenantId !== tenantId) {
169
- throw `Unavailable`
170
+ throw new EmailUnavailableError(email)
170
171
  }
171
172
  }
172
173
  }
@@ -235,7 +236,7 @@ export const save = async (
235
236
  // no id was specified - load from email instead
236
237
  dbUser = await usersCore.getGlobalUserByEmail(email)
237
238
  if (dbUser && dbUser._id !== _id) {
238
- throw `Unavailable`
239
+ throw new EmailUnavailableError(email)
239
240
  }
240
241
  }
241
242
 
@@ -4,6 +4,7 @@ import { TestAPI } from "../base"
4
4
  const defaultConfig = {
5
5
  expect: 200,
6
6
  setHeaders: true,
7
+ skipContentTypeCheck: false,
7
8
  }
8
9
 
9
10
  export type RequestSettings = typeof defaultConfig
@@ -27,7 +28,7 @@ export abstract class ScimTestAPI extends TestAPI {
27
28
  "application/scim+json; charset=utf-8"
28
29
  )
29
30
 
30
- if (method !== "delete") {
31
+ if (method !== "delete" && !requestSettings?.skipContentTypeCheck) {
31
32
  request = request.expect("Content-Type", /json/)
32
33
  }
33
34
 
@@ -2,7 +2,6 @@ process.env.SELF_HOSTED = "0"
2
2
  process.env.NODE_ENV = "jest"
3
3
  process.env.JWT_SECRET = "test-jwtsecret"
4
4
  process.env.LOG_LEVEL = process.env.LOG_LEVEL || "error"
5
- process.env.ENABLE_4XX_HTTP_LOGGING = "0"
6
5
  process.env.MULTI_TENANCY = "1"
7
6
  process.env.MINIO_URL = "http://localhost"
8
7
  process.env.MINIO_ACCESS_KEY = "test"
@@ -1,5 +1,3 @@
1
- import "./logging"
2
-
3
1
  import { mocks, testContainerUtils } from "@budibase/backend-core/tests"
4
2
  import env from "../environment"
5
3
  import { env as coreEnv, timers } from "@budibase/backend-core"
package/src/elasticApm.ts DELETED
@@ -1,10 +0,0 @@
1
- import apm from "elastic-apm-node"
2
-
3
- // enable APM if configured
4
- if (process.env.ELASTIC_APM_ENABLED) {
5
- console.log("Starting elastic-apm-node")
6
- apm.start({
7
- serviceName: process.env.SERVICE,
8
- environment: process.env.BUDIBASE_ENVIRONMENT,
9
- })
10
- }
@@ -1,34 +0,0 @@
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
- }