Merge pull request #7450 from Budibase/cheeks-fixes

App navigation and screen migration for imports and templates
This commit is contained in:
Andrew Kingston 2022-08-31 15:27:18 +01:00 committed by GitHub
commit 512d225389
8 changed files with 148 additions and 54 deletions

View File

@ -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 => {

View File

@ -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
}
}

View File

@ -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

View File

@ -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
}

View File

@ -1,3 +1,5 @@
import { Document } from "../document" import { Document } from "../document"
export interface Layout extends Document {} export interface Layout extends Document {
props: any
}

View File

@ -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: {

View File

@ -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
} }