searching components

This commit is contained in:
michael shanks 2019-07-28 08:03:11 +01:00
parent 55bf142a95
commit 1413ee6f2c
8 changed files with 280 additions and 44 deletions

View File

@ -0,0 +1,18 @@
const apiCall = (method) => (url, body, returnResponse=false) =>
fetch(url, {
method: method,
headers: {
'Content-Type': 'application/json',
},
body: body && JSON.stringify(body),
}).then(r => returnResponse ? r : r.json());
const post = apiCall("POST");
const get = apiCall("GET");
const patch = apiCall("PATCH");
const del = apiCall("DELETE");
export default {
post, get, patch, delete:del
};

View File

@ -1,12 +1,31 @@
import {hierarchy as hierarchyFunctions,
common, getTemplateApi } from "budibase-core";
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 {
hierarchy as hierarchyFunctions,
common
} from "budibase-core";
import {
filter,
cloneDeep,
sortBy,
map,
last,
keys,
cloneDeep,
concat,
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"
import api from "./api";
const pipe = common.$;
@ -53,6 +72,8 @@ export const getStore = () => {
store.saveDerivedComponent = saveDerivedComponent(store);
store.refreshComponents = refreshComponents(store);
store.addComponentLibrary = addComponentLibrary(store);
store.renameDerivedComponent = renameDerivedComponent(store);
store.deleteDerivedComponent = deleteDerivedComponent(store);
return store;
}
@ -65,20 +86,20 @@ const initialise = (store, initial) => async () => {
: "";
if(!appname) {
initial.apps = await fetch(`/_builder/api/apps`)
.then(r => r.json());
initial.apps = await api.get(`/_builder/api/apps`);
initial.hasAppPackage = false;
store.set(initial);
return initial;
}
const pkg = await fetch(`/_builder/api/${appname}/appPackage`)
.then(r => r.json());
const pkg = await api.get(`/_builder/api/${appname}/appPackage`);
initial.appname = appname;
initial.hasAppPackage = true;
initial.hierarchy = pkg.appDefinition.hierarchy;
initial.accessLevels = pkg.accessLevels;
initial.derivedComponents = pkg.derivedComponents;
initial.rootComponents = pkg.rootComponents;
initial.actions = reduce((arr, action) => {
arr.push(action);
return arr;
@ -344,23 +365,53 @@ const saveDerivedComponent = store => (derivedComponent) => {
s.derivedComponents = derivedComponents;
fetch(`/_builder/api/${s.appname}/derivedcomponent`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(derivedComponent),
});
api.post(`/_builder/api/${s.appname}/derivedcomponent`, derivedComponent);
return s;
})
};
const deleteDerivedComponent = store => name => {
store.update(s => {
const derivedComponents = pipe(s.derivedComponents, [
filter(c => c._name !== name)
]);
s.derivedComponents = derivedComponents;
api.delete(`/_builder/api/${s.appname}/derivedcomponent/${name}`);
return s;
})
}
const renameDerivedComponent = store => (oldname, newname) => {
store.update(s => {
const component = pipe(s.derivedComponents, [
find(c => c._name === name)
]);
component._name = newname;
const derivedComponents = pipe(s.derivedComponents, [
filter(c => c._name !== name),
concat(component)
]);
s.derivedComponent = derivedComponents;
api.delete(`/_builder/api/${s.appname}/derivedcomponent/${name}`);
return s;
})
}
const addComponentLibrary = store => async lib => {
const response =
await fetch(`/_builder/api/${db.appname}/components?${encodeURI(lib)}`);
await api.get(`/_builder/api/${db.appname}/components?${encodeURI(lib)}`,undefined, true);
const success = response.status === 200;
@ -390,8 +441,7 @@ const addComponentLibrary = store => async lib => {
const refreshComponents = store => async () => {
const components =
await fetch(`/_builder/api/${db.appname}/components`)
.then(r => jQuery.json());
await api.get(`/_builder/api/${db.appname}/components`);
const rootComponents = pipe(components, [
keys,
@ -420,11 +470,5 @@ const savePackage = (store, s) => {
accessLevels:s.accessLevels
}
fetch(`/_builder/api/${s.appname}/appPackage`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
});
api.post(`/_builder/api/${s.appname}/appPackage`, data);
}

View File

@ -0,0 +1,82 @@
import {
find,
isUndefined,
filter,
some,
includes
} from "lodash/fp";
import {
common
} from "budibase-core";
const pipe = common.$;
const normalString = s => (s||"").trim().toLowerCase();
const isRootComponent = c => isUndefined(c.inherits);
export const searchAllComponents = (derivedComponents, rootComponents, phrase) => {
const hasPhrase = (...vals) => pipe(vals, [
some(v => includes(phrase)(v))
]);
const rootComponentMatches = c =>
hasPhrase(c.name, ...(c.tags || []));
const derivedComponentMatches = c => {
if(hasPhrase(c.name, ...(c.tags || []))) return true;
const parent = getExactComponent(
derivedComponents,
rootComponents,
c.inherits);
if(isRootComponent(parent))
return rootComponentMatches(parent);
return derivedComponentMatches(parent);
}
return ([
...filter(derivedComponentMatches)(derivedComponents),
...filter(rootComponentMatches)(rootComponents)
]);
}
export const getExactComponent = (derivedComponents, rootComponents, name) => {
const stringEquals = (s1, s2) =>
normalString(s1) === normalString(s2);
const derived = pipe(derivedComponents,[
find(c => stringEquals(c.name, name))
]);
if(derived) return derived;
const root = pipe(rootComponents,[
find(c => stringEquals(c.name, name))
]);
return root;
}
export const getAncestorProps = (derivedComponents, rootComponents, name, found=[]) => {
const thisComponent = getExactComponent(
derivedComponents, rootComponents, name);
if(isRootComponent(thisComponent))
return [thisComponent.props, ...found];
return getAncestorProps(
derivedComponents,
rootComponents,
thisComponent.inherits,
[{_component:thisComponent.inherits, ...thisComponent.props},
...found]);
}

View File

@ -0,0 +1,78 @@
import {
searchAllComponents,
getExactComponent,
getAncestorProps
} from "../src/userInterface/pagesParsing/searchComponents";
describe("searchAllComponents", () => {
it("should match derived component by name", () => {
const results = searchAllComponents(
derivedComponents(),
rootComponents(),
"smalltextbox"
);
expect(results.length).toBe(1);
expect(results[0].name).toBe("common/SmallTextbox");
})
});
describe("getExactComponent", () => {
});
describe("getAncestorProps", () => {
})
const derivedComponents = () => ([
{
inherits:"budibase-components/TextBox",
name: "common/SmallTextbox",
props: {
size: "small"
}
},
{
inherits:"common/SmallTextbox",
name: "common/PasswordBox",
tags: ["mask"],
props: {
isPassword: true
}
},
{
inherits:"budibase-components/Button",
name:"PrimaryButton",
props: {
css:"btn-primary"
}
}
])
const rootComponents = () => ([
{
name: "budibase-components/TextBox",
tags: ["Text", "input"],
props: {
size: {type:"options", options:["small", "medium", "large"]},
isPassword: "boolean",
placeholder: "string",
label:"string"
}
},
{
name: "budibase-components/Button",
tags: ["input"],
props: {
size: {type:"options", options:["small", "medium", "large"]},
css: "string",
content: "component"
}
}
])

View File

@ -1,4 +1,9 @@
{
"_component": "./customComponents/textbox",
"inherits": "./customComponents/textbox",
"name": "myTextBox",
"tags": [],
"description": "A text input, with a label",
"props" : {
"label": "hello"
}
}

View File

@ -1,4 +1,9 @@
{
"_component": "./moreCustomComponents/textbox",
"inherits": "./moreCustomComponents/textbox",
"name":"subfolder/otherTextBox.json",
"tags": [],
"description": "A text input, with a label",
"props" : {
"label": "hello"
}
}

View File

@ -68,8 +68,8 @@ it("/apppackage should get derivedComponents", async () => {
.expect(statusCodes.OK);
const expectedComponents = {
"myTextBox" : {...derivedComponent1, _name:"myTextBox"},
"subfolder/otherTextBox": {...derivedComponent2, _name:"subfolder/otherTextBox"}
"myTextBox" : {...derivedComponent1, name:"myTextBox"},
"subfolder/otherTextBox": {...derivedComponent2, name:"subfolder/otherTextBox"}
};
expect(body.derivedComponents).toEqual(expectedComponents);
@ -77,9 +77,11 @@ it("/apppackage should get derivedComponents", async () => {
it("should be able to create new derived component", async () => {
const newDerivedComponent = {
_name: "newTextBox",
_component: "./customComponents/textbox",
name: "newTextBox",
inherits: "./customComponents/textbox",
props: {
label: "something"
}
};
await app.post("/_builder/api/testApp/derivedcomponent", newDerivedComponent)
@ -93,9 +95,11 @@ it("should be able to create new derived component", async () => {
it("should be able to update derived component", async () => {
const updatedDerivedComponent = {
_name: "newTextBox",
_component: "./customComponents/textbox",
name: "newTextBox",
inherits: "./customComponents/textbox",
props: {
label: "something else"
}
};
await app.post("/_builder/api/testApp/derivedcomponent", updatedDerivedComponent)

View File

@ -41,7 +41,7 @@ module.exports.getPackageForBuilder = async (config, appname) => {
rootComponents: await getRootComponents(appPath, pages),
derivedComponents: keyBy("_name")(
derivedComponents: keyBy("name")(
await fetchDerivedComponents(appPath))
})
@ -73,7 +73,7 @@ module.exports.saveDerivedComponent = async (config, appname, component) => {
const appPath = appPackageFolder(config, appname);
await writeJSON(
componentPath(appPath, component._name),
componentPath(appPath, component.name),
component,
{encoding:"utf8", flag:"w"});
}
@ -177,7 +177,7 @@ const fetchDerivedComponents = async (appPath, relativePath = "") => {
const component =
await readJSON(itemFullPath);
component._name = itemRelativePath
component.name = itemRelativePath
.substring(0, itemRelativePath.length - 5)
.replace(/\\/g, "/");