This commit is contained in:
Mitch-Budibase 2023-07-19 15:42:12 +01:00
parent 7eb65ed347
commit ecf75a9685
14 changed files with 140 additions and 143 deletions

View File

@ -19,4 +19,4 @@ export interface SearchAccountsRequest {
tenantId?: string tenantId?: string
} }
export type SearchAccountsResponse = Account[] export type SearchAccountsResponse = Account[]

View File

@ -13,7 +13,7 @@ function init() {
BB_ADMIN_USER_PASSWORD: "admin", BB_ADMIN_USER_PASSWORD: "admin",
LOG_LEVEL: "info", LOG_LEVEL: "info",
JEST_TIMEOUT: "60000", JEST_TIMEOUT: "60000",
DISABLE_PINO_LOGGER: "1" DISABLE_PINO_LOGGER: "1",
} }
let envFile = "" let envFile = ""
Object.keys(envFileJson).forEach(key => { Object.keys(envFileJson).forEach(key => {

View File

@ -48,7 +48,7 @@ export default class AccountInternalAPIClient {
if (options.internal) { if (options.internal) {
requestOptions.headers = { requestOptions.headers = {
...requestOptions.headers, ...requestOptions.headers,
...{ [Header.API_KEY] : env.ACCOUNT_PORTAL_API_KEY }, ...{ [Header.API_KEY]: env.ACCOUNT_PORTAL_API_KEY },
} }
} }

View File

@ -1,5 +1,10 @@
import { Response } from "node-fetch" import { Response } from "node-fetch"
import { Account, CreateAccountRequest, SearchAccountsRequest, SearchAccountsResponse } from "@budibase/types" import {
Account,
CreateAccountRequest,
SearchAccountsRequest,
SearchAccountsResponse,
} from "@budibase/types"
import AccountInternalAPIClient from "../AccountInternalAPIClient" import AccountInternalAPIClient from "../AccountInternalAPIClient"
import { APIRequestOpts } from "../../../types" import { APIRequestOpts } from "../../../types"
import { Header } from "@budibase/backend-core" import { Header } from "@budibase/backend-core"
@ -13,17 +18,11 @@ export default class AccountAPI extends BaseAPI {
this.client = client this.client = client
} }
async validateEmail( async validateEmail(email: string, opts: APIRequestOpts = { status: 200 }) {
email: string,
opts: APIRequestOpts = { status: 200 }
) {
return this.doRequest(() => { return this.doRequest(() => {
return this.client.post( return this.client.post(`/api/accounts/validate/email`, {
`/api/accounts/validate/email`, body: { email },
{ })
body: { email },
}
)
}, opts) }, opts)
} }
@ -32,22 +31,22 @@ export default class AccountAPI extends BaseAPI {
opts: APIRequestOpts = { status: 200 } opts: APIRequestOpts = { status: 200 }
) { ) {
return this.doRequest(() => { return this.doRequest(() => {
return this.client.post( return this.client.post(`/api/accounts/validate/tenantId`, {
`/api/accounts/validate/tenantId`, body: { tenantId },
{ })
body: { tenantId },
}
)
}, opts) }, opts)
} }
async create( async create(
body: CreateAccountRequest, body: CreateAccountRequest,
opts: APIRequestOpts & { autoVerify: boolean } = { status: 201, autoVerify: false } opts: APIRequestOpts & { autoVerify: boolean } = {
status: 201,
autoVerify: false,
}
): Promise<[Response, Account]> { ): Promise<[Response, Account]> {
return this.doRequest(() => { return this.doRequest(() => {
const headers = { const headers = {
"no-verify": opts.autoVerify ? "1" : "0" "no-verify": opts.autoVerify ? "1" : "0",
} }
return this.client.post(`/api/accounts`, { return this.client.post(`/api/accounts`, {
body, body,
@ -56,81 +55,63 @@ export default class AccountAPI extends BaseAPI {
}, opts) }, opts)
} }
async delete( async delete(accountID: string, opts: APIRequestOpts = { status: 204 }) {
accountID: string,
opts: APIRequestOpts = { status: 204 }) {
return this.doRequest(() => { return this.doRequest(() => {
return this.client.del( return this.client.del(`/api/accounts/${accountID}`, {
`/api/accounts/${accountID}`, internal: true,
{ })
internal: true,
}
)
}, opts) }, opts)
} }
async deleteCurrentAccount( async deleteCurrentAccount(opts: APIRequestOpts = { status: 204 }) {
opts: APIRequestOpts = { status: 204 }
) {
return this.doRequest(() => { return this.doRequest(() => {
return this.client.del( return this.client.del(`/api/accounts`)
`/api/accounts`
)
}, opts) }, opts)
} }
async verifyAccount( async verifyAccount(
verificationCode: string, verificationCode: string,
opts: APIRequestOpts = { status: 200 } opts: APIRequestOpts = { status: 200 }
){ ) {
return this.doRequest(() => { return this.doRequest(() => {
return this.client.post( return this.client.post(`/api/accounts/verify`, {
`/api/accounts/verify`, body: { verificationCode },
{ })
body: { verificationCode },
}
)
}, opts) }, opts)
} }
async sendVerificationEmail( async sendVerificationEmail(
email: string, email: string,
opts: APIRequestOpts = { status: 200 } opts: APIRequestOpts = { status: 200 }
): Promise<[Response, string]> { ): Promise<[Response, string]> {
return this.doRequest(async () => { return this.doRequest(async () => {
const [response] = await this.client.post( const [response] = await this.client.post(`/api/accounts/verify/send`, {
`/api/accounts/verify/send`, body: { email },
{ headers: {
body: { email }, [Header.RETURN_VERIFICATION_CODE]: "1",
headers: { },
[Header.RETURN_VERIFICATION_CODE]: "1" })
}
}
)
const code = response.headers.get(Header.VERIFICATION_CODE) const code = response.headers.get(Header.VERIFICATION_CODE)
return [response, code] return [response, code]
}, opts) }, opts)
} }
async search( async search(
searchType: string, searchType: string,
search: 'email' | 'tenantId', search: "email" | "tenantId",
opts: APIRequestOpts = { status: 200 } opts: APIRequestOpts = { status: 200 }
): Promise<[Response, SearchAccountsResponse]> { ): Promise<[Response, SearchAccountsResponse]> {
return this.doRequest(() => { return this.doRequest(() => {
let body: SearchAccountsRequest = {} let body: SearchAccountsRequest = {}
if (search === 'email') { if (search === "email") {
body.email = searchType body.email = searchType
} else if (search === 'tenantId') { } else if (search === "tenantId") {
body.tenantId = searchType body.tenantId = searchType
} }
return this.client.post( return this.client.post(`/api/accounts/search`, {
`/api/accounts/search`, body,
{ internal: true,
body, })
internal: true
}
)
}, opts) }, opts)
} }
} }

View File

@ -17,15 +17,12 @@ export default class AuthAPI extends BaseAPI {
opts: APIRequestOpts = { doExpect: true, status: 200 } opts: APIRequestOpts = { doExpect: true, status: 200 }
): Promise<[Response, string]> { ): Promise<[Response, string]> {
return this.doRequest(async () => { return this.doRequest(async () => {
const [res] = await this.client.post( const [res] = await this.client.post(`/api/auth/login`, {
`/api/auth/login`, body: {
{ email: email,
body: { password: password,
email: email, },
password: password, })
},
}
)
const cookie = res.headers.get("set-cookie") const cookie = res.headers.get("set-cookie")
return [res, cookie] return [res, cookie]
}, opts) }, opts)

View File

@ -4,10 +4,10 @@ import { APIRequestOpts } from "../../../types"
// TODO: Add to LicenseAPI // TODO: Add to LicenseAPI
export default class BaseAPI { export default class BaseAPI {
async doRequest( async doRequest(
request: () => Promise<[Response, any]>, request: () => Promise<[Response, any]>,
opts: APIRequestOpts): Promise<[Response, any]> { opts: APIRequestOpts
): Promise<[Response, any]> {
const [response, body] = await request() const [response, body] = await request()
// do expect on by default // do expect on by default
@ -19,4 +19,4 @@ export default class BaseAPI {
} }
return [response, body] return [response, body]
} }
} }

View File

@ -18,13 +18,10 @@ export default class LicenseAPI extends BaseAPI {
opts: APIRequestOpts = { status: 200 } opts: APIRequestOpts = { status: 200 }
): Promise<[Response, Account]> { ): Promise<[Response, Account]> {
return this.doRequest(() => { return this.doRequest(() => {
return this.client.put( return this.client.put(`/api/accounts/${accountId}/license`, {
`/api/accounts/${accountId}/license`, body,
{ internal: true,
body, })
internal: true,
}
)
}, opts) }, opts)
} }
} }

View File

@ -2,28 +2,28 @@ import { AccountInternalAPI } from "../api"
import { BudibaseTestConfiguration } from "../../shared" import { BudibaseTestConfiguration } from "../../shared"
export default class TestConfiguration<T> extends BudibaseTestConfiguration { export default class TestConfiguration<T> extends BudibaseTestConfiguration {
// apis // apis
api: AccountInternalAPI api: AccountInternalAPI
context: T context: T
constructor() { constructor() {
super() super()
this.api = new AccountInternalAPI(this.state) this.api = new AccountInternalAPI(this.state)
this.context = <T>{} this.context = <T>{}
} }
async beforeAll() { async beforeAll() {
await super.beforeAll() await super.beforeAll()
await this.setApiKey() await this.setApiKey()
} }
async afterAll() { async afterAll() {
await super.afterAll() await super.afterAll()
} }
async setApiKey() { async setApiKey() {
const apiKeyResponse = await this.internalApi.self.getApiKey() const apiKeyResponse = await this.internalApi.self.getApiKey()
this.state.apiKey = apiKeyResponse.apiKey this.state.apiKey = apiKeyResponse.apiKey
} }
} }

View File

@ -3,19 +3,19 @@ import { Hosting, CreateAccountRequest } from "@budibase/types"
// TODO: Refactor me to central location // TODO: Refactor me to central location
export const generateAccount = (): CreateAccountRequest => { export const generateAccount = (): CreateAccountRequest => {
const uuid = generator.guid() const uuid = generator.guid()
const email = `${uuid}@budibase.com` const email = `${uuid}@budibase.com`
const tenant = `tenant${uuid.replace(/-/g, "")}` const tenant = `tenant${uuid.replace(/-/g, "")}`
return { return {
email, email,
hosting: Hosting.CLOUD, hosting: Hosting.CLOUD,
name: email, name: email,
password: uuid, password: uuid,
profession: "software_engineer", profession: "software_engineer",
size: "10+", size: "10+",
tenantId: tenant, tenantId: tenant,
tenantName: tenant, tenantName: tenant,
} }
} }

View File

@ -1 +1 @@
export * as accounts from "./accounts" export * as accounts from "./accounts"

View File

@ -35,7 +35,9 @@ describe("Accounts", () => {
await config.loginAsAccount(createAccountRequest, { status: 400 }) await config.loginAsAccount(createAccountRequest, { status: 400 })
// Re-send verification email to get access to code // Re-send verification email to get access to code
const [_, code] = await config.accountsApi.accounts.sendVerificationEmail(email) const [_, code] = await config.accountsApi.accounts.sendVerificationEmail(
email
)
// Send the verification request // Send the verification request
await config.accountsApi.accounts.verifyAccount(code!) await config.accountsApi.accounts.verifyAccount(code!)
@ -56,11 +58,17 @@ describe("Accounts", () => {
const tenantId = generator.string() const tenantId = generator.string()
// Empty result // Empty result
const [_, emptyBody] = await config.api.accounts.search(tenantId, "tenantId") const [_, emptyBody] = await config.api.accounts.search(
tenantId,
"tenantId"
)
expect(emptyBody.length).toBe(0) expect(emptyBody.length).toBe(0)
// Hit result // Hit result
const [hitRes, hitBody] = await config.api.accounts.search(config.state.tenantId!, "tenantId") const [hitRes, hitBody] = await config.api.accounts.search(
config.state.tenantId!,
"tenantId"
)
expect(hitBody.length).toBe(1) expect(hitBody.length).toBe(1)
expect(hitBody[0].tenantId).toBe(config.state.tenantId) expect(hitBody[0].tenantId).toBe(config.state.tenantId)
}) })
@ -73,7 +81,10 @@ describe("Accounts", () => {
expect(emptyBody.length).toBe(0) expect(emptyBody.length).toBe(0)
// Hit result // Hit result
const [hitRes, hitBody] = await config.api.accounts.search(config.state.email!, "email") const [hitRes, hitBody] = await config.api.accounts.search(
config.state.email!,
"email"
)
expect(hitBody.length).toBe(1) expect(hitBody.length).toBe(1)
expect(hitBody[0].email).toBe(config.state.email) expect(hitBody[0].email).toBe(config.state.email)
}) })

View File

@ -9,7 +9,7 @@ import { APIRequestOpts } from "../types"
const accountsApi = new AccountInternalAPI({}) const accountsApi = new AccountInternalAPI({})
const internalApi = new BudibaseInternalAPI({}) const internalApi = new BudibaseInternalAPI({})
const API_OPTS: APIRequestOpts = { doExpect: false} const API_OPTS: APIRequestOpts = { doExpect: false }
// @ts-ignore // @ts-ignore
global.qa = {} global.qa = {}
@ -18,8 +18,10 @@ async function createAccount(): Promise<[CreateAccountRequest, Account]> {
const account = fixtures.accounts.generateAccount() const account = fixtures.accounts.generateAccount()
await accountsApi.accounts.validateEmail(account.email, API_OPTS) await accountsApi.accounts.validateEmail(account.email, API_OPTS)
await accountsApi.accounts.validateTenantId(account.tenantId, API_OPTS) await accountsApi.accounts.validateTenantId(account.tenantId, API_OPTS)
const [res, newAccount] = await accountsApi.accounts.create( const [res, newAccount] = await accountsApi.accounts.create(account, {
account, {...API_OPTS, autoVerify: true }) ...API_OPTS,
autoVerify: true,
})
await updateLicense(newAccount.accountId) await updateLicense(newAccount.accountId)
return [account, newAccount] return [account, newAccount]
} }
@ -27,25 +29,29 @@ async function createAccount(): Promise<[CreateAccountRequest, Account]> {
const UNLIMITED = { value: -1 } const UNLIMITED = { value: -1 }
async function updateLicense(accountId: string) { async function updateLicense(accountId: string) {
const [response] = await accountsApi.licenses.updateLicense(accountId, { const [response] = await accountsApi.licenses.updateLicense(
overrides: { accountId,
// add all features {
features: Object.values(Feature), overrides: {
quotas: { // add all features
usage: { features: Object.values(Feature),
monthly: { quotas: {
automations: UNLIMITED, usage: {
}, monthly: {
static: { automations: UNLIMITED,
rows: UNLIMITED, },
users: UNLIMITED, static: {
userGroups: UNLIMITED, rows: UNLIMITED,
plugins: UNLIMITED, users: UNLIMITED,
userGroups: UNLIMITED,
plugins: UNLIMITED,
},
}, },
}, },
}, },
}, },
}, { doExpect: false }) { doExpect: false }
)
if (response.status !== 200) { if (response.status !== 200) {
throw new Error( throw new Error(
`Could not update license for accountId=${accountId}: ${response.status}` `Could not update license for accountId=${accountId}: ${response.status}`

View File

@ -11,7 +11,9 @@ async function deleteAccount() {
// @ts-ignore // @ts-ignore
const accountID = global.qa.accountId const accountID = global.qa.accountId
const [response] = await accountsApi.accounts.delete(accountID, { doExpect: false }) const [response] = await accountsApi.accounts.delete(accountID, {
doExpect: false,
})
if (response.status !== 204) { if (response.status !== 204) {
throw new Error(`status: ${response.status} not equal to expected: 201`) throw new Error(`status: ${response.status} not equal to expected: 201`)
} }

View File

@ -69,7 +69,10 @@ export default class BudibaseTestConfiguration {
this.state.email = original.email this.state.email = original.email
} }
async loginAsAccount(account: CreateAccountRequest, opts: APIRequestOpts = {}) { async loginAsAccount(
account: CreateAccountRequest,
opts: APIRequestOpts = {}
) {
const [_, cookie] = await this.accountsApi.auth.login( const [_, cookie] = await this.accountsApi.auth.login(
account.email, account.email,
account.password, account.password,