derived components endpoints

This commit is contained in:
michael shanks 2019-07-26 17:08:59 +01:00
parent ebf5dfeeea
commit af2fc95a6b
21 changed files with 193 additions and 118 deletions

View File

@ -344,17 +344,13 @@ const saveDerivedComponent = store => (derivedComponent) => {
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);
fetch(`/_builder/api/${s.appname}/derivedcomponent`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(derivedComponent),
});
return s;
})

View File

@ -4,8 +4,17 @@ import ComingSoon from "../common/ComingSoon.svelte";
</script>
<ComingSoon name="User Interface Designer"/>
<div class="root">
<div class="uiNav">
</div>
</div>
<style>
.root {
display: grid;
grid-template-columns: [uiNav] 200px [preview] auto;
}
</style>

View File

@ -11,6 +11,5 @@ export const defaultPagesObject = () => ({
},
appBody: "bbapp.unauthenticated.json"
},
componentLibraries: ["./components"],
derivedComponents:[]
componentLibraries: ["./components"]
});

View File

@ -5,7 +5,7 @@ const { take, takeRight, last } = require("lodash/fp");
const { splitKey, $, joinKey } = require("budibase-core").common;
const { unzipTarGzPackageToRuntime } = require("../../utilities/targzAppPackage");
const { getRuntimePackageDirectory } = require("../../utilities/runtimePackages");
const { exists } = require("../../utilities/fsawait");
const { pathExists } = require("fs-extra");
const createInstanceDb = require("../../initialise/createInstanceDb");
const { createWriteStream } = require("fs");
const { applictionVersionPackage } = require("../../utilities/createAppPackage");
@ -33,7 +33,7 @@ module.exports = (context) => {
application.name,
versionId);
if(!await exists(runtimeDir))
if(!await pathExists(runtimeDir))
await downloadAppPackage(apis, instance, application.name, versionId);
const dbConfig = await createInstanceDb(

View File

@ -0,0 +1 @@
{"_name":"newTextBox","_component":"./customComponents/textbox","label":"something else"}

View File

@ -0,0 +1 @@
{"oldname":"newTextBox","newName":"anotherSubFolder/newTextBox"}

View File

@ -2,7 +2,7 @@ const create = require("./createMasterDb");
const argv = require("yargs").argv
const readline = require('readline');
const { promisify } = require('util');
const { mkdir, rimraf } = require("../utilities/fsawait");
const { mkdir, remove } = require("fs-extra");
const budibaseConfig = require("../config");
const buildAppContext = require("../initialise/buildAppContext");
@ -61,7 +61,7 @@ const question = async (q) => {
if(cleanDev) {
try {
await rimraf(rootconfig.rootPath);
await remove(rootconfig.rootPath);
}
catch(_){}
await mkdir(rootconfig.rootPath);

View File

@ -1,11 +1,10 @@
const { ncp } = require('ncp');
const { masterAppPackage } = require("../utilities/createAppPackage");
const { mkdir, rimraf, exists } = require("../utilities/fsawait");
const { mkdir, remove, pathExists, copy } = require("fs-extra");
const { runtimePackagesDirectory } = require("../utilities/runtimePackages");
const copyfolder = (source, destination) =>
new Promise((resolve, reject) => {
ncp(source, destination, function (err) {
copy(source, destination, function (err) {
if (err) {
reject(err);
} else {
@ -19,9 +18,9 @@ module.exports = async (context, bbMaster, latestAppsFolder) => {
// create runtime folder
// copy master into /master/latest
if(await exists(runtimePackagesDirectory)) {
if(await pathExists(runtimePackagesDirectory)) {
try {
await rimraf(runtimePackagesDirectory);
await remove(runtimePackagesDirectory);
} catch(err) {
console.log(err);
}

View File

@ -4,8 +4,14 @@ const StatusCodes = require("../utilities/statusCodes");
const fs = require("fs");
const { resolve } = require("path");
const send = require('koa-send');
const { getPackageForBuilder, getRootComponents: getComponents,
savePackage, getApps } = require("../utilities/builder");
const {
getPackageForBuilder,
getRootComponents,
savePackage,
getApps,
saveDerivedComponent,
renameDerivedComponent
} = require("../utilities/builder");
const builderPath = resolve(__dirname, "../builder");
@ -119,23 +125,11 @@ module.exports = (config, app) => {
ctx.response.status = StatusCodes.OK;
})
.get("/_builder/api/apps", async (ctx) => {
if(!config.dev) {
ctx.response.status = StatusCodes.FORBIDDEN;
ctx.response.body = "run in dev mode to access builder";
return;
}
ctx.body = await getApps(config);
ctx.response.status = StatusCodes.OK;
})
.get("/_builder/api/:appname/appPackage", async (ctx) => {
if(!config.dev) {
ctx.response.status = StatusCodes.FORBIDDEN;
ctx.body = "run in dev mode to access builder";
return;
}
ctx.body = await getPackageForBuilder(
config,
ctx.params.appname);
@ -143,27 +137,16 @@ module.exports = (config, app) => {
})
.post("/_builder/api/:appname/appPackage", async (ctx) => {
if(!config.dev) {
ctx.response.status = StatusCodes.FORBIDDEN;
ctx.body = "run in dev mode to access builder";
return;
}
ctx.body = await savePackage(
config,
ctx.params.appname,
ctx.request.body);
ctx.response.status = StatusCodes.OK;
})
.get("/_builder/api/:appname/components", async (ctx) => {
if(!config.dev) {
ctx.response.status = StatusCodes.FORBIDDEN;
ctx.body = "run in dev mode to access builder";
return;
}
.get("/_builder/api/:appname/rootcomponents", async (ctx) => {
try {
ctx.body = getComponents(
ctx.body = getRootComponents(
config,
ctx.params.appname,
ctx.query.lib);
@ -176,6 +159,28 @@ module.exports = (config, app) => {
}
}
})
.post("/_builder/api/:appname/derivedcomponent", async (ctx) => {
await saveDerivedComponent(
config,
ctx.params.appname,
ctx.request.body);
ctx.response.status = StatusCodes.OK;
})
.patch("/_builder/api/:appname/derivedcomponent", async (ctx) => {
await renameDerivedComponent(
config,
ctx.params.appname,
ctx.request.body.oldname,
ctx.request.body.newname);
ctx.response.status = StatusCodes.OK;
})
.delete("/_builder/api/:appname/derivedcomponent", async (ctx) => {
await deleteDerivedComponent(
config,
ctx.params.appname,
ctx.request.body.name);
ctx.response.status = StatusCodes.OK;
})
.get("/:appname", async (ctx) => {
await send(ctx, "/index.html", { root: ctx.publicPath });
})

View File

@ -15,14 +15,13 @@
"@koa/router": "^8.0.0",
"argon2": "^0.23.0",
"budibase-core": "file:../core/dist",
"fs-extra": "^8.1.0",
"koa": "^2.7.0",
"koa-body": "^4.1.0",
"koa-send": "^5.0.0",
"koa-session": "^5.12.0",
"koa-static": "^5.0.0",
"lodash": "^4.17.13",
"ncp": "^2.0.0",
"rimraf": "^2.6.3",
"tar-fs": "^2.0.0",
"uuid": "^3.3.2",
"yargs": "^13.2.4"

View File

@ -1,5 +1,5 @@
const statusCodes = require("../utilities/statusCodes");
const { readFile } = require("../utilities/fsawait");
const { readFile } = require("fs-extra");
const { timeout } = require("./helpers");
module.exports = (app, appName, userName) => {

View File

@ -7,6 +7,7 @@ const testMoreComponents = require("../appPackages/testApp/moreCustomComponents/
const statusCodes = require("../utilities/statusCodes");
const derivedComponent1 = require("../appPackages/testApp/components/myTextBox.json");
const derivedComponent2 = require("../appPackages/testApp/components/subfolder/otherTextBox.json");
const { readJSON, pathExists } = require("fs-extra");
const app = require("./testApp")();
@ -64,3 +65,48 @@ it("/apppackage should get derivedComponents", async () => {
expect(body.derivedComponents).toEqual(expectedComponents);
});
it("should be able to create new derived component", async () => {
const newDerivedComponent = {
_name: "newTextBox",
_component: "./customComponents/textbox",
label: "something"
};
await app.post("/_builder/api/testApp/derivedcomponent", newDerivedComponent)
.expect(statusCodes.OK);
const componentFile = "./appPackages/testApp/components/newTextBox.json";
expect(await pathExists(componentFile)).toBe(true);
expect(await readJSON(componentFile)).toEqual(newDerivedComponent);
});
it("should be able to update derived component", async () => {
const updatedDerivedComponent = {
_name: "newTextBox",
_component: "./customComponents/textbox",
label: "something else"
};
await app.post("/_builder/api/testApp/derivedcomponent", updatedDerivedComponent)
.expect(statusCodes.OK);
const componentFile = "./appPackages/testApp/components/newTextBox.json";
expect(await readJSON(componentFile)).toEqual(updatedDerivedComponent);
});
it("should be able to rename derived component", async () => {
await app.post("/_builder/api/testApp/derivedcomponent", {
oldname: "newTextBox", newName: "anotherSubFolder/newTextBox"
}).expect(statusCodes.OK);
const oldcomponentFile = "./appPackages/testApp/components/newTextBox.json";
const newcomponentFile = "./appPackages/testApp/components/anotherSubFolder/newTextBox.json";
expect(await pathExists(oldcomponentFile)).toBe(false);
expect(await pathExists(newcomponentFile)).toBe(true);
});
it("should be able to delete derived component", async () => {
});

View File

@ -1,6 +1,6 @@
const statusCodes = require("../utilities/statusCodes");
const constructHierarchy = require("../utilities/constructHierarchy");
const { readFile } = require("../utilities/fsawait");
const { readFile } = require("fs-extra");
const { hierarchy } = require("budibase-core");
const { take } = require("lodash/fp");
const masterAppDefinition = constructHierarchy(

View File

@ -1,6 +1,6 @@
const statusCodes = require("../utilities/statusCodes");
const constructHierarchy = require("../utilities/constructHierarchy");
const { readFile } = require("../utilities/fsawait");
const { readFile } = require("fs-extra");
const {getRecordApi, getAuthApi} = require("budibase-core");
const masterAppDefinition = constructHierarchy(
require("../appPackages/master/appDefinition.json"));

View File

@ -1,5 +1,5 @@
const statusCodes = require("../utilities/statusCodes");
const { readFile } = require("../utilities/fsawait");
const { readFile } = require("fs-extra");
module.exports = (app) => {

View File

@ -1,5 +1,5 @@
const app = require("../app");
const { rimraf, mkdir } = require("../utilities/fsawait");
const { remove, mkdir } = require("fs-extra");
const createMasterDb = require("../initialise/createMasterDb");
const request = require("supertest");
const fs = require("fs");
@ -123,7 +123,7 @@ const getRequest = (server, url) =>
const reInitialize = async () => {
try {
await rimraf(config.datastoreConfig.rootPath);
await remove(config.datastoreConfig.rootPath);
} catch(_){}
await mkdir(config.datastoreConfig.rootPath);

View File

@ -3,15 +3,19 @@ const {
appsFolder
} = require("./createAppPackage");
const {
writeFile,
readFile,
readJSON,
writeJSON,
readdir,
exists,
stat
} = require("./fsawait");
stat,
ensureDir,
rename,
unlink
} = require("fs-extra");
const {
resolve,
join
join,
dirname
} = require("path");
const { $ } = require("budibase-core").common;
const {
@ -25,18 +29,12 @@ const {merge} = require("lodash");
module.exports.getPackageForBuilder = async (config, appname) => {
const appPath = appPackageFolder(config, appname);
const pages = JSON.parse(await readFile(
`${appPath}/pages.json`,
"utf8"));
const pages = await readJSON(`${appPath}/pages.json`);
return ({
appDefinition: JSON.parse(await readFile(
`${appPath}/appDefinition.json`,
"utf8")),
appDefinition: await readJSON(`${appPath}/appDefinition.json`),
accessLevels: JSON.parse(await readFile(
`${appPath}/access_levels.json`,
"utf8")),
accessLevels: await readJSON(`${appPath}/access_levels.json`),
pages,
@ -50,26 +48,55 @@ module.exports.getPackageForBuilder = async (config, appname) => {
module.exports.savePackage = async (config, appname, pkg) => {
const appPath = appPackageFolder(config, appname);
await writeFile(
await writeJSON(
`${appPath}/appDefinition.json`,
JSON.stringify(pkg.appDefinition),
"utf8");
pkg.appDefinition);
await writeFile(
await writeJSON(
`${appPath}/access_levels.json`,
JSON.stringify(pkg.accessLevels),
"utf8");
pkg.accessLevels);
await writeFile(
await writeJSON(
`${appPath}/pages.json`,
JSON.stringify(pkg.pages),
"utf8");
pkg.pages);
}
module.exports.getApps = async (config) =>
await readdir(appsFolder(config));
const componentPath = (appPath, name) =>
join(appPath, "components", name + ".json");
module.exports.saveDerivedComponent = async (config, appname, component) => {
const appPath = appPackageFolder(config, appname);
await writeJSON(
componentPath(appPath, component._name),
component,
{encoding:"utf8", flag:"w"});
}
module.exports.renameDerivedComponent = async (config, appname, oldName, newName) => {
const appPath = appPackageFolder(config, appname);
const oldComponentPath = componentPath(
appPath, oldName);
const newComponentPath = join(
appPath, newName);
await ensureDir(dirname(newComponentPath));
await rename(
oldComponentPath,
newComponentPath);
}
module.exports.deleteDerivedComponent = async (config, appname, name) => {
const appPath = appPackageFolder(config, appname);
await unlink(componentPath(appPath, name));
}
const getRootComponents = async (appPath, pages ,lib) => {
const componentsInLibrary = async (libname) => {
@ -88,8 +115,7 @@ const getRootComponents = async (appPath, pages ,lib) => {
let components;
try {
components = JSON.parse(
await readFile(componentsPath, "utf8"));
components = await readJSON(componentsPath);
} catch(e) {
const err = `could not parse JSON - ${componentsPath} : ${e.message}`;
throw new Error(err);
@ -106,9 +132,8 @@ const getRootComponents = async (appPath, pages ,lib) => {
let libs;
if(!lib) {
pages = pages || JSON.parse(await readFile(
`${appPath}/pages.json`,
"utf8"));
pages = pages || await readJSON(
`${appPath}/pages.json`);
if(!pages.componentLibraries) return [];
@ -142,8 +167,8 @@ const fetchDerivedComponents = async (appPath, relativePath = "") => {
if(!item.endsWith(".json")) continue;
const component = JSON.parse(
await readFile(itemFullPath, "utf8"));
const component =
await readJSON(itemFullPath);
component._name = itemRelativePath
.substring(0, itemRelativePath.length - 5)

View File

@ -1,21 +0,0 @@
const util = require("util");
const fs = require("fs");
module.exports.readFile = util.promisify(fs.readFile);
module.exports.readdir = util.promisify(fs.readdir);
module.exports.writeFile = util.promisify(fs.writeFile);
module.exports.rimraf = util.promisify(require("rimraf"));
module.exports.mkdir = util.promisify(fs.mkdir);
module.exports.unlink = util.promisify(fs.unlink);
module.exports.stat = util.promisify(fs.stat);
module.exports.exists = async (path) => {
try {
await util.promisify(fs.access)(
path
);
} catch (e) {
return false;
}
return true;
};

View File

@ -5,7 +5,7 @@ const fs = require("fs");
const tar = require('tar-fs');
const zlib = require("zlib");
const { join, dirname, sep } = require("path");
const { exists, mkdir, unlink, stat } = require("../utilities/fsawait");
const { exists, mkdir, unlink, stat } = require("fs-extra");
const { getRuntimePackageDirectory, getRuntimeAppsDirectory } = require("./runtimePackages");
module.exports.createTarGzPackage = async (config, appName) => {
@ -33,7 +33,7 @@ module.exports.unzipTarGzPackageToRuntime = async (src, appName, versionId) => {
if(await exists(appDir)) {
if(await exists(versionDir)) {
await rimraf(versionDir);
await remove(versionDir);
}
} else {
await mkdir(appDir);

View File

@ -1373,6 +1373,15 @@ fs-constants@^1.0.0:
resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad"
integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==
fs-extra@^8.1.0:
version "8.1.0"
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0"
integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==
dependencies:
graceful-fs "^4.2.0"
jsonfile "^4.0.0"
universalify "^0.1.0"
fs-minipass@^1.2.5:
version "1.2.6"
resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.6.tgz#2c5cc30ded81282bfe8a0d7c7c1853ddeb102c07"
@ -1458,7 +1467,7 @@ globals@^11.1.0:
resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e"
integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==
graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2:
graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.0.tgz#8d8fdc73977cb04104721cb53666c1ca64cd328b"
integrity sha512-jpSvDPV4Cq/bgtpndIWbI5hmYxhQGHPC4d4cqBPb4DLniCfhJokdXhwhaDuLBGLQdvvRum/UiX6ECVIPvDXqdg==
@ -2339,6 +2348,13 @@ json5@^2.1.0:
dependencies:
minimist "^1.2.0"
jsonfile@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb"
integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=
optionalDependencies:
graceful-fs "^4.1.6"
jsprim@^1.2.2:
version "1.4.1"
resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2"
@ -2744,11 +2760,6 @@ natural-compare@^1.4.0:
resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=
ncp@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ncp/-/ncp-2.0.0.tgz#195a21d6c46e361d2fb1281ba38b91e9df7bdbb3"
integrity sha1-GVoh1sRuNh0vsSgbo4uR6d9727M=
needle@^2.2.1:
version "2.4.0"
resolved "https://registry.yarnpkg.com/needle/-/needle-2.4.0.tgz#6833e74975c444642590e15a750288c5f939b57c"
@ -3964,6 +3975,11 @@ union-value@^1.0.0:
is-extendable "^0.1.1"
set-value "^2.0.1"
universalify@^0.1.0:
version "0.1.2"
resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"
integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==
unpipe@1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"