WIP: Memory leak fix
This commit is contained in:
parent
3f5c00a404
commit
1c4473ba30
|
@ -68,6 +68,7 @@
|
|||
"@types/semver": "7.3.7",
|
||||
"@types/tar-fs": "2.0.1",
|
||||
"@types/uuid": "8.3.4",
|
||||
"@types/lodash": "4.14.180",
|
||||
"ioredis-mock": "5.8.0",
|
||||
"jest": "27.5.1",
|
||||
"koa": "2.7.0",
|
||||
|
@ -78,4 +79,4 @@
|
|||
"typescript": "4.7.3"
|
||||
},
|
||||
"gitHead": "d1836a898cab3f8ab80ee6d8f42be1a9eed7dcdc"
|
||||
}
|
||||
}
|
|
@ -1,33 +1,49 @@
|
|||
const env = require("../environment")
|
||||
const { SEPARATOR, DocumentTypes } = require("../db/constants")
|
||||
const { DEFAULT_TENANT_ID } = require("../constants")
|
||||
const cls = require("./FunctionContext")
|
||||
const { dangerousGetDB, closeDB } = require("../db")
|
||||
const { getProdAppID, getDevelopmentAppID } = require("../db/conversions")
|
||||
const { baseGlobalDBName } = require("../tenancy/utils")
|
||||
const { isEqual } = require("lodash")
|
||||
import env from "../environment"
|
||||
import { SEPARATOR, DocumentTypes } from "../db/constants"
|
||||
import cls from "./FunctionContext"
|
||||
import { dangerousGetDB, closeDB } from "../db"
|
||||
import { getProdAppID, getDevelopmentAppID } from "../db/conversions"
|
||||
import { baseGlobalDBName } from "../tenancy/utils"
|
||||
import { IdentityContext } from "@budibase/types"
|
||||
import { isEqual } from "lodash"
|
||||
import { DEFAULT_TENANT_ID as _DEFAULT_TENANT_ID } from "../constants"
|
||||
|
||||
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 = null
|
||||
let TEST_APP_ID: string | null = null
|
||||
|
||||
const ContextKeys = {
|
||||
TENANT_ID: "tenantId",
|
||||
GLOBAL_DB: "globalDb",
|
||||
APP_ID: "appId",
|
||||
IDENTITY: "identity",
|
||||
enum ContextKeys {
|
||||
TENANT_ID = "tenantId",
|
||||
GLOBAL_DB = "globalDb",
|
||||
APP_ID = "appId",
|
||||
IDENTITY = "identity",
|
||||
// whatever the request app DB was
|
||||
CURRENT_DB: "currentDb",
|
||||
CURRENT_DB = "currentDb",
|
||||
// get the prod app DB from the request
|
||||
PROD_DB: "prodDb",
|
||||
PROD_DB = "prodDb",
|
||||
// get the dev app DB from the request
|
||||
DEV_DB: "devDb",
|
||||
DB_OPTS: "dbOpts",
|
||||
DEV_DB = "devDb",
|
||||
DB_OPTS = "dbOpts",
|
||||
// check if something else is using the context, don't close DB
|
||||
IN_USE: "inUse",
|
||||
TENANCY_IN_USE = "tenancyInUse",
|
||||
APP_IN_USE = "appInUse",
|
||||
IDENTITY_IN_USE = "identityInUse",
|
||||
}
|
||||
|
||||
exports.DEFAULT_TENANT_ID = DEFAULT_TENANT_ID
|
||||
let openAppCount = 0
|
||||
let closeAppCount = 0
|
||||
let openTenancyCount = 0
|
||||
let closeTenancyCount = 0
|
||||
|
||||
setInterval(function () {
|
||||
console.log("openAppCount: " + openAppCount)
|
||||
console.log("closeAppCount: " + closeAppCount)
|
||||
console.log("openTenancyCount: " + openTenancyCount)
|
||||
console.log("closeTenancyCount: " + closeTenancyCount)
|
||||
console.log("------------------ ")
|
||||
}, 5000)
|
||||
|
||||
// this function makes sure the PouchDB objects are closed and
|
||||
// fully deleted when finished - this protects against memory leaks
|
||||
|
@ -42,6 +58,7 @@ async function closeAppDBs() {
|
|||
if (!db) {
|
||||
continue
|
||||
}
|
||||
closeAppCount++
|
||||
await closeDB(db)
|
||||
// clear the DB from context, incase someone tries to use it again
|
||||
cls.setOnContext(dbKey, null)
|
||||
|
@ -55,67 +72,29 @@ async function closeAppDBs() {
|
|||
}
|
||||
}
|
||||
|
||||
exports.closeTenancy = async () => {
|
||||
export const closeTenancy = async () => {
|
||||
closeTenancyCount++
|
||||
if (env.USE_COUCH) {
|
||||
await closeDB(exports.getGlobalDB())
|
||||
await closeDB(getGlobalDB())
|
||||
}
|
||||
// clear from context now that database is closed/task is finished
|
||||
cls.setOnContext(ContextKeys.TENANT_ID, null)
|
||||
cls.setOnContext(ContextKeys.GLOBAL_DB, null)
|
||||
}
|
||||
|
||||
exports.isDefaultTenant = () => {
|
||||
return exports.getTenantId() === exports.DEFAULT_TENANT_ID
|
||||
}
|
||||
// export const isDefaultTenant = () => {
|
||||
// return getTenantId() === DEFAULT_TENANT_ID
|
||||
// }
|
||||
|
||||
exports.isMultiTenant = () => {
|
||||
export const isMultiTenant = () => {
|
||||
return env.MULTI_TENANCY
|
||||
}
|
||||
|
||||
// used for automations, API endpoints should always be in context already
|
||||
exports.doInTenant = (tenantId, task, { forceNew } = {}) => {
|
||||
// the internal function is so that we can re-use an existing
|
||||
// context - don't want to close DB on a parent context
|
||||
async function internal(opts = { existing: false }) {
|
||||
// set the tenant id
|
||||
if (!opts.existing) {
|
||||
exports.updateTenantId(tenantId)
|
||||
}
|
||||
|
||||
try {
|
||||
// invoke the task
|
||||
return await task()
|
||||
} finally {
|
||||
const using = cls.getFromContext(ContextKeys.IN_USE)
|
||||
if (!using || using <= 1) {
|
||||
await exports.closeTenancy()
|
||||
} else {
|
||||
cls.setOnContext(using - 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const using = cls.getFromContext(ContextKeys.IN_USE)
|
||||
if (
|
||||
!forceNew &&
|
||||
using &&
|
||||
cls.getFromContext(ContextKeys.TENANT_ID) === tenantId
|
||||
) {
|
||||
cls.setOnContext(ContextKeys.IN_USE, using + 1)
|
||||
return internal({ existing: true })
|
||||
} else {
|
||||
return cls.run(async () => {
|
||||
cls.setOnContext(ContextKeys.IN_USE, 1)
|
||||
return internal()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
exports.getTenantIDFromAppID = appId => {
|
||||
export const getTenantIDFromAppID = (appId: string) => {
|
||||
if (!appId) {
|
||||
return null
|
||||
}
|
||||
|
@ -131,18 +110,54 @@ exports.getTenantIDFromAppID = appId => {
|
|||
}
|
||||
}
|
||||
|
||||
const setAppTenantId = appId => {
|
||||
const appTenantId =
|
||||
exports.getTenantIDFromAppID(appId) || exports.DEFAULT_TENANT_ID
|
||||
exports.updateTenantId(appTenantId)
|
||||
const setAppTenantId = (appId: string) => {
|
||||
const appTenantId = getTenantIDFromAppID(appId) || DEFAULT_TENANT_ID
|
||||
updateTenantId(appTenantId)
|
||||
}
|
||||
|
||||
exports.doInAppContext = (appId, task, { forceNew } = {}) => {
|
||||
// used for automations, API endpoints should always be in context already
|
||||
export const doInTenant = (tenantId: string | null, task: any) => {
|
||||
// the internal function is so that we can re-use an existing
|
||||
// context - don't want to close DB on a parent context
|
||||
async function internal(opts = { existing: false }) {
|
||||
// set the tenant id + global db if this is a new context
|
||||
if (!opts.existing) {
|
||||
updateTenantId(tenantId)
|
||||
}
|
||||
|
||||
try {
|
||||
// invoke the task
|
||||
return await task()
|
||||
} finally {
|
||||
const using = cls.getFromContext(ContextKeys.TENANCY_IN_USE)
|
||||
if (!using || using <= 1) {
|
||||
await closeTenancy()
|
||||
} else {
|
||||
cls.setOnContext(using - 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const using = cls.getFromContext(ContextKeys.TENANCY_IN_USE)
|
||||
if (using && cls.getFromContext(ContextKeys.TENANT_ID) === tenantId) {
|
||||
// the tenant id of the current context matches the one we want to use
|
||||
// don't create a new context, just use the existing one
|
||||
cls.setOnContext(ContextKeys.TENANCY_IN_USE, using + 1)
|
||||
return internal({ existing: true })
|
||||
} else {
|
||||
return cls.run(async () => {
|
||||
cls.setOnContext(ContextKeys.TENANCY_IN_USE, 1)
|
||||
return internal()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export const doInAppContext = (appId: string, task: any) => {
|
||||
if (!appId) {
|
||||
throw new Error("appId is required")
|
||||
}
|
||||
|
||||
const identity = exports.getIdentity()
|
||||
const identity = getIdentity()
|
||||
|
||||
// the internal function is so that we can re-use an existing
|
||||
// context - don't want to close DB on a parent context
|
||||
|
@ -153,33 +168,38 @@ exports.doInAppContext = (appId, task, { forceNew } = {}) => {
|
|||
}
|
||||
// set the app ID
|
||||
cls.setOnContext(ContextKeys.APP_ID, appId)
|
||||
setAppTenantId(appId)
|
||||
|
||||
// preserve the identity
|
||||
exports.setIdentity(identity)
|
||||
if (identity) {
|
||||
setIdentity(identity)
|
||||
}
|
||||
try {
|
||||
// invoke the task
|
||||
return await task()
|
||||
} finally {
|
||||
const using = cls.getFromContext(ContextKeys.IN_USE)
|
||||
const using = cls.getFromContext(ContextKeys.APP_IN_USE)
|
||||
if (!using || using <= 1) {
|
||||
await closeAppDBs()
|
||||
await closeTenancy()
|
||||
} else {
|
||||
cls.setOnContext(using - 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
const using = cls.getFromContext(ContextKeys.IN_USE)
|
||||
if (!forceNew && using && cls.getFromContext(ContextKeys.APP_ID) === appId) {
|
||||
cls.setOnContext(ContextKeys.IN_USE, using + 1)
|
||||
const using = cls.getFromContext(ContextKeys.APP_IN_USE)
|
||||
if (using && cls.getFromContext(ContextKeys.APP_ID) === appId) {
|
||||
cls.setOnContext(ContextKeys.APP_IN_USE, using + 1)
|
||||
return internal({ existing: true })
|
||||
} else {
|
||||
return cls.run(async () => {
|
||||
cls.setOnContext(ContextKeys.IN_USE, 1)
|
||||
cls.setOnContext(ContextKeys.APP_IN_USE, 1)
|
||||
return internal()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
exports.doInIdentityContext = (identity, task) => {
|
||||
export const doInIdentityContext = (identity: IdentityContext, task: any) => {
|
||||
if (!identity) {
|
||||
throw new Error("identity is required")
|
||||
}
|
||||
|
@ -189,7 +209,7 @@ exports.doInIdentityContext = (identity, task) => {
|
|||
cls.setOnContext(ContextKeys.IDENTITY, identity)
|
||||
// set the tenant so that doInTenant will preserve identity
|
||||
if (identity.tenantId) {
|
||||
exports.updateTenantId(identity.tenantId)
|
||||
updateTenantId(identity.tenantId)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -197,9 +217,10 @@ exports.doInIdentityContext = (identity, task) => {
|
|||
// invoke the task
|
||||
return await task()
|
||||
} finally {
|
||||
const using = cls.getFromContext(ContextKeys.IN_USE)
|
||||
const using = cls.getFromContext(ContextKeys.IDENTITY_IN_USE)
|
||||
if (!using || using <= 1) {
|
||||
exports.setIdentity(null)
|
||||
setIdentity(null)
|
||||
await closeTenancy()
|
||||
} else {
|
||||
cls.setOnContext(using - 1)
|
||||
}
|
||||
|
@ -207,23 +228,23 @@ exports.doInIdentityContext = (identity, task) => {
|
|||
}
|
||||
|
||||
const existing = cls.getFromContext(ContextKeys.IDENTITY)
|
||||
const using = cls.getFromContext(ContextKeys.IN_USE)
|
||||
const using = cls.getFromContext(ContextKeys.IDENTITY_IN_USE)
|
||||
if (using && existing && existing._id === identity._id) {
|
||||
cls.setOnContext(ContextKeys.IN_USE, using + 1)
|
||||
cls.setOnContext(ContextKeys.IDENTITY_IN_USE, using + 1)
|
||||
return internal({ existing: true })
|
||||
} else {
|
||||
return cls.run(async () => {
|
||||
cls.setOnContext(ContextKeys.IN_USE, 1)
|
||||
cls.setOnContext(ContextKeys.IDENTITY_IN_USE, 1)
|
||||
return internal({ existing: false })
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
exports.setIdentity = identity => {
|
||||
const setIdentity = (identity: IdentityContext | null) => {
|
||||
cls.setOnContext(ContextKeys.IDENTITY, identity)
|
||||
}
|
||||
|
||||
exports.getIdentity = () => {
|
||||
export const getIdentity = (): IdentityContext | undefined => {
|
||||
try {
|
||||
return cls.getFromContext(ContextKeys.IDENTITY)
|
||||
} catch (e) {
|
||||
|
@ -231,14 +252,14 @@ exports.getIdentity = () => {
|
|||
}
|
||||
}
|
||||
|
||||
exports.updateTenantId = tenantId => {
|
||||
export const updateTenantId = (tenantId: string | null) => {
|
||||
cls.setOnContext(ContextKeys.TENANT_ID, tenantId)
|
||||
if (env.USE_COUCH) {
|
||||
exports.setGlobalDB(tenantId)
|
||||
setGlobalDB(tenantId)
|
||||
}
|
||||
}
|
||||
|
||||
exports.updateAppId = async appId => {
|
||||
export const updateAppId = async (appId: string) => {
|
||||
try {
|
||||
// have to close first, before removing the databases from context
|
||||
await closeAppDBs()
|
||||
|
@ -252,14 +273,15 @@ exports.updateAppId = async appId => {
|
|||
}
|
||||
}
|
||||
|
||||
exports.setGlobalDB = tenantId => {
|
||||
export const setGlobalDB = (tenantId: string | null) => {
|
||||
const dbName = baseGlobalDBName(tenantId)
|
||||
openTenancyCount++
|
||||
const db = dangerousGetDB(dbName)
|
||||
cls.setOnContext(ContextKeys.GLOBAL_DB, db)
|
||||
return db
|
||||
}
|
||||
|
||||
exports.getGlobalDB = () => {
|
||||
export const getGlobalDB = () => {
|
||||
const db = cls.getFromContext(ContextKeys.GLOBAL_DB)
|
||||
if (!db) {
|
||||
throw new Error("Global DB not found")
|
||||
|
@ -267,14 +289,14 @@ exports.getGlobalDB = () => {
|
|||
return db
|
||||
}
|
||||
|
||||
exports.isTenantIdSet = () => {
|
||||
export const isTenantIdSet = () => {
|
||||
const tenantId = cls.getFromContext(ContextKeys.TENANT_ID)
|
||||
return !!tenantId
|
||||
}
|
||||
|
||||
exports.getTenantId = () => {
|
||||
if (!exports.isMultiTenant()) {
|
||||
return exports.DEFAULT_TENANT_ID
|
||||
export const getTenantId = () => {
|
||||
if (!isMultiTenant()) {
|
||||
return DEFAULT_TENANT_ID
|
||||
}
|
||||
const tenantId = cls.getFromContext(ContextKeys.TENANT_ID)
|
||||
if (!tenantId) {
|
||||
|
@ -283,7 +305,7 @@ exports.getTenantId = () => {
|
|||
return tenantId
|
||||
}
|
||||
|
||||
exports.getAppId = () => {
|
||||
export const getAppId = () => {
|
||||
const foundId = cls.getFromContext(ContextKeys.APP_ID)
|
||||
if (!foundId && env.isTest() && TEST_APP_ID) {
|
||||
return TEST_APP_ID
|
||||
|
@ -292,7 +314,7 @@ exports.getAppId = () => {
|
|||
}
|
||||
}
|
||||
|
||||
function getContextDB(key, opts) {
|
||||
function getContextDB(key: string, opts: any) {
|
||||
const dbOptsKey = `${key}${ContextKeys.DB_OPTS}`
|
||||
let storedOpts = cls.getFromContext(dbOptsKey)
|
||||
let db = cls.getFromContext(key)
|
||||
|
@ -300,7 +322,7 @@ function getContextDB(key, opts) {
|
|||
return db
|
||||
}
|
||||
|
||||
const appId = exports.getAppId()
|
||||
const appId = getAppId()
|
||||
let toUseAppId
|
||||
|
||||
switch (key) {
|
||||
|
@ -314,6 +336,7 @@ function getContextDB(key, opts) {
|
|||
toUseAppId = getDevelopmentAppID(appId)
|
||||
break
|
||||
}
|
||||
openAppCount++
|
||||
db = dangerousGetDB(toUseAppId, opts)
|
||||
try {
|
||||
cls.setOnContext(key, db)
|
||||
|
@ -332,7 +355,7 @@ function getContextDB(key, opts) {
|
|||
* Opens the app database based on whatever the request
|
||||
* contained, dev or prod.
|
||||
*/
|
||||
exports.getAppDB = (opts = null) => {
|
||||
export const getAppDB = (opts?: any) => {
|
||||
return getContextDB(ContextKeys.CURRENT_DB, opts)
|
||||
}
|
||||
|
||||
|
@ -340,7 +363,7 @@ exports.getAppDB = (opts = null) => {
|
|||
* This specifically gets the prod app ID, if the request
|
||||
* contained a development app ID, this will open the prod one.
|
||||
*/
|
||||
exports.getProdAppDB = (opts = null) => {
|
||||
export const getProdAppDB = (opts?: any) => {
|
||||
return getContextDB(ContextKeys.PROD_DB, opts)
|
||||
}
|
||||
|
||||
|
@ -348,6 +371,6 @@ exports.getProdAppDB = (opts = null) => {
|
|||
* This specifically gets the dev app ID, if the request
|
||||
* contained a prod app ID, this will open the dev one.
|
||||
*/
|
||||
exports.getDevAppDB = (opts = null) => {
|
||||
export const getDevAppDB = (opts?: any) => {
|
||||
return getContextDB(ContextKeys.DEV_DB, opts)
|
||||
}
|
|
@ -0,0 +1,148 @@
|
|||
import "../../../tests/utilities/TestConfiguration"
|
||||
import * as context from ".."
|
||||
import { DEFAULT_TENANT_ID } from "../../constants"
|
||||
import env from "../../environment"
|
||||
|
||||
// must use require to spy index file exports due to known issue in jest
|
||||
const dbUtils = require("../../db")
|
||||
jest.spyOn(dbUtils, "closeDB")
|
||||
jest.spyOn(dbUtils, "dangerousGetDB")
|
||||
|
||||
describe("context", () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
describe("doInTenant", () => {
|
||||
describe("single-tenancy", () => {
|
||||
it("defaults to the default tenant", () => {
|
||||
const tenantId = context.getTenantId()
|
||||
expect(tenantId).toBe(DEFAULT_TENANT_ID)
|
||||
})
|
||||
|
||||
it("defaults to the default tenant db", async () => {
|
||||
await context.doInTenant(DEFAULT_TENANT_ID, () => {
|
||||
const db = context.getGlobalDB()
|
||||
expect(db.name).toBe("global-db")
|
||||
})
|
||||
expect(dbUtils.dangerousGetDB).toHaveBeenCalledTimes(1)
|
||||
expect(dbUtils.closeDB).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
})
|
||||
|
||||
describe("multi-tenancy", () => {
|
||||
beforeEach(() => {
|
||||
env._set("MULTI_TENANCY", 1)
|
||||
})
|
||||
|
||||
it("fails when no tenant id is set", () => {
|
||||
const test = () => {
|
||||
let error
|
||||
try {
|
||||
context.getTenantId()
|
||||
} catch (e: any) {
|
||||
error = e
|
||||
}
|
||||
expect(error.message).toBe("Tenant id not found")
|
||||
}
|
||||
|
||||
// test under no tenancy
|
||||
test()
|
||||
|
||||
// test after tenancy has been accessed to ensure cleanup
|
||||
context.doInTenant("test", () => {})
|
||||
test()
|
||||
})
|
||||
|
||||
it("fails when no tenant db is set", () => {
|
||||
const test = () => {
|
||||
let error
|
||||
try {
|
||||
context.getGlobalDB()
|
||||
} catch (e: any) {
|
||||
error = e
|
||||
}
|
||||
expect(error.message).toBe("Global DB not found")
|
||||
}
|
||||
|
||||
// test under no tenancy
|
||||
test()
|
||||
|
||||
// test after tenancy has been accessed to ensure cleanup
|
||||
context.doInTenant("test", () => {})
|
||||
test()
|
||||
})
|
||||
|
||||
it("sets tenant id", () => {
|
||||
context.doInTenant("test", () => {
|
||||
const tenantId = context.getTenantId()
|
||||
expect(tenantId).toBe("test")
|
||||
})
|
||||
})
|
||||
|
||||
it("initialises the tenant db", async () => {
|
||||
await context.doInTenant("test", () => {
|
||||
const db = context.getGlobalDB()
|
||||
expect(db.name).toBe("test_global-db")
|
||||
})
|
||||
expect(dbUtils.dangerousGetDB).toHaveBeenCalledTimes(1)
|
||||
expect(dbUtils.closeDB).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it("sets the tenant id when nested with same tenant id", async () => {
|
||||
await context.doInTenant("test", async () => {
|
||||
const tenantId = context.getTenantId()
|
||||
expect(tenantId).toBe("test")
|
||||
|
||||
await context.doInTenant("test", async () => {
|
||||
const tenantId = context.getTenantId()
|
||||
expect(tenantId).toBe("test")
|
||||
|
||||
await context.doInTenant("test", () => {
|
||||
const tenantId = context.getTenantId()
|
||||
expect(tenantId).toBe("test")
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it("initialises the tenant db when nested with same tenant id", async () => {
|
||||
await context.doInTenant("test", async () => {
|
||||
const db = context.getGlobalDB()
|
||||
expect(db.name).toBe("test_global-db")
|
||||
|
||||
await context.doInTenant("test", async () => {
|
||||
const db = context.getGlobalDB()
|
||||
expect(db.name).toBe("test_global-db")
|
||||
|
||||
await context.doInTenant("test", () => {
|
||||
const db = context.getGlobalDB()
|
||||
expect(db.name).toBe("test_global-db")
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
// only 1 db is opened and closed
|
||||
expect(dbUtils.dangerousGetDB).toHaveBeenCalledTimes(1)
|
||||
expect(dbUtils.closeDB).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it("sets different tenant id inside another context", () => {
|
||||
context.doInTenant("test", () => {
|
||||
const tenantId = context.getTenantId()
|
||||
expect(tenantId).toBe("test")
|
||||
|
||||
context.doInTenant("nested", () => {
|
||||
const tenantId = context.getTenantId()
|
||||
expect(tenantId).toBe("nested")
|
||||
|
||||
context.doInTenant("double-nested", () => {
|
||||
const tenantId = context.getTenantId()
|
||||
expect(tenantId).toBe("double-nested")
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
|
@ -2,7 +2,7 @@ import PostHog from "posthog-node"
|
|||
import { Event, Identity, Group, BaseEvent } from "@budibase/types"
|
||||
import { EventProcessor } from "./types"
|
||||
import env from "../../environment"
|
||||
import context from "../../context"
|
||||
import * as context from "../../context"
|
||||
const pkg = require("../../../package.json")
|
||||
|
||||
export default class PosthogProcessor implements EventProcessor {
|
||||
|
|
|
@ -9,7 +9,7 @@ import {
|
|||
getGlobalDBName,
|
||||
getTenantId,
|
||||
} from "../tenancy"
|
||||
import context from "../context"
|
||||
import * as context from "../context"
|
||||
import { DEFINITIONS } from "."
|
||||
import {
|
||||
Migration,
|
||||
|
|
|
@ -764,6 +764,11 @@
|
|||
"@types/koa-compose" "*"
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/lodash@4.14.180":
|
||||
version "4.14.180"
|
||||
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.180.tgz#4ab7c9ddfc92ec4a887886483bc14c79fb380670"
|
||||
integrity sha512-XOKXa1KIxtNXgASAnwj7cnttJxS4fksBRywK/9LzRV5YxrF80BXZIGeQSuoESQ/VkUj30Ae0+YcuHc15wJCB2g==
|
||||
|
||||
"@types/mime@^1":
|
||||
version "1.3.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.2.tgz#93e25bf9ee75fe0fd80b594bc4feb0e862111b5a"
|
||||
|
|
|
@ -45,11 +45,7 @@ const { getTenantId, isMultiTenant } = require("@budibase/backend-core/tenancy")
|
|||
import { syncGlobalUsers } from "./user"
|
||||
const { app: appCache } = require("@budibase/backend-core/cache")
|
||||
import { cleanupAutomations } from "../../automations/utils"
|
||||
const {
|
||||
getAppDB,
|
||||
getProdAppDB,
|
||||
updateAppId,
|
||||
} = require("@budibase/backend-core/context")
|
||||
import { context } from "@budibase/backend-core"
|
||||
import { getUniqueRows } from "../../utilities/usageQuota/rows"
|
||||
import { quotas } from "@budibase/pro"
|
||||
import { errors, events, migrations } from "@budibase/backend-core"
|
||||
|
@ -59,7 +55,7 @@ const URL_REGEX_SLASH = /\/|\\/g
|
|||
|
||||
// utility function, need to do away with this
|
||||
async function getLayouts() {
|
||||
const db = getAppDB()
|
||||
const db = context.getAppDB()
|
||||
return (
|
||||
await db.allDocs(
|
||||
getLayoutParams(null, {
|
||||
|
@ -70,7 +66,7 @@ async function getLayouts() {
|
|||
}
|
||||
|
||||
async function getScreens() {
|
||||
const db = getAppDB()
|
||||
const db = context.getAppDB()
|
||||
return (
|
||||
await db.allDocs(
|
||||
getScreenParams(null, {
|
||||
|
@ -133,9 +129,9 @@ async function createInstance(template: any) {
|
|||
const tenantId = isMultiTenant() ? getTenantId() : null
|
||||
const baseAppId = generateAppID(tenantId)
|
||||
const appId = generateDevAppID(baseAppId)
|
||||
await updateAppId(appId)
|
||||
await context.updateAppId(appId)
|
||||
|
||||
const db = getAppDB()
|
||||
const db = context.getAppDB()
|
||||
await db.put({
|
||||
_id: "_design/database",
|
||||
// view collation information, read before writing any complex views:
|
||||
|
@ -211,7 +207,7 @@ export const fetchAppDefinition = async (ctx: any) => {
|
|||
}
|
||||
|
||||
export const fetchAppPackage = async (ctx: any) => {
|
||||
const db = getAppDB()
|
||||
const db = context.getAppDB()
|
||||
const application = await db.get(DocumentTypes.APP_METADATA)
|
||||
const layouts = await getLayouts()
|
||||
let screens = await getScreens()
|
||||
|
@ -254,7 +250,7 @@ const performAppCreate = async (ctx: any) => {
|
|||
const instance = await createInstance(instanceConfig)
|
||||
const appId = instance._id
|
||||
|
||||
const db = getAppDB()
|
||||
const db = context.getAppDB()
|
||||
let _rev
|
||||
try {
|
||||
// if template there will be an existing doc
|
||||
|
@ -382,7 +378,7 @@ export const update = async (ctx: any) => {
|
|||
|
||||
export const updateClient = async (ctx: any) => {
|
||||
// Get current app version
|
||||
const db = getAppDB()
|
||||
const db = context.getAppDB()
|
||||
const application = await db.get(DocumentTypes.APP_METADATA)
|
||||
const currentVersion = application.version
|
||||
|
||||
|
@ -406,7 +402,7 @@ export const updateClient = async (ctx: any) => {
|
|||
|
||||
export const revertClient = async (ctx: any) => {
|
||||
// Check app can be reverted
|
||||
const db = getAppDB()
|
||||
const db = context.getAppDB()
|
||||
const application = await db.get(DocumentTypes.APP_METADATA)
|
||||
if (!application.revertableVersion) {
|
||||
ctx.throw(400, "There is no version to revert to")
|
||||
|
@ -438,7 +434,7 @@ const destroyApp = async (ctx: any) => {
|
|||
appId = getProdAppID(appId)
|
||||
}
|
||||
|
||||
const db = isUnpublish ? getProdAppDB() : getAppDB()
|
||||
const db = isUnpublish ? context.getProdAppDB() : context.getAppDB()
|
||||
const app = await db.get(DocumentTypes.APP_METADATA)
|
||||
const result = await db.destroy()
|
||||
|
||||
|
@ -506,7 +502,7 @@ export const sync = async (ctx: any, next: any) => {
|
|||
|
||||
try {
|
||||
// specific case, want to make sure setup is skipped
|
||||
const prodDb = getProdAppDB({ skip_setup: true })
|
||||
const prodDb = context.getProdAppDB({ skip_setup: true })
|
||||
const info = await prodDb.info()
|
||||
if (info.error) throw info.error
|
||||
} catch (err) {
|
||||
|
@ -548,7 +544,7 @@ export const sync = async (ctx: any, next: any) => {
|
|||
}
|
||||
|
||||
const updateAppPackage = async (appPackage: any, appId: any) => {
|
||||
const db = getAppDB()
|
||||
const db = context.getAppDB()
|
||||
const application = await db.get(DocumentTypes.APP_METADATA)
|
||||
|
||||
const newAppPackage = { ...application, ...appPackage }
|
||||
|
@ -567,7 +563,7 @@ const updateAppPackage = async (appPackage: any, appId: any) => {
|
|||
}
|
||||
|
||||
const createEmptyAppPackage = async (ctx: any, app: any) => {
|
||||
const db = getAppDB()
|
||||
const db = context.getAppDB()
|
||||
|
||||
let screensAndLayouts = []
|
||||
for (let layout of BASE_LAYOUTS) {
|
||||
|
|
|
@ -95,21 +95,31 @@ class TestConfiguration {
|
|||
|
||||
// UTILS
|
||||
|
||||
async _req(config, params, controlFunc) {
|
||||
async _req(body, params, controlFunc, opts = { prodApp: false }) {
|
||||
// create a fake request ctx
|
||||
const request = {}
|
||||
|
||||
// set the app id
|
||||
let appId
|
||||
if (opts.prodApp) {
|
||||
appId = this.prodAppId
|
||||
} else {
|
||||
appId = this.appId
|
||||
}
|
||||
request.appId = appId
|
||||
|
||||
// fake cookies, we don't need them
|
||||
request.cookies = { set: () => {}, get: () => {} }
|
||||
request.config = { jwtSecret: env.JWT_SECRET }
|
||||
request.appId = this.appId
|
||||
request.user = { appId: this.appId, tenantId: TENANT_ID }
|
||||
request.user = { appId, tenantId: TENANT_ID }
|
||||
request.query = {}
|
||||
request.request = {
|
||||
body: config,
|
||||
body,
|
||||
}
|
||||
return this.doInContext(this.appId, async () => {
|
||||
if (params) {
|
||||
request.params = params
|
||||
}
|
||||
if (params) {
|
||||
request.params = params
|
||||
}
|
||||
return this.doInContext(appId, async () => {
|
||||
await controlFunc(request)
|
||||
return request.body
|
||||
})
|
||||
|
@ -304,7 +314,6 @@ class TestConfiguration {
|
|||
|
||||
// create production app
|
||||
this.prodApp = await this.deploy()
|
||||
this.prodAppId = this.prodApp.appId
|
||||
|
||||
this.allApps.push(this.prodApp)
|
||||
this.allApps.push(this.app)
|
||||
|
@ -315,11 +324,13 @@ class TestConfiguration {
|
|||
async deploy() {
|
||||
await this._req(null, null, controllers.deploy.deployApp)
|
||||
const prodAppId = this.getAppId().replace("_dev", "")
|
||||
this.prodAppId = prodAppId
|
||||
return context.doInAppContext(prodAppId, async () => {
|
||||
const appPackage = await this._req(
|
||||
null,
|
||||
{ appId: prodAppId },
|
||||
controllers.app.fetchAppPackage
|
||||
controllers.app.fetchAppPackage,
|
||||
{ prodApp: true }
|
||||
)
|
||||
return appPackage.application
|
||||
})
|
||||
|
|
|
@ -1028,10 +1028,10 @@
|
|||
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
|
||||
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
|
||||
|
||||
"@budibase/backend-core@1.0.216":
|
||||
version "1.0.216"
|
||||
resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-1.0.216.tgz#f97caefffcc5b5bfa23740178b3f7efc945ef226"
|
||||
integrity sha512-mGbevDtnyCJu/M1U3mnu8Ynxx0hMAlZg1RUX71eizvENuBRWFA7mEXlN0ay1uC0xiROllJCWI0zucYXkTxuu0w==
|
||||
"@budibase/backend-core@1.0.218":
|
||||
version "1.0.218"
|
||||
resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-1.0.218.tgz#be101c8baf220fa3fc55d63071bb38fc6c8cf4d8"
|
||||
integrity sha512-v9+bvQ2+OsK7eGyDHzMuPawTu2LovRCsuArFeMnaG/AbexkqnbB74w+h3vh/2npuHzrnk8RZkM2c4pp/ycqfKw==
|
||||
dependencies:
|
||||
"@techpass/passport-openidconnect" "0.3.2"
|
||||
aws-sdk "2.1030.0"
|
||||
|
@ -1109,12 +1109,12 @@
|
|||
svelte-flatpickr "^3.2.3"
|
||||
svelte-portal "^1.0.0"
|
||||
|
||||
"@budibase/pro@1.0.216":
|
||||
version "1.0.216"
|
||||
resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-1.0.216.tgz#bf4b2851d8bff3ada05deb0ec3e2ae3eadf998a2"
|
||||
integrity sha512-YL9fpZCMBrwpJEk86slwegGEtrX2isW77E2A0Z9ZPKQehghdEBcOp2HIZJPhKXmo0TePbW8SHblC8LnrSbaMdg==
|
||||
"@budibase/pro@1.0.218":
|
||||
version "1.0.218"
|
||||
resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-1.0.218.tgz#b9e5bb95cca996dc1f7f783a3a02e51cbbf3df55"
|
||||
integrity sha512-LJpV4rYPP9DzMNkL2Y0euZplkubBKBE+gc5JBTMt1l9Fwn2Sri/Y5bQ+U8fjczjmHxYnZLrFwH+o6LCnk/QJow==
|
||||
dependencies:
|
||||
"@budibase/backend-core" "1.0.216"
|
||||
"@budibase/backend-core" "1.0.218"
|
||||
node-fetch "^2.6.1"
|
||||
|
||||
"@budibase/standard-components@^0.9.139":
|
||||
|
|
|
@ -4,6 +4,7 @@ import { IdentityType } from "./events/identification"
|
|||
export interface BaseContext {
|
||||
_id: string
|
||||
type: IdentityType
|
||||
tenantId?: string
|
||||
}
|
||||
|
||||
export interface AccountUserContext extends BaseContext {
|
||||
|
@ -13,6 +14,7 @@ export interface AccountUserContext extends BaseContext {
|
|||
|
||||
export interface UserContext extends BaseContext, User {
|
||||
_id: string
|
||||
tenantId: string
|
||||
account?: Account
|
||||
}
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
"postbuild": "copyfiles -u 1 src/**/*.hbs dist/",
|
||||
"build:dev": "yarn prebuild && tsc --build --watch --preserveWatchOutput",
|
||||
"run:docker": "node dist/index.js",
|
||||
"debug": "yarn build && node --expose-gc --inspect=9223 dist/index.js",
|
||||
"run:docker:cluster": "pm2-runtime start pm2.config.js",
|
||||
"build:docker": "docker build . -t worker-service --label version=$BUDIBASE_RELEASE_VERSION",
|
||||
"dev:stack:init": "node ./scripts/dev/manage.js init",
|
||||
|
@ -101,4 +102,4 @@
|
|||
]
|
||||
},
|
||||
"gitHead": "d1836a898cab3f8ab80ee6d8f42be1a9eed7dcdc"
|
||||
}
|
||||
}
|
|
@ -291,10 +291,10 @@
|
|||
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
|
||||
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
|
||||
|
||||
"@budibase/backend-core@1.0.216":
|
||||
version "1.0.216"
|
||||
resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-1.0.216.tgz#f97caefffcc5b5bfa23740178b3f7efc945ef226"
|
||||
integrity sha512-mGbevDtnyCJu/M1U3mnu8Ynxx0hMAlZg1RUX71eizvENuBRWFA7mEXlN0ay1uC0xiROllJCWI0zucYXkTxuu0w==
|
||||
"@budibase/backend-core@1.0.218":
|
||||
version "1.0.218"
|
||||
resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-1.0.218.tgz#be101c8baf220fa3fc55d63071bb38fc6c8cf4d8"
|
||||
integrity sha512-v9+bvQ2+OsK7eGyDHzMuPawTu2LovRCsuArFeMnaG/AbexkqnbB74w+h3vh/2npuHzrnk8RZkM2c4pp/ycqfKw==
|
||||
dependencies:
|
||||
"@techpass/passport-openidconnect" "0.3.2"
|
||||
aws-sdk "2.1030.0"
|
||||
|
@ -322,12 +322,12 @@
|
|||
uuid "8.3.2"
|
||||
zlib "1.0.5"
|
||||
|
||||
"@budibase/pro@1.0.216":
|
||||
version "1.0.216"
|
||||
resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-1.0.216.tgz#bf4b2851d8bff3ada05deb0ec3e2ae3eadf998a2"
|
||||
integrity sha512-YL9fpZCMBrwpJEk86slwegGEtrX2isW77E2A0Z9ZPKQehghdEBcOp2HIZJPhKXmo0TePbW8SHblC8LnrSbaMdg==
|
||||
"@budibase/pro@1.0.218":
|
||||
version "1.0.218"
|
||||
resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-1.0.218.tgz#b9e5bb95cca996dc1f7f783a3a02e51cbbf3df55"
|
||||
integrity sha512-LJpV4rYPP9DzMNkL2Y0euZplkubBKBE+gc5JBTMt1l9Fwn2Sri/Y5bQ+U8fjczjmHxYnZLrFwH+o6LCnk/QJow==
|
||||
dependencies:
|
||||
"@budibase/backend-core" "1.0.216"
|
||||
"@budibase/backend-core" "1.0.218"
|
||||
node-fetch "^2.6.1"
|
||||
|
||||
"@cspotcode/source-map-consumer@0.8.0":
|
||||
|
|
Loading…
Reference in New Issue