@budibase/worker 3.23.28 → 3.23.29
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 +2 -2
- package/src/api/controllers/system/tenants.ts +26 -1
- package/src/api/routes/system/tenants.ts +4 -0
- package/src/api/routes/system/tests/tenants.spec.ts +79 -0
- package/src/sdk/tenants/tenants.ts +14 -0
- package/src/sdk/tenants/tests/tenants.spec.ts +80 -1
- package/src/tests/api/tenants.ts +13 -1
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@budibase/worker",
|
|
3
3
|
"email": "hi@budibase.com",
|
|
4
|
-
"version": "3.23.
|
|
4
|
+
"version": "3.23.29",
|
|
5
5
|
"description": "Budibase background service",
|
|
6
6
|
"main": "src/index.ts",
|
|
7
7
|
"repository": {
|
|
@@ -109,5 +109,5 @@
|
|
|
109
109
|
}
|
|
110
110
|
}
|
|
111
111
|
},
|
|
112
|
-
"gitHead": "
|
|
112
|
+
"gitHead": "e9bb53161a551bda13ab996da6cba563f6be5400"
|
|
113
113
|
}
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
ActivationRequest,
|
|
3
|
+
GetTenantInfoResponse,
|
|
4
|
+
UserCtx,
|
|
5
|
+
} from "@budibase/types"
|
|
2
6
|
import { LockRequest, LockReason } from "@budibase/types"
|
|
3
7
|
import * as tenantSdk from "../../../sdk/tenants"
|
|
4
8
|
|
|
@@ -47,6 +51,27 @@ export async function lock(ctx: UserCtx<LockRequest, void>) {
|
|
|
47
51
|
}
|
|
48
52
|
}
|
|
49
53
|
|
|
54
|
+
export async function activation(ctx: UserCtx<ActivationRequest, void>) {
|
|
55
|
+
if (!ctx.internal) {
|
|
56
|
+
ctx.throw(403, "Only internal user can set activation for a tenant")
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const { active } = ctx.request.body
|
|
60
|
+
if (typeof active !== "boolean") {
|
|
61
|
+
ctx.throw(403, "Only boolean values allowed for 'active' property")
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const tenantId = ctx.params.tenantId
|
|
65
|
+
|
|
66
|
+
try {
|
|
67
|
+
await tenantSdk.setActivation(tenantId, active)
|
|
68
|
+
ctx.status = 204
|
|
69
|
+
} catch (err) {
|
|
70
|
+
ctx.log.error(err)
|
|
71
|
+
throw err
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
50
75
|
export async function info(ctx: UserCtx<void, GetTenantInfoResponse>) {
|
|
51
76
|
ctx.body = await tenantSdk.tenantInfo(ctx.params.tenantId)
|
|
52
77
|
}
|
|
@@ -3,3 +3,7 @@ import { adminRoutes } from "../endpointGroups"
|
|
|
3
3
|
|
|
4
4
|
adminRoutes.delete("/api/system/tenants/:tenantId", controller.destroy)
|
|
5
5
|
adminRoutes.put("/api/system/tenants/:tenantId/lock", controller.lock)
|
|
6
|
+
adminRoutes.put(
|
|
7
|
+
"/api/system/tenants/:tenantId/activation",
|
|
8
|
+
controller.activation
|
|
9
|
+
)
|
|
@@ -7,6 +7,7 @@ jest.mock("../../../../sdk/tenants", () => ({
|
|
|
7
7
|
lockTenant: jest.fn(),
|
|
8
8
|
unlockTenant: jest.fn(),
|
|
9
9
|
deleteTenant: jest.fn(),
|
|
10
|
+
setActivation: jest.fn(),
|
|
10
11
|
}))
|
|
11
12
|
|
|
12
13
|
describe("/api/global/tenants", () => {
|
|
@@ -147,4 +148,82 @@ describe("/api/global/tenants", () => {
|
|
|
147
148
|
expect(tenantSdk.unlockTenant).not.toHaveBeenCalled()
|
|
148
149
|
})
|
|
149
150
|
})
|
|
151
|
+
|
|
152
|
+
describe("PUT /api/system/tenants/:tenantId/activation", () => {
|
|
153
|
+
it("allows activating tenant", async () => {
|
|
154
|
+
const user = await config.createTenant()
|
|
155
|
+
|
|
156
|
+
await config.api.tenants.activation(
|
|
157
|
+
user.tenantId,
|
|
158
|
+
{
|
|
159
|
+
active: true,
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
headers: config.internalAPIHeaders(),
|
|
163
|
+
}
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
expect(tenantSdk.setActivation).toHaveBeenCalledWith(user.tenantId, true)
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
it("allows deactivating tenant", async () => {
|
|
170
|
+
const user = await config.createTenant()
|
|
171
|
+
|
|
172
|
+
await config.api.tenants.activation(
|
|
173
|
+
user.tenantId,
|
|
174
|
+
{
|
|
175
|
+
active: false,
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
headers: config.internalAPIHeaders(),
|
|
179
|
+
}
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
expect(tenantSdk.setActivation).toHaveBeenCalledWith(user.tenantId, false)
|
|
183
|
+
})
|
|
184
|
+
|
|
185
|
+
it("rejects non-boolean active value", async () => {
|
|
186
|
+
const user = await config.createTenant()
|
|
187
|
+
|
|
188
|
+
const status = 403
|
|
189
|
+
const res = await config.api.tenants.activation(
|
|
190
|
+
user.tenantId,
|
|
191
|
+
{
|
|
192
|
+
active: "invalid" as any,
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
status,
|
|
196
|
+
headers: config.internalAPIHeaders(),
|
|
197
|
+
}
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
expect(res.body).toEqual({
|
|
201
|
+
message: "Only boolean values allowed for 'active' property",
|
|
202
|
+
status,
|
|
203
|
+
})
|
|
204
|
+
expect(tenantSdk.setActivation).not.toHaveBeenCalled()
|
|
205
|
+
})
|
|
206
|
+
|
|
207
|
+
it("rejects non-internal user", async () => {
|
|
208
|
+
const user = await config.createTenant()
|
|
209
|
+
|
|
210
|
+
const status = 403
|
|
211
|
+
const res = await config.api.tenants.activation(
|
|
212
|
+
user.tenantId,
|
|
213
|
+
{
|
|
214
|
+
active: true,
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
status,
|
|
218
|
+
headers: config.authHeaders(user),
|
|
219
|
+
}
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
expect(res.body).toEqual({
|
|
223
|
+
message: "Only internal user can set activation for a tenant",
|
|
224
|
+
status,
|
|
225
|
+
})
|
|
226
|
+
expect(tenantSdk.setActivation).not.toHaveBeenCalled()
|
|
227
|
+
})
|
|
228
|
+
})
|
|
150
229
|
})
|
|
@@ -22,6 +22,20 @@ export async function unlockTenant(tenantId: string) {
|
|
|
22
22
|
return await setLock(tenantId)
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
export async function setActivation(tenantId: string, active: boolean) {
|
|
26
|
+
const db = tenancy.getTenantDB(tenantId)
|
|
27
|
+
const settingsConfig = await db.tryGet<SettingsConfig>(
|
|
28
|
+
configs.generateConfigID(ConfigType.SETTINGS)
|
|
29
|
+
)
|
|
30
|
+
if (!settingsConfig?.config) {
|
|
31
|
+
throw new Error(
|
|
32
|
+
`Cannot set activation. Settings config not found for tenant ${tenantId}`
|
|
33
|
+
)
|
|
34
|
+
}
|
|
35
|
+
settingsConfig.config.active = active
|
|
36
|
+
await db.put(settingsConfig)
|
|
37
|
+
}
|
|
38
|
+
|
|
25
39
|
async function setLock(tenantId: string, lockReason?: LockReason) {
|
|
26
40
|
const db = tenancy.getTenantDB(tenantId)
|
|
27
41
|
const settingsConfig = await db.tryGet<SettingsConfig>(
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { structures } from "../../../tests"
|
|
2
|
-
import { lockTenant, unlockTenant } from "../tenants"
|
|
2
|
+
import { lockTenant, unlockTenant, setActivation } from "../tenants"
|
|
3
3
|
import { configs, tenancy } from "@budibase/backend-core"
|
|
4
4
|
import { LockReason, ConfigType, SettingsConfig } from "@budibase/types"
|
|
5
5
|
|
|
@@ -159,4 +159,83 @@ describe("tenants", () => {
|
|
|
159
159
|
)
|
|
160
160
|
})
|
|
161
161
|
})
|
|
162
|
+
|
|
163
|
+
describe("setActivation", () => {
|
|
164
|
+
it("should activate tenant", async () => {
|
|
165
|
+
const tenantId = structures.tenant.id()
|
|
166
|
+
|
|
167
|
+
const settingsConfig: SettingsConfig = {
|
|
168
|
+
_id: "config_settings",
|
|
169
|
+
type: ConfigType.SETTINGS,
|
|
170
|
+
config: {},
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
mockDb.tryGet.mockResolvedValue(settingsConfig)
|
|
174
|
+
|
|
175
|
+
await setActivation(tenantId, true)
|
|
176
|
+
|
|
177
|
+
expect(mockDb.tryGet).toHaveBeenCalledWith(
|
|
178
|
+
configs.generateConfigID(ConfigType.SETTINGS)
|
|
179
|
+
)
|
|
180
|
+
expect(mockDb.put).toHaveBeenCalledWith({
|
|
181
|
+
...settingsConfig,
|
|
182
|
+
config: {
|
|
183
|
+
...settingsConfig.config,
|
|
184
|
+
active: true,
|
|
185
|
+
},
|
|
186
|
+
})
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
it("should deactivate tenant", async () => {
|
|
190
|
+
const tenantId = structures.tenant.id()
|
|
191
|
+
|
|
192
|
+
const settingsConfig: SettingsConfig = {
|
|
193
|
+
_id: "config_settings",
|
|
194
|
+
type: ConfigType.SETTINGS,
|
|
195
|
+
config: {
|
|
196
|
+
active: true,
|
|
197
|
+
},
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
mockDb.tryGet.mockResolvedValue(settingsConfig)
|
|
201
|
+
|
|
202
|
+
await setActivation(tenantId, false)
|
|
203
|
+
|
|
204
|
+
expect(mockDb.tryGet).toHaveBeenCalledWith(
|
|
205
|
+
configs.generateConfigID(ConfigType.SETTINGS)
|
|
206
|
+
)
|
|
207
|
+
expect(mockDb.put).toHaveBeenCalledWith({
|
|
208
|
+
...settingsConfig,
|
|
209
|
+
config: {
|
|
210
|
+
...settingsConfig.config,
|
|
211
|
+
active: false,
|
|
212
|
+
},
|
|
213
|
+
})
|
|
214
|
+
})
|
|
215
|
+
|
|
216
|
+
it("should throw error when settings config not found", async () => {
|
|
217
|
+
const tenantId = structures.tenant.id()
|
|
218
|
+
|
|
219
|
+
mockDb.tryGet.mockResolvedValue(null)
|
|
220
|
+
|
|
221
|
+
await expect(setActivation(tenantId, true)).rejects.toThrow(
|
|
222
|
+
`Cannot set activation. Settings config not found for tenant ${tenantId}`
|
|
223
|
+
)
|
|
224
|
+
})
|
|
225
|
+
|
|
226
|
+
it("should throw error when settings config has no config property", async () => {
|
|
227
|
+
const tenantId = structures.tenant.id()
|
|
228
|
+
|
|
229
|
+
const settingsConfig = {
|
|
230
|
+
_id: "config_settings",
|
|
231
|
+
type: ConfigType.SETTINGS,
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
mockDb.tryGet.mockResolvedValue(settingsConfig)
|
|
235
|
+
|
|
236
|
+
await expect(setActivation(tenantId, true)).rejects.toThrow(
|
|
237
|
+
`Cannot set activation. Settings config not found for tenant ${tenantId}`
|
|
238
|
+
)
|
|
239
|
+
})
|
|
240
|
+
})
|
|
162
241
|
})
|
package/src/tests/api/tenants.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import TestConfiguration from "../TestConfiguration"
|
|
2
2
|
import { TestAPI, TestAPIOpts } from "./base"
|
|
3
|
-
import { LockRequest } from "@budibase/types"
|
|
3
|
+
import { LockRequest, ActivationRequest } from "@budibase/types"
|
|
4
4
|
|
|
5
5
|
export class TenantAPI extends TestAPI {
|
|
6
6
|
config: TestConfiguration
|
|
@@ -23,4 +23,16 @@ export class TenantAPI extends TestAPI {
|
|
|
23
23
|
.set(opts?.headers)
|
|
24
24
|
.expect(opts?.status ? opts.status : 204)
|
|
25
25
|
}
|
|
26
|
+
|
|
27
|
+
activation = (
|
|
28
|
+
tenantId: string,
|
|
29
|
+
body: ActivationRequest,
|
|
30
|
+
opts?: TestAPIOpts
|
|
31
|
+
) => {
|
|
32
|
+
return this.request
|
|
33
|
+
.put(`/api/system/tenants/${tenantId}/activation`)
|
|
34
|
+
.send(body)
|
|
35
|
+
.set(opts?.headers)
|
|
36
|
+
.expect(opts?.status ? opts.status : 204)
|
|
37
|
+
}
|
|
26
38
|
}
|