budibase/packages/backend-core/src/context/index.ts

200 lines
4.9 KiB
TypeScript
Raw Normal View History

2022-09-28 09:56:45 +02:00
import env from "../environment"
import { SEPARATOR, DocumentType } from "../db/constants"
import cls from "./FunctionContext"
import { baseGlobalDBName } from "../db/tenancy"
import { IdentityContext } from "@budibase/types"
import { DEFAULT_TENANT_ID as _DEFAULT_TENANT_ID } from "../constants"
import { ContextKey } from "./constants"
import { PouchLike } from "../couch"
import { getDevelopmentAppID, getProdAppID } from "../db/conversions"
2022-09-28 09:56:45 +02:00
export const DEFAULT_TENANT_ID = _DEFAULT_TENANT_ID
// some test cases call functions directly, need to
// store an app ID to pretend there is a context
let TEST_APP_ID: string | null = null
export const isMultiTenant = () => {
return env.MULTI_TENANCY
2022-09-28 09:56:45 +02:00
}
const setAppTenantId = (appId: string) => {
const appTenantId = getTenantIDFromAppID(appId) || DEFAULT_TENANT_ID
updateTenantId(appTenantId)
}
2022-09-28 09:56:45 +02:00
const setIdentity = (identity: IdentityContext | null) => {
cls.setOnContext(ContextKey.IDENTITY, identity)
2022-09-28 09:56:45 +02:00
}
/**
* Given an app ID this will attempt to retrieve the tenant ID from it.
* @return {null|string} The tenant ID found within the app ID.
*/
export const getTenantIDFromAppID = (appId: string) => {
if (!appId) {
return null
}
if (!isMultiTenant()) {
return DEFAULT_TENANT_ID
}
2022-09-28 09:56:45 +02:00
const split = appId.split(SEPARATOR)
const hasDev = split[1] === DocumentType.DEV
if ((hasDev && split.length === 3) || (!hasDev && split.length === 2)) {
return null
}
if (hasDev) {
return split[2]
} else {
return split[1]
}
}
export const doInContext = async (appId: string, task: any) => {
// gets the tenant ID from the app ID
const tenantId = getTenantIDFromAppID(appId)
return doInTenant(tenantId, async () => {
return doInAppContext(appId, async () => {
return task()
})
})
}
export const doInTenant = (tenantId: string | null, task: any): any => {
2022-09-28 09:56:45 +02:00
// make sure default always selected in single tenancy
if (!env.MULTI_TENANCY) {
tenantId = tenantId || DEFAULT_TENANT_ID
}
return cls.run(async () => {
updateTenantId(tenantId)
return await task()
})
2022-09-28 09:56:45 +02:00
}
export const doInAppContext = (appId: string, task: any): any => {
2022-09-28 09:56:45 +02:00
if (!appId) {
throw new Error("appId is required")
}
const identity = getIdentity()
return cls.run(async () => {
2022-09-28 09:56:45 +02:00
// set the app tenant id
setAppTenantId(appId)
2022-09-28 09:56:45 +02:00
// set the app ID
cls.setOnContext(ContextKey.APP_ID, appId)
// preserve the identity
if (identity) {
setIdentity(identity)
}
// invoke the task
return await task()
})
2022-09-28 09:56:45 +02:00
}
export const doInIdentityContext = (
identity: IdentityContext,
task: any
): any => {
2022-09-28 09:56:45 +02:00
if (!identity) {
throw new Error("identity is required")
}
return cls.run(async () => {
cls.setOnContext(ContextKey.IDENTITY, identity)
// set the tenant so that doInTenant will preserve identity
if (identity.tenantId) {
updateTenantId(identity.tenantId)
2022-09-28 09:56:45 +02:00
}
// invoke the task
return await task()
})
2022-09-28 09:56:45 +02:00
}
export const getIdentity = (): IdentityContext | undefined => {
try {
return cls.getFromContext(ContextKey.IDENTITY)
} catch (e) {
// do nothing - identity is not in context
}
}
export const updateTenantId = (tenantId: string | null) => {
cls.setOnContext(ContextKey.TENANT_ID, tenantId)
}
export const updateAppId = async (appId: string) => {
try {
cls.setOnContext(ContextKey.APP_ID, appId)
} catch (err) {
if (env.isTest()) {
TEST_APP_ID = appId
} else {
throw err
}
}
}
export const getGlobalDB = (): PouchLike => {
const tenantId = cls.getFromContext(ContextKey.TENANT_ID)
return new PouchLike(baseGlobalDBName(tenantId))
2022-09-28 09:56:45 +02:00
}
export const isTenantIdSet = () => {
const tenantId = cls.getFromContext(ContextKey.TENANT_ID)
return !!tenantId
}
export const getTenantId = () => {
if (!isMultiTenant()) {
return DEFAULT_TENANT_ID
}
const tenantId = cls.getFromContext(ContextKey.TENANT_ID)
if (!tenantId) {
throw new Error("Tenant id not found")
}
return tenantId
}
export const getAppId = () => {
const foundId = cls.getFromContext(ContextKey.APP_ID)
if (!foundId && env.isTest() && TEST_APP_ID) {
return TEST_APP_ID
} else {
return foundId
}
}
export const isTenancyEnabled = () => {
return env.MULTI_TENANCY
}
/**
* Opens the app database based on whatever the request
* contained, dev or prod.
*/
export const getAppDB = (opts?: any): PouchLike => {
const appId = getAppId()
return new PouchLike(appId, opts)
2022-09-28 09:56:45 +02:00
}
/**
* This specifically gets the prod app ID, if the request
* contained a development app ID, this will open the prod one.
*/
export const getProdAppDB = (opts?: any): PouchLike => {
const appId = getAppId()
return new PouchLike(getProdAppID(appId), opts)
2022-09-28 09:56:45 +02:00
}
/**
* This specifically gets the dev app ID, if the request
* contained a prod app ID, this will open the dev one.
*/
export const getDevAppDB = (opts?: any): PouchLike => {
const appId = getAppId()
return new PouchLike(getDevelopmentAppID(appId), opts)
2022-09-28 09:56:45 +02:00
}