adding in routes for components and pages

This commit is contained in:
michael shanks 2019-07-25 07:31:54 +01:00
parent 70082147c7
commit 4891c6de80
19 changed files with 286 additions and 24 deletions

View File

@ -1,10 +1,14 @@
import {hierarchy as hierarchyFunctions,
common, getTemplateApi } from "budibase-core";
import {filter, cloneDeep, sortBy, map, last,
import {filter, cloneDeep, sortBy, map, last, keys,
cloneDeep, keyBy,
find, isEmpty, groupBy, reduce} from "lodash/fp";
import {chain, getNode, validate,
constructHierarchy, templateApi} from "../common/core";
import {writable} from "svelte/store";
import { defaultPagesObject } from "../userInterface/pagesParsing/defaultPagesObject"
const pipe = common.$;
export const getStore = () => {
@ -14,6 +18,11 @@ export const getStore = () => {
hierarchy: {},
actions: [],
triggers: [],
pages:defaultPagesObject(),
mainUi:{},
unauthenticatedUi:{},
derivedComponents:[],
rootComponents:[],
currentNodeIsNew: false,
errors: [],
activeNav: "database",
@ -41,6 +50,9 @@ export const getStore = () => {
store.saveLevel = saveLevel(store);
store.deleteLevel = deleteLevel(store);
store.setActiveNav = setActiveNav(store);
store.saveDerivedComponent = saveDerivedComponent(store);
store.refreshComponents = refreshComponents(store);
store.addComponentLibrary = addComponentLibrary(store);
return store;
}
@ -312,30 +324,107 @@ const deleteLevel = store => level => {
});
}
const setActiveNav = databaseStore => navName => {
databaseStore.update(db => {
db.activeNav = navName;
return db;
const setActiveNav = store => navName => {
store.update(s => {
s.activeNav = navName;
return s;
});
}
const createShadowHierarchy = hierarchy =>
constructHierarchy(JSON.parse(JSON.stringify(hierarchy)));
const savePackage = (store, db) => {
const saveDerivedComponent = store => (derivedComponent) => {
store.update(s => {
const derivedComponents = pipe(s.derivedComponents, [
filter(c => c._name !== derivedComponent._name)
]);
s.derivedComponents = derivedComponents;
const forSave = pipe(derivedComponents, [
cloneDeep,
keyBy("_name")
]);
for(let c of forSave) {
delete c._name;
}
s.pages.derivedComponents = forSave;
savePackage(store, s);
return s;
})
};
const addComponentLibrary = store => async lib => {
const response =
await fetch(`/_builder/api/${db.appname}/components?${encodeURI(lib)}`);
const success = response.status === 200;
const error = response.status === 404
? `Could not find library ${lib}`
: success
? ""
: response.statusText;
const components = success
? await response.json()
: [];
store.update(s => {
s.componentsErrors.addComponent = error;
if(success) {
s.pages.componentLibraries.push(lib);
s.rootComponents = [...s.rootComponents, components];
}
return s;
})
}
const refreshComponents = store => async () => {
const components =
await fetch(`/_builder/api/${db.appname}/components`)
.then(r => jQuery.json());
const rootComponents = pipe(components, [
keys,
map(k => ({...components[k], _name:k}))
]);
store.update(s => {
s.rootComponents = rootComponents;
return s;
});
};
const savePackage = (store, s) => {
const appDefinition = {
hierarchy:db.hierarchy,
triggers:db.triggers,
actions: groupBy("name")(db.actions)
hierarchy:s.hierarchy,
triggers:s.triggers,
actions: groupBy("name")(s.actions),
pages:s.pages,
mainUi: s.mainUi,
unauthenticatedUi: s.unauthenticatedUi
};
const data = {
appDefinition,
accessLevels:db.accessLevels
accessLevels:s.accessLevels
}
fetch(`/_builder/api/${db.appname}/appPackage`, {
fetch(`/_builder/api/${s.appname}/appPackage`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',

View File

@ -0,0 +1,16 @@
export const defaultPagesObject = () => ({
main: {
index: {
_component : "./components/indexHtml"
},
appBody: "bbapp.main.json"
},
unauthenticated: {
index: {
_component : "./components/indexHtml"
},
appBody: "bbapp.unauthenticated.json"
},
componentLibraries: ["./components"],
derivedComponents:[]
});

View File

@ -1,7 +1,7 @@
import {
validatePages,
validatePage
} from "../src/userInterface/propsDefinitionParsing/validatePages";
} from "../src/userInterface/pagesParsing/validatePages";
const validPages = () => ({
"main" : {
@ -85,8 +85,37 @@ describe("validate pages", () => {
expect(errors).toEqual([]);
});
it("should return error when index is not set, or set incorrectly", () => {
it("should return error when component libraries not set", () => {
const pages = validPages();
delete pages.componentLibraries;
let errors = validatePages(pages, getComponent);
expect(errors.length).toBe(1);
pages.componentLibraries = [];
errors = validatePages(pages, getComponent);
expect(errors.length).toBe(1);
});
it("should return error when no main or unauthenticated page", () => {
let pages = validPages();
delete pages.main;
let errors = validatePages(pages, getComponent);
expect(errors.length).toBe(1);
pages = validPages();
delete pages.unauthenticated;
errors = validatePages(pages, getComponent);
expect(errors.length).toBe(1);
});
it("should return error when page is invalid", () => {
const pages = validPages();
delete pages.main.index;
const errors = validatePages(pages, getComponent);
expect(errors.length).toBe(1);
});
});

View File

@ -0,0 +1,11 @@
{
"textbox" : {
"path": "./textbox",
"name": "Textbox",
"description": "A text input, with a label",
"props": {
"label": "string"
},
"tags": ["textboxt", "input", "text"]
}
}

View File

@ -0,0 +1,19 @@
{
"main" : {
"index" : {
},
"appBody" : "./main.app.json"
},
"unauthenticated" : {
"index" : {
"_component": "budibase-components/indexHtml",
"title": "Test App 1 - Login",
"customScripts": [
"MyCustomComponents.js"
]
},
"appBody" : "./unauthenticated.app.json"
},
"componentLibraries": ["./myComponents"]
}

View File

@ -4,7 +4,7 @@ const StatusCodes = require("../utilities/statusCodes");
const fs = require("fs");
const { resolve } = require("path");
const send = require('koa-send');
const { getPackageForBuilder,
const { getPackageForBuilder, getComponents,
savePackage, getApps } = require("../utilities/builder");
const builderPath = resolve(__dirname, "../builder");
@ -152,6 +152,27 @@ module.exports = (config, app) => {
ctx.request.body);
ctx.response.status = StatusCodes.OK;
})
.get("/_builder/api/:appname/components", async (ctx) => {
if(!config.dev) {
ctx.request.status = StatusCodes.FORBIDDEN;
ctx.body = "run in dev mode to access builder";
return;
}
try {
ctx.body = getComponents(
config,
ctx.params.appname,
ctx.query.lib);
ctx.response.status = StatusCodes.OK;
} catch(e) {
if(e.status) {
ctx.response.status = e.status;
} else {
throw e;
}
}
})
.get("/:appname", async (ctx) => {
await send(ctx, "/index.html", { root: ctx.publicPath });
})

View File

@ -7,7 +7,7 @@ module.exports = (app) => {
const response = await app.get("/testApp")
.expect(statusCodes.OK);
const expectedIndexHtml = await readFile("appPackages/testApp/ui/unauthenticated/public/index.html", "utf8");
const expectedIndexHtml = await readFile("appPackages/testApp/public/unauthenticated/index.html", "utf8");
expect(response.text).toBe(expectedIndexHtml);
@ -17,7 +17,7 @@ module.exports = (app) => {
const response = await app.get("/testApp/app.js")
.expect(statusCodes.OK);
const expectedFile = await readFile("appPackages/testApp/ui/unauthenticated/public/app.js", "utf8");
const expectedFile = await readFile("appPackages/testApp/public/unauthenticated/app.js", "utf8");
expect(response.text).toBe(expectedFile);
@ -28,7 +28,7 @@ module.exports = (app) => {
.set("cookie", app.credentials.testAppUser1.cookie)
.expect(statusCodes.OK);
const expectedIndexHtml = await readFile("appPackages/testApp/ui/main/public/index.html", "utf8");
const expectedIndexHtml = await readFile("appPackages/testApp/public/main/index.html", "utf8");
expect(response.text).toBe(expectedIndexHtml);
@ -39,7 +39,7 @@ module.exports = (app) => {
.set("cookie", app.credentials.testAppUser1.cookie)
.expect(statusCodes.OK);
const expectedFile = await readFile("appPackages/testApp/ui/main/public/app.js", "utf8");
const expectedFile = await readFile("appPackages/testApp/public/main/app.js", "utf8");
expect(response.text).toBe(expectedFile);

View File

@ -1,6 +1,22 @@
const { appPackageFolder, appsFolder } = require("./createAppPackage");
const { writeFile, readFile, readdir } = require("./fsawait");
const { pipe : $ } = require("budibase-core").common;
const {
appPackageFolder,
appsFolder
} = require("./createAppPackage");
const {
writeFile,
readFile,
readdir,
exists
} = require("./fsawait");
const { resolve } = require("path");
const { $ } = require("budibase-core").common;
const {
keys,
reduce,
map,
flatten,
some
} = require("lodash/fp");
module.exports.getPackageForBuilder = async (config, appname) => {
const appPath = appPackageFolder(config, appname);
@ -11,12 +27,15 @@ module.exports.getPackageForBuilder = async (config, appname) => {
accessLevels: JSON.parse(await readFile(
`${appPath}/access_levels.json`,
"utf8")),
pages: JSON.parse(await readFile(
`${appPath}/pages.json`,
"utf8"))
})
}
module.exports.savePackage = async (config, appname, pkg) => {
const appPath = appPackageFolder(config, appname);
await writeFile(
@ -29,10 +48,68 @@ module.exports.savePackage = async (config, appname, pkg) => {
JSON.stringify(pkg.accessLevels),
"utf8");
await writeFile(
`${appPath}/pages.json`,
JSON.stringify(pkg.accessLevels),
"utf8");
}
module.exports.getApps = async (config) =>
await readdir(appsFolder(config));
module.exports.getComponents = async (config, appname, lib) => {
const componentsInLibrary = (libname) => {
const isRelative = some(c => c === libname.substring(0,1))
("./~\\".split(""));
const componentsPath = isRelative
? resolve(appPath, libname, "components.json")
: resolve(libname, "components.json");
if(!await exists(componentsPath)) {
const e = new Error(`could not find components definition file at ${componentsPath}`);
e.statusCode = 404;
throw e;
}
let components;
try {
components = JSON.parse(
readFile(componentsPath, "utf8"));
} catch(e) {
const e = new Error(`could not parse JSON - ${componentsPath} `);
throw e;
}
return $(components, [
keys,
reduce((obj, k) => {
obj[`${libname}/${k}`] = components[k]
return obj;
}, {})
])
}
let libs;
if(!lib) {
const appPath = appPackageFolder(config, appname);
const pages = JSON.parse(await readFile(
`${appPath}/pages.json`,
"utf8"));
if(!pages.componentLibraries) return [];
libs = pages.componentLibraries;
} else {
libs = [lib];
}
return $(libs, [
map(componentsInLibrary),
flatten
]);
}

View File

@ -65,9 +65,9 @@ const applictionVersionPath = (appname, versionId) =>
const publicPaths = (appPath) => ({
mainUiPath: resolve(join(
__dirname, appPath, "ui", "main", "public")),
__dirname, appPath, "public", "main")),
unauthenticatedUiPath: resolve(join(
__dirname, appPath, "ui", "unauthenticated", "public"))
__dirname, appPath, "public", "unauthenticated"))
});