diff --git a/packages/server/src/api/controllers/routing.js b/packages/server/src/api/controllers/routing.js index 763683185a..8d96863593 100644 --- a/packages/server/src/api/controllers/routing.js +++ b/packages/server/src/api/controllers/routing.js @@ -4,6 +4,39 @@ const { BUILTIN_LEVEL_IDS, } = 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. * @param {string} appId The application to produce the routing structure for. @@ -12,84 +45,15 @@ const { */ async function getRoutingStructure(appId) { const screenRoutes = await getRoutingInfo(appId) - const routing = {} + const routing = new Routing() + for (let screenRoute of screenRoutes) { - const 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) - } + let fullpath = screenRoute.routing.route const accessLevel = screenRoute.routing.accessLevelId - // iterate through the tree initially to flesh out all the required subpaths - 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 + routing.addScreenId(fullpath, accessLevel, screenRoute.id) } - return { routes: routing } -} - -/** - * 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 + return { routes: routing.json } } exports.fetch = async ctx => { @@ -108,5 +72,30 @@ exports.clientFetch = async ctx => { ctx.appId, 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 }