@budibase/worker 2.6.16-alpha.0 → 2.6.16
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/jest.config.ts +5 -5
- package/nodemon.json +2 -6
- package/package.json +6 -6
- package/src/api/controllers/global/users.ts +16 -17
- package/src/api/routes/global/tests/groups.spec.ts +0 -176
- package/src/tests/api/groups.ts +2 -32
- package/src/tests/structures/groups.ts +4 -19
- package/tsconfig.json +1 -1
package/jest.config.ts
CHANGED
|
@@ -20,11 +20,11 @@ const config: Config.InitialOptions = {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
// add pro sources if they exist
|
|
23
|
-
if (fs.existsSync("
|
|
24
|
-
config.moduleNameMapper
|
|
25
|
-
"<rootDir
|
|
26
|
-
config.moduleNameMapper
|
|
27
|
-
"<rootDir
|
|
23
|
+
if (fs.existsSync("../../../budibase-pro")) {
|
|
24
|
+
config.moduleNameMapper["@budibase/pro/(.*)"] =
|
|
25
|
+
"<rootDir>/../../../budibase-pro/packages/pro/$1"
|
|
26
|
+
config.moduleNameMapper["@budibase/pro"] =
|
|
27
|
+
"<rootDir>/../../../budibase-pro/packages/pro/src"
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
export default config
|
package/nodemon.json
CHANGED
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"watch": ["src", "../backend-core", "
|
|
2
|
+
"watch": ["src", "../backend-core", "../../../budibase-pro/packages/pro"],
|
|
3
3
|
"ext": "js,ts,json",
|
|
4
|
-
"ignore": [
|
|
5
|
-
"src/**/*.spec.ts",
|
|
6
|
-
"src/**/*.spec.js",
|
|
7
|
-
"../backend-core/dist/**/*"
|
|
8
|
-
],
|
|
4
|
+
"ignore": ["src/**/*.spec.ts", "src/**/*.spec.js", "../backend-core/dist/**/*"],
|
|
9
5
|
"exec": "ts-node src/index.ts"
|
|
10
6
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@budibase/worker",
|
|
3
3
|
"email": "hi@budibase.com",
|
|
4
|
-
"version": "2.6.16
|
|
4
|
+
"version": "2.6.16",
|
|
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.6.16
|
|
41
|
-
"@budibase/pro": "2.6.
|
|
42
|
-
"@budibase/string-templates": "2.6.16
|
|
43
|
-
"@budibase/types": "2.6.16
|
|
40
|
+
"@budibase/backend-core": "^2.6.16",
|
|
41
|
+
"@budibase/pro": "2.6.15",
|
|
42
|
+
"@budibase/string-templates": "^2.6.16",
|
|
43
|
+
"@budibase/types": "^2.6.16",
|
|
44
44
|
"@koa/router": "8.0.8",
|
|
45
45
|
"@sentry/node": "6.17.7",
|
|
46
46
|
"@techpass/passport-openidconnect": "0.3.2",
|
|
@@ -102,5 +102,5 @@
|
|
|
102
102
|
"typescript": "4.7.3",
|
|
103
103
|
"update-dotenv": "1.1.1"
|
|
104
104
|
},
|
|
105
|
-
"gitHead": "
|
|
105
|
+
"gitHead": "d43a6a44bd074e077ea131303c0f7a2b0e0ddb75"
|
|
106
106
|
}
|
|
@@ -69,11 +69,9 @@ const bulkCreate = async (users: User[], groupIds: string[]) => {
|
|
|
69
69
|
return await userSdk.bulkCreate(users, groupIds)
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
-
export const bulkUpdate = async (
|
|
73
|
-
ctx: Ctx<BulkUserRequest, BulkUserResponse>
|
|
74
|
-
) => {
|
|
72
|
+
export const bulkUpdate = async (ctx: any) => {
|
|
75
73
|
const currentUserId = ctx.user._id
|
|
76
|
-
const input = ctx.request.body
|
|
74
|
+
const input = ctx.request.body as BulkUserRequest
|
|
77
75
|
let created, deleted
|
|
78
76
|
try {
|
|
79
77
|
if (input.create) {
|
|
@@ -85,7 +83,7 @@ export const bulkUpdate = async (
|
|
|
85
83
|
} catch (err: any) {
|
|
86
84
|
ctx.throw(err.status || 400, err?.message || err)
|
|
87
85
|
}
|
|
88
|
-
ctx.body = { created, deleted }
|
|
86
|
+
ctx.body = { created, deleted } as BulkUserResponse
|
|
89
87
|
}
|
|
90
88
|
|
|
91
89
|
const parseBooleanParam = (param: any) => {
|
|
@@ -186,15 +184,15 @@ export const destroy = async (ctx: any) => {
|
|
|
186
184
|
}
|
|
187
185
|
}
|
|
188
186
|
|
|
189
|
-
export const getAppUsers = async (ctx:
|
|
190
|
-
const body = ctx.request.body
|
|
187
|
+
export const getAppUsers = async (ctx: any) => {
|
|
188
|
+
const body = ctx.request.body as SearchUsersRequest
|
|
191
189
|
const users = await userSdk.getUsersByAppAccess(body?.appId)
|
|
192
190
|
|
|
193
191
|
ctx.body = { data: users }
|
|
194
192
|
}
|
|
195
193
|
|
|
196
|
-
export const search = async (ctx:
|
|
197
|
-
const body = ctx.request.body
|
|
194
|
+
export const search = async (ctx: any) => {
|
|
195
|
+
const body = ctx.request.body as SearchUsersRequest
|
|
198
196
|
|
|
199
197
|
if (body.paginated === false) {
|
|
200
198
|
await getAppUsers(ctx)
|
|
@@ -240,8 +238,8 @@ export const tenantUserLookup = async (ctx: any) => {
|
|
|
240
238
|
/*
|
|
241
239
|
Encapsulate the app user onboarding flows here.
|
|
242
240
|
*/
|
|
243
|
-
export const onboardUsers = async (ctx:
|
|
244
|
-
const request = ctx.request.body
|
|
241
|
+
export const onboardUsers = async (ctx: any) => {
|
|
242
|
+
const request = ctx.request.body as InviteUsersRequest | BulkUserRequest
|
|
245
243
|
const isBulkCreate = "create" in request
|
|
246
244
|
|
|
247
245
|
const emailConfigured = await isEmailConfigured()
|
|
@@ -257,7 +255,7 @@ export const onboardUsers = async (ctx: Ctx<InviteUsersRequest>) => {
|
|
|
257
255
|
} else if (emailConfigured) {
|
|
258
256
|
onboardingResponse = await inviteMultiple(ctx)
|
|
259
257
|
} else if (!emailConfigured) {
|
|
260
|
-
const inviteRequest = ctx.request.body
|
|
258
|
+
const inviteRequest = ctx.request.body as InviteUsersRequest
|
|
261
259
|
|
|
262
260
|
let createdPasswords: any = {}
|
|
263
261
|
|
|
@@ -297,10 +295,10 @@ export const onboardUsers = async (ctx: Ctx<InviteUsersRequest>) => {
|
|
|
297
295
|
}
|
|
298
296
|
}
|
|
299
297
|
|
|
300
|
-
export const invite = async (ctx:
|
|
301
|
-
const request = ctx.request.body
|
|
298
|
+
export const invite = async (ctx: any) => {
|
|
299
|
+
const request = ctx.request.body as InviteUserRequest
|
|
302
300
|
|
|
303
|
-
let multiRequest = [request]
|
|
301
|
+
let multiRequest = [request] as InviteUsersRequest
|
|
304
302
|
const response = await userSdk.invite(multiRequest)
|
|
305
303
|
|
|
306
304
|
// explicitly throw for single user invite
|
|
@@ -320,8 +318,8 @@ export const invite = async (ctx: Ctx<InviteUserRequest>) => {
|
|
|
320
318
|
}
|
|
321
319
|
}
|
|
322
320
|
|
|
323
|
-
export const inviteMultiple = async (ctx:
|
|
324
|
-
const request = ctx.request.body
|
|
321
|
+
export const inviteMultiple = async (ctx: any) => {
|
|
322
|
+
const request = ctx.request.body as InviteUsersRequest
|
|
325
323
|
ctx.body = await userSdk.invite(request)
|
|
326
324
|
}
|
|
327
325
|
|
|
@@ -426,6 +424,7 @@ export const inviteAccept = async (
|
|
|
426
424
|
if (err.code === ErrorCode.USAGE_LIMIT_EXCEEDED) {
|
|
427
425
|
// explicitly re-throw limit exceeded errors
|
|
428
426
|
ctx.throw(400, err)
|
|
427
|
+
return
|
|
429
428
|
}
|
|
430
429
|
console.warn("Error inviting user", err)
|
|
431
430
|
ctx.throw(400, "Unable to create new user, invitation invalid.")
|
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
import { events } from "@budibase/backend-core"
|
|
2
|
-
import { generator } from "@budibase/backend-core/tests"
|
|
3
2
|
import { structures, TestConfiguration, mocks } from "../../../../tests"
|
|
4
|
-
import { UserGroup } from "@budibase/types"
|
|
5
|
-
|
|
6
|
-
mocks.licenses.useGroups()
|
|
7
3
|
|
|
8
4
|
describe("/api/global/groups", () => {
|
|
9
5
|
const config = new TestConfiguration()
|
|
@@ -17,7 +13,6 @@ describe("/api/global/groups", () => {
|
|
|
17
13
|
})
|
|
18
14
|
|
|
19
15
|
beforeEach(async () => {
|
|
20
|
-
jest.resetAllMocks()
|
|
21
16
|
mocks.licenses.useGroups()
|
|
22
17
|
})
|
|
23
18
|
|
|
@@ -29,63 +24,6 @@ describe("/api/global/groups", () => {
|
|
|
29
24
|
expect(events.group.updated).not.toBeCalled()
|
|
30
25
|
expect(events.group.permissionsEdited).not.toBeCalled()
|
|
31
26
|
})
|
|
32
|
-
|
|
33
|
-
it("should not allow undefined names", async () => {
|
|
34
|
-
const group = { ...structures.groups.UserGroup(), name: undefined } as any
|
|
35
|
-
const response = await config.api.groups.saveGroup(group, { expect: 400 })
|
|
36
|
-
expect(JSON.parse(response.text).message).toEqual(
|
|
37
|
-
'Invalid body - "name" is required'
|
|
38
|
-
)
|
|
39
|
-
})
|
|
40
|
-
|
|
41
|
-
it("should not allow empty names", async () => {
|
|
42
|
-
const group = { ...structures.groups.UserGroup(), name: "" }
|
|
43
|
-
const response = await config.api.groups.saveGroup(group, { expect: 400 })
|
|
44
|
-
expect(JSON.parse(response.text).message).toEqual(
|
|
45
|
-
'Invalid body - "name" is not allowed to be empty'
|
|
46
|
-
)
|
|
47
|
-
})
|
|
48
|
-
|
|
49
|
-
it("should not allow whitespace names", async () => {
|
|
50
|
-
const group = { ...structures.groups.UserGroup(), name: " " }
|
|
51
|
-
const response = await config.api.groups.saveGroup(group, { expect: 400 })
|
|
52
|
-
expect(JSON.parse(response.text).message).toEqual(
|
|
53
|
-
'Invalid body - "name" is not allowed to be empty'
|
|
54
|
-
)
|
|
55
|
-
})
|
|
56
|
-
|
|
57
|
-
it("should trim names", async () => {
|
|
58
|
-
const group = { ...structures.groups.UserGroup(), name: " group name " }
|
|
59
|
-
await config.api.groups.saveGroup(group)
|
|
60
|
-
expect(events.group.created).toBeCalledWith(
|
|
61
|
-
expect.objectContaining({ name: "group name" })
|
|
62
|
-
)
|
|
63
|
-
})
|
|
64
|
-
|
|
65
|
-
describe("name max length", () => {
|
|
66
|
-
const maxLength = 50
|
|
67
|
-
|
|
68
|
-
it(`should allow names shorter than ${maxLength} characters`, async () => {
|
|
69
|
-
const group = {
|
|
70
|
-
...structures.groups.UserGroup(),
|
|
71
|
-
name: structures.generator.word({ length: maxLength }),
|
|
72
|
-
}
|
|
73
|
-
await config.api.groups.saveGroup(group, { expect: 200 })
|
|
74
|
-
})
|
|
75
|
-
|
|
76
|
-
it(`should not allow names longer than ${maxLength} characters`, async () => {
|
|
77
|
-
const group = {
|
|
78
|
-
...structures.groups.UserGroup(),
|
|
79
|
-
name: structures.generator.word({ length: maxLength + 1 }),
|
|
80
|
-
}
|
|
81
|
-
const response = await config.api.groups.saveGroup(group, {
|
|
82
|
-
expect: 400,
|
|
83
|
-
})
|
|
84
|
-
expect(JSON.parse(response.text).message).toEqual(
|
|
85
|
-
'Invalid body - "name" length must be less than or equal to 50 characters long'
|
|
86
|
-
)
|
|
87
|
-
})
|
|
88
|
-
})
|
|
89
27
|
})
|
|
90
28
|
|
|
91
29
|
describe("update", () => {
|
|
@@ -117,118 +55,4 @@ describe("/api/global/groups", () => {
|
|
|
117
55
|
})
|
|
118
56
|
})
|
|
119
57
|
})
|
|
120
|
-
|
|
121
|
-
describe("find users", () => {
|
|
122
|
-
describe("without users", () => {
|
|
123
|
-
let group: UserGroup
|
|
124
|
-
beforeAll(async () => {
|
|
125
|
-
group = structures.groups.UserGroup()
|
|
126
|
-
await config.api.groups.saveGroup(group)
|
|
127
|
-
})
|
|
128
|
-
|
|
129
|
-
it("should return empty", async () => {
|
|
130
|
-
const result = await config.api.groups.searchUsers(group._id!)
|
|
131
|
-
expect(result.body).toEqual({
|
|
132
|
-
users: [],
|
|
133
|
-
bookmark: undefined,
|
|
134
|
-
hasNextPage: false,
|
|
135
|
-
})
|
|
136
|
-
})
|
|
137
|
-
})
|
|
138
|
-
|
|
139
|
-
describe("existing users", () => {
|
|
140
|
-
let groupId: string
|
|
141
|
-
let users: { _id: string; email: string }[] = []
|
|
142
|
-
|
|
143
|
-
beforeAll(async () => {
|
|
144
|
-
groupId = (
|
|
145
|
-
await config.api.groups.saveGroup(structures.groups.UserGroup())
|
|
146
|
-
).body._id
|
|
147
|
-
|
|
148
|
-
await Promise.all(
|
|
149
|
-
Array.from({ length: 30 }).map(async (_, i) => {
|
|
150
|
-
const email = `user${i}@${generator.domain()}`
|
|
151
|
-
const user = await config.api.users.saveUser({
|
|
152
|
-
...structures.users.user(),
|
|
153
|
-
email,
|
|
154
|
-
})
|
|
155
|
-
users.push({ _id: user.body._id, email })
|
|
156
|
-
})
|
|
157
|
-
)
|
|
158
|
-
users = users.sort((a, b) => a._id.localeCompare(b._id))
|
|
159
|
-
await config.api.groups.updateGroupUsers(groupId, {
|
|
160
|
-
add: users.map(u => u._id),
|
|
161
|
-
remove: [],
|
|
162
|
-
})
|
|
163
|
-
})
|
|
164
|
-
|
|
165
|
-
describe("pagination", () => {
|
|
166
|
-
it("should return first page", async () => {
|
|
167
|
-
const result = await config.api.groups.searchUsers(groupId)
|
|
168
|
-
expect(result.body).toEqual({
|
|
169
|
-
users: users.slice(0, 10),
|
|
170
|
-
bookmark: users[10]._id,
|
|
171
|
-
hasNextPage: true,
|
|
172
|
-
})
|
|
173
|
-
})
|
|
174
|
-
|
|
175
|
-
it("given a bookmark, should return skip items", async () => {
|
|
176
|
-
const result = await config.api.groups.searchUsers(groupId, {
|
|
177
|
-
bookmark: users[7]._id,
|
|
178
|
-
})
|
|
179
|
-
expect(result.body).toEqual({
|
|
180
|
-
users: users.slice(7, 17),
|
|
181
|
-
bookmark: users[17]._id,
|
|
182
|
-
hasNextPage: true,
|
|
183
|
-
})
|
|
184
|
-
})
|
|
185
|
-
|
|
186
|
-
it("bookmarking the last page, should return last page info", async () => {
|
|
187
|
-
const result = await config.api.groups.searchUsers(groupId, {
|
|
188
|
-
bookmark: users[20]._id,
|
|
189
|
-
})
|
|
190
|
-
expect(result.body).toEqual({
|
|
191
|
-
users: users.slice(20),
|
|
192
|
-
bookmark: undefined,
|
|
193
|
-
hasNextPage: false,
|
|
194
|
-
})
|
|
195
|
-
})
|
|
196
|
-
})
|
|
197
|
-
|
|
198
|
-
describe("search by email", () => {
|
|
199
|
-
it('should be able to search "starting" by email', async () => {
|
|
200
|
-
const result = await config.api.groups.searchUsers(groupId, {
|
|
201
|
-
emailSearch: `user1`,
|
|
202
|
-
})
|
|
203
|
-
|
|
204
|
-
const matchedUsers = users
|
|
205
|
-
.filter(u => u.email.startsWith("user1"))
|
|
206
|
-
.sort((a, b) => a.email.localeCompare(b.email))
|
|
207
|
-
|
|
208
|
-
expect(result.body).toEqual({
|
|
209
|
-
users: matchedUsers.slice(0, 10),
|
|
210
|
-
bookmark: matchedUsers[10].email,
|
|
211
|
-
hasNextPage: true,
|
|
212
|
-
})
|
|
213
|
-
})
|
|
214
|
-
|
|
215
|
-
it("should be able to bookmark when searching by email", async () => {
|
|
216
|
-
const matchedUsers = users
|
|
217
|
-
.filter(u => u.email.startsWith("user1"))
|
|
218
|
-
.sort((a, b) => a.email.localeCompare(b.email))
|
|
219
|
-
|
|
220
|
-
const result = await config.api.groups.searchUsers(groupId, {
|
|
221
|
-
emailSearch: `user1`,
|
|
222
|
-
bookmark: matchedUsers[4].email,
|
|
223
|
-
})
|
|
224
|
-
|
|
225
|
-
expect(result.body).toEqual({
|
|
226
|
-
users: matchedUsers.slice(4),
|
|
227
|
-
bookmark: undefined,
|
|
228
|
-
hasNextPage: false,
|
|
229
|
-
})
|
|
230
|
-
})
|
|
231
|
-
})
|
|
232
|
-
})
|
|
233
|
-
})
|
|
234
58
|
})
|
package/src/tests/api/groups.ts
CHANGED
|
@@ -7,13 +7,13 @@ export class GroupsAPI extends TestAPI {
|
|
|
7
7
|
super(config)
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
saveGroup = (group: UserGroup
|
|
10
|
+
saveGroup = (group: UserGroup) => {
|
|
11
11
|
return this.request
|
|
12
12
|
.post(`/api/global/groups`)
|
|
13
13
|
.send(group)
|
|
14
14
|
.set(this.config.defaultHeaders())
|
|
15
15
|
.expect("Content-Type", /json/)
|
|
16
|
-
.expect(
|
|
16
|
+
.expect(200)
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
deleteGroup = (id: string, rev: string) => {
|
|
@@ -23,34 +23,4 @@ export class GroupsAPI extends TestAPI {
|
|
|
23
23
|
.expect("Content-Type", /json/)
|
|
24
24
|
.expect(200)
|
|
25
25
|
}
|
|
26
|
-
|
|
27
|
-
searchUsers = (
|
|
28
|
-
id: string,
|
|
29
|
-
params?: { bookmark?: string; emailSearch?: string }
|
|
30
|
-
) => {
|
|
31
|
-
let url = `/api/global/groups/${id}/users?`
|
|
32
|
-
if (params?.bookmark) {
|
|
33
|
-
url += `bookmark=${params.bookmark}&`
|
|
34
|
-
}
|
|
35
|
-
if (params?.emailSearch) {
|
|
36
|
-
url += `emailSearch=${params.emailSearch}&`
|
|
37
|
-
}
|
|
38
|
-
return this.request
|
|
39
|
-
.get(url)
|
|
40
|
-
.set(this.config.defaultHeaders())
|
|
41
|
-
.expect("Content-Type", /json/)
|
|
42
|
-
.expect(200)
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
updateGroupUsers = (
|
|
46
|
-
id: string,
|
|
47
|
-
body: { add: string[]; remove: string[] }
|
|
48
|
-
) => {
|
|
49
|
-
return this.request
|
|
50
|
-
.post(`/api/global/groups/${id}/users`)
|
|
51
|
-
.send(body)
|
|
52
|
-
.set(this.config.defaultHeaders())
|
|
53
|
-
.expect("Content-Type", /json/)
|
|
54
|
-
.expect(200)
|
|
55
|
-
}
|
|
56
26
|
}
|
|
@@ -1,25 +1,10 @@
|
|
|
1
|
-
import { generator } from "@budibase/backend-core/tests"
|
|
2
|
-
import { db } from "@budibase/backend-core"
|
|
3
|
-
import { UserGroupRoles } from "@budibase/types"
|
|
4
|
-
|
|
5
1
|
export const UserGroup = () => {
|
|
6
|
-
const appsCount = generator.integer({ min: 0, max: 3 })
|
|
7
|
-
const roles = Array.from({ length: appsCount }).reduce(
|
|
8
|
-
(p: UserGroupRoles, v) => {
|
|
9
|
-
return {
|
|
10
|
-
...p,
|
|
11
|
-
[db.generateAppID()]: generator.pickone(["ADMIN", "POWER", "BASIC"]),
|
|
12
|
-
}
|
|
13
|
-
},
|
|
14
|
-
{}
|
|
15
|
-
)
|
|
16
|
-
|
|
17
2
|
let group = {
|
|
18
3
|
apps: [],
|
|
19
|
-
color:
|
|
20
|
-
icon:
|
|
21
|
-
name:
|
|
22
|
-
roles:
|
|
4
|
+
color: "var(--spectrum-global-color-blue-600)",
|
|
5
|
+
icon: "UserGroup",
|
|
6
|
+
name: "New group",
|
|
7
|
+
roles: { app_uuid1: "ADMIN", app_uuid2: "POWER" },
|
|
23
8
|
users: [],
|
|
24
9
|
}
|
|
25
10
|
return group
|
package/tsconfig.json
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"@budibase/types": ["../types/src"],
|
|
10
10
|
"@budibase/backend-core": ["../backend-core/src"],
|
|
11
11
|
"@budibase/backend-core/*": ["../backend-core/*"],
|
|
12
|
-
"@budibase/pro": ["
|
|
12
|
+
"@budibase/pro": ["../../../budibase-pro/packages/pro/src"]
|
|
13
13
|
}
|
|
14
14
|
},
|
|
15
15
|
"ts-node": {
|