Merge pull request #16164 from Budibase/BUDI-9294/project-apps-to-workspace-apps
Rename project app to workspace app
This commit is contained in:
commit
0b6e78c006
|
@ -214,9 +214,9 @@ export const getOAuth2ConfigParams = (
|
||||||
/**
|
/**
|
||||||
* Gets parameters for retrieving project apps, this is a utility function for the getDocParams function.
|
* Gets parameters for retrieving project apps, this is a utility function for the getDocParams function.
|
||||||
*/
|
*/
|
||||||
export const getProjectAppParams = (
|
export const getWorkspaceAppParams = (
|
||||||
projectAppId?: string | null,
|
workspaceAppId?: string | null,
|
||||||
otherProps: Partial<DatabaseQueryOpts> = {}
|
otherProps: Partial<DatabaseQueryOpts> = {}
|
||||||
) => {
|
) => {
|
||||||
return getDocParams(DocumentType.PROJECT_APP, projectAppId, otherProps)
|
return getDocParams(DocumentType.WORKSPACE_APP, workspaceAppId, otherProps)
|
||||||
}
|
}
|
||||||
|
|
|
@ -90,8 +90,8 @@ export class ScreenStore extends BudiStore<ScreenState> {
|
||||||
*/
|
*/
|
||||||
syncAppScreens(pkg: FetchAppPackageResponse) {
|
syncAppScreens(pkg: FetchAppPackageResponse) {
|
||||||
let screens = [...pkg.screens]
|
let screens = [...pkg.screens]
|
||||||
if (featureFlag.isEnabled(FeatureFlag.PROJECT_APPS)) {
|
if (featureFlag.isEnabled(FeatureFlag.WORKSPACE_APPS)) {
|
||||||
screens = [...pkg.projectApps.flatMap(p => p.screens)]
|
screens = [...pkg.workspaceApps.flatMap(p => p.screens)]
|
||||||
}
|
}
|
||||||
this.update(state => ({
|
this.update(state => ({
|
||||||
...state,
|
...state,
|
||||||
|
|
|
@ -184,19 +184,19 @@ async function addSampleDataDocs() {
|
||||||
|
|
||||||
async function addSampleDataScreen() {
|
async function addSampleDataScreen() {
|
||||||
const db = context.getAppDB()
|
const db = context.getAppDB()
|
||||||
let projectAppId: string | undefined
|
let workspaceAppId: string | undefined
|
||||||
if (await features.isEnabled(FeatureFlag.PROJECT_APPS)) {
|
if (await features.isEnabled(FeatureFlag.WORKSPACE_APPS)) {
|
||||||
const appMetadata = await sdk.applications.metadata.get()
|
const appMetadata = await sdk.applications.metadata.get()
|
||||||
|
|
||||||
const projectApp = await sdk.projectApps.create({
|
const workspaceApp = await sdk.workspaceApps.create({
|
||||||
name: appMetadata.name,
|
name: appMetadata.name,
|
||||||
urlPrefix: "/",
|
urlPrefix: "/",
|
||||||
icon: "Monitoring",
|
icon: "Monitoring",
|
||||||
})
|
})
|
||||||
projectAppId = projectApp._id!
|
workspaceAppId = workspaceApp._id!
|
||||||
}
|
}
|
||||||
|
|
||||||
let screen = await createSampleDataTableScreen(projectAppId)
|
let screen = await createSampleDataTableScreen(workspaceAppId)
|
||||||
screen._id = generateScreenID()
|
screen._id = generateScreenID()
|
||||||
await db.put(screen)
|
await db.put(screen)
|
||||||
}
|
}
|
||||||
|
@ -282,10 +282,10 @@ export async function fetchAppPackage(
|
||||||
screens = await accessController.checkScreensAccess(screens, userRoleId)
|
screens = await accessController.checkScreensAccess(screens, userRoleId)
|
||||||
}
|
}
|
||||||
|
|
||||||
let projectApps: FetchAppPackageResponse["projectApps"] = []
|
let workspaceApps: FetchAppPackageResponse["workspaceApps"] = []
|
||||||
|
|
||||||
if (await features.flags.isEnabled(FeatureFlag.PROJECT_APPS)) {
|
if (await features.flags.isEnabled(FeatureFlag.WORKSPACE_APPS)) {
|
||||||
projectApps = await extractScreensByProjectApp(screens)
|
workspaceApps = await extractScreensByWorkspaceApp(screens)
|
||||||
screens = []
|
screens = []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -297,7 +297,7 @@ export async function fetchAppPackage(
|
||||||
ctx.body = {
|
ctx.body = {
|
||||||
application: { ...application, upgradableVersion: envCore.VERSION },
|
application: { ...application, upgradableVersion: envCore.VERSION },
|
||||||
licenseType: license?.plan.type || PlanType.FREE,
|
licenseType: license?.plan.type || PlanType.FREE,
|
||||||
projectApps,
|
workspaceApps,
|
||||||
screens,
|
screens,
|
||||||
layouts,
|
layouts,
|
||||||
clientLibPath,
|
clientLibPath,
|
||||||
|
@ -305,20 +305,20 @@ export async function fetchAppPackage(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function extractScreensByProjectApp(
|
async function extractScreensByWorkspaceApp(
|
||||||
screens: Screen[]
|
screens: Screen[]
|
||||||
): Promise<FetchAppPackageResponse["projectApps"]> {
|
): Promise<FetchAppPackageResponse["workspaceApps"]> {
|
||||||
const result: FetchAppPackageResponse["projectApps"] = []
|
const result: FetchAppPackageResponse["workspaceApps"] = []
|
||||||
|
|
||||||
const projectApps = await sdk.projectApps.fetch()
|
const workspaceApps = await sdk.workspaceApps.fetch()
|
||||||
|
|
||||||
const screensByProjectApp = groupBy(s => s.projectAppId, screens)
|
const screensByWorkspaceApp = groupBy(s => s.workspaceAppId, screens)
|
||||||
for (const projectAppId of Object.keys(screensByProjectApp)) {
|
for (const workspaceAppId of Object.keys(screensByWorkspaceApp)) {
|
||||||
const projectApp = projectApps.find(p => p._id === projectAppId)
|
const workspaceApp = workspaceApps.find(p => p._id === workspaceAppId)
|
||||||
|
|
||||||
result.push({
|
result.push({
|
||||||
...projectApp!,
|
...workspaceApp!,
|
||||||
screens: screensByProjectApp[projectAppId],
|
screens: screensByWorkspaceApp[workspaceAppId],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,70 +0,0 @@
|
||||||
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<InsertProjectAppRequest, InsertProjectAppResponse>
|
|
||||||
) {
|
|
||||||
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<UpdateProjectAppRequest, UpdateProjectAppResponse>
|
|
||||||
) {
|
|
||||||
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<void, void>) {
|
|
||||||
const { id, rev } = ctx.params
|
|
||||||
|
|
||||||
await sdk.projectApps.remove(id, rev)
|
|
||||||
ctx.status = 204
|
|
||||||
}
|
|
|
@ -0,0 +1,72 @@
|
||||||
|
import {
|
||||||
|
Ctx,
|
||||||
|
InsertWorkspaceAppRequest,
|
||||||
|
InsertWorkspaceAppResponse,
|
||||||
|
WorkspaceApp,
|
||||||
|
WorkspaceAppResponse,
|
||||||
|
UpdateWorkspaceAppRequest,
|
||||||
|
UpdateWorkspaceAppResponse,
|
||||||
|
} from "@budibase/types"
|
||||||
|
import sdk from "../../sdk"
|
||||||
|
|
||||||
|
function toWorkspaceAppResponse(
|
||||||
|
workspaceApp: WorkspaceApp
|
||||||
|
): WorkspaceAppResponse {
|
||||||
|
return {
|
||||||
|
_id: workspaceApp._id!,
|
||||||
|
_rev: workspaceApp._rev!,
|
||||||
|
name: workspaceApp.name,
|
||||||
|
urlPrefix: workspaceApp.urlPrefix,
|
||||||
|
icon: workspaceApp.icon,
|
||||||
|
iconColor: workspaceApp.iconColor,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function create(
|
||||||
|
ctx: Ctx<InsertWorkspaceAppRequest, InsertWorkspaceAppResponse>
|
||||||
|
) {
|
||||||
|
const { body } = ctx.request
|
||||||
|
const newWorkspaceApp = {
|
||||||
|
name: body.name,
|
||||||
|
urlPrefix: body.urlPrefix,
|
||||||
|
icon: body.icon,
|
||||||
|
iconColor: body.iconColor,
|
||||||
|
}
|
||||||
|
|
||||||
|
const workspaceApp = await sdk.workspaceApps.create(newWorkspaceApp)
|
||||||
|
ctx.status = 201
|
||||||
|
ctx.body = {
|
||||||
|
workspaceApp: toWorkspaceAppResponse(workspaceApp),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function edit(
|
||||||
|
ctx: Ctx<UpdateWorkspaceAppRequest, UpdateWorkspaceAppResponse>
|
||||||
|
) {
|
||||||
|
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 workspaceApp = await sdk.workspaceApps.update(toUpdate)
|
||||||
|
ctx.body = {
|
||||||
|
workspaceApp: toWorkspaceAppResponse(workspaceApp),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function remove(ctx: Ctx<void, void>) {
|
||||||
|
const { id, rev } = ctx.params
|
||||||
|
|
||||||
|
await sdk.workspaceApps.remove(id, rev)
|
||||||
|
ctx.status = 204
|
||||||
|
}
|
|
@ -32,7 +32,7 @@ import rowActionRoutes from "./rowAction"
|
||||||
import oauth2Routes from "./oauth2"
|
import oauth2Routes from "./oauth2"
|
||||||
import featuresRoutes from "./features"
|
import featuresRoutes from "./features"
|
||||||
import aiRoutes from "./ai"
|
import aiRoutes from "./ai"
|
||||||
import projectApps from "./projectApp"
|
import workspaceApps from "./workspaceApp"
|
||||||
|
|
||||||
export { default as staticRoutes } from "./static"
|
export { default as staticRoutes } from "./static"
|
||||||
export { default as publicRoutes } from "./public"
|
export { default as publicRoutes } from "./public"
|
||||||
|
@ -73,7 +73,7 @@ export const mainRoutes: Router[] = [
|
||||||
rowActionRoutes,
|
rowActionRoutes,
|
||||||
oauth2Routes,
|
oauth2Routes,
|
||||||
featuresRoutes,
|
featuresRoutes,
|
||||||
projectApps,
|
workspaceApps,
|
||||||
// these need to be handled last as they still use /api/:tableId
|
// these need to be handled last as they still use /api/:tableId
|
||||||
// this could be breaking as koa may recognise other routes as this
|
// this could be breaking as koa may recognise other routes as this
|
||||||
tableRoutes,
|
tableRoutes,
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { PermissionType } from "@budibase/types"
|
||||||
import { middleware } from "@budibase/backend-core"
|
import { middleware } from "@budibase/backend-core"
|
||||||
import authorized from "../../middleware/authorized"
|
import authorized from "../../middleware/authorized"
|
||||||
|
|
||||||
import * as controller from "../controllers/projectApp"
|
import * as controller from "../controllers/workspaceApp"
|
||||||
import Joi from "joi"
|
import Joi from "joi"
|
||||||
|
|
||||||
const baseSchema = {
|
const baseSchema = {
|
||||||
|
@ -23,7 +23,7 @@ const updateSchema = Joi.object({
|
||||||
...baseSchema,
|
...baseSchema,
|
||||||
})
|
})
|
||||||
|
|
||||||
function projectAppValidator(
|
function workspaceAppValidator(
|
||||||
schema: typeof insertSchema | typeof updateSchema
|
schema: typeof insertSchema | typeof updateSchema
|
||||||
) {
|
) {
|
||||||
return middleware.joiValidator.body(schema, { allowUnknown: false })
|
return middleware.joiValidator.body(schema, { allowUnknown: false })
|
||||||
|
@ -32,19 +32,19 @@ function projectAppValidator(
|
||||||
const router: Router = new Router()
|
const router: Router = new Router()
|
||||||
|
|
||||||
router.post(
|
router.post(
|
||||||
"/api/projectApp",
|
"/api/workspaceApp",
|
||||||
authorized(PermissionType.BUILDER),
|
authorized(PermissionType.BUILDER),
|
||||||
projectAppValidator(insertSchema),
|
workspaceAppValidator(insertSchema),
|
||||||
controller.create
|
controller.create
|
||||||
)
|
)
|
||||||
router.put(
|
router.put(
|
||||||
"/api/projectApp/:id",
|
"/api/workspaceApp/:id",
|
||||||
authorized(PermissionType.BUILDER),
|
authorized(PermissionType.BUILDER),
|
||||||
projectAppValidator(updateSchema),
|
workspaceAppValidator(updateSchema),
|
||||||
controller.edit
|
controller.edit
|
||||||
)
|
)
|
||||||
router.delete(
|
router.delete(
|
||||||
"/api/projectApp/:id/:rev",
|
"/api/workspaceApp/:id/:rev",
|
||||||
authorized(PermissionType.BUILDER),
|
authorized(PermissionType.BUILDER),
|
||||||
controller.remove
|
controller.remove
|
||||||
)
|
)
|
|
@ -4,7 +4,7 @@ import { features } from "@budibase/backend-core"
|
||||||
import { AppMigration } from "."
|
import { AppMigration } from "."
|
||||||
|
|
||||||
import m20240604153647_initial_sqs from "./migrations/20240604153647_initial_sqs"
|
import m20240604153647_initial_sqs from "./migrations/20240604153647_initial_sqs"
|
||||||
import m20250514133719_project_apps from "./migrations/20250514133719_project_apps"
|
import m20250514133719_workspace_apps from "./migrations/20250514133719_workspace_apps"
|
||||||
import { FeatureFlag } from "@budibase/types"
|
import { FeatureFlag } from "@budibase/types"
|
||||||
|
|
||||||
export const MIGRATIONS: AppMigration[] = [
|
export const MIGRATIONS: AppMigration[] = [
|
||||||
|
@ -14,12 +14,12 @@ export const MIGRATIONS: AppMigration[] = [
|
||||||
func: m20240604153647_initial_sqs,
|
func: m20240604153647_initial_sqs,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "20250514133719_project_apps",
|
id: "m20250514133719_workspace_apps",
|
||||||
func: m20250514133719_project_apps,
|
func: m20250514133719_workspace_apps,
|
||||||
// Disabling it, enabling it via env variables to enable development.
|
// 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
|
// Using the existing flag system would require async checks and we could run to race conditions, so this keeps is simple
|
||||||
disabled: !features
|
disabled: !features
|
||||||
.getEnvFlags()
|
.getEnvFlags()
|
||||||
.some(f => f.key === FeatureFlag.PROJECT_APPS && f.value === true),
|
.some(f => f.key === FeatureFlag.WORKSPACE_APPS && f.value === true),
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
|
@ -6,24 +6,26 @@ const migration = async () => {
|
||||||
const screens = await sdk.screens.fetch()
|
const screens = await sdk.screens.fetch()
|
||||||
|
|
||||||
const application = await sdk.applications.metadata.get()
|
const application = await sdk.applications.metadata.get()
|
||||||
const allProjectApps = await sdk.projectApps.fetch()
|
const allWorkspaceApps = await sdk.workspaceApps.fetch()
|
||||||
let projectAppId = allProjectApps.find(p => p.name === application.name)?._id
|
let workspaceAppId = allWorkspaceApps.find(
|
||||||
if (!projectAppId) {
|
p => p.name === application.name
|
||||||
const projectApp = await sdk.projectApps.create({
|
)?._id
|
||||||
|
if (!workspaceAppId) {
|
||||||
|
const workspaceApp = await sdk.workspaceApps.create({
|
||||||
name: application.name,
|
name: application.name,
|
||||||
urlPrefix: "/",
|
urlPrefix: "/",
|
||||||
icon: "Monitoring",
|
icon: "Monitoring",
|
||||||
})
|
})
|
||||||
projectAppId = projectApp._id
|
workspaceAppId = workspaceApp._id
|
||||||
}
|
}
|
||||||
|
|
||||||
const db = context.getAppDB()
|
const db = context.getAppDB()
|
||||||
await db.bulkDocs(
|
await db.bulkDocs(
|
||||||
screens
|
screens
|
||||||
.filter(s => !s.projectAppId)
|
.filter(s => !s.workspaceAppId)
|
||||||
.map<Screen>(s => ({
|
.map<Screen>(s => ({
|
||||||
...s,
|
...s,
|
||||||
projectAppId,
|
workspaceAppId,
|
||||||
}))
|
}))
|
||||||
)
|
)
|
||||||
}
|
}
|
|
@ -3,14 +3,14 @@ import { Screen } from "@budibase/types"
|
||||||
export const SAMPLE_DATA_SCREEN_NAME = "sample-data-inventory-screen"
|
export const SAMPLE_DATA_SCREEN_NAME = "sample-data-inventory-screen"
|
||||||
|
|
||||||
export function createSampleDataTableScreen(
|
export function createSampleDataTableScreen(
|
||||||
projectAppId: string | undefined
|
workspaceAppId: string | undefined
|
||||||
): Screen {
|
): Screen {
|
||||||
return {
|
return {
|
||||||
showNavigation: true,
|
showNavigation: true,
|
||||||
width: "Large",
|
width: "Large",
|
||||||
routing: { route: "/inventory", roleId: "BASIC", homeScreen: false },
|
routing: { route: "/inventory", roleId: "BASIC", homeScreen: false },
|
||||||
name: SAMPLE_DATA_SCREEN_NAME,
|
name: SAMPLE_DATA_SCREEN_NAME,
|
||||||
projectAppId,
|
workspaceAppId,
|
||||||
props: {
|
props: {
|
||||||
_id: "c38f2b9f250fb4c33965ce47e12c02a80",
|
_id: "c38f2b9f250fb4c33965ce47e12c02a80",
|
||||||
_component: "@budibase/standard-components/container",
|
_component: "@budibase/standard-components/container",
|
||||||
|
|
|
@ -1,83 +0,0 @@
|
||||||
import { context, docIds, HTTPError, utils } from "@budibase/backend-core"
|
|
||||||
import {
|
|
||||||
DocumentType,
|
|
||||||
ProjectApp,
|
|
||||||
SEPARATOR,
|
|
||||||
WithoutDocMetadata,
|
|
||||||
} 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<ProjectApp[]> {
|
|
||||||
const db = context.getAppDB()
|
|
||||||
const docs = await db.allDocs<ProjectApp>(
|
|
||||||
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 get(id: string): Promise<ProjectApp | undefined> {
|
|
||||||
const db = context.getAppDB()
|
|
||||||
const projectApp = await db.tryGet<ProjectApp>(id)
|
|
||||||
return projectApp
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function create(projectApp: WithoutDocMetadata<ProjectApp>) {
|
|
||||||
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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function update(
|
|
||||||
projectApp: Omit<ProjectApp, "createdAt" | "updatedAt">
|
|
||||||
) {
|
|
||||||
const db = context.getAppDB()
|
|
||||||
|
|
||||||
await guardName(projectApp.name, projectApp._id)
|
|
||||||
|
|
||||||
const response = await db.put(projectApp)
|
|
||||||
return {
|
|
||||||
_id: response.id!,
|
|
||||||
_rev: response.rev!,
|
|
||||||
...projectApp,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function remove(
|
|
||||||
projectAppId: string,
|
|
||||||
_rev: string
|
|
||||||
): Promise<void> {
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,83 @@
|
||||||
|
import { context, docIds, HTTPError, utils } from "@budibase/backend-core"
|
||||||
|
import {
|
||||||
|
DocumentType,
|
||||||
|
WorkspaceApp,
|
||||||
|
SEPARATOR,
|
||||||
|
WithoutDocMetadata,
|
||||||
|
} from "@budibase/types"
|
||||||
|
|
||||||
|
async function guardName(name: string, id?: string) {
|
||||||
|
const existingWorkspaceApps = await fetch()
|
||||||
|
|
||||||
|
if (existingWorkspaceApps.find(p => p.name === name && p._id !== id)) {
|
||||||
|
throw new HTTPError(`App with name '${name}' is already taken.`, 400)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function fetch(): Promise<WorkspaceApp[]> {
|
||||||
|
const db = context.getAppDB()
|
||||||
|
const docs = await db.allDocs<WorkspaceApp>(
|
||||||
|
docIds.getWorkspaceAppParams(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 get(id: string): Promise<WorkspaceApp | undefined> {
|
||||||
|
const db = context.getAppDB()
|
||||||
|
const workspaceApp = await db.tryGet<WorkspaceApp>(id)
|
||||||
|
return workspaceApp
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function create(workspaceApp: WithoutDocMetadata<WorkspaceApp>) {
|
||||||
|
const db = context.getAppDB()
|
||||||
|
|
||||||
|
await guardName(workspaceApp.name)
|
||||||
|
|
||||||
|
const response = await db.put({
|
||||||
|
_id: `${DocumentType.WORKSPACE_APP}${SEPARATOR}${utils.newid()}`,
|
||||||
|
...workspaceApp,
|
||||||
|
})
|
||||||
|
return {
|
||||||
|
_id: response.id!,
|
||||||
|
_rev: response.rev!,
|
||||||
|
...workspaceApp,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function update(
|
||||||
|
workspaceApp: Omit<WorkspaceApp, "createdAt" | "updatedAt">
|
||||||
|
) {
|
||||||
|
const db = context.getAppDB()
|
||||||
|
|
||||||
|
await guardName(workspaceApp.name, workspaceApp._id)
|
||||||
|
|
||||||
|
const response = await db.put(workspaceApp)
|
||||||
|
return {
|
||||||
|
_id: response.id!,
|
||||||
|
_rev: response.rev!,
|
||||||
|
...workspaceApp,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function remove(
|
||||||
|
workspaceAppId: string,
|
||||||
|
_rev: string
|
||||||
|
): Promise<void> {
|
||||||
|
const db = context.getAppDB()
|
||||||
|
try {
|
||||||
|
await db.remove(workspaceAppId, _rev)
|
||||||
|
} catch (e: any) {
|
||||||
|
if (e.status === 404) {
|
||||||
|
throw new HTTPError(
|
||||||
|
`Project app with id '${workspaceAppId}' not found.`,
|
||||||
|
404
|
||||||
|
)
|
||||||
|
}
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,7 +15,7 @@ import * as screens from "./app/screens"
|
||||||
import * as common from "./app/common"
|
import * as common from "./app/common"
|
||||||
import * as oauth2 from "./app/oauth2"
|
import * as oauth2 from "./app/oauth2"
|
||||||
import * as ai from "./app/ai"
|
import * as ai from "./app/ai"
|
||||||
import * as projectApps from "./app/projectApps"
|
import * as workspaceApps from "./app/workspaceApps"
|
||||||
|
|
||||||
const sdk = {
|
const sdk = {
|
||||||
backups,
|
backups,
|
||||||
|
@ -35,7 +35,7 @@ const sdk = {
|
||||||
common,
|
common,
|
||||||
oauth2,
|
oauth2,
|
||||||
ai,
|
ai,
|
||||||
projectApps,
|
workspaceApps,
|
||||||
}
|
}
|
||||||
|
|
||||||
// default export for TS
|
// default export for TS
|
||||||
|
|
|
@ -587,7 +587,7 @@ function createHomeScreen(
|
||||||
roleId: config.roleId,
|
roleId: config.roleId,
|
||||||
},
|
},
|
||||||
name: "home-screen",
|
name: "home-screen",
|
||||||
projectAppId: "projectAppId",
|
workspaceAppId: "workspaceAppId",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -65,7 +65,7 @@ export function createTableScreen(
|
||||||
homeScreen: false,
|
homeScreen: false,
|
||||||
},
|
},
|
||||||
name: "screen-id",
|
name: "screen-id",
|
||||||
projectAppId: "projectAppId",
|
workspaceAppId: "workspaceAppId",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,7 +117,7 @@ export function createViewScreen(view: ViewV2): Screen {
|
||||||
homeScreen: false,
|
homeScreen: false,
|
||||||
},
|
},
|
||||||
name: "view-id",
|
name: "view-id",
|
||||||
projectAppId: "projectAppId",
|
workspaceAppId: "workspaceAppId",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -173,6 +173,6 @@ export function createQueryScreen(datasourceId: string, query: Query): Screen {
|
||||||
homeScreen: false,
|
homeScreen: false,
|
||||||
},
|
},
|
||||||
name: "screen-id",
|
name: "screen-id",
|
||||||
projectAppId: "projectAppId",
|
workspaceAppId: "workspaceAppId",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import type { PlanType } from "../../../sdk"
|
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"
|
import { ReadStream } from "fs"
|
||||||
|
|
||||||
export interface SyncAppResponse {
|
export interface SyncAppResponse {
|
||||||
|
@ -35,7 +35,7 @@ export interface FetchAppDefinitionResponse {
|
||||||
libraries: string[]
|
libraries: string[]
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ProjectAppResponse extends ProjectApp {
|
interface WorkspaceAppResponse extends WorkspaceApp {
|
||||||
screens: Screen[]
|
screens: Screen[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,7 +43,7 @@ export interface FetchAppPackageResponse {
|
||||||
application: App
|
application: App
|
||||||
licenseType: PlanType
|
licenseType: PlanType
|
||||||
screens: Screen[]
|
screens: Screen[]
|
||||||
projectApps: ProjectAppResponse[]
|
workspaceApps: WorkspaceAppResponse[]
|
||||||
layouts: Layout[]
|
layouts: Layout[]
|
||||||
clientLibPath: string
|
clientLibPath: string
|
||||||
hasLock: boolean
|
hasLock: boolean
|
||||||
|
|
|
@ -11,7 +11,7 @@ export * from "./layout"
|
||||||
export * from "./metadata"
|
export * from "./metadata"
|
||||||
export * from "./oauth2"
|
export * from "./oauth2"
|
||||||
export * from "./permission"
|
export * from "./permission"
|
||||||
export * from "./projectApp"
|
export * from "./workspaceApp"
|
||||||
export * from "./query"
|
export * from "./query"
|
||||||
export * from "./role"
|
export * from "./role"
|
||||||
export * from "./rowAction"
|
export * from "./rowAction"
|
||||||
|
|
|
@ -1,32 +0,0 @@
|
||||||
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
|
|
||||||
}
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
export interface WorkspaceAppResponse {
|
||||||
|
_id: string
|
||||||
|
_rev: string
|
||||||
|
name: string
|
||||||
|
urlPrefix: string
|
||||||
|
icon: string
|
||||||
|
iconColor?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface InsertWorkspaceAppRequest {
|
||||||
|
name: string
|
||||||
|
urlPrefix: string
|
||||||
|
icon: string
|
||||||
|
iconColor: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface InsertWorkspaceAppResponse {
|
||||||
|
workspaceApp: WorkspaceAppResponse
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UpdateWorkspaceAppRequest {
|
||||||
|
_id: string
|
||||||
|
_rev: string
|
||||||
|
name: string
|
||||||
|
urlPrefix: string
|
||||||
|
icon: string
|
||||||
|
iconColor: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UpdateWorkspaceAppResponse {
|
||||||
|
workspaceApp: WorkspaceAppResponse
|
||||||
|
}
|
|
@ -9,7 +9,7 @@ export * from "./layout"
|
||||||
export * from "./links"
|
export * from "./links"
|
||||||
export * from "./metadata"
|
export * from "./metadata"
|
||||||
export * from "./oauth2"
|
export * from "./oauth2"
|
||||||
export * from "./projectApp"
|
export * from "./workspaceApp"
|
||||||
export * from "./query"
|
export * from "./query"
|
||||||
export * from "./role"
|
export * from "./role"
|
||||||
export * from "./row"
|
export * from "./row"
|
||||||
|
|
|
@ -29,7 +29,7 @@ export interface Screen extends Document {
|
||||||
pluginAdded?: boolean
|
pluginAdded?: boolean
|
||||||
onLoad?: EventHandler[]
|
onLoad?: EventHandler[]
|
||||||
variant?: ScreenVariant
|
variant?: ScreenVariant
|
||||||
projectAppId?: string
|
workspaceAppId?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ScreenRoutesViewOutput extends Document {
|
export interface ScreenRoutesViewOutput extends Document {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { Document } from "../document"
|
import { Document } from "../document"
|
||||||
|
|
||||||
export interface ProjectApp extends Document {
|
export interface WorkspaceApp extends Document {
|
||||||
name: string
|
name: string
|
||||||
urlPrefix: string
|
urlPrefix: string
|
||||||
icon: string
|
icon: string
|
|
@ -43,7 +43,7 @@ export enum DocumentType {
|
||||||
OAUTH2_CONFIG = "oauth2",
|
OAUTH2_CONFIG = "oauth2",
|
||||||
OAUTH2_CONFIG_LOG = "oauth2log",
|
OAUTH2_CONFIG_LOG = "oauth2log",
|
||||||
AGENT_CHAT = "agentchat",
|
AGENT_CHAT = "agentchat",
|
||||||
PROJECT_APP = "project_app",
|
WORKSPACE_APP = "workspace_app",
|
||||||
}
|
}
|
||||||
|
|
||||||
// Because DocumentTypes can overlap, we need to make sure that we search
|
// Because DocumentTypes can overlap, we need to make sure that we search
|
||||||
|
|
|
@ -4,7 +4,7 @@ export enum FeatureFlag {
|
||||||
AI_JS_GENERATION = "AI_JS_GENERATION",
|
AI_JS_GENERATION = "AI_JS_GENERATION",
|
||||||
AI_TABLE_GENERATION = "AI_TABLE_GENERATION",
|
AI_TABLE_GENERATION = "AI_TABLE_GENERATION",
|
||||||
AI_AGENTS = "AI_AGENTS",
|
AI_AGENTS = "AI_AGENTS",
|
||||||
PROJECT_APPS = "PROJECT_APPS",
|
WORKSPACE_APPS = "WORKSPACE_APPS",
|
||||||
|
|
||||||
// Account-portal
|
// Account-portal
|
||||||
DIRECT_LOGIN_TO_ACCOUNT_PORTAL = "DIRECT_LOGIN_TO_ACCOUNT_PORTAL",
|
DIRECT_LOGIN_TO_ACCOUNT_PORTAL = "DIRECT_LOGIN_TO_ACCOUNT_PORTAL",
|
||||||
|
@ -15,7 +15,7 @@ export const FeatureFlagDefaults: Record<FeatureFlag, boolean> = {
|
||||||
[FeatureFlag.AI_JS_GENERATION]: false,
|
[FeatureFlag.AI_JS_GENERATION]: false,
|
||||||
[FeatureFlag.AI_TABLE_GENERATION]: false,
|
[FeatureFlag.AI_TABLE_GENERATION]: false,
|
||||||
[FeatureFlag.AI_AGENTS]: false,
|
[FeatureFlag.AI_AGENTS]: false,
|
||||||
[FeatureFlag.PROJECT_APPS]: false,
|
[FeatureFlag.WORKSPACE_APPS]: false,
|
||||||
|
|
||||||
// Account-portal
|
// Account-portal
|
||||||
[FeatureFlag.DIRECT_LOGIN_TO_ACCOUNT_PORTAL]: false,
|
[FeatureFlag.DIRECT_LOGIN_TO_ACCOUNT_PORTAL]: false,
|
||||||
|
|
Loading…
Reference in New Issue