@budibase/worker 2.21.8 → 2.22.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.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@budibase/worker",
3
3
  "email": "hi@budibase.com",
4
- "version": "2.21.8",
4
+ "version": "2.22.0",
5
5
  "description": "Budibase background service",
6
6
  "main": "src/index.ts",
7
7
  "repository": {
@@ -37,10 +37,10 @@
37
37
  "author": "Budibase",
38
38
  "license": "GPL-3.0",
39
39
  "dependencies": {
40
- "@budibase/backend-core": "2.21.8",
41
- "@budibase/pro": "2.21.8",
42
- "@budibase/string-templates": "2.21.8",
43
- "@budibase/types": "2.21.8",
40
+ "@budibase/backend-core": "2.22.0",
41
+ "@budibase/pro": "2.22.0",
42
+ "@budibase/string-templates": "2.22.0",
43
+ "@budibase/types": "2.22.0",
44
44
  "@koa/router": "8.0.8",
45
45
  "@techpass/passport-openidconnect": "0.3.2",
46
46
  "@types/global-agent": "2.1.1",
@@ -108,5 +108,5 @@
108
108
  }
109
109
  }
110
110
  },
111
- "gitHead": "f2541bd150fcb84cd403e29a759843b2952563a4"
111
+ "gitHead": "45f438318a63b0f60cbee8cef5115ca7947f356b"
112
112
  }
@@ -9,7 +9,12 @@ import {
9
9
  } from "@budibase/backend-core"
10
10
  import env from "../../../environment"
11
11
  import { groups } from "@budibase/pro"
12
- import { UpdateSelfRequest, UpdateSelfResponse, UserCtx } from "@budibase/types"
12
+ import {
13
+ UpdateSelfRequest,
14
+ UpdateSelfResponse,
15
+ User,
16
+ UserCtx,
17
+ } from "@budibase/types"
13
18
 
14
19
  const { newid } = utils
15
20
 
@@ -105,16 +110,63 @@ export async function getSelf(ctx: any) {
105
110
  addSessionAttributesToUser(ctx)
106
111
  }
107
112
 
113
+ export const syncAppFavourites = async (processedAppIds: string[]) => {
114
+ if (processedAppIds.length === 0) {
115
+ return []
116
+ }
117
+ const apps = await fetchAppsByIds(processedAppIds)
118
+ return apps?.reduce((acc: string[], app) => {
119
+ const id = app.appId.replace(dbCore.APP_DEV_PREFIX, "")
120
+ if (processedAppIds.includes(id)) {
121
+ acc.push(id)
122
+ }
123
+ return acc
124
+ }, [])
125
+ }
126
+
127
+ export const fetchAppsByIds = async (processedAppIds: string[]) => {
128
+ return await dbCore.getAppsByIDs(
129
+ processedAppIds.map(appId => `${dbCore.APP_DEV_PREFIX}${appId}`)
130
+ )
131
+ }
132
+
133
+ const processUserAppFavourites = async (
134
+ user: User,
135
+ update: UpdateSelfRequest
136
+ ) => {
137
+ if (!("appFavourites" in update)) {
138
+ // Ignore requests without an explicit update to favourites.
139
+ return
140
+ }
141
+
142
+ const userAppFavourites = user.appFavourites || []
143
+ const requestAppFavourites = new Set(update.appFavourites || [])
144
+ const containsAll = userAppFavourites.every(v => requestAppFavourites.has(v))
145
+
146
+ if (containsAll && requestAppFavourites.size === userAppFavourites.length) {
147
+ // Ignore request if the outcome will have no change
148
+ return
149
+ }
150
+
151
+ // Clean up the request by purging apps that no longer exist.
152
+ const syncedAppFavourites = await syncAppFavourites([...requestAppFavourites])
153
+ return syncedAppFavourites
154
+ }
155
+
108
156
  export async function updateSelf(
109
157
  ctx: UserCtx<UpdateSelfRequest, UpdateSelfResponse>
110
158
  ) {
111
159
  const update = ctx.request.body
112
160
 
113
161
  let user = await userSdk.db.getUser(ctx.user._id!)
162
+ const updatedAppFavourites = await processUserAppFavourites(user, update)
163
+
114
164
  user = {
115
165
  ...user,
116
166
  ...update,
167
+ ...(updatedAppFavourites ? { appFavourites: updatedAppFavourites } : {}),
117
168
  }
169
+
118
170
  user = await userSdk.db.save(user, { requirePassword: false })
119
171
 
120
172
  if (update.password) {
@@ -1,10 +1,18 @@
1
- import { Ctx } from "@budibase/types"
1
+ import { Ctx, MaintenanceType } from "@budibase/types"
2
2
  import env from "../../../environment"
3
3
  import { env as coreEnv } from "@budibase/backend-core"
4
4
  import nodeFetch from "node-fetch"
5
5
 
6
+ // When we come to move to SQS fully and move away from Clouseau, we will need
7
+ // to flip this to true (or remove it entirely). This will then be used to
8
+ // determine if we should show the maintenance page that links to the SQS
9
+ // migration docs.
10
+ const sqsRequired = false
11
+
6
12
  let sqsAvailable: boolean
7
13
  async function isSqsAvailable() {
14
+ // We cache this value for the duration of the Node process because we don't
15
+ // want every page load to be making this relatively expensive check.
8
16
  if (sqsAvailable !== undefined) {
9
17
  return sqsAvailable
10
18
  }
@@ -21,6 +29,10 @@ async function isSqsAvailable() {
21
29
  }
22
30
  }
23
31
 
32
+ async function isSqsMissing() {
33
+ return sqsRequired && !(await isSqsAvailable())
34
+ }
35
+
24
36
  export const fetch = async (ctx: Ctx) => {
25
37
  ctx.body = {
26
38
  multiTenancy: !!env.MULTI_TENANCY,
@@ -30,11 +42,12 @@ export const fetch = async (ctx: Ctx) => {
30
42
  disableAccountPortal: env.DISABLE_ACCOUNT_PORTAL,
31
43
  baseUrl: env.PLATFORM_URL,
32
44
  isDev: env.isDev() && !env.isTest(),
45
+ maintenance: [],
33
46
  }
34
47
 
35
48
  if (env.SELF_HOSTED) {
36
- ctx.body.infrastructure = {
37
- sqs: await isSqsAvailable(),
49
+ if (await isSqsMissing()) {
50
+ ctx.body.maintenance.push({ type: MaintenanceType.SQS_MISSING })
38
51
  }
39
52
  }
40
53
  }
@@ -2,6 +2,7 @@ import tk from "timekeeper"
2
2
  import _ from "lodash"
3
3
  import { generator, mocks, structures } from "@budibase/backend-core/tests"
4
4
  import {
5
+ CloudAccount,
5
6
  ScimCreateUserRequest,
6
7
  ScimGroupResponse,
7
8
  ScimUpdateRequest,
@@ -604,6 +605,25 @@ describe("scim", () => {
604
605
 
605
606
  expect(events.user.deleted).toBeCalledTimes(1)
606
607
  })
608
+
609
+ it("an account holder cannot be removed even when synched", async () => {
610
+ const account: CloudAccount = {
611
+ ...structures.accounts.account(),
612
+ budibaseUserId: user.id,
613
+ email: user.emails![0].value,
614
+ }
615
+ mocks.accounts.getAccount.mockResolvedValue(account)
616
+
617
+ await deleteScimUser(user.id, {
618
+ expect: {
619
+ message: "Account holder cannot be deleted",
620
+ status: 400,
621
+ error: { code: "http" },
622
+ },
623
+ })
624
+
625
+ await config.api.scimUsersAPI.find(user.id, { expect: 200 })
626
+ })
607
627
  })
608
628
  })
609
629
 
@@ -27,6 +27,7 @@ describe("/api/system/environment", () => {
27
27
  multiTenancy: true,
28
28
  baseUrl: "http://localhost:10000",
29
29
  offlineMode: false,
30
+ maintenance: [],
30
31
  })
31
32
  })
32
33
 
@@ -40,9 +41,7 @@ describe("/api/system/environment", () => {
40
41
  multiTenancy: true,
41
42
  baseUrl: "http://localhost:10000",
42
43
  offlineMode: false,
43
- infrastructure: {
44
- sqs: false,
45
- },
44
+ maintenance: [],
46
45
  })
47
46
  })
48
47
  })
@@ -26,6 +26,7 @@ export const buildSelfSaveValidation = () => {
26
26
  firstName: OPTIONAL_STRING,
27
27
  lastName: OPTIONAL_STRING,
28
28
  onboardedAt: Joi.string().optional(),
29
+ appFavourites: Joi.array().optional(),
29
30
  tours: Joi.object().optional(),
30
31
  }
31
32
  return auth.joiValidator.body(Joi.object(schema).required().unknown(false))
@@ -1,13 +1,17 @@
1
1
  import TestConfiguration from "../../TestConfiguration"
2
2
  import { TestAPI } from "../base"
3
3
 
4
- const defaultConfig = {
4
+ const defaultConfig: RequestSettings = {
5
5
  expect: 200,
6
6
  setHeaders: true,
7
7
  skipContentTypeCheck: false,
8
8
  }
9
9
 
10
- export type RequestSettings = typeof defaultConfig
10
+ export type RequestSettings = {
11
+ expect: number | object
12
+ setHeaders: boolean
13
+ skipContentTypeCheck: boolean
14
+ }
11
15
 
12
16
  export abstract class ScimTestAPI extends TestAPI {
13
17
  constructor(config: TestConfiguration) {