Merge branch 'feature/backend-routing' of github.com:Budibase/budibase into routing-ui
This commit is contained in:
commit
a989d8ad41
|
@ -4,6 +4,39 @@ const {
|
||||||
BUILTIN_LEVEL_IDS,
|
BUILTIN_LEVEL_IDS,
|
||||||
} = require("../../utilities/security/accessLevels")
|
} = require("../../utilities/security/accessLevels")
|
||||||
|
|
||||||
|
const URL_SEPARATOR = "/"
|
||||||
|
|
||||||
|
function Routing() {
|
||||||
|
this.json = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
Routing.prototype.getTopLevel = function(fullpath) {
|
||||||
|
if (fullpath.charAt(0) !== URL_SEPARATOR) {
|
||||||
|
fullpath = URL_SEPARATOR + fullpath
|
||||||
|
}
|
||||||
|
// replace the first value with the home route
|
||||||
|
return URL_SEPARATOR + fullpath.split(URL_SEPARATOR)[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
Routing.prototype.getScreensProp = function(fullpath) {
|
||||||
|
const topLevel = this.getTopLevel(fullpath)
|
||||||
|
if (!this.json[topLevel]) {
|
||||||
|
this.json[topLevel] = {
|
||||||
|
subpaths: {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!this.json[topLevel].subpaths[fullpath]) {
|
||||||
|
this.json[topLevel].subpaths[fullpath] = {
|
||||||
|
screens: {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this.json[topLevel].subpaths[fullpath].screens
|
||||||
|
}
|
||||||
|
|
||||||
|
Routing.prototype.addScreenId = function(fullpath, accessLevel, screenId) {
|
||||||
|
this.getScreensProp(fullpath)[accessLevel] = screenId
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the full routing structure by querying the routing view and processing the result into the tree.
|
* Gets the full routing structure by querying the routing view and processing the result into the tree.
|
||||||
* @param {string} appId The application to produce the routing structure for.
|
* @param {string} appId The application to produce the routing structure for.
|
||||||
|
@ -12,84 +45,15 @@ const {
|
||||||
*/
|
*/
|
||||||
async function getRoutingStructure(appId) {
|
async function getRoutingStructure(appId) {
|
||||||
const screenRoutes = await getRoutingInfo(appId)
|
const screenRoutes = await getRoutingInfo(appId)
|
||||||
const routing = {}
|
const routing = new Routing()
|
||||||
|
|
||||||
for (let screenRoute of screenRoutes) {
|
for (let screenRoute of screenRoutes) {
|
||||||
const fullpath = screenRoute.routing.route
|
let fullpath = screenRoute.routing.route
|
||||||
// replace the first value with the home route
|
|
||||||
const subpaths = ["/"].concat(fullpath.split("/").splice(1))
|
|
||||||
// special case for when it is simply the home route "/", this creates a weird scenario
|
|
||||||
if (subpaths[1] === "") {
|
|
||||||
subpaths.splice(1, 1)
|
|
||||||
}
|
|
||||||
const accessLevel = screenRoute.routing.accessLevelId
|
const accessLevel = screenRoute.routing.accessLevelId
|
||||||
// iterate through the tree initially to flesh out all the required subpaths
|
routing.addScreenId(fullpath, accessLevel, screenRoute.id)
|
||||||
let currentPath = routing,
|
|
||||||
nextSubpath = routing
|
|
||||||
for (let subpath of subpaths) {
|
|
||||||
if (!nextSubpath[subpath]) {
|
|
||||||
nextSubpath[subpath] = {
|
|
||||||
subpaths: {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
currentPath = nextSubpath ? nextSubpath : currentPath[subpath]
|
|
||||||
nextSubpath = currentPath[subpath].subpaths
|
|
||||||
}
|
|
||||||
const correctPath = currentPath[subpaths[subpaths.length - 1]]
|
|
||||||
if (!correctPath.screens) {
|
|
||||||
correctPath.screens = {}
|
|
||||||
}
|
|
||||||
correctPath.screens[accessLevel] = screenRoute.id
|
|
||||||
correctPath.fullpath = fullpath
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return { routes: routing }
|
return { routes: routing.json }
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A function for recursing through the routing structure and adjusting it to match the user's access level
|
|
||||||
* @param {object} path The routing path, retrieved from the getRoutingStructure function, when this recurses it will
|
|
||||||
* call with this parameter updated to the various subpaths.
|
|
||||||
* @param {string[]} accessLevelIds The full list of access level IDs, this has to be passed in as otherwise we would
|
|
||||||
* need to make this an async function purely for the first call, adds confusion to the recursion.
|
|
||||||
* @returns {object} The routing structure after it has been updated.
|
|
||||||
*/
|
|
||||||
function updateRoutingStructureForUserLevel(path, accessLevelIds) {
|
|
||||||
for (let routeKey of Object.keys(path)) {
|
|
||||||
const pathStructure = path[routeKey]
|
|
||||||
if (pathStructure.subpaths) {
|
|
||||||
pathStructure.subpaths = updateRoutingStructureForUserLevel(
|
|
||||||
pathStructure.subpaths,
|
|
||||||
accessLevelIds
|
|
||||||
)
|
|
||||||
}
|
|
||||||
if (pathStructure.screens) {
|
|
||||||
const accessLevelOptions = Object.keys(pathStructure.screens)
|
|
||||||
// starts with highest level and works down through inheritance
|
|
||||||
let found = false
|
|
||||||
// special case for when the screen has no access control
|
|
||||||
if (accessLevelOptions.length === 1 && !accessLevelOptions[0]) {
|
|
||||||
pathStructure.screenId = pathStructure.screens[accessLevelOptions[0]]
|
|
||||||
pathStructure.accessLevelId = BUILTIN_LEVEL_IDS.BASIC
|
|
||||||
found = true
|
|
||||||
} else {
|
|
||||||
for (let levelId of accessLevelIds) {
|
|
||||||
if (accessLevelOptions.indexOf(levelId) !== -1) {
|
|
||||||
pathStructure.screenId = pathStructure.screens[levelId]
|
|
||||||
pathStructure.accessLevelId = levelId
|
|
||||||
found = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// remove the screen options now that we've processed it
|
|
||||||
delete pathStructure.screens
|
|
||||||
// if no option was found then remove the route, user can't access it
|
|
||||||
if (!found) {
|
|
||||||
delete path[routeKey]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return path
|
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.fetch = async ctx => {
|
exports.fetch = async ctx => {
|
||||||
|
@ -108,5 +72,30 @@ exports.clientFetch = async ctx => {
|
||||||
ctx.appId,
|
ctx.appId,
|
||||||
accessLevelId
|
accessLevelId
|
||||||
)
|
)
|
||||||
ctx.body = updateRoutingStructureForUserLevel(routing.routes, accessLevelIds)
|
for (let topLevel of Object.values(routing.routes)) {
|
||||||
|
for (let subpathKey of Object.keys(topLevel.subpaths)) {
|
||||||
|
let found = false
|
||||||
|
const subpath = topLevel.subpaths[subpathKey]
|
||||||
|
const accessLevelOptions = Object.keys(subpath.screens)
|
||||||
|
if (accessLevelOptions.length === 1 && !accessLevelOptions[0]) {
|
||||||
|
subpath.screenId = subpath.screens[accessLevelOptions[0]]
|
||||||
|
subpath.accessLevelId = BUILTIN_LEVEL_IDS.BASIC
|
||||||
|
found = true
|
||||||
|
} else {
|
||||||
|
for (let levelId of accessLevelIds) {
|
||||||
|
if (accessLevelOptions.indexOf(levelId) !== -1) {
|
||||||
|
subpath.screenId = subpath.screens[levelId]
|
||||||
|
subpath.accessLevelId = levelId
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delete subpath.screens
|
||||||
|
if (!found) {
|
||||||
|
delete topLevel.subpaths[subpathKey]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ctx.body = routing
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue