diff --git a/packages/backend-core/src/db/utils.ts b/packages/backend-core/src/db/utils.ts index 076552beec..9920be7e55 100644 --- a/packages/backend-core/src/db/utils.ts +++ b/packages/backend-core/src/db/utils.ts @@ -26,7 +26,7 @@ export * from "./tenancy" * Generates a new app ID. * @returns {string} The new app ID which the app doc can be stored under. */ -export const generateAppID = (tenantId = null) => { +export const generateAppID = (tenantId?: string | null) => { let id = APP_PREFIX if (tenantId) { id += `${tenantId}${SEPARATOR}` @@ -251,7 +251,7 @@ export function generateRoleID(id: any) { /** * Gets parameters for retrieving a role, this is a utility function for the getDocParams function. */ -export function getRoleParams(roleId = null, otherProps = {}) { +export function getRoleParams(roleId?: string | null, otherProps = {}) { return getDocParams(DocumentType.ROLE, roleId, otherProps) } diff --git a/packages/server/src/api/controllers/application.ts b/packages/server/src/api/controllers/application.ts index b97e980a5e..dcd092d06a 100644 --- a/packages/server/src/api/controllers/application.ts +++ b/packages/server/src/api/controllers/application.ts @@ -14,17 +14,16 @@ import { DocumentType, AppStatus, } from "../../db/utils" -const { - BUILTIN_ROLE_IDS, - AccessController, -} = require("@budibase/backend-core/roles") -const { CacheKeys, bustCache } = require("@budibase/backend-core/cache") -const { - getAllApps, - isDevAppID, - getProdAppID, - Replication, -} = require("@budibase/backend-core/db") +import { + db as dbCore, + roles, + cache, + tenancy, + context, + errors, + events, + migrations, +} from "@budibase/backend-core" import { USERS_TABLE_SCHEMA } from "../../constants" import { removeAppFromUserRoles } from "../../utilities/workerRequests" import { clientLibraryPath, stringToReadStream } from "../../utilities" @@ -34,15 +33,11 @@ import { backupClientLibrary, revertClientLibrary, } from "../../utilities/fileSystem/clientLibrary" -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" -import { context } from "@budibase/backend-core" import { checkAppMetadata } from "../../automations/logging" import { getUniqueRows } from "../../utilities/usageQuota/rows" import { quotas, groups } from "@budibase/pro" -import { errors, events, migrations } from "@budibase/backend-core" import { App, Layout, Screen, MigrationType } from "@budibase/types" import { BASE_LAYOUT_PROP_IDS } from "../../constants/layouts" import { enrichPluginURLs } from "../../utilities/plugins" @@ -75,7 +70,7 @@ async function getScreens() { function getUserRoleId(ctx: any) { return !ctx.user.role || !ctx.user.role._id - ? BUILTIN_ROLE_IDS.PUBLIC + ? roles.BUILTIN_ROLE_IDS.PUBLIC : ctx.user.role._id } @@ -123,7 +118,7 @@ const checkAppName = ( } async function createInstance(template: any) { - const tenantId = isMultiTenant() ? getTenantId() : null + const tenantId = tenancy.isMultiTenant() ? tenancy.getTenantId() : null const baseAppId = generateAppID(tenantId) const appId = generateDevAppID(baseAppId) await context.updateAppId(appId) @@ -162,7 +157,7 @@ async function createInstance(template: any) { export const fetch = async (ctx: any) => { const dev = ctx.query && ctx.query.status === AppStatus.DEV const all = ctx.query && ctx.query.status === AppStatus.ALL - const apps = await getAllApps({ dev, all }) + const apps = (await dbCore.getAllApps({ dev, all })) as App[] const appIds = apps .filter((app: any) => app.status === "development") @@ -187,7 +182,7 @@ export const fetch = async (ctx: any) => { export const fetchAppDefinition = async (ctx: any) => { const layouts = await getLayouts() const userRoleId = getUserRoleId(ctx) - const accessController = new AccessController() + const accessController = new roles.AccessController() const screens = await accessController.checkScreensAccess( await getScreens(), userRoleId @@ -211,7 +206,7 @@ export const fetchAppPackage = async (ctx: any) => { // Only filter screens if the user is not a builder if (!(ctx.user.builder && ctx.user.builder.global)) { const userRoleId = getUserRoleId(ctx) - const accessController = new AccessController() + const accessController = new roles.AccessController() screens = await accessController.checkScreensAccess(screens, userRoleId) } @@ -224,7 +219,7 @@ export const fetchAppPackage = async (ctx: any) => { } const performAppCreate = async (ctx: any) => { - const apps = await getAllApps({ dev: true }) + const apps = await dbCore.getAllApps({ dev: true }) const name = ctx.request.body.name checkAppName(ctx, apps, name) const url = getAppUrl(ctx) @@ -254,7 +249,7 @@ const performAppCreate = async (ctx: any) => { url: url, template: templateKey, instance, - tenantId: getTenantId(), + tenantId: tenancy.getTenantId(), updatedAt: new Date().toISOString(), createdAt: new Date().toISOString(), status: AppStatus.DEV, @@ -313,7 +308,7 @@ const performAppCreate = async (ctx: any) => { await createApp(appId) } - await appCache.invalidateAppMetadata(appId, newApplication) + await cache.app.invalidateAppMetadata(appId, newApplication) return newApplication } @@ -343,7 +338,7 @@ const creationEvents = async (request: any, app: App) => { } const appPostCreate = async (ctx: any, app: App) => { - const tenantId = getTenantId() + const tenantId = tenancy.getTenantId() await migrations.backPopulateMigrations({ type: MigrationType.APP, tenantId, @@ -356,7 +351,9 @@ const appPostCreate = async (ctx: any, app: App) => { const rowCount = rows ? rows.length : 0 if (rowCount) { try { - await quotas.addRows(rowCount) + await context.doInAppContext(app.appId, () => { + return quotas.addRows(rowCount) + }) } catch (err: any) { if (err.code && err.code === errors.codes.USAGE_LIMIT_EXCEEDED) { // this import resulted in row usage exceeding the quota @@ -374,7 +371,7 @@ const appPostCreate = async (ctx: any, app: App) => { export const create = async (ctx: any) => { const newApplication = await quotas.addApp(() => performAppCreate(ctx)) await appPostCreate(ctx, newApplication) - await bustCache(CacheKeys.CHECKLIST) + await cache.bustCache(cache.CacheKeys.CHECKLIST) ctx.body = newApplication ctx.status = 200 } @@ -382,7 +379,7 @@ export const create = async (ctx: any) => { // This endpoint currently operates as a PATCH rather than a PUT // Thus name and url fields are handled only if present export const update = async (ctx: any) => { - const apps = await getAllApps({ dev: true }) + const apps = await dbCore.getAllApps({ dev: true }) // validation const name = ctx.request.body.name if (name) { @@ -455,7 +452,7 @@ const destroyApp = async (ctx: any) => { let isUnpublish = ctx.query && ctx.query.unpublish if (isUnpublish) { - appId = getProdAppID(appId) + appId = dbCore.getProdAppID(appId) } const db = isUnpublish ? context.getProdAppDB() : context.getAppDB() @@ -481,7 +478,7 @@ const destroyApp = async (ctx: any) => { else { await removeAppFromUserRoles(ctx, appId) } - await appCache.invalidateAppMetadata(appId) + await cache.app.invalidateAppMetadata(appId) return result } @@ -517,12 +514,12 @@ export const sync = async (ctx: any, next: any) => { } const appId = ctx.params.appId - if (!isDevAppID(appId)) { + if (!dbCore.isDevAppID(appId)) { ctx.throw(400, "This action cannot be performed for production apps") } // replicate prod to dev - const prodAppId = getProdAppID(appId) + const prodAppId = dbCore.getProdAppID(appId) // specific case, want to make sure setup is skipped const prodDb = context.getProdAppDB({ skip_setup: true }) @@ -536,7 +533,7 @@ export const sync = async (ctx: any, next: any) => { return next() } - const replication = new Replication({ + const replication = new dbCore.Replication({ source: prodAppId, target: appId, }) @@ -577,7 +574,7 @@ export const updateAppPackage = async (appPackage: any, appId: any) => { await db.put(newAppPackage) // remove any cached metadata, so that it will be updated - await appCache.invalidateAppMetadata(appId) + await cache.app.invalidateAppMetadata(appId) return newAppPackage }) } diff --git a/packages/server/src/api/controllers/public/applications.ts b/packages/server/src/api/controllers/public/applications.ts index c2c62ffc28..c4d41fe5fb 100644 --- a/packages/server/src/api/controllers/public/applications.ts +++ b/packages/server/src/api/controllers/public/applications.ts @@ -1,5 +1,4 @@ -const { getAllApps } = require("@budibase/backend-core/db") -const { doInAppContext } = require("@budibase/backend-core/context") +import { db as dbCore, context } from "@budibase/backend-core" import { search as stringSearch, addRev } from "./utils" import * as controller from "../application" import { Application } from "../../../definitions/common" @@ -15,15 +14,22 @@ function fixAppID(app: Application, params: any) { } async function setResponseApp(ctx: any) { - if (ctx.body && ctx.body.appId && (!ctx.params || !ctx.params.appId)) { - ctx.params = { appId: ctx.body.appId } + const appId = ctx.body?.appId + if (appId && (!ctx.params || !ctx.params.appId)) { + ctx.params = { appId } + } + if (appId) { + await context.doInContext(appId, () => { + return controller.fetchAppPackage(ctx) + }) + } else { + return controller.fetchAppPackage(ctx) } - await controller.fetchAppPackage(ctx) } export async function search(ctx: any, next: any) { const { name } = ctx.request.body - const apps = await getAllApps({ all: true }) + const apps = await dbCore.getAllApps({ all: true }) ctx.body = stringSearch(apps, name) await next() } @@ -41,7 +47,7 @@ export async function create(ctx: any, next: any) { } export async function read(ctx: any, next: any) { - await doInAppContext(ctx.params.appId, async () => { + await context.doInAppContext(ctx.params.appId, async () => { await setResponseApp(ctx) await next() }) @@ -49,7 +55,7 @@ export async function read(ctx: any, next: any) { export async function update(ctx: any, next: any) { ctx.request.body = await addRev(fixAppID(ctx.request.body, ctx.params)) - await doInAppContext(ctx.params.appId, async () => { + await context.doInAppContext(ctx.params.appId, async () => { await controller.update(ctx) await setResponseApp(ctx) await next() @@ -57,7 +63,7 @@ export async function update(ctx: any, next: any) { } export async function destroy(ctx: any, next: any) { - await doInAppContext(ctx.params.appId, async () => { + await context.doInAppContext(ctx.params.appId, async () => { // get the app before deleting it await setResponseApp(ctx) const body = ctx.body diff --git a/packages/types/src/documents/app/app.ts b/packages/types/src/documents/app/app.ts index a03875aa50..c91d575714 100644 --- a/packages/types/src/documents/app/app.ts +++ b/packages/types/src/documents/app/app.ts @@ -1,4 +1,4 @@ -import { Document } from "../document" +import { User, Document } from "../" export type AppMetadataErrors = { [key: string]: string[] } @@ -16,6 +16,7 @@ export interface App extends Document { theme?: string customTheme?: AppCustomTheme revertableVersion?: string + lockedBy?: User navigation?: AppNavigation automationErrors?: AppMetadataErrors icon?: AppIcon diff --git a/qa-core/src/tests/internal-api/applications/applications.spec.ts b/qa-core/src/tests/internal-api/applications/applications.spec.ts index 4b3208ee10..98895a6f28 100644 --- a/qa-core/src/tests/internal-api/applications/applications.spec.ts +++ b/qa-core/src/tests/internal-api/applications/applications.spec.ts @@ -70,7 +70,7 @@ describe("Internal API - Application creation, update, publish and delete", () = await config.applications.publish(app.url) // check published app renders - config.applications.api.appId = db.getProdAppID(app.appId) + config.applications.api.appId = db.getProdAppID(app.appId!) await config.applications.canRender() // unpublish app diff --git a/qa-core/yarn.lock b/qa-core/yarn.lock index 5b86c6084f..b090ff872f 100644 --- a/qa-core/yarn.lock +++ b/qa-core/yarn.lock @@ -390,7 +390,7 @@ jest-util "^28.1.3" slash "^3.0.0" -"@jest/core@^28.0.2", "@jest/core@^28.1.3": +"@jest/core@^28.1.1", "@jest/core@^28.1.3": version "28.1.3" resolved "https://registry.yarnpkg.com/@jest/core/-/core-28.1.3.tgz#0ebf2bd39840f1233cd5f2d1e6fc8b71bd5a1ac7" integrity sha512-CIKBrlaKOzA7YG19BEqCw3SLIsEwjZkeJzf5bdooVnW4bH5cktqe3JX+G2YV1aK5vP8N9na1IGWFzYaTp6k6NA== @@ -573,7 +573,7 @@ slash "^3.0.0" write-file-atomic "^4.0.1" -"@jest/types@^28.1.3": +"@jest/types@^28.1.1", "@jest/types@^28.1.3": version "28.1.3" resolved "https://registry.yarnpkg.com/@jest/types/-/types-28.1.3.tgz#b05de80996ff12512bc5ceb1d208285a7d11748b" integrity sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ== @@ -2299,7 +2299,7 @@ jest-circus@^28.1.3: slash "^3.0.0" stack-utils "^2.0.3" -jest-cli@^28.0.2: +jest-cli@^28.1.1: version "28.1.3" resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-28.1.3.tgz#558b33c577d06de55087b8448d373b9f654e46b2" integrity sha512-roY3kvrv57Azn1yPgdTebPAXvdR2xfezaKKYzVxZ6It/5NCxzJym6tUI5P1zkdWhfUYkxEI9uZWcQdaFLo8mJQ== @@ -2666,14 +2666,15 @@ jest-worker@^28.1.3: merge-stream "^2.0.0" supports-color "^8.0.0" -jest@28.0.2: - version "28.0.2" - resolved "https://registry.yarnpkg.com/jest/-/jest-28.0.2.tgz#41385ca21d009708bb9fc65e08663110e08e2cc0" - integrity sha512-COUtjybolW4koQvO7kCfq5kgbeeU5WbSJfVZprz4zbS8AL32+RAZZTUjBEyRRdpsXqss/pHIvSL7/P+LyMYHXg== +jest@28.1.1: + version "28.1.1" + resolved "https://registry.yarnpkg.com/jest/-/jest-28.1.1.tgz#3c39a3a09791e16e9ef283597d24ab19a0df701e" + integrity sha512-qw9YHBnjt6TCbIDMPMpJZqf9E12rh6869iZaN08/vpOGgHJSAaLLUn6H8W3IAEuy34Ls3rct064mZLETkxJ2XA== dependencies: - "@jest/core" "^28.0.2" + "@jest/core" "^28.1.1" + "@jest/types" "^28.1.1" import-local "^3.0.2" - jest-cli "^28.0.2" + jest-cli "^28.1.1" jmespath@0.15.0: version "0.15.0" @@ -4351,10 +4352,10 @@ ts-jest@28.0.8: semver "7.x" yargs-parser "^21.0.1" -ts-node@10.9.1: - version "10.9.1" - resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.1.tgz#e73de9102958af9e1f0b168a6ff320e25adcff4b" - integrity sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw== +ts-node@10.8.1: + version "10.8.1" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.8.1.tgz#ea2bd3459011b52699d7e88daa55a45a1af4f066" + integrity sha512-Wwsnao4DQoJsN034wePSg5nZiw4YKXf56mPIAeD6wVmiv+RytNSWqc2f3fKvcUoV+Yn2+yocD71VOfQHbmVX4g== dependencies: "@cspotcode/source-map-support" "^0.8.0" "@tsconfig/node10" "^1.0.7" @@ -4370,10 +4371,10 @@ ts-node@10.9.1: v8-compile-cache-lib "^3.0.1" yn "3.1.1" -tsconfig-paths@4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-4.1.0.tgz#f8ef7d467f08ae3a695335bf1ece088c5538d2c1" - integrity sha512-AHx4Euop/dXFC+Vx589alFba8QItjF+8hf8LtmuiCwHyI4rHXQtOOENaM8kvYf5fR0dRChy3wzWIZ9WbB7FWow== +tsconfig-paths@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-4.0.0.tgz#1082f5d99fd127b72397eef4809e4dd06d229b64" + integrity sha512-SLBg2GBKlR6bVtMgJJlud/o3waplKtL7skmLkExomIiaAtLGtVsoXIqP3SYdjbcH9lq/KVv7pMZeCBpLYOit6Q== dependencies: json5 "^2.2.1" minimist "^1.2.6"