adding in routes for components and pages
This commit is contained in:
parent
70082147c7
commit
4891c6de80
|
@ -1,10 +1,14 @@
|
||||||
import {hierarchy as hierarchyFunctions,
|
import {hierarchy as hierarchyFunctions,
|
||||||
common, getTemplateApi } from "budibase-core";
|
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";
|
find, isEmpty, groupBy, reduce} from "lodash/fp";
|
||||||
import {chain, getNode, validate,
|
import {chain, getNode, validate,
|
||||||
constructHierarchy, templateApi} from "../common/core";
|
constructHierarchy, templateApi} from "../common/core";
|
||||||
import {writable} from "svelte/store";
|
import {writable} from "svelte/store";
|
||||||
|
import { defaultPagesObject } from "../userInterface/pagesParsing/defaultPagesObject"
|
||||||
|
|
||||||
|
const pipe = common.$;
|
||||||
|
|
||||||
export const getStore = () => {
|
export const getStore = () => {
|
||||||
|
|
||||||
|
@ -14,6 +18,11 @@ export const getStore = () => {
|
||||||
hierarchy: {},
|
hierarchy: {},
|
||||||
actions: [],
|
actions: [],
|
||||||
triggers: [],
|
triggers: [],
|
||||||
|
pages:defaultPagesObject(),
|
||||||
|
mainUi:{},
|
||||||
|
unauthenticatedUi:{},
|
||||||
|
derivedComponents:[],
|
||||||
|
rootComponents:[],
|
||||||
currentNodeIsNew: false,
|
currentNodeIsNew: false,
|
||||||
errors: [],
|
errors: [],
|
||||||
activeNav: "database",
|
activeNav: "database",
|
||||||
|
@ -41,6 +50,9 @@ export const getStore = () => {
|
||||||
store.saveLevel = saveLevel(store);
|
store.saveLevel = saveLevel(store);
|
||||||
store.deleteLevel = deleteLevel(store);
|
store.deleteLevel = deleteLevel(store);
|
||||||
store.setActiveNav = setActiveNav(store);
|
store.setActiveNav = setActiveNav(store);
|
||||||
|
store.saveDerivedComponent = saveDerivedComponent(store);
|
||||||
|
store.refreshComponents = refreshComponents(store);
|
||||||
|
store.addComponentLibrary = addComponentLibrary(store);
|
||||||
return store;
|
return store;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -312,30 +324,107 @@ const deleteLevel = store => level => {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const setActiveNav = databaseStore => navName => {
|
const setActiveNav = store => navName => {
|
||||||
databaseStore.update(db => {
|
store.update(s => {
|
||||||
db.activeNav = navName;
|
s.activeNav = navName;
|
||||||
return db;
|
return s;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const createShadowHierarchy = hierarchy =>
|
const createShadowHierarchy = hierarchy =>
|
||||||
constructHierarchy(JSON.parse(JSON.stringify(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 = {
|
const appDefinition = {
|
||||||
hierarchy:db.hierarchy,
|
hierarchy:s.hierarchy,
|
||||||
triggers:db.triggers,
|
triggers:s.triggers,
|
||||||
actions: groupBy("name")(db.actions)
|
actions: groupBy("name")(s.actions),
|
||||||
|
pages:s.pages,
|
||||||
|
mainUi: s.mainUi,
|
||||||
|
unauthenticatedUi: s.unauthenticatedUi
|
||||||
};
|
};
|
||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
appDefinition,
|
appDefinition,
|
||||||
accessLevels:db.accessLevels
|
accessLevels:s.accessLevels
|
||||||
}
|
}
|
||||||
|
|
||||||
fetch(`/_builder/api/${db.appname}/appPackage`, {
|
fetch(`/_builder/api/${s.appname}/appPackage`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
|
|
|
@ -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:[]
|
||||||
|
});
|
|
@ -1,7 +1,7 @@
|
||||||
import {
|
import {
|
||||||
validatePages,
|
validatePages,
|
||||||
validatePage
|
validatePage
|
||||||
} from "../src/userInterface/propsDefinitionParsing/validatePages";
|
} from "../src/userInterface/pagesParsing/validatePages";
|
||||||
|
|
||||||
const validPages = () => ({
|
const validPages = () => ({
|
||||||
"main" : {
|
"main" : {
|
||||||
|
@ -85,8 +85,37 @@ describe("validate pages", () => {
|
||||||
expect(errors).toEqual([]);
|
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);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
Binary file not shown.
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"textbox" : {
|
||||||
|
"path": "./textbox",
|
||||||
|
"name": "Textbox",
|
||||||
|
"description": "A text input, with a label",
|
||||||
|
"props": {
|
||||||
|
"label": "string"
|
||||||
|
},
|
||||||
|
"tags": ["textboxt", "input", "text"]
|
||||||
|
}
|
||||||
|
}
|
|
@ -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"]
|
||||||
|
}
|
|
@ -4,7 +4,7 @@ const StatusCodes = require("../utilities/statusCodes");
|
||||||
const fs = require("fs");
|
const fs = require("fs");
|
||||||
const { resolve } = require("path");
|
const { resolve } = require("path");
|
||||||
const send = require('koa-send');
|
const send = require('koa-send');
|
||||||
const { getPackageForBuilder,
|
const { getPackageForBuilder, getComponents,
|
||||||
savePackage, getApps } = require("../utilities/builder");
|
savePackage, getApps } = require("../utilities/builder");
|
||||||
|
|
||||||
const builderPath = resolve(__dirname, "../builder");
|
const builderPath = resolve(__dirname, "../builder");
|
||||||
|
@ -152,6 +152,27 @@ module.exports = (config, app) => {
|
||||||
ctx.request.body);
|
ctx.request.body);
|
||||||
ctx.response.status = StatusCodes.OK;
|
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) => {
|
.get("/:appname", async (ctx) => {
|
||||||
await send(ctx, "/index.html", { root: ctx.publicPath });
|
await send(ctx, "/index.html", { root: ctx.publicPath });
|
||||||
})
|
})
|
||||||
|
|
|
@ -7,7 +7,7 @@ module.exports = (app) => {
|
||||||
const response = await app.get("/testApp")
|
const response = await app.get("/testApp")
|
||||||
.expect(statusCodes.OK);
|
.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);
|
expect(response.text).toBe(expectedIndexHtml);
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ module.exports = (app) => {
|
||||||
const response = await app.get("/testApp/app.js")
|
const response = await app.get("/testApp/app.js")
|
||||||
.expect(statusCodes.OK);
|
.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);
|
expect(response.text).toBe(expectedFile);
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ module.exports = (app) => {
|
||||||
.set("cookie", app.credentials.testAppUser1.cookie)
|
.set("cookie", app.credentials.testAppUser1.cookie)
|
||||||
.expect(statusCodes.OK);
|
.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);
|
expect(response.text).toBe(expectedIndexHtml);
|
||||||
|
|
||||||
|
@ -39,7 +39,7 @@ module.exports = (app) => {
|
||||||
.set("cookie", app.credentials.testAppUser1.cookie)
|
.set("cookie", app.credentials.testAppUser1.cookie)
|
||||||
.expect(statusCodes.OK);
|
.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);
|
expect(response.text).toBe(expectedFile);
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,22 @@
|
||||||
const { appPackageFolder, appsFolder } = require("./createAppPackage");
|
const {
|
||||||
const { writeFile, readFile, readdir } = require("./fsawait");
|
appPackageFolder,
|
||||||
const { pipe : $ } = require("budibase-core").common;
|
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) => {
|
module.exports.getPackageForBuilder = async (config, appname) => {
|
||||||
const appPath = appPackageFolder(config, appname);
|
const appPath = appPackageFolder(config, appname);
|
||||||
|
@ -11,12 +27,15 @@ module.exports.getPackageForBuilder = async (config, appname) => {
|
||||||
|
|
||||||
accessLevels: JSON.parse(await readFile(
|
accessLevels: JSON.parse(await readFile(
|
||||||
`${appPath}/access_levels.json`,
|
`${appPath}/access_levels.json`,
|
||||||
|
"utf8")),
|
||||||
|
|
||||||
|
pages: JSON.parse(await readFile(
|
||||||
|
`${appPath}/pages.json`,
|
||||||
"utf8"))
|
"utf8"))
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
module.exports.savePackage = async (config, appname, pkg) => {
|
module.exports.savePackage = async (config, appname, pkg) => {
|
||||||
const appPath = appPackageFolder(config, appname);
|
const appPath = appPackageFolder(config, appname);
|
||||||
await writeFile(
|
await writeFile(
|
||||||
|
@ -29,10 +48,68 @@ module.exports.savePackage = async (config, appname, pkg) => {
|
||||||
JSON.stringify(pkg.accessLevels),
|
JSON.stringify(pkg.accessLevels),
|
||||||
"utf8");
|
"utf8");
|
||||||
|
|
||||||
|
await writeFile(
|
||||||
|
`${appPath}/pages.json`,
|
||||||
|
JSON.stringify(pkg.accessLevels),
|
||||||
|
"utf8");
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports.getApps = async (config) =>
|
module.exports.getApps = async (config) =>
|
||||||
await readdir(appsFolder(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
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -65,9 +65,9 @@ const applictionVersionPath = (appname, versionId) =>
|
||||||
|
|
||||||
const publicPaths = (appPath) => ({
|
const publicPaths = (appPath) => ({
|
||||||
mainUiPath: resolve(join(
|
mainUiPath: resolve(join(
|
||||||
__dirname, appPath, "ui", "main", "public")),
|
__dirname, appPath, "public", "main")),
|
||||||
unauthenticatedUiPath: resolve(join(
|
unauthenticatedUiPath: resolve(join(
|
||||||
__dirname, appPath, "ui", "unauthenticated", "public"))
|
__dirname, appPath, "public", "unauthenticated"))
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue