Main work to get screens into the DB, fixing up issue with async page updates not being handled in order.

This commit is contained in:
Michael Drury 2020-11-04 12:36:38 +00:00
parent 22cacbbdb3
commit 8c6a97198f
10 changed files with 107 additions and 122 deletions

View File

@ -77,23 +77,18 @@ export const getStore = () => {
export default getStore export default getStore
const setPackage = (store, initial) => async pkg => { const setPackage = (store, initial) => async pkg => {
const [main_screens, unauth_screens] = await Promise.all([ const screens = await api.get("/api/screens").then(r => r.json())
api
.get(`/_builder/api/${pkg.application._id}/pages/main/screens`)
.then(r => r.json()),
api
.get(`/_builder/api/${pkg.application._id}/pages/unauthenticated/screens`)
.then(r => r.json()),
])
const mainScreens = screens.filter(screen => screen._id.includes(pkg.pages.main._id)),
unauthScreens = screens.filter(screen => screen._id.includes(pkg.pages.unauthenticated._id))
pkg.pages = { pkg.pages = {
main: { main: {
...pkg.pages.main, ...pkg.pages.main,
_screens: Object.values(main_screens), _screens: mainScreens,
}, },
unauthenticated: { unauthenticated: {
...pkg.pages.unauthenticated, ...pkg.pages.unauthenticated,
_screens: Object.values(unauth_screens), _screens: unauthScreens,
}, },
} }
@ -107,7 +102,7 @@ const setPackage = (store, initial) => async pkg => {
regenerateCssForScreen(screen) regenerateCssForScreen(screen)
} }
await api.post(`/_builder/api/${pkg.application._id}/pages/${name}`, { await api.post(`/api/pages/${page._id}`, {
page: { page: {
componentLibraries: pkg.application.componentLibraries, componentLibraries: pkg.application.componentLibraries,
...page, ...page,
@ -115,8 +110,8 @@ const setPackage = (store, initial) => async pkg => {
screens: page._screens, screens: page._screens,
}) })
} }
generateInitialPageCss("main") await generateInitialPageCss("main")
generateInitialPageCss("unauthenticated") await generateInitialPageCss("unauthenticated")
pkg.justCreated = false pkg.justCreated = false
} }
@ -128,8 +123,8 @@ const setPackage = (store, initial) => async pkg => {
initial.pages = pkg.pages initial.pages = pkg.pages
initial.hasAppPackage = true initial.hasAppPackage = true
initial.screens = [ initial.screens = [
...Object.values(main_screens), ...Object.values(mainScreens),
...Object.values(unauth_screens), ...Object.values(unauthScreens),
] ]
initial.builtins = [getBuiltin("##builtin/screenslot")] initial.builtins = [getBuiltin("##builtin/screenslot")]
initial.appInstance = pkg.application.instance initial.appInstance = pkg.application.instance
@ -139,40 +134,36 @@ const setPackage = (store, initial) => async pkg => {
return initial return initial
} }
const saveScreen = store => screen => { const saveScreen = store => async screen => {
store.update(state => { const storeContents = get(store)
return _saveScreen(store, state, screen) const pageName = storeContents.currentPageName || "main"
}) const currentPage = storeContents.pages[pageName]
} const currentPageScreens = currentPage._screens
const _saveScreen = async (store, s, screen) => {
const pageName = s.currentPageName || "main"
const currentPageScreens = s.pages[pageName]._screens
let savePromise
await api await api
.post(`/_builder/api/${s.appId}/pages/${pageName}/screen`, screen) .post(`/api/${currentPage._id}/screens`, screen)
.then(() => { .then(() => {
if (currentPageScreens.includes(screen)) return if (currentPageScreens.includes(screen)) return
const screens = [...currentPageScreens, screen] const screens = [...currentPageScreens, screen]
store.update(innerState => { // TODO: should carry out all server updates to screen in a single call
innerState.pages[pageName]._screens = screens store.update(state => {
innerState.screens = screens state.pages[pageName]._screens = screens
innerState.currentPreviewItem = screen state.screens = screens
const safeProps = makePropsSafe( state.currentPreviewItem = screen
innerState.components[screen.props._component], const safeProps = makePropsSafe(
screen.props state.components[screen.props._component],
) screen.props
innerState.currentComponentInfo = safeProps )
screen.props = safeProps state.currentComponentInfo = safeProps
screen.props = safeProps
_savePage(innerState) savePromise = _savePage(state)
return innerState return state
})
}) })
}) await savePromise
return s
} }
const createScreen = store => async screen => { const createScreen = store => async screen => {
@ -182,7 +173,7 @@ const createScreen = store => async screen => {
state.currentComponentInfo = screen.props state.currentComponentInfo = screen.props
state.currentFrontEndType = "screen" state.currentFrontEndType = "screen"
regenerateCssForCurrentScreen(state) regenerateCssForCurrentScreen(state)
savePromise = _saveScreen(store, state, screen) savePromise = saveScreen(store)(screen)
return state return state
}) })
await savePromise await savePromise

View File

@ -3,6 +3,7 @@ import {
getBuiltin, getBuiltin,
} from "components/userInterface/pagesParsing/createProps" } from "components/userInterface/pagesParsing/createProps"
import api from "./api" import api from "./api"
import { store } from "builderStore"
import { generate_screen_css } from "./generate_css" import { generate_screen_css } from "./generate_css"
import { uuid } from "./uuid" import { uuid } from "./uuid"
import getNewComponentName from "./getNewComponentName" import getNewComponentName from "./getNewComponentName"
@ -35,14 +36,20 @@ export const saveCurrentPreviewItem = s =>
? savePage(s) ? savePage(s)
: saveScreenApi(s.currentPreviewItem, s) : saveScreenApi(s.currentPreviewItem, s)
export const savePage = async s => { export const savePage = async state => {
const pageName = s.currentPageName || "main" const pageName = state.currentPageName || "main"
const page = s.pages[pageName] const page = state.pages[pageName]
await api.post(`/api/pages/${pageName}`, {
page: { componentLibraries: s.pages.componentLibraries, ...page }, const response = await api.post(`/api/pages/${page._id}`, {
uiFunctions: s.currentPageFunctions, page: { componentLibraries: state.pages.componentLibraries, ...page },
uiFunctions: state.currentPageFunctions,
screens: page._screens, screens: page._screens,
}).then(response => response.json())
store.update(innerState => {
innerState.pages[pageName]._rev = response.rev
return innerState
}) })
return state
} }
export const saveScreenApi = (screen, s) => { export const saveScreenApi = (screen, s) => {

View File

@ -140,6 +140,7 @@ exports.delete = async function(ctx) {
const result = await db.destroy() const result = await db.destroy()
// remove top level directory // remove top level directory
// TODO: look into why this isn't a callback
await fs.rmdir(join(budibaseAppsDir(), ctx.params.appId), { await fs.rmdir(join(budibaseAppsDir(), ctx.params.appId), {
recursive: true, recursive: true,
}) })

View File

@ -8,27 +8,12 @@ exports.save = async function(ctx) {
const appPackage = ctx.request.body const appPackage = ctx.request.body
// TODO: rename to something more descriptive // TODO: rename to something more descriptive
await buildPage( await buildPage(ctx.user.appId, ctx.params.pageId, ctx.request.body)
ctx.config,
ctx.user.appId,
ctx.params.pageId,
ctx.request.body
)
// write the page data to couchDB // remove special doc props which couch will complain about
// if (pkg.page._css) { delete appPackage.page._css
// delete pkg.page._css delete appPackage.page._screens
// }
// if (pkg.page.name) {
// delete pkg.page.name
// }
// if (pkg.page._screens) {
// delete pkg.page._screens
// }
appPackage.page._id = appPackage.page._id || generatePageID() appPackage.page._id = appPackage.page._id || generatePageID()
await db.put(appPackage.page) ctx.body = await db.put(appPackage.page)
ctx.message = "Page saved successfully."
ctx.status = 200 ctx.status = 200
} }

View File

@ -10,7 +10,19 @@ exports.fetch = async ctx => {
const db = new CouchDB(ctx.user.appId) const db = new CouchDB(ctx.user.appId)
const screens = await db.allDocs( const screens = await db.allDocs(
getScreenParams(ctx.params.pageId, null, { getScreenParams(null, {
include_docs: true,
})
)
ctx.body = screens.rows.map(element => element.doc)
}
exports.find = async ctx => {
const db = new CouchDB(ctx.user.appId)
const screens = await db.allDocs(
getScreenParams(ctx.params.pageId, {
include_docs: true, include_docs: true,
}) })
) )
@ -18,33 +30,15 @@ exports.fetch = async ctx => {
ctx.body = screens.response.rows ctx.body = screens.response.rows
} }
exports.create = async ctx => {
const db = new CouchDB(ctx.user.appId)
const screen = {
// name: ctx.request.body.name,
// _rev: ctx.request.body._rev,
// permissions: ctx.request.body.permissions || [],
// _id: generateAccessLevelID(),
// type: "accesslevel",
}
const response = await db.put(screen)
ctx.body = {
...screen,
...response,
}
ctx.message = `Screen '${screen.name}' created successfully.`
}
exports.save = async ctx => { exports.save = async ctx => {
const appId = ctx.user.appId const appId = ctx.user.appId
const db = new CouchDB(appId) const db = new CouchDB(appId)
const screen = ctx.request.body const screen = ctx.request.body
if (!screen._id) { if (!screen._id) {
screen._id = generateScreenID() screen._id = generateScreenID(ctx.params.pageId)
} }
delete screen._css
const response = await db.put(screen) const response = await db.put(screen)
ctx.message = `Screen ${screen.name} saved.` ctx.message = `Screen ${screen.name} saved.`

View File

@ -7,6 +7,7 @@ const { isDev } = require("../utilities")
const { const {
authRoutes, authRoutes,
pageRoutes, pageRoutes,
screenRoutes,
userRoutes, userRoutes,
deployRoutes, deployRoutes,
applicationRoutes, applicationRoutes,
@ -97,6 +98,9 @@ router.use(templatesRoutes.allowedMethods())
router.use(pageRoutes.routes()) router.use(pageRoutes.routes())
router.use(pageRoutes.allowedMethods()) router.use(pageRoutes.allowedMethods())
router.use(screenRoutes.routes())
router.use(screenRoutes.allowedMethods())
router.use(applicationRoutes.routes()) router.use(applicationRoutes.routes())
router.use(applicationRoutes.allowedMethods()) router.use(applicationRoutes.allowedMethods())

View File

@ -1,5 +1,6 @@
const authRoutes = require("./auth") const authRoutes = require("./auth")
const pageRoutes = require("./pages") const pageRoutes = require("./pages.new")
const screenRoutes = require("./screen")
const userRoutes = require("./user") const userRoutes = require("./user")
const applicationRoutes = require("./application") const applicationRoutes = require("./application")
const tableRoutes = require("./table") const tableRoutes = require("./table")
@ -19,6 +20,7 @@ module.exports = {
deployRoutes, deployRoutes,
authRoutes, authRoutes,
pageRoutes, pageRoutes,
screenRoutes,
userRoutes, userRoutes,
applicationRoutes, applicationRoutes,
rowRoutes, rowRoutes,

View File

@ -1,35 +1,10 @@
const Router = require("@koa/router") const Router = require("@koa/router")
const joiValidator = require("../../middleware/joi-validator")
const Joi = require("joi")
const authorized = require("../../middleware/authorized") const authorized = require("../../middleware/authorized")
const { BUILDER } = require("../../utilities/accessLevels") const { BUILDER } = require("../../utilities/accessLevels")
const controller = require("../controllers/page") const controller = require("../controllers/page")
const router = Router() const router = Router()
function generateSaveValidation() { router.post("/api/pages/:pageId", authorized(BUILDER), controller.save)
// prettier-ignore
return joiValidator.body(Joi.object({
_css: Joi.string().allow(""),
name: Joi.string().required(),
route: Joi.string().required(),
props: Joi.object({
_id: Joi.string().required(),
_component: Joi.string().required(),
_children: Joi.array().required(),
_instanceName: Joi.string().required(),
_styles: Joi.object().required(),
type: Joi.string().optional(),
table: Joi.string().optional(),
}).required().unknown(true),
}).unknown(true))
}
router.post(
"/api/pages/:pageId",
authorized(BUILDER),
generateSaveValidation(),
controller.save
)
module.exports = router module.exports = router

View File

@ -2,12 +2,38 @@ const Router = require("@koa/router")
const controller = require("../controllers/screen") const controller = require("../controllers/screen")
const authorized = require("../../middleware/authorized") const authorized = require("../../middleware/authorized")
const { BUILDER } = require("../../utilities/accessLevels") const { BUILDER } = require("../../utilities/accessLevels")
const joiValidator = require("../../middleware/joi-validator")
const Joi = require("joi")
const router = Router() const router = Router()
function generateSaveValidation() {
// prettier-ignore
return joiValidator.body(Joi.object({
_css: Joi.string().allow(""),
name: Joi.string().required(),
route: Joi.string().required(),
props: Joi.object({
_id: Joi.string().required(),
_component: Joi.string().required(),
_children: Joi.array().required(),
_instanceName: Joi.string().required(),
_styles: Joi.object().required(),
type: Joi.string().optional(),
table: Joi.string().optional(),
}).required().unknown(true),
}).unknown(true))
}
router router
.get("/api/:pageId/screens", authorized(BUILDER), controller.fetch) .get("/api/screens", authorized(BUILDER), controller.fetch)
.post("/api/:pageId/screens", authorized(BUILDER), controller.save) .get("/api/:pageId/screens", authorized(BUILDER), controller.find)
.post(
"/api/:pageId/screens",
authorized(BUILDER),
generateSaveValidation(),
controller.save
)
.delete("/api/:screenId/:revId", authorized(BUILDER), controller.destroy) .delete("/api/:screenId/:revId", authorized(BUILDER), controller.destroy)
module.exports = router module.exports = router

View File

@ -204,7 +204,7 @@ exports.generateScreenID = pageId => {
* Gets parameters for retrieving screens for a particular page, this is a utility function for the getDocParams function. * Gets parameters for retrieving screens for a particular page, this is a utility function for the getDocParams function.
*/ */
exports.getScreenParams = (pageId = null, otherProps = {}) => { exports.getScreenParams = (pageId = null, otherProps = {}) => {
return getDocParams(DocumentType.SCREEN, pageId, otherProps) return getDocParams(DocumentTypes.SCREEN, pageId, otherProps)
} }
/** /**