Typescript conversions - trying to get all of context/db layer into TS.

This commit is contained in:
mike12345567 2022-11-11 11:57:50 +00:00
parent c63c3b48c5
commit bc94f20794
9 changed files with 85 additions and 89 deletions

View File

@ -1,10 +1,10 @@
const cls = require("../clshooked") import cls from "../clshooked"
const { newid } = require("../hashing") import { newid } from "../hashing"
const REQUEST_ID_KEY = "requestId" const REQUEST_ID_KEY = "requestId"
const MAIN_CTX = cls.createNamespace("main") const MAIN_CTX = cls.createNamespace("main")
function getContextStorage(namespace) { function getContextStorage(namespace: any) {
if (namespace && namespace.active) { if (namespace && namespace.active) {
let contextData = namespace.active let contextData = namespace.active
delete contextData.id delete contextData.id
@ -15,7 +15,7 @@ function getContextStorage(namespace) {
} }
class FunctionContext { class FunctionContext {
static run(callback) { static run(callback: any) {
return MAIN_CTX.runAndReturn(async () => { return MAIN_CTX.runAndReturn(async () => {
const namespaceId = newid() const namespaceId = newid()
MAIN_CTX.set(REQUEST_ID_KEY, namespaceId) MAIN_CTX.set(REQUEST_ID_KEY, namespaceId)
@ -26,13 +26,13 @@ class FunctionContext {
}) })
} }
static setOnContext(key, value) { static setOnContext(key: string, value: any) {
const namespaceId = MAIN_CTX.get(REQUEST_ID_KEY) const namespaceId = MAIN_CTX.get(REQUEST_ID_KEY)
const namespace = cls.getNamespace(namespaceId) const namespace = cls.getNamespace(namespaceId)
namespace.set(key, value) namespace.set(key, value)
} }
static getFromContext(key) { static getFromContext(key: string) {
const namespaceId = MAIN_CTX.get(REQUEST_ID_KEY) const namespaceId = MAIN_CTX.get(REQUEST_ID_KEY)
const namespace = cls.getNamespace(namespaceId) const namespace = cls.getNamespace(namespaceId)
const context = getContextStorage(namespace) const context = getContextStorage(namespace)
@ -44,4 +44,4 @@ class FunctionContext {
} }
} }
module.exports = FunctionContext export = FunctionContext

View File

@ -1,16 +1,20 @@
const { getGlobalUserParams, getAllApps } = require("../db/utils") import { getGlobalUserParams, getAllApps } from "../db/utils"
const { doWithDB } = require("../db") import { doWithDB } from "../db"
const { doWithGlobalDB } = require("../tenancy") import { doWithGlobalDB } from "../tenancy"
const { StaticDatabases } = require("../db/constants") import { StaticDatabases } from "../db/constants"
import { PouchLike } from "../couch"
import { User } from "@budibase/types"
const TENANT_DOC = StaticDatabases.PLATFORM_INFO.docs.tenants const TENANT_DOC = StaticDatabases.PLATFORM_INFO.docs.tenants
const PLATFORM_INFO_DB = StaticDatabases.PLATFORM_INFO.name const PLATFORM_INFO_DB = StaticDatabases.PLATFORM_INFO.name
const removeTenantFromInfoDB = async tenantId => { async function removeTenantFromInfoDB(tenantId: string) {
try { try {
await doWithDB(PLATFORM_INFO_DB, async infoDb => { await doWithDB(PLATFORM_INFO_DB, async (infoDb: PouchLike) => {
let tenants = await infoDb.get(TENANT_DOC) let tenants = await infoDb.get(TENANT_DOC)
tenants.tenantIds = tenants.tenantIds.filter(id => id !== tenantId) tenants.tenantIds = tenants.tenantIds.filter(
(id: string) => id !== tenantId
)
await infoDb.put(tenants) await infoDb.put(tenants)
}) })
@ -20,32 +24,15 @@ const removeTenantFromInfoDB = async tenantId => {
} }
} }
exports.removeUserFromInfoDB = async dbUser => { async function removeUsersFromInfoDB(tenantId: string) {
await doWithDB(PLATFORM_INFO_DB, async infoDb => { return doWithGlobalDB(tenantId, async (db: PouchLike) => {
const keys = [dbUser._id, dbUser.email]
const userDocs = await infoDb.allDocs({
keys,
include_docs: true,
})
const toDelete = userDocs.rows.map(row => {
return {
...row.doc,
_deleted: true,
}
})
await infoDb.bulkDocs(toDelete)
})
}
const removeUsersFromInfoDB = async tenantId => {
return doWithGlobalDB(tenantId, async db => {
try { try {
const allUsers = await db.allDocs( const allUsers = await db.allDocs(
getGlobalUserParams(null, { getGlobalUserParams(null, {
include_docs: true, include_docs: true,
}) })
) )
await doWithDB(PLATFORM_INFO_DB, async infoDb => { await doWithDB(PLATFORM_INFO_DB, async (infoDb: PouchLike) => {
const allEmails = allUsers.rows.map(row => row.doc.email) const allEmails = allUsers.rows.map(row => row.doc.email)
// get the id docs // get the id docs
let keys = allUsers.rows.map(row => row.id) let keys = allUsers.rows.map(row => row.id)
@ -71,8 +58,8 @@ const removeUsersFromInfoDB = async tenantId => {
}) })
} }
const removeGlobalDB = async tenantId => { async function removeGlobalDB(tenantId: string) {
return doWithGlobalDB(tenantId, async db => { return doWithGlobalDB(tenantId, async (db: PouchLike) => {
try { try {
await db.destroy() await db.destroy()
} catch (err) { } catch (err) {
@ -82,11 +69,11 @@ const removeGlobalDB = async tenantId => {
}) })
} }
const removeTenantApps = async tenantId => { async function removeTenantApps(tenantId: string) {
try { try {
const apps = await getAllApps({ all: true }) const apps = await getAllApps({ all: true })
const destroyPromises = apps.map(app => const destroyPromises = apps.map(app =>
doWithDB(app.appId, db => db.destroy()) doWithDB(app.appId, (db: PouchLike) => db.destroy())
) )
await Promise.allSettled(destroyPromises) await Promise.allSettled(destroyPromises)
} catch (err) { } catch (err) {
@ -95,8 +82,25 @@ const removeTenantApps = async tenantId => {
} }
} }
export async function removeUserFromInfoDB(dbUser: User) {
await doWithDB(PLATFORM_INFO_DB, async (infoDb: PouchLike) => {
const keys = [dbUser._id!, dbUser.email]
const userDocs = await infoDb.allDocs({
keys,
include_docs: true,
})
const toDelete = userDocs.rows.map(row => {
return {
...row.doc,
_deleted: true,
}
})
await infoDb.bulkDocs(toDelete)
})
}
// can't live in tenancy package due to circular dependency on db/utils // can't live in tenancy package due to circular dependency on db/utils
exports.deleteTenant = async tenantId => { export async function deleteTenant(tenantId: string) {
await removeTenantFromInfoDB(tenantId) await removeTenantFromInfoDB(tenantId)
await removeUsersFromInfoDB(tenantId) await removeUsersFromInfoDB(tenantId)
await removeGlobalDB(tenantId) await removeGlobalDB(tenantId)

View File

@ -203,6 +203,9 @@ export function getAppDB(opts?: any): PouchLike {
*/ */
export function getProdAppDB(opts?: any): PouchLike { export function getProdAppDB(opts?: any): PouchLike {
const appId = getAppId() const appId = getAppId()
if (!appId) {
throw new Error("Unable to retrieve prod DB - no app ID.")
}
return new PouchLike(getProdAppID(appId), opts) return new PouchLike(getProdAppID(appId), opts)
} }
@ -212,5 +215,8 @@ export function getProdAppDB(opts?: any): PouchLike {
*/ */
export function getDevAppDB(opts?: any): PouchLike { export function getDevAppDB(opts?: any): PouchLike {
const appId = getAppId() const appId = getAppId()
if (!appId) {
throw new Error("Unable to retrieve dev DB - no app ID.")
}
return new PouchLike(getDevelopmentAppID(appId), opts) return new PouchLike(getDevelopmentAppID(appId), opts)
} }

View File

@ -1,32 +1,33 @@
import { APP_DEV_PREFIX, APP_PREFIX } from "./constants"
import { App } from "@budibase/types"
const NO_APP_ERROR = "No app provided" const NO_APP_ERROR = "No app provided"
const { APP_DEV_PREFIX, APP_PREFIX } = require("./constants")
exports.isDevAppID = appId => { export function isDevAppID(appId?: string) {
if (!appId) { if (!appId) {
throw NO_APP_ERROR throw NO_APP_ERROR
} }
return appId.startsWith(APP_DEV_PREFIX) return appId.startsWith(APP_DEV_PREFIX)
} }
exports.isProdAppID = appId => { export function isProdAppID(appId?: string) {
if (!appId) { if (!appId) {
throw NO_APP_ERROR throw NO_APP_ERROR
} }
return appId.startsWith(APP_PREFIX) && !exports.isDevAppID(appId) return appId.startsWith(APP_PREFIX) && !isDevAppID(appId)
} }
exports.isDevApp = app => { export function isDevApp(app: App) {
if (!app) { if (!app) {
throw NO_APP_ERROR throw NO_APP_ERROR
} }
return exports.isDevAppID(app.appId) return isDevAppID(app.appId)
} }
/** /**
* Generates a development app ID from a real app ID. * Generates a development app ID from a real app ID.
* @returns {string} the dev app ID which can be used for dev database. * @returns {string} the dev app ID which can be used for dev database.
*/ */
exports.getDevelopmentAppID = appId => { export function getDevelopmentAppID(appId: string) {
if (!appId || appId.startsWith(APP_DEV_PREFIX)) { if (!appId || appId.startsWith(APP_DEV_PREFIX)) {
return appId return appId
} }
@ -36,12 +37,12 @@ exports.getDevelopmentAppID = appId => {
const rest = split.join(APP_PREFIX) const rest = split.join(APP_PREFIX)
return `${APP_DEV_PREFIX}${rest}` return `${APP_DEV_PREFIX}${rest}`
} }
exports.getDevAppID = exports.getDevelopmentAppID export const getDevAppID = getDevelopmentAppID
/** /**
* Convert a development app ID to a deployed app ID. * Convert a development app ID to a deployed app ID.
*/ */
exports.getProdAppID = appId => { export function getProdAppID(appId: string) {
if (!appId || !appId.startsWith(APP_DEV_PREFIX)) { if (!appId || !appId.startsWith(APP_DEV_PREFIX)) {
return appId return appId
} }
@ -52,7 +53,7 @@ exports.getProdAppID = appId => {
return `${APP_PREFIX}${rest}` return `${APP_PREFIX}${rest}`
} }
exports.extractAppUUID = id => { export function extractAppUUID(id: string) {
const split = id?.split("_") || [] const split = id?.split("_") || []
return split.length ? split[split.length - 1] : null return split.length ? split[split.length - 1] : null
} }

View File

@ -11,7 +11,7 @@ import env from "./environment"
import tenancy from "./tenancy" import tenancy from "./tenancy"
import featureFlags from "./featureFlags" import featureFlags from "./featureFlags"
import * as sessions from "./security/sessions" import * as sessions from "./security/sessions"
import deprovisioning from "./context/deprovision" import * as deprovisioning from "./context/deprovision"
import auth from "./auth" import auth from "./auth"
import constants from "./constants" import constants from "./constants"
import * as dbConstants from "./db/constants" import * as dbConstants from "./db/constants"

View File

@ -68,7 +68,7 @@ export const getGlobalUserByAppPage = (appId: string, user: User) => {
if (!user) { if (!user) {
return return
} }
return generateAppUserID(getProdAppID(appId), user._id!) return generateAppUserID(getProdAppID(appId)!, user._id!)
} }
/** /**

View File

@ -1,23 +1,11 @@
import Deployment from "./Deployment" import Deployment from "./Deployment"
import { import { context, db as dbCore, events, cache } from "@budibase/backend-core"
getDevelopmentAppID,
getProdAppID,
Replication,
} from "@budibase/backend-core/db"
import { DocumentType, getAutomationParams } from "../../../db/utils" import { DocumentType, getAutomationParams } from "../../../db/utils"
import { import {
clearMetadata, clearMetadata,
disableAllCrons, disableAllCrons,
enableCronTrigger, enableCronTrigger,
} from "../../../automations/utils" } from "../../../automations/utils"
import { app as appCache } from "@budibase/backend-core/cache"
import {
getAppDB,
getAppId,
getDevAppDB,
getProdAppDB,
} from "@budibase/backend-core/context"
import { events } from "@budibase/backend-core"
import { backups } from "@budibase/pro" import { backups } from "@budibase/pro"
import { AppBackupTrigger } from "@budibase/types" import { AppBackupTrigger } from "@budibase/types"
@ -49,7 +37,7 @@ async function checkAllDeployments(deployments: any) {
async function storeDeploymentHistory(deployment: any) { async function storeDeploymentHistory(deployment: any) {
const deploymentJSON = deployment.getJSON() const deploymentJSON = deployment.getJSON()
const db = getAppDB() const db = context.getAppDB()
let deploymentDoc let deploymentDoc
try { try {
@ -77,7 +65,7 @@ async function storeDeploymentHistory(deployment: any) {
} }
async function initDeployedApp(prodAppId: any) { async function initDeployedApp(prodAppId: any) {
const db = getProdAppDB() const db = context.getProdAppDB()
console.log("Reading automation docs") console.log("Reading automation docs")
const automations = ( const automations = (
await db.allDocs( await db.allDocs(
@ -103,9 +91,9 @@ async function initDeployedApp(prodAppId: any) {
async function deployApp(deployment: any, userId: string) { async function deployApp(deployment: any, userId: string) {
let replication let replication
try { try {
const appId = getAppId() const appId = context.getAppId()!
const devAppId = getDevelopmentAppID(appId) const devAppId = dbCore.getDevelopmentAppID(appId)
const productionAppId = getProdAppID(appId) const productionAppId = dbCore.getProdAppID(appId)
// don't try this if feature isn't allowed, will error // don't try this if feature isn't allowed, will error
if (await backups.isEnabled()) { if (await backups.isEnabled()) {
@ -122,8 +110,8 @@ async function deployApp(deployment: any, userId: string) {
source: devAppId, source: devAppId,
target: productionAppId, target: productionAppId,
} }
replication = new Replication(config) replication = new dbCore.Replication(config)
const devDb = getDevAppDB() const devDb = context.getDevAppDB()
console.log("Compacting development DB") console.log("Compacting development DB")
await devDb.compact() await devDb.compact()
console.log("Replication object created") console.log("Replication object created")
@ -131,7 +119,7 @@ async function deployApp(deployment: any, userId: string) {
console.log("replication complete.. replacing app meta doc") console.log("replication complete.. replacing app meta doc")
// app metadata is excluded as it is likely to be in conflict // app metadata is excluded as it is likely to be in conflict
// replicate the app metadata document manually // replicate the app metadata document manually
const db = getProdAppDB() const db = context.getProdAppDB()
const appDoc = await devDb.get(DocumentType.APP_METADATA) const appDoc = await devDb.get(DocumentType.APP_METADATA)
try { try {
const prodAppDoc = await db.get(DocumentType.APP_METADATA) const prodAppDoc = await db.get(DocumentType.APP_METADATA)
@ -147,7 +135,7 @@ async function deployApp(deployment: any, userId: string) {
// remove automation errors if they exist // remove automation errors if they exist
delete appDoc.automationErrors delete appDoc.automationErrors
await db.put(appDoc) await db.put(appDoc)
await appCache.invalidateAppMetadata(productionAppId) await cache.app.invalidateAppMetadata(productionAppId)
console.log("New app doc written successfully.") console.log("New app doc written successfully.")
await initDeployedApp(productionAppId) await initDeployedApp(productionAppId)
console.log("Deployed app initialised, setting deployment to successful") console.log("Deployed app initialised, setting deployment to successful")
@ -170,7 +158,7 @@ async function deployApp(deployment: any, userId: string) {
export async function fetchDeployments(ctx: any) { export async function fetchDeployments(ctx: any) {
try { try {
const db = getAppDB() const db = context.getAppDB()
const deploymentDoc = await db.get(DocumentType.DEPLOYMENTS) const deploymentDoc = await db.get(DocumentType.DEPLOYMENTS)
const { updated, deployments } = await checkAllDeployments(deploymentDoc) const { updated, deployments } = await checkAllDeployments(deploymentDoc)
if (updated) { if (updated) {
@ -184,7 +172,7 @@ export async function fetchDeployments(ctx: any) {
export async function deploymentProgress(ctx: any) { export async function deploymentProgress(ctx: any) {
try { try {
const db = getAppDB() const db = context.getAppDB()
const deploymentDoc = await db.get(DocumentType.DEPLOYMENTS) const deploymentDoc = await db.get(DocumentType.DEPLOYMENTS)
ctx.body = deploymentDoc[ctx.params.deploymentId] ctx.body = deploymentDoc[ctx.params.deploymentId]
} catch (err) { } catch (err) {
@ -197,7 +185,7 @@ export async function deploymentProgress(ctx: any) {
const isFirstDeploy = async () => { const isFirstDeploy = async () => {
try { try {
const db = getProdAppDB() const db = context.getProdAppDB()
await db.get(DocumentType.APP_METADATA) await db.get(DocumentType.APP_METADATA)
} catch (e: any) { } catch (e: any) {
if (e.status === 404) { if (e.status === 404) {

View File

@ -4,15 +4,9 @@ import { automationQueue } from "./bullboard"
import newid from "../db/newid" import newid from "../db/newid"
import { updateEntityMetadata } from "../utilities" import { updateEntityMetadata } from "../utilities"
import { MetadataTypes } from "../constants" import { MetadataTypes } from "../constants"
import { getProdAppID, doWithDB } from "@budibase/backend-core/db" import { db as dbCore, context } from "@budibase/backend-core"
import { getAutomationMetadataParams } from "../db/utils" import { getAutomationMetadataParams } from "../db/utils"
import { cloneDeep } from "lodash/fp" import { cloneDeep } from "lodash/fp"
import {
getAppDB,
getAppId,
getProdAppDB,
} from "@budibase/backend-core/context"
import { context } from "@budibase/backend-core"
import { quotas } from "@budibase/pro" import { quotas } from "@budibase/pro"
import { Automation, WebhookActionType } from "@budibase/types" import { Automation, WebhookActionType } from "@budibase/types"
import sdk from "../sdk" import sdk from "../sdk"
@ -102,7 +96,7 @@ export async function disableCronById(jobId: number | string) {
} }
export async function clearMetadata() { export async function clearMetadata() {
const db = getProdAppDB() const db = context.getProdAppDB()
const automationMetadata = ( const automationMetadata = (
await db.allDocs( await db.allDocs(
getAutomationMetadataParams({ getAutomationMetadataParams({
@ -157,7 +151,7 @@ export async function enableCronTrigger(appId: any, automation: Automation) {
// can't use getAppDB here as this is likely to be called from dev app, // can't use getAppDB here as this is likely to be called from dev app,
// but this call could be for dev app or prod app, need to just use what // but this call could be for dev app or prod app, need to just use what
// was passed in // was passed in
await doWithDB(appId, async (db: any) => { await dbCore.doWithDB(appId, async (db: any) => {
const response = await db.put(automation) const response = await db.put(automation)
automation._id = response.id automation._id = response.id
automation._rev = response.rev automation._rev = response.rev
@ -175,7 +169,10 @@ export async function enableCronTrigger(appId: any, automation: Automation) {
* written to DB (this does not write to DB as it would be wasteful to repeat). * written to DB (this does not write to DB as it would be wasteful to repeat).
*/ */
export async function checkForWebhooks({ oldAuto, newAuto }: any) { export async function checkForWebhooks({ oldAuto, newAuto }: any) {
const appId = getAppId() const appId = context.getAppId()
if (!appId) {
throw new Error("Unable to check webhooks - no app ID in context.")
}
const oldTrigger = oldAuto ? oldAuto.definition.trigger : null const oldTrigger = oldAuto ? oldAuto.definition.trigger : null
const newTrigger = newAuto ? newAuto.definition.trigger : null const newTrigger = newAuto ? newAuto.definition.trigger : null
const triggerChanged = const triggerChanged =
@ -194,7 +191,7 @@ export async function checkForWebhooks({ oldAuto, newAuto }: any) {
oldTrigger.webhookId oldTrigger.webhookId
) { ) {
try { try {
let db = getAppDB() let db = context.getAppDB()
// need to get the webhook to get the rev // need to get the webhook to get the rev
const webhook = await db.get(oldTrigger.webhookId) const webhook = await db.get(oldTrigger.webhookId)
// might be updating - reset the inputs to remove the URLs // might be updating - reset the inputs to remove the URLs
@ -224,7 +221,7 @@ export async function checkForWebhooks({ oldAuto, newAuto }: any) {
// the app ID has to be development for this endpoint // the app ID has to be development for this endpoint
// it can only be used when building the app // it can only be used when building the app
// but the trigger endpoint will always be used in production // but the trigger endpoint will always be used in production
const prodAppId = getProdAppID(appId) const prodAppId = dbCore.getProdAppID(appId)
newTrigger.inputs = { newTrigger.inputs = {
schemaUrl: `api/webhooks/schema/${appId}/${id}`, schemaUrl: `api/webhooks/schema/${appId}/${id}`,
triggerUrl: `api/webhooks/trigger/${prodAppId}/${id}`, triggerUrl: `api/webhooks/trigger/${prodAppId}/${id}`,

View File

@ -8,7 +8,7 @@ export interface RowResponse<T> {
key: string key: string
error: string error: string
value: RowValue value: RowValue
doc?: T doc?: T | any
} }
export interface AllDocsResponse<T> { export interface AllDocsResponse<T> {