Working on typing TestConfiguration.ts.

This commit is contained in:
Sam Rose 2024-02-28 10:08:42 +00:00
parent 6e4c2b7242
commit 7ac2449201
No known key found for this signature in database
3 changed files with 107 additions and 125 deletions

View File

@ -248,4 +248,10 @@ describe("/applications", () => {
expect(devLogs.data.length).toBe(0) expect(devLogs.data.length).toBe(0)
}) })
}) })
describe("permissions", () => {
it("should return the list of apps the user has access to", async () => {
const user = config.user
})
})
}) })

View File

@ -29,6 +29,6 @@ start().catch(err => {
throw err throw err
}) })
export function getServer() { export function getServer(): Server {
return server return server
} }

View File

@ -49,6 +49,7 @@ import {
AuthToken, AuthToken,
Automation, Automation,
CreateViewRequest, CreateViewRequest,
Ctx,
Datasource, Datasource,
FieldType, FieldType,
INTERNAL_TABLE_SOURCE_ID, INTERNAL_TABLE_SOURCE_ID,
@ -68,6 +69,8 @@ import {
import API from "./api" import API from "./api"
import { cloneDeep } from "lodash" import { cloneDeep } from "lodash"
import jwt, { Secret } from "jsonwebtoken" import jwt, { Secret } from "jsonwebtoken"
import { Server } from "http"
import { userDetailListType } from "aws-sdk/clients/iam"
mocks.licenses.init(pro) mocks.licenses.init(pro)
@ -82,16 +85,16 @@ export interface TableToBuild extends Omit<Table, "sourceId" | "sourceType"> {
} }
export default class TestConfiguration { export default class TestConfiguration {
server: any server?: Server
request: supertest.SuperTest<supertest.Test> | undefined request?: supertest.SuperTest<supertest.Test>
started: boolean started: boolean
appId: string | null appId?: string
allApps: any[] allApps: any[]
app?: App app?: App
prodApp: any prodApp?: App
prodAppId: any prodAppId?: string
user: any user?: User
userMetadataId: any userMetadataId?: string
table?: Table table?: Table
automation: any automation: any
datasource?: Datasource datasource?: Datasource
@ -99,10 +102,6 @@ export default class TestConfiguration {
api: API api: API
csrfToken?: string csrfToken?: string
private get globalUserId() {
return this.user._id
}
constructor(openServer = true) { constructor(openServer = true) {
if (openServer) { if (openServer) {
// use a random port because it doesn't matter // use a random port because it doesn't matter
@ -114,7 +113,7 @@ export default class TestConfiguration {
} else { } else {
this.started = false this.started = false
} }
this.appId = null this.appId = undefined
this.allApps = [] this.allApps = []
this.api = new API(this) this.api = new API(this)
@ -134,37 +133,49 @@ export default class TestConfiguration {
getAppId() { getAppId() {
if (!this.appId) { if (!this.appId) {
throw "appId has not been initialised properly" throw new Error("appId has not been initialised properly")
} }
return this.appId return this.appId
} }
getProdAppId() { getProdAppId() {
if (!this.prodAppId) {
throw new Error(
"prodAppId has not been initialised, call config.init() first"
)
}
return this.prodAppId return this.prodAppId
} }
getUser(): User {
if (!this.user) {
throw new Error("User has not been initialised, call config.init() first")
}
return this.user
}
getUserDetails() { getUserDetails() {
const user = this.getUser()
return { return {
globalId: this.globalUserId, globalId: user._id!,
email: this.user.email, email: user.email,
firstName: this.user.firstName, firstName: user.firstName,
lastName: this.user.lastName, lastName: user.lastName,
} }
} }
async doInContext<T>( async doInContext<T>(
appId: string | null, appId: string | undefined,
task: () => Promise<T> task: () => Promise<T>
): Promise<T> { ): Promise<T> {
const tenant = this.getTenantId()
return tenancy.doInTenant(tenant, () => {
if (!appId) { if (!appId) {
appId = this.appId appId = this.appId
} }
const tenant = this.getTenantId()
return tenancy.doInTenant(tenant, () => {
// check if already in a context // check if already in a context
if (context.getAppId() == null && appId !== null) { if (context.getAppId() == null && appId) {
return context.doInAppContext(appId, async () => { return context.doInAppContext(appId, async () => {
return task() return task()
}) })
@ -259,7 +270,11 @@ export default class TestConfiguration {
// UTILS // UTILS
_req(body: any, params: any, controlFunc: any) { _req<Req extends Record<string, any>, Res, Context extends Ctx<Req, Res>>(
handler: (ctx: Context) => Promise<void>,
body?: Req,
params?: Record<string, string>
) {
// create a fake request ctx // create a fake request ctx
const request: any = {} const request: any = {}
const appId = this.appId const appId = this.appId
@ -278,29 +293,19 @@ export default class TestConfiguration {
throw new Error(`Error ${status} - ${message}`) throw new Error(`Error ${status} - ${message}`)
} }
return this.doInContext(appId, async () => { return this.doInContext(appId, async () => {
await controlFunc(request) await handler(request)
return request.body return request.body
}) })
} }
// USER / AUTH // USER / AUTH
async globalUser( async globalUser(config: Partial<User> = {}): Promise<User> {
config: {
id?: string
firstName?: string
lastName?: string
builder?: boolean
admin?: boolean
email?: string
roles?: any
} = {}
): Promise<User> {
const { const {
id = `us_${newid()}`, _id = `us_${newid()}`,
firstName = generator.first(), firstName = generator.first(),
lastName = generator.last(), lastName = generator.last(),
builder = true, builder = { global: true },
admin = false, admin = { global: false },
email = generator.email(), email = generator.email(),
roles, roles,
} = config } = config
@ -308,72 +313,30 @@ export default class TestConfiguration {
const db = tenancy.getTenantDB(this.getTenantId()) const db = tenancy.getTenantDB(this.getTenantId())
let existing let existing
try { try {
existing = await db.get<any>(id) existing = await db.get<User>(_id)
} catch (err) { } catch (err) {
existing = { email } existing = { email }
} }
const user: User = { const user: User = {
_id: id, _id: _id,
...existing, ...existing,
roles: roles || {}, roles: roles || {},
tenantId: this.getTenantId(), tenantId: this.getTenantId(),
firstName, firstName,
lastName, lastName,
} }
await sessions.createASession(id, { await sessions.createASession(_id, {
sessionId: "sessionid", sessionId: "sessionid",
tenantId: this.getTenantId(), tenantId: this.getTenantId(),
csrfToken: this.csrfToken, csrfToken: this.csrfToken,
}) })
if (builder) {
user.builder = { global: true }
} else {
user.builder = { global: false }
}
if (admin) {
user.admin = { global: true }
} else {
user.admin = { global: false }
}
const resp = await db.put(user) const resp = await db.put(user)
return { return { _rev: resp.rev, ...user }
_rev: resp.rev,
...user,
}
} }
async createUser( async createUser(user: Partial<User> = {}): Promise<User> {
user: { const resp = await this.globalUser(user)
id?: string await cache.user.invalidateUser(resp._id!)
firstName?: string
lastName?: string
email?: string
builder?: boolean
admin?: boolean
roles?: UserRoles
} = {}
): Promise<User> {
const {
id,
firstName = generator.first(),
lastName = generator.last(),
email = generator.email(),
builder = true,
admin,
roles,
} = user
const globalId = !id ? `us_${Math.random()}` : `us_${id}`
const resp = await this.globalUser({
id: globalId,
firstName,
lastName,
email,
builder,
admin,
roles: roles || {},
})
await cache.user.invalidateUser(globalId)
return resp return resp
} }
@ -381,7 +344,7 @@ export default class TestConfiguration {
return context.doInTenant(this.tenantId!, async () => { return context.doInTenant(this.tenantId!, async () => {
const baseGroup = structures.userGroups.userGroup() const baseGroup = structures.userGroups.userGroup()
baseGroup.roles = { baseGroup.roles = {
[this.prodAppId]: roleId, [this.getProdAppId()]: roleId,
} }
const { id, rev } = await pro.sdk.groups.save(baseGroup) const { id, rev } = await pro.sdk.groups.save(baseGroup)
return { return {
@ -404,8 +367,18 @@ export default class TestConfiguration {
}) })
} }
async login({ roleId, userId, builder, prodApp = false }: any = {}) { async login({
const appId = prodApp ? this.prodAppId : this.appId roleId,
userId,
builder,
prodApp,
}: {
roleId: string
userId: string
builder: boolean
prodApp: boolean
}) {
const appId = prodApp ? this.getProdAppId() : this.getAppId()
return context.doInAppContext(appId, async () => { return context.doInAppContext(appId, async () => {
userId = !userId ? `us_uuid1` : userId userId = !userId ? `us_uuid1` : userId
if (!this.request) { if (!this.request) {
@ -414,9 +387,9 @@ export default class TestConfiguration {
// make sure the user exists in the global DB // make sure the user exists in the global DB
if (roleId !== roles.BUILTIN_ROLE_IDS.PUBLIC) { if (roleId !== roles.BUILTIN_ROLE_IDS.PUBLIC) {
await this.globalUser({ await this.globalUser({
id: userId, _id: userId,
builder, builder: { global: builder },
roles: { [this.prodAppId]: roleId }, roles: { [appId]: roleId },
}) })
} }
await sessions.createASession(userId, { await sessions.createASession(userId, {
@ -445,8 +418,9 @@ export default class TestConfiguration {
defaultHeaders(extras = {}, prodApp = false) { defaultHeaders(extras = {}, prodApp = false) {
const tenantId = this.getTenantId() const tenantId = this.getTenantId()
const user = this.getUser()
const authObj: AuthToken = { const authObj: AuthToken = {
userId: this.globalUserId, userId: user._id!,
sessionId: "sessionid", sessionId: "sessionid",
tenantId, tenantId,
} }
@ -498,7 +472,7 @@ export default class TestConfiguration {
builder = false, builder = false,
prodApp = true, prodApp = true,
} = {}) { } = {}) {
return this.login({ email, roleId, builder, prodApp }) return this.login({ userId: email, roleId, builder, prodApp })
} }
// TENANCY // TENANCY
@ -521,7 +495,7 @@ export default class TestConfiguration {
this.tenantId = structures.tenant.id() this.tenantId = structures.tenant.id()
this.user = await this.globalUser() this.user = await this.globalUser()
this.userMetadataId = generateUserMetadataID(this.user._id) this.userMetadataId = generateUserMetadataID(this.user._id!)
return this.createApp(appName) return this.createApp(appName)
} }
@ -532,7 +506,11 @@ export default class TestConfiguration {
// API // API
async generateApiKey(userId = this.user._id) { async generateApiKey(userId?: string) {
const user = this.getUser()
if (!userId) {
userId = user._id!
}
const db = tenancy.getTenantDB(this.getTenantId()) const db = tenancy.getTenantDB(this.getTenantId())
const id = dbCore.generateDevInfoID(userId) const id = dbCore.generateDevInfoID(userId)
let devInfo: any let devInfo: any
@ -552,13 +530,15 @@ export default class TestConfiguration {
async createApp(appName: string): Promise<App> { async createApp(appName: string): Promise<App> {
// create dev app // create dev app
// clear any old app // clear any old app
this.appId = null this.appId = undefined
this.app = await context.doInTenant(this.tenantId!, async () => { this.app = await context.doInTenant(this.tenantId!, async () => {
const app = await this._req({ name: appName }, null, appController.create) const app = (await this._req(appController.create, {
name: appName,
})) as App
this.appId = app.appId! this.appId = app.appId!
return app return app
}) })
return await context.doInAppContext(this.getAppId(), async () => { return await context.doInAppContext(this.app.appId!, async () => {
// create production app // create production app
this.prodApp = await this.publish() this.prodApp = await this.publish()
@ -570,7 +550,7 @@ export default class TestConfiguration {
} }
async publish() { async publish() {
await this._req(null, null, deployController.publishApp) await this._req(deployController.publishApp)
// @ts-ignore // @ts-ignore
const prodAppId = this.getAppId().replace("_dev", "") const prodAppId = this.getAppId().replace("_dev", "")
this.prodAppId = prodAppId this.prodAppId = prodAppId
@ -582,13 +562,11 @@ export default class TestConfiguration {
} }
async unpublish() { async unpublish() {
const response = await this._req( const response = await this._req(appController.unpublish, {
null, appId: this.appId,
{ appId: this.appId }, })
appController.unpublish this.prodAppId = undefined
) this.prodApp = undefined
this.prodAppId = null
this.prodApp = null
return response return response
} }
@ -716,8 +694,7 @@ export default class TestConfiguration {
// ROLE // ROLE
async createRole(config?: any) { async createRole(config?: any) {
config = config || basicRole() return this._req(roleController.save, config || basicRole())
return this._req(config, null, roleController.save)
} }
// VIEW // VIEW
@ -730,7 +707,7 @@ export default class TestConfiguration {
tableId: this.table!._id, tableId: this.table!._id,
name: generator.guid(), name: generator.guid(),
} }
return this._req(view, null, viewController.v1.save) return this._req(viewController.v1.save, view)
} }
async createView( async createView(
@ -760,13 +737,13 @@ export default class TestConfiguration {
delete config._rev delete config._rev
} }
this.automation = ( this.automation = (
await this._req(config, null, automationController.create) await this._req(automationController.create, config)
).automation ).automation
return this.automation return this.automation
} }
async getAllAutomations() { async getAllAutomations() {
return this._req(null, null, automationController.fetch) return this._req(automationController.fetch)
} }
async deleteAutomation(automation?: any) { async deleteAutomation(automation?: any) {
@ -774,11 +751,10 @@ export default class TestConfiguration {
if (!automation) { if (!automation) {
return return
} }
return this._req( return this._req(automationController.destroy, {
null, id: automation._id,
{ id: automation._id, rev: automation._rev }, rev: automation._rev,
automationController.destroy })
)
} }
async createWebhook(config?: any) { async createWebhook(config?: any) {
@ -787,7 +763,7 @@ export default class TestConfiguration {
} }
config = config || basicWebhook(this.automation._id) config = config || basicWebhook(this.automation._id)
return (await this._req(config, null, webhookController.save)).webhook return (await this._req(webhookController.save, config)).webhook
} }
// DATASOURCE // DATASOURCE
@ -871,21 +847,21 @@ export default class TestConfiguration {
throw "No datasource created for query." throw "No datasource created for query."
} }
config = config || basicQuery(this.datasource!._id!) config = config || basicQuery(this.datasource!._id!)
return this._req(config, null, queryController.save) return this._req(queryController.save, config)
} }
// SCREEN // SCREEN
async createScreen(config?: any) { async createScreen(config?: any) {
config = config || basicScreen() config = config || basicScreen()
return this._req(config, null, screenController.save) return this._req(screenController.save, config)
} }
// LAYOUT // LAYOUT
async createLayout(config?: any) { async createLayout(config?: any) {
config = config || basicLayout() config = config || basicLayout()
return await this._req(config, null, layoutController.save) return await this._req(layoutController.save, config)
} }
} }