Merge pull request #11980 from Budibase/account-portal-api-license-key
Account Portal License Key - Activate License Test
This commit is contained in:
commit
2062d34fb6
|
@ -17,7 +17,7 @@
|
||||||
"test:notify": "node scripts/testResultsWebhook",
|
"test:notify": "node scripts/testResultsWebhook",
|
||||||
"test:cloud:prod": "yarn run test --testPathIgnorePatterns=\\.integration\\.",
|
"test:cloud:prod": "yarn run test --testPathIgnorePatterns=\\.integration\\.",
|
||||||
"test:cloud:qa": "yarn run test",
|
"test:cloud:qa": "yarn run test",
|
||||||
"test:self:ci": "yarn run test --testPathIgnorePatterns=\\.integration\\. \\.cloud\\.",
|
"test:self:ci": "yarn run test --testPathIgnorePatterns=\\.integration\\. \\.cloud\\. \\.license\\.",
|
||||||
"serve:test:self:ci": "start-server-and-test dev:built http://localhost:4001/health test:self:ci",
|
"serve:test:self:ci": "start-server-and-test dev:built http://localhost:4001/health test:self:ci",
|
||||||
"serve": "start-server-and-test dev:built http://localhost:4001/health",
|
"serve": "start-server-and-test dev:built http://localhost:4001/health",
|
||||||
"dev:built": "cd ../ && yarn dev:built"
|
"dev:built": "cd ../ && yarn dev:built"
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import AccountInternalAPIClient from "./AccountInternalAPIClient"
|
import AccountInternalAPIClient from "./AccountInternalAPIClient"
|
||||||
import { AccountAPI, LicenseAPI, AuthAPI } from "./apis"
|
import { AccountAPI, LicenseAPI, AuthAPI, StripeAPI } from "./apis"
|
||||||
import { State } from "../../types"
|
import { State } from "../../types"
|
||||||
|
|
||||||
export default class AccountInternalAPI {
|
export default class AccountInternalAPI {
|
||||||
|
@ -8,11 +8,13 @@ export default class AccountInternalAPI {
|
||||||
auth: AuthAPI
|
auth: AuthAPI
|
||||||
accounts: AccountAPI
|
accounts: AccountAPI
|
||||||
licenses: LicenseAPI
|
licenses: LicenseAPI
|
||||||
|
stripe: StripeAPI
|
||||||
|
|
||||||
constructor(state: State) {
|
constructor(state: State) {
|
||||||
this.client = new AccountInternalAPIClient(state)
|
this.client = new AccountInternalAPIClient(state)
|
||||||
this.auth = new AuthAPI(this.client)
|
this.auth = new AuthAPI(this.client)
|
||||||
this.accounts = new AccountAPI(this.client)
|
this.accounts = new AccountAPI(this.client)
|
||||||
this.licenses = new LicenseAPI(this.client)
|
this.licenses = new LicenseAPI(this.client)
|
||||||
|
this.stripe = new StripeAPI(this.client)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,21 +2,19 @@ import AccountInternalAPIClient from "../AccountInternalAPIClient"
|
||||||
import {
|
import {
|
||||||
Account,
|
Account,
|
||||||
CreateOfflineLicenseRequest,
|
CreateOfflineLicenseRequest,
|
||||||
|
GetLicenseKeyResponse,
|
||||||
GetOfflineLicenseResponse,
|
GetOfflineLicenseResponse,
|
||||||
UpdateLicenseRequest,
|
UpdateLicenseRequest,
|
||||||
} from "@budibase/types"
|
} from "@budibase/types"
|
||||||
import { Response } from "node-fetch"
|
import { Response } from "node-fetch"
|
||||||
import BaseAPI from "./BaseAPI"
|
import BaseAPI from "./BaseAPI"
|
||||||
import { APIRequestOpts } from "../../../types"
|
import { APIRequestOpts } from "../../../types"
|
||||||
|
|
||||||
export default class LicenseAPI extends BaseAPI {
|
export default class LicenseAPI extends BaseAPI {
|
||||||
client: AccountInternalAPIClient
|
client: AccountInternalAPIClient
|
||||||
|
|
||||||
constructor(client: AccountInternalAPIClient) {
|
constructor(client: AccountInternalAPIClient) {
|
||||||
super()
|
super()
|
||||||
this.client = client
|
this.client = client
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateLicense(
|
async updateLicense(
|
||||||
accountId: string,
|
accountId: string,
|
||||||
body: UpdateLicenseRequest,
|
body: UpdateLicenseRequest,
|
||||||
|
@ -29,9 +27,7 @@ export default class LicenseAPI extends BaseAPI {
|
||||||
})
|
})
|
||||||
}, opts)
|
}, opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Better approach for setting tenant id header
|
// TODO: Better approach for setting tenant id header
|
||||||
|
|
||||||
async createOfflineLicense(
|
async createOfflineLicense(
|
||||||
accountId: string,
|
accountId: string,
|
||||||
tenantId: string,
|
tenantId: string,
|
||||||
|
@ -51,7 +47,6 @@ export default class LicenseAPI extends BaseAPI {
|
||||||
expect(response.status).toBe(opts.status ? opts.status : 201)
|
expect(response.status).toBe(opts.status ? opts.status : 201)
|
||||||
return response
|
return response
|
||||||
}
|
}
|
||||||
|
|
||||||
async getOfflineLicense(
|
async getOfflineLicense(
|
||||||
accountId: string,
|
accountId: string,
|
||||||
tenantId: string,
|
tenantId: string,
|
||||||
|
@ -69,4 +64,74 @@ export default class LicenseAPI extends BaseAPI {
|
||||||
expect(response.status).toBe(opts.status ? opts.status : 200)
|
expect(response.status).toBe(opts.status ? opts.status : 200)
|
||||||
return [response, json]
|
return [response, json]
|
||||||
}
|
}
|
||||||
|
async getLicenseKey(
|
||||||
|
opts: { status?: number } = {}
|
||||||
|
): Promise<[Response, GetLicenseKeyResponse]> {
|
||||||
|
const [response, json] = await this.client.get(`/api/license/key`)
|
||||||
|
expect(response.status).toBe(opts.status || 200)
|
||||||
|
return [response, json]
|
||||||
|
}
|
||||||
|
async activateLicense(
|
||||||
|
apiKey: string,
|
||||||
|
tenantId: string,
|
||||||
|
licenseKey: string,
|
||||||
|
opts: APIRequestOpts = { status: 200 }
|
||||||
|
) {
|
||||||
|
return this.doRequest(() => {
|
||||||
|
return this.client.post(`/api/license/activate`, {
|
||||||
|
body: {
|
||||||
|
apiKey: apiKey,
|
||||||
|
tenantId: tenantId,
|
||||||
|
licenseKey: licenseKey,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}, opts)
|
||||||
|
}
|
||||||
|
async regenerateLicenseKey(opts: APIRequestOpts = { status: 200 }) {
|
||||||
|
return this.doRequest(() => {
|
||||||
|
return this.client.post(`/api/license/key/regenerate`, {})
|
||||||
|
}, opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
async getPlans(opts: APIRequestOpts = { status: 200 }) {
|
||||||
|
return this.doRequest(() => {
|
||||||
|
return this.client.get(`/api/plans`)
|
||||||
|
}, opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
async updatePlan(opts: APIRequestOpts = { status: 200 }) {
|
||||||
|
return this.doRequest(() => {
|
||||||
|
return this.client.put(`/api/license/plan`)
|
||||||
|
}, opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
async refreshAccountLicense(
|
||||||
|
accountId: string,
|
||||||
|
opts: { status?: number } = {}
|
||||||
|
): Promise<Response> {
|
||||||
|
const [response, json] = await this.client.post(
|
||||||
|
`/api/accounts/${accountId}/license/refresh`,
|
||||||
|
{
|
||||||
|
internal: true,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
expect(response.status).toBe(opts.status ? opts.status : 201)
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
|
||||||
|
async getLicenseUsage(opts: APIRequestOpts = { status: 200 }) {
|
||||||
|
return this.doRequest(() => {
|
||||||
|
return this.client.get(`/api/license/usage`)
|
||||||
|
}, opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
async licenseUsageTriggered(
|
||||||
|
opts: { status?: number } = {}
|
||||||
|
): Promise<Response> {
|
||||||
|
const [response, json] = await this.client.post(
|
||||||
|
`/api/license/usage/triggered`
|
||||||
|
)
|
||||||
|
expect(response.status).toBe(opts.status ? opts.status : 201)
|
||||||
|
return response
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,64 @@
|
||||||
|
import AccountInternalAPIClient from "../AccountInternalAPIClient"
|
||||||
|
import BaseAPI from "./BaseAPI"
|
||||||
|
import { APIRequestOpts } from "../../../types"
|
||||||
|
|
||||||
|
export default class StripeAPI extends BaseAPI {
|
||||||
|
client: AccountInternalAPIClient
|
||||||
|
|
||||||
|
constructor(client: AccountInternalAPIClient) {
|
||||||
|
super()
|
||||||
|
this.client = client
|
||||||
|
}
|
||||||
|
|
||||||
|
async createCheckoutSession(
|
||||||
|
priceId: string,
|
||||||
|
opts: APIRequestOpts = { status: 200 }
|
||||||
|
) {
|
||||||
|
return this.doRequest(() => {
|
||||||
|
return this.client.post(`/api/stripe/checkout-session`, {
|
||||||
|
body: { priceId },
|
||||||
|
})
|
||||||
|
}, opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
async checkoutSuccess(opts: APIRequestOpts = { status: 200 }) {
|
||||||
|
return this.doRequest(() => {
|
||||||
|
return this.client.post(`/api/stripe/checkout-success`)
|
||||||
|
}, opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
async createPortalSession(
|
||||||
|
stripeCustomerId: string,
|
||||||
|
opts: APIRequestOpts = { status: 200 }
|
||||||
|
) {
|
||||||
|
return this.doRequest(() => {
|
||||||
|
return this.client.post(`/api/stripe/portal-session`, {
|
||||||
|
body: { stripeCustomerId },
|
||||||
|
})
|
||||||
|
}, opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
async linkStripeCustomer(opts: APIRequestOpts = { status: 200 }) {
|
||||||
|
return this.doRequest(() => {
|
||||||
|
return this.client.post(`/api/stripe/link`)
|
||||||
|
}, opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
async getInvoices(opts: APIRequestOpts = { status: 200 }) {
|
||||||
|
return this.doRequest(() => {
|
||||||
|
return this.client.get(`/api/stripe/invoices`)
|
||||||
|
}, opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
async getUpcomingInvoice(opts: APIRequestOpts = { status: 200 }) {
|
||||||
|
return this.doRequest(() => {
|
||||||
|
return this.client.get(`/api/stripe/upcoming-invoice`)
|
||||||
|
}, opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
async getStripeCustomers(opts: APIRequestOpts = { status: 200 }) {
|
||||||
|
return this.doRequest(() => {
|
||||||
|
return this.client.get(`/api/stripe/customers`)
|
||||||
|
}, opts)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,3 +1,4 @@
|
||||||
export { default as AuthAPI } from "./AuthAPI"
|
export { default as AuthAPI } from "./AuthAPI"
|
||||||
export { default as AccountAPI } from "./AccountAPI"
|
export { default as AccountAPI } from "./AccountAPI"
|
||||||
export { default as LicenseAPI } from "./LicenseAPI"
|
export { default as LicenseAPI } from "./LicenseAPI"
|
||||||
|
export { default as StripeAPI } from "./StripeAPI"
|
||||||
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
import TestConfiguration from "../../config/TestConfiguration"
|
||||||
|
import * as fixures from "../../fixtures"
|
||||||
|
import { Feature, Hosting } from "@budibase/types"
|
||||||
|
|
||||||
|
describe("license activation", () => {
|
||||||
|
const config = new TestConfiguration()
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
await config.beforeAll()
|
||||||
|
})
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
await config.afterAll()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("creates, activates and deletes online license - self host", async () => {
|
||||||
|
// Remove existing license key
|
||||||
|
await config.internalApi.license.deleteLicenseKey()
|
||||||
|
|
||||||
|
// Verify license key not found
|
||||||
|
await config.internalApi.license.getLicenseKey({ status: 404 })
|
||||||
|
|
||||||
|
// Create self host account
|
||||||
|
const createAccountRequest = fixures.accounts.generateAccount({
|
||||||
|
hosting: Hosting.SELF,
|
||||||
|
})
|
||||||
|
const [createAccountRes, account] =
|
||||||
|
await config.accountsApi.accounts.create(createAccountRequest, {
|
||||||
|
autoVerify: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
let licenseKey: string = " "
|
||||||
|
await config.doInNewState(async () => {
|
||||||
|
await config.loginAsAccount(createAccountRequest)
|
||||||
|
// Retrieve license key
|
||||||
|
const [res, body] = await config.accountsApi.licenses.getLicenseKey()
|
||||||
|
licenseKey = body.licenseKey
|
||||||
|
})
|
||||||
|
|
||||||
|
const accountId = account.accountId!
|
||||||
|
|
||||||
|
// Update license to have paid feature
|
||||||
|
const [res, acc] = await config.accountsApi.licenses.updateLicense(
|
||||||
|
accountId,
|
||||||
|
{
|
||||||
|
overrides: {
|
||||||
|
features: [Feature.APP_BACKUPS],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// Activate license key
|
||||||
|
await config.internalApi.license.activateLicenseKey({ licenseKey })
|
||||||
|
|
||||||
|
// Verify license updated with new feature
|
||||||
|
await config.doInNewState(async () => {
|
||||||
|
await config.loginAsAccount(createAccountRequest)
|
||||||
|
const [selfRes, body] = await config.api.accounts.self()
|
||||||
|
expect(body.license.features[0]).toBe("appBackups")
|
||||||
|
})
|
||||||
|
|
||||||
|
// Remove license key
|
||||||
|
await config.internalApi.license.deleteLicenseKey()
|
||||||
|
|
||||||
|
// Verify license key not found
|
||||||
|
await config.internalApi.license.getLicenseKey({ status: 404 })
|
||||||
|
})
|
||||||
|
})
|
|
@ -1,17 +1,19 @@
|
||||||
import { Response } from "node-fetch"
|
import { Response } from "node-fetch"
|
||||||
import {
|
import {
|
||||||
|
ActivateLicenseKeyRequest,
|
||||||
ActivateOfflineLicenseTokenRequest,
|
ActivateOfflineLicenseTokenRequest,
|
||||||
|
GetLicenseKeyResponse,
|
||||||
GetOfflineIdentifierResponse,
|
GetOfflineIdentifierResponse,
|
||||||
GetOfflineLicenseTokenResponse,
|
GetOfflineLicenseTokenResponse,
|
||||||
} from "@budibase/types"
|
} from "@budibase/types"
|
||||||
import BudibaseInternalAPIClient from "../BudibaseInternalAPIClient"
|
import BudibaseInternalAPIClient from "../BudibaseInternalAPIClient"
|
||||||
import BaseAPI from "./BaseAPI"
|
import BaseAPI from "./BaseAPI"
|
||||||
|
import { APIRequestOpts } from "../../../types"
|
||||||
|
|
||||||
export default class LicenseAPI extends BaseAPI {
|
export default class LicenseAPI extends BaseAPI {
|
||||||
constructor(client: BudibaseInternalAPIClient) {
|
constructor(client: BudibaseInternalAPIClient) {
|
||||||
super(client)
|
super(client)
|
||||||
}
|
}
|
||||||
|
|
||||||
async getOfflineLicenseToken(
|
async getOfflineLicenseToken(
|
||||||
opts: { status?: number } = {}
|
opts: { status?: number } = {}
|
||||||
): Promise<[Response, GetOfflineLicenseTokenResponse]> {
|
): Promise<[Response, GetOfflineLicenseTokenResponse]> {
|
||||||
|
@ -21,19 +23,16 @@ export default class LicenseAPI extends BaseAPI {
|
||||||
)
|
)
|
||||||
return [response, body]
|
return [response, body]
|
||||||
}
|
}
|
||||||
|
|
||||||
async deleteOfflineLicenseToken(): Promise<[Response]> {
|
async deleteOfflineLicenseToken(): Promise<[Response]> {
|
||||||
const [response] = await this.del(`/global/license/offline`, 204)
|
const [response] = await this.del(`/global/license/offline`, 204)
|
||||||
return [response]
|
return [response]
|
||||||
}
|
}
|
||||||
|
|
||||||
async activateOfflineLicenseToken(
|
async activateOfflineLicenseToken(
|
||||||
body: ActivateOfflineLicenseTokenRequest
|
body: ActivateOfflineLicenseTokenRequest
|
||||||
): Promise<[Response]> {
|
): Promise<[Response]> {
|
||||||
const [response] = await this.post(`/global/license/offline`, body)
|
const [response] = await this.post(`/global/license/offline`, body)
|
||||||
return [response]
|
return [response]
|
||||||
}
|
}
|
||||||
|
|
||||||
async getOfflineIdentifier(): Promise<
|
async getOfflineIdentifier(): Promise<
|
||||||
[Response, GetOfflineIdentifierResponse]
|
[Response, GetOfflineIdentifierResponse]
|
||||||
> {
|
> {
|
||||||
|
@ -42,4 +41,23 @@ export default class LicenseAPI extends BaseAPI {
|
||||||
)
|
)
|
||||||
return [response, body]
|
return [response, body]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getLicenseKey(
|
||||||
|
opts: { status?: number } = {}
|
||||||
|
): Promise<[Response, GetLicenseKeyResponse]> {
|
||||||
|
const [response, body] = await this.get(`/global/license/key`, opts.status)
|
||||||
|
return [response, body]
|
||||||
|
}
|
||||||
|
|
||||||
|
async activateLicenseKey(
|
||||||
|
body: ActivateLicenseKeyRequest
|
||||||
|
): Promise<[Response]> {
|
||||||
|
const [response] = await this.post(`/global/license/key`, body)
|
||||||
|
return [response]
|
||||||
|
}
|
||||||
|
|
||||||
|
async deleteLicenseKey(): Promise<[Response]> {
|
||||||
|
const [response] = await this.del(`/global/license/key`, 204)
|
||||||
|
return [response]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue