state event handlers - API

This commit is contained in:
Michael Shanks 2019-09-23 06:08:06 +01:00
parent 9953989de6
commit bfda230aae
13 changed files with 266 additions and 11 deletions

View File

@ -2,5 +2,5 @@ Contributors
=== ===
* Michael Shanks - [@mjashanks](https://github.com/mjashanks) * Michael Shanks - [@mjashanks](https://github.com/mjashanks)
* Dan - [@danbudi](https://github.com/danbudi) * Daniel Loudon - [@danbudi](https://github.com/marblekirby)
* Joe - [@joebudi](https://github.com/joebudi) * Joe - [@joebudi](https://github.com/joebudi)

View File

@ -0,0 +1,61 @@
import { ERROR } from "../state/standardState";
import {loadRecord} from "./loadRecord";
import {listRecords} from "./listRecords";
export const createApi = ({rootPath, setState, getState}) => {
const apiCall = (method) => ({url, body, notFound, badRequest, forbidden}) => {
fetch(url, {
method: method,
headers: {
'Content-Type': 'application/json',
},
body: body && JSON.stringify(body),
credentials: "same-origin"
}).then(r => {
switch (r.status) {
case 200:
return r.json();
case 404:
return error(notFound || `${url} Not found`);
case 400:
return error(badRequest || `${url} Bad Request`);
case 403:
return error(forbidden || `${url} Forbidden`);
default:
if(r.status.toString().startsWith("2")
|| r.status.toString().startsWith("3"))
return r.json()
else
return error(`${url} - ${r.statusText}`);
}
});
}
const post = apiCall("POST");
const get = apiCall("GET");
const patch = apiCall("PATCH");
const del = apiCall("DELETE");
const ERROR_MEMBER = "##error";
const error = message => {
const e = {};
e[ERROR_MEMBER] = message;
setState(ERROR, message);
return e;
}
const isSuccess = obj => !!obj[ERROR_MEMBER];
const apiOpts = {
rootPath, setState, getState, isSuccess, error,
post, get, patch, delete:del
};
return {
loadRecord:loadRecord(apiOpts),
listRecords: listRecords(api)
}
}

View File

@ -0,0 +1,18 @@
export const listRecords = api => async ({indexKey, statePath}) => {
if(!recordKey) {
api.error("Load Record: record key not set");
return;
}
if(!statePath) {
api.error("Load Record: state path not set");
return;
}
const records = get({
url:`${rootPath}/api/listRecords/${indexKey}`
});
if(api.isSuccess(records))
api.setState(statePath, records);
}

View File

@ -0,0 +1,21 @@
export const loadRecord = (api) => async ({recordKey, statePath}) => {
if(!recordKey) {
api.error("Load Record: record key not set");
return;
}
if(!statePath) {
api.error("Load Record: state path not set");
return;
}
const record = await get({
url:`${rootPath}/api/record/${key}`
});
if(api.isSuccess(record))
api.setState(statePath, record);
}

View File

@ -0,0 +1,29 @@
export const saveRecord = (api) => async ({statePath}) => {
if(!statePath) {
api.error("Load Record: state path not set");
return;
}
const recordtoSave = api.getState(statePath);
if(!recordtoSave) {
api.error(`there is no record in state: ${statePath}`);
return;
}
if(!recordtoSave.key) {
api.error(`item in state does not appear to be a record - it has no key (${statePath})`);
return;
}
const savedRecord = await post({
url:`${rootPath}/api/record/${recordtoSave.key}`,
body: recordtoSave
});
if(api.isSuccess(record))
api.setState(statePath, savedRecord);
}

View File

@ -0,0 +1,15 @@
export const createCoreApp = (appDefinition, user) => {
const app = {
datastore: null,
crypto:null,
publish: () => {},
hierarchy: appDefinition.hierarchy,
actions: appDefinition.actions
};
app.asUser(user);
return app;
}

View File

@ -0,0 +1,17 @@
import { createCoreApp } from "./createCoreApp"
import {
getNew, getNewChild
} from "../../../core/src/recordApi/getNew";
export const createCoreApi = (appDefinition, user) => {
const app = createCoreApp(appDefinition, user);
return {
recordApi: {
getNew: getNew(app),
getNewChild: getNewChild(app)
}
}
}

View File

@ -5,8 +5,9 @@ import {
import {writable} from "svelte/store"; import {writable} from "svelte/store";
import { $ } from "./core/common"; import { $ } from "./core/common";
import { setupBinding } from "./state/stateBinding"; import { setupBinding } from "./state/stateBinding";
import { createCoreApi } from "./core";
export const createApp = componentLibraries => { export const createApp = (componentLibraries, appDefinition, user) => {
const initialiseComponent = (props, htmlElement) => { const initialiseComponent = (props, htmlElement) => {
@ -14,7 +15,7 @@ export const createApp = componentLibraries => {
if(!componentName || !libName) return; if(!componentName || !libName) return;
const {initialProps, bind} = setupBinding(store, props); const {initialProps, bind} = setupBinding(store, props, coreApi);
const component = new (componentLibraries[libName][componentName])({ const component = new (componentLibraries[libName][componentName])({
target: htmlElement, target: htmlElement,
@ -25,6 +26,7 @@ export const createApp = componentLibraries => {
} }
const coreApi = createCoreApi(appDefinition, user);
const store = writable({}); const store = writable({});
const _app = { const _app = {

View File

@ -4,6 +4,12 @@ import { createApp } from "./createApp";
export const loadBudibase = async (componentLibraries, props) => { export const loadBudibase = async (componentLibraries, props) => {
const appDefinition = window["##BUDIBASE_APPDEFINITION##"]; const appDefinition = window["##BUDIBASE_APPDEFINITION##"];
const user = localStorage.getItem("budibase:user") || {
name: "annonymous",
permissions : [],
isUser:false,
temp:false
}
if(!componentLibraries) { if(!componentLibraries) {
@ -22,7 +28,7 @@ export const loadBudibase = async (componentLibraries, props) => {
props = appDefinition.props; props = appDefinition.props;
} }
const _app = createApp(componentLibraries); const _app = createApp(componentLibraries, user);
_app.initialiseComponent( _app.initialiseComponent(
props, props,
document.body); document.body);

View File

@ -0,0 +1,63 @@
import { ERROR } from "./standardState";
export const getNewChildRecordToState = (store, coreApi) =>
({recordKey, collectionName,childRecordType,statePath}) => {
const error = errorHandler(setState);
try {
if(!recordKey) {
error("getNewChild > recordKey not set");
return;
}
if(!collectionName) {
error("getNewChild > collectionName not set");
return;
}
if(!childRecordType) {
error("getNewChild > childRecordType not set");
return;
}
if(!statePath) {
error("getNewChild > statePath not set");
return;
}
const rec = coreApi.recordApi.getNewChild(recordKey, collectionName, childRecordType);
setState(store, statePath, rec);
}
catch(e) {
error(e.message);
}
}
export const getNewRecordToState = (store, coreApi) =>
({collectionKey,childRecordType,statePath}) => {
const error = errorHandler(setState);
try {
if(!collectionKey) {
error("getNewChild > collectionKey not set");
return;
}
if(!childRecordType) {
error("getNewChild > childRecordType not set");
return;
}
if(!statePath) {
error("getNewChild > statePath not set");
return;
}
const rec = coreApi.recordApi.getNew(collectionKey, childRecordType);
setState(store, statePath, rec);
}
catch(e) {
error(e.message);
}
}
const errorHandler = setState => message => setState(ERROR, message);

View File

@ -1,22 +1,43 @@
import { import { setState } from "./setState";
setState import { getState } from "./getState";
} from "./setState";
import { import {
isArray, isUndefined isArray, isUndefined
} from "lodash/fp"; } from "lodash/fp";
import { createApi } from "../api";
import {
getNewChildRecordToState, getNewRecordToState
} from "./coreHandlers";
export const EVENT_TYPE_MEMBER_NAME = "##eventHandlerType"; export const EVENT_TYPE_MEMBER_NAME = "##eventHandlerType";
export const eventHandlers = store => { export const eventHandlers = (store,coreApi) => {
const handler = (parameters, execute) => ({ const handler = (parameters, execute) => ({
execute, parameters execute, parameters
}); });
const api = createApi({
rootPath:"",
setState: (path, value) => setState(store, path, value),
getState: (path, fallback) => getState(store, path, fallback)
});
const setStateHandler = ({path, value}) => setState(store, path, value); const setStateHandler = ({path, value}) => setState(store, path, value);
return { return {
"Set State": handler(["path", "value"], setStateHandler) "Set State": handler(["path", "value"], setStateHandler),
"Load Record": handler(["recordKey", "statePath"], api.loadRecord),
"List Records": handler(["indexKey", "statePath"], api.listRecords),
"Save Record": handler(["statePath"], api.saveRecord),
"Get New Child Record": handler(
["recordKey", "collectionName", "childRecordType", "statePath"],
getNewChildRecordToState(store, coreApi)),
"Get New Record": handler(
["collectionKey", "childRecordType", "statePath"],
getNewRecordToState(store, coreApi)),
}; };
}; };

View File

@ -0,0 +1,2 @@
export const ERROR = "##error_message";

View File

@ -9,7 +9,7 @@ import {
export const BB_STATE_BINDINGPATH = "##bbstate"; export const BB_STATE_BINDINGPATH = "##bbstate";
export const BB_STATE_FALLBACK = "##bbstatefallback"; export const BB_STATE_FALLBACK = "##bbstatefallback";
const doNothing = () => {}; const doNothing = () => {};
export const setupBinding = (store, props) => { export const setupBinding = (store, props, coreApi) => {
const initialProps = {...props}; const initialProps = {...props};
const boundProps = []; const boundProps = [];
@ -50,7 +50,7 @@ export const setupBinding = (store, props) => {
if(boundProps.length === 0 && componentEventHandlers.length === 0) return; if(boundProps.length === 0 && componentEventHandlers.length === 0) return;
const handlerTypes = eventHandlers(store); const handlerTypes = eventHandlers(store, coreApi);
const unsubscribe = store.subscribe(s => { const unsubscribe = store.subscribe(s => {
const newProps = {}; const newProps = {};