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"
|
2022-11-10 18:38:26 +01:00
|
|
|
import { ContextMap, ContextKey } from "./constants"
|
2022-11-11 13:46:32 +01:00
|
|
|
import { PouchLike } from "../db"
|
2022-11-09 17:53:42 +01:00
|
|
|
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
|
|
|
|
|
2022-11-10 17:38:32 +01:00
|
|
|
export function isMultiTenant() {
|
2022-11-09 17:53:42 +01:00
|
|
|
return env.MULTI_TENANCY
|
2022-09-28 09:56:45 +02:00
|
|
|
}
|
|
|
|
|
2022-11-10 17:38:32 +01:00
|
|
|
export function isTenantIdSet() {
|
|
|
|
const context = cls.getFromContext(ContextKey.MAIN) as ContextMap
|
2022-11-10 18:38:26 +01:00
|
|
|
return !!context?.tenantId
|
2022-11-09 17:53:42 +01:00
|
|
|
}
|
2022-09-28 09:56:45 +02:00
|
|
|
|
2022-11-10 17:38:32 +01:00
|
|
|
export function isTenancyEnabled() {
|
|
|
|
return env.MULTI_TENANCY
|
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.
|
|
|
|
*/
|
2022-11-10 17:38:32 +01:00
|
|
|
export function getTenantIDFromAppID(appId: string) {
|
2022-09-28 09:56:45 +02:00
|
|
|
if (!appId) {
|
2022-11-10 18:38:26 +01:00
|
|
|
return undefined
|
2022-09-28 09:56:45 +02:00
|
|
|
}
|
2022-10-14 21:10:44 +02:00
|
|
|
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)) {
|
2022-11-10 18:38:26 +01:00
|
|
|
return undefined
|
2022-09-28 09:56:45 +02:00
|
|
|
}
|
|
|
|
if (hasDev) {
|
|
|
|
return split[2]
|
|
|
|
} else {
|
|
|
|
return split[1]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-10 17:38:32 +01:00
|
|
|
function updateContext(updates: ContextMap) {
|
|
|
|
let context: ContextMap
|
|
|
|
try {
|
|
|
|
context = cls.getFromContext(ContextKey.MAIN)
|
|
|
|
} catch (err) {
|
|
|
|
// no context, start empty
|
|
|
|
context = {}
|
|
|
|
}
|
|
|
|
context = {
|
|
|
|
...context,
|
|
|
|
...updates,
|
|
|
|
}
|
|
|
|
return context
|
|
|
|
}
|
|
|
|
|
|
|
|
async function newContext(updates: ContextMap, task: any) {
|
|
|
|
// see if there already is a context setup
|
|
|
|
let context: ContextMap = updateContext(updates)
|
|
|
|
return cls.run(async () => {
|
|
|
|
cls.setOnContext(ContextKey.MAIN, context)
|
|
|
|
return await task()
|
2022-09-28 09:56:45 +02:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2022-11-10 17:38:32 +01:00
|
|
|
export async function doInContext(appId: string, task: any): Promise<any> {
|
|
|
|
const tenantId = getTenantIDFromAppID(appId)
|
|
|
|
return newContext(
|
|
|
|
{
|
2022-11-10 18:38:26 +01:00
|
|
|
tenantId,
|
|
|
|
appId,
|
2022-11-10 17:38:32 +01:00
|
|
|
},
|
|
|
|
task
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
export async function doInTenant(
|
|
|
|
tenantId: string | null,
|
|
|
|
task: any
|
|
|
|
): Promise<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
|
|
|
|
}
|
|
|
|
|
2022-11-10 18:38:26 +01:00
|
|
|
const updates = tenantId ? { tenantId } : {}
|
|
|
|
return newContext(updates, task)
|
2022-09-28 09:56:45 +02:00
|
|
|
}
|
|
|
|
|
2022-11-10 17:38:32 +01:00
|
|
|
export async function doInAppContext(appId: string, task: any): Promise<any> {
|
2022-09-28 09:56:45 +02:00
|
|
|
if (!appId) {
|
|
|
|
throw new Error("appId is required")
|
|
|
|
}
|
|
|
|
|
2022-11-10 17:38:32 +01:00
|
|
|
const tenantId = getTenantIDFromAppID(appId)
|
|
|
|
return newContext(
|
|
|
|
{
|
2022-11-10 18:38:26 +01:00
|
|
|
tenantId,
|
|
|
|
appId,
|
2022-11-10 17:38:32 +01:00
|
|
|
},
|
|
|
|
task
|
|
|
|
)
|
2022-09-28 09:56:45 +02:00
|
|
|
}
|
|
|
|
|
2022-11-10 17:38:32 +01:00
|
|
|
export async function doInIdentityContext(
|
2022-11-09 17:53:42 +01:00
|
|
|
identity: IdentityContext,
|
|
|
|
task: any
|
2022-11-10 17:38:32 +01:00
|
|
|
): Promise<any> {
|
2022-09-28 09:56:45 +02:00
|
|
|
if (!identity) {
|
|
|
|
throw new Error("identity is required")
|
|
|
|
}
|
|
|
|
|
2022-11-10 17:38:32 +01:00
|
|
|
const context: ContextMap = {
|
2022-11-10 18:38:26 +01:00
|
|
|
identity,
|
2022-11-10 17:38:32 +01:00
|
|
|
}
|
|
|
|
if (identity.tenantId) {
|
2022-11-10 18:38:26 +01:00
|
|
|
context.tenantId = identity.tenantId
|
2022-11-10 17:38:32 +01:00
|
|
|
}
|
|
|
|
return newContext(context, task)
|
2022-09-28 09:56:45 +02:00
|
|
|
}
|
|
|
|
|
2022-11-10 17:38:32 +01:00
|
|
|
export function getIdentity(): IdentityContext | undefined {
|
2022-09-28 09:56:45 +02:00
|
|
|
try {
|
2022-11-10 17:38:32 +01:00
|
|
|
const context = cls.getFromContext(ContextKey.MAIN) as ContextMap
|
2022-11-10 18:38:26 +01:00
|
|
|
return context?.identity
|
2022-09-28 09:56:45 +02:00
|
|
|
} catch (e) {
|
|
|
|
// do nothing - identity is not in context
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-10 17:38:32 +01:00
|
|
|
export function getTenantId(): string {
|
2022-09-28 09:56:45 +02:00
|
|
|
if (!isMultiTenant()) {
|
|
|
|
return DEFAULT_TENANT_ID
|
|
|
|
}
|
2022-11-10 17:38:32 +01:00
|
|
|
const context = cls.getFromContext(ContextKey.MAIN) as ContextMap
|
2022-11-10 18:38:26 +01:00
|
|
|
const tenantId = context?.tenantId
|
2022-09-28 09:56:45 +02:00
|
|
|
if (!tenantId) {
|
|
|
|
throw new Error("Tenant id not found")
|
|
|
|
}
|
|
|
|
return tenantId
|
|
|
|
}
|
|
|
|
|
2022-11-10 17:38:32 +01:00
|
|
|
export function getAppId(): string | undefined {
|
|
|
|
const context = cls.getFromContext(ContextKey.MAIN) as ContextMap
|
2022-11-10 18:38:26 +01:00
|
|
|
const foundId = context?.appId
|
2022-09-28 09:56:45 +02:00
|
|
|
if (!foundId && env.isTest() && TEST_APP_ID) {
|
|
|
|
return TEST_APP_ID
|
|
|
|
} else {
|
|
|
|
return foundId
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-10 18:38:26 +01:00
|
|
|
export function updateTenantId(tenantId?: string) {
|
2022-11-10 17:38:32 +01:00
|
|
|
let context: ContextMap = updateContext({
|
2022-11-10 18:38:26 +01:00
|
|
|
tenantId,
|
2022-11-10 17:38:32 +01:00
|
|
|
})
|
|
|
|
cls.setOnContext(ContextKey.MAIN, context)
|
|
|
|
}
|
|
|
|
|
|
|
|
export function updateAppId(appId: string) {
|
|
|
|
let context: ContextMap = updateContext({
|
2022-11-10 18:38:26 +01:00
|
|
|
appId,
|
2022-11-10 17:38:32 +01:00
|
|
|
})
|
|
|
|
try {
|
|
|
|
cls.setOnContext(ContextKey.MAIN, context)
|
|
|
|
} catch (err) {
|
|
|
|
if (env.isTest()) {
|
|
|
|
TEST_APP_ID = appId
|
|
|
|
} else {
|
|
|
|
throw err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export function getGlobalDB(): PouchLike {
|
|
|
|
const context = cls.getFromContext(ContextKey.MAIN) as ContextMap
|
2022-11-10 18:38:26 +01:00
|
|
|
return new PouchLike(baseGlobalDBName(context?.tenantId))
|
2022-09-28 09:56:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2022-11-10 17:38:32 +01:00
|
|
|
* Gets the app database based on whatever the request
|
2022-09-28 09:56:45 +02:00
|
|
|
* contained, dev or prod.
|
|
|
|
*/
|
2022-11-10 17:38:32 +01:00
|
|
|
export function getAppDB(opts?: any): PouchLike {
|
2022-11-09 17:53:42 +01:00
|
|
|
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
|
2022-11-10 17:38:32 +01:00
|
|
|
* contained a development app ID, this will get the prod one.
|
2022-09-28 09:56:45 +02:00
|
|
|
*/
|
2022-11-10 17:38:32 +01:00
|
|
|
export function getProdAppDB(opts?: any): PouchLike {
|
2022-11-09 17:53:42 +01:00
|
|
|
const appId = getAppId()
|
2022-11-11 12:57:50 +01:00
|
|
|
if (!appId) {
|
|
|
|
throw new Error("Unable to retrieve prod DB - no app ID.")
|
|
|
|
}
|
2022-11-09 17:53:42 +01:00
|
|
|
return new PouchLike(getProdAppID(appId), opts)
|
2022-09-28 09:56:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This specifically gets the dev app ID, if the request
|
2022-11-10 17:38:32 +01:00
|
|
|
* contained a prod app ID, this will get the dev one.
|
2022-09-28 09:56:45 +02:00
|
|
|
*/
|
2022-11-10 17:38:32 +01:00
|
|
|
export function getDevAppDB(opts?: any): PouchLike {
|
2022-11-09 17:53:42 +01:00
|
|
|
const appId = getAppId()
|
2022-11-11 12:57:50 +01:00
|
|
|
if (!appId) {
|
|
|
|
throw new Error("Unable to retrieve dev DB - no app ID.")
|
|
|
|
}
|
2022-11-09 17:53:42 +01:00
|
|
|
return new PouchLike(getDevelopmentAppID(appId), opts)
|
2022-09-28 09:56:45 +02:00
|
|
|
}
|