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

346 lines
7.7 KiB
TypeScript
Raw Normal View History

// some test cases call functions directly, need to
// store an app ID to pretend there is a context
import env from "../environment"
import Context from "./Context"
import * as conversions from "../docIds/conversions"
import { getDB } from "../db/db"
import {
DocumentType,
SEPARATOR,
StaticDatabases,
DEFAULT_TENANT_ID,
} from "../constants"
import { Database, IdentityContext } from "@budibase/types"
import { ContextMap } from "./types"
let TEST_APP_ID: string | null = null
export function getGlobalDBName(tenantId?: string) {
// tenant ID can be set externally, for example user API where
// new tenants are being created, this may be the case
if (!tenantId) {
tenantId = getTenantId()
}
return baseGlobalDBName(tenantId)
}
export function getAuditLogDBName(tenantId?: string) {
if (!tenantId) {
tenantId = getTenantId()
}
if (tenantId === DEFAULT_TENANT_ID) {
return StaticDatabases.AUDIT_LOGS.name
} else {
return `${tenantId}${SEPARATOR}${StaticDatabases.AUDIT_LOGS.name}`
}
}
export function baseGlobalDBName(tenantId: string | undefined | null) {
if (!tenantId || tenantId === DEFAULT_TENANT_ID) {
return StaticDatabases.GLOBAL.name
} else {
return `${tenantId}${SEPARATOR}${StaticDatabases.GLOBAL.name}`
}
}
export function getPlatformURL() {
return env.PLATFORM_URL
}
export function isMultiTenant() {
2023-03-30 18:27:13 +02:00
return !!env.MULTI_TENANCY
}
export function isTenantIdSet() {
const context = Context.get()
return !!context?.tenantId
}
export function isTenancyEnabled() {
return env.MULTI_TENANCY
}
/**
* Given an app ID this will attempt to retrieve the tenant ID from it.
* @return The tenant ID found within the app ID.
*/
export function getTenantIDFromAppID(appId: string) {
if (!appId) {
return undefined
}
if (!isMultiTenant()) {
return DEFAULT_TENANT_ID
}
const split = appId.split(SEPARATOR)
const hasDev = split[1] === DocumentType.DEV
if ((hasDev && split.length === 3) || (!hasDev && split.length === 2)) {
return undefined
}
if (hasDev) {
return split[2]
} else {
return split[1]
}
}
function updateContext(updates: ContextMap): ContextMap {
let context: ContextMap
try {
context = Context.get()
} catch (err) {
// no context, start empty
context = {}
}
context = {
...context,
...updates,
}
return context
}
2023-11-08 17:17:24 +01:00
async function newContext<T>(updates: ContextMap, task: () => T) {
2023-12-01 10:39:44 +01:00
guardMigration()
// see if there already is a context setup
let context: ContextMap = updateContext(updates)
return Context.run(context, task)
}
2023-11-08 17:17:24 +01:00
export async function doInAutomationContext<T>(params: {
2023-05-17 16:31:14 +02:00
appId: string
automationId: string
2023-11-08 17:17:24 +01:00
task: () => T
}): Promise<T> {
const tenantId = getTenantIDFromAppID(params.appId)
2023-05-17 14:54:20 +02:00
return newContext(
{
tenantId,
appId: params.appId,
automationId: params.automationId,
2023-05-17 14:54:20 +02:00
},
params.task
2023-05-17 14:54:20 +02:00
)
}
export async function doInContext(appId: string, task: any): Promise<any> {
const tenantId = getTenantIDFromAppID(appId)
return newContext(
{
tenantId,
appId,
},
task
)
}
Per user pricing (#10378) * Update pro version to 2.4.44-alpha.9 (#10231) Co-authored-by: Budibase Staging Release Bot <> * Track installation and unique tenant id on licence activate (#10146) * changes and exports * removing the extend * Lint + tidy * Update account.ts --------- Co-authored-by: Rory Powell <rory.codes@gmail.com> Co-authored-by: mike12345567 <me@michaeldrury.co.uk> * Type updates for loading new plans (#10245) * Add new quota for max users on free plan * Split available vs purchased plan & price type definitions. Update usages of available prices and plans * Type fixes * Add types for minimums * New `PlanModel` type for `PER_USER` and `DAY_PASS` (#10247) * Add new quota for max users on free plan * Split available vs purchased plan & price type definitions. Update usages of available prices and plans * Type fixes * Add types for minimums * New `PlanModel` type for `PER_USER` and `DAY_PASS` * Add loadEnvFiles to lerna config for run command to prevent local test failures * Fix types in license test structure * Add quotas integration to user create / delete + migration (#10250) * Add new quota for max users on free plan * Split available vs purchased plan & price type definitions. Update usages of available prices and plans * Type fixes * Add types for minimums * New `PlanModel` type for `PER_USER` and `DAY_PASS` * Add loadEnvFiles to lerna config for run command to prevent local test failures * Fix types in license test structure * Add quotas integration to user create / delete * Always sync user count from view total_rows value for accuracy * Add migration to sync users * Add syncUsers.spec.ts * Lint * Types and structures for user subscription quantity sync (#10280) * Add new quota for max users on free plan * Split available vs purchased plan & price type definitions. Update usages of available prices and plans * Type fixes * Add types for minimums * New `PlanModel` type for `PER_USER` and `DAY_PASS` * Add loadEnvFiles to lerna config for run command to prevent local test failures * Fix types in license test structure * Add quotas integration to user create / delete * Always sync user count from view total_rows value for accuracy * Add migration to sync users * Add syncUsers.spec.ts * Prevent old installs from activating, track install info via get license request instead of on activation. * Add usesInvoicing to PurchasedPlan * Add min/max users to PurchasedPlan * Additional test structures for generating a license, remove maxUsers from PurchasedPlan - this is already present in the license quotas * Stripe integration for monthly prorations on annual plans * Integrate annual prorations with test clocks * Updated types, test utils and date processing for licensing (#10346) * Add new quota for max users on free plan * Split available vs purchased plan & price type definitions. Update usages of available prices and plans * Type fixes * Add types for minimums * New `PlanModel` type for `PER_USER` and `DAY_PASS` * Add loadEnvFiles to lerna config for run command to prevent local test failures * Fix types in license test structure * Add quotas integration to user create / delete * Always sync user count from view total_rows value for accuracy * Add migration to sync users * Add syncUsers.spec.ts * Prevent old installs from activating, track install info via get license request instead of on activation. * Add usesInvoicing to PurchasedPlan * Add min/max users to PurchasedPlan * Additional test structures for generating a license, remove maxUsers from PurchasedPlan - this is already present in the license quotas * Stripe integration for monthly prorations on annual plans * Integrate annual prorations with test clocks * Updated types, test utils and date processing * Lint * Pricing/billing page (#10353) * bbui updates for billing page * Require all PlanTypes in PlanMinimums for compile time safety * fix test package utils * Incoming user limits warnings (#10379) * incoming user limits warning * fix inlinealert button * add corretc button link and text to user alert * pr comments * simplify limit check * Types and test updates for subscription quantity changes in account-portal (#10372) * Add chance extensions for `arrayOf`. Update events spies with license events * Add generics to doInTenant response * Update account structure with quota usage * User count limits (#10385) * incoming user limits warning * fix inlinealert button * add corretc button link and text to user alert * pr comments * simplify limit check * user limit messaging on add users modal * user limit messaging on import users modal * update licensing store to be more generic * some styling updates * remove console log * Store tweaks * Add startDate to Quota type --------- Co-authored-by: Rory Powell <rory.codes@gmail.com> * Lint * Support custom lock options * Reactivity fixes for add user modals * Update ethereal email creds * Add warn for getting invite from code error * Extract disabling user import condition * Handling unlimited users in modals logic and adding start date processing to store * Lint * Integration testing fixes (#10389) * lint --------- Co-authored-by: Mateus Badan de Pieri <mateuspieri@gmail.com> Co-authored-by: mike12345567 <me@michaeldrury.co.uk> Co-authored-by: Peter Clement <PClmnt@users.noreply.github.com>
2023-04-24 10:31:48 +02:00
export async function doInTenant<T>(
tenantId: string | null,
Per user pricing (#10378) * Update pro version to 2.4.44-alpha.9 (#10231) Co-authored-by: Budibase Staging Release Bot <> * Track installation and unique tenant id on licence activate (#10146) * changes and exports * removing the extend * Lint + tidy * Update account.ts --------- Co-authored-by: Rory Powell <rory.codes@gmail.com> Co-authored-by: mike12345567 <me@michaeldrury.co.uk> * Type updates for loading new plans (#10245) * Add new quota for max users on free plan * Split available vs purchased plan & price type definitions. Update usages of available prices and plans * Type fixes * Add types for minimums * New `PlanModel` type for `PER_USER` and `DAY_PASS` (#10247) * Add new quota for max users on free plan * Split available vs purchased plan & price type definitions. Update usages of available prices and plans * Type fixes * Add types for minimums * New `PlanModel` type for `PER_USER` and `DAY_PASS` * Add loadEnvFiles to lerna config for run command to prevent local test failures * Fix types in license test structure * Add quotas integration to user create / delete + migration (#10250) * Add new quota for max users on free plan * Split available vs purchased plan & price type definitions. Update usages of available prices and plans * Type fixes * Add types for minimums * New `PlanModel` type for `PER_USER` and `DAY_PASS` * Add loadEnvFiles to lerna config for run command to prevent local test failures * Fix types in license test structure * Add quotas integration to user create / delete * Always sync user count from view total_rows value for accuracy * Add migration to sync users * Add syncUsers.spec.ts * Lint * Types and structures for user subscription quantity sync (#10280) * Add new quota for max users on free plan * Split available vs purchased plan & price type definitions. Update usages of available prices and plans * Type fixes * Add types for minimums * New `PlanModel` type for `PER_USER` and `DAY_PASS` * Add loadEnvFiles to lerna config for run command to prevent local test failures * Fix types in license test structure * Add quotas integration to user create / delete * Always sync user count from view total_rows value for accuracy * Add migration to sync users * Add syncUsers.spec.ts * Prevent old installs from activating, track install info via get license request instead of on activation. * Add usesInvoicing to PurchasedPlan * Add min/max users to PurchasedPlan * Additional test structures for generating a license, remove maxUsers from PurchasedPlan - this is already present in the license quotas * Stripe integration for monthly prorations on annual plans * Integrate annual prorations with test clocks * Updated types, test utils and date processing for licensing (#10346) * Add new quota for max users on free plan * Split available vs purchased plan & price type definitions. Update usages of available prices and plans * Type fixes * Add types for minimums * New `PlanModel` type for `PER_USER` and `DAY_PASS` * Add loadEnvFiles to lerna config for run command to prevent local test failures * Fix types in license test structure * Add quotas integration to user create / delete * Always sync user count from view total_rows value for accuracy * Add migration to sync users * Add syncUsers.spec.ts * Prevent old installs from activating, track install info via get license request instead of on activation. * Add usesInvoicing to PurchasedPlan * Add min/max users to PurchasedPlan * Additional test structures for generating a license, remove maxUsers from PurchasedPlan - this is already present in the license quotas * Stripe integration for monthly prorations on annual plans * Integrate annual prorations with test clocks * Updated types, test utils and date processing * Lint * Pricing/billing page (#10353) * bbui updates for billing page * Require all PlanTypes in PlanMinimums for compile time safety * fix test package utils * Incoming user limits warnings (#10379) * incoming user limits warning * fix inlinealert button * add corretc button link and text to user alert * pr comments * simplify limit check * Types and test updates for subscription quantity changes in account-portal (#10372) * Add chance extensions for `arrayOf`. Update events spies with license events * Add generics to doInTenant response * Update account structure with quota usage * User count limits (#10385) * incoming user limits warning * fix inlinealert button * add corretc button link and text to user alert * pr comments * simplify limit check * user limit messaging on add users modal * user limit messaging on import users modal * update licensing store to be more generic * some styling updates * remove console log * Store tweaks * Add startDate to Quota type --------- Co-authored-by: Rory Powell <rory.codes@gmail.com> * Lint * Support custom lock options * Reactivity fixes for add user modals * Update ethereal email creds * Add warn for getting invite from code error * Extract disabling user import condition * Handling unlimited users in modals logic and adding start date processing to store * Lint * Integration testing fixes (#10389) * lint --------- Co-authored-by: Mateus Badan de Pieri <mateuspieri@gmail.com> Co-authored-by: mike12345567 <me@michaeldrury.co.uk> Co-authored-by: Peter Clement <PClmnt@users.noreply.github.com>
2023-04-24 10:31:48 +02:00
task: () => T
): Promise<T> {
// make sure default always selected in single tenancy
if (!env.MULTI_TENANCY) {
tenantId = tenantId || DEFAULT_TENANT_ID
}
const updates = tenantId ? { tenantId } : {}
return newContext(updates, task)
}
2023-11-08 17:17:24 +01:00
export async function doInAppContext<T>(
2023-12-01 09:36:24 +01:00
appId: string,
2023-11-08 17:17:24 +01:00
task: () => T
2023-12-01 10:19:28 +01:00
): Promise<T> {
return _doInAppContext(appId, task)
}
async function _doInAppContext<T>(
appId: string,
task: () => T,
extraContextSettings?: ContextMap
2023-11-08 17:17:24 +01:00
): Promise<T> {
2023-12-01 09:36:24 +01:00
if (!appId) {
throw new Error("appId is required")
}
2023-12-01 09:36:24 +01:00
const tenantId = getTenantIDFromAppID(appId)
2023-12-01 10:19:28 +01:00
const updates: ContextMap = { appId, ...extraContextSettings }
2023-12-01 09:36:24 +01:00
if (tenantId) {
updates.tenantId = tenantId
}
2023-12-01 09:36:24 +01:00
return newContext(updates, task)
}
2023-11-08 17:17:24 +01:00
export async function doInIdentityContext<T>(
identity: IdentityContext,
2023-11-08 17:17:24 +01:00
task: () => T
): Promise<T> {
if (!identity) {
throw new Error("identity is required")
}
const context: ContextMap = {
identity,
}
if (identity.tenantId) {
context.tenantId = identity.tenantId
}
return newContext(context, task)
}
2023-12-01 10:39:44 +01:00
function guardMigration() {
const context = Context.get()
if (context?.isMigrating) {
throw new Error(
"The context cannot be changed, a migration is currently running"
2023-12-01 10:39:44 +01:00
)
}
}
2023-12-01 09:36:24 +01:00
export async function doInAppMigrationContext<T>(
appId: string,
task: () => T
): Promise<T> {
2023-12-01 10:39:44 +01:00
return _doInAppContext(appId, task, {
isMigrating: true,
})
2023-12-01 09:36:24 +01:00
}
export function getIdentity(): IdentityContext | undefined {
try {
const context = Context.get()
return context?.identity
} catch (e) {
// do nothing - identity is not in context
}
}
export function getTenantId(): string {
if (!isMultiTenant()) {
return DEFAULT_TENANT_ID
}
const context = Context.get()
const tenantId = context?.tenantId
if (!tenantId) {
throw new Error("Tenant id not found")
}
return tenantId
}
2023-05-17 14:54:20 +02:00
export function getAutomationId(): string | undefined {
const context = Context.get()
return context?.automationId
}
export function getAppId(): string | undefined {
const context = Context.get()
const foundId = context?.appId
if (!foundId && env.isTest() && TEST_APP_ID) {
return TEST_APP_ID
} else {
return foundId
}
}
export const getProdAppId = () => {
const appId = getAppId()
if (!appId) {
throw new Error("Could not get appId")
}
return conversions.getProdAppID(appId)
}
export function doInEnvironmentContext(
values: Record<string, string>,
task: any
) {
if (!values) {
throw new Error("Must supply environment variables.")
}
const updates = {
environmentVariables: values,
}
return newContext(updates, task)
}
2023-03-27 12:10:07 +02:00
export function doInScimContext(task: any) {
const updates: ContextMap = {
2023-03-27 16:26:07 +02:00
isScim: true,
2023-03-27 12:10:07 +02:00
}
return newContext(updates, task)
}
export function getEnvironmentVariables() {
const context = Context.get()
if (!context.environmentVariables) {
return null
} else {
return context.environmentVariables
}
}
export function getGlobalDB(): Database {
const context = Context.get()
if (!context || (env.MULTI_TENANCY && !context.tenantId)) {
throw new Error("Global DB not found")
}
return getDB(baseGlobalDBName(context?.tenantId))
}
export function getAuditLogsDB(): Database {
if (!getTenantId()) {
2023-02-24 14:32:45 +01:00
throw new Error("No tenant ID found - cannot open audit log DB")
}
return getDB(getAuditLogDBName())
}
/**
* Gets the app database based on whatever the request
* contained, dev or prod.
*/
export function getAppDB(opts?: any): Database {
const appId = getAppId()
2023-11-08 15:37:19 +01:00
if (!appId) {
throw new Error("Unable to retrieve app DB - no app ID.")
}
return getDB(appId, opts)
}
/**
* This specifically gets the prod app ID, if the request
* contained a development app ID, this will get the prod one.
*/
export function getProdAppDB(opts?: any): Database {
const appId = getAppId()
if (!appId) {
throw new Error("Unable to retrieve prod DB - no app ID.")
}
return getDB(conversions.getProdAppID(appId), opts)
}
/**
* This specifically gets the dev app ID, if the request
* contained a prod app ID, this will get the dev one.
*/
export function getDevAppDB(opts?: any): Database {
const appId = getAppId()
if (!appId) {
throw new Error("Unable to retrieve dev DB - no app ID.")
}
return getDB(conversions.getDevelopmentAppID(appId), opts)
}
2023-03-27 12:10:07 +02:00
2023-03-27 16:26:07 +02:00
export function isScim(): boolean {
2023-03-27 12:10:07 +02:00
const context = Context.get()
2023-03-27 16:26:07 +02:00
const scimCall = context?.isScim
2023-03-27 12:10:07 +02:00
return !!scimCall
}
export function getCurrentContext(): ContextMap | undefined {
try {
return Context.get()
} catch (e) {
return undefined
}
}