@budibase/worker 2.13.36 → 2.13.38

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.13.36",
4
+ "version": "2.13.38",
5
5
  "description": "Budibase background service",
6
6
  "main": "src/index.ts",
7
7
  "repository": {
@@ -21,7 +21,7 @@
21
21
  "debug": "yarn build && node --expose-gc --inspect=9223 dist/index.js",
22
22
  "run:docker:cluster": "pm2-runtime start pm2.config.js",
23
23
  "dev:stack:init": "node ./scripts/dev/manage.js init",
24
- "dev:builder": "npm run dev:stack:init && nodemon",
24
+ "dev": "npm run dev:stack:init && nodemon",
25
25
  "dev:built": "yarn run dev:stack:init && yarn run run:docker",
26
26
  "test": "bash scripts/test.sh",
27
27
  "test:watch": "jest --watch",
@@ -37,10 +37,10 @@
37
37
  "author": "Budibase",
38
38
  "license": "GPL-3.0",
39
39
  "dependencies": {
40
- "@budibase/backend-core": "2.13.36",
41
- "@budibase/pro": "2.13.36",
42
- "@budibase/string-templates": "2.13.36",
43
- "@budibase/types": "2.13.36",
40
+ "@budibase/backend-core": "2.13.38",
41
+ "@budibase/pro": "2.13.38",
42
+ "@budibase/string-templates": "2.13.38",
43
+ "@budibase/types": "2.13.38",
44
44
  "@koa/router": "8.0.8",
45
45
  "@techpass/passport-openidconnect": "0.3.2",
46
46
  "@types/global-agent": "2.1.1",
@@ -75,26 +75,26 @@
75
75
  "@swc/jest": "0.2.27",
76
76
  "@trendyol/jest-testcontainers": "2.1.1",
77
77
  "@types/jest": "29.5.5",
78
- "@types/jsonwebtoken": "8.5.1",
78
+ "@types/jsonwebtoken": "9.0.3",
79
79
  "@types/koa": "2.13.4",
80
80
  "@types/koa__router": "8.0.8",
81
81
  "@types/lodash": "4.14.200",
82
82
  "@types/node": "18.17.0",
83
83
  "@types/node-fetch": "2.6.4",
84
84
  "@types/server-destroy": "1.0.1",
85
- "@types/supertest": "2.0.12",
85
+ "@types/supertest": "2.0.14",
86
86
  "@types/uuid": "8.3.4",
87
- "jest": "29.6.2",
87
+ "jest": "29.7.0",
88
88
  "nodemon": "2.0.15",
89
89
  "rimraf": "3.0.2",
90
- "supertest": "6.2.2",
90
+ "supertest": "6.3.3",
91
91
  "timekeeper": "2.2.0",
92
92
  "typescript": "5.2.2",
93
93
  "update-dotenv": "1.1.1"
94
94
  },
95
95
  "nx": {
96
96
  "targets": {
97
- "dev:builder": {
97
+ "dev": {
98
98
  "dependsOn": [
99
99
  {
100
100
  "comment": "Required for pro usage when submodule not loaded",
@@ -107,5 +107,5 @@
107
107
  }
108
108
  }
109
109
  },
110
- "gitHead": "933421818c287b302773026855256fce4845259a"
110
+ "gitHead": "02e573ced8c766dcac96b29af4eeeab1416e22f4"
111
111
  }
@@ -1,44 +1,40 @@
1
1
  #!/usr/bin/env node
2
- const path = require("path")
3
- const fs = require("fs")
2
+ const { parsed: existingConfig } = require("dotenv").config()
3
+ const updateDotEnv = require("update-dotenv")
4
4
 
5
5
  async function init() {
6
- const envFilePath = path.join(process.cwd(), ".env")
7
- if (!fs.existsSync(envFilePath)) {
8
- const envFileJson = {
9
- SELF_HOSTED: 1,
10
- PORT: 4002,
11
- CLUSTER_PORT: 10000,
12
- JWT_SECRET: "testsecret",
13
- INTERNAL_API_KEY: "budibase",
14
- MINIO_ACCESS_KEY: "budibase",
15
- MINIO_SECRET_KEY: "budibase",
16
- REDIS_URL: "localhost:6379",
17
- REDIS_PASSWORD: "budibase",
18
- MINIO_URL: "http://localhost:4004",
19
- COUCH_DB_URL: "http://budibase:budibase@localhost:4005",
20
- COUCH_DB_USERNAME: "budibase",
21
- COUCH_DB_PASSWORD: "budibase",
22
- // empty string is false
23
- MULTI_TENANCY: "",
24
- DISABLE_ACCOUNT_PORTAL: 1,
25
- ACCOUNT_PORTAL_URL: "http://localhost:10001",
26
- ACCOUNT_PORTAL_API_KEY: "budibase",
27
- PLATFORM_URL: "http://localhost:10000",
28
- APPS_URL: "http://localhost:4001",
29
- SERVICE: "worker-service",
30
- DEPLOYMENT_ENVIRONMENT: "development",
31
- TENANT_FEATURE_FLAGS: "*:LICENSING,*:USER_GROUPS,*:ONBOARDING_TOUR",
32
- ENABLE_EMAIL_TEST_MODE: 1,
33
- HTTP_LOGGING: 0,
34
- VERSION: "0.0.0+local",
35
- }
36
- let envFile = ""
37
- Object.keys(envFileJson).forEach(key => {
38
- envFile += `${key}=${envFileJson[key]}\n`
39
- })
40
- fs.writeFileSync(envFilePath, envFile)
6
+ let config = {
7
+ SELF_HOSTED: "1",
8
+ PORT: "4002",
9
+ CLUSTER_PORT: "10000",
10
+ JWT_SECRET: "testsecret",
11
+ INTERNAL_API_KEY: "budibase",
12
+ MINIO_ACCESS_KEY: "budibase",
13
+ MINIO_SECRET_KEY: "budibase",
14
+ REDIS_URL: "localhost:6379",
15
+ REDIS_PASSWORD: "budibase",
16
+ MINIO_URL: "http://localhost:4004",
17
+ COUCH_DB_URL: "http://budibase:budibase@localhost:4005",
18
+ COUCH_DB_USERNAME: "budibase",
19
+ COUCH_DB_PASSWORD: "budibase",
20
+ // empty string is false
21
+ MULTI_TENANCY: "",
22
+ DISABLE_ACCOUNT_PORTAL: "1",
23
+ ACCOUNT_PORTAL_URL: "http://localhost:10001",
24
+ ACCOUNT_PORTAL_API_KEY: "budibase",
25
+ PLATFORM_URL: "http://localhost:10000",
26
+ APPS_URL: "http://localhost:4001",
27
+ SERVICE: "worker-service",
28
+ DEPLOYMENT_ENVIRONMENT: "development",
29
+ TENANT_FEATURE_FLAGS: "*:LICENSING,*:USER_GROUPS,*:ONBOARDING_TOUR",
30
+ ENABLE_EMAIL_TEST_MODE: "1",
31
+ HTTP_LOGGING: "0",
32
+ VERSION: "0.0.0+local",
41
33
  }
34
+
35
+ config = { ...config, ...existingConfig }
36
+
37
+ await updateDotEnv(config)
42
38
  }
43
39
 
44
40
  // if more than init required use this to determine the command type
@@ -26,6 +26,7 @@ import {
26
26
  migrations,
27
27
  platform,
28
28
  tenancy,
29
+ db,
29
30
  } from "@budibase/backend-core"
30
31
  import { checkAnyUserExists } from "../../../utilities/users"
31
32
  import { isEmailConfigured } from "../../../utilities/email"
@@ -185,9 +186,27 @@ export const getAppUsers = async (ctx: Ctx<SearchUsersRequest>) => {
185
186
  export const search = async (ctx: Ctx<SearchUsersRequest>) => {
186
187
  const body = ctx.request.body
187
188
 
188
- // TODO: for now only one supported search key, string.email
189
- if (body?.query && !userSdk.core.isSupportedUserSearch(body.query)) {
190
- ctx.throw(501, "Can only search by string.email or equal._id")
189
+ // TODO: for now only two supported search keys; string.email and equal._id
190
+ if (body?.query) {
191
+ // Clean numeric prefixing. This will overwrite duplicate search fields,
192
+ // but this is fine because we only support a single custom search on
193
+ // email and id
194
+ for (let filters of Object.values(body.query)) {
195
+ if (filters && typeof filters === "object") {
196
+ for (let [field, value] of Object.entries(filters)) {
197
+ delete filters[field]
198
+ const cleanedField = db.removeKeyNumbering(field)
199
+ if (filters[cleanedField] !== undefined) {
200
+ ctx.throw(400, "Only 1 filter per field is supported")
201
+ }
202
+ filters[cleanedField] = value
203
+ }
204
+ }
205
+ }
206
+ // Validate we aren't trying to search on any illegal fields
207
+ if (!userSdk.core.isSupportedUserSearch(body.query)) {
208
+ ctx.throw(400, "Can only search by string.email or equal._id")
209
+ }
191
210
  }
192
211
 
193
212
  if (body.paginate === false) {
@@ -1,6 +1,25 @@
1
1
  import { Ctx } from "@budibase/types"
2
2
  import env from "../../../environment"
3
3
  import { env as coreEnv } from "@budibase/backend-core"
4
+ import nodeFetch from "node-fetch"
5
+
6
+ let sqsAvailable: boolean
7
+ async function isSqsAvailable() {
8
+ if (sqsAvailable !== undefined) {
9
+ return sqsAvailable
10
+ }
11
+
12
+ try {
13
+ await nodeFetch(coreEnv.COUCH_DB_SQL_URL, {
14
+ timeout: 1000,
15
+ })
16
+ sqsAvailable = true
17
+ return true
18
+ } catch (e) {
19
+ sqsAvailable = false
20
+ return false
21
+ }
22
+ }
4
23
 
5
24
  export const fetch = async (ctx: Ctx) => {
6
25
  ctx.body = {
@@ -12,4 +31,10 @@ export const fetch = async (ctx: Ctx) => {
12
31
  baseUrl: env.PLATFORM_URL,
13
32
  isDev: env.isDev() && !env.isTest(),
14
33
  }
34
+
35
+ if (env.SELF_HOSTED) {
36
+ ctx.body.infrastructure = {
37
+ sqs: await isSqsAvailable(),
38
+ }
39
+ }
15
40
  }
@@ -590,6 +590,15 @@ describe("/api/global/users", () => {
590
590
  expect(response.body.data[0].email).toBe(user.email)
591
591
  })
592
592
 
593
+ it("should be able to search by email with numeric prefixing", async () => {
594
+ const user = await config.createUser()
595
+ const response = await config.api.users.searchUsers({
596
+ query: { string: { ["999:email"]: user.email } },
597
+ })
598
+ expect(response.body.data.length).toBe(1)
599
+ expect(response.body.data[0].email).toBe(user.email)
600
+ })
601
+
593
602
  it("should be able to search by _id", async () => {
594
603
  const user = await config.createUser()
595
604
  const response = await config.api.users.searchUsers({
@@ -599,13 +608,52 @@ describe("/api/global/users", () => {
599
608
  expect(response.body.data[0]._id).toBe(user._id)
600
609
  })
601
610
 
611
+ it("should be able to search by _id with numeric prefixing", async () => {
612
+ const user = await config.createUser()
613
+ const response = await config.api.users.searchUsers({
614
+ query: { equal: { ["1:_id"]: user._id } },
615
+ })
616
+ expect(response.body.data.length).toBe(1)
617
+ expect(response.body.data[0]._id).toBe(user._id)
618
+ })
619
+
620
+ it("should throw an error when using multiple filters on the same field", async () => {
621
+ const user = await config.createUser()
622
+ await config.api.users.searchUsers(
623
+ {
624
+ query: {
625
+ string: {
626
+ ["1:email"]: user.email,
627
+ ["2:email"]: "something else",
628
+ },
629
+ },
630
+ },
631
+ { status: 400 }
632
+ )
633
+ })
634
+
635
+ it("should throw an error when using multiple filters on the same field without prefixes", async () => {
636
+ const user = await config.createUser()
637
+ await config.api.users.searchUsers(
638
+ {
639
+ query: {
640
+ string: {
641
+ ["_id"]: user.email,
642
+ ["999:_id"]: "something else",
643
+ },
644
+ },
645
+ },
646
+ { status: 400 }
647
+ )
648
+ })
649
+
602
650
  it("should throw an error when unimplemented options used", async () => {
603
651
  const user = await config.createUser()
604
652
  await config.api.users.searchUsers(
605
653
  {
606
654
  query: { equal: { firstName: user.firstName } },
607
655
  },
608
- { status: 501 }
656
+ { status: 400 }
609
657
  )
610
658
  })
611
659
 
@@ -1,5 +1,7 @@
1
1
  import { TestConfiguration } from "../../../../tests"
2
2
 
3
+ jest.unmock("node-fetch")
4
+
3
5
  describe("/api/system/environment", () => {
4
6
  const config = new TestConfiguration()
5
7
 
@@ -27,5 +29,22 @@ describe("/api/system/environment", () => {
27
29
  offlineMode: false,
28
30
  })
29
31
  })
32
+
33
+ it("returns the expected environment for self hosters", async () => {
34
+ await config.withEnv({ SELF_HOSTED: true }, async () => {
35
+ const env = await config.api.environment.getEnvironment()
36
+ expect(env.body).toEqual({
37
+ cloud: false,
38
+ disableAccountPortal: 0,
39
+ isDev: false,
40
+ multiTenancy: true,
41
+ baseUrl: "http://localhost:10000",
42
+ offlineMode: false,
43
+ infrastructure: {
44
+ sqs: false,
45
+ },
46
+ })
47
+ })
48
+ })
30
49
  })
31
50
  })
@@ -36,6 +36,7 @@ import {
36
36
  } from "@budibase/types"
37
37
  import API from "./api"
38
38
  import jwt, { Secret } from "jsonwebtoken"
39
+ import cloneDeep from "lodash/fp/cloneDeep"
39
40
 
40
41
  class TestConfiguration {
41
42
  server: any
@@ -240,6 +241,34 @@ class TestConfiguration {
240
241
  return { message: "Admin user only endpoint.", status: 403 }
241
242
  }
242
243
 
244
+ async withEnv(newEnvVars: Partial<typeof env>, f: () => Promise<void>) {
245
+ let cleanup = this.setEnv(newEnvVars)
246
+ try {
247
+ await f()
248
+ } finally {
249
+ cleanup()
250
+ }
251
+ }
252
+
253
+ /*
254
+ * Sets the environment variables to the given values and returns a function
255
+ * that can be called to reset the environment variables to their original values.
256
+ */
257
+ setEnv(newEnvVars: Partial<typeof env>): () => void {
258
+ const oldEnv = cloneDeep(env)
259
+
260
+ let key: keyof typeof newEnvVars
261
+ for (key in newEnvVars) {
262
+ env._set(key, newEnvVars[key])
263
+ }
264
+
265
+ return () => {
266
+ for (const [key, value] of Object.entries(oldEnv)) {
267
+ env._set(key, value)
268
+ }
269
+ }
270
+ }
271
+
243
272
  // USERS
244
273
 
245
274
  async createDefaultUser() {