Merge pull request #16129 from Budibase/BUDI-9294/project-apps-crud
New project apps backend crud
This commit is contained in:
commit
b573e9cb2b
|
@ -210,3 +210,13 @@ export const getOAuth2ConfigParams = (
|
||||||
) => {
|
) => {
|
||||||
return getDocParams(DocumentType.OAUTH2_CONFIG, configId, otherProps)
|
return getDocParams(DocumentType.OAUTH2_CONFIG, configId, otherProps)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets parameters for retrieving project apps, this is a utility function for the getDocParams function.
|
||||||
|
*/
|
||||||
|
export const getWorkspaceAppParams = (
|
||||||
|
workspaceAppId?: string | null,
|
||||||
|
otherProps: Partial<DatabaseQueryOpts> = {}
|
||||||
|
) => {
|
||||||
|
return getDocParams(DocumentType.WORKSPACE_APP, workspaceAppId, otherProps)
|
||||||
|
}
|
||||||
|
|
|
@ -53,6 +53,10 @@ export function parseEnvFlags(flags: string): EnvFlagEntry[] {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getEnvFlags() {
|
||||||
|
return parseEnvFlags(env.TENANT_FEATURE_FLAGS || "")
|
||||||
|
}
|
||||||
|
|
||||||
export class FlagSet<T extends { [name: string]: boolean }> {
|
export class FlagSet<T extends { [name: string]: boolean }> {
|
||||||
// This is used to safely cache flags sets in the current request context.
|
// 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
|
// Because multiple sets could theoretically exist, we don't want the cache of
|
||||||
|
@ -89,9 +93,7 @@ export class FlagSet<T extends { [name: string]: boolean }> {
|
||||||
const currentTenantId = context.getTenantId()
|
const currentTenantId = context.getTenantId()
|
||||||
const specificallySetFalse = new Set<string>()
|
const specificallySetFalse = new Set<string>()
|
||||||
|
|
||||||
for (const { tenantId, key, value } of parseEnvFlags(
|
for (const { tenantId, key, value } of getEnvFlags()) {
|
||||||
env.TENANT_FEATURE_FLAGS || ""
|
|
||||||
)) {
|
|
||||||
if (!tenantId || (tenantId !== "*" && tenantId !== currentTenantId)) {
|
if (!tenantId || (tenantId !== "*" && tenantId !== currentTenantId)) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,11 +18,13 @@ import {
|
||||||
Component,
|
Component,
|
||||||
ComponentDefinition,
|
ComponentDefinition,
|
||||||
DeleteScreenResponse,
|
DeleteScreenResponse,
|
||||||
|
FeatureFlag,
|
||||||
FetchAppPackageResponse,
|
FetchAppPackageResponse,
|
||||||
SaveScreenResponse,
|
SaveScreenResponse,
|
||||||
Screen,
|
Screen,
|
||||||
ScreenVariant,
|
ScreenVariant,
|
||||||
} from "@budibase/types"
|
} from "@budibase/types"
|
||||||
|
import { featureFlag } from "@/helpers"
|
||||||
|
|
||||||
interface ScreenState {
|
interface ScreenState {
|
||||||
screens: Screen[]
|
screens: Screen[]
|
||||||
|
@ -87,9 +89,13 @@ export class ScreenStore extends BudiStore<ScreenState> {
|
||||||
* @param {FetchAppPackageResponse} pkg
|
* @param {FetchAppPackageResponse} pkg
|
||||||
*/
|
*/
|
||||||
syncAppScreens(pkg: FetchAppPackageResponse) {
|
syncAppScreens(pkg: FetchAppPackageResponse) {
|
||||||
|
let screens = [...pkg.screens]
|
||||||
|
if (featureFlag.isEnabled(FeatureFlag.WORKSPACE_APPS)) {
|
||||||
|
screens = [...pkg.workspaceApps.flatMap(p => p.screens)]
|
||||||
|
}
|
||||||
this.update(state => ({
|
this.update(state => ({
|
||||||
...state,
|
...state,
|
||||||
screens: [...pkg.screens],
|
screens,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@ import {
|
||||||
docIds,
|
docIds,
|
||||||
env as envCore,
|
env as envCore,
|
||||||
events,
|
events,
|
||||||
|
features,
|
||||||
objectStore,
|
objectStore,
|
||||||
roles,
|
roles,
|
||||||
tenancy,
|
tenancy,
|
||||||
|
@ -69,6 +70,7 @@ import {
|
||||||
UnpublishAppResponse,
|
UnpublishAppResponse,
|
||||||
SetRevertableAppVersionResponse,
|
SetRevertableAppVersionResponse,
|
||||||
ErrorCode,
|
ErrorCode,
|
||||||
|
FeatureFlag,
|
||||||
} from "@budibase/types"
|
} from "@budibase/types"
|
||||||
import { BASE_LAYOUT_PROP_IDS } from "../../constants/layouts"
|
import { BASE_LAYOUT_PROP_IDS } from "../../constants/layouts"
|
||||||
import sdk from "../../sdk"
|
import sdk from "../../sdk"
|
||||||
|
@ -76,6 +78,7 @@ import { builderSocket } from "../../websockets"
|
||||||
import { DefaultAppTheme, sdk as sharedCoreSDK } from "@budibase/shared-core"
|
import { DefaultAppTheme, sdk as sharedCoreSDK } from "@budibase/shared-core"
|
||||||
import * as appMigrations from "../../appMigrations"
|
import * as appMigrations from "../../appMigrations"
|
||||||
import { createSampleDataTableScreen } from "../../constants/screens"
|
import { createSampleDataTableScreen } from "../../constants/screens"
|
||||||
|
import { groupBy } from "lodash/fp"
|
||||||
|
|
||||||
// utility function, need to do away with this
|
// utility function, need to do away with this
|
||||||
async function getLayouts() {
|
async function getLayouts() {
|
||||||
|
@ -181,7 +184,19 @@ async function addSampleDataDocs() {
|
||||||
|
|
||||||
async function addSampleDataScreen() {
|
async function addSampleDataScreen() {
|
||||||
const db = context.getAppDB()
|
const db = context.getAppDB()
|
||||||
let screen = createSampleDataTableScreen()
|
let workspaceAppId: string | undefined
|
||||||
|
if (await features.isEnabled(FeatureFlag.WORKSPACE_APPS)) {
|
||||||
|
const appMetadata = await sdk.applications.metadata.get()
|
||||||
|
|
||||||
|
const workspaceApp = await sdk.workspaceApps.create({
|
||||||
|
name: appMetadata.name,
|
||||||
|
urlPrefix: "/",
|
||||||
|
icon: "Monitoring",
|
||||||
|
})
|
||||||
|
workspaceAppId = workspaceApp._id!
|
||||||
|
}
|
||||||
|
|
||||||
|
let screen = await createSampleDataTableScreen(workspaceAppId)
|
||||||
screen._id = generateScreenID()
|
screen._id = generateScreenID()
|
||||||
await db.put(screen)
|
await db.put(screen)
|
||||||
}
|
}
|
||||||
|
@ -267,6 +282,13 @@ export async function fetchAppPackage(
|
||||||
screens = await accessController.checkScreensAccess(screens, userRoleId)
|
screens = await accessController.checkScreensAccess(screens, userRoleId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let workspaceApps: FetchAppPackageResponse["workspaceApps"] = []
|
||||||
|
|
||||||
|
if (await features.flags.isEnabled(FeatureFlag.WORKSPACE_APPS)) {
|
||||||
|
workspaceApps = await extractScreensByWorkspaceApp(screens)
|
||||||
|
screens = []
|
||||||
|
}
|
||||||
|
|
||||||
const clientLibPath = objectStore.clientLibraryUrl(
|
const clientLibPath = objectStore.clientLibraryUrl(
|
||||||
ctx.params.appId,
|
ctx.params.appId,
|
||||||
application.version
|
application.version
|
||||||
|
@ -275,6 +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,
|
||||||
|
workspaceApps,
|
||||||
screens,
|
screens,
|
||||||
layouts,
|
layouts,
|
||||||
clientLibPath,
|
clientLibPath,
|
||||||
|
@ -282,6 +305,26 @@ export async function fetchAppPackage(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function extractScreensByWorkspaceApp(
|
||||||
|
screens: Screen[]
|
||||||
|
): Promise<FetchAppPackageResponse["workspaceApps"]> {
|
||||||
|
const result: FetchAppPackageResponse["workspaceApps"] = []
|
||||||
|
|
||||||
|
const workspaceApps = await sdk.workspaceApps.fetch()
|
||||||
|
|
||||||
|
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: screensByWorkspaceApp[workspaceAppId],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
async function performAppCreate(
|
async function performAppCreate(
|
||||||
ctx: UserCtx<CreateAppRequest, CreateAppResponse>
|
ctx: UserCtx<CreateAppRequest, CreateAppResponse>
|
||||||
) {
|
) {
|
||||||
|
@ -426,7 +469,7 @@ async function performAppCreate(
|
||||||
}
|
}
|
||||||
|
|
||||||
const latestMigrationId = appMigrations.getLatestEnabledMigrationId()
|
const latestMigrationId = appMigrations.getLatestEnabledMigrationId()
|
||||||
if (latestMigrationId) {
|
if (latestMigrationId && !isImport) {
|
||||||
// Initialise the app migration version as the latest one
|
// Initialise the app migration version as the latest one
|
||||||
await appMigrations.updateAppMigrationMetadata({
|
await appMigrations.updateAppMigrationMetadata({
|
||||||
appId,
|
appId,
|
||||||
|
|
|
@ -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,6 +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 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"
|
||||||
|
@ -72,6 +73,7 @@ export const mainRoutes: Router[] = [
|
||||||
rowActionRoutes,
|
rowActionRoutes,
|
||||||
oauth2Routes,
|
oauth2Routes,
|
||||||
featuresRoutes,
|
featuresRoutes,
|
||||||
|
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,
|
||||||
|
|
|
@ -235,9 +235,7 @@ describe("/screens", () => {
|
||||||
viewV2.createRequest(table._id!),
|
viewV2.createRequest(table._id!),
|
||||||
{ status: 201 }
|
{ status: 201 }
|
||||||
)
|
)
|
||||||
const screen = await config.api.screen.save(
|
const screen = await config.api.screen.save(createViewScreen(view))
|
||||||
createViewScreen("BudibaseDB", view)
|
|
||||||
)
|
|
||||||
const usage = await config.api.screen.usage(view.id)
|
const usage = await config.api.screen.usage(view.id)
|
||||||
expect(usage.sourceType).toEqual(SourceType.VIEW)
|
expect(usage.sourceType).toEqual(SourceType.VIEW)
|
||||||
confirmScreen(usage, screen)
|
confirmScreen(usage, screen)
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
import Router from "@koa/router"
|
||||||
|
import { PermissionType } from "@budibase/types"
|
||||||
|
import { middleware } from "@budibase/backend-core"
|
||||||
|
import authorized from "../../middleware/authorized"
|
||||||
|
|
||||||
|
import * as controller from "../controllers/workspaceApp"
|
||||||
|
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 workspaceAppValidator(
|
||||||
|
schema: typeof insertSchema | typeof updateSchema
|
||||||
|
) {
|
||||||
|
return middleware.joiValidator.body(schema, { allowUnknown: false })
|
||||||
|
}
|
||||||
|
|
||||||
|
const router: Router = new Router()
|
||||||
|
|
||||||
|
router.post(
|
||||||
|
"/api/workspaceApp",
|
||||||
|
authorized(PermissionType.BUILDER),
|
||||||
|
workspaceAppValidator(insertSchema),
|
||||||
|
controller.create
|
||||||
|
)
|
||||||
|
router.put(
|
||||||
|
"/api/workspaceApp/:id",
|
||||||
|
authorized(PermissionType.BUILDER),
|
||||||
|
workspaceAppValidator(updateSchema),
|
||||||
|
controller.edit
|
||||||
|
)
|
||||||
|
router.delete(
|
||||||
|
"/api/workspaceApp/:id/:rev",
|
||||||
|
authorized(PermissionType.BUILDER),
|
||||||
|
controller.remove
|
||||||
|
)
|
||||||
|
|
||||||
|
export default router
|
|
@ -1,13 +1,25 @@
|
||||||
// This file should never be manually modified, use `yarn add-app-migration` in order to add a new one
|
// 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 { AppMigration } from "."
|
||||||
|
|
||||||
import m20240604153647_initial_sqs from "./migrations/20240604153647_initial_sqs"
|
import m20240604153647_initial_sqs from "./migrations/20240604153647_initial_sqs"
|
||||||
|
import m20250514133719_workspace_apps from "./migrations/20250514133719_workspace_apps"
|
||||||
|
import { FeatureFlag } from "@budibase/types"
|
||||||
|
|
||||||
// Migrations will be executed sorted by ID
|
|
||||||
export const MIGRATIONS: AppMigration[] = [
|
export const MIGRATIONS: AppMigration[] = [
|
||||||
|
// Migrations will be executed sorted by id
|
||||||
{
|
{
|
||||||
id: "20240604153647_initial_sqs",
|
id: "20240604153647_initial_sqs",
|
||||||
func: m20240604153647_initial_sqs,
|
func: m20240604153647_initial_sqs,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
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
|
||||||
|
.getEnvFlags()
|
||||||
|
.some(f => f.key === FeatureFlag.WORKSPACE_APPS && f.value === true),
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
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 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",
|
||||||
|
})
|
||||||
|
workspaceAppId = workspaceApp._id
|
||||||
|
}
|
||||||
|
|
||||||
|
const db = context.getAppDB()
|
||||||
|
await db.bulkDocs(
|
||||||
|
screens
|
||||||
|
.filter(s => !s.workspaceAppId)
|
||||||
|
.map<Screen>(s => ({
|
||||||
|
...s,
|
||||||
|
workspaceAppId,
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default migration
|
|
@ -1,241 +1,16 @@
|
||||||
import { roles } from "@budibase/backend-core"
|
import { Screen } from "@budibase/types"
|
||||||
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 const SAMPLE_DATA_SCREEN_NAME = "sample-data-inventory-screen"
|
||||||
|
|
||||||
export function createHomeScreen(
|
export function createSampleDataTableScreen(
|
||||||
config: {
|
workspaceAppId: string | undefined
|
||||||
roleId: string
|
|
||||||
route: string
|
|
||||||
} = {
|
|
||||||
roleId: roles.BUILTIN_ROLE_IDS.BASIC,
|
|
||||||
route: "/",
|
|
||||||
}
|
|
||||||
): Screen {
|
): 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",
|
|
||||||
_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 {
|
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,
|
||||||
|
workspaceAppId,
|
||||||
props: {
|
props: {
|
||||||
_id: "c38f2b9f250fb4c33965ce47e12c02a80",
|
_id: "c38f2b9f250fb4c33965ce47e12c02a80",
|
||||||
_component: "@budibase/standard-components/container",
|
_component: "@budibase/standard-components/container",
|
||||||
|
|
|
@ -4,6 +4,7 @@ import {
|
||||||
OAuth2Config,
|
OAuth2Config,
|
||||||
PASSWORD_REPLACEMENT,
|
PASSWORD_REPLACEMENT,
|
||||||
SEPARATOR,
|
SEPARATOR,
|
||||||
|
WithoutDocMetadata,
|
||||||
WithRequired,
|
WithRequired,
|
||||||
} from "@budibase/types"
|
} from "@budibase/types"
|
||||||
|
|
||||||
|
@ -34,7 +35,7 @@ export async function fetch(): Promise<CreatedOAuthConfig[]> {
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function create(
|
export async function create(
|
||||||
config: Omit<OAuth2Config, "_id" | "_rev" | "createdAt" | "updatedAt">
|
config: WithoutDocMetadata<OAuth2Config>
|
||||||
): Promise<CreatedOAuthConfig> {
|
): Promise<CreatedOAuthConfig> {
|
||||||
const db = context.getAppDB()
|
const db = context.getAppDB()
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,17 @@
|
||||||
import { getScreenParams } from "../../../db/utils"
|
|
||||||
import { context } from "@budibase/backend-core"
|
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<Screen[]> {
|
export async function fetch(
|
||||||
return (
|
db: Database = context.getAppDB()
|
||||||
|
): Promise<Screen[]> {
|
||||||
|
const screens = (
|
||||||
await db.allDocs<Screen>(
|
await db.allDocs<Screen>(
|
||||||
getScreenParams(null, {
|
getScreenParams(null, {
|
||||||
include_docs: true,
|
include_docs: true,
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
).rows.map(el => el.doc!)
|
).rows.map(el => el.doc!)
|
||||||
|
|
||||||
|
return screens
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Row, Table } from "@budibase/types"
|
import { Row, Table, WithoutDocMetadata } from "@budibase/types"
|
||||||
|
|
||||||
import * as external from "./external"
|
import * as external from "./external"
|
||||||
import * as internal from "./internal"
|
import * as internal from "./internal"
|
||||||
|
@ -7,7 +7,7 @@ import { setPermissions } from "../permissions"
|
||||||
import { roles } from "@budibase/backend-core"
|
import { roles } from "@budibase/backend-core"
|
||||||
|
|
||||||
export async function create(
|
export async function create(
|
||||||
table: Omit<Table, "_id" | "_rev">,
|
table: WithoutDocMetadata<Table>,
|
||||||
rows?: Row[],
|
rows?: Row[],
|
||||||
userId?: string
|
userId?: string
|
||||||
): Promise<Table> {
|
): Promise<Table> {
|
||||||
|
|
|
@ -7,6 +7,7 @@ import {
|
||||||
TableRequest,
|
TableRequest,
|
||||||
ViewV2,
|
ViewV2,
|
||||||
AutoFieldSubType,
|
AutoFieldSubType,
|
||||||
|
WithoutDocMetadata,
|
||||||
} from "@budibase/types"
|
} from "@budibase/types"
|
||||||
import { context, HTTPError } from "@budibase/backend-core"
|
import { context, HTTPError } from "@budibase/backend-core"
|
||||||
import {
|
import {
|
||||||
|
@ -102,7 +103,7 @@ function getDatasourceId(table: Table) {
|
||||||
return breakExternalTableId(table._id).datasourceId
|
return breakExternalTableId(table._id).datasourceId
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function create(table: Omit<Table, "_id" | "_rev">) {
|
export async function create(table: WithoutDocMetadata<Table>) {
|
||||||
const datasourceId = getDatasourceId(table)
|
const datasourceId = getDatasourceId(table)
|
||||||
|
|
||||||
const tableToCreate = { ...table, created: true }
|
const tableToCreate = { ...table, created: true }
|
||||||
|
|
|
@ -6,6 +6,7 @@ import {
|
||||||
ViewV2,
|
ViewV2,
|
||||||
Row,
|
Row,
|
||||||
TableSourceType,
|
TableSourceType,
|
||||||
|
WithoutDocMetadata,
|
||||||
} from "@budibase/types"
|
} from "@budibase/types"
|
||||||
import {
|
import {
|
||||||
hasTypeChanged,
|
hasTypeChanged,
|
||||||
|
@ -25,7 +26,7 @@ import { generateTableID, getRowParams } from "../../../../db/utils"
|
||||||
import { quotas } from "@budibase/pro"
|
import { quotas } from "@budibase/pro"
|
||||||
|
|
||||||
export async function create(
|
export async function create(
|
||||||
table: Omit<Table, "_id" | "_rev">,
|
table: WithoutDocMetadata<Table>,
|
||||||
rows?: Row[],
|
rows?: Row[],
|
||||||
userId?: string
|
userId?: string
|
||||||
) {
|
) {
|
||||||
|
|
|
@ -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,6 +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 workspaceApps from "./app/workspaceApps"
|
||||||
|
|
||||||
const sdk = {
|
const sdk = {
|
||||||
backups,
|
backups,
|
||||||
|
@ -34,6 +35,7 @@ const sdk = {
|
||||||
common,
|
common,
|
||||||
oauth2,
|
oauth2,
|
||||||
ai,
|
ai,
|
||||||
|
workspaceApps,
|
||||||
}
|
}
|
||||||
|
|
||||||
// default export for TS
|
// default export for TS
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import { roles, utils } from "@budibase/backend-core"
|
import { roles, utils } from "@budibase/backend-core"
|
||||||
import { createHomeScreen } from "../../constants/screens"
|
import { BASE_LAYOUT_PROP_IDS, EMPTY_LAYOUT } from "../../constants/layouts"
|
||||||
import { EMPTY_LAYOUT } from "../../constants/layouts"
|
|
||||||
import { cloneDeep } from "lodash/fp"
|
import { cloneDeep } from "lodash/fp"
|
||||||
import {
|
import {
|
||||||
BUILTIN_ACTION_DEFINITIONS,
|
BUILTIN_ACTION_DEFINITIONS,
|
||||||
|
@ -38,6 +37,7 @@ import {
|
||||||
FilterCondition,
|
FilterCondition,
|
||||||
AutomationTriggerResult,
|
AutomationTriggerResult,
|
||||||
CreateEnvironmentVariableRequest,
|
CreateEnvironmentVariableRequest,
|
||||||
|
Screen,
|
||||||
} from "@budibase/types"
|
} from "@budibase/types"
|
||||||
import { LoopInput } from "../../definitions/automations"
|
import { LoopInput } from "../../definitions/automations"
|
||||||
import { merge } from "lodash"
|
import { merge } from "lodash"
|
||||||
|
@ -46,7 +46,7 @@ export {
|
||||||
createTableScreen,
|
createTableScreen,
|
||||||
createQueryScreen,
|
createQueryScreen,
|
||||||
createViewScreen,
|
createViewScreen,
|
||||||
} from "../../constants/screens"
|
} from "./structures/screens"
|
||||||
|
|
||||||
const { BUILTIN_ROLE_IDS } = roles
|
const { BUILTIN_ROLE_IDS } = roles
|
||||||
|
|
||||||
|
@ -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",
|
||||||
|
workspaceAppId: "workspaceAppId",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function basicScreen(route = "/") {
|
export function basicScreen(route = "/") {
|
||||||
return createHomeScreen({
|
return createHomeScreen({
|
||||||
roleId: BUILTIN_ROLE_IDS.BASIC,
|
roleId: BUILTIN_ROLE_IDS.BASIC,
|
||||||
|
|
|
@ -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",
|
||||||
|
workspaceAppId: "workspaceAppId",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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",
|
||||||
|
workspaceAppId: "workspaceAppId",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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",
|
||||||
|
workspaceAppId: "workspaceAppId",
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
import type { PlanType } from "../../../sdk"
|
import type { PlanType } from "../../../sdk"
|
||||||
import type { Layout, App, Screen } 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,10 +35,15 @@ export interface FetchAppDefinitionResponse {
|
||||||
libraries: string[]
|
libraries: string[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface WorkspaceAppResponse extends WorkspaceApp {
|
||||||
|
screens: Screen[]
|
||||||
|
}
|
||||||
|
|
||||||
export interface FetchAppPackageResponse {
|
export interface FetchAppPackageResponse {
|
||||||
application: App
|
application: App
|
||||||
licenseType: PlanType
|
licenseType: PlanType
|
||||||
screens: Screen[]
|
screens: Screen[]
|
||||||
|
workspaceApps: WorkspaceAppResponse[]
|
||||||
layouts: Layout[]
|
layouts: Layout[]
|
||||||
clientLibPath: string
|
clientLibPath: string
|
||||||
hasLock: boolean
|
hasLock: boolean
|
||||||
|
|
|
@ -11,6 +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 "./workspaceApp"
|
||||||
export * from "./query"
|
export * from "./query"
|
||||||
export * from "./role"
|
export * from "./role"
|
||||||
export * from "./rowAction"
|
export * from "./rowAction"
|
||||||
|
|
|
@ -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,6 +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 "./workspaceApp"
|
||||||
export * from "./query"
|
export * from "./query"
|
||||||
export * from "./role"
|
export * from "./role"
|
||||||
export * from "./row"
|
export * from "./row"
|
||||||
|
|
|
@ -29,6 +29,7 @@ export interface Screen extends Document {
|
||||||
pluginAdded?: boolean
|
pluginAdded?: boolean
|
||||||
onLoad?: EventHandler[]
|
onLoad?: EventHandler[]
|
||||||
variant?: ScreenVariant
|
variant?: ScreenVariant
|
||||||
|
workspaceAppId?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ScreenRoutesViewOutput extends Document {
|
export interface ScreenRoutesViewOutput extends Document {
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
import { Document } from "../document"
|
||||||
|
|
||||||
|
export interface WorkspaceApp extends Document {
|
||||||
|
name: string
|
||||||
|
urlPrefix: string
|
||||||
|
icon: string
|
||||||
|
iconColor?: string
|
||||||
|
}
|
|
@ -43,6 +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",
|
||||||
|
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,6 +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",
|
||||||
|
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",
|
||||||
|
@ -14,6 +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.WORKSPACE_APPS]: false,
|
||||||
|
|
||||||
// Account-portal
|
// Account-portal
|
||||||
[FeatureFlag.DIRECT_LOGIN_TO_ACCOUNT_PORTAL]: false,
|
[FeatureFlag.DIRECT_LOGIN_TO_ACCOUNT_PORTAL]: false,
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { Document } from "../documents"
|
||||||
|
|
||||||
export type DeepPartial<T> = {
|
export type DeepPartial<T> = {
|
||||||
[P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P]
|
[P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P]
|
||||||
}
|
}
|
||||||
|
@ -32,3 +34,8 @@ export type RequiredKeys<T> = {
|
||||||
}
|
}
|
||||||
|
|
||||||
export type WithRequired<T, K extends keyof T> = T & Required<Pick<T, K>>
|
export type WithRequired<T, K extends keyof T> = T & Required<Pick<T, K>>
|
||||||
|
|
||||||
|
export type WithoutDocMetadata<T extends Document> = Omit<
|
||||||
|
T,
|
||||||
|
"_id" | "_rev" | "createdAt" | "updatedAt"
|
||||||
|
>
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
import { structures, TestConfiguration } from "../../../../tests"
|
import { structures, TestConfiguration } from "../../../../tests"
|
||||||
import { context, db, roles } from "@budibase/backend-core"
|
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", () => {
|
jest.mock("@budibase/backend-core", () => {
|
||||||
const core = jest.requireActual("@budibase/backend-core")
|
const core = jest.requireActual("@budibase/backend-core")
|
||||||
|
@ -30,7 +35,7 @@ async function addAppMetadata() {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async function updateAppMetadata(update: Partial<Omit<App, "_id" | "_rev">>) {
|
async function updateAppMetadata(update: Partial<WithoutDocMetadata<App>>) {
|
||||||
const app = await appDb.get("app_metadata")
|
const app = await appDb.get("app_metadata")
|
||||||
await appDb.put({
|
await appDb.put({
|
||||||
...app,
|
...app,
|
||||||
|
|
Loading…
Reference in New Issue