From f838b72a41ce9d73ce2867bd855d81c605fc8026 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Mon, 5 May 2025 12:26:24 +0200 Subject: [PATCH 01/36] Create projectApp sdk --- .../server/src/sdk/app/projectApps/index.ts | 41 +++++++++++++++++++ packages/server/src/sdk/index.ts | 2 + packages/types/src/documents/app/index.ts | 1 + .../types/src/documents/app/projectApp.ts | 6 +++ packages/types/src/documents/app/screen.ts | 1 + packages/types/src/documents/document.ts | 1 + 6 files changed, 52 insertions(+) create mode 100644 packages/server/src/sdk/app/projectApps/index.ts create mode 100644 packages/types/src/documents/app/projectApp.ts diff --git a/packages/server/src/sdk/app/projectApps/index.ts b/packages/server/src/sdk/app/projectApps/index.ts new file mode 100644 index 0000000000..8c0218f9f1 --- /dev/null +++ b/packages/server/src/sdk/app/projectApps/index.ts @@ -0,0 +1,41 @@ +import { context, docIds, HTTPError, utils } from "@budibase/backend-core" +import { DocumentType, ProjectApp, SEPARATOR } from "@budibase/types" + +async function guardName(name: string, id?: string) { + const existingProjectApps = await fetch() + + if (existingProjectApps.find(p => p.name === name && p._id !== id)) { + throw new HTTPError(`App with name '${name}' is already taken.`, 400) + } +} + +export async function fetch(): Promise { + const db = context.getAppDB() + const docs = await db.allDocs( + docIds.getProjectAppParams(null, { include_docs: true }) + ) + const result = docs.rows.map(r => ({ + ...r.doc!, + _id: r.doc!._id!, + _rev: r.doc!._rev!, + })) + return result +} + +export async function create( + projectApp: Omit +) { + const db = context.getAppDB() + + await guardName(projectApp.name) + + const response = await db.put({ + _id: `${DocumentType.PROJECT_APP}${SEPARATOR}${utils.newid()}`, + ...projectApp, + }) + return { + _id: response.id!, + _rev: response.rev!, + ...projectApp, + } +} diff --git a/packages/server/src/sdk/index.ts b/packages/server/src/sdk/index.ts index a994e899be..447b8e4572 100644 --- a/packages/server/src/sdk/index.ts +++ b/packages/server/src/sdk/index.ts @@ -15,6 +15,7 @@ import * as screens from "./app/screens" import * as common from "./app/common" import * as oauth2 from "./app/oauth2" import * as ai from "./app/ai" +import * as projectApps from "./app/projectApps" const sdk = { backups, @@ -34,6 +35,7 @@ const sdk = { common, oauth2, ai, + projectApps, } // default export for TS diff --git a/packages/types/src/documents/app/index.ts b/packages/types/src/documents/app/index.ts index 3aafba9dd1..2e5ee05360 100644 --- a/packages/types/src/documents/app/index.ts +++ b/packages/types/src/documents/app/index.ts @@ -9,6 +9,7 @@ export * from "./layout" export * from "./links" export * from "./metadata" export * from "./oauth2" +export * from "./projectApp" export * from "./query" export * from "./role" export * from "./row" diff --git a/packages/types/src/documents/app/projectApp.ts b/packages/types/src/documents/app/projectApp.ts new file mode 100644 index 0000000000..d1261eb7aa --- /dev/null +++ b/packages/types/src/documents/app/projectApp.ts @@ -0,0 +1,6 @@ +import { Document } from "../document" + +export interface ProjectApp extends Document { + name: string + icon?: string +} diff --git a/packages/types/src/documents/app/screen.ts b/packages/types/src/documents/app/screen.ts index 29bc5a97e3..1ae03cf76c 100644 --- a/packages/types/src/documents/app/screen.ts +++ b/packages/types/src/documents/app/screen.ts @@ -29,6 +29,7 @@ export interface Screen extends Document { pluginAdded?: boolean onLoad?: EventHandler[] variant?: ScreenVariant + projectAppId?: string } export interface ScreenRoutesViewOutput extends Document { diff --git a/packages/types/src/documents/document.ts b/packages/types/src/documents/document.ts index cf868aa680..5b4b1708aa 100644 --- a/packages/types/src/documents/document.ts +++ b/packages/types/src/documents/document.ts @@ -43,6 +43,7 @@ export enum DocumentType { OAUTH2_CONFIG = "oauth2", OAUTH2_CONFIG_LOG = "oauth2log", AGENT_CHAT = "agentchat", + PROJECT_APP = "project_app", } // Because DocumentTypes can overlap, we need to make sure that we search From 0e26cb8a77d1063187d626beb3dc128f53364116 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Mon, 5 May 2025 12:26:49 +0200 Subject: [PATCH 02/36] Process projectApp migration --- packages/backend-core/src/docIds/params.ts | 10 ++++ .../server/src/api/controllers/application.ts | 47 ++++++++++++++++++- packages/types/src/api/web/app/application.ts | 4 +- 3 files changed, 58 insertions(+), 3 deletions(-) diff --git a/packages/backend-core/src/docIds/params.ts b/packages/backend-core/src/docIds/params.ts index 76d4efbde7..46cc0a924c 100644 --- a/packages/backend-core/src/docIds/params.ts +++ b/packages/backend-core/src/docIds/params.ts @@ -210,3 +210,13 @@ export const getOAuth2ConfigParams = ( ) => { return getDocParams(DocumentType.OAUTH2_CONFIG, configId, otherProps) } + +/** + * Gets parameters for retrieving project apps, this is a utility function for the getDocParams function. + */ +export const getProjectAppParams = ( + projectAppId?: string | null, + otherProps: Partial = {} +) => { + return getDocParams(DocumentType.PROJECT_APP, projectAppId, otherProps) +} diff --git a/packages/server/src/api/controllers/application.ts b/packages/server/src/api/controllers/application.ts index 20833dc321..286b063298 100644 --- a/packages/server/src/api/controllers/application.ts +++ b/packages/server/src/api/controllers/application.ts @@ -76,6 +76,7 @@ import { builderSocket } from "../../websockets" import { DefaultAppTheme, sdk as sharedCoreSDK } from "@budibase/shared-core" import * as appMigrations from "../../appMigrations" import { createSampleDataTableScreen } from "../../constants/screens" +import { groupBy } from "lodash/fp" // utility function, need to do away with this async function getLayouts() { @@ -248,6 +249,8 @@ export async function fetchAppPackage( let screens = await sdk.screens.fetch() const license = await licensing.cache.getCachedLicense() + screens = await ensureProjectApp(application, screens) + // Enrich plugin URLs application.usedPlugins = await objectStore.enrichPluginURLs( application.usedPlugins @@ -267,6 +270,8 @@ export async function fetchAppPackage( screens = await accessController.checkScreensAccess(screens, userRoleId) } + const projectApps = await extractScreensByProjectApp(screens) + const clientLibPath = objectStore.clientLibraryUrl( ctx.params.appId, application.version @@ -275,13 +280,53 @@ export async function fetchAppPackage( ctx.body = { application: { ...application, upgradableVersion: envCore.VERSION }, licenseType: license?.plan.type || PlanType.FREE, - screens, + projectApps, layouts, clientLibPath, hasLock: await doesUserHaveLock(application.appId, ctx.user), } } +async function ensureProjectApp(app: App, screens: Screen[]) { + if (screens.every(s => s.projectAppId)) { + return screens + } + + const createdProjectApp = await sdk.projectApps.create({ + name: app.name, + }) + + const db = context.getAppDB() + db.bulkDocs( + screens.map(s => ({ + ...s, + projectAppId: createdProjectApp._id, + })) + ) + + return getScreens() +} + +async function extractScreensByProjectApp( + screens: Screen[] +): Promise { + const result: FetchAppPackageResponse["projectApps"] = [] + + const projectApps = await sdk.projectApps.fetch() + + const screensByProjectApp = groupBy(s => s.projectAppId, screens) + for (const projectAppId of Object.keys(screensByProjectApp)) { + const projectApp = projectApps.find(p => p._id === projectAppId) + + result.push({ + ...projectApp!, + screens: screensByProjectApp[projectAppId], + }) + } + + return result +} + async function performAppCreate( ctx: UserCtx ) { diff --git a/packages/types/src/api/web/app/application.ts b/packages/types/src/api/web/app/application.ts index a0f53a6ba4..ada1e25b45 100644 --- a/packages/types/src/api/web/app/application.ts +++ b/packages/types/src/api/web/app/application.ts @@ -1,5 +1,5 @@ import type { PlanType } from "../../../sdk" -import type { Layout, App, Screen } from "../../../documents" +import type { Layout, App, Screen, ProjectApp } from "../../../documents" import { ReadStream } from "fs" export interface SyncAppResponse { @@ -38,7 +38,7 @@ export interface FetchAppDefinitionResponse { export interface FetchAppPackageResponse { application: App licenseType: PlanType - screens: Screen[] + projectApps: (ProjectApp & { screens: Screen[] })[] layouts: Layout[] clientLibPath: string hasLock: boolean From 31eee3863ad559e9577694ebf531a6f1e0f24ab9 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Mon, 5 May 2025 12:41:20 +0200 Subject: [PATCH 03/36] Validate projectAppId --- packages/server/src/api/routes/utils/validators.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/server/src/api/routes/utils/validators.ts b/packages/server/src/api/routes/utils/validators.ts index a8fa12ec3d..359a10a595 100644 --- a/packages/server/src/api/routes/utils/validators.ts +++ b/packages/server/src/api/routes/utils/validators.ts @@ -320,6 +320,7 @@ export function screenValidator() { }) .required() .unknown(true), + projectAppId: Joi.string().required(), }).unknown(true) ) } From 10fb9f3c6a8e9d5988354a135621b0869735daf6 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Mon, 5 May 2025 12:47:00 +0200 Subject: [PATCH 04/36] Use screen sdk --- packages/server/src/api/controllers/application.ts | 2 +- packages/server/src/sdk/app/screens/screens.ts | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/server/src/api/controllers/application.ts b/packages/server/src/api/controllers/application.ts index 286b063298..669602039c 100644 --- a/packages/server/src/api/controllers/application.ts +++ b/packages/server/src/api/controllers/application.ts @@ -304,7 +304,7 @@ async function ensureProjectApp(app: App, screens: Screen[]) { })) ) - return getScreens() + return sdk.screens.fetch() } async function extractScreensByProjectApp( diff --git a/packages/server/src/sdk/app/screens/screens.ts b/packages/server/src/sdk/app/screens/screens.ts index 49554fb28a..80498fb8d8 100644 --- a/packages/server/src/sdk/app/screens/screens.ts +++ b/packages/server/src/sdk/app/screens/screens.ts @@ -1,8 +1,10 @@ -import { getScreenParams } from "../../../db/utils" import { context } from "@budibase/backend-core" -import { Screen } from "@budibase/types" +import { Database, Screen } from "@budibase/types" +import { getScreenParams } from "../../../db/utils" -export async function fetch(db = context.getAppDB()): Promise { +export async function fetch( + db: Database = context.getAppDB() +): Promise { return ( await db.allDocs( getScreenParams(null, { From b3945423749f79cafa2dadc494725bfbf4659997 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Mon, 5 May 2025 12:54:16 +0200 Subject: [PATCH 05/36] Migrate ProjectAppId on screen sdk --- .../server/src/api/controllers/application.ts | 22 ------------ .../server/src/sdk/app/projectApps/index.ts | 15 ++++++++ .../server/src/sdk/app/screens/screens.ts | 35 +++++++++++++++++-- packages/types/src/documents/app/screen.ts | 2 +- 4 files changed, 49 insertions(+), 25 deletions(-) diff --git a/packages/server/src/api/controllers/application.ts b/packages/server/src/api/controllers/application.ts index 669602039c..417f60bfb5 100644 --- a/packages/server/src/api/controllers/application.ts +++ b/packages/server/src/api/controllers/application.ts @@ -249,8 +249,6 @@ export async function fetchAppPackage( let screens = await sdk.screens.fetch() const license = await licensing.cache.getCachedLicense() - screens = await ensureProjectApp(application, screens) - // Enrich plugin URLs application.usedPlugins = await objectStore.enrichPluginURLs( application.usedPlugins @@ -287,26 +285,6 @@ export async function fetchAppPackage( } } -async function ensureProjectApp(app: App, screens: Screen[]) { - if (screens.every(s => s.projectAppId)) { - return screens - } - - const createdProjectApp = await sdk.projectApps.create({ - name: app.name, - }) - - const db = context.getAppDB() - db.bulkDocs( - screens.map(s => ({ - ...s, - projectAppId: createdProjectApp._id, - })) - ) - - return sdk.screens.fetch() -} - async function extractScreensByProjectApp( screens: Screen[] ): Promise { diff --git a/packages/server/src/sdk/app/projectApps/index.ts b/packages/server/src/sdk/app/projectApps/index.ts index 8c0218f9f1..ebf252d2a2 100644 --- a/packages/server/src/sdk/app/projectApps/index.ts +++ b/packages/server/src/sdk/app/projectApps/index.ts @@ -39,3 +39,18 @@ export async function create( ...projectApp, } } + +export async function update( + projectApp: Omit +) { + const db = context.getAppDB() + + await guardName(projectApp.name, projectApp._id) + + const response = await db.put(projectApp) + return { + _id: response.id!, + _rev: response.rev!, + ...projectApp, + } +} diff --git a/packages/server/src/sdk/app/screens/screens.ts b/packages/server/src/sdk/app/screens/screens.ts index 80498fb8d8..0210921a87 100644 --- a/packages/server/src/sdk/app/screens/screens.ts +++ b/packages/server/src/sdk/app/screens/screens.ts @@ -1,15 +1,46 @@ import { context } from "@budibase/backend-core" -import { Database, Screen } from "@budibase/types" +import { Database, DocumentType, Screen, SEPARATOR } from "@budibase/types" import { getScreenParams } from "../../../db/utils" +import sdk from "../.." export async function fetch( db: Database = context.getAppDB() ): Promise { - return ( + const screens = ( await db.allDocs( getScreenParams(null, { include_docs: true, }) ) ).rows.map(el => el.doc!) + + if (screens.every(s => s.projectAppId)) { + return screens + } + + await migrateToProjectApp(screens) + return fetch(db) +} + +async function migrateToProjectApp(screens: Screen[]) { + const application = await sdk.applications.metadata.get() + if (screens.every(s => s.projectAppId)) { + return screens + } + + // Forcing the _id to avoid concurrency issues + const createdProjectApp = await sdk.projectApps.update({ + _id: `${DocumentType.PROJECT_APP}${SEPARATOR}default`, + name: application.name, + }) + + const db = context.getAppDB() + await db.bulkDocs( + screens.map(s => ({ + ...s, + projectAppId: createdProjectApp._id, + })) + ) + + return sdk.screens.fetch() } diff --git a/packages/types/src/documents/app/screen.ts b/packages/types/src/documents/app/screen.ts index 1ae03cf76c..79f34cac55 100644 --- a/packages/types/src/documents/app/screen.ts +++ b/packages/types/src/documents/app/screen.ts @@ -29,7 +29,7 @@ export interface Screen extends Document { pluginAdded?: boolean onLoad?: EventHandler[] variant?: ScreenVariant - projectAppId?: string + projectAppId: string } export interface ScreenRoutesViewOutput extends Document { From 8b06ddabcebbe97479bfd2902643b09f0d990e69 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Mon, 5 May 2025 13:03:24 +0200 Subject: [PATCH 06/36] Move createHomeScreen to structures --- packages/server/src/constants/screens.ts | 54 ------------------ .../server/src/tests/utilities/structures.ts | 57 ++++++++++++++++++- 2 files changed, 55 insertions(+), 56 deletions(-) diff --git a/packages/server/src/constants/screens.ts b/packages/server/src/constants/screens.ts index 3a7413633d..a506f123c0 100644 --- a/packages/server/src/constants/screens.ts +++ b/packages/server/src/constants/screens.ts @@ -1,61 +1,7 @@ -import { roles } from "@budibase/backend-core" -import { BASE_LAYOUT_PROP_IDS } from "./layouts" import { Screen, Table, Query, ViewV2, Component } from "@budibase/types" export const SAMPLE_DATA_SCREEN_NAME = "sample-data-inventory-screen" -export function createHomeScreen( - config: { - roleId: string - route: string - } = { - roleId: roles.BUILTIN_ROLE_IDS.BASIC, - route: "/", - } -): Screen { - return { - layoutId: BASE_LAYOUT_PROP_IDS.PRIVATE, - props: { - _id: "d834fea2-1b3e-4320-ab34-f9009f5ecc59", - _component: "@budibase/standard-components/container", - _styles: { - normal: {}, - hover: {}, - active: {}, - selected: {}, - }, - _transition: "fade", - _children: [ - { - _id: "ef60083f-4a02-4df3-80f3-a0d3d16847e7", - _component: "@budibase/standard-components/heading", - _styles: { - hover: {}, - active: {}, - selected: {}, - }, - text: "Welcome to your Budibase App 👋", - size: "M", - align: "left", - _instanceName: "Heading", - _children: [], - }, - ], - _instanceName: "Home", - direction: "column", - hAlign: "stretch", - vAlign: "top", - size: "grow", - gap: "M", - }, - routing: { - route: config.route, - roleId: config.roleId, - }, - name: "home-screen", - } -} - function heading(text: string): Component { return { _id: "c1bff24cd821e41d18c894ac77a80ef99", diff --git a/packages/server/src/tests/utilities/structures.ts b/packages/server/src/tests/utilities/structures.ts index 1679334cab..7ae902d7cf 100644 --- a/packages/server/src/tests/utilities/structures.ts +++ b/packages/server/src/tests/utilities/structures.ts @@ -1,6 +1,5 @@ import { roles, utils } from "@budibase/backend-core" -import { createHomeScreen } from "../../constants/screens" -import { EMPTY_LAYOUT } from "../../constants/layouts" +import { BASE_LAYOUT_PROP_IDS, EMPTY_LAYOUT } from "../../constants/layouts" import { cloneDeep } from "lodash/fp" import { BUILTIN_ACTION_DEFINITIONS, @@ -38,6 +37,7 @@ import { FilterCondition, AutomationTriggerResult, CreateEnvironmentVariableRequest, + Screen, } from "@budibase/types" import { LoopInput } from "../../definitions/automations" import { merge } from "lodash" @@ -538,6 +538,59 @@ export function basicUser(role: string) { } } +function createHomeScreen( + config: { + roleId: string + route: string + } = { + roleId: roles.BUILTIN_ROLE_IDS.BASIC, + route: "/", + } +): Screen { + return { + layoutId: BASE_LAYOUT_PROP_IDS.PRIVATE, + props: { + _id: "d834fea2-1b3e-4320-ab34-f9009f5ecc59", + _component: "@budibase/standard-components/container", + _styles: { + normal: {}, + hover: {}, + active: {}, + selected: {}, + }, + _transition: "fade", + _children: [ + { + _id: "ef60083f-4a02-4df3-80f3-a0d3d16847e7", + _component: "@budibase/standard-components/heading", + _styles: { + hover: {}, + active: {}, + selected: {}, + }, + text: "Welcome to your Budibase App 👋", + size: "M", + align: "left", + _instanceName: "Heading", + _children: [], + }, + ], + _instanceName: "Home", + direction: "column", + hAlign: "stretch", + vAlign: "top", + size: "grow", + gap: "M", + }, + routing: { + route: config.route, + roleId: config.roleId, + }, + name: "home-screen", + projectAppId: "appId", + } +} + export function basicScreen(route = "/") { return createHomeScreen({ roleId: BUILTIN_ROLE_IDS.BASIC, From 646782e38b0b37f3c20ae4e977d1c54788ef4529 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Mon, 5 May 2025 13:10:24 +0200 Subject: [PATCH 07/36] Move test code around --- .../src/api/routes/tests/screen.spec.ts | 4 +- packages/server/src/constants/screens.ts | 176 +---------------- .../server/src/tests/utilities/structures.ts | 4 +- .../src/tests/utilities/structures/screens.ts | 178 ++++++++++++++++++ 4 files changed, 182 insertions(+), 180 deletions(-) create mode 100644 packages/server/src/tests/utilities/structures/screens.ts diff --git a/packages/server/src/api/routes/tests/screen.spec.ts b/packages/server/src/api/routes/tests/screen.spec.ts index 266ed5b263..d31758aa64 100644 --- a/packages/server/src/api/routes/tests/screen.spec.ts +++ b/packages/server/src/api/routes/tests/screen.spec.ts @@ -235,9 +235,7 @@ describe("/screens", () => { viewV2.createRequest(table._id!), { status: 201 } ) - const screen = await config.api.screen.save( - createViewScreen("BudibaseDB", view) - ) + const screen = await config.api.screen.save(createViewScreen(view)) const usage = await config.api.screen.usage(view.id) expect(usage.sourceType).toEqual(SourceType.VIEW) confirmScreen(usage, screen) diff --git a/packages/server/src/constants/screens.ts b/packages/server/src/constants/screens.ts index a506f123c0..df57a6a13c 100644 --- a/packages/server/src/constants/screens.ts +++ b/packages/server/src/constants/screens.ts @@ -1,181 +1,7 @@ -import { Screen, Table, Query, ViewV2, Component } from "@budibase/types" +import { Screen } from "@budibase/types" export const SAMPLE_DATA_SCREEN_NAME = "sample-data-inventory-screen" -function heading(text: string): Component { - return { - _id: "c1bff24cd821e41d18c894ac77a80ef99", - _component: "@budibase/standard-components/heading", - _styles: { - normal: {}, - hover: {}, - active: {}, - selected: {}, - }, - _instanceName: "Table heading", - _children: [], - text, - } -} - -export function createTableScreen( - datasourceName: string, - table: Table -): Screen { - return { - props: { - _id: "cad0a0904cacd4678a2ac094e293db1a5", - _component: "@budibase/standard-components/container", - _styles: { - normal: {}, - hover: {}, - active: {}, - selected: {}, - }, - _children: [ - heading("table"), - { - _id: "ca6304be2079147bb9933092c4f8ce6fa", - _component: "@budibase/standard-components/gridblock", - _styles: { - normal: {}, - hover: {}, - active: {}, - selected: {}, - }, - _instanceName: "table - Table", - _children: [], - table: { - label: table.name, - tableId: table._id!, - type: "table", - datasourceName, - }, - }, - ], - _instanceName: "table - List", - layout: "grid", - direction: "column", - hAlign: "stretch", - vAlign: "top", - size: "grow", - gap: "M", - }, - routing: { - route: "/table", - roleId: "ADMIN", - homeScreen: false, - }, - name: "screen-id", - } -} - -export function createViewScreen(datasourceName: string, view: ViewV2): Screen { - return { - props: { - _id: "cc359092bbd6c4e10b57827155edb7872", - _component: "@budibase/standard-components/container", - _styles: { - normal: {}, - hover: {}, - active: {}, - selected: {}, - }, - _children: [ - heading("view"), - { - _id: "ccb4a9e3734794864b5c65b012a0bdc5a", - _component: "@budibase/standard-components/gridblock", - _styles: { - normal: {}, - hover: {}, - active: {}, - selected: {}, - }, - _instanceName: "view - Table", - _children: [], - table: { - ...view, - name: view.name, - tableId: view.tableId, - id: view.id, - label: view.name, - type: "viewV2", - }, - }, - ], - _instanceName: "view - List", - layout: "grid", - direction: "column", - hAlign: "stretch", - vAlign: "top", - size: "grow", - gap: "M", - }, - routing: { - route: "/view", - roleId: "ADMIN", - homeScreen: false, - }, - name: "view-id", - } -} - -export function createQueryScreen(datasourceId: string, query: Query): Screen { - return { - props: { - _id: "cc59b217aed264939a6c5249eee39cb25", - _component: "@budibase/standard-components/container", - _styles: { - normal: {}, - hover: {}, - active: {}, - selected: {}, - }, - _children: [ - { - _id: "c33a4a6e3cb5343158a08625c06b5cd7c", - _component: "@budibase/standard-components/gridblock", - _styles: { - normal: {}, - hover: {}, - active: {}, - }, - _instanceName: "New Table", - table: { - ...query, - label: query.name, - _id: query._id!, - name: query.name, - datasourceId: datasourceId, - type: "query", - }, - initialSortOrder: "Ascending", - allowAddRows: true, - allowEditRows: true, - allowDeleteRows: true, - stripeRows: false, - quiet: false, - columns: null, - }, - ], - _instanceName: "Blank screen", - layout: "grid", - direction: "column", - hAlign: "stretch", - vAlign: "top", - size: "grow", - gap: "M", - }, - routing: { - route: "/query", - roleId: "BASIC", - homeScreen: false, - }, - name: "screen-id", - } -} - export function createSampleDataTableScreen(): Screen { return { showNavigation: true, diff --git a/packages/server/src/tests/utilities/structures.ts b/packages/server/src/tests/utilities/structures.ts index 7ae902d7cf..7ca60f26c6 100644 --- a/packages/server/src/tests/utilities/structures.ts +++ b/packages/server/src/tests/utilities/structures.ts @@ -46,7 +46,7 @@ export { createTableScreen, createQueryScreen, createViewScreen, -} from "../../constants/screens" +} from "./structures/screens" const { BUILTIN_ROLE_IDS } = roles @@ -587,7 +587,7 @@ function createHomeScreen( roleId: config.roleId, }, name: "home-screen", - projectAppId: "appId", + projectAppId: "projectAppId", } } diff --git a/packages/server/src/tests/utilities/structures/screens.ts b/packages/server/src/tests/utilities/structures/screens.ts new file mode 100644 index 0000000000..dd5a120fea --- /dev/null +++ b/packages/server/src/tests/utilities/structures/screens.ts @@ -0,0 +1,178 @@ +import { Component, Query, Screen, Table, ViewV2 } from "@budibase/types" + +function heading(text: string): Component { + return { + _id: "c1bff24cd821e41d18c894ac77a80ef99", + _component: "@budibase/standard-components/heading", + _styles: { + normal: {}, + hover: {}, + active: {}, + selected: {}, + }, + _instanceName: "Table heading", + _children: [], + text, + } +} + +export function createTableScreen( + datasourceName: string, + table: Table +): Screen { + return { + props: { + _id: "cad0a0904cacd4678a2ac094e293db1a5", + _component: "@budibase/standard-components/container", + _styles: { + normal: {}, + hover: {}, + active: {}, + selected: {}, + }, + _children: [ + heading("table"), + { + _id: "ca6304be2079147bb9933092c4f8ce6fa", + _component: "@budibase/standard-components/gridblock", + _styles: { + normal: {}, + hover: {}, + active: {}, + selected: {}, + }, + _instanceName: "table - Table", + _children: [], + table: { + label: table.name, + tableId: table._id!, + type: "table", + datasourceName, + }, + }, + ], + _instanceName: "table - List", + layout: "grid", + direction: "column", + hAlign: "stretch", + vAlign: "top", + size: "grow", + gap: "M", + }, + routing: { + route: "/table", + roleId: "ADMIN", + homeScreen: false, + }, + name: "screen-id", + projectAppId: "projectAppId", + } +} + +export function createViewScreen(view: ViewV2): Screen { + return { + props: { + _id: "cc359092bbd6c4e10b57827155edb7872", + _component: "@budibase/standard-components/container", + _styles: { + normal: {}, + hover: {}, + active: {}, + selected: {}, + }, + _children: [ + heading("view"), + { + _id: "ccb4a9e3734794864b5c65b012a0bdc5a", + _component: "@budibase/standard-components/gridblock", + _styles: { + normal: {}, + hover: {}, + active: {}, + selected: {}, + }, + _instanceName: "view - Table", + _children: [], + table: { + ...view, + name: view.name, + tableId: view.tableId, + id: view.id, + label: view.name, + type: "viewV2", + }, + }, + ], + _instanceName: "view - List", + layout: "grid", + direction: "column", + hAlign: "stretch", + vAlign: "top", + size: "grow", + gap: "M", + }, + routing: { + route: "/view", + roleId: "ADMIN", + homeScreen: false, + }, + name: "view-id", + projectAppId: "projectAppId", + } +} + +export function createQueryScreen(datasourceId: string, query: Query): Screen { + return { + props: { + _id: "cc59b217aed264939a6c5249eee39cb25", + _component: "@budibase/standard-components/container", + _styles: { + normal: {}, + hover: {}, + active: {}, + selected: {}, + }, + _children: [ + { + _id: "c33a4a6e3cb5343158a08625c06b5cd7c", + _component: "@budibase/standard-components/gridblock", + _styles: { + normal: {}, + hover: {}, + active: {}, + }, + _instanceName: "New Table", + table: { + ...query, + label: query.name, + _id: query._id!, + name: query.name, + datasourceId: datasourceId, + type: "query", + }, + initialSortOrder: "Ascending", + allowAddRows: true, + allowEditRows: true, + allowDeleteRows: true, + stripeRows: false, + quiet: false, + columns: null, + }, + ], + _instanceName: "Blank screen", + layout: "grid", + direction: "column", + hAlign: "stretch", + vAlign: "top", + size: "grow", + gap: "M", + }, + routing: { + route: "/query", + roleId: "BASIC", + homeScreen: false, + }, + name: "screen-id", + projectAppId: "projectAppId", + } +} From bb070e7e1f20f0c980985c21092a5ab7eb6e1bfc Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 6 May 2025 16:31:10 +0200 Subject: [PATCH 08/36] ProjectApp on addSampleDataScreen --- packages/server/src/api/controllers/application.ts | 7 ++++++- packages/server/src/constants/screens.ts | 3 ++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/server/src/api/controllers/application.ts b/packages/server/src/api/controllers/application.ts index 417f60bfb5..d988d46e68 100644 --- a/packages/server/src/api/controllers/application.ts +++ b/packages/server/src/api/controllers/application.ts @@ -182,7 +182,12 @@ async function addSampleDataDocs() { async function addSampleDataScreen() { const db = context.getAppDB() - let screen = createSampleDataTableScreen() + + const projectApp = await sdk.projectApps.create({ + name: "TODO", + }) + + let screen = createSampleDataTableScreen(projectApp._id) screen._id = generateScreenID() await db.put(screen) } diff --git a/packages/server/src/constants/screens.ts b/packages/server/src/constants/screens.ts index df57a6a13c..8a94ccf205 100644 --- a/packages/server/src/constants/screens.ts +++ b/packages/server/src/constants/screens.ts @@ -2,12 +2,13 @@ import { Screen } from "@budibase/types" export const SAMPLE_DATA_SCREEN_NAME = "sample-data-inventory-screen" -export function createSampleDataTableScreen(): Screen { +export function createSampleDataTableScreen(projectAppId: string): Screen { return { showNavigation: true, width: "Large", routing: { route: "/inventory", roleId: "BASIC", homeScreen: false }, name: SAMPLE_DATA_SCREEN_NAME, + projectAppId, props: { _id: "c38f2b9f250fb4c33965ce47e12c02a80", _component: "@budibase/standard-components/container", From 34d00cbb54c14d4f20b4a32047cf2cc77e4e1323 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Mon, 12 May 2025 16:26:15 +0200 Subject: [PATCH 09/36] Use default name --- packages/server/src/api/controllers/application.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/server/src/api/controllers/application.ts b/packages/server/src/api/controllers/application.ts index d988d46e68..7eaceedeab 100644 --- a/packages/server/src/api/controllers/application.ts +++ b/packages/server/src/api/controllers/application.ts @@ -182,9 +182,10 @@ async function addSampleDataDocs() { async function addSampleDataScreen() { const db = context.getAppDB() + const appMetadata = await sdk.applications.metadata.get() const projectApp = await sdk.projectApps.create({ - name: "TODO", + name: appMetadata.name, }) let screen = createSampleDataTableScreen(projectApp._id) From 464deac18fa4f68facf02d39516403a506193b51 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Mon, 12 May 2025 16:27:22 +0200 Subject: [PATCH 10/36] Add default urlPrefix --- packages/server/src/api/controllers/application.ts | 1 + packages/server/src/sdk/app/screens/screens.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/packages/server/src/api/controllers/application.ts b/packages/server/src/api/controllers/application.ts index 7eaceedeab..63f474bf1b 100644 --- a/packages/server/src/api/controllers/application.ts +++ b/packages/server/src/api/controllers/application.ts @@ -186,6 +186,7 @@ async function addSampleDataScreen() { const projectApp = await sdk.projectApps.create({ name: appMetadata.name, + urlPrefix: "/", }) let screen = createSampleDataTableScreen(projectApp._id) diff --git a/packages/server/src/sdk/app/screens/screens.ts b/packages/server/src/sdk/app/screens/screens.ts index 0210921a87..15ab1d353e 100644 --- a/packages/server/src/sdk/app/screens/screens.ts +++ b/packages/server/src/sdk/app/screens/screens.ts @@ -32,6 +32,7 @@ async function migrateToProjectApp(screens: Screen[]) { const createdProjectApp = await sdk.projectApps.update({ _id: `${DocumentType.PROJECT_APP}${SEPARATOR}default`, name: application.name, + urlPrefix: "/", }) const db = context.getAppDB() From 1091299aeff4ef47e84c196de77cdc8dd523379a Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Mon, 12 May 2025 16:53:14 +0200 Subject: [PATCH 11/36] Validate projectAppId on screen save --- packages/server/src/api/controllers/screen.ts | 4 ++++ packages/server/src/sdk/app/projectApps/index.ts | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/packages/server/src/api/controllers/screen.ts b/packages/server/src/api/controllers/screen.ts index 038d87d548..d250df302e 100644 --- a/packages/server/src/api/controllers/screen.ts +++ b/packages/server/src/api/controllers/screen.ts @@ -48,6 +48,10 @@ export async function save( eventFn = events.screen.created } + if (!(await sdk.projectApps.get(screen.projectAppId))) { + ctx.throw("Project app is not valid") + } + const response = await db.put(screen) // Find any custom components being used diff --git a/packages/server/src/sdk/app/projectApps/index.ts b/packages/server/src/sdk/app/projectApps/index.ts index ebf252d2a2..805e278dc4 100644 --- a/packages/server/src/sdk/app/projectApps/index.ts +++ b/packages/server/src/sdk/app/projectApps/index.ts @@ -22,6 +22,12 @@ export async function fetch(): Promise { return result } +export async function get(id: string): Promise { + const db = context.getAppDB() + const projectApp = await db.tryGet(id) + return projectApp +} + export async function create( projectApp: Omit ) { From 6db5e1363589af09c292e2a5035c865d27fa4ac8 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Mon, 12 May 2025 17:31:49 +0200 Subject: [PATCH 12/36] Expose api --- .../server/src/api/controllers/projectApp.ts | 70 +++++++++++++++++++ packages/server/src/api/routes/index.ts | 2 + packages/server/src/api/routes/projectApp.ts | 56 +++++++++++++++ .../server/src/sdk/app/projectApps/index.ts | 18 +++++ packages/types/src/api/web/app/index.ts | 1 + packages/types/src/api/web/app/projectApp.ts | 32 +++++++++ 6 files changed, 179 insertions(+) create mode 100644 packages/server/src/api/controllers/projectApp.ts create mode 100644 packages/server/src/api/routes/projectApp.ts create mode 100644 packages/types/src/api/web/app/projectApp.ts diff --git a/packages/server/src/api/controllers/projectApp.ts b/packages/server/src/api/controllers/projectApp.ts new file mode 100644 index 0000000000..ba8e3a7cbe --- /dev/null +++ b/packages/server/src/api/controllers/projectApp.ts @@ -0,0 +1,70 @@ +import { + Ctx, + InsertProjectAppRequest, + InsertProjectAppResponse, + ProjectApp, + ProjectAppResponse, + UpdateProjectAppRequest, + UpdateProjectAppResponse, +} from "@budibase/types" +import sdk from "../../sdk" + +function toProjectAppResponse(projectApp: ProjectApp): ProjectAppResponse { + return { + _id: projectApp._id!, + _rev: projectApp._rev!, + name: projectApp.name, + urlPrefix: projectApp.urlPrefix, + icon: projectApp.icon, + iconColor: projectApp.iconColor, + } +} + +export async function create( + ctx: Ctx +) { + const { body } = ctx.request + const newProjectApp = { + name: body.name, + urlPrefix: body.urlPrefix, + icon: body.icon, + iconColor: body.iconColor, + } + + const projectApp = await sdk.projectApps.create(newProjectApp) + ctx.status = 201 + ctx.body = { + projectApp: toProjectAppResponse(projectApp), + } +} + +export async function edit( + ctx: Ctx +) { + const { body } = ctx.request + + if (ctx.params.id !== body._id) { + ctx.throw("Path and body ids do not match", 400) + } + + const toUpdate = { + _id: body._id, + _rev: body._rev, + name: body.name, + urlPrefix: body.urlPrefix, + icon: body.icon, + iconColor: body.iconColor, + } + + const projectApp = await sdk.projectApps.update(toUpdate) + ctx.body = { + projectApp: toProjectAppResponse(projectApp), + } +} + +export async function remove(ctx: Ctx) { + const { id, rev } = ctx.params + + await sdk.projectApps.remove(id, rev) + ctx.status = 204 +} diff --git a/packages/server/src/api/routes/index.ts b/packages/server/src/api/routes/index.ts index 19ebe7ef46..54b02a2a90 100644 --- a/packages/server/src/api/routes/index.ts +++ b/packages/server/src/api/routes/index.ts @@ -32,6 +32,7 @@ import rowActionRoutes from "./rowAction" import oauth2Routes from "./oauth2" import featuresRoutes from "./features" import aiRoutes from "./ai" +import projectApps from "./projectApp" export { default as staticRoutes } from "./static" export { default as publicRoutes } from "./public" @@ -72,6 +73,7 @@ export const mainRoutes: Router[] = [ rowActionRoutes, oauth2Routes, featuresRoutes, + projectApps, // these need to be handled last as they still use /api/:tableId // this could be breaking as koa may recognise other routes as this tableRoutes, diff --git a/packages/server/src/api/routes/projectApp.ts b/packages/server/src/api/routes/projectApp.ts new file mode 100644 index 0000000000..57a3d36edf --- /dev/null +++ b/packages/server/src/api/routes/projectApp.ts @@ -0,0 +1,56 @@ +import Router from "@koa/router" +import { + OAuth2CredentialsMethod, + OAuth2GrantType, + PermissionType, +} from "@budibase/types" +import { middleware } from "@budibase/backend-core" +import authorized from "../../middleware/authorized" + +import * as controller from "../controllers/projectApp" +import Joi from "joi" + +const baseSchema = { + name: Joi.string().required(), + urlPrefix: Joi.string().required(), + icon: Joi.string().required(), + iconColor: Joi.string().required(), +} + +const insertSchema = Joi.object({ + ...baseSchema, +}) + +const updateSchema = Joi.object({ + _id: Joi.string().required(), + _rev: Joi.string().required(), + ...baseSchema, +}) + +function projectAppValidator( + schema: typeof insertSchema | typeof updateSchema +) { + return middleware.joiValidator.body(schema, { allowUnknown: false }) +} + +const router: Router = new Router() + +router.post( + "/api/projectApp", + authorized(PermissionType.BUILDER), + projectAppValidator(insertSchema), + controller.create +) +router.put( + "/api/projectApp/:id", + authorized(PermissionType.BUILDER), + projectAppValidator(updateSchema), + controller.edit +) +router.delete( + "/api/projectApp/:id/:rev", + authorized(PermissionType.BUILDER), + controller.remove +) + +export default router diff --git a/packages/server/src/sdk/app/projectApps/index.ts b/packages/server/src/sdk/app/projectApps/index.ts index 805e278dc4..f41ab2873d 100644 --- a/packages/server/src/sdk/app/projectApps/index.ts +++ b/packages/server/src/sdk/app/projectApps/index.ts @@ -60,3 +60,21 @@ export async function update( ...projectApp, } } + +export async function remove( + projectAppId: string, + _rev: string +): Promise { + const db = context.getAppDB() + try { + await db.remove(projectAppId, _rev) + } catch (e: any) { + if (e.status === 404) { + throw new HTTPError( + `Project app with id '${projectAppId}' not found.`, + 404 + ) + } + throw e + } +} diff --git a/packages/types/src/api/web/app/index.ts b/packages/types/src/api/web/app/index.ts index 612f4bbc32..3c7d6e92f8 100644 --- a/packages/types/src/api/web/app/index.ts +++ b/packages/types/src/api/web/app/index.ts @@ -11,6 +11,7 @@ export * from "./layout" export * from "./metadata" export * from "./oauth2" export * from "./permission" +export * from "./projectApp" export * from "./query" export * from "./role" export * from "./rowAction" diff --git a/packages/types/src/api/web/app/projectApp.ts b/packages/types/src/api/web/app/projectApp.ts new file mode 100644 index 0000000000..dd55881072 --- /dev/null +++ b/packages/types/src/api/web/app/projectApp.ts @@ -0,0 +1,32 @@ +export interface ProjectAppResponse { + _id: string + _rev: string + name: string + urlPrefix: string + icon: string + iconColor: string +} + +export interface InsertProjectAppRequest { + name: string + urlPrefix: string + icon: string + iconColor: string +} + +export interface InsertProjectAppResponse { + projectApp: ProjectAppResponse +} + +export interface UpdateProjectAppRequest { + _id: string + _rev: string + name: string + urlPrefix: string + icon: string + iconColor: string +} + +export interface UpdateProjectAppResponse { + projectApp: ProjectAppResponse +} From abfa46b62e2fe7e7936aaf8cb5f610c54a9c5168 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Mon, 12 May 2025 17:32:07 +0200 Subject: [PATCH 13/36] Clean --- packages/server/src/api/routes/projectApp.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/server/src/api/routes/projectApp.ts b/packages/server/src/api/routes/projectApp.ts index 57a3d36edf..0be6e87f1f 100644 --- a/packages/server/src/api/routes/projectApp.ts +++ b/packages/server/src/api/routes/projectApp.ts @@ -1,9 +1,5 @@ import Router from "@koa/router" -import { - OAuth2CredentialsMethod, - OAuth2GrantType, - PermissionType, -} from "@budibase/types" +import { PermissionType } from "@budibase/types" import { middleware } from "@budibase/backend-core" import authorized from "../../middleware/authorized" From 90ad0660987587e7966261144f4a3a5af020e1fd Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 13 May 2025 16:41:24 +0200 Subject: [PATCH 14/36] Fix conflicts --- packages/builder/src/stores/builder/screens.ts | 2 +- packages/builder/src/templates/screenTemplating/Screen.ts | 1 + packages/server/src/api/controllers/application.ts | 1 + packages/server/src/sdk/app/screens/screens.ts | 5 +++-- packages/types/src/api/web/app/projectApp.ts | 2 +- packages/types/src/documents/app/projectApp.ts | 4 +++- 6 files changed, 10 insertions(+), 5 deletions(-) diff --git a/packages/builder/src/stores/builder/screens.ts b/packages/builder/src/stores/builder/screens.ts index a67490badb..ccf516f2f7 100644 --- a/packages/builder/src/stores/builder/screens.ts +++ b/packages/builder/src/stores/builder/screens.ts @@ -89,7 +89,7 @@ export class ScreenStore extends BudiStore { syncAppScreens(pkg: FetchAppPackageResponse) { this.update(state => ({ ...state, - screens: [...pkg.screens], + screens: [...pkg.projectApps.flatMap(p => p.screens)], })) } diff --git a/packages/builder/src/templates/screenTemplating/Screen.ts b/packages/builder/src/templates/screenTemplating/Screen.ts index 0749a6ea8b..a7e8d4e439 100644 --- a/packages/builder/src/templates/screenTemplating/Screen.ts +++ b/packages/builder/src/templates/screenTemplating/Screen.ts @@ -31,6 +31,7 @@ export class Screen extends BaseStructure { homeScreen: false, }, name: "screen-id", + projectAppId: "TODO", }) } diff --git a/packages/server/src/api/controllers/application.ts b/packages/server/src/api/controllers/application.ts index 63f474bf1b..b0fdcd5783 100644 --- a/packages/server/src/api/controllers/application.ts +++ b/packages/server/src/api/controllers/application.ts @@ -187,6 +187,7 @@ async function addSampleDataScreen() { const projectApp = await sdk.projectApps.create({ name: appMetadata.name, urlPrefix: "/", + icon: "Monitoring", }) let screen = createSampleDataTableScreen(projectApp._id) diff --git a/packages/server/src/sdk/app/screens/screens.ts b/packages/server/src/sdk/app/screens/screens.ts index 15ab1d353e..a9f45744fe 100644 --- a/packages/server/src/sdk/app/screens/screens.ts +++ b/packages/server/src/sdk/app/screens/screens.ts @@ -1,4 +1,4 @@ -import { context } from "@budibase/backend-core" +import { context, utils } from "@budibase/backend-core" import { Database, DocumentType, Screen, SEPARATOR } from "@budibase/types" import { getScreenParams } from "../../../db/utils" import sdk from "../.." @@ -30,9 +30,10 @@ async function migrateToProjectApp(screens: Screen[]) { // Forcing the _id to avoid concurrency issues const createdProjectApp = await sdk.projectApps.update({ - _id: `${DocumentType.PROJECT_APP}${SEPARATOR}default`, + _id: `${DocumentType.PROJECT_APP}${SEPARATOR}${utils.newid()}`, name: application.name, urlPrefix: "/", + icon: "Monitoring", }) const db = context.getAppDB() diff --git a/packages/types/src/api/web/app/projectApp.ts b/packages/types/src/api/web/app/projectApp.ts index dd55881072..b19e83d751 100644 --- a/packages/types/src/api/web/app/projectApp.ts +++ b/packages/types/src/api/web/app/projectApp.ts @@ -4,7 +4,7 @@ export interface ProjectAppResponse { name: string urlPrefix: string icon: string - iconColor: string + iconColor?: string } export interface InsertProjectAppRequest { diff --git a/packages/types/src/documents/app/projectApp.ts b/packages/types/src/documents/app/projectApp.ts index d1261eb7aa..6c6ddb051e 100644 --- a/packages/types/src/documents/app/projectApp.ts +++ b/packages/types/src/documents/app/projectApp.ts @@ -2,5 +2,7 @@ import { Document } from "../document" export interface ProjectApp extends Document { name: string - icon?: string + urlPrefix: string + icon: string + iconColor?: string } From 09efce387fe7e3416af15ad24922e122fcae5c3b Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 13 May 2025 16:47:02 +0200 Subject: [PATCH 15/36] Remove projectapp from screen --- packages/server/src/api/controllers/screen.ts | 4 ---- packages/server/src/api/routes/utils/validators.ts | 1 - 2 files changed, 5 deletions(-) diff --git a/packages/server/src/api/controllers/screen.ts b/packages/server/src/api/controllers/screen.ts index d250df302e..038d87d548 100644 --- a/packages/server/src/api/controllers/screen.ts +++ b/packages/server/src/api/controllers/screen.ts @@ -48,10 +48,6 @@ export async function save( eventFn = events.screen.created } - if (!(await sdk.projectApps.get(screen.projectAppId))) { - ctx.throw("Project app is not valid") - } - const response = await db.put(screen) // Find any custom components being used diff --git a/packages/server/src/api/routes/utils/validators.ts b/packages/server/src/api/routes/utils/validators.ts index 359a10a595..a8fa12ec3d 100644 --- a/packages/server/src/api/routes/utils/validators.ts +++ b/packages/server/src/api/routes/utils/validators.ts @@ -320,7 +320,6 @@ export function screenValidator() { }) .required() .unknown(true), - projectAppId: Joi.string().required(), }).unknown(true) ) } From ede6cf605ab48ef0e4639d22545d5e7af3330d05 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 13 May 2025 16:57:30 +0200 Subject: [PATCH 16/36] Fix tests --- .../src/stores/builder/tests/screens.test.js | 24 ++++++++++++++----- 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/packages/builder/src/stores/builder/tests/screens.test.js b/packages/builder/src/stores/builder/tests/screens.test.js index ea916c8d59..61e5c88c12 100644 --- a/packages/builder/src/stores/builder/tests/screens.test.js +++ b/packages/builder/src/stores/builder/tests/screens.test.js @@ -92,7 +92,9 @@ describe("Screens store", () => { .fill() .map(() => getScreenFixture().json()) - bb.screenStore.syncAppScreens({ screens }) + bb.screenStore.syncAppScreens({ + projectApps: [{ screens }], + }) expect(bb.store.screens).toStrictEqual(screens) }) @@ -104,7 +106,9 @@ describe("Screens store", () => { .fill() .map(() => getScreenFixture().json()) - bb.screenStore.syncAppScreens({ screens }) + bb.screenStore.syncAppScreens({ + projectApps: [{ screens }], + }) expect(bb.store.screens).toStrictEqual(screens) bb.screenStore.update(state => ({ @@ -122,7 +126,9 @@ describe("Screens store", () => { .fill() .map(() => getScreenFixture().json()) - bb.screenStore.syncAppScreens({ screens }) + bb.screenStore.syncAppScreens({ + projectApps: [{ screens }], + }) expect(bb.store.screens.length).toBe(2) bb.screenStore.select(screens[0]._id) @@ -135,7 +141,9 @@ describe("Screens store", () => { .fill() .map(() => getScreenFixture().json()) - bb.screenStore.syncAppScreens({ screens }) + bb.screenStore.syncAppScreens({ + projectApps: [{ screens }], + }) expect(bb.store.screens.length).toBe(2) bb.screenStore.select("screen_abc") @@ -410,7 +418,9 @@ describe("Screens store", () => { screenDoc._json._id = existingDocId return screenDoc.json() }) - bb.screenStore.syncAppScreens({ screens: existingScreens }) + bb.screenStore.syncAppScreens({ + projectApps: [{ screens: existingScreens }], + }) bb.screenStore.replace() @@ -428,7 +438,9 @@ describe("Screens store", () => { screenDoc._json._id = existingDocId return screenDoc.json() }) - bb.screenStore.syncAppScreens({ screens: existingScreens }) + bb.screenStore.syncAppScreens({ + projectApps: [{ screens: existingScreens }], + }) bb.screenStore.replace(existingScreens[1]._id) From fd95f0e6c9d49d80ad6d9a603aae416304874ede Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 13 May 2025 17:26:49 +0200 Subject: [PATCH 17/36] Fix test --- .../server/src/sdk/app/screens/screens.ts | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/packages/server/src/sdk/app/screens/screens.ts b/packages/server/src/sdk/app/screens/screens.ts index a9f45744fe..d0f70a407e 100644 --- a/packages/server/src/sdk/app/screens/screens.ts +++ b/packages/server/src/sdk/app/screens/screens.ts @@ -28,19 +28,24 @@ async function migrateToProjectApp(screens: Screen[]) { return screens } - // Forcing the _id to avoid concurrency issues - const createdProjectApp = await sdk.projectApps.update({ - _id: `${DocumentType.PROJECT_APP}${SEPARATOR}${utils.newid()}`, - name: application.name, - urlPrefix: "/", - icon: "Monitoring", - }) + const allProjectApps = await sdk.projectApps.fetch() + let projectAppId = allProjectApps.find(p => p.name === application.name)?._id + if (!projectAppId) { + // Forcing the _id to avoid concurrency issues + const projectApp = await sdk.projectApps.update({ + _id: `${DocumentType.PROJECT_APP}${SEPARATOR}${utils.newid()}`, + name: application.name, + urlPrefix: "/", + icon: "Monitoring", + }) + projectAppId = projectApp._id + } const db = context.getAppDB() await db.bulkDocs( screens.map(s => ({ ...s, - projectAppId: createdProjectApp._id, + projectAppId, })) ) From 976fc847953e0f4e0ebe77d278196790a0a56e08 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 14 May 2025 13:35:52 +0200 Subject: [PATCH 18/36] Extract interface --- packages/types/src/api/web/app/application.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/types/src/api/web/app/application.ts b/packages/types/src/api/web/app/application.ts index ada1e25b45..df5e443c93 100644 --- a/packages/types/src/api/web/app/application.ts +++ b/packages/types/src/api/web/app/application.ts @@ -35,10 +35,14 @@ export interface FetchAppDefinitionResponse { libraries: string[] } +interface ProjectAppResponse extends ProjectApp { + screens: Screen[] +} + export interface FetchAppPackageResponse { application: App licenseType: PlanType - projectApps: (ProjectApp & { screens: Screen[] })[] + projectApps: ProjectAppResponse[] layouts: Layout[] clientLibPath: string hasLock: boolean From 46a65cd7480c3b2abe7c8eb18d6fcca6bfe4e448 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 14 May 2025 13:48:55 +0200 Subject: [PATCH 19/36] Move to migration --- .../server/src/appMigrations/migrations.ts | 9 +++- .../migrations/20250514133719_project_apps.ts | 31 ++++++++++++++ .../server/src/sdk/app/screens/screens.ts | 42 ++----------------- 3 files changed, 41 insertions(+), 41 deletions(-) create mode 100644 packages/server/src/appMigrations/migrations/20250514133719_project_apps.ts diff --git a/packages/server/src/appMigrations/migrations.ts b/packages/server/src/appMigrations/migrations.ts index fc55f87d00..227294e327 100644 --- a/packages/server/src/appMigrations/migrations.ts +++ b/packages/server/src/appMigrations/migrations.ts @@ -3,11 +3,16 @@ import { AppMigration } from "." import m20240604153647_initial_sqs from "./migrations/20240604153647_initial_sqs" +import m20250514133719_project_apps from "./migrations/20250514133719_project_apps" -// Migrations will be executed sorted by ID export const MIGRATIONS: AppMigration[] = [ + // Migrations will be executed sorted by id { id: "20240604153647_initial_sqs", - func: m20240604153647_initial_sqs, + func: m20240604153647_initial_sqs + }, + { + id: "20250514133719_project_apps", + func: m20250514133719_project_apps }, ] diff --git a/packages/server/src/appMigrations/migrations/20250514133719_project_apps.ts b/packages/server/src/appMigrations/migrations/20250514133719_project_apps.ts new file mode 100644 index 0000000000..29dfb88bf7 --- /dev/null +++ b/packages/server/src/appMigrations/migrations/20250514133719_project_apps.ts @@ -0,0 +1,31 @@ +import { Screen } from "@budibase/types" +import sdk from "../../sdk" +import { context } from "@budibase/backend-core" + +const migration = async () => { + const screens = await sdk.screens.fetch() + + const application = await sdk.applications.metadata.get() + const allProjectApps = await sdk.projectApps.fetch() + let projectAppId = allProjectApps.find(p => p.name === application.name)?._id + if (!projectAppId) { + const projectApp = await sdk.projectApps.create({ + name: application.name, + urlPrefix: "/", + icon: "Monitoring", + }) + projectAppId = projectApp._id + } + + const db = context.getAppDB() + await db.bulkDocs( + screens + .filter(s => !s.projectAppId) + .map(s => ({ + ...s, + projectAppId, + })) + ) +} + +export default migration diff --git a/packages/server/src/sdk/app/screens/screens.ts b/packages/server/src/sdk/app/screens/screens.ts index d0f70a407e..6fb562dc88 100644 --- a/packages/server/src/sdk/app/screens/screens.ts +++ b/packages/server/src/sdk/app/screens/screens.ts @@ -1,7 +1,6 @@ -import { context, utils } from "@budibase/backend-core" -import { Database, DocumentType, Screen, SEPARATOR } from "@budibase/types" +import { context } from "@budibase/backend-core" +import { Database, Screen } from "@budibase/types" import { getScreenParams } from "../../../db/utils" -import sdk from "../.." export async function fetch( db: Database = context.getAppDB() @@ -14,40 +13,5 @@ export async function fetch( ) ).rows.map(el => el.doc!) - if (screens.every(s => s.projectAppId)) { - return screens - } - - await migrateToProjectApp(screens) - return fetch(db) -} - -async function migrateToProjectApp(screens: Screen[]) { - const application = await sdk.applications.metadata.get() - if (screens.every(s => s.projectAppId)) { - return screens - } - - const allProjectApps = await sdk.projectApps.fetch() - let projectAppId = allProjectApps.find(p => p.name === application.name)?._id - if (!projectAppId) { - // Forcing the _id to avoid concurrency issues - const projectApp = await sdk.projectApps.update({ - _id: `${DocumentType.PROJECT_APP}${SEPARATOR}${utils.newid()}`, - name: application.name, - urlPrefix: "/", - icon: "Monitoring", - }) - projectAppId = projectApp._id - } - - const db = context.getAppDB() - await db.bulkDocs( - screens.map(s => ({ - ...s, - projectAppId, - })) - ) - - return sdk.screens.fetch() + return screens } From e548187566f12d17d14c9dccc729743f4fc6d549 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 14 May 2025 14:31:00 +0200 Subject: [PATCH 20/36] Don't override migrations on imports --- packages/server/src/api/controllers/application.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/src/api/controllers/application.ts b/packages/server/src/api/controllers/application.ts index b0fdcd5783..432638c0d7 100644 --- a/packages/server/src/api/controllers/application.ts +++ b/packages/server/src/api/controllers/application.ts @@ -457,7 +457,7 @@ async function performAppCreate( } const latestMigrationId = appMigrations.getLatestEnabledMigrationId() - if (latestMigrationId) { + if (latestMigrationId && !isImport) { // Initialise the app migration version as the latest one await appMigrations.updateAppMigrationMetadata({ appId, From be73cfbc8d69e6a63a8a82e4a8fbe6e5a505d7a7 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 14 May 2025 14:54:12 +0200 Subject: [PATCH 21/36] Add WithoutDocMetadata "type" --- packages/server/src/sdk/app/projectApps/index.ts | 11 +++++++---- packages/types/src/shared/typeUtils.ts | 7 +++++++ 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/packages/server/src/sdk/app/projectApps/index.ts b/packages/server/src/sdk/app/projectApps/index.ts index f41ab2873d..e19243223d 100644 --- a/packages/server/src/sdk/app/projectApps/index.ts +++ b/packages/server/src/sdk/app/projectApps/index.ts @@ -1,5 +1,10 @@ import { context, docIds, HTTPError, utils } from "@budibase/backend-core" -import { DocumentType, ProjectApp, SEPARATOR } from "@budibase/types" +import { + DocumentType, + ProjectApp, + SEPARATOR, + WithoutDocMetadata, +} from "@budibase/types" async function guardName(name: string, id?: string) { const existingProjectApps = await fetch() @@ -28,9 +33,7 @@ export async function get(id: string): Promise { return projectApp } -export async function create( - projectApp: Omit -) { +export async function create(projectApp: WithoutDocMetadata) { const db = context.getAppDB() await guardName(projectApp.name) diff --git a/packages/types/src/shared/typeUtils.ts b/packages/types/src/shared/typeUtils.ts index dbb3fc2553..5673f23b36 100644 --- a/packages/types/src/shared/typeUtils.ts +++ b/packages/types/src/shared/typeUtils.ts @@ -1,3 +1,5 @@ +import { Document } from "../documents" + export type DeepPartial = { [P in keyof T]?: T[P] extends object ? DeepPartial : T[P] } @@ -32,3 +34,8 @@ export type RequiredKeys = { } export type WithRequired = T & Required> + +export type WithoutDocMetadata = Omit< + T, + "_id" | "_rev" | "createdAt" | "updatedAt" +> From 48c62c734c2651b167828744d0cc80ab54df2790 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 14 May 2025 14:56:45 +0200 Subject: [PATCH 22/36] Use WithoutDocMetadata --- packages/server/src/sdk/app/oauth2/crud.ts | 3 ++- packages/server/src/sdk/app/tables/create.ts | 4 ++-- packages/server/src/sdk/app/tables/external/index.ts | 3 ++- packages/server/src/sdk/app/tables/internal/index.ts | 3 ++- .../worker/src/api/routes/global/tests/roles.spec.ts | 9 +++++++-- 5 files changed, 15 insertions(+), 7 deletions(-) diff --git a/packages/server/src/sdk/app/oauth2/crud.ts b/packages/server/src/sdk/app/oauth2/crud.ts index 6619ab6a47..262ca70122 100644 --- a/packages/server/src/sdk/app/oauth2/crud.ts +++ b/packages/server/src/sdk/app/oauth2/crud.ts @@ -4,6 +4,7 @@ import { OAuth2Config, PASSWORD_REPLACEMENT, SEPARATOR, + WithoutDocMetadata, WithRequired, } from "@budibase/types" @@ -34,7 +35,7 @@ export async function fetch(): Promise { } export async function create( - config: Omit + config: WithoutDocMetadata ): Promise { const db = context.getAppDB() diff --git a/packages/server/src/sdk/app/tables/create.ts b/packages/server/src/sdk/app/tables/create.ts index 0b15cdb15a..22b0b624d0 100644 --- a/packages/server/src/sdk/app/tables/create.ts +++ b/packages/server/src/sdk/app/tables/create.ts @@ -1,4 +1,4 @@ -import { Row, Table } from "@budibase/types" +import { Row, Table, WithoutDocMetadata } from "@budibase/types" import * as external from "./external" import * as internal from "./internal" @@ -7,7 +7,7 @@ import { setPermissions } from "../permissions" import { roles } from "@budibase/backend-core" export async function create( - table: Omit, + table: WithoutDocMetadata, rows?: Row[], userId?: string ): Promise
{ diff --git a/packages/server/src/sdk/app/tables/external/index.ts b/packages/server/src/sdk/app/tables/external/index.ts index 7883a912b5..4e01d18fe9 100644 --- a/packages/server/src/sdk/app/tables/external/index.ts +++ b/packages/server/src/sdk/app/tables/external/index.ts @@ -7,6 +7,7 @@ import { TableRequest, ViewV2, AutoFieldSubType, + WithoutDocMetadata, } from "@budibase/types" import { context, HTTPError } from "@budibase/backend-core" import { @@ -102,7 +103,7 @@ function getDatasourceId(table: Table) { return breakExternalTableId(table._id).datasourceId } -export async function create(table: Omit) { +export async function create(table: WithoutDocMetadata
) { const datasourceId = getDatasourceId(table) const tableToCreate = { ...table, created: true } diff --git a/packages/server/src/sdk/app/tables/internal/index.ts b/packages/server/src/sdk/app/tables/internal/index.ts index e06ece917b..8a072a75d7 100644 --- a/packages/server/src/sdk/app/tables/internal/index.ts +++ b/packages/server/src/sdk/app/tables/internal/index.ts @@ -6,6 +6,7 @@ import { ViewV2, Row, TableSourceType, + WithoutDocMetadata, } from "@budibase/types" import { hasTypeChanged, @@ -25,7 +26,7 @@ import { generateTableID, getRowParams } from "../../../../db/utils" import { quotas } from "@budibase/pro" export async function create( - table: Omit, + table: WithoutDocMetadata
, rows?: Row[], userId?: string ) { diff --git a/packages/worker/src/api/routes/global/tests/roles.spec.ts b/packages/worker/src/api/routes/global/tests/roles.spec.ts index 4e7acb2741..b8ff654055 100644 --- a/packages/worker/src/api/routes/global/tests/roles.spec.ts +++ b/packages/worker/src/api/routes/global/tests/roles.spec.ts @@ -1,6 +1,11 @@ import { structures, TestConfiguration } from "../../../../tests" import { context, db, roles } from "@budibase/backend-core" -import { App, Database, BuiltinPermissionID } from "@budibase/types" +import { + App, + Database, + BuiltinPermissionID, + WithoutDocMetadata, +} from "@budibase/types" jest.mock("@budibase/backend-core", () => { const core = jest.requireActual("@budibase/backend-core") @@ -30,7 +35,7 @@ async function addAppMetadata() { }) } -async function updateAppMetadata(update: Partial>) { +async function updateAppMetadata(update: Partial>) { const app = await appDb.get("app_metadata") await appDb.put({ ...app, From ad0030755a3eda1a824a43930297ec8aef12fe4b Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 14 May 2025 14:58:27 +0200 Subject: [PATCH 23/36] Lint --- packages/server/src/appMigrations/migrations.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/server/src/appMigrations/migrations.ts b/packages/server/src/appMigrations/migrations.ts index 227294e327..315b9f6590 100644 --- a/packages/server/src/appMigrations/migrations.ts +++ b/packages/server/src/appMigrations/migrations.ts @@ -9,10 +9,10 @@ export const MIGRATIONS: AppMigration[] = [ // Migrations will be executed sorted by id { id: "20240604153647_initial_sqs", - func: m20240604153647_initial_sqs + func: m20240604153647_initial_sqs, }, { id: "20250514133719_project_apps", - func: m20250514133719_project_apps + func: m20250514133719_project_apps, }, ] From 93087df445276e176fcc1021bef451c12840a310 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 14 May 2025 16:09:28 +0200 Subject: [PATCH 24/36] Add flag --- packages/server/src/appMigrations/migrations.ts | 3 +++ packages/types/src/sdk/featureFlag.ts | 2 ++ 2 files changed, 5 insertions(+) diff --git a/packages/server/src/appMigrations/migrations.ts b/packages/server/src/appMigrations/migrations.ts index 315b9f6590..a21a719bd0 100644 --- a/packages/server/src/appMigrations/migrations.ts +++ b/packages/server/src/appMigrations/migrations.ts @@ -1,9 +1,11 @@ // This file should never be manually modified, use `yarn add-app-migration` in order to add a new one +import { features } from "@budibase/backend-core" import { AppMigration } from "." import m20240604153647_initial_sqs from "./migrations/20240604153647_initial_sqs" import m20250514133719_project_apps from "./migrations/20250514133719_project_apps" +import { FeatureFlag } from "@budibase/types" export const MIGRATIONS: AppMigration[] = [ // Migrations will be executed sorted by id @@ -14,5 +16,6 @@ export const MIGRATIONS: AppMigration[] = [ { id: "20250514133719_project_apps", func: m20250514133719_project_apps, + disabled: !features.flags.isEnabled(FeatureFlag.PROJECT_APPS), }, ] diff --git a/packages/types/src/sdk/featureFlag.ts b/packages/types/src/sdk/featureFlag.ts index c8857f7e11..ef0aa897b1 100644 --- a/packages/types/src/sdk/featureFlag.ts +++ b/packages/types/src/sdk/featureFlag.ts @@ -4,6 +4,7 @@ export enum FeatureFlag { AI_JS_GENERATION = "AI_JS_GENERATION", AI_TABLE_GENERATION = "AI_TABLE_GENERATION", AI_AGENTS = "AI_AGENTS", + PROJECT_APPS = "PROJECT_APPS", // Account-portal DIRECT_LOGIN_TO_ACCOUNT_PORTAL = "DIRECT_LOGIN_TO_ACCOUNT_PORTAL", @@ -14,6 +15,7 @@ export const FeatureFlagDefaults: Record = { [FeatureFlag.AI_JS_GENERATION]: false, [FeatureFlag.AI_TABLE_GENERATION]: false, [FeatureFlag.AI_AGENTS]: false, + [FeatureFlag.PROJECT_APPS]: false, // Account-portal [FeatureFlag.DIRECT_LOGIN_TO_ACCOUNT_PORTAL]: false, From b09dcf1839e3c5d0abe49e058d8a928cc8d01773 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 14 May 2025 16:23:35 +0200 Subject: [PATCH 25/36] Sync disable flag --- packages/backend-core/src/features/features.ts | 8 +++++--- packages/server/src/appMigrations/migrations.ts | 6 +++++- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/packages/backend-core/src/features/features.ts b/packages/backend-core/src/features/features.ts index a76ffe8a7b..67ed180ba3 100644 --- a/packages/backend-core/src/features/features.ts +++ b/packages/backend-core/src/features/features.ts @@ -53,6 +53,10 @@ export function parseEnvFlags(flags: string): EnvFlagEntry[] { return result } +export function getEnvFlags() { + return parseEnvFlags(env.TENANT_FEATURE_FLAGS || "") +} + export class FlagSet { // This is used to safely cache flags sets in the current request context. // Because multiple sets could theoretically exist, we don't want the cache of @@ -89,9 +93,7 @@ export class FlagSet { const currentTenantId = context.getTenantId() const specificallySetFalse = new Set() - for (const { tenantId, key, value } of parseEnvFlags( - env.TENANT_FEATURE_FLAGS || "" - )) { + for (const { tenantId, key, value } of getEnvFlags()) { if (!tenantId || (tenantId !== "*" && tenantId !== currentTenantId)) { continue } diff --git a/packages/server/src/appMigrations/migrations.ts b/packages/server/src/appMigrations/migrations.ts index a21a719bd0..7535c29e39 100644 --- a/packages/server/src/appMigrations/migrations.ts +++ b/packages/server/src/appMigrations/migrations.ts @@ -16,6 +16,10 @@ export const MIGRATIONS: AppMigration[] = [ { id: "20250514133719_project_apps", func: m20250514133719_project_apps, - disabled: !features.flags.isEnabled(FeatureFlag.PROJECT_APPS), + // Disabling it, enabling it via env variables to enable development. + // Using the existing flag system would require async checks and we could run to race conditions, so this keeps is simple + disabled: !features + .getEnvFlags() + .some(f => f.key === FeatureFlag.PROJECT_APPS && f.value === true), }, ] From 9280be30a5ab9dc86cc55ebb7ee9d3483874aa76 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 14 May 2025 16:30:39 +0200 Subject: [PATCH 26/36] Return both screens and projectApps --- packages/builder/src/stores/builder/screens.ts | 9 ++++++++- packages/server/src/api/controllers/application.ts | 11 ++++++++++- packages/types/src/api/web/app/application.ts | 1 + 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/packages/builder/src/stores/builder/screens.ts b/packages/builder/src/stores/builder/screens.ts index ccf516f2f7..ffe07e978a 100644 --- a/packages/builder/src/stores/builder/screens.ts +++ b/packages/builder/src/stores/builder/screens.ts @@ -6,6 +6,7 @@ import { findAllMatchingComponents } from "@/helpers/components" import { appStore, componentStore, + flags, layoutStore, navigationStore, previewStore, @@ -18,11 +19,13 @@ import { Component, ComponentDefinition, DeleteScreenResponse, + FeatureFlag, FetchAppPackageResponse, SaveScreenResponse, Screen, ScreenVariant, } from "@budibase/types" +import { featureFlag } from "@/helpers" interface ScreenState { screens: Screen[] @@ -87,9 +90,13 @@ export class ScreenStore extends BudiStore { * @param {FetchAppPackageResponse} pkg */ syncAppScreens(pkg: FetchAppPackageResponse) { + let screens = [...pkg.screens] + if (featureFlag.isEnabled(FeatureFlag.PROJECT_APPS)) { + screens = [...pkg.projectApps.flatMap(p => p.screens)] + } this.update(state => ({ ...state, - screens: [...pkg.projectApps.flatMap(p => p.screens)], + screens, })) } diff --git a/packages/server/src/api/controllers/application.ts b/packages/server/src/api/controllers/application.ts index 432638c0d7..a87cdc636e 100644 --- a/packages/server/src/api/controllers/application.ts +++ b/packages/server/src/api/controllers/application.ts @@ -26,6 +26,7 @@ import { docIds, env as envCore, events, + features, objectStore, roles, tenancy, @@ -69,6 +70,8 @@ import { UnpublishAppResponse, SetRevertableAppVersionResponse, ErrorCode, + FeatureFlag, + ProjectAppResponse, } from "@budibase/types" import { BASE_LAYOUT_PROP_IDS } from "../../constants/layouts" import sdk from "../../sdk" @@ -276,7 +279,12 @@ export async function fetchAppPackage( screens = await accessController.checkScreensAccess(screens, userRoleId) } - const projectApps = await extractScreensByProjectApp(screens) + let projectApps: FetchAppPackageResponse["projectApps"] = [] + + if (await features.flags.isEnabled(FeatureFlag.PROJECT_APPS)) { + projectApps = await extractScreensByProjectApp(screens) + screens = [] + } const clientLibPath = objectStore.clientLibraryUrl( ctx.params.appId, @@ -287,6 +295,7 @@ export async function fetchAppPackage( application: { ...application, upgradableVersion: envCore.VERSION }, licenseType: license?.plan.type || PlanType.FREE, projectApps, + screens, layouts, clientLibPath, hasLock: await doesUserHaveLock(application.appId, ctx.user), diff --git a/packages/types/src/api/web/app/application.ts b/packages/types/src/api/web/app/application.ts index df5e443c93..2380e363d9 100644 --- a/packages/types/src/api/web/app/application.ts +++ b/packages/types/src/api/web/app/application.ts @@ -42,6 +42,7 @@ interface ProjectAppResponse extends ProjectApp { export interface FetchAppPackageResponse { application: App licenseType: PlanType + screens: Screen[] projectApps: ProjectAppResponse[] layouts: Layout[] clientLibPath: string From 9cc2143dab4df1e361235fa0c2ca18f9a5ff7813 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 14 May 2025 16:34:47 +0200 Subject: [PATCH 27/36] Use flags on creation --- .../server/src/api/controllers/application.ts | 19 +++++++++++-------- packages/server/src/constants/screens.ts | 4 +++- packages/types/src/documents/app/screen.ts | 2 +- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/packages/server/src/api/controllers/application.ts b/packages/server/src/api/controllers/application.ts index a87cdc636e..938a41ca10 100644 --- a/packages/server/src/api/controllers/application.ts +++ b/packages/server/src/api/controllers/application.ts @@ -71,7 +71,6 @@ import { SetRevertableAppVersionResponse, ErrorCode, FeatureFlag, - ProjectAppResponse, } from "@budibase/types" import { BASE_LAYOUT_PROP_IDS } from "../../constants/layouts" import sdk from "../../sdk" @@ -185,15 +184,19 @@ async function addSampleDataDocs() { async function addSampleDataScreen() { const db = context.getAppDB() - const appMetadata = await sdk.applications.metadata.get() + let projectAppId: string | undefined + if (await features.isEnabled(FeatureFlag.PROJECT_APPS)) { + const appMetadata = await sdk.applications.metadata.get() - const projectApp = await sdk.projectApps.create({ - name: appMetadata.name, - urlPrefix: "/", - icon: "Monitoring", - }) + const projectApp = await sdk.projectApps.create({ + name: appMetadata.name, + urlPrefix: "/", + icon: "Monitoring", + }) + projectAppId = projectApp._id! + } - let screen = createSampleDataTableScreen(projectApp._id) + let screen = await createSampleDataTableScreen(projectAppId) screen._id = generateScreenID() await db.put(screen) } diff --git a/packages/server/src/constants/screens.ts b/packages/server/src/constants/screens.ts index 8a94ccf205..e206da0f27 100644 --- a/packages/server/src/constants/screens.ts +++ b/packages/server/src/constants/screens.ts @@ -2,7 +2,9 @@ import { Screen } from "@budibase/types" export const SAMPLE_DATA_SCREEN_NAME = "sample-data-inventory-screen" -export function createSampleDataTableScreen(projectAppId: string): Screen { +export function createSampleDataTableScreen( + projectAppId: string | undefined +): Screen { return { showNavigation: true, width: "Large", diff --git a/packages/types/src/documents/app/screen.ts b/packages/types/src/documents/app/screen.ts index 79f34cac55..1ae03cf76c 100644 --- a/packages/types/src/documents/app/screen.ts +++ b/packages/types/src/documents/app/screen.ts @@ -29,7 +29,7 @@ export interface Screen extends Document { pluginAdded?: boolean onLoad?: EventHandler[] variant?: ScreenVariant - projectAppId: string + projectAppId?: string } export interface ScreenRoutesViewOutput extends Document { From 44a00bd04c01039c665610483d67263898220b17 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 14 May 2025 16:40:21 +0200 Subject: [PATCH 28/36] Don't use projectAppId from builder --- .../src/stores/builder/tests/screens.test.js | 24 +++++-------------- .../src/templates/screenTemplating/Screen.ts | 1 - 2 files changed, 6 insertions(+), 19 deletions(-) diff --git a/packages/builder/src/stores/builder/tests/screens.test.js b/packages/builder/src/stores/builder/tests/screens.test.js index 61e5c88c12..ea916c8d59 100644 --- a/packages/builder/src/stores/builder/tests/screens.test.js +++ b/packages/builder/src/stores/builder/tests/screens.test.js @@ -92,9 +92,7 @@ describe("Screens store", () => { .fill() .map(() => getScreenFixture().json()) - bb.screenStore.syncAppScreens({ - projectApps: [{ screens }], - }) + bb.screenStore.syncAppScreens({ screens }) expect(bb.store.screens).toStrictEqual(screens) }) @@ -106,9 +104,7 @@ describe("Screens store", () => { .fill() .map(() => getScreenFixture().json()) - bb.screenStore.syncAppScreens({ - projectApps: [{ screens }], - }) + bb.screenStore.syncAppScreens({ screens }) expect(bb.store.screens).toStrictEqual(screens) bb.screenStore.update(state => ({ @@ -126,9 +122,7 @@ describe("Screens store", () => { .fill() .map(() => getScreenFixture().json()) - bb.screenStore.syncAppScreens({ - projectApps: [{ screens }], - }) + bb.screenStore.syncAppScreens({ screens }) expect(bb.store.screens.length).toBe(2) bb.screenStore.select(screens[0]._id) @@ -141,9 +135,7 @@ describe("Screens store", () => { .fill() .map(() => getScreenFixture().json()) - bb.screenStore.syncAppScreens({ - projectApps: [{ screens }], - }) + bb.screenStore.syncAppScreens({ screens }) expect(bb.store.screens.length).toBe(2) bb.screenStore.select("screen_abc") @@ -418,9 +410,7 @@ describe("Screens store", () => { screenDoc._json._id = existingDocId return screenDoc.json() }) - bb.screenStore.syncAppScreens({ - projectApps: [{ screens: existingScreens }], - }) + bb.screenStore.syncAppScreens({ screens: existingScreens }) bb.screenStore.replace() @@ -438,9 +428,7 @@ describe("Screens store", () => { screenDoc._json._id = existingDocId return screenDoc.json() }) - bb.screenStore.syncAppScreens({ - projectApps: [{ screens: existingScreens }], - }) + bb.screenStore.syncAppScreens({ screens: existingScreens }) bb.screenStore.replace(existingScreens[1]._id) diff --git a/packages/builder/src/templates/screenTemplating/Screen.ts b/packages/builder/src/templates/screenTemplating/Screen.ts index a7e8d4e439..0749a6ea8b 100644 --- a/packages/builder/src/templates/screenTemplating/Screen.ts +++ b/packages/builder/src/templates/screenTemplating/Screen.ts @@ -31,7 +31,6 @@ export class Screen extends BaseStructure { homeScreen: false, }, name: "screen-id", - projectAppId: "TODO", }) } From f81281e2ca7b59bf253950dd6147c44ea434dece Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 14 May 2025 16:43:10 +0200 Subject: [PATCH 29/36] Lint --- packages/builder/src/stores/builder/screens.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/builder/src/stores/builder/screens.ts b/packages/builder/src/stores/builder/screens.ts index ffe07e978a..82a8c340f1 100644 --- a/packages/builder/src/stores/builder/screens.ts +++ b/packages/builder/src/stores/builder/screens.ts @@ -6,7 +6,6 @@ import { findAllMatchingComponents } from "@/helpers/components" import { appStore, componentStore, - flags, layoutStore, navigationStore, previewStore, From ebf07aff2868544112871bf0c4cd53f0c06098cf Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Fri, 16 May 2025 16:27:21 +0200 Subject: [PATCH 30/36] Rename files from projectApp to workspaceApp --- .../src/api/controllers/{projectApp.ts => workspaceApp.ts} | 0 packages/server/src/api/routes/index.ts | 2 +- .../server/src/api/routes/{projectApp.ts => workspaceApp.ts} | 2 +- packages/server/src/appMigrations/migrations.ts | 2 +- ...4133719_project_apps.ts => 20250514133719_workspace_apps.ts} | 0 .../server/src/sdk/app/{projectApps => workspaceApps}/index.ts | 0 packages/server/src/sdk/index.ts | 2 +- packages/types/src/api/web/app/index.ts | 2 +- .../types/src/api/web/app/{projectApp.ts => workspaceApp.ts} | 0 packages/types/src/documents/app/index.ts | 2 +- .../types/src/documents/app/{projectApp.ts => workspaceApp.ts} | 0 11 files changed, 6 insertions(+), 6 deletions(-) rename packages/server/src/api/controllers/{projectApp.ts => workspaceApp.ts} (100%) rename packages/server/src/api/routes/{projectApp.ts => workspaceApp.ts} (95%) rename packages/server/src/appMigrations/migrations/{20250514133719_project_apps.ts => 20250514133719_workspace_apps.ts} (100%) rename packages/server/src/sdk/app/{projectApps => workspaceApps}/index.ts (100%) rename packages/types/src/api/web/app/{projectApp.ts => workspaceApp.ts} (100%) rename packages/types/src/documents/app/{projectApp.ts => workspaceApp.ts} (100%) diff --git a/packages/server/src/api/controllers/projectApp.ts b/packages/server/src/api/controllers/workspaceApp.ts similarity index 100% rename from packages/server/src/api/controllers/projectApp.ts rename to packages/server/src/api/controllers/workspaceApp.ts diff --git a/packages/server/src/api/routes/index.ts b/packages/server/src/api/routes/index.ts index 54b02a2a90..d8570844d1 100644 --- a/packages/server/src/api/routes/index.ts +++ b/packages/server/src/api/routes/index.ts @@ -32,7 +32,7 @@ import rowActionRoutes from "./rowAction" import oauth2Routes from "./oauth2" import featuresRoutes from "./features" import aiRoutes from "./ai" -import projectApps from "./projectApp" +import projectApps from "./workspaceApp" export { default as staticRoutes } from "./static" export { default as publicRoutes } from "./public" diff --git a/packages/server/src/api/routes/projectApp.ts b/packages/server/src/api/routes/workspaceApp.ts similarity index 95% rename from packages/server/src/api/routes/projectApp.ts rename to packages/server/src/api/routes/workspaceApp.ts index 0be6e87f1f..061b656186 100644 --- a/packages/server/src/api/routes/projectApp.ts +++ b/packages/server/src/api/routes/workspaceApp.ts @@ -3,7 +3,7 @@ import { PermissionType } from "@budibase/types" import { middleware } from "@budibase/backend-core" import authorized from "../../middleware/authorized" -import * as controller from "../controllers/projectApp" +import * as controller from "../controllers/workspaceApp" import Joi from "joi" const baseSchema = { diff --git a/packages/server/src/appMigrations/migrations.ts b/packages/server/src/appMigrations/migrations.ts index 7535c29e39..bb3216b613 100644 --- a/packages/server/src/appMigrations/migrations.ts +++ b/packages/server/src/appMigrations/migrations.ts @@ -4,7 +4,7 @@ import { features } from "@budibase/backend-core" import { AppMigration } from "." import m20240604153647_initial_sqs from "./migrations/20240604153647_initial_sqs" -import m20250514133719_project_apps from "./migrations/20250514133719_project_apps" +import m20250514133719_project_apps from "./migrations/20250514133719_workspace_apps" import { FeatureFlag } from "@budibase/types" export const MIGRATIONS: AppMigration[] = [ diff --git a/packages/server/src/appMigrations/migrations/20250514133719_project_apps.ts b/packages/server/src/appMigrations/migrations/20250514133719_workspace_apps.ts similarity index 100% rename from packages/server/src/appMigrations/migrations/20250514133719_project_apps.ts rename to packages/server/src/appMigrations/migrations/20250514133719_workspace_apps.ts diff --git a/packages/server/src/sdk/app/projectApps/index.ts b/packages/server/src/sdk/app/workspaceApps/index.ts similarity index 100% rename from packages/server/src/sdk/app/projectApps/index.ts rename to packages/server/src/sdk/app/workspaceApps/index.ts diff --git a/packages/server/src/sdk/index.ts b/packages/server/src/sdk/index.ts index 447b8e4572..ec483678ed 100644 --- a/packages/server/src/sdk/index.ts +++ b/packages/server/src/sdk/index.ts @@ -15,7 +15,7 @@ import * as screens from "./app/screens" import * as common from "./app/common" import * as oauth2 from "./app/oauth2" import * as ai from "./app/ai" -import * as projectApps from "./app/projectApps" +import * as projectApps from "./app/workspaceApps" const sdk = { backups, diff --git a/packages/types/src/api/web/app/index.ts b/packages/types/src/api/web/app/index.ts index 3c7d6e92f8..65e3269c1b 100644 --- a/packages/types/src/api/web/app/index.ts +++ b/packages/types/src/api/web/app/index.ts @@ -11,7 +11,7 @@ export * from "./layout" export * from "./metadata" export * from "./oauth2" export * from "./permission" -export * from "./projectApp" +export * from "./workspaceApp" export * from "./query" export * from "./role" export * from "./rowAction" diff --git a/packages/types/src/api/web/app/projectApp.ts b/packages/types/src/api/web/app/workspaceApp.ts similarity index 100% rename from packages/types/src/api/web/app/projectApp.ts rename to packages/types/src/api/web/app/workspaceApp.ts diff --git a/packages/types/src/documents/app/index.ts b/packages/types/src/documents/app/index.ts index 2e5ee05360..6edb10954b 100644 --- a/packages/types/src/documents/app/index.ts +++ b/packages/types/src/documents/app/index.ts @@ -9,7 +9,7 @@ export * from "./layout" export * from "./links" export * from "./metadata" export * from "./oauth2" -export * from "./projectApp" +export * from "./workspaceApp" export * from "./query" export * from "./role" export * from "./row" diff --git a/packages/types/src/documents/app/projectApp.ts b/packages/types/src/documents/app/workspaceApp.ts similarity index 100% rename from packages/types/src/documents/app/projectApp.ts rename to packages/types/src/documents/app/workspaceApp.ts From 70a3f70f866bfa2a45fe14d59c13b56679ac29be Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Fri, 16 May 2025 16:27:59 +0200 Subject: [PATCH 31/36] Rename flag --- packages/builder/src/stores/builder/screens.ts | 2 +- packages/server/src/api/controllers/application.ts | 4 ++-- packages/server/src/appMigrations/migrations.ts | 2 +- packages/types/src/sdk/featureFlag.ts | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/builder/src/stores/builder/screens.ts b/packages/builder/src/stores/builder/screens.ts index 82a8c340f1..08680f1235 100644 --- a/packages/builder/src/stores/builder/screens.ts +++ b/packages/builder/src/stores/builder/screens.ts @@ -90,7 +90,7 @@ export class ScreenStore extends BudiStore { */ syncAppScreens(pkg: FetchAppPackageResponse) { let screens = [...pkg.screens] - if (featureFlag.isEnabled(FeatureFlag.PROJECT_APPS)) { + if (featureFlag.isEnabled(FeatureFlag.WORKSPACE_APPS)) { screens = [...pkg.projectApps.flatMap(p => p.screens)] } this.update(state => ({ diff --git a/packages/server/src/api/controllers/application.ts b/packages/server/src/api/controllers/application.ts index 938a41ca10..ec10e62119 100644 --- a/packages/server/src/api/controllers/application.ts +++ b/packages/server/src/api/controllers/application.ts @@ -185,7 +185,7 @@ async function addSampleDataDocs() { async function addSampleDataScreen() { const db = context.getAppDB() let projectAppId: string | undefined - if (await features.isEnabled(FeatureFlag.PROJECT_APPS)) { + if (await features.isEnabled(FeatureFlag.WORKSPACE_APPS)) { const appMetadata = await sdk.applications.metadata.get() const projectApp = await sdk.projectApps.create({ @@ -284,7 +284,7 @@ export async function fetchAppPackage( let projectApps: FetchAppPackageResponse["projectApps"] = [] - if (await features.flags.isEnabled(FeatureFlag.PROJECT_APPS)) { + if (await features.flags.isEnabled(FeatureFlag.WORKSPACE_APPS)) { projectApps = await extractScreensByProjectApp(screens) screens = [] } diff --git a/packages/server/src/appMigrations/migrations.ts b/packages/server/src/appMigrations/migrations.ts index bb3216b613..404a634159 100644 --- a/packages/server/src/appMigrations/migrations.ts +++ b/packages/server/src/appMigrations/migrations.ts @@ -20,6 +20,6 @@ export const MIGRATIONS: AppMigration[] = [ // Using the existing flag system would require async checks and we could run to race conditions, so this keeps is simple disabled: !features .getEnvFlags() - .some(f => f.key === FeatureFlag.PROJECT_APPS && f.value === true), + .some(f => f.key === FeatureFlag.WORKSPACE_APPS && f.value === true), }, ] diff --git a/packages/types/src/sdk/featureFlag.ts b/packages/types/src/sdk/featureFlag.ts index ef0aa897b1..74ac3b0f67 100644 --- a/packages/types/src/sdk/featureFlag.ts +++ b/packages/types/src/sdk/featureFlag.ts @@ -4,7 +4,7 @@ export enum FeatureFlag { AI_JS_GENERATION = "AI_JS_GENERATION", AI_TABLE_GENERATION = "AI_TABLE_GENERATION", AI_AGENTS = "AI_AGENTS", - PROJECT_APPS = "PROJECT_APPS", + WORKSPACE_APPS = "WORKSPACE_APPS", // Account-portal DIRECT_LOGIN_TO_ACCOUNT_PORTAL = "DIRECT_LOGIN_TO_ACCOUNT_PORTAL", @@ -15,7 +15,7 @@ export const FeatureFlagDefaults: Record = { [FeatureFlag.AI_JS_GENERATION]: false, [FeatureFlag.AI_TABLE_GENERATION]: false, [FeatureFlag.AI_AGENTS]: false, - [FeatureFlag.PROJECT_APPS]: false, + [FeatureFlag.WORKSPACE_APPS]: false, // Account-portal [FeatureFlag.DIRECT_LOGIN_TO_ACCOUNT_PORTAL]: false, From f040a31323420ab3bbc416c31baec6735c93711b Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Fri, 16 May 2025 16:28:30 +0200 Subject: [PATCH 32/36] More renames --- packages/server/src/appMigrations/migrations.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/server/src/appMigrations/migrations.ts b/packages/server/src/appMigrations/migrations.ts index 404a634159..3af8d2b891 100644 --- a/packages/server/src/appMigrations/migrations.ts +++ b/packages/server/src/appMigrations/migrations.ts @@ -4,7 +4,7 @@ import { features } from "@budibase/backend-core" import { AppMigration } from "." import m20240604153647_initial_sqs from "./migrations/20240604153647_initial_sqs" -import m20250514133719_project_apps from "./migrations/20250514133719_workspace_apps" +import m20250514133719_workspace_apps from "./migrations/20250514133719_workspace_apps" import { FeatureFlag } from "@budibase/types" export const MIGRATIONS: AppMigration[] = [ @@ -14,8 +14,8 @@ export const MIGRATIONS: AppMigration[] = [ func: m20240604153647_initial_sqs, }, { - id: "20250514133719_project_apps", - func: m20250514133719_project_apps, + id: "m20250514133719_workspace_apps", + func: m20250514133719_workspace_apps, // Disabling it, enabling it via env variables to enable development. // Using the existing flag system would require async checks and we could run to race conditions, so this keeps is simple disabled: !features From 132949b276e0ffb2dcae4a422ac7044fca64ab25 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Fri, 16 May 2025 16:30:23 +0200 Subject: [PATCH 33/36] More renames --- packages/server/src/api/controllers/application.ts | 4 ++-- packages/server/src/api/controllers/workspaceApp.ts | 6 +++--- .../migrations/20250514133719_workspace_apps.ts | 4 ++-- packages/server/src/sdk/index.ts | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/server/src/api/controllers/application.ts b/packages/server/src/api/controllers/application.ts index ec10e62119..9fe7da5036 100644 --- a/packages/server/src/api/controllers/application.ts +++ b/packages/server/src/api/controllers/application.ts @@ -188,7 +188,7 @@ async function addSampleDataScreen() { if (await features.isEnabled(FeatureFlag.WORKSPACE_APPS)) { const appMetadata = await sdk.applications.metadata.get() - const projectApp = await sdk.projectApps.create({ + const projectApp = await sdk.workspaceApps.create({ name: appMetadata.name, urlPrefix: "/", icon: "Monitoring", @@ -310,7 +310,7 @@ async function extractScreensByProjectApp( ): Promise { const result: FetchAppPackageResponse["projectApps"] = [] - const projectApps = await sdk.projectApps.fetch() + const projectApps = await sdk.workspaceApps.fetch() const screensByProjectApp = groupBy(s => s.projectAppId, screens) for (const projectAppId of Object.keys(screensByProjectApp)) { diff --git a/packages/server/src/api/controllers/workspaceApp.ts b/packages/server/src/api/controllers/workspaceApp.ts index ba8e3a7cbe..b61fde546c 100644 --- a/packages/server/src/api/controllers/workspaceApp.ts +++ b/packages/server/src/api/controllers/workspaceApp.ts @@ -31,7 +31,7 @@ export async function create( iconColor: body.iconColor, } - const projectApp = await sdk.projectApps.create(newProjectApp) + const projectApp = await sdk.workspaceApps.create(newProjectApp) ctx.status = 201 ctx.body = { projectApp: toProjectAppResponse(projectApp), @@ -56,7 +56,7 @@ export async function edit( iconColor: body.iconColor, } - const projectApp = await sdk.projectApps.update(toUpdate) + const projectApp = await sdk.workspaceApps.update(toUpdate) ctx.body = { projectApp: toProjectAppResponse(projectApp), } @@ -65,6 +65,6 @@ export async function edit( export async function remove(ctx: Ctx) { const { id, rev } = ctx.params - await sdk.projectApps.remove(id, rev) + await sdk.workspaceApps.remove(id, rev) ctx.status = 204 } diff --git a/packages/server/src/appMigrations/migrations/20250514133719_workspace_apps.ts b/packages/server/src/appMigrations/migrations/20250514133719_workspace_apps.ts index 29dfb88bf7..bd83941c92 100644 --- a/packages/server/src/appMigrations/migrations/20250514133719_workspace_apps.ts +++ b/packages/server/src/appMigrations/migrations/20250514133719_workspace_apps.ts @@ -6,10 +6,10 @@ const migration = async () => { const screens = await sdk.screens.fetch() const application = await sdk.applications.metadata.get() - const allProjectApps = await sdk.projectApps.fetch() + const allProjectApps = await sdk.workspaceApps.fetch() let projectAppId = allProjectApps.find(p => p.name === application.name)?._id if (!projectAppId) { - const projectApp = await sdk.projectApps.create({ + const projectApp = await sdk.workspaceApps.create({ name: application.name, urlPrefix: "/", icon: "Monitoring", diff --git a/packages/server/src/sdk/index.ts b/packages/server/src/sdk/index.ts index ec483678ed..6dc08ded54 100644 --- a/packages/server/src/sdk/index.ts +++ b/packages/server/src/sdk/index.ts @@ -15,7 +15,7 @@ import * as screens from "./app/screens" import * as common from "./app/common" import * as oauth2 from "./app/oauth2" import * as ai from "./app/ai" -import * as projectApps from "./app/workspaceApps" +import * as workspaceApps from "./app/workspaceApps" const sdk = { backups, @@ -35,7 +35,7 @@ const sdk = { common, oauth2, ai, - projectApps, + workspaceApps, } // default export for TS From cdd34a23b8a1d6c4f2c436880263bcfb5e142ffc Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Fri, 16 May 2025 16:32:56 +0200 Subject: [PATCH 34/36] Renames --- .../builder/src/stores/builder/screens.ts | 2 +- .../server/src/api/controllers/application.ts | 20 ++++++------- .../src/api/controllers/workspaceApp.ts | 24 ++++++++-------- packages/server/src/api/routes/index.ts | 4 +-- .../server/src/api/routes/workspaceApp.ts | 6 ++-- .../20250514133719_workspace_apps.ts | 4 +-- .../server/src/sdk/app/workspaceApps/index.ts | 28 +++++++++---------- packages/types/src/api/web/app/application.ts | 6 ++-- .../types/src/api/web/app/workspaceApp.ts | 4 +-- .../types/src/documents/app/workspaceApp.ts | 2 +- 10 files changed, 50 insertions(+), 50 deletions(-) diff --git a/packages/builder/src/stores/builder/screens.ts b/packages/builder/src/stores/builder/screens.ts index 08680f1235..bec13426d3 100644 --- a/packages/builder/src/stores/builder/screens.ts +++ b/packages/builder/src/stores/builder/screens.ts @@ -91,7 +91,7 @@ export class ScreenStore extends BudiStore { syncAppScreens(pkg: FetchAppPackageResponse) { let screens = [...pkg.screens] if (featureFlag.isEnabled(FeatureFlag.WORKSPACE_APPS)) { - screens = [...pkg.projectApps.flatMap(p => p.screens)] + screens = [...pkg.workspaceApps.flatMap(p => p.screens)] } this.update(state => ({ ...state, diff --git a/packages/server/src/api/controllers/application.ts b/packages/server/src/api/controllers/application.ts index 9fe7da5036..6dbb09f7fd 100644 --- a/packages/server/src/api/controllers/application.ts +++ b/packages/server/src/api/controllers/application.ts @@ -188,12 +188,12 @@ async function addSampleDataScreen() { if (await features.isEnabled(FeatureFlag.WORKSPACE_APPS)) { const appMetadata = await sdk.applications.metadata.get() - const projectApp = await sdk.workspaceApps.create({ + const workspaceApp = await sdk.workspaceApps.create({ name: appMetadata.name, urlPrefix: "/", icon: "Monitoring", }) - projectAppId = projectApp._id! + projectAppId = workspaceApp._id! } let screen = await createSampleDataTableScreen(projectAppId) @@ -282,10 +282,10 @@ export async function fetchAppPackage( screens = await accessController.checkScreensAccess(screens, userRoleId) } - let projectApps: FetchAppPackageResponse["projectApps"] = [] + let workspaceApps: FetchAppPackageResponse["workspaceApps"] = [] if (await features.flags.isEnabled(FeatureFlag.WORKSPACE_APPS)) { - projectApps = await extractScreensByProjectApp(screens) + workspaceApps = await extractScreensByProjectApp(screens) screens = [] } @@ -297,7 +297,7 @@ export async function fetchAppPackage( ctx.body = { application: { ...application, upgradableVersion: envCore.VERSION }, licenseType: license?.plan.type || PlanType.FREE, - projectApps, + workspaceApps, screens, layouts, clientLibPath, @@ -307,17 +307,17 @@ export async function fetchAppPackage( async function extractScreensByProjectApp( screens: Screen[] -): Promise { - const result: FetchAppPackageResponse["projectApps"] = [] +): Promise { + const result: FetchAppPackageResponse["workspaceApps"] = [] - const projectApps = await sdk.workspaceApps.fetch() + const workspaceApps = await sdk.workspaceApps.fetch() const screensByProjectApp = groupBy(s => s.projectAppId, screens) for (const projectAppId of Object.keys(screensByProjectApp)) { - const projectApp = projectApps.find(p => p._id === projectAppId) + const workspaceApp = workspaceApps.find(p => p._id === projectAppId) result.push({ - ...projectApp!, + ...workspaceApp!, screens: screensByProjectApp[projectAppId], }) } diff --git a/packages/server/src/api/controllers/workspaceApp.ts b/packages/server/src/api/controllers/workspaceApp.ts index b61fde546c..90644e33d8 100644 --- a/packages/server/src/api/controllers/workspaceApp.ts +++ b/packages/server/src/api/controllers/workspaceApp.ts @@ -2,21 +2,21 @@ import { Ctx, InsertProjectAppRequest, InsertProjectAppResponse, - ProjectApp, + WorkspaceApp, ProjectAppResponse, UpdateProjectAppRequest, UpdateProjectAppResponse, } from "@budibase/types" import sdk from "../../sdk" -function toProjectAppResponse(projectApp: ProjectApp): ProjectAppResponse { +function toProjectAppResponse(workspaceApp: WorkspaceApp): ProjectAppResponse { return { - _id: projectApp._id!, - _rev: projectApp._rev!, - name: projectApp.name, - urlPrefix: projectApp.urlPrefix, - icon: projectApp.icon, - iconColor: projectApp.iconColor, + _id: workspaceApp._id!, + _rev: workspaceApp._rev!, + name: workspaceApp.name, + urlPrefix: workspaceApp.urlPrefix, + icon: workspaceApp.icon, + iconColor: workspaceApp.iconColor, } } @@ -31,10 +31,10 @@ export async function create( iconColor: body.iconColor, } - const projectApp = await sdk.workspaceApps.create(newProjectApp) + const workspaceApp = await sdk.workspaceApps.create(newProjectApp) ctx.status = 201 ctx.body = { - projectApp: toProjectAppResponse(projectApp), + workspaceApp: toProjectAppResponse(workspaceApp), } } @@ -56,9 +56,9 @@ export async function edit( iconColor: body.iconColor, } - const projectApp = await sdk.workspaceApps.update(toUpdate) + const workspaceApp = await sdk.workspaceApps.update(toUpdate) ctx.body = { - projectApp: toProjectAppResponse(projectApp), + workspaceApp: toProjectAppResponse(workspaceApp), } } diff --git a/packages/server/src/api/routes/index.ts b/packages/server/src/api/routes/index.ts index d8570844d1..8dc2132335 100644 --- a/packages/server/src/api/routes/index.ts +++ b/packages/server/src/api/routes/index.ts @@ -32,7 +32,7 @@ import rowActionRoutes from "./rowAction" import oauth2Routes from "./oauth2" import featuresRoutes from "./features" import aiRoutes from "./ai" -import projectApps from "./workspaceApp" +import workspaceApps from "./workspaceApp" export { default as staticRoutes } from "./static" export { default as publicRoutes } from "./public" @@ -73,7 +73,7 @@ export const mainRoutes: Router[] = [ rowActionRoutes, oauth2Routes, featuresRoutes, - projectApps, + workspaceApps, // these need to be handled last as they still use /api/:tableId // this could be breaking as koa may recognise other routes as this tableRoutes, diff --git a/packages/server/src/api/routes/workspaceApp.ts b/packages/server/src/api/routes/workspaceApp.ts index 061b656186..e109ff24fd 100644 --- a/packages/server/src/api/routes/workspaceApp.ts +++ b/packages/server/src/api/routes/workspaceApp.ts @@ -32,19 +32,19 @@ function projectAppValidator( const router: Router = new Router() router.post( - "/api/projectApp", + "/api/workspaceApp", authorized(PermissionType.BUILDER), projectAppValidator(insertSchema), controller.create ) router.put( - "/api/projectApp/:id", + "/api/workspaceApp/:id", authorized(PermissionType.BUILDER), projectAppValidator(updateSchema), controller.edit ) router.delete( - "/api/projectApp/:id/:rev", + "/api/workspaceApp/:id/:rev", authorized(PermissionType.BUILDER), controller.remove ) diff --git a/packages/server/src/appMigrations/migrations/20250514133719_workspace_apps.ts b/packages/server/src/appMigrations/migrations/20250514133719_workspace_apps.ts index bd83941c92..a23dfa065e 100644 --- a/packages/server/src/appMigrations/migrations/20250514133719_workspace_apps.ts +++ b/packages/server/src/appMigrations/migrations/20250514133719_workspace_apps.ts @@ -9,12 +9,12 @@ const migration = async () => { const allProjectApps = await sdk.workspaceApps.fetch() let projectAppId = allProjectApps.find(p => p.name === application.name)?._id if (!projectAppId) { - const projectApp = await sdk.workspaceApps.create({ + const workspaceApp = await sdk.workspaceApps.create({ name: application.name, urlPrefix: "/", icon: "Monitoring", }) - projectAppId = projectApp._id + projectAppId = workspaceApp._id } const db = context.getAppDB() diff --git a/packages/server/src/sdk/app/workspaceApps/index.ts b/packages/server/src/sdk/app/workspaceApps/index.ts index e19243223d..b786d9b66b 100644 --- a/packages/server/src/sdk/app/workspaceApps/index.ts +++ b/packages/server/src/sdk/app/workspaceApps/index.ts @@ -1,7 +1,7 @@ import { context, docIds, HTTPError, utils } from "@budibase/backend-core" import { DocumentType, - ProjectApp, + WorkspaceApp, SEPARATOR, WithoutDocMetadata, } from "@budibase/types" @@ -14,9 +14,9 @@ async function guardName(name: string, id?: string) { } } -export async function fetch(): Promise { +export async function fetch(): Promise { const db = context.getAppDB() - const docs = await db.allDocs( + const docs = await db.allDocs( docIds.getProjectAppParams(null, { include_docs: true }) ) const result = docs.rows.map(r => ({ @@ -27,40 +27,40 @@ export async function fetch(): Promise { return result } -export async function get(id: string): Promise { +export async function get(id: string): Promise { const db = context.getAppDB() - const projectApp = await db.tryGet(id) - return projectApp + const workspaceApp = await db.tryGet(id) + return workspaceApp } -export async function create(projectApp: WithoutDocMetadata) { +export async function create(workspaceApp: WithoutDocMetadata) { const db = context.getAppDB() - await guardName(projectApp.name) + await guardName(workspaceApp.name) const response = await db.put({ _id: `${DocumentType.PROJECT_APP}${SEPARATOR}${utils.newid()}`, - ...projectApp, + ...workspaceApp, }) return { _id: response.id!, _rev: response.rev!, - ...projectApp, + ...workspaceApp, } } export async function update( - projectApp: Omit + workspaceApp: Omit ) { const db = context.getAppDB() - await guardName(projectApp.name, projectApp._id) + await guardName(workspaceApp.name, workspaceApp._id) - const response = await db.put(projectApp) + const response = await db.put(workspaceApp) return { _id: response.id!, _rev: response.rev!, - ...projectApp, + ...workspaceApp, } } diff --git a/packages/types/src/api/web/app/application.ts b/packages/types/src/api/web/app/application.ts index 2380e363d9..f06559be6e 100644 --- a/packages/types/src/api/web/app/application.ts +++ b/packages/types/src/api/web/app/application.ts @@ -1,5 +1,5 @@ import type { PlanType } from "../../../sdk" -import type { Layout, App, Screen, ProjectApp } from "../../../documents" +import type { Layout, App, Screen, WorkspaceApp } from "../../../documents" import { ReadStream } from "fs" export interface SyncAppResponse { @@ -35,7 +35,7 @@ export interface FetchAppDefinitionResponse { libraries: string[] } -interface ProjectAppResponse extends ProjectApp { +interface ProjectAppResponse extends WorkspaceApp { screens: Screen[] } @@ -43,7 +43,7 @@ export interface FetchAppPackageResponse { application: App licenseType: PlanType screens: Screen[] - projectApps: ProjectAppResponse[] + workspaceApps: ProjectAppResponse[] layouts: Layout[] clientLibPath: string hasLock: boolean diff --git a/packages/types/src/api/web/app/workspaceApp.ts b/packages/types/src/api/web/app/workspaceApp.ts index b19e83d751..af3ced2d1c 100644 --- a/packages/types/src/api/web/app/workspaceApp.ts +++ b/packages/types/src/api/web/app/workspaceApp.ts @@ -15,7 +15,7 @@ export interface InsertProjectAppRequest { } export interface InsertProjectAppResponse { - projectApp: ProjectAppResponse + workspaceApp: ProjectAppResponse } export interface UpdateProjectAppRequest { @@ -28,5 +28,5 @@ export interface UpdateProjectAppRequest { } export interface UpdateProjectAppResponse { - projectApp: ProjectAppResponse + workspaceApp: ProjectAppResponse } diff --git a/packages/types/src/documents/app/workspaceApp.ts b/packages/types/src/documents/app/workspaceApp.ts index 6c6ddb051e..58f6ce941b 100644 --- a/packages/types/src/documents/app/workspaceApp.ts +++ b/packages/types/src/documents/app/workspaceApp.ts @@ -1,6 +1,6 @@ import { Document } from "../document" -export interface ProjectApp extends Document { +export interface WorkspaceApp extends Document { name: string urlPrefix: string icon: string From b69462680d5fd228eb11a104c61baae74e1b0a83 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Fri, 16 May 2025 16:33:32 +0200 Subject: [PATCH 35/36] Renames --- packages/backend-core/src/docIds/params.ts | 2 +- packages/server/src/sdk/app/workspaceApps/index.ts | 2 +- packages/types/src/documents/document.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/backend-core/src/docIds/params.ts b/packages/backend-core/src/docIds/params.ts index 46cc0a924c..809f252192 100644 --- a/packages/backend-core/src/docIds/params.ts +++ b/packages/backend-core/src/docIds/params.ts @@ -218,5 +218,5 @@ export const getProjectAppParams = ( projectAppId?: string | null, otherProps: Partial = {} ) => { - return getDocParams(DocumentType.PROJECT_APP, projectAppId, otherProps) + return getDocParams(DocumentType.WORKSPACE_APP, projectAppId, otherProps) } diff --git a/packages/server/src/sdk/app/workspaceApps/index.ts b/packages/server/src/sdk/app/workspaceApps/index.ts index b786d9b66b..971a574e1b 100644 --- a/packages/server/src/sdk/app/workspaceApps/index.ts +++ b/packages/server/src/sdk/app/workspaceApps/index.ts @@ -39,7 +39,7 @@ export async function create(workspaceApp: WithoutDocMetadata) { await guardName(workspaceApp.name) const response = await db.put({ - _id: `${DocumentType.PROJECT_APP}${SEPARATOR}${utils.newid()}`, + _id: `${DocumentType.WORKSPACE_APP}${SEPARATOR}${utils.newid()}`, ...workspaceApp, }) return { diff --git a/packages/types/src/documents/document.ts b/packages/types/src/documents/document.ts index 5b4b1708aa..4a420db850 100644 --- a/packages/types/src/documents/document.ts +++ b/packages/types/src/documents/document.ts @@ -43,7 +43,7 @@ export enum DocumentType { OAUTH2_CONFIG = "oauth2", OAUTH2_CONFIG_LOG = "oauth2log", AGENT_CHAT = "agentchat", - PROJECT_APP = "project_app", + WORKSPACE_APP = "workspace_app", } // Because DocumentTypes can overlap, we need to make sure that we search From 6ba7317c7a129b524ba15a4e0554e32b5eceac23 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Fri, 16 May 2025 16:34:21 +0200 Subject: [PATCH 36/36] Final renames --- packages/backend-core/src/docIds/params.ts | 6 ++--- .../server/src/api/controllers/application.ts | 18 ++++++------- .../src/api/controllers/workspaceApp.ts | 26 ++++++++++--------- .../server/src/api/routes/workspaceApp.ts | 6 ++--- .../20250514133719_workspace_apps.ts | 14 +++++----- packages/server/src/constants/screens.ts | 4 +-- .../server/src/sdk/app/workspaceApps/index.ts | 12 ++++----- .../server/src/tests/utilities/structures.ts | 2 +- .../src/tests/utilities/structures/screens.ts | 6 ++--- packages/types/src/api/web/app/application.ts | 4 +-- .../types/src/api/web/app/workspaceApp.ts | 14 +++++----- packages/types/src/documents/app/screen.ts | 2 +- 12 files changed, 59 insertions(+), 55 deletions(-) diff --git a/packages/backend-core/src/docIds/params.ts b/packages/backend-core/src/docIds/params.ts index 809f252192..be22eb4092 100644 --- a/packages/backend-core/src/docIds/params.ts +++ b/packages/backend-core/src/docIds/params.ts @@ -214,9 +214,9 @@ export const getOAuth2ConfigParams = ( /** * Gets parameters for retrieving project apps, this is a utility function for the getDocParams function. */ -export const getProjectAppParams = ( - projectAppId?: string | null, +export const getWorkspaceAppParams = ( + workspaceAppId?: string | null, otherProps: Partial = {} ) => { - return getDocParams(DocumentType.WORKSPACE_APP, projectAppId, otherProps) + return getDocParams(DocumentType.WORKSPACE_APP, workspaceAppId, otherProps) } diff --git a/packages/server/src/api/controllers/application.ts b/packages/server/src/api/controllers/application.ts index 6dbb09f7fd..f5448acb73 100644 --- a/packages/server/src/api/controllers/application.ts +++ b/packages/server/src/api/controllers/application.ts @@ -184,7 +184,7 @@ async function addSampleDataDocs() { async function addSampleDataScreen() { const db = context.getAppDB() - let projectAppId: string | undefined + let workspaceAppId: string | undefined if (await features.isEnabled(FeatureFlag.WORKSPACE_APPS)) { const appMetadata = await sdk.applications.metadata.get() @@ -193,10 +193,10 @@ async function addSampleDataScreen() { urlPrefix: "/", icon: "Monitoring", }) - projectAppId = workspaceApp._id! + workspaceAppId = workspaceApp._id! } - let screen = await createSampleDataTableScreen(projectAppId) + let screen = await createSampleDataTableScreen(workspaceAppId) screen._id = generateScreenID() await db.put(screen) } @@ -285,7 +285,7 @@ export async function fetchAppPackage( let workspaceApps: FetchAppPackageResponse["workspaceApps"] = [] if (await features.flags.isEnabled(FeatureFlag.WORKSPACE_APPS)) { - workspaceApps = await extractScreensByProjectApp(screens) + workspaceApps = await extractScreensByWorkspaceApp(screens) screens = [] } @@ -305,20 +305,20 @@ export async function fetchAppPackage( } } -async function extractScreensByProjectApp( +async function extractScreensByWorkspaceApp( screens: Screen[] ): Promise { const result: FetchAppPackageResponse["workspaceApps"] = [] const workspaceApps = await sdk.workspaceApps.fetch() - const screensByProjectApp = groupBy(s => s.projectAppId, screens) - for (const projectAppId of Object.keys(screensByProjectApp)) { - const workspaceApp = workspaceApps.find(p => p._id === projectAppId) + const screensByWorkspaceApp = groupBy(s => s.workspaceAppId, screens) + for (const workspaceAppId of Object.keys(screensByWorkspaceApp)) { + const workspaceApp = workspaceApps.find(p => p._id === workspaceAppId) result.push({ ...workspaceApp!, - screens: screensByProjectApp[projectAppId], + screens: screensByWorkspaceApp[workspaceAppId], }) } diff --git a/packages/server/src/api/controllers/workspaceApp.ts b/packages/server/src/api/controllers/workspaceApp.ts index 90644e33d8..7c2aa6ae2f 100644 --- a/packages/server/src/api/controllers/workspaceApp.ts +++ b/packages/server/src/api/controllers/workspaceApp.ts @@ -1,15 +1,17 @@ import { Ctx, - InsertProjectAppRequest, - InsertProjectAppResponse, + InsertWorkspaceAppRequest, + InsertWorkspaceAppResponse, WorkspaceApp, - ProjectAppResponse, - UpdateProjectAppRequest, - UpdateProjectAppResponse, + WorkspaceAppResponse, + UpdateWorkspaceAppRequest, + UpdateWorkspaceAppResponse, } from "@budibase/types" import sdk from "../../sdk" -function toProjectAppResponse(workspaceApp: WorkspaceApp): ProjectAppResponse { +function toWorkspaceAppResponse( + workspaceApp: WorkspaceApp +): WorkspaceAppResponse { return { _id: workspaceApp._id!, _rev: workspaceApp._rev!, @@ -21,25 +23,25 @@ function toProjectAppResponse(workspaceApp: WorkspaceApp): ProjectAppResponse { } export async function create( - ctx: Ctx + ctx: Ctx ) { const { body } = ctx.request - const newProjectApp = { + const newWorkspaceApp = { name: body.name, urlPrefix: body.urlPrefix, icon: body.icon, iconColor: body.iconColor, } - const workspaceApp = await sdk.workspaceApps.create(newProjectApp) + const workspaceApp = await sdk.workspaceApps.create(newWorkspaceApp) ctx.status = 201 ctx.body = { - workspaceApp: toProjectAppResponse(workspaceApp), + workspaceApp: toWorkspaceAppResponse(workspaceApp), } } export async function edit( - ctx: Ctx + ctx: Ctx ) { const { body } = ctx.request @@ -58,7 +60,7 @@ export async function edit( const workspaceApp = await sdk.workspaceApps.update(toUpdate) ctx.body = { - workspaceApp: toProjectAppResponse(workspaceApp), + workspaceApp: toWorkspaceAppResponse(workspaceApp), } } diff --git a/packages/server/src/api/routes/workspaceApp.ts b/packages/server/src/api/routes/workspaceApp.ts index e109ff24fd..38676a19ed 100644 --- a/packages/server/src/api/routes/workspaceApp.ts +++ b/packages/server/src/api/routes/workspaceApp.ts @@ -23,7 +23,7 @@ const updateSchema = Joi.object({ ...baseSchema, }) -function projectAppValidator( +function workspaceAppValidator( schema: typeof insertSchema | typeof updateSchema ) { return middleware.joiValidator.body(schema, { allowUnknown: false }) @@ -34,13 +34,13 @@ const router: Router = new Router() router.post( "/api/workspaceApp", authorized(PermissionType.BUILDER), - projectAppValidator(insertSchema), + workspaceAppValidator(insertSchema), controller.create ) router.put( "/api/workspaceApp/:id", authorized(PermissionType.BUILDER), - projectAppValidator(updateSchema), + workspaceAppValidator(updateSchema), controller.edit ) router.delete( diff --git a/packages/server/src/appMigrations/migrations/20250514133719_workspace_apps.ts b/packages/server/src/appMigrations/migrations/20250514133719_workspace_apps.ts index a23dfa065e..066e60579c 100644 --- a/packages/server/src/appMigrations/migrations/20250514133719_workspace_apps.ts +++ b/packages/server/src/appMigrations/migrations/20250514133719_workspace_apps.ts @@ -6,24 +6,26 @@ const migration = async () => { const screens = await sdk.screens.fetch() const application = await sdk.applications.metadata.get() - const allProjectApps = await sdk.workspaceApps.fetch() - let projectAppId = allProjectApps.find(p => p.name === application.name)?._id - if (!projectAppId) { + const allWorkspaceApps = await sdk.workspaceApps.fetch() + let workspaceAppId = allWorkspaceApps.find( + p => p.name === application.name + )?._id + if (!workspaceAppId) { const workspaceApp = await sdk.workspaceApps.create({ name: application.name, urlPrefix: "/", icon: "Monitoring", }) - projectAppId = workspaceApp._id + workspaceAppId = workspaceApp._id } const db = context.getAppDB() await db.bulkDocs( screens - .filter(s => !s.projectAppId) + .filter(s => !s.workspaceAppId) .map(s => ({ ...s, - projectAppId, + workspaceAppId, })) ) } diff --git a/packages/server/src/constants/screens.ts b/packages/server/src/constants/screens.ts index e206da0f27..7be75c4073 100644 --- a/packages/server/src/constants/screens.ts +++ b/packages/server/src/constants/screens.ts @@ -3,14 +3,14 @@ import { Screen } from "@budibase/types" export const SAMPLE_DATA_SCREEN_NAME = "sample-data-inventory-screen" export function createSampleDataTableScreen( - projectAppId: string | undefined + workspaceAppId: string | undefined ): Screen { return { showNavigation: true, width: "Large", routing: { route: "/inventory", roleId: "BASIC", homeScreen: false }, name: SAMPLE_DATA_SCREEN_NAME, - projectAppId, + workspaceAppId, props: { _id: "c38f2b9f250fb4c33965ce47e12c02a80", _component: "@budibase/standard-components/container", diff --git a/packages/server/src/sdk/app/workspaceApps/index.ts b/packages/server/src/sdk/app/workspaceApps/index.ts index 971a574e1b..a9170c7268 100644 --- a/packages/server/src/sdk/app/workspaceApps/index.ts +++ b/packages/server/src/sdk/app/workspaceApps/index.ts @@ -7,9 +7,9 @@ import { } from "@budibase/types" async function guardName(name: string, id?: string) { - const existingProjectApps = await fetch() + const existingWorkspaceApps = await fetch() - if (existingProjectApps.find(p => p.name === name && p._id !== id)) { + if (existingWorkspaceApps.find(p => p.name === name && p._id !== id)) { throw new HTTPError(`App with name '${name}' is already taken.`, 400) } } @@ -17,7 +17,7 @@ async function guardName(name: string, id?: string) { export async function fetch(): Promise { const db = context.getAppDB() const docs = await db.allDocs( - docIds.getProjectAppParams(null, { include_docs: true }) + docIds.getWorkspaceAppParams(null, { include_docs: true }) ) const result = docs.rows.map(r => ({ ...r.doc!, @@ -65,16 +65,16 @@ export async function update( } export async function remove( - projectAppId: string, + workspaceAppId: string, _rev: string ): Promise { const db = context.getAppDB() try { - await db.remove(projectAppId, _rev) + await db.remove(workspaceAppId, _rev) } catch (e: any) { if (e.status === 404) { throw new HTTPError( - `Project app with id '${projectAppId}' not found.`, + `Project app with id '${workspaceAppId}' not found.`, 404 ) } diff --git a/packages/server/src/tests/utilities/structures.ts b/packages/server/src/tests/utilities/structures.ts index 7ca60f26c6..fafd7887e4 100644 --- a/packages/server/src/tests/utilities/structures.ts +++ b/packages/server/src/tests/utilities/structures.ts @@ -587,7 +587,7 @@ function createHomeScreen( roleId: config.roleId, }, name: "home-screen", - projectAppId: "projectAppId", + workspaceAppId: "workspaceAppId", } } diff --git a/packages/server/src/tests/utilities/structures/screens.ts b/packages/server/src/tests/utilities/structures/screens.ts index dd5a120fea..daf1e170a7 100644 --- a/packages/server/src/tests/utilities/structures/screens.ts +++ b/packages/server/src/tests/utilities/structures/screens.ts @@ -65,7 +65,7 @@ export function createTableScreen( homeScreen: false, }, name: "screen-id", - projectAppId: "projectAppId", + workspaceAppId: "workspaceAppId", } } @@ -117,7 +117,7 @@ export function createViewScreen(view: ViewV2): Screen { homeScreen: false, }, name: "view-id", - projectAppId: "projectAppId", + workspaceAppId: "workspaceAppId", } } @@ -173,6 +173,6 @@ export function createQueryScreen(datasourceId: string, query: Query): Screen { homeScreen: false, }, name: "screen-id", - projectAppId: "projectAppId", + workspaceAppId: "workspaceAppId", } } diff --git a/packages/types/src/api/web/app/application.ts b/packages/types/src/api/web/app/application.ts index f06559be6e..641ffdadfe 100644 --- a/packages/types/src/api/web/app/application.ts +++ b/packages/types/src/api/web/app/application.ts @@ -35,7 +35,7 @@ export interface FetchAppDefinitionResponse { libraries: string[] } -interface ProjectAppResponse extends WorkspaceApp { +interface WorkspaceAppResponse extends WorkspaceApp { screens: Screen[] } @@ -43,7 +43,7 @@ export interface FetchAppPackageResponse { application: App licenseType: PlanType screens: Screen[] - workspaceApps: ProjectAppResponse[] + workspaceApps: WorkspaceAppResponse[] layouts: Layout[] clientLibPath: string hasLock: boolean diff --git a/packages/types/src/api/web/app/workspaceApp.ts b/packages/types/src/api/web/app/workspaceApp.ts index af3ced2d1c..96dd68c727 100644 --- a/packages/types/src/api/web/app/workspaceApp.ts +++ b/packages/types/src/api/web/app/workspaceApp.ts @@ -1,4 +1,4 @@ -export interface ProjectAppResponse { +export interface WorkspaceAppResponse { _id: string _rev: string name: string @@ -7,18 +7,18 @@ export interface ProjectAppResponse { iconColor?: string } -export interface InsertProjectAppRequest { +export interface InsertWorkspaceAppRequest { name: string urlPrefix: string icon: string iconColor: string } -export interface InsertProjectAppResponse { - workspaceApp: ProjectAppResponse +export interface InsertWorkspaceAppResponse { + workspaceApp: WorkspaceAppResponse } -export interface UpdateProjectAppRequest { +export interface UpdateWorkspaceAppRequest { _id: string _rev: string name: string @@ -27,6 +27,6 @@ export interface UpdateProjectAppRequest { iconColor: string } -export interface UpdateProjectAppResponse { - workspaceApp: ProjectAppResponse +export interface UpdateWorkspaceAppResponse { + workspaceApp: WorkspaceAppResponse } diff --git a/packages/types/src/documents/app/screen.ts b/packages/types/src/documents/app/screen.ts index 1ae03cf76c..b8738db70e 100644 --- a/packages/types/src/documents/app/screen.ts +++ b/packages/types/src/documents/app/screen.ts @@ -29,7 +29,7 @@ export interface Screen extends Document { pluginAdded?: boolean onLoad?: EventHandler[] variant?: ScreenVariant - projectAppId?: string + workspaceAppId?: string } export interface ScreenRoutesViewOutput extends Document {