@budibase/worker 2.8.22-alpha.1 → 2.8.22-alpha.3

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.
@@ -0,0 +1,27 @@
1
+ const actual = jest.requireActual("@budibase/pro")
2
+ const pro = {
3
+ ...actual,
4
+ licensing: {
5
+ keys: {
6
+ activateLicenseKey: jest.fn(),
7
+ getLicenseKey: jest.fn(),
8
+ deleteLicenseKey: jest.fn(),
9
+ },
10
+ offline: {
11
+ activateOfflineLicenseToken: jest.fn(),
12
+ getOfflineLicenseToken: jest.fn(),
13
+ deleteOfflineLicenseToken: jest.fn(),
14
+ getIdentifierBase64: jest.fn(),
15
+ },
16
+ cache: {
17
+ ...actual.licensing.cache,
18
+ refresh: jest.fn(),
19
+ },
20
+ },
21
+ quotas: {
22
+ ...actual.quotas,
23
+ getQuotaUsage: jest.fn(),
24
+ },
25
+ }
26
+
27
+ export = pro
@@ -0,0 +1,133 @@
1
+ openapi: 3.0.0
2
+ info:
3
+ title: Worker API Specification
4
+ version: 1.0.0
5
+ servers:
6
+ - url: "http://localhost:10000"
7
+ description: localhost
8
+ - url: "https://budibaseqa.app"
9
+ description: QA
10
+ - url: "https://preprod.qa.budibase.net"
11
+ description: Preprod
12
+ - url: "https://budibase.app"
13
+ description: Production
14
+
15
+ tags:
16
+ - name: license
17
+ description: License operations
18
+
19
+ paths:
20
+ /api/global/license/key:
21
+ post:
22
+ tags:
23
+ - license
24
+ summary: Activate license key
25
+ requestBody:
26
+ required: true
27
+ content:
28
+ application/json:
29
+ schema:
30
+ $ref: '#/components/schemas/ActivateLicenseKeyRequest'
31
+ responses:
32
+ '200':
33
+ description: Success
34
+ get:
35
+ tags:
36
+ - license
37
+ summary: Get license key
38
+ responses:
39
+ '200':
40
+ description: Success
41
+ content:
42
+ application/json:
43
+ schema:
44
+ $ref: '#/components/schemas/GetLicenseKeyResponse'
45
+ delete:
46
+ tags:
47
+ - license
48
+ summary: Delete license key
49
+ responses:
50
+ '204':
51
+ description: No content
52
+ /api/global/license/offline:
53
+ post:
54
+ tags:
55
+ - license
56
+ summary: Activate offline license
57
+ requestBody:
58
+ required: true
59
+ content:
60
+ application/json:
61
+ schema:
62
+ $ref: '#/components/schemas/ActivateOfflineLicenseTokenRequest'
63
+ responses:
64
+ '200':
65
+ description: Success
66
+ get:
67
+ tags:
68
+ - license
69
+ summary: Get offline license
70
+ responses:
71
+ '200':
72
+ description: Success
73
+ content:
74
+ application/json:
75
+ schema:
76
+ $ref: '#/components/schemas/GetOfflineLicenseTokenResponse'
77
+ delete:
78
+ tags:
79
+ - license
80
+ summary: Delete offline license
81
+ responses:
82
+ '204':
83
+ description: No content
84
+ /api/global/license/offline/identifier:
85
+ get:
86
+ tags:
87
+ - license
88
+ summary: Get offline identifier
89
+ responses:
90
+ '200':
91
+ description: Success
92
+ content:
93
+ application/json:
94
+ schema:
95
+ $ref: '#/components/schemas/GetOfflineIdentifierResponse'
96
+
97
+ components:
98
+ schemas:
99
+ ActivateOfflineLicenseTokenRequest:
100
+ type: object
101
+ properties:
102
+ offlineLicenseToken:
103
+ type: string
104
+ required:
105
+ - offlineLicenseToken
106
+ GetOfflineLicenseTokenResponse:
107
+ type: object
108
+ properties:
109
+ offlineLicenseToken:
110
+ type: string
111
+ required:
112
+ - offlineLicenseToken
113
+ ActivateLicenseKeyRequest:
114
+ type: object
115
+ properties:
116
+ licenseKey:
117
+ type: string
118
+ required:
119
+ - licenseKey
120
+ GetLicenseKeyResponse:
121
+ type: object
122
+ properties:
123
+ licenseKey:
124
+ type: string
125
+ required:
126
+ - licenseKey
127
+ GetOfflineIdentifierResponse:
128
+ type: object
129
+ properties:
130
+ identifierBase64:
131
+ type: string
132
+ required:
133
+ - identifierBase64
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@budibase/worker",
3
3
  "email": "hi@budibase.com",
4
- "version": "2.8.22-alpha.1",
4
+ "version": "2.8.22-alpha.3",
5
5
  "description": "Budibase background service",
6
6
  "main": "src/index.ts",
7
7
  "repository": {
@@ -38,10 +38,10 @@
38
38
  "author": "Budibase",
39
39
  "license": "GPL-3.0",
40
40
  "dependencies": {
41
- "@budibase/backend-core": "2.8.22-alpha.1",
42
- "@budibase/pro": "2.8.22-alpha.1",
43
- "@budibase/string-templates": "2.8.22-alpha.1",
44
- "@budibase/types": "2.8.22-alpha.1",
41
+ "@budibase/backend-core": "2.8.22-alpha.3",
42
+ "@budibase/pro": "2.8.22-alpha.3",
43
+ "@budibase/string-templates": "2.8.22-alpha.3",
44
+ "@budibase/types": "2.8.22-alpha.3",
45
45
  "@koa/router": "8.0.8",
46
46
  "@sentry/node": "6.17.7",
47
47
  "@techpass/passport-openidconnect": "0.3.2",
@@ -104,5 +104,5 @@
104
104
  "typescript": "4.7.3",
105
105
  "update-dotenv": "1.1.1"
106
106
  },
107
- "gitHead": "efcef8d6f8ce0744d1f80f8161a41f55ef6c8ea4"
107
+ "gitHead": "c628700e78b58122ffd2cc83a39360e12b7f3046"
108
108
  }
@@ -1,34 +1,83 @@
1
1
  import { licensing, quotas } from "@budibase/pro"
2
+ import {
3
+ ActivateLicenseKeyRequest,
4
+ ActivateOfflineLicenseTokenRequest,
5
+ GetLicenseKeyResponse,
6
+ GetOfflineIdentifierResponse,
7
+ GetOfflineLicenseTokenResponse,
8
+ UserCtx,
9
+ } from "@budibase/types"
2
10
 
3
- export const activate = async (ctx: any) => {
11
+ // LICENSE KEY
12
+
13
+ export async function activateLicenseKey(
14
+ ctx: UserCtx<ActivateLicenseKeyRequest>
15
+ ) {
4
16
  const { licenseKey } = ctx.request.body
5
- if (!licenseKey) {
6
- ctx.throw(400, "licenseKey is required")
17
+ await licensing.keys.activateLicenseKey(licenseKey)
18
+ ctx.status = 200
19
+ }
20
+
21
+ export async function getLicenseKey(ctx: UserCtx<void, GetLicenseKeyResponse>) {
22
+ const licenseKey = await licensing.keys.getLicenseKey()
23
+ if (licenseKey) {
24
+ ctx.body = { licenseKey: "*" }
25
+ ctx.status = 200
26
+ } else {
27
+ ctx.status = 404
7
28
  }
29
+ }
8
30
 
9
- await licensing.activateLicenseKey(licenseKey)
10
- ctx.status = 200
31
+ export async function deleteLicenseKey(ctx: UserCtx<void, void>) {
32
+ await licensing.keys.deleteLicenseKey()
33
+ ctx.status = 204
11
34
  }
12
35
 
13
- export const refresh = async (ctx: any) => {
14
- await licensing.cache.refresh()
36
+ // OFFLINE LICENSE
37
+
38
+ export async function activateOfflineLicenseToken(
39
+ ctx: UserCtx<ActivateOfflineLicenseTokenRequest>
40
+ ) {
41
+ const { offlineLicenseToken } = ctx.request.body
42
+ await licensing.offline.activateOfflineLicenseToken(offlineLicenseToken)
15
43
  ctx.status = 200
16
44
  }
17
45
 
18
- export const getInfo = async (ctx: any) => {
19
- const licenseInfo = await licensing.getLicenseInfo()
20
- if (licenseInfo) {
21
- licenseInfo.licenseKey = "*"
22
- ctx.body = licenseInfo
46
+ export async function getOfflineLicenseToken(
47
+ ctx: UserCtx<void, GetOfflineLicenseTokenResponse>
48
+ ) {
49
+ const offlineLicenseToken = await licensing.offline.getOfflineLicenseToken()
50
+ if (offlineLicenseToken) {
51
+ ctx.body = { offlineLicenseToken: "*" }
52
+ ctx.status = 200
53
+ } else {
54
+ ctx.status = 404
23
55
  }
56
+ }
57
+
58
+ export async function deleteOfflineLicenseToken(ctx: UserCtx<void, void>) {
59
+ await licensing.offline.deleteOfflineLicenseToken()
60
+ ctx.status = 204
61
+ }
62
+
63
+ export async function getOfflineLicenseIdentifier(
64
+ ctx: UserCtx<void, GetOfflineIdentifierResponse>
65
+ ) {
66
+ const identifierBase64 = await licensing.offline.getIdentifierBase64()
67
+ ctx.body = { identifierBase64 }
24
68
  ctx.status = 200
25
69
  }
26
70
 
27
- export const deleteInfo = async (ctx: any) => {
28
- await licensing.deleteLicenseInfo()
71
+ // LICENSES
72
+
73
+ export const refresh = async (ctx: any) => {
74
+ await licensing.cache.refresh()
29
75
  ctx.status = 200
30
76
  }
31
77
 
78
+ // USAGE
79
+
32
80
  export const getQuotaUsage = async (ctx: any) => {
33
81
  ctx.body = await quotas.getQuotaUsage()
82
+ ctx.status = 200
34
83
  }
@@ -1,10 +1,11 @@
1
1
  import { Ctx } from "@budibase/types"
2
2
  import env from "../../../environment"
3
+ import { env as coreEnv } from "@budibase/backend-core"
3
4
 
4
5
  export const fetch = async (ctx: Ctx) => {
5
6
  ctx.body = {
6
7
  multiTenancy: !!env.MULTI_TENANCY,
7
- offlineMode: !!env.OFFLINE_MODE,
8
+ offlineMode: !!coreEnv.OFFLINE_MODE,
8
9
  cloud: !env.SELF_HOSTED,
9
10
  accountPortalUrl: env.ACCOUNT_PORTAL_URL,
10
11
  disableAccountPortal: env.DISABLE_ACCOUNT_PORTAL,
@@ -1,13 +1,44 @@
1
1
  import Router from "@koa/router"
2
2
  import * as controller from "../../controllers/global/license"
3
+ import { middleware } from "@budibase/backend-core"
4
+ import Joi from "joi"
5
+
6
+ const activateLicenseKeyValidator = middleware.joiValidator.body(
7
+ Joi.object({
8
+ licenseKey: Joi.string().required(),
9
+ }).required()
10
+ )
11
+
12
+ const activateOfflineLicenseValidator = middleware.joiValidator.body(
13
+ Joi.object({
14
+ offlineLicenseToken: Joi.string().required(),
15
+ }).required()
16
+ )
3
17
 
4
18
  const router: Router = new Router()
5
19
 
6
20
  router
7
- .post("/api/global/license/activate", controller.activate)
8
21
  .post("/api/global/license/refresh", controller.refresh)
9
- .get("/api/global/license/info", controller.getInfo)
10
- .delete("/api/global/license/info", controller.deleteInfo)
11
22
  .get("/api/global/license/usage", controller.getQuotaUsage)
23
+ // LICENSE KEY
24
+ .post(
25
+ "/api/global/license/key",
26
+ activateLicenseKeyValidator,
27
+ controller.activateLicenseKey
28
+ )
29
+ .get("/api/global/license/key", controller.getLicenseKey)
30
+ .delete("/api/global/license/key", controller.deleteLicenseKey)
31
+ // OFFLINE LICENSE
32
+ .post(
33
+ "/api/global/license/offline",
34
+ activateOfflineLicenseValidator,
35
+ controller.activateOfflineLicenseToken
36
+ )
37
+ .get("/api/global/license/offline", controller.getOfflineLicenseToken)
38
+ .delete("/api/global/license/offline", controller.deleteOfflineLicenseToken)
39
+ .get(
40
+ "/api/global/license/offline/identifier",
41
+ controller.getOfflineLicenseIdentifier
42
+ )
12
43
 
13
44
  export default router
@@ -1,4 +1,6 @@
1
- import { TestConfiguration } from "../../../../tests"
1
+ import { TestConfiguration, mocks, structures } from "../../../../tests"
2
+ const licensing = mocks.pro.licensing
3
+ const quotas = mocks.pro.quotas
2
4
 
3
5
  describe("/api/global/license", () => {
4
6
  const config = new TestConfiguration()
@@ -12,18 +14,105 @@ describe("/api/global/license", () => {
12
14
  })
13
15
 
14
16
  afterEach(() => {
15
- jest.clearAllMocks()
17
+ jest.resetAllMocks()
16
18
  })
17
19
 
18
- describe("POST /api/global/license/activate", () => {
19
- it("activates license", () => {})
20
+ describe("POST /api/global/license/refresh", () => {
21
+ it("returns 200", async () => {
22
+ const res = await config.api.license.refresh()
23
+ expect(res.status).toBe(200)
24
+ expect(licensing.cache.refresh).toBeCalledTimes(1)
25
+ })
20
26
  })
21
27
 
22
- describe("POST /api/global/license/refresh", () => {})
28
+ describe("GET /api/global/license/usage", () => {
29
+ it("returns 200 + usage", async () => {
30
+ const usage = structures.quotas.usage()
31
+ quotas.getQuotaUsage.mockResolvedValue(usage)
32
+ const res = await config.api.license.getUsage()
33
+ expect(res.status).toBe(200)
34
+ expect(res.body).toEqual(usage)
35
+ })
36
+ })
37
+
38
+ describe("POST /api/global/license/key", () => {
39
+ it("returns 200", async () => {
40
+ const res = await config.api.license.activateLicenseKey({
41
+ licenseKey: "licenseKey",
42
+ })
43
+ expect(res.status).toBe(200)
44
+ expect(licensing.keys.activateLicenseKey).toBeCalledWith("licenseKey")
45
+ })
46
+ })
47
+
48
+ describe("GET /api/global/license/key", () => {
49
+ it("returns 404 when not found", async () => {
50
+ const res = await config.api.license.getLicenseKey()
51
+ expect(res.status).toBe(404)
52
+ })
53
+ it("returns 200 + license key", async () => {
54
+ licensing.keys.getLicenseKey.mockResolvedValue("licenseKey")
55
+ const res = await config.api.license.getLicenseKey()
56
+ expect(res.status).toBe(200)
57
+ expect(res.body).toEqual({
58
+ licenseKey: "*",
59
+ })
60
+ })
61
+ })
62
+
63
+ describe("DELETE /api/global/license/key", () => {
64
+ it("returns 204", async () => {
65
+ const res = await config.api.license.deleteLicenseKey()
66
+ expect(licensing.keys.deleteLicenseKey).toBeCalledTimes(1)
67
+ expect(res.status).toBe(204)
68
+ })
69
+ })
70
+
71
+ describe("POST /api/global/license/offline", () => {
72
+ it("activates offline license", async () => {
73
+ const res = await config.api.license.activateOfflineLicense({
74
+ offlineLicenseToken: "offlineLicenseToken",
75
+ })
76
+ expect(licensing.offline.activateOfflineLicenseToken).toBeCalledWith(
77
+ "offlineLicenseToken"
78
+ )
79
+ expect(res.status).toBe(200)
80
+ })
81
+ })
23
82
 
24
- describe("GET /api/global/license/info", () => {})
83
+ describe("GET /api/global/license/offline", () => {
84
+ it("returns 404 when not found", async () => {
85
+ const res = await config.api.license.getOfflineLicense()
86
+ expect(res.status).toBe(404)
87
+ })
88
+ it("returns 200 + offline license token", async () => {
89
+ licensing.offline.getOfflineLicenseToken.mockResolvedValue(
90
+ "offlineLicenseToken"
91
+ )
92
+ const res = await config.api.license.getOfflineLicense()
93
+ expect(res.status).toBe(200)
94
+ expect(res.body).toEqual({
95
+ offlineLicenseToken: "*",
96
+ })
97
+ })
98
+ })
25
99
 
26
- describe("DELETE /api/global/license/info", () => {})
100
+ describe("DELETE /api/global/license/offline", () => {
101
+ it("returns 204", async () => {
102
+ const res = await config.api.license.deleteOfflineLicense()
103
+ expect(res.status).toBe(204)
104
+ expect(licensing.offline.deleteOfflineLicenseToken).toBeCalledTimes(1)
105
+ })
106
+ })
27
107
 
28
- describe("GET /api/global/license/usage", () => {})
108
+ describe("GET /api/global/license/offline/identifier", () => {
109
+ it("returns 200 + identifier base64", async () => {
110
+ licensing.offline.getIdentifierBase64.mockResolvedValue("base64")
111
+ const res = await config.api.license.getOfflineLicenseIdentifier()
112
+ expect(res.status).toBe(200)
113
+ expect(res.body).toEqual({
114
+ identifierBase64: "base64",
115
+ })
116
+ })
117
+ })
29
118
  })
@@ -61,7 +61,6 @@ const environment = {
61
61
  CHECKLIST_CACHE_TTL: parseIntSafe(process.env.CHECKLIST_CACHE_TTL) || 3600,
62
62
  SESSION_UPDATE_PERIOD: process.env.SESSION_UPDATE_PERIOD,
63
63
  ENCRYPTED_TEST_PUBLIC_API_KEY: process.env.ENCRYPTED_TEST_PUBLIC_API_KEY,
64
- OFFLINE_MODE: process.env.OFFLINE_MODE,
65
64
  /**
66
65
  * Mock the email service in use - links to ethereal hosted emails are logged instead.
67
66
  */
@@ -1,8 +1,7 @@
1
1
  import mocks from "./mocks"
2
2
 
3
3
  // init the licensing mock
4
- import * as pro from "@budibase/pro"
5
- mocks.licenses.init(pro)
4
+ mocks.licenses.init(mocks.pro)
6
5
 
7
6
  // use unlimited license by default
8
7
  mocks.licenses.useUnlimited()
@@ -238,21 +237,21 @@ class TestConfiguration {
238
237
 
239
238
  const db = context.getGlobalDB()
240
239
 
241
- const id = dbCore.generateDevInfoID(this.user._id)
240
+ const id = dbCore.generateDevInfoID(this.user!._id)
242
241
  // TODO: dry
243
242
  this.apiKey = encryption.encrypt(
244
243
  `${this.tenantId}${dbCore.SEPARATOR}${utils.newid()}`
245
244
  )
246
245
  const devInfo = {
247
246
  _id: id,
248
- userId: this.user._id,
247
+ userId: this.user!._id,
249
248
  apiKey: this.apiKey,
250
249
  }
251
250
  await db.put(devInfo)
252
251
  })
253
252
  }
254
253
 
255
- async getUser(email: string): Promise<User> {
254
+ async getUser(email: string): Promise<User | undefined> {
256
255
  return context.doInTenant(this.getTenantId(), () => {
257
256
  return users.getGlobalUserByEmail(email)
258
257
  })
@@ -264,7 +263,7 @@ class TestConfiguration {
264
263
  }
265
264
  const response = await this._req(user, null, controllers.users.save)
266
265
  const body = response as SaveUserResponse
267
- return this.getUser(body.email)
266
+ return this.getUser(body.email) as Promise<User>
268
267
  }
269
268
 
270
269
  // CONFIGS
@@ -1,17 +1,62 @@
1
1
  import TestConfiguration from "../TestConfiguration"
2
2
  import { TestAPI } from "./base"
3
+ import {
4
+ ActivateLicenseKeyRequest,
5
+ ActivateOfflineLicenseTokenRequest,
6
+ } from "@budibase/types"
3
7
 
4
8
  export class LicenseAPI extends TestAPI {
5
9
  constructor(config: TestConfiguration) {
6
10
  super(config)
7
11
  }
8
12
 
9
- activate = async (licenseKey: string) => {
13
+ refresh = async () => {
10
14
  return this.request
11
- .post("/api/global/license/activate")
12
- .send({ licenseKey: licenseKey })
15
+ .post("/api/global/license/refresh")
16
+ .set(this.config.defaultHeaders())
17
+ }
18
+ getUsage = async () => {
19
+ return this.request
20
+ .get("/api/global/license/usage")
13
21
  .set(this.config.defaultHeaders())
14
22
  .expect("Content-Type", /json/)
15
23
  .expect(200)
16
24
  }
25
+ activateLicenseKey = async (body: ActivateLicenseKeyRequest) => {
26
+ return this.request
27
+ .post("/api/global/license/key")
28
+ .send(body)
29
+ .set(this.config.defaultHeaders())
30
+ }
31
+ getLicenseKey = async () => {
32
+ return this.request
33
+ .get("/api/global/license/key")
34
+ .set(this.config.defaultHeaders())
35
+ }
36
+ deleteLicenseKey = async () => {
37
+ return this.request
38
+ .delete("/api/global/license/key")
39
+ .set(this.config.defaultHeaders())
40
+ }
41
+ activateOfflineLicense = async (body: ActivateOfflineLicenseTokenRequest) => {
42
+ return this.request
43
+ .post("/api/global/license/offline")
44
+ .send(body)
45
+ .set(this.config.defaultHeaders())
46
+ }
47
+ getOfflineLicense = async () => {
48
+ return this.request
49
+ .get("/api/global/license/offline")
50
+ .set(this.config.defaultHeaders())
51
+ }
52
+ deleteOfflineLicense = async () => {
53
+ return this.request
54
+ .delete("/api/global/license/offline")
55
+ .set(this.config.defaultHeaders())
56
+ }
57
+ getOfflineLicenseIdentifier = async () => {
58
+ return this.request
59
+ .get("/api/global/license/offline/identifier")
60
+ .set(this.config.defaultHeaders())
61
+ }
17
62
  }
@@ -1,7 +1,11 @@
1
1
  import * as email from "./email"
2
2
  import { mocks } from "@budibase/backend-core/tests"
3
3
 
4
+ import * as _pro from "@budibase/pro"
5
+ const pro = jest.mocked(_pro, true)
6
+
4
7
  export default {
5
8
  email,
9
+ pro,
6
10
  ...mocks,
7
11
  }