Browse Source

builder now web based and talking to api

gh-pages
michael shanks 7 years ago
parent
commit
363cbdd6c3
  1. 0
      packages/builder/build/copy.js
  2. 3
      packages/builder/rollup.config.js
  3. 19
      packages/builder/src/NoPackage.svelte
  4. 4
      packages/builder/src/builderStore/createPackage.js
  5. 17
      packages/builder/src/builderStore/index.js
  6. 31
      packages/builder/src/builderStore/store.js
  7. 8
      packages/builder/src/common/core.js
  8. 76
      packages/server/appPackages/testApp/access_levels.json
  9. 85
      packages/server/appPackages/testApp/appDefinition.json
  10. 1
      packages/server/appPackages/testApp2/access_levels.json
  11. 1
      packages/server/appPackages/testApp2/appDefinition.json
  12. 9
      packages/server/appPackages/testApp2/plugins.js
  13. 16
      packages/server/middleware/routers.js
  14. 13
      packages/server/utilities/builder.js
  15. 2
      packages/server/utilities/createAppPackage.js
  16. 1
      packages/server/utilities/fsawait.js

0
packages/builder/build/copy.js

3
packages/builder/rollup.config.js

@ -10,8 +10,9 @@ import copy from 'rollup-plugin-copy';
import browsersync from "rollup-plugin-browsersync"; import browsersync from "rollup-plugin-browsersync";
import proxy from "http-proxy-middleware"; import proxy from "http-proxy-middleware";
const target = 'http://localhost:4001/_builder';
const apiProxy = proxy('/api', { const apiProxy = proxy('/api', {
target: 'http://localhost:4001/_builder', target,
logLevel: "debug", logLevel: "debug",
changeOrigin: true, changeOrigin: true,
cookieDomainRewrite: true, cookieDomainRewrite: true,

19
packages/builder/src/NoPackage.svelte

@ -1,6 +1,7 @@
<script> <script>
import Button from "./common/Button.svelte" import Button from "./common/Button.svelte"
import { database } from "./builderStore";
let errors = []; let errors = [];
@ -12,15 +13,10 @@ let errors = [];
<div> <div>
<div> <div>
<h4 style="margin-bottom: 20px">What would you like not to do?</h4> <h4 style="margin-bottom: 20px">Choose an Application</h4>
<Button color="primary" {#each $database.apps as app}
class="option"> <a href={`#/${app}`} class="app-link">{app}</a>
Create a New Package {/each}
</Button>
<Button color="primary-outline"
class="option">
Import a Package
</Button>
</div> </div>
</div> </div>
</div> </div>
@ -52,4 +48,9 @@ let errors = [];
width:250px; width:250px;
} }
.app-link {
margin-top: 10px;
display: block;
}
</style> </style>

4
packages/builder/src/builderStore/createPackage.js

@ -1,8 +1,8 @@
import {createNewHeirarchy} from "../common/core"; import {createNewHierarchy} from "../common/core";
export const createPackage = (packageInfo, database) => { export const createPackage = (packageInfo, database) => {
packageInfo.createNewPackage(""); packageInfo.createNewPackage("");
const root = createNewHeirarchy(); const root = createNewHierarchy();
database.importAppDefinition({ database.importAppDefinition({
hierarchy:root, hierarchy:root,
actions:[], actions:[],

17
packages/builder/src/builderStore/index.js

@ -2,18 +2,29 @@ import {createPackage} from "./createPackage";
import getStore from "./store"; import getStore from "./store";
import { last } from "lodash/fp"; import { last } from "lodash/fp";
const appName = last(window.location.hash.substr(1).split("/")); export const database = getStore();
export const database = getStore(appName);
export const createNewPackage = () => export const createNewPackage = () =>
createPackage(packageInfo, database); createPackage(packageInfo, database);
export const initialise = async () => { export const initialise = async () => {
try { try {
setupRouter(database);
await database.initialise(); await database.initialise();
} catch(err) { } catch(err) {
console.log(err); console.log(err);
} }
} }
const setupRouter = (writable) => {
const pushState = history.pushState;
history.pushState = () => {
pushState.apply(history, arguments);
//fireEvents('pushState', arguments);
writable.initialise();
}
window.addEventListener('hashchange',()=>{
writable.initialise();
})
}

31
packages/builder/src/builderStore/store.js

@ -1,28 +1,29 @@
import {hierarchy as hierarchyFunctions, import {hierarchy as hierarchyFunctions,
common, getTemplateApi } from "budibase-core"; common, getTemplateApi } from "budibase-core";
import {filter, cloneDeep, sortBy, map, import {filter, cloneDeep, sortBy, map, last,
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";
export const getStore = (appname) => { export const getStore = () => {
const initial = { const initial = {
apps:[],
appname:"",
hierarchy: {}, hierarchy: {},
actions: [], actions: [],
triggers: [], triggers: [],
currentNodeIsNew: false, currentNodeIsNew: false,
errors: [], errors: [],
activeNav: "database", activeNav: "database",
hasAppPackage: (!!appname && appname.length > 0), hasAppPackage: false,
accessLevels: [], accessLevels: [],
currentNode: null}; currentNode: null};
const store = writable(initial); const store = writable(initial);
store.appname = appname; store.initialise = initialise(store, initial);
store.initialise = initialise(store, initial, appname);
store.newChildRecord = newRecord(store, false); store.newChildRecord = newRecord(store, false);
store.newRootRecord = newRecord(store, true); store.newRootRecord = newRecord(store, true);
store.selectExistingNode = selectExistingNode(store); store.selectExistingNode = selectExistingNode(store);
@ -47,9 +48,23 @@ export default getStore;
const initialise = (store, initial) => async () => { const initialise = (store, initial) => async () => {
const pkg = await fetch(`/api/${store.appname}/appPackage`) const appname = window.location.hash
? last(window.location.hash.substr(1).split("/"))
: "";
if(!appname) {
initial.apps = await fetch(`/api/apps`)
.then(r => r.json());
initial.hasAppPackage = false;
store.set(initial);
return initial;
}
const pkg = await fetch(`/api/${appname}/appPackage`)
.then(r => r.json()); .then(r => r.json());
initial.appname = appname;
initial.hasAppPackage = true;
initial.hierarchy = pkg.appDefinition.hierarchy; initial.hierarchy = pkg.appDefinition.hierarchy;
initial.accessLevels = pkg.accessLevels; initial.accessLevels = pkg.accessLevels;
initial.actions = reduce((arr, action) => { initial.actions = reduce((arr, action) => {
@ -320,11 +335,11 @@ const savePackage = (store, db) => {
accessLevels:db.accessLevels accessLevels:db.accessLevels
} }
fetch(`/api/${store.appname}/appPackage`, { fetch(`/api/${db.appname}/appPackage`, {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
}, },
body: JSON.stringify(data), // body data type must match "Content-Type" header body: JSON.stringify(data),
}); });
} }

8
packages/builder/src/common/core.js

@ -15,16 +15,16 @@ export const getNode = (hierarchy, nodeId) =>
export const constructHierarchy = node => { export const constructHierarchy = node => {
if(!node) return node; if(!node) return node;
return templateApi(node).constructHeirarchy(node); return templateApi(node).constructHierarchy(node);
} }
export const createNewHeirarchy = () => { export const createNewHierarchy = () => {
return templateApi().getNewRootLevel(); return templateApi().getNewRootLevel();
} }
export const templateApi = hierarchy => getTemplateApi({heirarchy:hierarchy}) export const templateApi = hierarchy => getTemplateApi({hierarchy})
export const authApi = (hierarchy, actions) => getAuthApi({ export const authApi = (hierarchy, actions) => getAuthApi({
heirarchy:hierarchy, actions: keyBy("name")(actions), publish:()=>{}}) hierarchy, actions: keyBy("name")(actions), publish:()=>{}})
export const allTypes = templateApi({}).allTypes; export const allTypes = templateApi({}).allTypes;

76
packages/server/appPackages/testApp/access_levels.json

@ -1,75 +1 @@
{ {"levels":[{"name":"owner","permissions":[{"type":"create record","nodeKey":"/customers/1-{id}"},{"type":"delete record","nodeKey":"/customers/1-{id}"},{"type":"update record","nodeKey":"/customers/1-{id}"},{"type":"read record","nodeKey":"/customers/1-{id}"},{"type":"create record","nodeKey":"/customers/1-{id}/invoices/2-{id}"},{"type":"update record","nodeKey":"/customers/1-{id}/invoices/2-{id}"},{"type":"delete record","nodeKey":"/customers/1-{id}/invoices/2-{id}"},{"type":"read record","nodeKey":"/customers/1-{id}/invoices/2-{id}"},{"type":"write templates"},{"type":"create user"},{"type":"set password"},{"type":"create temporary access"},{"type":"enable or disable user"},{"type":"write access levels"},{"type":"list users"},{"type":"list access levels"},{"type":"manage index"},{"type":"set user access levels"},{"type":"manage collection"}]}],"version":0}
"levels": [
{
"name": "owner",
"permissions": [
{
"type": "create record",
"nodeKey": "/customers/1-{id}"
},
{
"type": "delete record",
"nodeKey": "/customers/1-{id}"
},
{
"type": "update record",
"nodeKey": "/customers/1-{id}"
},
{
"type": "read record",
"nodeKey": "/customers/1-{id}"
},
{
"type": "create record",
"nodeKey": "/customers/1-{id}/invoices/2-{id}"
},
{
"type": "update record",
"nodeKey": "/customers/1-{id}/invoices/2-{id}"
},
{
"type": "delete record",
"nodeKey": "/customers/1-{id}/invoices/2-{id}"
},
{
"type": "read record",
"nodeKey": "/customers/1-{id}/invoices/2-{id}"
},
{
"type": "write templates"
},
{
"type": "create user"
},
{
"type": "set password"
},
{
"type": "create temporary access"
},
{
"type": "enable or disable user"
},
{
"type": "write access levels"
},
{
"type": "list users"
},
{
"type": "list access levels"
},
{
"type": "manage index"
},
{
"type": "set user access levels"
},
{
"type": "manage collection"
}
]
}
],
"version": 0
}

85
packages/server/appPackages/testApp/appDefinition.json

@ -1,84 +1 @@
{ {"hierarchy":{"name":"root","type":"root","children":[{"name":"customer","type":"record","fields":[{"name":"name","type":"string","typeOptions":{"maxLength":1000,"values":null,"allowDeclaredValuesOnly":false},"label":"name","getInitialValue":"default","getUndefinedValue":"default"}],"children":[{"name":"invoiceyooo","type":"record","fields":[{"name":"amount","type":"number","typeOptions":{"minValue":99999999999,"maxValue":99999999999,"decimalPlaces":2},"label":"amount","getInitialValue":"default","getUndefinedValue":"default"}],"children":[],"validationRules":[],"nodeId":2,"indexes":[],"allidsShardFactor":1,"collectionName":"invoices","isSingle":false}],"validationRules":[],"nodeId":1,"indexes":[],"allidsShardFactor":64,"collectionName":"customers","isSingle":false}],"pathMaps":[],"indexes":[],"nodeId":0},"triggers":[{"actionName":"output_to_file","eventName":"authApi:createUser:onComplete","optionsCreator":"return { filename:'tempaccess' + context.user.name, content:context.result.tempCode };","condition":"!context.password"},{"actionName":"output_to_file","eventName":"authApi:createTemporaryAccess:onComplete","optionsCreator":"return { filename:'tempaccess' + context.userName, content:context.result };","condition":""}],"actions":{"output_to_file":[{"name":"output_to_file","behaviourSource":"main","behaviourName":"outputToFile","initialOptions":{}}]}}
"hierarchy": {
"name": "root",
"type": "root",
"children": [
{
"name": "customer",
"type": "record",
"fields": [
{
"name": "name",
"type": "string",
"typeOptions": {
"maxLength": 1000,
"values": null,
"allowDeclaredValuesOnly": false
},
"label": "name",
"getInitialValue": "default",
"getUndefinedValue": "default"
}
],
"children": [
{
"name": "invoice",
"type": "record",
"fields": [
{
"name": "amount",
"type": "number",
"typeOptions": {
"minValue": 99999999999,
"maxValue": 99999999999,
"decimalPlaces": 2
},
"label": "amount",
"getInitialValue": "default",
"getUndefinedValue": "default"
}
],
"children": [],
"validationRules": [],
"nodeId": 2,
"indexes": [],
"allidsShardFactor": 1,
"collectionName": "invoices",
"isSingle": false
}
],
"validationRules": [],
"nodeId": 1,
"indexes": [],
"allidsShardFactor": 64,
"collectionName": "customers",
"isSingle": false
}
],
"pathMaps": [],
"indexes": [],
"nodeId": 0
},
"actions": {
"output_to_file": {
"name": "output_to_file",
"behaviourSource": "main",
"behaviourName": "outputToFile",
"initialOptions": {}
}
},
"triggers": [
{
"actionName": "output_to_file",
"eventName": "authApi:createUser:onComplete",
"optionsCreator": "return { filename:'tempaccess' + context.user.name, content:context.result.tempCode };",
"condition": "!context.password"
},
{
"actionName": "output_to_file",
"eventName": "authApi:createTemporaryAccess:onComplete",
"optionsCreator": "return { filename:'tempaccess' + context.userName, content:context.result };",
"condition": ""
}
]
}

1
packages/server/appPackages/testApp2/access_levels.json

@ -0,0 +1 @@
{"levels":[{"name":"owner","permissions":[{"type":"create record","nodeKey":"/customers/1-{id}"},{"type":"delete record","nodeKey":"/customers/1-{id}"},{"type":"update record","nodeKey":"/customers/1-{id}"},{"type":"read record","nodeKey":"/customers/1-{id}"},{"type":"create record","nodeKey":"/customers/1-{id}/invoices/2-{id}"},{"type":"update record","nodeKey":"/customers/1-{id}/invoices/2-{id}"},{"type":"delete record","nodeKey":"/customers/1-{id}/invoices/2-{id}"},{"type":"read record","nodeKey":"/customers/1-{id}/invoices/2-{id}"},{"type":"write templates"},{"type":"create user"},{"type":"set password"},{"type":"create temporary access"},{"type":"enable or disable user"},{"type":"write access levels"},{"type":"list users"},{"type":"list access levels"},{"type":"manage index"},{"type":"set user access levels"},{"type":"manage collection"}]}],"version":0}

1
packages/server/appPackages/testApp2/appDefinition.json

@ -0,0 +1 @@
{"hierarchy":{"name":"root","type":"root","children":[{"name":"customer","type":"record","fields":[{"name":"name","type":"string","typeOptions":{"maxLength":1000,"values":null,"allowDeclaredValuesOnly":false},"label":"name","getInitialValue":"default","getUndefinedValue":"default"}],"children":[{"name":"invoiceyooo","type":"record","fields":[{"name":"amount","type":"number","typeOptions":{"minValue":99999999999,"maxValue":99999999999,"decimalPlaces":2},"label":"amount","getInitialValue":"default","getUndefinedValue":"default"}],"children":[],"validationRules":[],"nodeId":2,"indexes":[],"allidsShardFactor":1,"collectionName":"invoices","isSingle":false}],"validationRules":[],"nodeId":1,"indexes":[],"allidsShardFactor":64,"collectionName":"customers","isSingle":false}],"pathMaps":[],"indexes":[],"nodeId":0},"triggers":[{"actionName":"output_to_file","eventName":"authApi:createUser:onComplete","optionsCreator":"return { filename:'tempaccess' + context.user.name, content:context.result.tempCode };","condition":"!context.password"},{"actionName":"output_to_file","eventName":"authApi:createTemporaryAccess:onComplete","optionsCreator":"return { filename:'tempaccess' + context.userName, content:context.result };","condition":""}],"actions":{"undefined":[[[{"name":"output_to_file","behaviourSource":"main","behaviourName":"outputToFile","initialOptions":{}}]]]}}

9
packages/server/appPackages/testApp2/plugins.js

@ -0,0 +1,9 @@
const fs = require("fs");
module.exports = (config) => ({
main: {
outputToFile : ({filename, content}) => {
fs.writeFile(`./tests/.data/${filename}`, content, {encoding:"utf8"});
}
}
})

16
packages/server/middleware/routers.js

@ -2,7 +2,8 @@ const Router = require("@koa/router");
const session = require("./session"); const session = require("./session");
const StatusCodes = require("../utilities/statusCodes"); const StatusCodes = require("../utilities/statusCodes");
const fs = require("fs"); const fs = require("fs");
const { getPackageForBuilder, savePackage } = require("../utilities/builder"); const { getPackageForBuilder,
savePackage, getApps } = require("../utilities/builder");
module.exports = (config, app) => { module.exports = (config, app) => {
@ -74,6 +75,17 @@ module.exports = (config, app) => {
ctx.response.status = StatusCodes.OK; ctx.response.status = StatusCodes.OK;
}) })
.get("/_builder/api/apps", async (ctx) => {
if(!config.dev) {
ctx.request.status = StatusCodes.FORBIDDEN;
ctx.request.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) => { .get("/_builder/api/:appname/appPackage", async (ctx) => {
if(!config.dev) { if(!config.dev) {
ctx.request.status = StatusCodes.FORBIDDEN; ctx.request.status = StatusCodes.FORBIDDEN;
@ -98,7 +110,7 @@ module.exports = (config, app) => {
config, config,
ctx.params.appname, ctx.params.appname,
ctx.request.body); ctx.request.body);
ctx.reqponse.status = StatusCodes.OK; ctx.response.status = StatusCodes.OK;
}) })
.use(async (ctx, next) => { .use(async (ctx, next) => {

13
packages/server/utilities/builder.js

@ -1,5 +1,6 @@
const { appPackageFolder } = require("./createAppPackage"); const { appPackageFolder, appsFolder } = require("./createAppPackage");
const { writeFile, readFile } = require("./fsawait"); const { writeFile, readFile, readdir } = require("./fsawait");
const { pipe : $ } = require("budibase-core").common;
module.exports.getPackageForBuilder = async (config, appname) => { module.exports.getPackageForBuilder = async (config, appname) => {
const appPath = appPackageFolder(config, appname); const appPath = appPackageFolder(config, appname);
@ -28,4 +29,10 @@ module.exports.savePackage = async (config, appname, pkg) => {
JSON.stringify(pkg.accessLevels), JSON.stringify(pkg.accessLevels),
"utf8"); "utf8");
} }
module.exports.getApps = async (config) =>
await readdir(appsFolder(config));

2
packages/server/utilities/createAppPackage.js

@ -30,6 +30,8 @@ const appPackageFolder = (config, appname) =>
module.exports.appPackageFolder = appPackageFolder; module.exports.appPackageFolder = appPackageFolder;
module.exports.appsFolder = (config) => appPackageFolder(config, "");
module.exports.masterAppPackage = (context) => { module.exports.masterAppPackage = (context) => {
const { config } = context; const { config } = context;
const standardPackage = createAppPackage( const standardPackage = createAppPackage(

1
packages/server/utilities/fsawait.js

@ -2,6 +2,7 @@ const util = require("util");
const fs = require("fs"); const fs = require("fs");
module.exports.readFile = util.promisify(fs.readFile); module.exports.readFile = util.promisify(fs.readFile);
module.exports.readdir = util.promisify(fs.readdir);
module.exports.writeFile = util.promisify(fs.writeFile); module.exports.writeFile = util.promisify(fs.writeFile);
module.exports.rimraf = util.promisify(require("rimraf")); module.exports.rimraf = util.promisify(require("rimraf"));
module.exports.mkdir = util.promisify(fs.mkdir); module.exports.mkdir = util.promisify(fs.mkdir);

Loading…
Cancel
Save