Merge pull request #7450 from Budibase/cheeks-fixes
App navigation and screen migration for imports and templates
This commit is contained in:
commit
512d225389
|
@ -19,7 +19,6 @@ import {
|
||||||
makeComponentUnique,
|
makeComponentUnique,
|
||||||
} from "../componentUtils"
|
} from "../componentUtils"
|
||||||
import { Helpers } from "@budibase/bbui"
|
import { Helpers } from "@budibase/bbui"
|
||||||
import { DefaultAppTheme, LAYOUT_NAMES } from "../../constants"
|
|
||||||
import { Utils } from "@budibase/frontend-core"
|
import { Utils } from "@budibase/frontend-core"
|
||||||
|
|
||||||
const INITIAL_FRONTEND_STATE = {
|
const INITIAL_FRONTEND_STATE = {
|
||||||
|
@ -125,35 +124,6 @@ export const getFrontendStore = () => {
|
||||||
await integrations.init()
|
await integrations.init()
|
||||||
await queries.init()
|
await queries.init()
|
||||||
await tables.init()
|
await tables.init()
|
||||||
|
|
||||||
// Add navigation settings to old apps
|
|
||||||
if (!application.navigation) {
|
|
||||||
const layout = layouts.find(x => x._id === LAYOUT_NAMES.MASTER.PRIVATE)
|
|
||||||
const customTheme = application.customTheme
|
|
||||||
let navigationSettings = {
|
|
||||||
navigation: "Top",
|
|
||||||
title: application.name,
|
|
||||||
navWidth: "Large",
|
|
||||||
navBackground:
|
|
||||||
customTheme?.navBackground || DefaultAppTheme.navBackground,
|
|
||||||
navTextColor:
|
|
||||||
customTheme?.navTextColor || DefaultAppTheme.navTextColor,
|
|
||||||
}
|
|
||||||
if (layout) {
|
|
||||||
navigationSettings.hideLogo = layout.props.hideLogo
|
|
||||||
navigationSettings.hideTitle = layout.props.hideTitle
|
|
||||||
navigationSettings.title = layout.props.title || application.name
|
|
||||||
navigationSettings.logoUrl = layout.props.logoUrl
|
|
||||||
navigationSettings.links = layout.props.links
|
|
||||||
navigationSettings.navigation = layout.props.navigation || "Top"
|
|
||||||
navigationSettings.sticky = layout.props.sticky
|
|
||||||
navigationSettings.navWidth = layout.props.width || "Large"
|
|
||||||
if (navigationSettings.navigation === "None") {
|
|
||||||
navigationSettings.navigation = "Top"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
await store.actions.navigation.save(navigationSettings)
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
theme: {
|
theme: {
|
||||||
save: async theme => {
|
save: async theme => {
|
||||||
|
|
|
@ -47,7 +47,14 @@ import { checkAppMetadata } from "../../automations/logging"
|
||||||
import { getUniqueRows } from "../../utilities/usageQuota/rows"
|
import { getUniqueRows } from "../../utilities/usageQuota/rows"
|
||||||
import { quotas } from "@budibase/pro"
|
import { quotas } from "@budibase/pro"
|
||||||
import { errors, events, migrations } from "@budibase/backend-core"
|
import { errors, events, migrations } from "@budibase/backend-core"
|
||||||
import { App, MigrationType } from "@budibase/types"
|
import {
|
||||||
|
App,
|
||||||
|
Layout,
|
||||||
|
Screen,
|
||||||
|
MigrationType,
|
||||||
|
AppNavigation,
|
||||||
|
} from "@budibase/types"
|
||||||
|
import { BASE_LAYOUT_PROP_IDS } from "../../constants/layouts"
|
||||||
|
|
||||||
const URL_REGEX_SLASH = /\/|\\/g
|
const URL_REGEX_SLASH = /\/|\\/g
|
||||||
|
|
||||||
|
@ -243,27 +250,19 @@ const performAppCreate = async (ctx: any) => {
|
||||||
}
|
}
|
||||||
const instance = await createInstance(instanceConfig)
|
const instance = await createInstance(instanceConfig)
|
||||||
const appId = instance._id
|
const appId = instance._id
|
||||||
|
|
||||||
const db = context.getAppDB()
|
const db = context.getAppDB()
|
||||||
let _rev
|
|
||||||
try {
|
let newApplication: App = {
|
||||||
// if template there will be an existing doc
|
|
||||||
const existing = await db.get(DocumentType.APP_METADATA)
|
|
||||||
_rev = existing._rev
|
|
||||||
} catch (err) {
|
|
||||||
// nothing to do
|
|
||||||
}
|
|
||||||
const newApplication: App = {
|
|
||||||
_id: DocumentType.APP_METADATA,
|
_id: DocumentType.APP_METADATA,
|
||||||
_rev,
|
_rev: undefined,
|
||||||
appId: instance._id,
|
appId,
|
||||||
type: "app",
|
type: "app",
|
||||||
version: packageJson.version,
|
version: packageJson.version,
|
||||||
componentLibraries: ["@budibase/standard-components"],
|
componentLibraries: ["@budibase/standard-components"],
|
||||||
name: name,
|
name: name,
|
||||||
url: url,
|
url: url,
|
||||||
template: ctx.request.body.template,
|
template: templateKey,
|
||||||
instance: instance,
|
instance,
|
||||||
tenantId: getTenantId(),
|
tenantId: getTenantId(),
|
||||||
updatedAt: new Date().toISOString(),
|
updatedAt: new Date().toISOString(),
|
||||||
createdAt: new Date().toISOString(),
|
createdAt: new Date().toISOString(),
|
||||||
|
@ -285,6 +284,36 @@ const performAppCreate = async (ctx: any) => {
|
||||||
buttonBorderRadius: "16px",
|
buttonBorderRadius: "16px",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If we used a template or imported an app there will be an existing doc.
|
||||||
|
// Fetch and migrate some metadata from the existing app.
|
||||||
|
try {
|
||||||
|
const existing: App = await db.get(DocumentType.APP_METADATA)
|
||||||
|
const keys: (keyof App)[] = [
|
||||||
|
"_rev",
|
||||||
|
"navigation",
|
||||||
|
"theme",
|
||||||
|
"customTheme",
|
||||||
|
"icon",
|
||||||
|
]
|
||||||
|
keys.forEach(key => {
|
||||||
|
if (existing[key]) {
|
||||||
|
// @ts-ignore
|
||||||
|
newApplication[key] = existing[key]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Migrate navigation settings and screens if required
|
||||||
|
if (existing && !existing.navigation) {
|
||||||
|
const navigation = await migrateAppNavigation()
|
||||||
|
if (navigation) {
|
||||||
|
newApplication.navigation = navigation
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
// Nothing to do
|
||||||
|
}
|
||||||
|
|
||||||
const response = await db.put(newApplication, { force: true })
|
const response = await db.put(newApplication, { force: true })
|
||||||
newApplication._rev = response.rev
|
newApplication._rev = response.rev
|
||||||
|
|
||||||
|
@ -567,3 +596,55 @@ const updateAppPackage = async (appPackage: any, appId: any) => {
|
||||||
return newAppPackage
|
return newAppPackage
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const migrateAppNavigation = async () => {
|
||||||
|
const db = context.getAppDB()
|
||||||
|
const existing: App = await db.get(DocumentType.APP_METADATA)
|
||||||
|
const layouts: Layout[] = await getLayouts()
|
||||||
|
const screens: Screen[] = await getScreens()
|
||||||
|
|
||||||
|
// Migrate all screens, removing custom layouts
|
||||||
|
for (let screen of screens) {
|
||||||
|
if (!screen.layoutId) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const layout = layouts.find(layout => layout._id === screen.layoutId)
|
||||||
|
screen.layoutId = undefined
|
||||||
|
screen.showNavigation = layout?.props.navigation !== "None"
|
||||||
|
screen.width = layout?.props.width || "Large"
|
||||||
|
await db.put(screen)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Migrate layout navigation settings
|
||||||
|
const { name, customTheme } = existing
|
||||||
|
const layout = layouts?.find(
|
||||||
|
(layout: Layout) => layout._id === BASE_LAYOUT_PROP_IDS.PRIVATE
|
||||||
|
)
|
||||||
|
if (layout) {
|
||||||
|
let navigationSettings: any = {
|
||||||
|
navigation: "Top",
|
||||||
|
title: name,
|
||||||
|
navWidth: "Large",
|
||||||
|
navBackground:
|
||||||
|
customTheme?.navBackground || "var(--spectrum-global-color-gray-50)",
|
||||||
|
navTextColor:
|
||||||
|
customTheme?.navTextColor || "var(--spectrum-global-color-gray-800)",
|
||||||
|
}
|
||||||
|
if (layout) {
|
||||||
|
navigationSettings.hideLogo = layout.props.hideLogo
|
||||||
|
navigationSettings.hideTitle = layout.props.hideTitle
|
||||||
|
navigationSettings.title = layout.props.title || name
|
||||||
|
navigationSettings.logoUrl = layout.props.logoUrl
|
||||||
|
navigationSettings.links = layout.props.links
|
||||||
|
navigationSettings.navigation = layout.props.navigation || "Top"
|
||||||
|
navigationSettings.sticky = layout.props.sticky
|
||||||
|
navigationSettings.navWidth = layout.props.width || "Large"
|
||||||
|
if (navigationSettings.navigation === "None") {
|
||||||
|
navigationSettings.navigation = "Top"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return navigationSettings
|
||||||
|
} else {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ const {
|
||||||
checkBuilderEndpoint,
|
checkBuilderEndpoint,
|
||||||
} = require("./utilities/TestFunctions")
|
} = require("./utilities/TestFunctions")
|
||||||
const setup = require("./utilities")
|
const setup = require("./utilities")
|
||||||
|
const { basicScreen, basicLayout } = setup.structures
|
||||||
const { AppStatus } = require("../../../db/utils")
|
const { AppStatus } = require("../../../db/utils")
|
||||||
const { events } = require("@budibase/backend-core")
|
const { events } = require("@budibase/backend-core")
|
||||||
|
|
||||||
|
@ -81,6 +82,31 @@ describe("/applications", () => {
|
||||||
body: { name: "My App" },
|
body: { name: "My App" },
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("migrates navigation settings from old apps", async () => {
|
||||||
|
const res = await request
|
||||||
|
.post("/api/applications")
|
||||||
|
.field("name", "Old App")
|
||||||
|
.field("useTemplate", "true")
|
||||||
|
.set(config.defaultHeaders())
|
||||||
|
.attach("templateFile", "src/api/routes/tests/data/old-app.txt")
|
||||||
|
.expect("Content-Type", /json/)
|
||||||
|
.expect(200)
|
||||||
|
expect(res.body._id).toBeDefined()
|
||||||
|
expect(res.body.navigation).toBeDefined()
|
||||||
|
expect(res.body.navigation.hideLogo).toBe(true)
|
||||||
|
expect(res.body.navigation.title).toBe("Custom Title")
|
||||||
|
expect(res.body.navigation.hideLogo).toBe(true)
|
||||||
|
expect(res.body.navigation.navigation).toBe("Left")
|
||||||
|
expect(res.body.navigation.navBackground).toBe(
|
||||||
|
"var(--spectrum-global-color-blue-600)"
|
||||||
|
)
|
||||||
|
expect(res.body.navigation.navTextColor).toBe(
|
||||||
|
"var(--spectrum-global-color-gray-50)"
|
||||||
|
)
|
||||||
|
expect(events.app.created).toBeCalledTimes(1)
|
||||||
|
expect(events.app.fileImported).toBeCalledTimes(1)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("fetch", () => {
|
describe("fetch", () => {
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -14,14 +14,11 @@ export interface App extends Document {
|
||||||
tenantId: string
|
tenantId: string
|
||||||
status: string
|
status: string
|
||||||
theme?: string
|
theme?: string
|
||||||
customTheme?: {
|
customTheme?: AppCustomTheme
|
||||||
buttonBorderRadius?: string
|
|
||||||
primaryColor?: string
|
|
||||||
primaryColorHover?: string
|
|
||||||
}
|
|
||||||
revertableVersion?: string
|
revertableVersion?: string
|
||||||
navigation?: AppNavigation
|
navigation?: AppNavigation
|
||||||
automationErrors?: AppMetadataErrors
|
automationErrors?: AppMetadataErrors
|
||||||
|
icon?: AppIcon
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AppInstance {
|
export interface AppInstance {
|
||||||
|
@ -47,3 +44,18 @@ export interface AppNavigationLink {
|
||||||
id?: string
|
id?: string
|
||||||
roleId?: string
|
roleId?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface AppCustomTheme {
|
||||||
|
buttonBorderRadius?: string
|
||||||
|
primaryColor?: string
|
||||||
|
primaryColorHover?: string
|
||||||
|
|
||||||
|
// Used to exist before new design UI
|
||||||
|
navTextColor?: string
|
||||||
|
navBackground?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AppIcon {
|
||||||
|
name: string
|
||||||
|
color: string
|
||||||
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
import { Document } from "../document"
|
import { Document } from "../document"
|
||||||
|
|
||||||
export interface Layout extends Document {}
|
export interface Layout extends Document {
|
||||||
|
props: any
|
||||||
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { Document } from "../document"
|
import { Document } from "../document"
|
||||||
|
|
||||||
export interface Screen extends Document {
|
export interface Screen extends Document {
|
||||||
layoutId: string
|
layoutId?: string
|
||||||
showNavigation?: boolean
|
showNavigation?: boolean
|
||||||
width?: string
|
width?: string
|
||||||
routing: {
|
routing: {
|
||||||
|
|
|
@ -2,12 +2,12 @@ import { BaseEvent } from "./event"
|
||||||
|
|
||||||
export interface ScreenCreatedEvent extends BaseEvent {
|
export interface ScreenCreatedEvent extends BaseEvent {
|
||||||
screenId: string
|
screenId: string
|
||||||
layoutId: string
|
layoutId?: string
|
||||||
roleId: string
|
roleId: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ScreenDeletedEvent extends BaseEvent {
|
export interface ScreenDeletedEvent extends BaseEvent {
|
||||||
screenId: string
|
screenId: string
|
||||||
layoutId: string
|
layoutId?: string
|
||||||
roleId: string
|
roleId: string
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue