From e659e69e868473c876dee50665c05b1e46fd17a2 Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Thu, 21 May 2020 21:40:16 +0100 Subject: [PATCH 01/27] workflow UI with DnD --- packages/builder/src/builderStore/index.js | 2 + .../src/builderStore/store/workflow.js | 34 + .../CreateEditModel/CreateEditModel.svelte | 4 +- packages/builder/src/flowy.css | 640 ++++++++++++++++++ packages/builder/src/global.css | 10 + packages/builder/src/index.html | 3 + packages/builder/src/main.js | 1 + .../workflow/BlockPanel/BlockPanel.svelte | 99 +++ .../workflow/BlockPanel/blockDefinitions.js | 54 ++ .../workflow/BlockPanel/index.js | 1 + .../WorkflowList/CreateWorkflowModal.svelte | 87 +++ .../workflow/WorkflowList/WorkflowList.svelte | 84 +++ .../workflow/WorkflowList/index.js | 1 + .../[application]/workflow/_layout.svelte | 47 ++ .../workflow/flowy/WorkflowBuilder.svelte | 43 ++ .../pages/[application]/workflow/flowy/apl.js | 0 .../pages/[application]/workflow/index.svelte | 5 + .../workflow/orchestrator/index.js | 38 ++ packages/client/src/render/screenRouter.js | 1 + .../src/Templates/indexDatatable.js | 80 --- .../src/Templates/recordForm.js | 149 ---- 21 files changed, 1152 insertions(+), 231 deletions(-) create mode 100644 packages/builder/src/builderStore/store/workflow.js create mode 100644 packages/builder/src/flowy.css create mode 100644 packages/builder/src/pages/[application]/workflow/BlockPanel/BlockPanel.svelte create mode 100644 packages/builder/src/pages/[application]/workflow/BlockPanel/blockDefinitions.js create mode 100644 packages/builder/src/pages/[application]/workflow/BlockPanel/index.js create mode 100644 packages/builder/src/pages/[application]/workflow/WorkflowList/CreateWorkflowModal.svelte create mode 100644 packages/builder/src/pages/[application]/workflow/WorkflowList/WorkflowList.svelte create mode 100644 packages/builder/src/pages/[application]/workflow/WorkflowList/index.js create mode 100644 packages/builder/src/pages/[application]/workflow/_layout.svelte create mode 100644 packages/builder/src/pages/[application]/workflow/flowy/WorkflowBuilder.svelte create mode 100644 packages/builder/src/pages/[application]/workflow/flowy/apl.js create mode 100644 packages/builder/src/pages/[application]/workflow/index.svelte create mode 100644 packages/builder/src/pages/[application]/workflow/orchestrator/index.js delete mode 100644 packages/materialdesign-components/src/Templates/indexDatatable.js delete mode 100644 packages/materialdesign-components/src/Templates/recordForm.js diff --git a/packages/builder/src/builderStore/index.js b/packages/builder/src/builderStore/index.js index 8ba017a7c8..dd6784c135 100644 --- a/packages/builder/src/builderStore/index.js +++ b/packages/builder/src/builderStore/index.js @@ -1,9 +1,11 @@ import { getStore } from "./store" import { getBackendUiStore } from "./store/backend" +import { getWorkflowStore } from "./store/workflow" import LogRocket from "logrocket" export const store = getStore() export const backendUiStore = getBackendUiStore() +export const workflowStore = getWorkflowStore() export const initialise = async () => { try { diff --git a/packages/builder/src/builderStore/store/workflow.js b/packages/builder/src/builderStore/store/workflow.js new file mode 100644 index 0000000000..b6aef38c72 --- /dev/null +++ b/packages/builder/src/builderStore/store/workflow.js @@ -0,0 +1,34 @@ +import { writable } from "svelte/store" +import api from "../api" + +export const getWorkflowStore = () => { + const INITIAL_WORKFLOW_STATE = { + workflows: [] + } + + const store = writable(INITIAL_WORKFLOW_STATE) + + store.actions = { + fetch: async instanceId => { + const WORKFLOWS_URL = `/api/${instanceId}/workflows`; + const workflowResponse = await api.get(WORKFLOWS_URL); + const json = await workflowResponse.json(); + store.update(state => { + state.workflows = json + return state + }) + }, + create: async ({ instanceId, name }) => { + const workflow = { name } + const CREATE_WORKFLOW_URL = `/api/${instanceId}/workflows`; + const response = await api.post(CREATE_WORKFLOW_URL, workflow) + const json = await response.json(); + store.update(state => { + state.workflows = state.workflows.concat(json.workflow) + return state + }) + }, + } + + return store +} \ No newline at end of file diff --git a/packages/builder/src/components/database/ModelDataTable/modals/CreateEditModel/CreateEditModel.svelte b/packages/builder/src/components/database/ModelDataTable/modals/CreateEditModel/CreateEditModel.svelte index 97bb799c45..bd302df5b7 100644 --- a/packages/builder/src/components/database/ModelDataTable/modals/CreateEditModel/CreateEditModel.svelte +++ b/packages/builder/src/components/database/ModelDataTable/modals/CreateEditModel/CreateEditModel.svelte @@ -35,7 +35,7 @@ } - +
{#if !showFieldView}

Create / Edit Model

@@ -43,7 +43,7 @@

Create / Edit Field

{/if} - +
{#if !showFieldView}

Settings

diff --git a/packages/builder/src/flowy.css b/packages/builder/src/flowy.css new file mode 100644 index 0000000000..647f404730 --- /dev/null +++ b/packages/builder/src/flowy.css @@ -0,0 +1,640 @@ +body, html { + margin: 0px; + padding: 0px; + overflow: hidden; + background-repeat: repeat; + background-size: 30px 30px; + background-color: #FBFBFB; + height: 100%; +} +#navigation { + height: 71px; + background-color: #FFF; + border: 1px solid #E8E8EF; + width: 100%; + display: table; + box-sizing: border-box; + position: fixed; + top: 0; + z-index: 9 +} +#back { + width: 40px; + height: 40px; + border-radius: 100px; + background-color: #F1F4FC; + text-align: center; + display: inline-block; + vertical-align: top; + margin-top: 12px; + margin-right: 10px +} +#back img { + margin-top: 13px; +} +#names { + display: inline-block; + vertical-align: top; +} +#title { + font-family: Roboto; + font-weight: 500; + font-size: 16px; + color: #393C44; + margin-bottom: 0px; +} +#subtitle { + font-family: Roboto; + color: #808292; + font-size: 14px; + margin-top: 5px; +} +#leftside { + display: inline-block; + vertical-align: middle; + margin-left: 20px; +} +#centerswitch { + position: absolute; + width: 222px; + left: 50%; + margin-left: -111px; + top: 15px; +} +#leftswitch { + border: 1px solid #E8E8EF; + background-color: #FBFBFB; + width: 111px; + height: 39px; + line-height: 39px; + border-radius: 5px 0px 0px 5px; + font-family: Roboto; + color: #393C44; + display: inline-block; + font-size: 14px; + text-align: center; +} +#rightswitch { + font-family: Roboto; + color: #808292; + border-radius: 0px 5px 5px 0px; + border: 1px solid #E8E8EF; + height: 39px; + width: 102px; + display: inline-block; + font-size: 14px; + line-height: 39px; + text-align: center; + margin-left: -5px; +} +#discard { + font-family: Roboto; + font-weight: 500; + font-size: 14px; + color: #A6A6B3; + width: 95px; + height: 38px; + border: 1px solid #E8E8EF; + border-radius: 5px; + text-align: center; + line-height: 38px; + display: inline-block; + vertical-align: top; + transition: all .2s cubic-bezier(.05,.03,.35,1); +} +#discard:hover { + cursor: pointer; + opacity: .7; +} +#publish { + font-family: Roboto; + font-weight: 500; + font-size: 14px; + color: #FFF; + background-color: #217CE8; + border-radius: 5px; + width: 143px; + height: 38px; + margin-left: 10px; + display: inline-block; + vertical-align: top; + text-align: center; + line-height: 38px; + margin-right: 20px; + transition: all .2s cubic-bezier(.05,.03,.35,1); +} +#publish:hover { + cursor: pointer; + opacity: .7; +} +#buttonsright { + float: right; + margin-top: 15px; +} +#leftcard { + width: 363px; + background-color: #FFF; + border: 1px solid #E8E8EF; + box-sizing: border-box; + padding-top: 85px; + padding-left: 20px; + height: 100%; + position: absolute; + z-index: 2; +} +#search input { + width: 318px; + height: 40px; + background-color: #FFF; + border: 1px solid #E8E8EF; + box-sizing: border-box; + box-shadow: 0px 2px 8px rgba(34,34,87,0.05); + border-radius: 5px; + text-indent: 35px; + font-family: Roboto; + font-size: 16px; +} +::-webkit-input-placeholder { /* Edge */ + color: #C9C9D5; +} + +:-ms-input-placeholder { /* Internet Explorer 10-11 */ + color: #C9C9D5 +} + +::placeholder { + color: #C9C9D5; +} +#search img { + position: absolute; + margin-top: 10px; + width: 18px; + margin-left: 12px; +} +#header { + font-size: 20px; + font-family: Roboto; + font-weight: bold; + color: #393C44; +} +#subnav { + border-bottom: 1px solid #E8E8EF; + width: calc(100% + 20px); + margin-left: -20px; + margin-top: 10px; +} +.navdisabled { + transition: all .3s cubic-bezier(.05,.03,.35,1); +} +.navdisabled:hover { + cursor: pointer; + opacity: .5; +} +.navactive { + color: #393C44!important; +} +#triggers { + margin-left: 20px; + font-family: Roboto; + font-weight: 500; + font-size: 14px; + text-align: center; + color: #808292; + width: calc(88% / 3); + height: 48px; + line-height: 48px; + display: inline-block; + float: left; +} +.navactive:after { + display: block; + content: ""; + width: 100%; + height: 4px; + background-color: #217CE8; + margin-top: -4px; +} +#actions { + display: inline-block; + font-family: Roboto; + font-weight: 500; + color: #808292; + font-size: 14px; + height: 48px; + line-height: 48px; + width: calc(88% / 3); + text-align: center; + float: left; +} +#loggers { + width: calc(88% / 3); + display: inline-block; + font-family: Roboto; + font-weight: 500; + color: #808292; + font-size: 14px; + height: 48px; + line-height: 48px; + text-align: center; +} +#footer { + position: absolute; + left: 0; + padding-left: 20px; + line-height: 40px; + bottom: 0; + width: 362px; + border: 1px solid #E8E8EF; + height: 67px; + box-sizing: border-box; + background-color: #FFF; + font-family: Roboto; + font-size: 14px; +} +#footer a { + text-decoration: none; + color: #393C44; + transition: all .2s cubic-bezier(.05,.03,.35,1); +} +#footer a:hover { + opacity: .5; +} +#footer span { + color: #808292; +} +#footer p { + display: inline-block; + color: #808292; +} +#footer img { + margin-left: 5px; + margin-right: 5px; +} +.blockelem:first-child { + margin-top: 20px +} +.blockelem { + padding-top: 10px; + width: 318px; + border: 1px solid transparent; + transition-property: box-shadow, height; + transition-duration: .2s; + transition-timing-function: cubic-bezier(.05,.03,.35,1); + border-radius: 5px; + box-shadow: 0px 0px 30px rgba(22, 33, 74, 0); + box-sizing: border-box; +} +.blockelem:hover { + box-shadow: 0px 4px 30px rgba(22, 33, 74, 0.08); + border-radius: 5px; + background-color: #FFF; + cursor: pointer; +} +.grabme, .blockico { + display: inline-block; +} +.grabme { + margin-top: 10px; + margin-left: 10px; + margin-bottom: -14px; + width: 15px; +} +#blocklist { + height: calc(100% - 220px); + overflow: auto; +} +#proplist { + height: calc(100% - 305px); + overflow: auto; + margin-top: -30px; + padding-top: 30px; +} +.blockin { + display: inline-block; + vertical-align: top; + margin-left: 12px; +} +.blockico { + width: 36px; + height: 36px; + background-color: #F1F4FC; + border-radius: 5px; + text-align: center; + white-space: nowrap; +} +.blockico span { + height: 100%; + width: 0px; + display: inline-block; + vertical-align: middle; +} +.blockico img { + vertical-align: middle; + margin-left: auto; + margin-right: auto; + display: inline-block; +} +.blocktext { + display: inline-block; + width: 220px; + vertical-align: top; + margin-left: 12px +} +.blocktitle { + margin: 0px!important; + padding: 0px!important; + font-family: Roboto; + font-weight: 500; + font-size: 16px; + color: #393C44; +} +.blockdesc { + margin-top: 5px; + font-family: Roboto; + color: #808292; + font-size: 14px; + line-height: 21px; +} +.blockdisabled { + background-color: #F0F2F9; + opacity: .5; +} +#closecard { + position: absolute; + margin-left: 340px; + background-color: #FFF; + border-radius: 0px 5px 5px 0px; + border-bottom: 1px solid #E8E8EF; + border-right: 1px solid #E8E8EF; + border-top: 1px solid #E8E8EF; + width: 53px; + height: 53px; + text-align: center; + z-index: 10; +} +#closecard img { + margin-top: 15px +} +#canvas { + border: 1px solid green; + position: absolute; + width: calc(100% - 361px); + height: calc(100% - 71px); + top: 71px; + left: 361px; + z-index: 0; + overflow: auto; +} +#propwrap { + position: absolute; + right: 0; + top: 0; + width: 311px; + height: 100%; + padding-left: 20px; + overflow: hidden; + z-index: -2; +} +#properties { + position: absolute; + height: 100%; + width: 311px; + background-color: #FFF; + right: -150px; + opacity: 0; + z-index: 2; + top: 0px; + box-shadow: -4px 0px 40px rgba(26, 26, 73, 0); + padding-left: 20px; + transition: all .25s cubic-bezier(.05,.03,.35,1); +} +.itson { + z-index: 2!important; +} +.expanded { + right: 0!important; + opacity: 1!important; + box-shadow: -4px 0px 40px rgba(26, 26, 73, 0.05); + z-index: 2; +} +#header2 { + font-size: 20px; + font-family: Roboto; + font-weight: bold; + color: #393C44; + margin-top: 101px; +} +#close { + margin-top: 100px; + position: absolute; + right: 20px; + z-index: 9999; + transition: all .25s cubic-bezier(.05,.03,.35,1); +} +#close:hover { + cursor: pointer; + opacity: .7; +} +#propswitch { + border-bottom: 1px solid #E8E8EF; + width: 331px; + margin-top: 10px; + margin-left: -20px; + margin-bottom: 30px; +} +#dataprop { + font-family: Roboto; + font-weight: 500; + font-size: 14px; + text-align: center; + color: #393C44; + width: calc(88% / 3); + height: 48px; + line-height: 48px; + display: inline-block; + float: left; + margin-left: 20px; +} +#dataprop:after { + display: block; + content: ""; + width: 100%; + height: 4px; + background-color: #217CE8; + margin-top: -4px; +} +#alertprop { + display: inline-block; + font-family: Roboto; + font-weight: 500; + color: #808292; + font-size: 14px; + height: 48px; + line-height: 48px; + width: calc(88% / 3); + text-align: center; + float: left; +} +#logsprop { + width: calc(88% / 3); + display: inline-block; + font-family: Roboto; + font-weight: 500; + color: #808292; + font-size: 14px; + height: 48px; + line-height: 48px; + text-align: center; +} +.inputlabel { + font-family: Roboto; + font-size: 14px; + color: #253134; +} +.dropme { + background-color: #FFF; + border-radius: 5px; + border: 1px solid #E8E8EF; + box-shadow: 0px 2px 8px rgba(34, 34, 87, 0.05); + font-family: Roboto; + font-size: 14px; + color: #253134; + text-indent: 20px; + height: 40px; + line-height: 40px; + width: 287px; + margin-bottom: 25px; +} +.dropme img { + margin-top: 17px; + float: right; + margin-right: 15px; +} +.checkus { + margin-bottom: 10px; +} +.checkus img { + display: inline-block; + vertical-align: middle; +} +.checkus p { + display: inline-block; + font-family: Roboto; + font-size: 14px; + vertical-align: middle; + margin-left: 10px; +} +#divisionthing { + height: 1px; + width: 100%; + background-color: #E8E8EF; + position: absolute; + right: 0px; + bottom: 80; +} +#removeblock { + border-radius: 5px; + position: absolute; + bottom: 20px; + font-family: Roboto; + font-size: 14px; + text-align: center; + width: 287px; + height: 38px; + line-height: 38px; + color: #253134; + border: 1px solid #E8E8EF; + transition: all .3s cubic-bezier(.05,.03,.35,1); +} +#removeblock:hover { + cursor: pointer; + opacity: .5; +} +.noselect { + -webkit-touch-callout: none; /* iOS Safari */ + -webkit-user-select: none; /* Safari */ + -khtml-user-select: none; /* Konqueror HTML */ + -moz-user-select: none; /* Old versions of Firefox */ + -ms-user-select: none; /* Internet Explorer/Edge */ + user-select: none; /* Non-prefixed version, currently + supported by Chrome, Opera and Firefox */ +} +.blockyname { + font-family: Roboto; + font-weight: 500; + color: #253134; + display: inline-block; + vertical-align: middle; + margin-left: 8px; + font-size: 16px; +} +.blockyleft img { + display: inline-block; + vertical-align: middle; +} +.blockyright { + display: inline-block; + float: right; + vertical-align: middle; + margin-right: 20px; + margin-top: 10px; + width: 28px; + height: 28px; + border-radius: 5px; + text-align: center; + background-color: #FFF; + transition: all .3s cubic-bezier(.05,.03,.35,1); + z-index: 10; +} +.blockyright:hover { + background-color: #F1F4FC; + cursor: pointer; +} +.blockyright img { + margin-top: 12px; +} +.blockyleft { + display: inline-block; + margin-left: 20px; +} +.blockydiv { + width: 100%; + height: 1px; + background-color: #E9E9EF; +} +.blockyinfo { + font-family: Roboto; + font-size: 14px; + color: #808292; + margin-top: 15px; + text-indent: 20px; + margin-bottom: 20px; +} +.blockyinfo span { + color: #253134; + font-weight: 500; + display: inline-block; + border-bottom: 1px solid #D3DCEA; + line-height: 20px; + text-indent: 0px; +} +.block { + background-color: #FFF; + margin-top: 0px!important; + box-shadow: 0px 4px 30px rgba(22, 33, 74, 0.05); +} +.selectedblock { + border: 2px solid #217CE8; + box-shadow: 0px 4px 30px rgba(22, 33, 74, 0.08); +} + +@media only screen and (max-width: 832px) { + #centerswitch { + display: none; + } +} +@media only screen and (max-width: 560px) { + #names { + display: none; + } +} \ No newline at end of file diff --git a/packages/builder/src/global.css b/packages/builder/src/global.css index 999415931a..08921f95d2 100644 --- a/packages/builder/src/global.css +++ b/packages/builder/src/global.css @@ -58,6 +58,16 @@ --background-button: #f9f9f9; --button-text: #0055ff; + + /* Budibase Styleguide Colors */ + --primary: #0055ff; + --secondary: #f1f4fc; + --color: #393c44; + --dark-grey: #808192; + --medium-grey: #e8e8ef; + --background: rgb(251, 251, 251); + --font: #393c44; + --coral: #eb5757; } html, body { diff --git a/packages/builder/src/index.html b/packages/builder/src/index.html index 23c5aaf6c0..65239c2e69 100644 --- a/packages/builder/src/index.html +++ b/packages/builder/src/index.html @@ -10,15 +10,18 @@ + + + \ No newline at end of file diff --git a/packages/builder/src/main.js b/packages/builder/src/main.js index eb1357fde2..f664629c66 100644 --- a/packages/builder/src/main.js +++ b/packages/builder/src/main.js @@ -1,4 +1,5 @@ import "./global.css" +import "./flowy.css"; import "./fonts.css" import "./budibase.css" import "/assets/roboto-v20-latin-ext_latin-300" diff --git a/packages/builder/src/pages/[application]/workflow/BlockPanel/BlockPanel.svelte b/packages/builder/src/pages/[application]/workflow/BlockPanel/BlockPanel.svelte new file mode 100644 index 0000000000..f66cdcc580 --- /dev/null +++ b/packages/builder/src/pages/[application]/workflow/BlockPanel/BlockPanel.svelte @@ -0,0 +1,99 @@ + + +
+
+ Blocks + Props +
+
+ {#each SUB_TABS as tab} + (selectedTab = tab.key)}> + {tab.name} + + {/each} +
+
+ {#each definitions as blockDefinition} +
+ +
+ +
+
+
+ + +
+
+

{blockDefinition.name}

+

{blockDefinition.description}

+
+
+
+ {/each} +
+
+ + diff --git a/packages/builder/src/pages/[application]/workflow/BlockPanel/blockDefinitions.js b/packages/builder/src/pages/[application]/workflow/BlockPanel/blockDefinitions.js new file mode 100644 index 0000000000..d13f317a2b --- /dev/null +++ b/packages/builder/src/pages/[application]/workflow/BlockPanel/blockDefinitions.js @@ -0,0 +1,54 @@ +const ACTIONS = { + SET_STATE: { + name: "Update UI", + icon: "", + description: "Update your User Interface with some data.", + type: "CLIENT", + }, + NAVIGATE: { + name: "Navigate", + icon: "", + description: "Navigate to another page.", + type: "CLIENT" + }, + CREATE_RECORD: { + name: "Save Record", + icon: "", + description: "Save a record to your database.", + type: "SERVER", + }, + DELETE_RECORD: { + description: "Delete a record from your database.", + icon: "", + name: "Delete Record", + type: "SERVER", + } +}; + +const TRIGGERS = { + CLICK: { + name: "Click", + icon: "", + description: "Trigger when you click on an element in the UI." + }, + LOAD: { + name: "Load", + icon: "", + description: "Trigger an element has finished loading." + }, + INPUT: { + name: "Input", + icon: "", + description: "Trigger when you type into an input box." + }, +}; + +const UTILITIES = { + +} + +export default { + ACTIONS, + TRIGGERS, + UTILITIES +} \ No newline at end of file diff --git a/packages/builder/src/pages/[application]/workflow/BlockPanel/index.js b/packages/builder/src/pages/[application]/workflow/BlockPanel/index.js new file mode 100644 index 0000000000..73ebcd6949 --- /dev/null +++ b/packages/builder/src/pages/[application]/workflow/BlockPanel/index.js @@ -0,0 +1 @@ +export { default as BlockPanel } from "./BlockPanel.svelte"; \ No newline at end of file diff --git a/packages/builder/src/pages/[application]/workflow/WorkflowList/CreateWorkflowModal.svelte b/packages/builder/src/pages/[application]/workflow/WorkflowList/CreateWorkflowModal.svelte new file mode 100644 index 0000000000..510b21704e --- /dev/null +++ b/packages/builder/src/pages/[application]/workflow/WorkflowList/CreateWorkflowModal.svelte @@ -0,0 +1,87 @@ + + +
+ + Create Workflow +
+
+ + +
+ + + diff --git a/packages/builder/src/pages/[application]/workflow/WorkflowList/WorkflowList.svelte b/packages/builder/src/pages/[application]/workflow/WorkflowList/WorkflowList.svelte new file mode 100644 index 0000000000..c611143f78 --- /dev/null +++ b/packages/builder/src/pages/[application]/workflow/WorkflowList/WorkflowList.svelte @@ -0,0 +1,84 @@ + + +
+
+ Workflows + +
+
    + {#each $workflowStore.workflows as workflow} +
  • + + {workflow.name} +
  • + {/each} +
+
+ + diff --git a/packages/builder/src/pages/[application]/workflow/WorkflowList/index.js b/packages/builder/src/pages/[application]/workflow/WorkflowList/index.js new file mode 100644 index 0000000000..18bd078eaa --- /dev/null +++ b/packages/builder/src/pages/[application]/workflow/WorkflowList/index.js @@ -0,0 +1 @@ +export { default as WorkflowList } from "./WorkflowList.svelte"; \ No newline at end of file diff --git a/packages/builder/src/pages/[application]/workflow/_layout.svelte b/packages/builder/src/pages/[application]/workflow/_layout.svelte new file mode 100644 index 0000000000..ec0a7d4ef9 --- /dev/null +++ b/packages/builder/src/pages/[application]/workflow/_layout.svelte @@ -0,0 +1,47 @@ + + +
+ +
+ +
+ +
+ + diff --git a/packages/builder/src/pages/[application]/workflow/flowy/WorkflowBuilder.svelte b/packages/builder/src/pages/[application]/workflow/flowy/WorkflowBuilder.svelte new file mode 100644 index 0000000000..0d596915c3 --- /dev/null +++ b/packages/builder/src/pages/[application]/workflow/flowy/WorkflowBuilder.svelte @@ -0,0 +1,43 @@ + + +
diff --git a/packages/builder/src/pages/[application]/workflow/flowy/apl.js b/packages/builder/src/pages/[application]/workflow/flowy/apl.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/builder/src/pages/[application]/workflow/index.svelte b/packages/builder/src/pages/[application]/workflow/index.svelte new file mode 100644 index 0000000000..766d9efaea --- /dev/null +++ b/packages/builder/src/pages/[application]/workflow/index.svelte @@ -0,0 +1,5 @@ + + + \ No newline at end of file diff --git a/packages/builder/src/pages/[application]/workflow/orchestrator/index.js b/packages/builder/src/pages/[application]/workflow/orchestrator/index.js new file mode 100644 index 0000000000..5f037c07ff --- /dev/null +++ b/packages/builder/src/pages/[application]/workflow/orchestrator/index.js @@ -0,0 +1,38 @@ +import api from "builderStore/api"; + +class Orchestrator { + set strategy(strategy) { + this._stategy = strategy + } + + execute(workflow) { + this._strategy.execute(workflow); + } +} + +const ClientStrategy = { + execute: function(workflow) { + const block = workflow.next; + const EXECUTE_WORKFLOW_URL = `api/${workflow.instanceId}/workflows/${workflow._id}`; + + switch (block.type) { + case "CLIENT": + // fetch the workflow code from the server, then execute it here in the client + // catch any errors + // check against the conditions in the workflow + // if everything is fine, recurse + this.execute(workflow.next); + break; + case "SERVER": + // hit the server endpoint and wait for the response + // catch any errors + // check against the conditions in the workflow + // if everything is fine, recurse + await api.post() + break; + default: + break; + } + + } +} \ No newline at end of file diff --git a/packages/client/src/render/screenRouter.js b/packages/client/src/render/screenRouter.js index a45daca54f..f04df723a6 100644 --- a/packages/client/src/render/screenRouter.js +++ b/packages/client/src/render/screenRouter.js @@ -1,6 +1,7 @@ import regexparam from "regexparam" import { writable } from "svelte/store" +// TODO: refactor export const screenRouter = (screens, onScreenSelected, appRootPath) => { const makeRootedPath = url => { if (appRootPath) { diff --git a/packages/materialdesign-components/src/Templates/indexDatatable.js b/packages/materialdesign-components/src/Templates/indexDatatable.js deleted file mode 100644 index f242e7d8d6..0000000000 --- a/packages/materialdesign-components/src/Templates/indexDatatable.js +++ /dev/null @@ -1,80 +0,0 @@ -export default ({ indexes, helpers }) => - indexes.map(i => ({ - name: `Table based on view: ${i.name} `, - props: tableProps( - i, - helpers.indexSchema(i).filter(c => !excludedColumns.includes(c.name)) - ), - })) - -const excludedColumns = ["id", "key", "sortKey", "type", "isNew"] - -const tableProps = (index, indexSchema) => ({ - _component: "@budibase/materialdesign-components/Datatable", - _children: [ - { - _component: "@budibase/materialdesign-components/DatatableHead", - _children: [ - { - _component: "@budibase/materialdesign-components/DatatableRow", - isHeader: true, - _children: columnHeaders(indexSchema), - }, - ], - }, - { - _component: "@budibase/materialdesign-components/DatatableBody", - _children: [ - { - _code: rowCode(index), - _component: "@budibase/materialdesign-components/DatatableRow", - _children: dataCells(index, indexSchema), - }, - ], - }, - ], - onLoad: [ - { - "##eventHandlerType": "List Records", - parameters: { - indexKey: index.nodeKey(), - statePath: index.name, - }, - }, - ], -}) - -const columnHeaders = indexSchema => - indexSchema.map(col => ({ - _component: "@budibase/materialdesign-components/DatatableCell", - isHeader: true, - _children: [ - { - _component: "@budibase/standard-components/text", - type: "none", - text: col.name, - formattingTag: " - bold", - }, - ], - })) - -const dataCells = (index, indexSchema) => - indexSchema.map(col => ({ - _component: "@budibase/materialdesign-components/DatatableCell", - _children: [ - { - _component: "@budibase/standard-components/text", - type: "none", - text: `context.${dataItem(index)}.${col.name}`, - }, - ], - })) - -const dataItem = index => `${index.name}_item` -const dataCollection = index => `state.${index.name}` -const rowCode = index => - ` -if (!${dataCollection(index)}) return - -for (let ${dataItem(index)} of ${dataCollection(index)}) - render( { ${dataItem(index)} } )` diff --git a/packages/materialdesign-components/src/Templates/recordForm.js b/packages/materialdesign-components/src/Templates/recordForm.js deleted file mode 100644 index 38ab602a8e..0000000000 --- a/packages/materialdesign-components/src/Templates/recordForm.js +++ /dev/null @@ -1,149 +0,0 @@ -export default ({ records }) => - records.map(r => ({ - name: `Form for Record: ${r.nodeName()}`, - props: outerContainer(r), - })) - -const outerContainer = record => ({ - _component: "@budibase/standard-components/container", - _code: "", - type: "div", - onLoad: [ - { - "##eventHandlerType": "Get New Record", - parameters: { - collectionKey: record.collectionNodeKey(), - childRecordType: record.name, - statePath: record.name, - }, - }, - ], - _children: [ - heading(record), - ...record.fields.map(f => field(record, f)), - buttons(record), - ], -}) - -const heading = record => ({ - _component: "@budibase/materialdesign-components/H3", - text: capitalize(record.name), -}) - -const field = (record, f) => { - if (f.type === "bool") return checkbox(record, f) - if ( - f.type === "string" && - f.typeOptions && - f.typeOptions.values && - f.typeOptions.values.length > 0 - ) - return select(record, f) - return textField(record, f) -} - -const textField = (record, f) => ({ - _component: "@budibase/materialdesign-components/Textfield", - label: f.label, - variant: "filled", - disabled: false, - fullwidth: false, - colour: "primary", - maxLength: - f.typeOptions && f.typeOptions.maxLength ? f.typeOptions.maxLength : 0, - placeholder: f.label, - value: fieldValueBinding(record, f), -}) - -const checkbox = (record, f) => ({ - _component: "@budibase/materialdesign-components/Checkbox", - label: f.label, - checked: fieldValueBinding(record, f), -}) - -const select = (record, f) => ({ - _component: "@budibase/materialdesign-components/Select", - value: fieldValueBinding(record, f), - _children: f.typeOptions.values.map(val => ({ - _component: "@budibase/materialdesign-components/ListItem", - value: val, - text: val, - })), -}) - -const fieldValueBinding = (record, f) => `state.${record.name}.${f.name}` - -const capitalize = s => s.charAt(0).toUpperCase() + s.slice(1) - -const buttons = record => ({ - _component: "@budibase/standard-components/container", - borderWidth: "1px 0px 0px 0px", - borderColor: "lightgray", - borderStyle: "solid", - _styles: { - position: { - column: ["", ""], - row: ["", ""], - margin: ["", "", "", ""], - padding: ["30px", "", "", ""], - height: [""], - width: [""], - zindex: [""], - }, - layout: { - templaterows: [""], - templatecolumns: [""], - }, - }, - _children: [ - { - _component: "@budibase/materialdesign-components/Button", - onClick: [ - { - "##eventHandlerType": "Save Record", - parameters: { - statePath: `${record.name}`, - }, - }, - { - "##eventHandlerType": "Navigate To", - parameters: { - url: `/${record.name}s`, - }, - }, - ], - variant: "raised", - colour: "primary", - size: "medium", - text: `Save ${capitalize(record.name)}`, - }, - { - _component: "@budibase/materialdesign-components/Button", - _styles: { - position: { - row: ["", ""], - column: ["", ""], - padding: ["", "", "", ""], - margin: ["", "", "", "10px"], - width: [""], - height: [""], - zindex: [""], - }, - layout: { - templatecolumns: [""], - templaterows: [""], - }, - }, - onClick: [ - { - "##eventHandlerType": "Navigate To", - parameters: { - url: `/${record.name}s`, - }, - }, - ], - colour: "secondary", - text: "Cancel", - }, - ], -}) From cacee947bfcc9f49f1ce6fbf6c16e302570be4d3 Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Fri, 22 May 2020 16:32:23 +0100 Subject: [PATCH 02/27] workflow charts --- packages/builder/src/builderStore/api.js | 2 + .../src/builderStore/store/workflow.js | 62 ++++++++++++------- packages/builder/src/flowy.css | 7 +++ .../workflow/BlockPanel/BlockPanel.svelte | 11 ++-- .../workflow/BlockPanel/blockDefinitions.js | 27 +++++--- .../WorkflowList/CreateWorkflowModal.svelte | 1 + .../workflow/WorkflowList/WorkflowList.svelte | 18 ++++-- .../[application]/workflow/_layout.svelte | 7 ++- .../workflow/flowy/WorkflowBuilder.svelte | 28 +++++---- .../materialdesign-components/src/index.js | 2 - packages/server/package.json | 2 +- .../server/src/api/controllers/workflow.js | 18 +++++- .../src/api/routes/tests/workflow.spec.js | 19 ++++++ packages/server/src/api/routes/workflow.js | 2 +- 14 files changed, 146 insertions(+), 60 deletions(-) diff --git a/packages/builder/src/builderStore/api.js b/packages/builder/src/builderStore/api.js index 7440fd7031..c9327a98f2 100644 --- a/packages/builder/src/builderStore/api.js +++ b/packages/builder/src/builderStore/api.js @@ -18,10 +18,12 @@ const post = apiCall("POST") const get = apiCall("GET") const patch = apiCall("PATCH") const del = apiCall("DELETE") +const put = apiCall("PUT") export default { post, get, patch, delete: del, + put } diff --git a/packages/builder/src/builderStore/store/workflow.js b/packages/builder/src/builderStore/store/workflow.js index b6aef38c72..6ce68e2fa9 100644 --- a/packages/builder/src/builderStore/store/workflow.js +++ b/packages/builder/src/builderStore/store/workflow.js @@ -1,6 +1,46 @@ import { writable } from "svelte/store" import api from "../api" +const workflowActions = store => ({ + fetch: async instanceId => { + const WORKFLOWS_URL = `/api/${instanceId}/workflows`; + const workflowResponse = await api.get(WORKFLOWS_URL); + const json = await workflowResponse.json(); + store.update(state => { + state.workflows = json + return state + }) + }, + create: async ({ instanceId, name }) => { + const workflow = { name } + const CREATE_WORKFLOW_URL = `/api/${instanceId}/workflows`; + const response = await api.post(CREATE_WORKFLOW_URL, workflow) + const json = await response.json(); + store.update(state => { + state.workflows = state.workflows.concat(json.workflow) + state.selectedWorkflowId = json.workflow._id + return state + }) + }, + update: async ({ instanceId, workflow }) => { + const UPDATE_WORKFLOW_URL = `/api/${instanceId}/workflows`; + const response = await api.put(UPDATE_WORKFLOW_URL, workflow) + const json = await response.json(); + store.update(state => { + const existingIdx = state.workflows.findIndex(existing => existing._id === workflow._id); + state.workflows.splice(existingIdx, 1, json.workflow); + state.workflows = state.workflows + return state + }) + }, + select: workflow => { + store.update(state => { + state.selectedWorkflowId = workflow._id + return state; + }) + } +}); + export const getWorkflowStore = () => { const INITIAL_WORKFLOW_STATE = { workflows: [] @@ -8,27 +48,7 @@ export const getWorkflowStore = () => { const store = writable(INITIAL_WORKFLOW_STATE) - store.actions = { - fetch: async instanceId => { - const WORKFLOWS_URL = `/api/${instanceId}/workflows`; - const workflowResponse = await api.get(WORKFLOWS_URL); - const json = await workflowResponse.json(); - store.update(state => { - state.workflows = json - return state - }) - }, - create: async ({ instanceId, name }) => { - const workflow = { name } - const CREATE_WORKFLOW_URL = `/api/${instanceId}/workflows`; - const response = await api.post(CREATE_WORKFLOW_URL, workflow) - const json = await response.json(); - store.update(state => { - state.workflows = state.workflows.concat(json.workflow) - return state - }) - }, - } + store.actions = workflowActions(store); return store } \ No newline at end of file diff --git a/packages/builder/src/flowy.css b/packages/builder/src/flowy.css index 647f404730..1fc6d12b3e 100644 --- a/packages/builder/src/flowy.css +++ b/packages/builder/src/flowy.css @@ -293,6 +293,7 @@ body, html { .grabme, .blockico { display: inline-block; } + .grabme { margin-top: 10px; margin-left: 10px; @@ -322,6 +323,12 @@ body, html { text-align: center; white-space: nowrap; } + +.blockico i { + font-size: 24px; + color: var(--dark-grey); +} + .blockico span { height: 100%; width: 0px; diff --git a/packages/builder/src/pages/[application]/workflow/BlockPanel/BlockPanel.svelte b/packages/builder/src/pages/[application]/workflow/BlockPanel/BlockPanel.svelte index f66cdcc580..4f2bc3c5cc 100644 --- a/packages/builder/src/pages/[application]/workflow/BlockPanel/BlockPanel.svelte +++ b/packages/builder/src/pages/[application]/workflow/BlockPanel/BlockPanel.svelte @@ -42,19 +42,18 @@
{#each definitions as blockDefinition} -
+
-
- -
- - +

{blockDefinition.name}

diff --git a/packages/builder/src/pages/[application]/workflow/BlockPanel/blockDefinitions.js b/packages/builder/src/pages/[application]/workflow/BlockPanel/blockDefinitions.js index d13f317a2b..2a90c8eeda 100644 --- a/packages/builder/src/pages/[application]/workflow/BlockPanel/blockDefinitions.js +++ b/packages/builder/src/pages/[application]/workflow/BlockPanel/blockDefinitions.js @@ -7,44 +7,55 @@ const ACTIONS = { }, NAVIGATE: { name: "Navigate", - icon: "", + icon: "ri-navigation-line", description: "Navigate to another page.", type: "CLIENT" }, - CREATE_RECORD: { + SAVE_RECORD: { name: "Save Record", - icon: "", + icon: "ri-save-3-fill", description: "Save a record to your database.", type: "SERVER", }, DELETE_RECORD: { description: "Delete a record from your database.", - icon: "", + icon: "ri-delete-bin-line", name: "Delete Record", type: "SERVER", } }; const TRIGGERS = { + SAVE_RECORD: { + name: "Record Saved", + icon: "ri-delete-bin-line", + description: "Save a record to your database.", + type: "SERVER", + }, CLICK: { name: "Click", - icon: "", + icon: "ri-cursor-line", description: "Trigger when you click on an element in the UI." }, LOAD: { name: "Load", - icon: "", + icon: "ri-loader-line", description: "Trigger an element has finished loading." }, INPUT: { name: "Input", - icon: "", + icon: "ri-text", description: "Trigger when you type into an input box." }, }; const UTILITIES = { - + IFELSE: { + name: "If/Else", + icon: "ri-git-branch-line", + description: "Perform different actions based on a condition", + type: "CLIENT" + }, } export default { diff --git a/packages/builder/src/pages/[application]/workflow/WorkflowList/CreateWorkflowModal.svelte b/packages/builder/src/pages/[application]/workflow/WorkflowList/CreateWorkflowModal.svelte index 510b21704e..4420ab4db3 100644 --- a/packages/builder/src/pages/[application]/workflow/WorkflowList/CreateWorkflowModal.svelte +++ b/packages/builder/src/pages/[application]/workflow/WorkflowList/CreateWorkflowModal.svelte @@ -16,6 +16,7 @@ name, instanceId, }) + flowy.deleteBlocks(); onClosed() } diff --git a/packages/builder/src/pages/[application]/workflow/WorkflowList/WorkflowList.svelte b/packages/builder/src/pages/[application]/workflow/WorkflowList/WorkflowList.svelte index c611143f78..5e1efe6868 100644 --- a/packages/builder/src/pages/[application]/workflow/WorkflowList/WorkflowList.svelte +++ b/packages/builder/src/pages/[application]/workflow/WorkflowList/WorkflowList.svelte @@ -1,10 +1,9 @@ @@ -30,7 +29,10 @@
    {#each $workflowStore.workflows as workflow} -
  • +
  • workflowStore.actions.select(workflow)}> {workflow.name}
  • @@ -71,7 +73,7 @@ border-radius: 3px; height: 40px; } - + .workflow-item i { font-size: 24px; margin-right: 10px; @@ -81,4 +83,8 @@ cursor: pointer; background: var(--secondary); } + + .workflow-item.selected { + background: var(--secondary); + } diff --git a/packages/builder/src/pages/[application]/workflow/_layout.svelte b/packages/builder/src/pages/[application]/workflow/_layout.svelte index ec0a7d4ef9..7b546e0397 100644 --- a/packages/builder/src/pages/[application]/workflow/_layout.svelte +++ b/packages/builder/src/pages/[application]/workflow/_layout.svelte @@ -1,6 +1,7 @@
    @@ -11,7 +12,9 @@
diff --git a/packages/builder/src/pages/[application]/workflow/flowy/WorkflowBuilder.svelte b/packages/builder/src/pages/[application]/workflow/flowy/WorkflowBuilder.svelte index 0d596915c3..2990eb9e86 100644 --- a/packages/builder/src/pages/[application]/workflow/flowy/WorkflowBuilder.svelte +++ b/packages/builder/src/pages/[application]/workflow/flowy/WorkflowBuilder.svelte @@ -1,25 +1,31 @@ \ No newline at end of file diff --git a/packages/builder/src/main.js b/packages/builder/src/main.js index f664629c66..eb1357fde2 100644 --- a/packages/builder/src/main.js +++ b/packages/builder/src/main.js @@ -1,5 +1,4 @@ import "./global.css" -import "./flowy.css"; import "./fonts.css" import "./budibase.css" import "/assets/roboto-v20-latin-ext_latin-300" diff --git a/packages/builder/src/pages/[application]/workflow/BlockPanel/index.js b/packages/builder/src/pages/[application]/workflow/BlockPanel/index.js deleted file mode 100644 index 73ebcd6949..0000000000 --- a/packages/builder/src/pages/[application]/workflow/BlockPanel/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default as BlockPanel } from "./BlockPanel.svelte"; \ No newline at end of file diff --git a/packages/builder/src/pages/[application]/workflow/SetupPanel/SetupPanel.svelte b/packages/builder/src/pages/[application]/workflow/SetupPanel/SetupPanel.svelte new file mode 100644 index 0000000000..1bfd17779a --- /dev/null +++ b/packages/builder/src/pages/[application]/workflow/SetupPanel/SetupPanel.svelte @@ -0,0 +1,88 @@ + + +
+
+ Setup +
+
+ {#if workflowBlock} + + + {:else if $workflowStore.selectedWorkflowId} + +
+ +
+ +
+
+
+ + Some User Access Stuff Here +
+ + {/if} +
+
+ + diff --git a/packages/builder/src/pages/[application]/workflow/SetupPanel/WorkflowBlockSetup.svelte b/packages/builder/src/pages/[application]/workflow/SetupPanel/WorkflowBlockSetup.svelte new file mode 100644 index 0000000000..1613a78f18 --- /dev/null +++ b/packages/builder/src/pages/[application]/workflow/SetupPanel/WorkflowBlockSetup.svelte @@ -0,0 +1,17 @@ + + + +{#each workflowArgs as workflowArg} +
+ +
+ +
+
+{/each} diff --git a/packages/builder/src/pages/[application]/workflow/SetupPanel/index.js b/packages/builder/src/pages/[application]/workflow/SetupPanel/index.js new file mode 100644 index 0000000000..800406ff7a --- /dev/null +++ b/packages/builder/src/pages/[application]/workflow/SetupPanel/index.js @@ -0,0 +1 @@ +export { default as SetupPanel } from "./SetupPanel.svelte"; \ No newline at end of file diff --git a/packages/builder/src/pages/[application]/workflow/WorkflowBuilder/WorkflowBuilder.svelte b/packages/builder/src/pages/[application]/workflow/WorkflowBuilder/WorkflowBuilder.svelte new file mode 100644 index 0000000000..b9e43e7847 --- /dev/null +++ b/packages/builder/src/pages/[application]/workflow/WorkflowBuilder/WorkflowBuilder.svelte @@ -0,0 +1,51 @@ + + +
+ +
diff --git a/packages/builder/src/pages/[application]/workflow/WorkflowBuilder/svelte-flows/FlowChart.svelte b/packages/builder/src/pages/[application]/workflow/WorkflowBuilder/svelte-flows/FlowChart.svelte new file mode 100644 index 0000000000..d444c14848 --- /dev/null +++ b/packages/builder/src/pages/[application]/workflow/WorkflowBuilder/svelte-flows/FlowChart.svelte @@ -0,0 +1,23 @@ + + +
+ {#each blocks as block, idx} + + {#if idx !== blocks.length - 1} + + {/if} + {/each} +
+ + diff --git a/packages/builder/src/pages/[application]/workflow/WorkflowBuilder/svelte-flows/FlowItem.svelte b/packages/builder/src/pages/[application]/workflow/WorkflowBuilder/svelte-flows/FlowItem.svelte new file mode 100644 index 0000000000..d533e01fa8 --- /dev/null +++ b/packages/builder/src/pages/[application]/workflow/WorkflowBuilder/svelte-flows/FlowItem.svelte @@ -0,0 +1,33 @@ + + +
+
{block.heading}
+
+

{block.body}

+
+ + diff --git a/packages/builder/src/pages/[application]/workflow/flowy/apl.js b/packages/builder/src/pages/[application]/workflow/WorkflowBuilder/svelte-flows/api.js similarity index 100% rename from packages/builder/src/pages/[application]/workflow/flowy/apl.js rename to packages/builder/src/pages/[application]/workflow/WorkflowBuilder/svelte-flows/api.js diff --git a/packages/builder/src/pages/[application]/workflow/WorkflowBuilder/svelte-flows/index.js b/packages/builder/src/pages/[application]/workflow/WorkflowBuilder/svelte-flows/index.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/builder/src/pages/[application]/workflow/WorkflowList/index.js b/packages/builder/src/pages/[application]/workflow/WorkflowList/index.js deleted file mode 100644 index 18bd078eaa..0000000000 --- a/packages/builder/src/pages/[application]/workflow/WorkflowList/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default as WorkflowList } from "./WorkflowList.svelte"; \ No newline at end of file diff --git a/packages/builder/src/pages/[application]/workflow/BlockPanel/BlockPanel.svelte b/packages/builder/src/pages/[application]/workflow/WorkflowPanel/BlockList/BlockList.svelte similarity index 57% rename from packages/builder/src/pages/[application]/workflow/BlockPanel/BlockPanel.svelte rename to packages/builder/src/pages/[application]/workflow/WorkflowPanel/BlockList/BlockList.svelte index 4f2bc3c5cc..66330f11b5 100644 --- a/packages/builder/src/pages/[application]/workflow/BlockPanel/BlockPanel.svelte +++ b/packages/builder/src/pages/[application]/workflow/WorkflowPanel/BlockList/BlockList.svelte @@ -1,8 +1,10 @@
-
- Blocks - Props -
{#each SUB_TABS as tab}
{#each definitions as blockDefinition} -
- -
-
- -
-
-

{blockDefinition.name}

-

{blockDefinition.description}

-
-
-
+ {/each}
diff --git a/packages/builder/src/pages/[application]/workflow/WorkflowList/CreateWorkflowModal.svelte b/packages/builder/src/pages/[application]/workflow/WorkflowPanel/WorkflowList/CreateWorkflowModal.svelte similarity index 98% rename from packages/builder/src/pages/[application]/workflow/WorkflowList/CreateWorkflowModal.svelte rename to packages/builder/src/pages/[application]/workflow/WorkflowPanel/WorkflowList/CreateWorkflowModal.svelte index 4420ab4db3..510b21704e 100644 --- a/packages/builder/src/pages/[application]/workflow/WorkflowList/CreateWorkflowModal.svelte +++ b/packages/builder/src/pages/[application]/workflow/WorkflowPanel/WorkflowList/CreateWorkflowModal.svelte @@ -16,7 +16,6 @@ name, instanceId, }) - flowy.deleteBlocks(); onClosed() } diff --git a/packages/builder/src/pages/[application]/workflow/WorkflowList/WorkflowList.svelte b/packages/builder/src/pages/[application]/workflow/WorkflowPanel/WorkflowList/WorkflowList.svelte similarity index 74% rename from packages/builder/src/pages/[application]/workflow/WorkflowList/WorkflowList.svelte rename to packages/builder/src/pages/[application]/workflow/WorkflowPanel/WorkflowList/WorkflowList.svelte index 5e1efe6868..ed28f1b2c2 100644 --- a/packages/builder/src/pages/[application]/workflow/WorkflowList/WorkflowList.svelte +++ b/packages/builder/src/pages/[application]/workflow/WorkflowPanel/WorkflowList/WorkflowList.svelte @@ -23,17 +23,16 @@
-
- Workflows - -
+
    {#each $workflowStore.workflows as workflow}
  • workflowStore.actions.select(workflow)}> - + {workflow.name}
  • {/each} @@ -41,16 +40,8 @@
diff --git a/packages/builder/src/pages/[application]/workflow/WorkflowPanel/WorkflowPanel.svelte b/packages/builder/src/pages/[application]/workflow/WorkflowPanel/WorkflowPanel.svelte new file mode 100644 index 0000000000..7de90a6e05 --- /dev/null +++ b/packages/builder/src/pages/[application]/workflow/WorkflowPanel/WorkflowPanel.svelte @@ -0,0 +1,61 @@ + + +
+
+ (selectedTab = 'WORKFLOWS')}> + Workflows + + {#if $workflowStore.selectedWorkflowId} + (selectedTab = 'ADD')}> + Add + + {/if} +
+ {#if selectedTab === 'WORKFLOWS'} + + {:else if selectedTab === 'ADD'} + + {/if} +
+ + diff --git a/packages/builder/src/pages/[application]/workflow/BlockPanel/blockDefinitions.js b/packages/builder/src/pages/[application]/workflow/WorkflowPanel/blockDefinitions.js similarity index 62% rename from packages/builder/src/pages/[application]/workflow/BlockPanel/blockDefinitions.js rename to packages/builder/src/pages/[application]/workflow/WorkflowPanel/blockDefinitions.js index 2a90c8eeda..5920070a7a 100644 --- a/packages/builder/src/pages/[application]/workflow/BlockPanel/blockDefinitions.js +++ b/packages/builder/src/pages/[application]/workflow/WorkflowPanel/blockDefinitions.js @@ -1,6 +1,6 @@ const ACTIONS = { SET_STATE: { - name: "Update UI", + name: "Update UI State", icon: "", description: "Update your User Interface with some data.", type: "CLIENT", @@ -22,6 +22,24 @@ const ACTIONS = { icon: "ri-delete-bin-line", name: "Delete Record", type: "SERVER", + }, + FIND_RECORD: { + description: "Delete a record from your database.", + icon: "ri-delete-bin-line", + name: "Find Record", + type: "SERVER", + }, + CREATE_USER: { + description: "Create a new user.", + icon: "ri-user-add-fill", + name: "Create User", + type: "SERVER", + }, + SEND_EMAIL: { + description: "Send an email.", + icon: "ri-mail-open-fill", + name: "Send Email", + type: "SERVER", } }; @@ -49,11 +67,17 @@ const TRIGGERS = { }, }; -const UTILITIES = { - IFELSE: { - name: "If/Else", +const LOGIC = { + FILTER: { + name: "Filter", icon: "ri-git-branch-line", - description: "Perform different actions based on a condition", + description: "Filter any workflows which do not meet certain conditions.", + type: "CLIENT" + }, + DELAY: { + name: "Delay", + icon: "ri-git-branch-line", + description: "Delay the workflow until an amount of time has passed.", type: "CLIENT" }, } @@ -61,5 +85,5 @@ const UTILITIES = { export default { ACTIONS, TRIGGERS, - UTILITIES + LOGIC } \ No newline at end of file diff --git a/packages/builder/src/pages/[application]/workflow/WorkflowPanel/index.js b/packages/builder/src/pages/[application]/workflow/WorkflowPanel/index.js new file mode 100644 index 0000000000..dea50a6d41 --- /dev/null +++ b/packages/builder/src/pages/[application]/workflow/WorkflowPanel/index.js @@ -0,0 +1,3 @@ +export { default as WorkflowPanel } from "./WorkflowPanel.svelte"; +export { default as BlockList } from "./BlockList/BlockList.svelte"; +export { default as WorkflowList } from "./WorkflowList/WorkflowList.svelte"; \ No newline at end of file diff --git a/packages/builder/src/pages/[application]/workflow/_layout.svelte b/packages/builder/src/pages/[application]/workflow/_layout.svelte index 7b546e0397..54aa07717f 100644 --- a/packages/builder/src/pages/[application]/workflow/_layout.svelte +++ b/packages/builder/src/pages/[application]/workflow/_layout.svelte @@ -1,20 +1,18 @@
diff --git a/packages/builder/src/pages/[application]/workflow/flowy/WorkflowBuilder.svelte b/packages/builder/src/pages/[application]/workflow/flowy/WorkflowBuilder.svelte deleted file mode 100644 index 2990eb9e86..0000000000 --- a/packages/builder/src/pages/[application]/workflow/flowy/WorkflowBuilder.svelte +++ /dev/null @@ -1,49 +0,0 @@ - - -
diff --git a/packages/builder/src/pages/[application]/workflow/index.svelte b/packages/builder/src/pages/[application]/workflow/index.svelte index 766d9efaea..817aa67552 100644 --- a/packages/builder/src/pages/[application]/workflow/index.svelte +++ b/packages/builder/src/pages/[application]/workflow/index.svelte @@ -1,5 +1,5 @@ \ No newline at end of file diff --git a/packages/builder/src/pages/[application]/workflow/orchestrator/index.js b/packages/builder/src/pages/[application]/workflow/orchestrator/index.js deleted file mode 100644 index 5f037c07ff..0000000000 --- a/packages/builder/src/pages/[application]/workflow/orchestrator/index.js +++ /dev/null @@ -1,38 +0,0 @@ -import api from "builderStore/api"; - -class Orchestrator { - set strategy(strategy) { - this._stategy = strategy - } - - execute(workflow) { - this._strategy.execute(workflow); - } -} - -const ClientStrategy = { - execute: function(workflow) { - const block = workflow.next; - const EXECUTE_WORKFLOW_URL = `api/${workflow.instanceId}/workflows/${workflow._id}`; - - switch (block.type) { - case "CLIENT": - // fetch the workflow code from the server, then execute it here in the client - // catch any errors - // check against the conditions in the workflow - // if everything is fine, recurse - this.execute(workflow.next); - break; - case "SERVER": - // hit the server endpoint and wait for the response - // catch any errors - // check against the conditions in the workflow - // if everything is fine, recurse - await api.post() - break; - default: - break; - } - - } -} \ No newline at end of file diff --git a/packages/client/src/api/workflow/index.js b/packages/client/src/api/workflow/index.js index 7af5fc39dd..f9aa3403b3 100644 --- a/packages/client/src/api/workflow/index.js +++ b/packages/client/src/api/workflow/index.js @@ -2,7 +2,6 @@ import Orchestrator, { clientStrategy } from "./orchestrator"; export const triggerWorkflow = api => ({ workflow }) => { - console.log(workflow); const workflowOrchestrator = new Orchestrator( api, "inst_60dd510_700f7dc06735403e81d5af91072d7241" diff --git a/packages/client/src/api/workflow/orchestrator.js b/packages/client/src/api/workflow/orchestrator.js index 36c460876f..46b12f89a2 100644 --- a/packages/client/src/api/workflow/orchestrator.js +++ b/packages/client/src/api/workflow/orchestrator.js @@ -31,7 +31,7 @@ export default class Orchestrator { // Execute a workflow from a running budibase app export const clientStrategy = { context: {}, - bindContextArgs: function(args) { + bindContextArgs: function(args, api) { const mappedArgs = { ...args }; console.log("original args", args) @@ -50,12 +50,16 @@ export const clientStrategy = { // if the value is bound to state if (argValue.startsWith("$state")) { - const path = argValue.match("$context.", ""); - // pass in the value from context - mappedArgs[arg] = get(path, this.context); + const path = argValue.replace("$state.", ""); + // pass in the value from state + // TODO: not working + mappedArgs[arg] = api.getState(path); } } } + + console.log(mappedArgs); + return Object.values(mappedArgs); }, run: async function({ workflow, api, instanceId }) { @@ -85,7 +89,7 @@ export const clientStrategy = { url: EXECUTE_WORKFLOW_URL, body: { action: block.actionId, - args: block.args + args: this.bindContextArgs(block.args, api) } }); diff --git a/packages/server/package.json b/packages/server/package.json index 446679c5f0..6e2d095655 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -44,6 +44,7 @@ "@budibase/client": "^0.0.32", "@budibase/core": "^0.0.32", "@koa/router": "^8.0.0", + "@sendgrid/mail": "^7.1.1", "ajv": "^6.12.2", "bcryptjs": "^2.4.3", "dotenv": "^8.2.0", diff --git a/packages/server/src/api/controllers/workflow/actions/SEND_EMAIL.js b/packages/server/src/api/controllers/workflow/actions/SEND_EMAIL.js new file mode 100644 index 0000000000..072d61932f --- /dev/null +++ b/packages/server/src/api/controllers/workflow/actions/SEND_EMAIL.js @@ -0,0 +1,27 @@ +const sgMail = require('@sendgrid/mail'); + +sgMail.setApiKey(process.env.SENDGRID_API_KEY); + +module.exports = async function sendEmail(args) { + + const msg = { + to: args.to, + from: args.from, + subject: args.subject, + text: args.text + }; + + try { + await sgMail.send(msg); + return { + success: true, + err + } + } catch (err) { + return { + success: false, + err + } + } + +} \ No newline at end of file diff --git a/packages/server/src/api/controllers/workflow/index.js b/packages/server/src/api/controllers/workflow/index.js index 8d9c7bc1e7..ee7c536519 100644 --- a/packages/server/src/api/controllers/workflow/index.js +++ b/packages/server/src/api/controllers/workflow/index.js @@ -65,7 +65,7 @@ exports.update = async function(ctx) { exports.fetch = async function(ctx) { const db = new CouchDB(ctx.params.instanceId) const response = await db.query(`database/by_type`, { - type: "workflow", + key: ["workflow"], include_docs: true, }) ctx.body = response.rows.map(row => row.doc) diff --git a/packages/server/yarn.lock b/packages/server/yarn.lock index 8a64a7cb8a..763e28134e 100644 --- a/packages/server/yarn.lock +++ b/packages/server/yarn.lock @@ -194,6 +194,20 @@ lodash "^4.17.13" to-fast-properties "^2.0.0" +"@budibase/client@^0.0.32": + version "0.0.32" + resolved "https://registry.yarnpkg.com/@budibase/client/-/client-0.0.32.tgz#76d9f147563a0bf939eae7f32ce75b2a527ba496" + integrity sha512-jmCCLn0CUoQbL6h623S5IqK6+GYLqX3WzUTZInSb1SCBOM3pI0eLP5HwTR6s7r42SfD0v9jTWRdyTnHiElNj8A== + dependencies: + "@nx-js/compiler-util" "^2.0.0" + bcryptjs "^2.4.3" + deep-equal "^2.0.1" + lodash "^4.17.15" + lunr "^2.3.5" + regexparam "^1.3.0" + shortid "^2.2.8" + svelte "^3.9.2" + "@budibase/core@^0.0.32": version "0.0.32" resolved "https://registry.yarnpkg.com/@budibase/core/-/core-0.0.32.tgz#c5d9ab869c5e9596a1ac337aaf041e795b1cc7fa" @@ -409,6 +423,30 @@ resolved "https://registry.yarnpkg.com/@nx-js/compiler-util/-/compiler-util-2.0.0.tgz#c74c12165fa2f017a292bb79af007e8fce0af297" integrity sha512-AxSQbwj9zqt8DYPZ6LwZdytqnwfiOEdcFdq4l8sdjkZmU2clTht7RDLCI8xvkp7KqgcNaOGlTeCM55TULWruyQ== +"@sendgrid/client@^7.1.1": + version "7.1.1" + resolved "https://registry.yarnpkg.com/@sendgrid/client/-/client-7.1.1.tgz#09a25e58ac7e5321d66807e7110ff0fb61bb534f" + integrity sha512-V2BmOO81wHNmbTDwTJ07Olb9dWrj1G19xK4crwds68b9R0w05aOWDddZTvpn9mZnHwIJYqcZcBJuhdHDejuSHg== + dependencies: + "@sendgrid/helpers" "^7.0.1" + axios "^0.19.2" + +"@sendgrid/helpers@^7.0.1": + version "7.0.1" + resolved "https://registry.yarnpkg.com/@sendgrid/helpers/-/helpers-7.0.1.tgz#b97debc793ed3f9f56102e12c2a6d2bbbf0ffb78" + integrity sha512-i/zsissq1upgdywtuJKysaplJJZC24GdtEKiJC1IRlXvBHzIjH4eU+rqUFO8h+hGji3UMURGgMFuLUXTUYvZ9w== + dependencies: + chalk "^2.0.1" + deepmerge "^4.2.2" + +"@sendgrid/mail@^7.1.1": + version "7.1.1" + resolved "https://registry.yarnpkg.com/@sendgrid/mail/-/mail-7.1.1.tgz#26191594722d5961de1b61cd9c48fa9a69fd197b" + integrity sha512-VXdJ9J6vBNMw+wMIGFRvms6EmV6pvoRHMWoLJGweHlsZDnvmK3rWUnnNaS3OdDQ3A8B5bMv2WKsEnHsMZ6iDUg== + dependencies: + "@sendgrid/client" "^7.1.1" + "@sendgrid/helpers" "^7.0.1" + "@sindresorhus/is@^0.14.0": version "0.14.0" resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" @@ -825,6 +863,11 @@ array-equal@^1.0.0: resolved "https://registry.yarnpkg.com/array-equal/-/array-equal-1.0.0.tgz#8c2a5ef2472fd9ea742b04c77a75093ba2757c93" integrity sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM= +array-filter@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/array-filter/-/array-filter-1.0.0.tgz#baf79e62e6ef4c2a4c0b831232daffec251f9d83" + integrity sha1-uveeYubvTCpMC4MSMtr/7CUfnYM= + array-unique@^0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" @@ -897,6 +940,13 @@ atomic-sleep@^1.0.0: resolved "https://registry.yarnpkg.com/atomic-sleep/-/atomic-sleep-1.0.0.tgz#eb85b77a601fc932cfe432c5acd364a9e2c9075b" integrity sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ== +available-typed-arrays@^1.0.0, available-typed-arrays@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.2.tgz#6b098ca9d8039079ee3f77f7b783c4480ba513f5" + integrity sha512-XWX3OX8Onv97LMk/ftVyBibpGwY5a8SmuxZPzeOxqmuEqUCOM9ZE+uIaD1VNJ5QnvU2UQusvmKbuM1FR8QWGfQ== + dependencies: + array-filter "^1.0.0" + aws-sign2@~0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" @@ -907,6 +957,13 @@ aws4@^1.8.0: resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.9.1.tgz#7e33d8f7d449b3f673cd72deb9abdc552dbe528e" integrity sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug== +axios@^0.19.2: + version "0.19.2" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.19.2.tgz#3ea36c5d8818d0d5f8a8a97a6d36b86cdc00cb27" + integrity sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA== + dependencies: + follow-redirects "1.5.10" + babel-jest@^24.9.0: version "24.9.0" resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-24.9.0.tgz#3fc327cb8467b89d14d7bc70e315104a783ccd54" @@ -1567,6 +1624,13 @@ dateformat@^3.0.3: resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae" integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q== +debug@=3.1.0, debug@~3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" + integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== + dependencies: + ms "2.0.0" + debug@^2.2.0, debug@^2.3.3, debug@^2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" @@ -1588,13 +1652,6 @@ debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: dependencies: ms "^2.1.1" -debug@~3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" - integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== - dependencies: - ms "2.0.0" - decamelize@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" @@ -1612,6 +1669,26 @@ decompress-response@^3.3.0: dependencies: mimic-response "^1.0.0" +deep-equal@^2.0.1: + version "2.0.3" + resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-2.0.3.tgz#cad1c15277ad78a5c01c49c2dee0f54de8a6a7b0" + integrity sha512-Spqdl4H+ky45I9ByyJtXteOm9CaIrPmnIPmOhrkKGNYWeDgCvJ8jNYVCTjChxW4FqGuZnLHADc8EKRMX6+CgvA== + dependencies: + es-abstract "^1.17.5" + es-get-iterator "^1.1.0" + is-arguments "^1.0.4" + is-date-object "^1.0.2" + is-regex "^1.0.5" + isarray "^2.0.5" + object-is "^1.1.2" + object-keys "^1.1.1" + object.assign "^4.1.0" + regexp.prototype.flags "^1.3.0" + side-channel "^1.0.2" + which-boxed-primitive "^1.0.1" + which-collection "^1.0.1" + which-typed-array "^1.1.2" + deep-equal@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5" @@ -1627,6 +1704,11 @@ deep-is@~0.1.3: resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= +deepmerge@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" + integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== + defer-to-connect@^1.0.1: version "1.1.3" resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591" @@ -1965,7 +2047,7 @@ error-inject@^1.0.0: resolved "https://registry.yarnpkg.com/error-inject/-/error-inject-1.0.0.tgz#e2b3d91b54aed672f309d950d154850fa11d4f37" integrity sha1-4rPZG1Su1nLzCdlQ0VSFD6EdTzc= -es-abstract@^1.17.0-next.1, es-abstract@^1.17.2, es-abstract@^1.17.5: +es-abstract@^1.17.0-next.1, es-abstract@^1.17.2, es-abstract@^1.17.4, es-abstract@^1.17.5: version "1.17.5" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.5.tgz#d8c9d1d66c8981fb9200e2251d799eee92774ae9" integrity sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg== @@ -1982,6 +2064,19 @@ es-abstract@^1.17.0-next.1, es-abstract@^1.17.2, es-abstract@^1.17.5: string.prototype.trimleft "^2.1.1" string.prototype.trimright "^2.1.1" +es-get-iterator@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/es-get-iterator/-/es-get-iterator-1.1.0.tgz#bb98ad9d6d63b31aacdc8f89d5d0ee57bcb5b4c8" + integrity sha512-UfrmHuWQlNMTs35e1ypnvikg6jCz3SK8v8ImvmDsh36fCVUR1MqoFDiyn0/k52C8NqO3YsO8Oe0azeesNuqSsQ== + dependencies: + es-abstract "^1.17.4" + has-symbols "^1.0.1" + is-arguments "^1.0.4" + is-map "^2.0.1" + is-set "^2.0.1" + is-string "^1.0.5" + isarray "^2.0.5" + es-to-primitive@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" @@ -2444,6 +2539,13 @@ flatted@^2.0.0: resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.2.tgz#4575b21e2bcee7434aa9be662f4b7b5f9c2b5138" integrity sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA== +follow-redirects@1.5.10: + version "1.5.10" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.5.10.tgz#7b7a9f9aea2fdff36786a94ff643ed07f4ff5e2a" + integrity sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ== + dependencies: + debug "=3.1.0" + for-in@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" @@ -2967,11 +3069,21 @@ is-accessor-descriptor@^1.0.0: dependencies: kind-of "^6.0.0" +is-arguments@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.0.4.tgz#3faf966c7cba0ff437fb31f6250082fcf0448cf3" + integrity sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA== + is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= +is-bigint@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.0.tgz#73da8c33208d00f130e9b5e15d23eac9215601c4" + integrity sha512-t5mGUXC/xRheCK431ylNiSkGGpBp8bHENBcENTkDT6ppwPzEVxNGZRvgvmOEfbWkFhA7D2GEuE2mmQTr78sl2g== + is-binary-path@~2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" @@ -2979,6 +3091,11 @@ is-binary-path@~2.1.0: dependencies: binary-extensions "^2.0.0" +is-boolean-object@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.0.1.tgz#10edc0900dd127697a92f6f9807c7617d68ac48e" + integrity sha512-TqZuVwa/sppcrhUCAYkGBk7w0yxfQQnxq28fjkO53tnK9FQXmdwz2JS5+GjsWQ6RByES1K40nI+yDic5c9/aAQ== + is-buffer@^1.1.5: version "1.1.6" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" @@ -3015,7 +3132,7 @@ is-data-descriptor@^1.0.0: dependencies: kind-of "^6.0.0" -is-date-object@^1.0.1: +is-date-object@^1.0.1, is-date-object@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.2.tgz#bda736f2cd8fd06d32844e7743bfa7494c3bfd7e" integrity sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g== @@ -3090,11 +3207,21 @@ is-installed-globally@^0.3.1: global-dirs "^2.0.1" is-path-inside "^3.0.1" +is-map@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.1.tgz#520dafc4307bb8ebc33b813de5ce7c9400d644a1" + integrity sha512-T/S49scO8plUiAOA2DBTBG3JHpn1yiw0kRp6dgiZ0v2/6twi5eiB0rHtHFH9ZIrvlWc6+4O+m4zg5+Z833aXgw== + is-npm@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-4.0.0.tgz#c90dd8380696df87a7a6d823c20d0b12bbe3c84d" integrity sha512-96ECIfh9xtDDlPylNPXhzjsykHsMJZ18ASpaWzQyBr4YRTcVjUvzaHayDAES2oU/3KpljhHUjtSRNiDwi0F0ig== +is-number-object@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.4.tgz#36ac95e741cf18b283fc1ddf5e83da798e3ec197" + integrity sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw== + is-number@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" @@ -3131,11 +3258,21 @@ is-regex@^1.0.5: dependencies: has "^1.0.3" +is-set@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.1.tgz#d1604afdab1724986d30091575f54945da7e5f43" + integrity sha512-eJEzOtVyenDs1TMzSQ3kU3K+E0GUS9sno+F0OBT97xsgcJsF9nXMBtkT9/kut5JEpM7oL7X/0qxR17K3mcwIAA== + is-stream@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= +is-string@^1.0.4, is-string@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.5.tgz#40493ed198ef3ff477b8c7f92f644ec82a5cd3a6" + integrity sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ== + is-symbol@^1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.3.tgz#38e1014b9e6329be0de9d24a414fd7441ec61937" @@ -3152,11 +3289,31 @@ is-type-of@^1.0.0: is-class-hotfix "~0.0.6" isstream "~0.1.2" +is-typed-array@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.3.tgz#a4ff5a5e672e1a55f99c7f54e59597af5c1df04d" + integrity sha512-BSYUBOK/HJibQ30wWkWold5txYwMUXQct9YHAQJr8fSwvZoiglcqB0pd7vEN23+Tsi9IUEjztdOSzl4qLVYGTQ== + dependencies: + available-typed-arrays "^1.0.0" + es-abstract "^1.17.4" + foreach "^2.0.5" + has-symbols "^1.0.1" + is-typedarray@^1.0.0, is-typedarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= +is-weakmap@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-weakmap/-/is-weakmap-2.0.1.tgz#5008b59bdc43b698201d18f62b37b2ca243e8cf2" + integrity sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA== + +is-weakset@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-weakset/-/is-weakset-2.0.1.tgz#e9a0af88dbd751589f5e50d80f4c98b780884f83" + integrity sha512-pi4vhbhVHGLxohUw7PhGsueT4vRGFoXhP7+RGN0jKIv9+8PWYCQTqtADngrxOm2g46hoH0+g8uZZBzMrvVGDmw== + is-windows@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" @@ -3182,6 +3339,11 @@ isarray@1.0.0, isarray@~1.0.0: resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= +isarray@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" + integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== + isbinaryfile@^4.0.6: version "4.0.6" resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-4.0.6.tgz#edcb62b224e2b4710830b67498c8e4e5a4d2610b" @@ -4589,6 +4751,14 @@ object-inspect@^1.7.0: resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.7.0.tgz#f4f6bd181ad77f006b5ece60bd0b6f398ff74a67" integrity sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw== +object-is@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.2.tgz#c5d2e87ff9e119f78b7a088441519e2eec1573b6" + integrity sha512-5lHCz+0uufF6wZ7CRFWJN3hp8Jqblpgve06U5CMQ3f//6iDjPr2PEo9MWCjEssDsa+UZEL4PkFpr+BMop6aKzQ== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.5" + object-keys@^1.0.11, object-keys@^1.0.12, object-keys@^1.0.6, object-keys@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" @@ -5310,6 +5480,19 @@ regex-not@^1.0.0, regex-not@^1.0.2: extend-shallow "^3.0.2" safe-regex "^1.1.0" +regexp.prototype.flags@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz#7aba89b3c13a64509dabcf3ca8d9fbb9bdf5cb75" + integrity sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.0-next.1" + +regexparam@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/regexparam/-/regexparam-1.3.0.tgz#2fe42c93e32a40eff6235d635e0ffa344b92965f" + integrity sha512-6IQpFBv6e5vz1QAqI+V4k8P2e/3gRrqfCJ9FI+O1FLQTO+Uz6RXZEZOPmTJ6hlGj7gkERzY5BRCv09whKP96/g== + regexpp@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f" @@ -5638,6 +5821,14 @@ shortid@^2.2.8: dependencies: nanoid "^2.1.0" +side-channel@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.2.tgz#df5d1abadb4e4bf4af1cd8852bf132d2f7876947" + integrity sha512-7rL9YlPHg7Ancea1S96Pa8/QWb4BtXL/TZvS6B8XFetGBeuhAsfmUspK6DokBeZ64+Kj9TCNRD/30pVz1BvQNA== + dependencies: + es-abstract "^1.17.0-next.1" + object-inspect "^1.7.0" + signal-exit@^3.0.0, signal-exit@^3.0.2: version "3.0.3" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" @@ -6046,6 +6237,11 @@ supports-color@^7.1.0: dependencies: has-flag "^4.0.0" +svelte@^3.9.2: + version "3.22.3" + resolved "https://registry.yarnpkg.com/svelte/-/svelte-3.22.3.tgz#6af3bdcfea44c2fadbf17a32c479f49bdf1aba4b" + integrity sha512-DumSy5eWPFPlMUGf3+eHyFSkt5yLqyAmMdCuXOE4qc5GtFyLxwTAGKZmgKmW2jmbpTTeFQ/fSQfDBQbl9Eo7yw== + symbol-tree@^3.2.2: version "3.2.4" resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" @@ -6526,11 +6722,44 @@ whatwg-url@^7.0.0: tr46 "^1.0.1" webidl-conversions "^4.0.2" +which-boxed-primitive@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.1.tgz#cbe8f838ebe91ba2471bb69e9edbda67ab5a5ec1" + integrity sha512-7BT4TwISdDGBgaemWU0N0OU7FeAEJ9Oo2P1PHRm/FCWoEi2VLWC9b6xvxAA3C/NMpxg3HXVgi0sMmGbNUbNepQ== + dependencies: + is-bigint "^1.0.0" + is-boolean-object "^1.0.0" + is-number-object "^1.0.3" + is-string "^1.0.4" + is-symbol "^1.0.2" + +which-collection@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/which-collection/-/which-collection-1.0.1.tgz#70eab71ebbbd2aefaf32f917082fc62cdcb70906" + integrity sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A== + dependencies: + is-map "^2.0.1" + is-set "^2.0.1" + is-weakmap "^2.0.1" + is-weakset "^2.0.1" + which-module@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= +which-typed-array@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.2.tgz#e5f98e56bda93e3dac196b01d47c1156679c00b2" + integrity sha512-KT6okrd1tE6JdZAy3o2VhMoYPh3+J6EMZLyrxBQsZflI1QCZIxMrIYLkosd8Twf+YfknVIHmYQPgJt238p8dnQ== + dependencies: + available-typed-arrays "^1.0.2" + es-abstract "^1.17.5" + foreach "^2.0.5" + function-bind "^1.1.1" + has-symbols "^1.0.1" + is-typed-array "^1.1.3" + which@^1.2.9, which@^1.3.0: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" From 5894feaaff3a90e9da7ab85e84a5c26eb3835950 Mon Sep 17 00:00:00 2001 From: Michael Shanks Date: Thu, 28 May 2020 09:17:14 +0100 Subject: [PATCH 10/27] removed auth on execute workflow action --- packages/server/src/api/routes/workflow.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/server/src/api/routes/workflow.js b/packages/server/src/api/routes/workflow.js index 38563f0976..b4d8920261 100644 --- a/packages/server/src/api/routes/workflow.js +++ b/packages/server/src/api/routes/workflow.js @@ -15,11 +15,7 @@ router ) .put("/api/:instanceId/workflows", authorized(BUILDER), controller.update) .post("/api/:instanceId/workflows", authorized(BUILDER), controller.create) - .post( - "/api/:instanceId/workflows/action", - authorized(EXECUTE_WORKFLOW), - controller.executeAction - ) + .post("/api/:instanceId/workflows/action", controller.executeAction) .delete( "/api/:instanceId/workflows/:id/:rev", authorized(BUILDER), From 6f0a84dd38735dfe113e10072ab9cf1ce54df824 Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Thu, 28 May 2020 20:20:03 +0100 Subject: [PATCH 11/27] adding workflow blocks, parameter parsing, templating --- packages/builder/package.json | 2 + packages/builder/src/builderStore/api.js | 2 +- packages/builder/src/builderStore/index.js | 2 +- .../src/builderStore/store/workflow.js | 55 ----- .../builderStore/store/workflow/Workflow.js | 79 ++++++++ .../src/builderStore/store/workflow/index.js | 102 ++++++++++ .../store/workflow/tests/Workflow.js | 1 + .../ModelDataTable/modals/CreateUser.svelte | 2 +- .../ComponentPropertiesPanel.svelte | 2 +- packages/builder/src/global.css | 1 + .../SetupPanel/DeleteWorkflowModal.svelte | 87 ++++++++ .../ParamInputs/ComponentParam.svelte | 0 .../SetupPanel/ParamInputs/ModelParam.svelte | 0 .../SetupPanel/ParamInputs/NumberParam.svelte | 0 .../SetupPanel/ParamInputs/StringParam.svelte | 0 .../workflow/SetupPanel/SetupPanel.svelte | 35 ++-- .../SetupPanel/WorkflowBlockSetup.svelte | 63 +++++- .../workflow/SetupPanel/index.js | 2 +- .../WorkflowBuilder/WorkflowBuilder.svelte | 68 ++++--- .../svelte-flows/FlowChart.svelte | 4 +- .../svelte-flows/FlowItem.svelte | 26 ++- .../WorkflowPanel/BlockList/BlockList.svelte | 20 +- .../BlockList/WorkflowBlock.svelte | 10 +- .../WorkflowList/WorkflowList.svelte | 18 +- .../WorkflowPanel/WorkflowPanel.svelte | 12 +- .../WorkflowPanel/blockDefinitions.js | 81 ++++++-- .../workflow/WorkflowPanel/index.js | 6 +- .../[application]/workflow/_layout.svelte | 1 + .../pages/[application]/workflow/index.svelte | 6 +- packages/client/src/api/index.js | 8 +- packages/client/src/api/workflow/index.js | 10 +- .../client/src/api/workflow/orchestrator.js | 76 +++---- packages/server/src/api/controllers/record.js | 2 +- .../controllers/workflow/actions/CUSTOM_JS.js | 12 +- .../workflow/actions/SAVE_RECORD.js | 14 +- .../workflow/actions/SEND_EMAIL.js | 18 +- .../src/api/controllers/workflow/index.js | 23 +-- packages/server/src/app.js | 4 +- packages/server/src/events/index.js | 4 +- packages/server/src/schemas/index.js | 27 +-- packages/server/yarn.lock | 190 +----------------- 41 files changed, 630 insertions(+), 445 deletions(-) delete mode 100644 packages/builder/src/builderStore/store/workflow.js create mode 100644 packages/builder/src/builderStore/store/workflow/Workflow.js create mode 100644 packages/builder/src/builderStore/store/workflow/index.js create mode 100644 packages/builder/src/builderStore/store/workflow/tests/Workflow.js create mode 100644 packages/builder/src/pages/[application]/workflow/SetupPanel/DeleteWorkflowModal.svelte create mode 100644 packages/builder/src/pages/[application]/workflow/SetupPanel/ParamInputs/ComponentParam.svelte create mode 100644 packages/builder/src/pages/[application]/workflow/SetupPanel/ParamInputs/ModelParam.svelte create mode 100644 packages/builder/src/pages/[application]/workflow/SetupPanel/ParamInputs/NumberParam.svelte create mode 100644 packages/builder/src/pages/[application]/workflow/SetupPanel/ParamInputs/StringParam.svelte diff --git a/packages/builder/package.json b/packages/builder/package.json index 086d22382a..0926b82fb0 100644 --- a/packages/builder/package.json +++ b/packages/builder/package.json @@ -47,9 +47,11 @@ "lodash": "^4.17.13", "logrocket": "^1.0.6", "lunr": "^2.3.5", + "mustache": "^4.0.1", "safe-buffer": "^5.1.2", "shortid": "^2.2.8", "string_decoder": "^1.2.0", + "svelte-grid": "^1.10.8", "svelte-simple-modal": "^0.3.0", "uikit": "^3.1.7" }, diff --git a/packages/builder/src/builderStore/api.js b/packages/builder/src/builderStore/api.js index c9327a98f2..04dcce6cb9 100644 --- a/packages/builder/src/builderStore/api.js +++ b/packages/builder/src/builderStore/api.js @@ -25,5 +25,5 @@ export default { get, patch, delete: del, - put + put, } diff --git a/packages/builder/src/builderStore/index.js b/packages/builder/src/builderStore/index.js index dd6784c135..2af3a66667 100644 --- a/packages/builder/src/builderStore/index.js +++ b/packages/builder/src/builderStore/index.js @@ -1,6 +1,6 @@ import { getStore } from "./store" import { getBackendUiStore } from "./store/backend" -import { getWorkflowStore } from "./store/workflow" +import { getWorkflowStore } from "./store/workflow/" import LogRocket from "logrocket" export const store = getStore() diff --git a/packages/builder/src/builderStore/store/workflow.js b/packages/builder/src/builderStore/store/workflow.js deleted file mode 100644 index f1eb8b93f5..0000000000 --- a/packages/builder/src/builderStore/store/workflow.js +++ /dev/null @@ -1,55 +0,0 @@ -import { writable } from "svelte/store" -import api from "../api" - -const workflowActions = store => ({ - fetch: async instanceId => { - const WORKFLOWS_URL = `/api/${instanceId}/workflows`; - const workflowResponse = await api.get(WORKFLOWS_URL); - const json = await workflowResponse.json(); - store.update(state => { - state.workflows = json - return state - }) - }, - create: async ({ instanceId, name }) => { - const workflow = { name } - const CREATE_WORKFLOW_URL = `/api/${instanceId}/workflows`; - const response = await api.post(CREATE_WORKFLOW_URL, workflow) - const json = await response.json(); - store.update(state => { - state.workflows = state.workflows.concat(json.workflow) - state.selectedWorkflowId = json.workflow._id - return state - }) - }, - update: async ({ instanceId, workflow }) => { - const UPDATE_WORKFLOW_URL = `/api/${instanceId}/workflows`; - const response = await api.put(UPDATE_WORKFLOW_URL, workflow) - const json = await response.json(); - store.update(state => { - const existingIdx = state.workflows.findIndex(existing => existing._id === workflow._id); - state.workflows.splice(existingIdx, 1, json.workflow); - state.workflows = state.workflows - return state - }) - }, - select: workflow => { - store.update(state => { - state.selectedWorkflowId = workflow._id - state.selectedWorkflowBlock = null - return state; - }) - } -}); - -export const getWorkflowStore = () => { - const INITIAL_WORKFLOW_STATE = { - workflows: [] - } - - const store = writable(INITIAL_WORKFLOW_STATE) - - store.actions = workflowActions(store); - - return store -} \ No newline at end of file diff --git a/packages/builder/src/builderStore/store/workflow/Workflow.js b/packages/builder/src/builderStore/store/workflow/Workflow.js new file mode 100644 index 0000000000..b3e50466f7 --- /dev/null +++ b/packages/builder/src/builderStore/store/workflow/Workflow.js @@ -0,0 +1,79 @@ +import mustache from "mustache" +// TODO: tidy up import +import blockDefinitions from "../../../pages/[application]/workflow/WorkflowPanel/blockDefinitions" + +/** + * Class responsible for the traversing of the workflow definition. + * Workflow definitions are stored in linked lists. + */ +export default class Workflow { + constructor(workflow) { + this.workflow = workflow + } + + addBlock(block) { + let node = this.workflow.definition + while (node.next) node = node.next + node.next = block + } + + updateBlock(updatedBlock, id) { + let block = this.workflow.definition + + while (block.id !== id) block = block.next + if (!block) throw new Error("Block not found.") + + block = updatedBlock + } + + deleteBlock(id) { + let previous = null + let block = this.workflow.definition + + // iterate through the blocks + while (block.id !== id) { + previous = block + block = block.next + } + + // delete the block found + previous.next = block.next || {} + } + + createUiTree() { + if (!this.workflow.definition.next) return [] + return Workflow.buildUiTree(this.workflow.definition.next) + } + + static buildUiTree(block, tree = []) { + if (!block) return tree + + // The client side display definition for the block + const definition = blockDefinitions[block.type][block.actionId] + if (!definition) { + throw new Error( + `No block definition exists for the chosen block. Check there's an entry in the block definitions for ${block.actionId}` + ) + } + + if (!definition.params) { + throw new Error( + `Blocks should always have parameters. Ensure that the block definition is correct for ${block.actionId}` + ) + } + + const tagline = definition.tagline || "" + const args = block.args || {} + + tree.push({ + id: block.id, + type: block.type, + params: block.params, + args, + heading: block.actionId, + body: mustache.render(tagline, args), + }) + + return this.buildUiTree(block.next, tree) + } +} diff --git a/packages/builder/src/builderStore/store/workflow/index.js b/packages/builder/src/builderStore/store/workflow/index.js new file mode 100644 index 0000000000..fcdc299271 --- /dev/null +++ b/packages/builder/src/builderStore/store/workflow/index.js @@ -0,0 +1,102 @@ +import { writable } from "svelte/store" +import api from "../../api" +import Workflow from "./Workflow" + +const workflowActions = store => ({ + fetch: async instanceId => { + const WORKFLOWS_URL = `/api/${instanceId}/workflows` + const workflowResponse = await api.get(WORKFLOWS_URL) + const json = await workflowResponse.json() + store.update(state => { + state.workflows = json + return state + }) + }, + create: async ({ instanceId, name }) => { + const workflow = { name, definition: {} } + const CREATE_WORKFLOW_URL = `/api/${instanceId}/workflows` + const response = await api.post(CREATE_WORKFLOW_URL, workflow) + const json = await response.json() + store.update(state => { + state.workflows = state.workflows.concat(json.workflow) + state.currentWorkflow = new Workflow(json.workflow) + return state + }) + }, + save: async ({ instanceId, workflow }) => { + const UPDATE_WORKFLOW_URL = `/api/${instanceId}/workflows` + const response = await api.put(UPDATE_WORKFLOW_URL, workflow) + const json = await response.json() + store.update(state => { + const existingIdx = state.workflows.findIndex( + existing => existing._id === workflow._id + ) + state.workflows.splice(existingIdx, 1, json.workflow) + state.workflows = state.workflows + state.currentWorkflow = new Workflow(json.workflow) + return state + }) + }, + update: async ({ instanceId, workflow }) => { + const UPDATE_WORKFLOW_URL = `/api/${instanceId}/workflows` + const response = await api.put(UPDATE_WORKFLOW_URL, workflow) + const json = await response.json() + store.update(state => { + const existingIdx = state.workflows.findIndex( + existing => existing._id === workflow._id + ) + state.workflows.splice(existingIdx, 1, json.workflow) + state.workflows = state.workflows + return state + }) + }, + delete: async ({ instanceId, workflow }) => { + const { _id, _rev } = workflow + const DELETE_WORKFLOW_URL = `/api/${instanceId}/workflows/${_id}/${_rev}` + await api.delete(DELETE_WORKFLOW_URL) + + store.update(state => { + const existingIdx = state.workflows.findIndex( + existing => existing._id === _id + ) + state.workflows.splice(existingIdx, 1) + state.workflows = state.workflows + state.currentWorkflow = null + return state + }) + }, + select: workflow => { + store.update(state => { + // TODO: better naming + state.currentWorkflow = new Workflow(workflow) + state.selectedWorkflowBlock = null + return state + }) + }, + addBlockToWorkflow: block => { + store.update(state => { + state.currentWorkflow.addBlock(block) + state.selectedWorkflowBlock = block + return state + }) + }, + deleteWorkflowBlock: block => { + store.update(state => { + state.currentWorkflow.deleteBlock(block._id) + state.selectedWorkflowBlock = null + return state + }) + }, +}) + +export const getWorkflowStore = () => { + const INITIAL_WORKFLOW_STATE = { + workflows: [], + } + + const store = writable(INITIAL_WORKFLOW_STATE) + + store.actions = workflowActions(store) + + return store +} diff --git a/packages/builder/src/builderStore/store/workflow/tests/Workflow.js b/packages/builder/src/builderStore/store/workflow/tests/Workflow.js new file mode 100644 index 0000000000..a892d688b3 --- /dev/null +++ b/packages/builder/src/builderStore/store/workflow/tests/Workflow.js @@ -0,0 +1 @@ +describe("Workflow Data Object", () => {}) diff --git a/packages/builder/src/components/database/ModelDataTable/modals/CreateUser.svelte b/packages/builder/src/components/database/ModelDataTable/modals/CreateUser.svelte index 77e0850b87..e81659f716 100644 --- a/packages/builder/src/components/database/ModelDataTable/modals/CreateUser.svelte +++ b/packages/builder/src/components/database/ModelDataTable/modals/CreateUser.svelte @@ -34,7 +34,7 @@
diff --git a/packages/builder/src/components/userInterface/ComponentPropertiesPanel.svelte b/packages/builder/src/components/userInterface/ComponentPropertiesPanel.svelte index a255e92d90..c2cee9e6af 100644 --- a/packages/builder/src/components/userInterface/ComponentPropertiesPanel.svelte +++ b/packages/builder/src/components/userInterface/ComponentPropertiesPanel.svelte @@ -25,7 +25,7 @@ let categories = [ { value: "design", name: "Design" }, { value: "settings", name: "Settings" }, - { value: "events", name: "Events" } + { value: "events", name: "Events" }, ] let selectedCategory = categories[0] diff --git a/packages/builder/src/global.css b/packages/builder/src/global.css index 7edc1c6d40..852c5f044b 100644 --- a/packages/builder/src/global.css +++ b/packages/builder/src/global.css @@ -78,6 +78,7 @@ --primary: #0055ff; --secondary: #f1f4fc; --color: #393c44; + --light-grey: #fbfbfb; --dark-grey: #808192; --medium-grey: #e8e8ef; --background: rgb(251, 251, 251); diff --git a/packages/builder/src/pages/[application]/workflow/SetupPanel/DeleteWorkflowModal.svelte b/packages/builder/src/pages/[application]/workflow/SetupPanel/DeleteWorkflowModal.svelte new file mode 100644 index 0000000000..82af963cfc --- /dev/null +++ b/packages/builder/src/pages/[application]/workflow/SetupPanel/DeleteWorkflowModal.svelte @@ -0,0 +1,87 @@ + + +
+ + Delete Workflow +
+
+

+ Are you sure you want to delete this workflow? This action can't be undone. +

+
+ + + diff --git a/packages/builder/src/pages/[application]/workflow/SetupPanel/ParamInputs/ComponentParam.svelte b/packages/builder/src/pages/[application]/workflow/SetupPanel/ParamInputs/ComponentParam.svelte new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/builder/src/pages/[application]/workflow/SetupPanel/ParamInputs/ModelParam.svelte b/packages/builder/src/pages/[application]/workflow/SetupPanel/ParamInputs/ModelParam.svelte new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/builder/src/pages/[application]/workflow/SetupPanel/ParamInputs/NumberParam.svelte b/packages/builder/src/pages/[application]/workflow/SetupPanel/ParamInputs/NumberParam.svelte new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/builder/src/pages/[application]/workflow/SetupPanel/ParamInputs/StringParam.svelte b/packages/builder/src/pages/[application]/workflow/SetupPanel/ParamInputs/StringParam.svelte new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/builder/src/pages/[application]/workflow/SetupPanel/SetupPanel.svelte b/packages/builder/src/pages/[application]/workflow/SetupPanel/SetupPanel.svelte index 1bfd17779a..b23e7f4c4a 100644 --- a/packages/builder/src/pages/[application]/workflow/SetupPanel/SetupPanel.svelte +++ b/packages/builder/src/pages/[application]/workflow/SetupPanel/SetupPanel.svelte @@ -1,20 +1,27 @@ @@ -26,10 +33,12 @@
{#if workflowBlock} - - {:else if $workflowStore.selectedWorkflowId} + {:else if $workflowStore.currentWorkflow}
@@ -44,7 +53,9 @@ Some User Access Stuff Here
- {/if} diff --git a/packages/builder/src/pages/[application]/workflow/SetupPanel/WorkflowBlockSetup.svelte b/packages/builder/src/pages/[application]/workflow/SetupPanel/WorkflowBlockSetup.svelte index 1613a78f18..f3d0bdaba8 100644 --- a/packages/builder/src/pages/[application]/workflow/SetupPanel/WorkflowBlockSetup.svelte +++ b/packages/builder/src/pages/[application]/workflow/SetupPanel/WorkflowBlockSetup.svelte @@ -1,17 +1,62 @@ - -{#each workflowArgs as workflowArg} -
- + +{#each workflowParams as [parameter, type]} +
+
- + {#if type === 'number'} + + {:else if type === 'model'} + + {:else if type === 'component'} + + {:else if type === 'string'} + + {/if}
{/each} + + diff --git a/packages/builder/src/pages/[application]/workflow/SetupPanel/index.js b/packages/builder/src/pages/[application]/workflow/SetupPanel/index.js index 800406ff7a..ccfbddaf1c 100644 --- a/packages/builder/src/pages/[application]/workflow/SetupPanel/index.js +++ b/packages/builder/src/pages/[application]/workflow/SetupPanel/index.js @@ -1 +1 @@ -export { default as SetupPanel } from "./SetupPanel.svelte"; \ No newline at end of file +export { default as SetupPanel } from "./SetupPanel.svelte" diff --git a/packages/builder/src/pages/[application]/workflow/WorkflowBuilder/WorkflowBuilder.svelte b/packages/builder/src/pages/[application]/workflow/WorkflowBuilder/WorkflowBuilder.svelte index b9e43e7847..70f37e2638 100644 --- a/packages/builder/src/pages/[application]/workflow/WorkflowBuilder/WorkflowBuilder.svelte +++ b/packages/builder/src/pages/[application]/workflow/WorkflowBuilder/WorkflowBuilder.svelte @@ -9,29 +9,13 @@ let uiTree let instanceId = $backendUiStore.selectedDatabase._id - $: workflow = $workflowStore.workflows.find( - wf => wf._id === $workflowStore.selectedWorkflowId - ) + $: workflow = $workflowStore.currentWorkflow - // Build a renderable UI Tree for the flowchart generator - function buildUiTree(block, tree = []) { - if (!block) return tree - - tree.push({ - type: block.type, - heading: block.actionId, - args: block.args, - body: JSON.stringify(block.args), - }) - - return buildUiTree(block.next, tree) - } - - $: if (workflow) uiTree = workflow.definition ? buildUiTree(workflow.definition.next) : [] + $: if (workflow) uiTree = workflow ? workflow.createUiTree() : [] function onDelete(block) { // TODO finish - workflowStore.actions.deleteWorkflowBlock(block); + workflowStore.actions.deleteWorkflowBlock(block) } function onSelect(block) { @@ -43,9 +27,45 @@
- + +
+ + +
+ + diff --git a/packages/builder/src/pages/[application]/workflow/WorkflowBuilder/svelte-flows/FlowChart.svelte b/packages/builder/src/pages/[application]/workflow/WorkflowBuilder/svelte-flows/FlowChart.svelte index d444c14848..dc35b682c4 100644 --- a/packages/builder/src/pages/[application]/workflow/WorkflowBuilder/svelte-flows/FlowChart.svelte +++ b/packages/builder/src/pages/[application]/workflow/WorkflowBuilder/svelte-flows/FlowChart.svelte @@ -7,9 +7,9 @@
{#each blocks as block, idx} - + {#if idx !== blocks.length - 1} - + {/if} {/each}
diff --git a/packages/builder/src/pages/[application]/workflow/WorkflowBuilder/svelte-flows/FlowItem.svelte b/packages/builder/src/pages/[application]/workflow/WorkflowBuilder/svelte-flows/FlowItem.svelte index d533e01fa8..858d7dc075 100644 --- a/packages/builder/src/pages/[application]/workflow/WorkflowBuilder/svelte-flows/FlowItem.svelte +++ b/packages/builder/src/pages/[application]/workflow/WorkflowBuilder/svelte-flows/FlowItem.svelte @@ -3,19 +3,22 @@ export let block function selectBlock() { - onSelect(block); + onSelect(block) } + + console.log(block) -
+
{block.heading}

-

{block.body}

+

+ {@html block.body} +

diff --git a/packages/builder/src/pages/[application]/workflow/WorkflowPanel/blockDefinitions.js b/packages/builder/src/pages/[application]/workflow/WorkflowPanel/blockDefinitions.js index 5920070a7a..c0d81b95e8 100644 --- a/packages/builder/src/pages/[application]/workflow/WorkflowPanel/blockDefinitions.js +++ b/packages/builder/src/pages/[application]/workflow/WorkflowPanel/blockDefinitions.js @@ -1,89 +1,126 @@ -const ACTIONS = { +const ACTION = { SET_STATE: { name: "Update UI State", + tagline: "Update {{path}} to {{value}}", icon: "", description: "Update your User Interface with some data.", - type: "CLIENT", + environment: "CLIENT", + params: { + path: "string", + value: "string", + }, }, NAVIGATE: { name: "Navigate", icon: "ri-navigation-line", description: "Navigate to another page.", - type: "CLIENT" + environment: "CLIENT", + params: { + url: "string", + }, }, SAVE_RECORD: { name: "Save Record", icon: "ri-save-3-fill", description: "Save a record to your database.", - type: "SERVER", + environment: "SERVER", + params: { + model: "string", + }, }, DELETE_RECORD: { description: "Delete a record from your database.", icon: "ri-delete-bin-line", name: "Delete Record", - type: "SERVER", + environment: "SERVER", + params: { + record: "string", + }, }, FIND_RECORD: { description: "Delete a record from your database.", icon: "ri-delete-bin-line", name: "Find Record", - type: "SERVER", + environment: "SERVER", + params: { + record: "string", + }, }, CREATE_USER: { description: "Create a new user.", icon: "ri-user-add-fill", name: "Create User", - type: "SERVER", + environment: "SERVER", + params: { + name: "string", + password: "password", + accessLevel: "accessLevel", + }, }, SEND_EMAIL: { description: "Send an email.", + tagline: "Send email to {{to}}", icon: "ri-mail-open-fill", name: "Send Email", - type: "SERVER", - } -}; + environment: "SERVER", + params: { + to: "string", + from: "string", + subject: "string", + text: "string", + }, + }, +} -const TRIGGERS = { +const TRIGGER = { SAVE_RECORD: { name: "Record Saved", icon: "ri-delete-bin-line", + tagline: "Record is added to {{model}}", description: "Save a record to your database.", - type: "SERVER", + environment: "SERVER", + params: { + model: "model", + }, }, CLICK: { name: "Click", icon: "ri-cursor-line", - description: "Trigger when you click on an element in the UI." + description: "Trigger when you click on an element in the UI.", }, LOAD: { name: "Load", icon: "ri-loader-line", - description: "Trigger an element has finished loading." + description: "Trigger an element has finished loading.", }, INPUT: { name: "Input", icon: "ri-text", - description: "Trigger when you type into an input box." + description: "Trigger when you environment into an input box.", }, -}; +} const LOGIC = { FILTER: { name: "Filter", + tagline: "{{key}} {{condition}} {{value}}", icon: "ri-git-branch-line", description: "Filter any workflows which do not meet certain conditions.", - type: "CLIENT" + environment: "CLIENT", + params: { + if: "string", + }, }, DELAY: { name: "Delay", icon: "ri-git-branch-line", description: "Delay the workflow until an amount of time has passed.", - type: "CLIENT" + environment: "CLIENT", }, } export default { - ACTIONS, - TRIGGERS, - LOGIC -} \ No newline at end of file + ACTION, + TRIGGER, + LOGIC, +} diff --git a/packages/builder/src/pages/[application]/workflow/WorkflowPanel/index.js b/packages/builder/src/pages/[application]/workflow/WorkflowPanel/index.js index dea50a6d41..35fec385a7 100644 --- a/packages/builder/src/pages/[application]/workflow/WorkflowPanel/index.js +++ b/packages/builder/src/pages/[application]/workflow/WorkflowPanel/index.js @@ -1,3 +1,3 @@ -export { default as WorkflowPanel } from "./WorkflowPanel.svelte"; -export { default as BlockList } from "./BlockList/BlockList.svelte"; -export { default as WorkflowList } from "./WorkflowList/WorkflowList.svelte"; \ No newline at end of file +export { default as WorkflowPanel } from "./WorkflowPanel.svelte" +export { default as BlockList } from "./BlockList/BlockList.svelte" +export { default as WorkflowList } from "./WorkflowList/WorkflowList.svelte" diff --git a/packages/builder/src/pages/[application]/workflow/_layout.svelte b/packages/builder/src/pages/[application]/workflow/_layout.svelte index 54aa07717f..f1cd9e76dd 100644 --- a/packages/builder/src/pages/[application]/workflow/_layout.svelte +++ b/packages/builder/src/pages/[application]/workflow/_layout.svelte @@ -24,6 +24,7 @@ .nav { padding: 20px; + height: 100%; } .root { diff --git a/packages/builder/src/pages/[application]/workflow/index.svelte b/packages/builder/src/pages/[application]/workflow/index.svelte index 817aa67552..88f65b0b18 100644 --- a/packages/builder/src/pages/[application]/workflow/index.svelte +++ b/packages/builder/src/pages/[application]/workflow/index.svelte @@ -1,5 +1,5 @@ - - \ No newline at end of file + diff --git a/packages/client/src/api/index.js b/packages/client/src/api/index.js index ef6c6d06b9..00f8405cfb 100644 --- a/packages/client/src/api/index.js +++ b/packages/client/src/api/index.js @@ -2,8 +2,7 @@ import { loadRecord } from "./loadRecord" import { listRecords } from "./listRecords" import { authenticate } from "./authenticate" import { saveRecord } from "./saveRecord" -import { triggerWorkflow } from "./workflow"; - +import { triggerWorkflow } from "./workflow" export const createApi = ({ rootPath = "", setState, getState }) => { const apiCall = method => async ({ url, body }) => { @@ -16,7 +15,6 @@ export const createApi = ({ rootPath = "", setState, getState }) => { credentials: "same-origin", }) - switch (response.status) { case 200: return response.json() @@ -27,7 +25,7 @@ export const createApi = ({ rootPath = "", setState, getState }) => { case 403: return error(`${url} Forbidden`) default: - if (response.status >= 200 && response.status < 400) { + if (response.status >= 200 && response.status < 400) { return response.json() } @@ -66,6 +64,6 @@ export const createApi = ({ rootPath = "", setState, getState }) => { listRecords: listRecords(apiOpts), authenticate: authenticate(apiOpts), saveRecord: saveRecord(apiOpts), - triggerWorkflow: triggerWorkflow(apiOpts) + triggerWorkflow: triggerWorkflow(apiOpts), } } diff --git a/packages/client/src/api/workflow/index.js b/packages/client/src/api/workflow/index.js index f9aa3403b3..2e3c025adb 100644 --- a/packages/client/src/api/workflow/index.js +++ b/packages/client/src/api/workflow/index.js @@ -1,15 +1,13 @@ -import Orchestrator, { clientStrategy } from "./orchestrator"; - +import Orchestrator, { clientStrategy } from "./orchestrator" export const triggerWorkflow = api => ({ workflow }) => { const workflowOrchestrator = new Orchestrator( api, "inst_60dd510_700f7dc06735403e81d5af91072d7241" - ); + ) workflowOrchestrator.strategy = clientStrategy - workflowOrchestrator.execute(workflow); + workflowOrchestrator.execute(workflow) // hit the API and get the workflow data back - -} \ No newline at end of file +} diff --git a/packages/client/src/api/workflow/orchestrator.js b/packages/client/src/api/workflow/orchestrator.js index 46b12f89a2..afa92d007e 100644 --- a/packages/client/src/api/workflow/orchestrator.js +++ b/packages/client/src/api/workflow/orchestrator.js @@ -1,11 +1,11 @@ -import get from "lodash/fp/get"; +import get from "lodash/fp/get" /** * The workflow orhestrator is a class responsible for executing workflows. - * It relies on the strategy pattern, which allows composable behaviour to be + * It relies on the strategy pattern, which allows composable behaviour to be * passed into its execute() function. This allows custom execution behaviour based * on where the orchestrator is run. - * + * */ export default class Orchestrator { constructor(api, instanceId) { @@ -18,13 +18,13 @@ export default class Orchestrator { } async execute(workflowId) { - const EXECUTE_WORKFLOW_URL = `/api/${this.instanceId}/workflows/${workflowId}`; - const workflow = await this.api.get({ url: EXECUTE_WORKFLOW_URL }) + const EXECUTE_WORKFLOW_URL = `/api/${this.instanceId}/workflows/${workflowId}` + const workflow = await this.api.get({ url: EXECUTE_WORKFLOW_URL }) this._strategy.run({ workflow: workflow.definition, api: this.api, - instanceId: this.instanceId - }); + instanceId: this.instanceId, + }) } } @@ -32,76 +32,76 @@ export default class Orchestrator { export const clientStrategy = { context: {}, bindContextArgs: function(args, api) { - const mappedArgs = { ...args }; + const mappedArgs = { ...args } console.log("original args", args) // bind the workflow action args to the workflow context, if required for (let arg in args) { - const argValue = args[arg]; + const argValue = args[arg] // Means that it's bound to state or workflow context if (argValue.startsWith("$")) { // if value is bound to workflow context. if (argValue.startsWith("$context")) { - const path = argValue.replace("$context.", ""); + const path = argValue.replace("$context.", "") // pass in the value from context - mappedArgs[arg] = get(path, this.context); + mappedArgs[arg] = get(path, this.context) } // if the value is bound to state if (argValue.startsWith("$state")) { - const path = argValue.replace("$state.", ""); + const path = argValue.replace("$state.", "") // pass in the value from state // TODO: not working - mappedArgs[arg] = api.getState(path); + mappedArgs[arg] = api.getState(path) } } } - console.log(mappedArgs); + console.log(mappedArgs) - return Object.values(mappedArgs); + return Object.values(mappedArgs) }, run: async function({ workflow, api, instanceId }) { - const block = workflow.next; + const block = workflow.next - console.log("Executing workflow block", block); + console.log("Executing workflow block", block) - if (!block) return; + if (!block) return // This code gets run in the browser - if (block.type === "CLIENT") { - if (block.actionId === "SET_STATE") { - // get props from the workflow context if required - api.setState(...this.bindContextArgs(block.args)) - // update the context with the data - this.context = { - ...this.context, - SET_STATE: block.args - } + if (block.environment === "CLIENT") { + if (block.actionId === "SET_STATE") { + // get props from the workflow context if required + api.setState(...this.bindContextArgs(block.args)) + // update the context with the data + this.context = { + ...this.context, + SET_STATE: block.args, } - }; + } + } // this workflow block gets executed on the server - if (block.type === "SERVER") { + if (block.environment === "SERVER") { const EXECUTE_WORKFLOW_URL = `/api/${instanceId}/workflows/action` - const response = await api.post({ - url: EXECUTE_WORKFLOW_URL, + const response = await api.post({ + url: EXECUTE_WORKFLOW_URL, body: { action: block.actionId, - args: this.bindContextArgs(block.args, api) - } - }); + args: this.bindContextArgs(block.args, api), + }, + }) this.context = { ...this.context, - [block.actionId]: response + [block.actionId]: response, } } console.log("workflowContext", this.context) // TODO: clean this up, don't pass all those args - this.run({ workflow: workflow.next, instanceId, api }); - } -} \ No newline at end of file + this.run({ workflow: workflow.next, instanceId, api }) + }, +} diff --git a/packages/server/src/api/controllers/record.js b/packages/server/src/api/controllers/record.js index c4ac1deba3..c3e1fa940d 100644 --- a/packages/server/src/api/controllers/record.js +++ b/packages/server/src/api/controllers/record.js @@ -5,7 +5,7 @@ const newid = require("../../db/newid") const ajv = new Ajv() exports.save = async function(ctx) { - console.log("THIS INSTANCE", ctx.params.instanceId); + console.log("THIS INSTANCE", ctx.params.instanceId) const db = new CouchDB(ctx.params.instanceId) const record = ctx.request.body record.modelId = ctx.params.modelId diff --git a/packages/server/src/api/controllers/workflow/actions/CUSTOM_JS.js b/packages/server/src/api/controllers/workflow/actions/CUSTOM_JS.js index 77ac39783d..73072f2ac9 100644 --- a/packages/server/src/api/controllers/workflow/actions/CUSTOM_JS.js +++ b/packages/server/src/api/controllers/workflow/actions/CUSTOM_JS.js @@ -1,9 +1,9 @@ -export default async function () { - const response = await fetch("www.google.com"); - console.log(response); - console.log("CUSTOM ACTION"); +export default async function() { + const response = await fetch("www.google.com") + console.log(response) + console.log("CUSTOM ACTION") return { message: "CUSTOM_WORKFLOW_SCRIPT", - response + response, } -} \ No newline at end of file +} diff --git a/packages/server/src/api/controllers/workflow/actions/SAVE_RECORD.js b/packages/server/src/api/controllers/workflow/actions/SAVE_RECORD.js index 05d42bfc11..24225f2287 100644 --- a/packages/server/src/api/controllers/workflow/actions/SAVE_RECORD.js +++ b/packages/server/src/api/controllers/workflow/actions/SAVE_RECORD.js @@ -1,20 +1,20 @@ -const recordController = require("../../record"); +const recordController = require("../../record") module.exports = async function saveRecord(args) { - console.log("SAVING this record", args.record); + console.log("SAVING this record", args.record) const ctx = { params: { instanceId: "inst_60dd510_700f7dc06735403e81d5af91072d7241", }, request: { - body: args.record - } + body: args.record, + }, } - await recordController.save(ctx); + await recordController.save(ctx) return { - record: ctx.body + record: ctx.body, } -} \ No newline at end of file +} diff --git a/packages/server/src/api/controllers/workflow/actions/SEND_EMAIL.js b/packages/server/src/api/controllers/workflow/actions/SEND_EMAIL.js index 072d61932f..ef582781b8 100644 --- a/packages/server/src/api/controllers/workflow/actions/SEND_EMAIL.js +++ b/packages/server/src/api/controllers/workflow/actions/SEND_EMAIL.js @@ -1,27 +1,25 @@ -const sgMail = require('@sendgrid/mail'); +const sgMail = require("@sendgrid/mail") -sgMail.setApiKey(process.env.SENDGRID_API_KEY); +sgMail.setApiKey(process.env.SENDGRID_API_KEY) module.exports = async function sendEmail(args) { - const msg = { to: args.to, from: args.from, subject: args.subject, - text: args.text - }; + text: args.text, + } try { - await sgMail.send(msg); + await sgMail.send(msg) return { success: true, - err + err, } } catch (err) { return { success: false, - err + err, } } - -} \ No newline at end of file +} diff --git a/packages/server/src/api/controllers/workflow/index.js b/packages/server/src/api/controllers/workflow/index.js index ee7c536519..1a4cfa8591 100644 --- a/packages/server/src/api/controllers/workflow/index.js +++ b/packages/server/src/api/controllers/workflow/index.js @@ -28,7 +28,6 @@ exports.create = async function(ctx) { // return // } - workflow.type = "workflow" const response = await db.post(workflow) workflow._rev = response.rev @@ -39,14 +38,14 @@ exports.create = async function(ctx) { workflow: { ...workflow, _rev: response.rev, - _id: response.id - } - }; + _id: response.id, + }, + } } exports.update = async function(ctx) { const db = new CouchDB(ctx.params.instanceId) - const workflow = ctx.request.body; + const workflow = ctx.request.body const response = await db.put(workflow) workflow._rev = response.rev @@ -57,7 +56,7 @@ exports.update = async function(ctx) { workflow: { ...workflow, _rev: response.rev, - _id: response.id + _id: response.id, }, } } @@ -77,15 +76,15 @@ exports.find = async function(ctx) { } exports.executeAction = async function(ctx) { - const workflowAction = require(`./actions/${ctx.request.body.action}`); - const response = await workflowAction(ctx.request.body.args); - ctx.body = response; + const workflowAction = require(`./actions/${ctx.request.body.action}`) + const response = await workflowAction(ctx.request.body.args) + ctx.body = response } exports.fetchActionScript = async function(ctx) { - const workflowAction = require(`./actions/${ctx.action}`); - console.log(workflowAction); - ctx.body = workflowAction; + const workflowAction = require(`./actions/${ctx.action}`) + console.log(workflowAction) + ctx.body = workflowAction } exports.destroy = async function(ctx) { diff --git a/packages/server/src/app.js b/packages/server/src/app.js index f947738567..57fb037626 100644 --- a/packages/server/src/app.js +++ b/packages/server/src/app.js @@ -4,7 +4,7 @@ const logger = require("koa-pino-logger") const http = require("http") const api = require("./api") const env = require("./environment") -const eventPublisher = require("./events"); +const eventPublisher = require("./events") const app = new Koa() @@ -20,7 +20,7 @@ app.use( }) ) -app.context.publisher = eventPublisher; +app.context.publisher = eventPublisher // api routes app.use(api.routes()) diff --git a/packages/server/src/events/index.js b/packages/server/src/events/index.js index 14e4e052e1..7d54c4cd95 100644 --- a/packages/server/src/events/index.js +++ b/packages/server/src/events/index.js @@ -1,3 +1,3 @@ -const EventEmitter = require("events").EventEmitter; +const EventEmitter = require("events").EventEmitter -module.exports = new EventEmitter(); \ No newline at end of file +module.exports = new EventEmitter() diff --git a/packages/server/src/schemas/index.js b/packages/server/src/schemas/index.js index 23a839bf97..63a6514043 100644 --- a/packages/server/src/schemas/index.js +++ b/packages/server/src/schemas/index.js @@ -2,37 +2,38 @@ const WORKFLOW_SCHEMA = { properties: { type: "workflow", pageId: { - type: "string" + type: "string", }, screenId: { - type: "string" + type: "string", }, live: { - type: "boolean" + type: "boolean", }, uiTree: { - type: "object" + type: "object", }, definition: { type: "object", properties: { triggers: { type: "array" }, - next: { + next: { type: "object", properties: { + environment: { environment: "string" }, type: { type: "string" }, actionId: { type: "string" }, args: { type: "object" }, conditions: { type: "array" }, errorHandling: { type: "object" }, - next: { type: "object" } - } + next: { type: "object" }, + }, }, - } - } - } -}; + }, + }, + }, +} module.exports = { - WORKFLOW_SCHEMA -}; \ No newline at end of file + WORKFLOW_SCHEMA, +} diff --git a/packages/server/yarn.lock b/packages/server/yarn.lock index 763e28134e..3fc1f15622 100644 --- a/packages/server/yarn.lock +++ b/packages/server/yarn.lock @@ -194,20 +194,6 @@ lodash "^4.17.13" to-fast-properties "^2.0.0" -"@budibase/client@^0.0.32": - version "0.0.32" - resolved "https://registry.yarnpkg.com/@budibase/client/-/client-0.0.32.tgz#76d9f147563a0bf939eae7f32ce75b2a527ba496" - integrity sha512-jmCCLn0CUoQbL6h623S5IqK6+GYLqX3WzUTZInSb1SCBOM3pI0eLP5HwTR6s7r42SfD0v9jTWRdyTnHiElNj8A== - dependencies: - "@nx-js/compiler-util" "^2.0.0" - bcryptjs "^2.4.3" - deep-equal "^2.0.1" - lodash "^4.17.15" - lunr "^2.3.5" - regexparam "^1.3.0" - shortid "^2.2.8" - svelte "^3.9.2" - "@budibase/core@^0.0.32": version "0.0.32" resolved "https://registry.yarnpkg.com/@budibase/core/-/core-0.0.32.tgz#c5d9ab869c5e9596a1ac337aaf041e795b1cc7fa" @@ -863,11 +849,6 @@ array-equal@^1.0.0: resolved "https://registry.yarnpkg.com/array-equal/-/array-equal-1.0.0.tgz#8c2a5ef2472fd9ea742b04c77a75093ba2757c93" integrity sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM= -array-filter@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/array-filter/-/array-filter-1.0.0.tgz#baf79e62e6ef4c2a4c0b831232daffec251f9d83" - integrity sha1-uveeYubvTCpMC4MSMtr/7CUfnYM= - array-unique@^0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" @@ -940,13 +921,6 @@ atomic-sleep@^1.0.0: resolved "https://registry.yarnpkg.com/atomic-sleep/-/atomic-sleep-1.0.0.tgz#eb85b77a601fc932cfe432c5acd364a9e2c9075b" integrity sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ== -available-typed-arrays@^1.0.0, available-typed-arrays@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.2.tgz#6b098ca9d8039079ee3f77f7b783c4480ba513f5" - integrity sha512-XWX3OX8Onv97LMk/ftVyBibpGwY5a8SmuxZPzeOxqmuEqUCOM9ZE+uIaD1VNJ5QnvU2UQusvmKbuM1FR8QWGfQ== - dependencies: - array-filter "^1.0.0" - aws-sign2@~0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" @@ -1669,26 +1643,6 @@ decompress-response@^3.3.0: dependencies: mimic-response "^1.0.0" -deep-equal@^2.0.1: - version "2.0.3" - resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-2.0.3.tgz#cad1c15277ad78a5c01c49c2dee0f54de8a6a7b0" - integrity sha512-Spqdl4H+ky45I9ByyJtXteOm9CaIrPmnIPmOhrkKGNYWeDgCvJ8jNYVCTjChxW4FqGuZnLHADc8EKRMX6+CgvA== - dependencies: - es-abstract "^1.17.5" - es-get-iterator "^1.1.0" - is-arguments "^1.0.4" - is-date-object "^1.0.2" - is-regex "^1.0.5" - isarray "^2.0.5" - object-is "^1.1.2" - object-keys "^1.1.1" - object.assign "^4.1.0" - regexp.prototype.flags "^1.3.0" - side-channel "^1.0.2" - which-boxed-primitive "^1.0.1" - which-collection "^1.0.1" - which-typed-array "^1.1.2" - deep-equal@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5" @@ -2047,7 +2001,7 @@ error-inject@^1.0.0: resolved "https://registry.yarnpkg.com/error-inject/-/error-inject-1.0.0.tgz#e2b3d91b54aed672f309d950d154850fa11d4f37" integrity sha1-4rPZG1Su1nLzCdlQ0VSFD6EdTzc= -es-abstract@^1.17.0-next.1, es-abstract@^1.17.2, es-abstract@^1.17.4, es-abstract@^1.17.5: +es-abstract@^1.17.0-next.1, es-abstract@^1.17.2, es-abstract@^1.17.5: version "1.17.5" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.5.tgz#d8c9d1d66c8981fb9200e2251d799eee92774ae9" integrity sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg== @@ -2064,19 +2018,6 @@ es-abstract@^1.17.0-next.1, es-abstract@^1.17.2, es-abstract@^1.17.4, es-abstrac string.prototype.trimleft "^2.1.1" string.prototype.trimright "^2.1.1" -es-get-iterator@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/es-get-iterator/-/es-get-iterator-1.1.0.tgz#bb98ad9d6d63b31aacdc8f89d5d0ee57bcb5b4c8" - integrity sha512-UfrmHuWQlNMTs35e1ypnvikg6jCz3SK8v8ImvmDsh36fCVUR1MqoFDiyn0/k52C8NqO3YsO8Oe0azeesNuqSsQ== - dependencies: - es-abstract "^1.17.4" - has-symbols "^1.0.1" - is-arguments "^1.0.4" - is-map "^2.0.1" - is-set "^2.0.1" - is-string "^1.0.5" - isarray "^2.0.5" - es-to-primitive@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" @@ -3069,21 +3010,11 @@ is-accessor-descriptor@^1.0.0: dependencies: kind-of "^6.0.0" -is-arguments@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.0.4.tgz#3faf966c7cba0ff437fb31f6250082fcf0448cf3" - integrity sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA== - is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= -is-bigint@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.0.tgz#73da8c33208d00f130e9b5e15d23eac9215601c4" - integrity sha512-t5mGUXC/xRheCK431ylNiSkGGpBp8bHENBcENTkDT6ppwPzEVxNGZRvgvmOEfbWkFhA7D2GEuE2mmQTr78sl2g== - is-binary-path@~2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" @@ -3091,11 +3022,6 @@ is-binary-path@~2.1.0: dependencies: binary-extensions "^2.0.0" -is-boolean-object@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.0.1.tgz#10edc0900dd127697a92f6f9807c7617d68ac48e" - integrity sha512-TqZuVwa/sppcrhUCAYkGBk7w0yxfQQnxq28fjkO53tnK9FQXmdwz2JS5+GjsWQ6RByES1K40nI+yDic5c9/aAQ== - is-buffer@^1.1.5: version "1.1.6" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" @@ -3132,7 +3058,7 @@ is-data-descriptor@^1.0.0: dependencies: kind-of "^6.0.0" -is-date-object@^1.0.1, is-date-object@^1.0.2: +is-date-object@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.2.tgz#bda736f2cd8fd06d32844e7743bfa7494c3bfd7e" integrity sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g== @@ -3207,21 +3133,11 @@ is-installed-globally@^0.3.1: global-dirs "^2.0.1" is-path-inside "^3.0.1" -is-map@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.1.tgz#520dafc4307bb8ebc33b813de5ce7c9400d644a1" - integrity sha512-T/S49scO8plUiAOA2DBTBG3JHpn1yiw0kRp6dgiZ0v2/6twi5eiB0rHtHFH9ZIrvlWc6+4O+m4zg5+Z833aXgw== - is-npm@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-4.0.0.tgz#c90dd8380696df87a7a6d823c20d0b12bbe3c84d" integrity sha512-96ECIfh9xtDDlPylNPXhzjsykHsMJZ18ASpaWzQyBr4YRTcVjUvzaHayDAES2oU/3KpljhHUjtSRNiDwi0F0ig== -is-number-object@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.4.tgz#36ac95e741cf18b283fc1ddf5e83da798e3ec197" - integrity sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw== - is-number@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" @@ -3258,21 +3174,11 @@ is-regex@^1.0.5: dependencies: has "^1.0.3" -is-set@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.1.tgz#d1604afdab1724986d30091575f54945da7e5f43" - integrity sha512-eJEzOtVyenDs1TMzSQ3kU3K+E0GUS9sno+F0OBT97xsgcJsF9nXMBtkT9/kut5JEpM7oL7X/0qxR17K3mcwIAA== - is-stream@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= -is-string@^1.0.4, is-string@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.5.tgz#40493ed198ef3ff477b8c7f92f644ec82a5cd3a6" - integrity sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ== - is-symbol@^1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.3.tgz#38e1014b9e6329be0de9d24a414fd7441ec61937" @@ -3289,31 +3195,11 @@ is-type-of@^1.0.0: is-class-hotfix "~0.0.6" isstream "~0.1.2" -is-typed-array@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.3.tgz#a4ff5a5e672e1a55f99c7f54e59597af5c1df04d" - integrity sha512-BSYUBOK/HJibQ30wWkWold5txYwMUXQct9YHAQJr8fSwvZoiglcqB0pd7vEN23+Tsi9IUEjztdOSzl4qLVYGTQ== - dependencies: - available-typed-arrays "^1.0.0" - es-abstract "^1.17.4" - foreach "^2.0.5" - has-symbols "^1.0.1" - is-typedarray@^1.0.0, is-typedarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= -is-weakmap@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-weakmap/-/is-weakmap-2.0.1.tgz#5008b59bdc43b698201d18f62b37b2ca243e8cf2" - integrity sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA== - -is-weakset@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-weakset/-/is-weakset-2.0.1.tgz#e9a0af88dbd751589f5e50d80f4c98b780884f83" - integrity sha512-pi4vhbhVHGLxohUw7PhGsueT4vRGFoXhP7+RGN0jKIv9+8PWYCQTqtADngrxOm2g46hoH0+g8uZZBzMrvVGDmw== - is-windows@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" @@ -3339,11 +3225,6 @@ isarray@1.0.0, isarray@~1.0.0: resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= -isarray@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" - integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== - isbinaryfile@^4.0.6: version "4.0.6" resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-4.0.6.tgz#edcb62b224e2b4710830b67498c8e4e5a4d2610b" @@ -4751,14 +4632,6 @@ object-inspect@^1.7.0: resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.7.0.tgz#f4f6bd181ad77f006b5ece60bd0b6f398ff74a67" integrity sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw== -object-is@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.2.tgz#c5d2e87ff9e119f78b7a088441519e2eec1573b6" - integrity sha512-5lHCz+0uufF6wZ7CRFWJN3hp8Jqblpgve06U5CMQ3f//6iDjPr2PEo9MWCjEssDsa+UZEL4PkFpr+BMop6aKzQ== - dependencies: - define-properties "^1.1.3" - es-abstract "^1.17.5" - object-keys@^1.0.11, object-keys@^1.0.12, object-keys@^1.0.6, object-keys@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" @@ -5480,19 +5353,6 @@ regex-not@^1.0.0, regex-not@^1.0.2: extend-shallow "^3.0.2" safe-regex "^1.1.0" -regexp.prototype.flags@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz#7aba89b3c13a64509dabcf3ca8d9fbb9bdf5cb75" - integrity sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ== - dependencies: - define-properties "^1.1.3" - es-abstract "^1.17.0-next.1" - -regexparam@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/regexparam/-/regexparam-1.3.0.tgz#2fe42c93e32a40eff6235d635e0ffa344b92965f" - integrity sha512-6IQpFBv6e5vz1QAqI+V4k8P2e/3gRrqfCJ9FI+O1FLQTO+Uz6RXZEZOPmTJ6hlGj7gkERzY5BRCv09whKP96/g== - regexpp@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f" @@ -5821,14 +5681,6 @@ shortid@^2.2.8: dependencies: nanoid "^2.1.0" -side-channel@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.2.tgz#df5d1abadb4e4bf4af1cd8852bf132d2f7876947" - integrity sha512-7rL9YlPHg7Ancea1S96Pa8/QWb4BtXL/TZvS6B8XFetGBeuhAsfmUspK6DokBeZ64+Kj9TCNRD/30pVz1BvQNA== - dependencies: - es-abstract "^1.17.0-next.1" - object-inspect "^1.7.0" - signal-exit@^3.0.0, signal-exit@^3.0.2: version "3.0.3" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" @@ -6237,11 +6089,6 @@ supports-color@^7.1.0: dependencies: has-flag "^4.0.0" -svelte@^3.9.2: - version "3.22.3" - resolved "https://registry.yarnpkg.com/svelte/-/svelte-3.22.3.tgz#6af3bdcfea44c2fadbf17a32c479f49bdf1aba4b" - integrity sha512-DumSy5eWPFPlMUGf3+eHyFSkt5yLqyAmMdCuXOE4qc5GtFyLxwTAGKZmgKmW2jmbpTTeFQ/fSQfDBQbl9Eo7yw== - symbol-tree@^3.2.2: version "3.2.4" resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" @@ -6722,44 +6569,11 @@ whatwg-url@^7.0.0: tr46 "^1.0.1" webidl-conversions "^4.0.2" -which-boxed-primitive@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.1.tgz#cbe8f838ebe91ba2471bb69e9edbda67ab5a5ec1" - integrity sha512-7BT4TwISdDGBgaemWU0N0OU7FeAEJ9Oo2P1PHRm/FCWoEi2VLWC9b6xvxAA3C/NMpxg3HXVgi0sMmGbNUbNepQ== - dependencies: - is-bigint "^1.0.0" - is-boolean-object "^1.0.0" - is-number-object "^1.0.3" - is-string "^1.0.4" - is-symbol "^1.0.2" - -which-collection@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/which-collection/-/which-collection-1.0.1.tgz#70eab71ebbbd2aefaf32f917082fc62cdcb70906" - integrity sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A== - dependencies: - is-map "^2.0.1" - is-set "^2.0.1" - is-weakmap "^2.0.1" - is-weakset "^2.0.1" - which-module@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= -which-typed-array@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.2.tgz#e5f98e56bda93e3dac196b01d47c1156679c00b2" - integrity sha512-KT6okrd1tE6JdZAy3o2VhMoYPh3+J6EMZLyrxBQsZflI1QCZIxMrIYLkosd8Twf+YfknVIHmYQPgJt238p8dnQ== - dependencies: - available-typed-arrays "^1.0.2" - es-abstract "^1.17.5" - foreach "^2.0.5" - function-bind "^1.1.1" - has-symbols "^1.0.1" - is-typed-array "^1.1.3" - which@^1.2.9, which@^1.3.0: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" From 7a3b36839955cf4fde2ee940a4b7ca29a067245b Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Thu, 28 May 2020 23:31:55 +0100 Subject: [PATCH 12/27] make alerts live, more hooks, app notifications --- packages/builder/package.json | 1 + packages/builder/rollup.config.js | 2 +- packages/builder/src/App.svelte | 5 +- packages/builder/src/budibase.css | 3 +- .../builderStore/store/workflow/Workflow.js | 8 ++- .../src/builderStore/store/workflow/index.js | 2 +- .../EventsEditor/EventsEditor.svelte | 1 + .../EventsEditor/StateBindingCascader.svelte | 34 +++++++----- .../src/pages/[application]/_layout.svelte | 14 +++-- .../SetupPanel/DeleteWorkflowModal.svelte | 2 + .../workflow/SetupPanel/SetupPanel.svelte | 5 +- .../SetupPanel/WorkflowBlockSetup.svelte | 21 +++++--- .../WorkflowBuilder/WorkflowBuilder.svelte | 50 ++++++++++++----- .../WorkflowBuilder/svelte-flows/Arrow.svelte | 9 ++++ .../svelte-flows/FlowChart.svelte | 3 +- .../svelte-flows/FlowItem.svelte | 32 +++++++++-- .../WorkflowPanel/BlockList/BlockList.svelte | 9 +++- .../WorkflowList/CreateWorkflowModal.svelte | 2 + .../WorkflowList/WorkflowList.svelte | 13 +++-- .../WorkflowPanel/WorkflowPanel.svelte | 22 -------- .../WorkflowPanel/blockDefinitions.js | 53 +++++++++++++++---- .../client/src/api/workflow/orchestrator.js | 9 +++- 22 files changed, 208 insertions(+), 92 deletions(-) create mode 100644 packages/builder/src/pages/[application]/workflow/WorkflowBuilder/svelte-flows/Arrow.svelte diff --git a/packages/builder/package.json b/packages/builder/package.json index 0926b82fb0..1cb53bc99b 100644 --- a/packages/builder/package.json +++ b/packages/builder/package.json @@ -38,6 +38,7 @@ ] }, "dependencies": { + "@beyonk/svelte-notifications": "^2.0.3", "@budibase/client": "^0.0.32", "@nx-js/compiler-util": "^2.0.0", "codemirror": "^5.51.0", diff --git a/packages/builder/rollup.config.js b/packages/builder/rollup.config.js index fd7c487c3f..ae9a7e069e 100644 --- a/packages/builder/rollup.config.js +++ b/packages/builder/rollup.config.js @@ -152,7 +152,7 @@ export default { { find: "builderStore", replacement: path.resolve(projectRootDir, "src/builderStore"), - }, + } ], customResolver, }), diff --git a/packages/builder/src/App.svelte b/packages/builder/src/App.svelte index c6b76090f9..54968a52c2 100644 --- a/packages/builder/src/App.svelte +++ b/packages/builder/src/App.svelte @@ -7,6 +7,8 @@ import AppNotification, { showAppNotification, } from "components/common/AppNotification.svelte" + import { NotificationDisplay } from '@beyonk/svelte-notifications' + function showErrorBanner() { showAppNotification({ @@ -24,8 +26,7 @@ $basepath = "/_builder" - - + diff --git a/packages/builder/src/budibase.css b/packages/builder/src/budibase.css index f433b016da..2117926dd2 100644 --- a/packages/builder/src/budibase.css +++ b/packages/builder/src/budibase.css @@ -77,7 +77,8 @@ } .budibase__input { - width: 250px; + width: 100%; + max-width: 250px; height: 35px; border-radius: 3px; border: 1px solid #DBDBDB; diff --git a/packages/builder/src/builderStore/store/workflow/Workflow.js b/packages/builder/src/builderStore/store/workflow/Workflow.js index b3e50466f7..4a7daa5279 100644 --- a/packages/builder/src/builderStore/store/workflow/Workflow.js +++ b/packages/builder/src/builderStore/store/workflow/Workflow.js @@ -1,6 +1,7 @@ import mustache from "mustache" // TODO: tidy up import import blockDefinitions from "../../../pages/[application]/workflow/WorkflowPanel/blockDefinitions" +import { generate } from "shortid" /** * Class responsible for the traversing of the workflow definition. @@ -14,7 +15,10 @@ export default class Workflow { addBlock(block) { let node = this.workflow.definition while (node.next) node = node.next - node.next = block + node.next = { + id: generate(), + ...block + } } updateBlock(updatedBlock, id) { @@ -70,7 +74,7 @@ export default class Workflow { type: block.type, params: block.params, args, - heading: block.actionId, + heading: definition.actionId, body: mustache.render(tagline, args), }) diff --git a/packages/builder/src/builderStore/store/workflow/index.js b/packages/builder/src/builderStore/store/workflow/index.js index fcdc299271..971312c2ef 100644 --- a/packages/builder/src/builderStore/store/workflow/index.js +++ b/packages/builder/src/builderStore/store/workflow/index.js @@ -82,7 +82,7 @@ const workflowActions = store => ({ }, deleteWorkflowBlock: block => { store.update(state => { - state.currentWorkflow.deleteBlock(block._id) + state.currentWorkflow.deleteBlock(block.id) state.selectedWorkflowBlock = null return state }) diff --git a/packages/builder/src/components/userInterface/EventsEditor/EventsEditor.svelte b/packages/builder/src/components/userInterface/EventsEditor/EventsEditor.svelte index 2a61912173..d897ad9c2f 100644 --- a/packages/builder/src/components/userInterface/EventsEditor/EventsEditor.svelte +++ b/packages/builder/src/components/userInterface/EventsEditor/EventsEditor.svelte @@ -32,6 +32,7 @@ $: { events = Object.keys(component) + // TODO: use real events .filter(propName => ["onChange", "onClick", "onLoad"].includes(propName)) .map(propName => ({ name: propName, diff --git a/packages/builder/src/components/userInterface/EventsEditor/StateBindingCascader.svelte b/packages/builder/src/components/userInterface/EventsEditor/StateBindingCascader.svelte index e3811cc176..09aa7ae9ad 100644 --- a/packages/builder/src/components/userInterface/EventsEditor/StateBindingCascader.svelte +++ b/packages/builder/src/components/userInterface/EventsEditor/StateBindingCascader.svelte @@ -9,7 +9,7 @@ EVENT_TYPE_MEMBER_NAME, allHandlers, } from "components/common/eventHandlers" - import { store } from "builderStore" + import { store, workflowStore } from "builderStore" import StateBindingOptions from "../PropertyCascader/StateBindingOptions.svelte" import { ArrowDownIcon } from "components/common/Icons/" @@ -22,18 +22,26 @@
{parameter.name}
- - - {#if isOpen} - { - onChange(option) - isOpen = false - }} /> + {#if parameter.name === 'workflow'} + + {:else} + + + {#if isOpen} + { + onChange(option) + isOpen = false + }} /> + {/if} {/if}
diff --git a/packages/builder/src/pages/[application]/_layout.svelte b/packages/builder/src/pages/[application]/_layout.svelte index 5de5e3379b..6e1cd982e8 100644 --- a/packages/builder/src/pages/[application]/_layout.svelte +++ b/packages/builder/src/pages/[application]/_layout.svelte @@ -56,11 +56,10 @@ on:click={() => $goto(`/settings`)}> - (location = `/${application}`)}> - + + + +
@@ -84,6 +83,11 @@ flex-direction: column; } + a { + text-transform: none; + color: var(--ink-lighter); + } + .top-nav { flex: 0 0 auto; height: 60px; diff --git a/packages/builder/src/pages/[application]/workflow/SetupPanel/DeleteWorkflowModal.svelte b/packages/builder/src/pages/[application]/workflow/SetupPanel/DeleteWorkflowModal.svelte index 82af963cfc..a3eef9d9d2 100644 --- a/packages/builder/src/pages/[application]/workflow/SetupPanel/DeleteWorkflowModal.svelte +++ b/packages/builder/src/pages/[application]/workflow/SetupPanel/DeleteWorkflowModal.svelte @@ -1,5 +1,6 @@ diff --git a/packages/builder/src/pages/[application]/workflow/SetupPanel/SetupPanel.svelte b/packages/builder/src/pages/[application]/workflow/SetupPanel/SetupPanel.svelte index b23e7f4c4a..a7417002de 100644 --- a/packages/builder/src/pages/[application]/workflow/SetupPanel/SetupPanel.svelte +++ b/packages/builder/src/pages/[application]/workflow/SetupPanel/SetupPanel.svelte @@ -1,13 +1,14 @@ diff --git a/packages/builder/src/pages/[application]/workflow/SetupPanel/WorkflowBlockSetup.svelte b/packages/builder/src/pages/[application]/workflow/SetupPanel/WorkflowBlockSetup.svelte index f3d0bdaba8..e719da9fc2 100644 --- a/packages/builder/src/pages/[application]/workflow/SetupPanel/WorkflowBlockSetup.svelte +++ b/packages/builder/src/pages/[application]/workflow/SetupPanel/WorkflowBlockSetup.svelte @@ -1,5 +1,5 @@ @@ -18,7 +19,15 @@
- {#if type === 'number'} + {#if Array.isArray(type)} + + {:else if type === 'number'} {:else if type === 'component'} - + {:else if type === 'string'} import { onMount } from "svelte" import { workflowStore, backendUiStore } from "builderStore" + import { notifier } from "@beyonk/svelte-notifications" import Flowchart from "./svelte-flows/Flowchart.svelte" import api from "builderStore/api" - let canvas - let workflow + let selectedWorkflow let uiTree let instanceId = $backendUiStore.selectedDatabase._id - $: workflow = $workflowStore.currentWorkflow + // TODO: better naming + $: selectedWorkflow = $workflowStore.currentWorkflow - $: if (workflow) uiTree = workflow ? workflow.createUiTree() : [] + $: workflowLive = selectedWorkflow && selectedWorkflow.workflow.live + + $: if (selectedWorkflow) + uiTree = selectedWorkflow ? selectedWorkflow.createUiTree() : [] + + $: instanceId = $backendUiStore.selectedDatabase._id function onDelete(block) { // TODO finish @@ -24,17 +30,37 @@ return state }) } + + function setWorkflowLive(live) { + const { workflow } = selectedWorkflow + workflow.live = live + workflowStore.actions.save({ instanceId, workflow }) + if (live) { + notifier.info(`Workflow ${workflow.name} enabled.`) + } else { + notifier.danger(`Workflow ${workflow.name} disabled.`) + } + }
- - + {#if selectedWorkflow} + + + {/if}
@@ -61,11 +87,11 @@ margin-right: 24px; } - .play-button:hover { + .play-button.highlighted { background: var(--primary); } - .stop-button:hover { + .stop-button.highlighted { background: var(--coral); } diff --git a/packages/builder/src/pages/[application]/workflow/WorkflowBuilder/svelte-flows/Arrow.svelte b/packages/builder/src/pages/[application]/workflow/WorkflowBuilder/svelte-flows/Arrow.svelte new file mode 100644 index 0000000000..f5b855c341 --- /dev/null +++ b/packages/builder/src/pages/[application]/workflow/WorkflowBuilder/svelte-flows/Arrow.svelte @@ -0,0 +1,9 @@ + + + + diff --git a/packages/builder/src/pages/[application]/workflow/WorkflowBuilder/svelte-flows/FlowChart.svelte b/packages/builder/src/pages/[application]/workflow/WorkflowBuilder/svelte-flows/FlowChart.svelte index dc35b682c4..40c54246e9 100644 --- a/packages/builder/src/pages/[application]/workflow/WorkflowBuilder/svelte-flows/FlowChart.svelte +++ b/packages/builder/src/pages/[application]/workflow/WorkflowBuilder/svelte-flows/FlowChart.svelte @@ -1,5 +1,6 @@ -
-
{block.heading}
+
+
+ {#if block.type === 'TRIGGER'} + + When this happens... + {:else if block.type === 'ACTION'} + + Do this... + {:else if block.type === 'LOGIC'} + + Only continue if... + {/if} +

{@html block.body} @@ -21,7 +32,6 @@ div { width: 320px; padding: 20px; - margin-bottom: 60px; border-radius: 5px; transition: 0.3s all; box-shadow: 0 4px 30px 0 rgba(57, 60, 68, 0.08); @@ -30,6 +40,18 @@ color: var(--white); } + header { + font-size: 16px; + font-weight: 500; + display: flex; + align-items: center; + } + + header i { + font-size: 20px; + margin-right: 5px; + } + .ACTION { background-color: var(--white); color: var(--font); diff --git a/packages/builder/src/pages/[application]/workflow/WorkflowPanel/BlockList/BlockList.svelte b/packages/builder/src/pages/[application]/workflow/WorkflowPanel/BlockList/BlockList.svelte index 8db781af36..e288e78e94 100644 --- a/packages/builder/src/pages/[application]/workflow/WorkflowPanel/BlockList/BlockList.svelte +++ b/packages/builder/src/pages/[application]/workflow/WorkflowPanel/BlockList/BlockList.svelte @@ -52,16 +52,21 @@ grid-gap: 5px; grid-auto-flow: column; grid-auto-columns: 1fr 1fr 1fr; + margin-bottom: 10px; } .subtabs span { + transition: 0.3s all; text-align: center; - color: var(--font); + color: var(--dark-grey); font-weight: 500; + padding: 10px; } .subtabs span.selected { - border-bottom: 4px solid var(--primary); + background: var(--dark-grey); + color: var(--white); + border-radius: 2px; } .subtabs span:not(.selected) { diff --git a/packages/builder/src/pages/[application]/workflow/WorkflowPanel/WorkflowList/CreateWorkflowModal.svelte b/packages/builder/src/pages/[application]/workflow/WorkflowPanel/WorkflowList/CreateWorkflowModal.svelte index 510b21704e..0a31cf3702 100644 --- a/packages/builder/src/pages/[application]/workflow/WorkflowPanel/WorkflowList/CreateWorkflowModal.svelte +++ b/packages/builder/src/pages/[application]/workflow/WorkflowPanel/WorkflowList/CreateWorkflowModal.svelte @@ -1,5 +1,6 @@ diff --git a/packages/builder/src/pages/[application]/workflow/WorkflowPanel/WorkflowList/WorkflowList.svelte b/packages/builder/src/pages/[application]/workflow/WorkflowPanel/WorkflowList/WorkflowList.svelte index 2218414ad9..2dc3e05a80 100644 --- a/packages/builder/src/pages/[application]/workflow/WorkflowPanel/WorkflowList/WorkflowList.svelte +++ b/packages/builder/src/pages/[application]/workflow/WorkflowPanel/WorkflowList/WorkflowList.svelte @@ -1,5 +1,6 @@ @@ -82,7 +85,7 @@ display: flex; align-items: center; border-radius: 3px; - height: 40px; + height: 32px; font-weight: 500; } diff --git a/packages/builder/src/pages/[application]/workflow/WorkflowPanel/WorkflowPanel.svelte b/packages/builder/src/pages/[application]/workflow/WorkflowPanel/WorkflowPanel.svelte index c9625d2441..016c789c64 100644 --- a/packages/builder/src/pages/[application]/workflow/WorkflowPanel/WorkflowPanel.svelte +++ b/packages/builder/src/pages/[application]/workflow/WorkflowPanel/WorkflowPanel.svelte @@ -5,17 +5,6 @@ import api from "builderStore/api" import blockDefinitions from "./blockDefinitions" - const WORKFLOW_TABS = [ - { - name: "Workflows", - key: "WORKFLOWS", - }, - { - name: "Add", - key: "ADD", - }, - ] - let selectedTab = "WORKFLOWS" let definitions = [] @@ -57,15 +46,4 @@ span:not(.selected) { color: var(--dark-grey); } - - .delete-workflow-button { - font-family: Roboto; - width: 100%; - border: solid 1px #f2f2f2; - border-radius: 2px; - background: var(--white); - height: 32px; - font-size: 12px; - font-weight: 500; - } diff --git a/packages/builder/src/pages/[application]/workflow/WorkflowPanel/blockDefinitions.js b/packages/builder/src/pages/[application]/workflow/WorkflowPanel/blockDefinitions.js index c0d81b95e8..1d6402ebe5 100644 --- a/packages/builder/src/pages/[application]/workflow/WorkflowPanel/blockDefinitions.js +++ b/packages/builder/src/pages/[application]/workflow/WorkflowPanel/blockDefinitions.js @@ -2,7 +2,7 @@ const ACTION = { SET_STATE: { name: "Update UI State", tagline: "Update {{path}} to {{value}}", - icon: "", + icon: "ri-refresh-line", description: "Update your User Interface with some data.", environment: "CLIENT", params: { @@ -38,8 +38,8 @@ const ACTION = { }, }, FIND_RECORD: { - description: "Delete a record from your database.", - icon: "ri-delete-bin-line", + description: "Find a record in your database.", + icon: "ri-search-line", name: "Find Record", environment: "SERVER", params: { @@ -59,7 +59,7 @@ const ACTION = { }, SEND_EMAIL: { description: "Send an email.", - tagline: "Send email to {{to}}", + tagline: "Send email to {{to}}", icon: "ri-mail-open-fill", name: "Send Email", environment: "SERVER", @@ -73,9 +73,9 @@ const ACTION = { } const TRIGGER = { - SAVE_RECORD: { + RECORD_SAVED: { name: "Record Saved", - icon: "ri-delete-bin-line", + icon: "ri-save-line", tagline: "Record is added to {{model}}", description: "Save a record to your database.", environment: "SERVER", @@ -83,39 +83,72 @@ const TRIGGER = { model: "model", }, }, + RECORD_DELETED: { + name: "Record Deleted", + icon: "ri-delete-bin-line", + tagline: "Record is deleted from {{model}}", + description: "Fired when a record is deleted from your database.", + environment: "SERVER", + params: { + model: "model" + }, + }, CLICK: { name: "Click", icon: "ri-cursor-line", + tagline: "{{component}} is clicked", description: "Trigger when you click on an element in the UI.", + environment: "CLIENT", + params: { + component: "component" + } }, LOAD: { name: "Load", icon: "ri-loader-line", + tagline: "{{component}} is loaded", description: "Trigger an element has finished loading.", + environment: "CLIENT", + params: { + component: "component" + } }, INPUT: { name: "Input", icon: "ri-text", - description: "Trigger when you environment into an input box.", + tagline: "Text entered into {{component}", + description: "Trigger when you type into an input box.", + environment: "CLIENT", + params: { + component: "component" + } }, } const LOGIC = { FILTER: { name: "Filter", - tagline: "{{key}} {{condition}} {{value}}", + tagline: "{{field}} {{condition}} {{value}}", icon: "ri-git-branch-line", description: "Filter any workflows which do not meet certain conditions.", environment: "CLIENT", params: { - if: "string", + field: "string", + condition: [ + "equals" + ], + value: "string" }, }, DELAY: { name: "Delay", - icon: "ri-git-branch-line", + icon: "ri-time-fill", + tagline: "Delay for {{time}} milliseconds", description: "Delay the workflow until an amount of time has passed.", environment: "CLIENT", + params: { + time: "number", + }, }, } diff --git a/packages/client/src/api/workflow/orchestrator.js b/packages/client/src/api/workflow/orchestrator.js index afa92d007e..532219eebc 100644 --- a/packages/client/src/api/workflow/orchestrator.js +++ b/packages/client/src/api/workflow/orchestrator.js @@ -1,7 +1,7 @@ import get from "lodash/fp/get" /** - * The workflow orhestrator is a class responsible for executing workflows. + * The workflow orchestrator is a class responsible for executing workflows. * It relies on the strategy pattern, which allows composable behaviour to be * passed into its execute() function. This allows custom execution behaviour based * on where the orchestrator is run. @@ -30,6 +30,7 @@ export default class Orchestrator { // Execute a workflow from a running budibase app export const clientStrategy = { + delay: ms => new Promise(resolve => setTimeout(resolve, ms)), context: {}, bindContextArgs: function(args, api) { const mappedArgs = { ...args } @@ -80,6 +81,10 @@ export const clientStrategy = { SET_STATE: block.args, } } + + if (block.actionId === "DELAY") { + await this.delay(block.args.time) + } } // this workflow block gets executed on the server @@ -102,6 +107,6 @@ export const clientStrategy = { console.log("workflowContext", this.context) // TODO: clean this up, don't pass all those args - this.run({ workflow: workflow.next, instanceId, api }) + await this.run({ workflow: workflow.next, instanceId, api }) }, } From e648dc80e836bada7a33aa39a2d3a82bbf3f1c9a Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Fri, 29 May 2020 14:06:10 +0100 Subject: [PATCH 13/27] refactor client library --- .../builder/src/builderStore/store/index.js | 2 - .../AppPreview/CurrentItemPreview.svelte | 3 +- .../AppPreview/iframeTemplate.js | 1 - .../SetupPanel/WorkflowBlockSetup.svelte | 5 + .../WorkflowPanel/blockDefinitions.js | 4 +- .../appDirectoryTemplate/pages/main/page.json | 3 +- .../pages/unauthenticated/page.json | 3 +- .../cli/src/commands/new/page.template.json | 3 +- packages/client/package.json | 1 + packages/client/src/api/index.js | 6 - packages/client/src/api/listRecords.js | 19 -- packages/client/src/api/loadRecord.js | 19 -- packages/client/src/api/saveRecord.js | 29 -- .../client/src/api/workflow/orchestrator.js | 38 ++- packages/client/src/createApp.js | 8 +- packages/client/src/index.js | 11 +- packages/client/src/render/attachChildren.js | 4 - .../src/render/prepareRenderComponent.js | 11 +- packages/client/src/state/bbComponentApi.js | 17 +- packages/client/src/state/coreHandlers.js | 71 ----- packages/client/src/state/eventHandlers.js | 2 +- packages/client/src/state/getState.js | 67 +++-- packages/client/src/state/parseBinding.js | 2 +- packages/client/src/state/setState.js | 38 +-- packages/client/src/state/stateManager.js | 103 ++++--- .../client/src/state/stateManager/index.js | 283 ------------------ packages/client/tests/testAppDef.js | 32 +- .../server/src/utilities/builder/buildPage.js | 1 - 28 files changed, 161 insertions(+), 625 deletions(-) delete mode 100644 packages/client/src/api/listRecords.js delete mode 100644 packages/client/src/api/loadRecord.js delete mode 100644 packages/client/src/api/saveRecord.js delete mode 100644 packages/client/src/state/coreHandlers.js delete mode 100644 packages/client/src/state/stateManager/index.js diff --git a/packages/builder/src/builderStore/store/index.js b/packages/builder/src/builderStore/store/index.js index f4d47064be..2009737254 100644 --- a/packages/builder/src/builderStore/store/index.js +++ b/packages/builder/src/builderStore/store/index.js @@ -155,7 +155,6 @@ const createScreen = store => (screenName, route, layoutComponentName) => { description: "", url: "", _css: "", - uiFunctions: "", props: createProps(rootComponent).props, } @@ -281,7 +280,6 @@ const _savePage = async s => { const page = s.pages[s.currentPageName] await api.post(`/_builder/api/${s.appId}/pages/${s.currentPageName}`, { page: { componentLibraries: s.pages.componentLibraries, ...page }, - uiFunctions: s.currentPageFunctions, screens: page._screens, }) } diff --git a/packages/builder/src/components/userInterface/AppPreview/CurrentItemPreview.svelte b/packages/builder/src/components/userInterface/AppPreview/CurrentItemPreview.svelte index 27a4851c8d..ad52602afc 100644 --- a/packages/builder/src/components/userInterface/AppPreview/CurrentItemPreview.svelte +++ b/packages/builder/src/components/userInterface/AppPreview/CurrentItemPreview.svelte @@ -116,8 +116,7 @@ stylesheetLinks, selectedComponentType, selectedComponentId, - frontendDefinition: JSON.stringify(frontendDefinition), - currentPageFunctions: $store.currentPageFunctions, + frontendDefinition: JSON.stringify(frontendDefinition) })} /> {/if}

diff --git a/packages/builder/src/components/userInterface/AppPreview/iframeTemplate.js b/packages/builder/src/components/userInterface/AppPreview/iframeTemplate.js index dd2ca69dbb..da17bead3f 100644 --- a/packages/builder/src/components/userInterface/AppPreview/iframeTemplate.js +++ b/packages/builder/src/components/userInterface/AppPreview/iframeTemplate.js @@ -36,7 +36,6 @@ export default ({ @@ -31,15 +32,15 @@
Setup
-
- {#if workflowBlock} - - - {:else if $workflowStore.currentWorkflow} + {#if workflowBlock} + + + {:else if $workflowStore.currentWorkflow} +
@@ -54,19 +55,22 @@ Some User Access Stuff Here
- - {/if} -
+
+ + {/if}
diff --git a/packages/builder/src/components/workflow/WorkflowPanel/BlockList/BlockList.svelte b/packages/builder/src/components/workflow/WorkflowPanel/BlockList/BlockList.svelte index e288e78e94..a690492672 100644 --- a/packages/builder/src/components/workflow/WorkflowPanel/BlockList/BlockList.svelte +++ b/packages/builder/src/components/workflow/WorkflowPanel/BlockList/BlockList.svelte @@ -1,42 +1,45 @@
- {#each SUB_TABS as tab} + {#if $workflowStore.currentWorkflow.isEmpty()} (selectedTab = tab.key)}> - {tab.name} + class:selected={'TRIGGER' === selectedTab} + on:click={() => (selectedTab = 'TRIGGER')}> + Triggers - {/each} + {/if} + (selectedTab = 'ACTION')}> + Actions + + (selectedTab = 'LOGIC')}> + Logic +
{#each definitions as [actionId, blockDefinition]} diff --git a/packages/builder/src/components/workflow/WorkflowPanel/BlockList/WorkflowBlock.svelte b/packages/builder/src/components/workflow/WorkflowPanel/BlockList/WorkflowBlock.svelte index 31014b86f7..0907efe7ae 100644 --- a/packages/builder/src/components/workflow/WorkflowPanel/BlockList/WorkflowBlock.svelte +++ b/packages/builder/src/components/workflow/WorkflowPanel/BlockList/WorkflowBlock.svelte @@ -6,7 +6,6 @@ export let actionId function addBlockToWorkflow() { - // TODO: store the block type in the DB as well workflowStore.actions.addBlockToWorkflow({ ...blockDefinition, args: {}, diff --git a/packages/builder/src/components/workflow/WorkflowPanel/WorkflowPanel.svelte b/packages/builder/src/components/workflow/WorkflowPanel/WorkflowPanel.svelte index c831ed0304..b057177cae 100644 --- a/packages/builder/src/components/workflow/WorkflowPanel/WorkflowPanel.svelte +++ b/packages/builder/src/components/workflow/WorkflowPanel/WorkflowPanel.svelte @@ -11,7 +11,7 @@
(selectedTab = 'WORKFLOWS')}> Workflows @@ -37,10 +37,13 @@ font-weight: bold; display: flex; align-items: center; - justify-content: space-between; margin-bottom: 20px; } + .workflow-header { + margin-right: 20px; + } + span:not(.selected) { color: var(--dark-grey); } diff --git a/packages/builder/src/components/workflow/WorkflowPanel/blockDefinitions.js b/packages/builder/src/components/workflow/WorkflowPanel/blockDefinitions.js index 3f61e4f5d3..17203646a8 100644 --- a/packages/builder/src/components/workflow/WorkflowPanel/blockDefinitions.js +++ b/packages/builder/src/components/workflow/WorkflowPanel/blockDefinitions.js @@ -7,7 +7,7 @@ const ACTION = { environment: "CLIENT", params: { path: "string", - value: "string", + value: "longText", }, }, NAVIGATE: { @@ -77,8 +77,9 @@ const ACTION = { const TRIGGER = { RECORD_SAVED: { name: "Record Saved", + event: "record:save", icon: "ri-save-line", - tagline: "Record is added to {{model}}", + tagline: "Record is added to {{model.name}}", description: "Save a record to your database.", environment: "SERVER", params: { @@ -87,44 +88,45 @@ const TRIGGER = { }, RECORD_DELETED: { name: "Record Deleted", + event: "record:delete", icon: "ri-delete-bin-line", - tagline: "Record is deleted from {{model}}", + tagline: "Record is deleted from {{model.name}}", description: "Fired when a record is deleted from your database.", environment: "SERVER", params: { model: "model" }, }, - CLICK: { - name: "Click", - icon: "ri-cursor-line", - tagline: "{{component}} is clicked", - description: "Trigger when you click on an element in the UI.", - environment: "CLIENT", - params: { - component: "component" - } - }, - LOAD: { - name: "Load", - icon: "ri-loader-line", - tagline: "{{component}} is loaded", - description: "Trigger an element has finished loading.", - environment: "CLIENT", - params: { - component: "component" - } - }, - INPUT: { - name: "Input", - icon: "ri-text", - tagline: "Text entered into {{component}", - description: "Trigger when you type into an input box.", - environment: "CLIENT", - params: { - component: "component" - } - }, + // CLICK: { + // name: "Click", + // icon: "ri-cursor-line", + // tagline: "{{component}} is clicked", + // description: "Trigger when you click on an element in the UI.", + // environment: "CLIENT", + // params: { + // component: "component" + // } + // }, + // LOAD: { + // name: "Load", + // icon: "ri-loader-line", + // tagline: "{{component}} is loaded", + // description: "Trigger an element has finished loading.", + // environment: "CLIENT", + // params: { + // component: "component" + // } + // }, + // INPUT: { + // name: "Input", + // icon: "ri-text", + // tagline: "Text entered into {{component}", + // description: "Trigger when you type into an input box.", + // environment: "CLIENT", + // params: { + // component: "component" + // } + // }, } const LOGIC = { @@ -135,9 +137,9 @@ const LOGIC = { description: "Filter any workflows which do not meet certain conditions.", environment: "CLIENT", params: { - field: "string", + filter: "string", condition: [ - "equals" + "equals", ], value: "string" }, diff --git a/packages/client/src/api/workflow/index.js b/packages/client/src/api/workflow/index.js index 2e3c025adb..4226027df1 100644 --- a/packages/client/src/api/workflow/index.js +++ b/packages/client/src/api/workflow/index.js @@ -8,6 +8,4 @@ export const triggerWorkflow = api => ({ workflow }) => { workflowOrchestrator.strategy = clientStrategy workflowOrchestrator.execute(workflow) - - // hit the API and get the workflow data back } diff --git a/packages/client/src/api/workflow/orchestrator.js b/packages/client/src/api/workflow/orchestrator.js index 7a26c1648c..bd8f111861 100644 --- a/packages/client/src/api/workflow/orchestrator.js +++ b/packages/client/src/api/workflow/orchestrator.js @@ -23,7 +23,10 @@ export default class Orchestrator { async execute(workflowId) { const EXECUTE_WORKFLOW_URL = `/api/${this.instanceId}/workflows/${workflowId}` const workflow = await this.api.get({ url: EXECUTE_WORKFLOW_URL }) - this._strategy.run(workflow.definition) + + if (workflow.live) { + this._strategy.run(workflow.definition) + } } } @@ -80,17 +83,9 @@ export const clientStrategy = ({ api, instanceId }) => ({ if (block.actionId === "FILTER") { const { field, condition, value } = block.args; switch (condition) { - case "=": + case "equals": if (field !== value) return; break; - case "!=": - if (field === value) return; - break; - case "gt": - if (field < value) return; - break; - case "lt": - if (field > value) return; default: return; } diff --git a/packages/server/src/api/controllers/instance.js b/packages/server/src/api/controllers/instance.js index 573860438c..f5e8239168 100644 --- a/packages/server/src/api/controllers/instance.js +++ b/packages/server/src/api/controllers/instance.js @@ -29,6 +29,16 @@ exports.create = async function(ctx) { emit([doc.type], doc._id) }.toString(), }, + by_workflow_trigger: { + map: function(doc) { + if (doc.type === "workflow") { + const trigger = doc.definition.next + if (trigger) { + emit([trigger.event], trigger) + } + } + }.toString() + } }, }) diff --git a/packages/server/src/api/controllers/record.js b/packages/server/src/api/controllers/record.js index c3e1fa940d..f1c04d4a7a 100644 --- a/packages/server/src/api/controllers/record.js +++ b/packages/server/src/api/controllers/record.js @@ -5,7 +5,6 @@ const newid = require("../../db/newid") const ajv = new Ajv() exports.save = async function(ctx) { - console.log("THIS INSTANCE", ctx.params.instanceId) const db = new CouchDB(ctx.params.instanceId) const record = ctx.request.body record.modelId = ctx.params.modelId @@ -45,7 +44,11 @@ exports.save = async function(ctx) { record.type = "record" const response = await db.post(record) record._rev = response.rev - // ctx.eventPublisher.emit("RECORD_CREATED", record) + + ctx.eventEmitter.emit(`record:save`, { + record, + instanceId: ctx.params.instanceId + }) ctx.body = record ctx.status = 200 ctx.message = `${model.name} created successfully` @@ -85,4 +88,5 @@ exports.destroy = async function(ctx) { return } ctx.body = await db.remove(ctx.params.recordId, ctx.params.revId) + ctx.eventEmitter.emit(`record:delete`, record) } diff --git a/packages/server/src/api/controllers/workflow/index.js b/packages/server/src/api/controllers/workflow/index.js index 1a4cfa8591..34438b0892 100644 --- a/packages/server/src/api/controllers/workflow/index.js +++ b/packages/server/src/api/controllers/workflow/index.js @@ -10,24 +10,6 @@ exports.create = async function(ctx) { workflow._id = newid() - // TODO: Possibly validate the workflow against a schema - - // // validation with ajv - // const model = await db.get(record.modelId) - // const validate = ajv.compile({ - // properties: model.schema, - // }) - // const valid = validate(record) - - // if (!valid) { - // ctx.status = 400 - // ctx.body = { - // status: 400, - // errors: validate.errors, - // } - // return - // } - workflow.type = "workflow" const response = await db.post(workflow) workflow._rev = response.rev diff --git a/packages/server/src/app.js b/packages/server/src/app.js index 57fb037626..2fb959933e 100644 --- a/packages/server/src/app.js +++ b/packages/server/src/app.js @@ -4,7 +4,7 @@ const logger = require("koa-pino-logger") const http = require("http") const api = require("./api") const env = require("./environment") -const eventPublisher = require("./events") +const eventEmitter = require("./events") const app = new Koa() @@ -20,7 +20,7 @@ app.use( }) ) -app.context.publisher = eventPublisher +app.context.eventEmitter = eventEmitter // api routes app.use(api.routes()) diff --git a/packages/server/src/events/index.js b/packages/server/src/events/index.js index 7d54c4cd95..3e6d30869c 100644 --- a/packages/server/src/events/index.js +++ b/packages/server/src/events/index.js @@ -1,3 +1,31 @@ const EventEmitter = require("events").EventEmitter +const CouchDB = require("../db"); -module.exports = new EventEmitter() +const emitter = new EventEmitter() + +function determineWorkflowsToTrigger(instanceId, event) { + const db = new CouchDB(instanceId); + const workflowsToTrigger = await db.query("database/by_workflow_trigger", { + key: [event] + }) + + return workflowsToTrigger.rows; +} + +emitter.on("record:save", async function(event) { + const workflowsToTrigger = await determineWorkflowsToTrigger(instanceId, "record:save") + + for (let workflow of workflowsToTrigger) { + // SERVER SIDE STUFF!! + } +}) + +emitter.on("record:delete", function(event) { + const workflowsToTrigger = await determineWorkflowsToTrigger(instanceId, "record:delete") + + for (let workflow of workflowsToTrigger) { + // SERVER SIDE STUFF!! + } +}) + +module.exports = emitter diff --git a/packages/server/src/schemas/index.js b/packages/server/src/schemas/index.js index 63a6514043..93fa005a80 100644 --- a/packages/server/src/schemas/index.js +++ b/packages/server/src/schemas/index.js @@ -17,18 +17,19 @@ const WORKFLOW_SCHEMA = { type: "object", properties: { triggers: { type: "array" }, - next: { - type: "object", - properties: { - environment: { environment: "string" }, - type: { type: "string" }, - actionId: { type: "string" }, - args: { type: "object" }, - conditions: { type: "array" }, - errorHandling: { type: "object" }, - next: { type: "object" }, - }, - }, + steps: { type: "array" } + // next: { + // type: "object", + // properties: { + // environment: { environment: "string" }, + // type: { type: "string" }, + // actionId: { type: "string" }, + // args: { type: "object" }, + // conditions: { type: "array" }, + // errorHandling: { type: "object" }, + // next: { type: "object" }, + // }, + // }, }, }, }, From 15ba1a5a12fdc19b98f53c446f744ca083b756e3 Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Mon, 1 Jun 2020 10:41:28 +0100 Subject: [PATCH 19/27] move workflow to array data structure --- packages/builder/rollup.config.js | 2 +- packages/builder/src/App.svelte | 3 +- .../builderStore/store/workflow/Workflow.js | 133 +++++++++++------- .../src/builderStore/store/workflow/index.js | 3 +- .../AppPreview/CurrentItemPreview.svelte | 2 +- .../EventsEditor/StateBindingCascader.svelte | 6 +- .../SetupPanel/DeleteWorkflowModal.svelte | 2 +- .../ParamInputs/ComponentSelector.svelte | 2 +- .../ParamInputs/ModelSelector.svelte | 1 - .../SetupPanel/WorkflowBlockSetup.svelte | 8 +- .../svelte-flows/FlowChart.svelte | 2 +- .../WorkflowPanel/BlockList/BlockList.svelte | 5 +- .../WorkflowList/CreateWorkflowModal.svelte | 2 +- .../WorkflowList/WorkflowList.svelte | 9 +- .../WorkflowPanel/blockDefinitions.js | 8 +- .../builder/src/components/workflow/index.js | 6 +- packages/client/src/api/workflow/actions.js | 29 ++++ packages/client/src/api/workflow/index.js | 2 +- .../client/src/api/workflow/orchestrator.js | 95 +++++-------- packages/client/src/createApp.js | 4 +- packages/client/src/index.js | 2 +- packages/client/src/render/attachChildren.js | 2 +- .../src/render/prepareRenderComponent.js | 16 ++- packages/client/src/render/screenRouter.js | 6 +- packages/client/src/state/bbComponentApi.js | 2 +- packages/client/src/state/eventHandlers.js | 6 +- packages/client/src/state/getState.js | 10 +- packages/client/src/state/setState.js | 6 +- packages/client/src/state/stateManager.js | 56 ++++---- packages/client/src/state/store.js | 19 +-- packages/client/tests/testAppDef.js | 2 +- .../server/src/api/controllers/instance.js | 4 +- packages/server/src/api/controllers/record.js | 2 +- .../workflow/actions/CREATE_USER.js | 8 +- packages/server/src/events/index.js | 22 +-- packages/server/src/schemas/index.js | 2 +- 36 files changed, 257 insertions(+), 232 deletions(-) create mode 100644 packages/client/src/api/workflow/actions.js diff --git a/packages/builder/rollup.config.js b/packages/builder/rollup.config.js index ae9a7e069e..fd7c487c3f 100644 --- a/packages/builder/rollup.config.js +++ b/packages/builder/rollup.config.js @@ -152,7 +152,7 @@ export default { { find: "builderStore", replacement: path.resolve(projectRootDir, "src/builderStore"), - } + }, ], customResolver, }), diff --git a/packages/builder/src/App.svelte b/packages/builder/src/App.svelte index 54968a52c2..4ead618ba6 100644 --- a/packages/builder/src/App.svelte +++ b/packages/builder/src/App.svelte @@ -7,8 +7,7 @@ import AppNotification, { showAppNotification, } from "components/common/AppNotification.svelte" - import { NotificationDisplay } from '@beyonk/svelte-notifications' - + import { NotificationDisplay } from "@beyonk/svelte-notifications" function showErrorBanner() { showAppNotification({ diff --git a/packages/builder/src/builderStore/store/workflow/Workflow.js b/packages/builder/src/builderStore/store/workflow/Workflow.js index 3e5a0e8b4e..c8ae55dbf3 100644 --- a/packages/builder/src/builderStore/store/workflow/Workflow.js +++ b/packages/builder/src/builderStore/store/workflow/Workflow.js @@ -13,82 +13,109 @@ export default class Workflow { } isEmpty() { - return !this.workflow.definition.next + // return this.workflow.definition.next + return this.workflow.length > 0 } addBlock(block) { - let node = this.workflow.definition - while (node.next) node = node.next - node.next = { + // Make sure to add trigger if doesn't exist + this.workflow.definition.steps.push({ id: generate(), - ...block - } + ...block, + }) } updateBlock(updatedBlock, id) { - let block = this.workflow.definition + const { steps, trigger } = this.workflow.definition - while (block.id !== id) block = block.next - if (!block) throw new Error("Block not found.") + // if the block is a trigger do X - block = updatedBlock + // if step + const stepIdx = steps.findIndex(step => step.id === id) + + // while (block.id !== id) block = block.next + if (stepIdx < 0) throw new Error("Block not found.") + + steps.splice(stepIdx, 1, updatedBlock) } deleteBlock(id) { - let previous = null - let block = this.workflow.definition + const { steps, trigger } = this.workflow.definition - // iterate through the blocks - while (block.id !== id) { - previous = block - block = block.next - } + const stepIdx = steps.findIndex(step => step.id === id) - // delete the block matching your id - if (!block.next) { - delete previous.next - } else { - previous.next = block.next - } + if (stepIdx < 0) throw new Error("Block not found.") + steps.splice(stepIdx, 1) } createUiTree() { - if (!this.workflow.definition.next) return [] - return Workflow.buildUiTree(this.workflow.definition.next) + if (!this.workflow.definition) return [] + return Workflow.buildUiTree(this.workflow.definition) } - static buildUiTree(block, tree = []) { - if (!block) return tree + static buildUiTree(definition) { + return definition.steps.map(step => { + // The client side display definition for the block + const definition = blockDefinitions[step.type][step.actionId] + if (!definition) { + throw new Error( + `No block definition exists for the chosen block. Check there's an entry in the block definitions for ${step.actionId}` + ) + } - // The client side display definition for the block - const definition = blockDefinitions[block.type][block.actionId] - if (!definition) { - throw new Error( - `No block definition exists for the chosen block. Check there's an entry in the block definitions for ${block.actionId}` - ) - } + if (!definition.params) { + throw new Error( + `Blocks should always have parameters. Ensure that the block definition is correct for ${step.actionId}` + ) + } - if (!definition.params) { - throw new Error( - `Blocks should always have parameters. Ensure that the block definition is correct for ${block.actionId}` - ) - } + const tagline = definition.tagline || "" + const args = step.args || {} - const tagline = definition.tagline || "" - const args = block.args || {} - - // all the fields the workflow block needs to render in the UI - tree.push({ - id: block.id, - type: block.type, - params: block.params, - args, - heading: block.actionId, - body: mustache.render(tagline, args), - name: definition.name + return { + id: step.id, + type: step.type, + params: step.params, + args, + heading: step.actionId, + body: mustache.render(tagline, args), + name: definition.name, + } }) - - return this.buildUiTree(block.next, tree) } + + // static buildUiTree(block, tree = []) { + // if (!block) return tree + + // // The client side display definition for the block + // const definition = blockDefinitions[block.type][block.actionId] + // if (!definition) { + // throw new Error( + // `No block definition exists for the chosen block. Check there's an entry in the block definitions for ${block.actionId}` + // ) + // } + + // if (!definition.params) { + // throw new Error( + // `Blocks should always have parameters. Ensure that the block definition is correct for ${block.actionId}` + // ) + // } + + // const tagline = definition.tagline || "" + // const args = block.args || {} + + // // all the fields the workflow block needs to render in the UI + // tree.push({ + // id: block.id, + // type: block.type, + // params: block.params, + // args, + // heading: block.actionId, + // body: mustache.render(tagline, args), + // name: definition.name + // }) + + // return this.buildUiTree(block.next, tree) + // } } diff --git a/packages/builder/src/builderStore/store/workflow/index.js b/packages/builder/src/builderStore/store/workflow/index.js index 971312c2ef..8b534aab5c 100644 --- a/packages/builder/src/builderStore/store/workflow/index.js +++ b/packages/builder/src/builderStore/store/workflow/index.js @@ -13,7 +13,8 @@ const workflowActions = store => ({ }) }, create: async ({ instanceId, name }) => { - const workflow = { name, definition: {} } + // TODO: set these defaults in the backend + const workflow = { name, definition: { trigger: {}, steps: [] } } const CREATE_WORKFLOW_URL = `/api/${instanceId}/workflows` const response = await api.post(CREATE_WORKFLOW_URL, workflow) const json = await response.json() diff --git a/packages/builder/src/components/userInterface/AppPreview/CurrentItemPreview.svelte b/packages/builder/src/components/userInterface/AppPreview/CurrentItemPreview.svelte index ad52602afc..a7288c8c91 100644 --- a/packages/builder/src/components/userInterface/AppPreview/CurrentItemPreview.svelte +++ b/packages/builder/src/components/userInterface/AppPreview/CurrentItemPreview.svelte @@ -116,7 +116,7 @@ stylesheetLinks, selectedComponentType, selectedComponentId, - frontendDefinition: JSON.stringify(frontendDefinition) + frontendDefinition: JSON.stringify(frontendDefinition), })} /> {/if}
diff --git a/packages/builder/src/components/userInterface/EventsEditor/StateBindingCascader.svelte b/packages/builder/src/components/userInterface/EventsEditor/StateBindingCascader.svelte index 79cd776c61..37681aa399 100644 --- a/packages/builder/src/components/userInterface/EventsEditor/StateBindingCascader.svelte +++ b/packages/builder/src/components/userInterface/EventsEditor/StateBindingCascader.svelte @@ -17,14 +17,16 @@ export let onChange let isOpen = false -
{parameter.name}
{#if parameter.name === 'workflow'} - {#each $workflowStore.workflows as workflow} {/each} diff --git a/packages/builder/src/components/workflow/SetupPanel/DeleteWorkflowModal.svelte b/packages/builder/src/components/workflow/SetupPanel/DeleteWorkflowModal.svelte index a3eef9d9d2..9f51362419 100644 --- a/packages/builder/src/components/workflow/SetupPanel/DeleteWorkflowModal.svelte +++ b/packages/builder/src/components/workflow/SetupPanel/DeleteWorkflowModal.svelte @@ -1,6 +1,6 @@ - + {#each workflowParams as [parameter, type]}
diff --git a/packages/builder/src/components/workflow/WorkflowBuilder/svelte-flows/FlowChart.svelte b/packages/builder/src/components/workflow/WorkflowBuilder/svelte-flows/FlowChart.svelte index 40c54246e9..a1f488fecb 100644 --- a/packages/builder/src/components/workflow/WorkflowBuilder/svelte-flows/FlowChart.svelte +++ b/packages/builder/src/components/workflow/WorkflowBuilder/svelte-flows/FlowChart.svelte @@ -1,6 +1,6 @@ diff --git a/packages/builder/src/components/workflow/WorkflowPanel/blockDefinitions.js b/packages/builder/src/components/workflow/WorkflowPanel/blockDefinitions.js index 17203646a8..4e36353532 100644 --- a/packages/builder/src/components/workflow/WorkflowPanel/blockDefinitions.js +++ b/packages/builder/src/components/workflow/WorkflowPanel/blockDefinitions.js @@ -94,7 +94,7 @@ const TRIGGER = { description: "Fired when a record is deleted from your database.", environment: "SERVER", params: { - model: "model" + model: "model", }, }, // CLICK: { @@ -138,10 +138,8 @@ const LOGIC = { environment: "CLIENT", params: { filter: "string", - condition: [ - "equals", - ], - value: "string" + condition: ["equals"], + value: "string", }, }, DELAY: { diff --git a/packages/builder/src/components/workflow/index.js b/packages/builder/src/components/workflow/index.js index 2b3d68b5b7..a3f5c4a2bc 100644 --- a/packages/builder/src/components/workflow/index.js +++ b/packages/builder/src/components/workflow/index.js @@ -1,3 +1,3 @@ -export { default as WorkflowBuilder } from "./WorkflowBuilder/WorkflowBuilder.svelte"; -export { default as SetupPanel } from "./SetupPanel/SetupPanel.svelte"; -export { default as WorkflowPanel } from "./WorkflowPanel/WorkflowPanel.svelte"; +export { default as WorkflowBuilder } from "./WorkflowBuilder/WorkflowBuilder.svelte" +export { default as SetupPanel } from "./SetupPanel/SetupPanel.svelte" +export { default as WorkflowPanel } from "./WorkflowPanel/WorkflowPanel.svelte" diff --git a/packages/client/src/api/workflow/actions.js b/packages/client/src/api/workflow/actions.js new file mode 100644 index 0000000000..44d863cd3f --- /dev/null +++ b/packages/client/src/api/workflow/actions.js @@ -0,0 +1,29 @@ +import { get } from "svelte/store" +import { setState } from "../../state/setState" +import { appStore } from "../../state/store" + +const delay = ms => new Promise(resolve => setTimeout(resolve, ms)) + +export default { + SET_STATE: ({ context, args, id }) => { + // get props from the workflow context if required + setState(...Object.values(args)) + // update the context with the data + context = { + ...context, + [id]: args, + } + }, + NAVIGATE: ({ context, args, id }) => {}, + DELAY: async ({ context, args }) => await delay(args.time), + FILTER: (context, args) => { + const { field, condition, value } = args + switch (condition) { + case "equals": + if (field !== value) return + break + default: + return + } + }, +} diff --git a/packages/client/src/api/workflow/index.js b/packages/client/src/api/workflow/index.js index 4226027df1..795dafca20 100644 --- a/packages/client/src/api/workflow/index.js +++ b/packages/client/src/api/workflow/index.js @@ -3,7 +3,7 @@ import Orchestrator, { clientStrategy } from "./orchestrator" export const triggerWorkflow = api => ({ workflow }) => { const workflowOrchestrator = new Orchestrator( api, - "inst_60dd510_700f7dc06735403e81d5af91072d7241" + "inst_ad75c7f_4f3e7d5d80a74b17a5187a18e2aba85e" ) workflowOrchestrator.strategy = clientStrategy diff --git a/packages/client/src/api/workflow/orchestrator.js b/packages/client/src/api/workflow/orchestrator.js index bd8f111861..1473b183e7 100644 --- a/packages/client/src/api/workflow/orchestrator.js +++ b/packages/client/src/api/workflow/orchestrator.js @@ -1,7 +1,7 @@ -import { get } from "svelte/store"; -import { setState } from "../../state/setState"; -import mustache from "mustache"; -import { appStore } from "../../state/store"; +import { get } from "svelte/store" +import mustache from "mustache" +import { appStore } from "../../state/store" +import clientActions from "./actions" /** * The workflow orchestrator is a class responsible for executing workflows. @@ -17,7 +17,7 @@ export default class Orchestrator { } set strategy(strategy) { - this._strategy = strategy({ api: this.api, instanceId: this.instanceId }); + this._strategy = strategy({ api: this.api, instanceId: this.instanceId }) } async execute(workflowId) { @@ -32,85 +32,58 @@ export default class Orchestrator { // Execute a workflow from a running budibase app export const clientStrategy = ({ api, instanceId }) => ({ - delay: ms => new Promise(resolve => setTimeout(resolve, ms)), context: {}, bindContextArgs: function(args) { const mappedArgs = { ...args } - console.log("original args", args) - // bind the workflow action args to the workflow context, if required for (let arg in args) { const argValue = args[arg] + + // We don't want to render mustache templates on non-strings + if (typeof argValue !== "string") continue + // Means that it's bound to state or workflow context - console.log(argValue, get(appStore)); mappedArgs[arg] = mustache.render(argValue, { context: this.context, - state: get(appStore) - }); + state: get(appStore), + }) } - console.log(mappedArgs) - return mappedArgs }, run: async function(workflow) { - const block = workflow.next + for (let block of workflow.steps) { + console.log("Executing workflow block", block) - console.log("Executing workflow block", block) + // This code gets run in the browser + if (block.environment === "CLIENT") { + const action = clientActions[block.actionId] + await action({ + context: this.context, + args: this.bindContextArgs(block.args), + id: block.id, + }) + } - if (!block) return + // this workflow block gets executed on the server + if (block.environment === "SERVER") { + const EXECUTE_WORKFLOW_URL = `/api/${instanceId}/workflows/action` + const response = await api.post({ + url: EXECUTE_WORKFLOW_URL, + body: { + action: block.actionId, + args: this.bindContextArgs(block.args, api), + }, + }) - // This code gets run in the browser - if (block.environment === "CLIENT") { - if (block.actionId === "SET_STATE") { - // get props from the workflow context if required - setState(...Object.values(this.bindContextArgs(block.args))) - // update the context with the data this.context = { ...this.context, - SET_STATE: block.args, + [block.actionId]: response, } } - if (block.actionId === "NAVIGATE") { - } - - if (block.actionId === "DELAY") { - await this.delay(block.args.time) - } - - if (block.actionId === "FILTER") { - const { field, condition, value } = block.args; - switch (condition) { - case "equals": - if (field !== value) return; - break; - default: - return; - } - } + console.log("workflowContext", this.context) } - - // this workflow block gets executed on the server - if (block.environment === "SERVER") { - const EXECUTE_WORKFLOW_URL = `/api/${instanceId}/workflows/action` - const response = await api.post({ - url: EXECUTE_WORKFLOW_URL, - body: { - action: block.actionId, - args: this.bindContextArgs(block.args, api), - }, - }) - - this.context = { - ...this.context, - [block.actionId]: response, - } - } - - console.log("workflowContext", this.context) - - await this.run(workflow.next) }, }) diff --git a/packages/client/src/createApp.js b/packages/client/src/createApp.js index 9da7c80905..4655c0ebe7 100644 --- a/packages/client/src/createApp.js +++ b/packages/client/src/createApp.js @@ -8,7 +8,7 @@ export const createApp = ({ componentLibraries, frontendDefinition, user, - window + window, }) => { let routeTo let currentUrl @@ -38,7 +38,7 @@ export const createApp = ({ routeTo = screenRouter({ screens: frontendDefinition.screens, onScreenSelected, - appRootPath: frontendDefinition.appRootPath + appRootPath: frontendDefinition.appRootPath, }) const fallbackPath = window.location.pathname.replace( frontendDefinition.appRootPath, diff --git a/packages/client/src/index.js b/packages/client/src/index.js index 7de1d4b478..3e86f3ddec 100644 --- a/packages/client/src/index.js +++ b/packages/client/src/index.js @@ -39,7 +39,7 @@ export const loadBudibase = async opts => { componentLibraries: componentLibraryModules, frontendDefinition, user, - window + window, }) const route = _window.location diff --git a/packages/client/src/render/attachChildren.js b/packages/client/src/render/attachChildren.js index 89ee0cfe4a..db3c3a212c 100644 --- a/packages/client/src/render/attachChildren.js +++ b/packages/client/src/render/attachChildren.js @@ -42,7 +42,7 @@ export const attachChildren = initialiseOpts => (htmlElement, options) => { parentNode: treeNode, ComponentConstructor, htmlElement, - anchor + anchor, }) for (let childNode of childNodesThisIteration) { diff --git a/packages/client/src/render/prepareRenderComponent.js b/packages/client/src/render/prepareRenderComponent.js index b92f946f90..30af819221 100644 --- a/packages/client/src/render/prepareRenderComponent.js +++ b/packages/client/src/render/prepareRenderComponent.js @@ -1,12 +1,12 @@ import { appStore } from "../state/store" -import mustache from "mustache"; +import mustache from "mustache" export const prepareRenderComponent = ({ ComponentConstructor, htmlElement, anchor, props, - parentNode + parentNode, }) => { const parentContext = (parentNode && parentNode.context) || {} @@ -42,14 +42,16 @@ export const prepareRenderComponent = ({ // make this node listen to the store if (thisNode.stateBound) { const unsubscribe = appStore.subscribe(state => { - const storeBoundProps = { ...initialProps._bb.props }; + const storeBoundProps = { ...initialProps._bb.props } for (let prop in storeBoundProps) { if (typeof storeBoundProps[prop] === "string") { - storeBoundProps[prop] = mustache.render(storeBoundProps[prop], { state }); + storeBoundProps[prop] = mustache.render(storeBoundProps[prop], { + state, + }) } - } - thisNode.component.$set(storeBoundProps); - }); + } + thisNode.component.$set(storeBoundProps) + }) thisNode.unsubscribe = unsubscribe } } diff --git a/packages/client/src/render/screenRouter.js b/packages/client/src/render/screenRouter.js index afcb3049cf..1325bbd20a 100644 --- a/packages/client/src/render/screenRouter.js +++ b/packages/client/src/render/screenRouter.js @@ -1,5 +1,5 @@ import regexparam from "regexparam" -import { routerStore } from "../state/store"; +import { routerStore } from "../state/store" import { initRouteStore } from "../state/store" // TODO: refactor @@ -43,8 +43,8 @@ export const screenRouter = ({ screens, onScreenSelected, appRootPath }) => { } routerStore.update(state => { - state["##routeParams"] = params; - return state; + state["##routeParams"] = params + return state }) const screenIndex = current !== -1 ? current : fallback diff --git a/packages/client/src/state/bbComponentApi.js b/packages/client/src/state/bbComponentApi.js index 71235756b3..34746058f7 100644 --- a/packages/client/src/state/bbComponentApi.js +++ b/packages/client/src/state/bbComponentApi.js @@ -51,7 +51,7 @@ export const bbFactory = ({ componentLibraries, treeNode, onScreenSlotRendered, - setupState + setupState, } return { diff --git a/packages/client/src/state/eventHandlers.js b/packages/client/src/state/eventHandlers.js index 926eff89ca..f1de2592f8 100644 --- a/packages/client/src/state/eventHandlers.js +++ b/packages/client/src/state/eventHandlers.js @@ -1,7 +1,7 @@ import { setState } from "./setState" import { getState } from "./getState" import { isArray, isUndefined } from "lodash/fp" -import { appStore } from "./store"; +import { appStore } from "./store" import { createApi } from "../api" @@ -21,7 +21,7 @@ export const eventHandlers = (store, rootPath, routeTo) => { const api = createApi({ rootPath, setState, - getState: (path, fallback) => getState(path, fallback) + getState: (path, fallback) => getState(path, fallback), }) const setStateHandler = ({ path, value }) => setState(path, value) @@ -29,7 +29,7 @@ export const eventHandlers = (store, rootPath, routeTo) => { return { "Set State": handler(["path", "value"], setStateHandler), "Navigate To": handler(["url"], param => routeTo(param && param.url)), - "Trigger Workflow": handler(["workflow"], api.triggerWorkflow) + "Trigger Workflow": handler(["workflow"], api.triggerWorkflow), } } diff --git a/packages/client/src/state/getState.js b/packages/client/src/state/getState.js index 980bb82b6e..4afe27236a 100644 --- a/packages/client/src/state/getState.js +++ b/packages/client/src/state/getState.js @@ -1,10 +1,10 @@ // import { isUndefined, isObject } from "lodash/fp" -import { get } from "svelte/store"; -import getOr from "lodash/fp/getOr"; -import { appStore } from "./store"; +import { get } from "svelte/store" +import getOr from "lodash/fp/getOr" +import { appStore } from "./store" export const getState = (path, fallback) => { if (!path || path.length === 0) return fallback - return getOr(fallback, path, get(appStore)); -} \ No newline at end of file + return getOr(fallback, path, get(appStore)) +} diff --git a/packages/client/src/state/setState.js b/packages/client/src/state/setState.js index d50e6e6e62..3d3b2045c8 100644 --- a/packages/client/src/state/setState.js +++ b/packages/client/src/state/setState.js @@ -1,11 +1,11 @@ -import set from "lodash/fp/set"; -import { appStore } from "./store"; +import set from "lodash/fp/set" +import { appStore } from "./store" export const setState = (path, value) => { if (!path || path.length === 0) return appStore.update(state => { - state = set(path, value, state); + state = set(path, value, state) return state }) } diff --git a/packages/client/src/state/stateManager.js b/packages/client/src/state/stateManager.js index c299c51b07..b4597be5dd 100644 --- a/packages/client/src/state/stateManager.js +++ b/packages/client/src/state/stateManager.js @@ -5,8 +5,8 @@ import { } from "./eventHandlers" import { bbFactory } from "./bbComponentApi" import mustache from "mustache" -import { get } from "svelte/store"; -import { appStore } from "./store"; +import { get } from "svelte/store" +import { appStore } from "./store" const doNothing = () => {} doNothing.isPlaceholder = true @@ -55,9 +55,9 @@ export const createStateManager = ({ // TODO: remove const unsubscribe = appStore.subscribe(state => { - console.log("store updated", state); - return state; - }); + console.log("store updated", state) + return state + }) // const unsubscribe = store.subscribe( // onStoreStateUpdated({ @@ -84,20 +84,18 @@ const onStoreStateUpdated = ({ getCurrentState, componentLibraries, onScreenSlotRendered, - setupState + setupState, }) => state => { - // fire the state update event to re-render anything bound to this - // setCurrentState(state) - - // setCurrentState(state) - // attachChildren({ - // componentLibraries, - // treeNode: createTreeNode(), - // onScreenSlotRendered, - // setupState, - // getCurrentState, - // })(document.querySelector("#app"), { hydrate: true, force: true }) - + // fire the state update event to re-render anything bound to this + // setCurrentState(state) + // setCurrentState(state) + // attachChildren({ + // componentLibraries, + // treeNode: createTreeNode(), + // onScreenSlotRendered, + // setupState, + // getCurrentState, + // })(document.querySelector("#app"), { hydrate: true, force: true }) // // the original array gets changed by components' destroy() // // so we make a clone and check if they are still in the original // const nodesWithBoundChildren_clone = [...nodesWithCodeBoundChildren] @@ -161,19 +159,14 @@ const onStoreStateUpdated = ({ // node.component.$set(newProps) // } -const _setup = ({ - handlerTypes, - getCurrentState, - bb, - store -}) => node => { +const _setup = ({ handlerTypes, getCurrentState, bb, store }) => node => { const props = node.props const context = node.context || {} const initialProps = { ...props } // const storeBoundProps = [] const currentStoreState = get(appStore) - console.log("node", node); + console.log("node", node) // console.log("node", node); // console.log("nodeComponent", node.component); @@ -185,12 +178,12 @@ const _setup = ({ // const binding = parseBinding(propValue) // TODO: better binding stuff - const isBound = typeof propValue === "string" && propValue.startsWith("{{"); + const isBound = typeof propValue === "string" && propValue.startsWith("{{") if (isBound) { initialProps[propName] = mustache.render(propValue, { state: currentStoreState, - context + context, }) if (!node.stateBound) { @@ -230,10 +223,11 @@ const _setup = ({ const resolvedParams = {} for (let paramName in handlerInfo.parameters) { const paramValue = handlerInfo.parameters[paramName] - resolvedParams[paramName] = () => mustache.render(paramValue, { - state: getCurrentState(), - context, - }) + resolvedParams[paramName] = () => + mustache.render(paramValue, { + state: getCurrentState(), + context, + }) // const paramBinding = parseBinding(paramValue) // if (!paramBinding) { // resolvedParams[paramName] = () => paramValue diff --git a/packages/client/src/state/store.js b/packages/client/src/state/store.js index e7e6351184..bdf85c1ae6 100644 --- a/packages/client/src/state/store.js +++ b/packages/client/src/state/store.js @@ -1,16 +1,9 @@ -import { writable } from "svelte/store"; +import { writable } from "svelte/store" -const appStore = writable({}); -appStore.actions = { +const appStore = writable({}) +appStore.actions = {} -}; +const routerStore = writable({}) +routerStore.actions = {} -const routerStore = writable({}); -routerStore.actions = { - -} - -export { - appStore, - routerStore -} +export { appStore, routerStore } diff --git a/packages/client/tests/testAppDef.js b/packages/client/tests/testAppDef.js index 03a5df9d11..c02c18a3b2 100644 --- a/packages/client/tests/testAppDef.js +++ b/packages/client/tests/testAppDef.js @@ -182,4 +182,4 @@ const maketestlib = window => ({ set(opts.props) opts.target.appendChild(node) }, -}) \ No newline at end of file +}) diff --git a/packages/server/src/api/controllers/instance.js b/packages/server/src/api/controllers/instance.js index f5e8239168..4e6db897d5 100644 --- a/packages/server/src/api/controllers/instance.js +++ b/packages/server/src/api/controllers/instance.js @@ -37,8 +37,8 @@ exports.create = async function(ctx) { emit([trigger.event], trigger) } } - }.toString() - } + }.toString(), + }, }, }) diff --git a/packages/server/src/api/controllers/record.js b/packages/server/src/api/controllers/record.js index f1c04d4a7a..7e43a9c882 100644 --- a/packages/server/src/api/controllers/record.js +++ b/packages/server/src/api/controllers/record.js @@ -47,7 +47,7 @@ exports.save = async function(ctx) { ctx.eventEmitter.emit(`record:save`, { record, - instanceId: ctx.params.instanceId + instanceId: ctx.params.instanceId, }) ctx.body = record ctx.status = 200 diff --git a/packages/server/src/api/controllers/workflow/actions/CREATE_USER.js b/packages/server/src/api/controllers/workflow/actions/CREATE_USER.js index 96b0fa2584..f53cf2c716 100644 --- a/packages/server/src/api/controllers/workflow/actions/CREATE_USER.js +++ b/packages/server/src/api/controllers/workflow/actions/CREATE_USER.js @@ -8,19 +8,19 @@ module.exports = async function createUser(user) { instanceId: "inst_60dd510_700f7dc06735403e81d5af91072d7241", }, request: { - body: user + body: user, }, } try { const response = await userController.create(ctx) return { - user: response + user: response, } } catch (err) { - console.error(err); + console.error(err) return { - user: null + user: null, } } } diff --git a/packages/server/src/events/index.js b/packages/server/src/events/index.js index 3e6d30869c..76dc231f15 100644 --- a/packages/server/src/events/index.js +++ b/packages/server/src/events/index.js @@ -1,27 +1,33 @@ const EventEmitter = require("events").EventEmitter -const CouchDB = require("../db"); +const CouchDB = require("../db") const emitter = new EventEmitter() -function determineWorkflowsToTrigger(instanceId, event) { - const db = new CouchDB(instanceId); +async function determineWorkflowsToTrigger(instanceId, event) { + const db = new CouchDB(instanceId) const workflowsToTrigger = await db.query("database/by_workflow_trigger", { - key: [event] + key: [event], }) - return workflowsToTrigger.rows; + return workflowsToTrigger.rows } emitter.on("record:save", async function(event) { - const workflowsToTrigger = await determineWorkflowsToTrigger(instanceId, "record:save") + const workflowsToTrigger = await determineWorkflowsToTrigger( + instanceId, + "record:save" + ) for (let workflow of workflowsToTrigger) { // SERVER SIDE STUFF!! } }) -emitter.on("record:delete", function(event) { - const workflowsToTrigger = await determineWorkflowsToTrigger(instanceId, "record:delete") +emitter.on("record:delete", async function(event) { + const workflowsToTrigger = await determineWorkflowsToTrigger( + instanceId, + "record:delete" + ) for (let workflow of workflowsToTrigger) { // SERVER SIDE STUFF!! diff --git a/packages/server/src/schemas/index.js b/packages/server/src/schemas/index.js index 93fa005a80..edc94dba52 100644 --- a/packages/server/src/schemas/index.js +++ b/packages/server/src/schemas/index.js @@ -17,7 +17,7 @@ const WORKFLOW_SCHEMA = { type: "object", properties: { triggers: { type: "array" }, - steps: { type: "array" } + steps: { type: "array" }, // next: { // type: "object", // properties: { From cb496d18193baaf908e60018e3289e0875707d82 Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Mon, 1 Jun 2020 11:52:15 +0100 Subject: [PATCH 20/27] workflow DAO tests --- packages/builder/src/App.svelte | 4 ++ .../builderStore/store/workflow/Workflow.js | 52 ++++----------- .../src/builderStore/store/workflow/index.js | 1 - .../store/workflow/tests/Workflow.js | 1 - .../store/workflow/tests/Workflow.spec.js | 57 +++++++++++++++++ .../tests/__snapshots__/Workflow.spec.js.snap | 49 ++++++++++++++ .../store/workflow/tests/testWorkflow.js | 64 +++++++++++++++++++ .../WorkflowBuilder/WorkflowBuilder.svelte | 3 +- .../{svelte-flows => flowchart}/Arrow.svelte | 0 .../FlowChart.svelte | 0 .../FlowItem.svelte | 0 .../{svelte-flows => flowchart}/api.js | 0 .../{svelte-flows => flowchart}/index.js | 0 .../workflow/[workflow]/_layout.svelte | 0 packages/client/src/api/workflow/actions.js | 8 +-- .../client/src/api/workflow/orchestrator.js | 4 +- packages/client/src/createApp.js | 1 - packages/client/src/index.js | 1 - packages/client/src/state/bbComponentApi.js | 1 - packages/client/src/state/eventHandlers.js | 1 - packages/client/src/state/getState.js | 1 - .../workflow/actions/SAVE_RECORD.js | 2 - packages/server/src/events/index.js | 4 +- 23 files changed, 193 insertions(+), 61 deletions(-) delete mode 100644 packages/builder/src/builderStore/store/workflow/tests/Workflow.js create mode 100644 packages/builder/src/builderStore/store/workflow/tests/Workflow.spec.js create mode 100644 packages/builder/src/builderStore/store/workflow/tests/__snapshots__/Workflow.spec.js.snap create mode 100644 packages/builder/src/builderStore/store/workflow/tests/testWorkflow.js rename packages/builder/src/components/workflow/WorkflowBuilder/{svelte-flows => flowchart}/Arrow.svelte (100%) rename packages/builder/src/components/workflow/WorkflowBuilder/{svelte-flows => flowchart}/FlowChart.svelte (100%) rename packages/builder/src/components/workflow/WorkflowBuilder/{svelte-flows => flowchart}/FlowItem.svelte (100%) rename packages/builder/src/components/workflow/WorkflowBuilder/{svelte-flows => flowchart}/api.js (100%) rename packages/builder/src/components/workflow/WorkflowBuilder/{svelte-flows => flowchart}/index.js (100%) create mode 100644 packages/builder/src/pages/[application]/workflow/[workflow]/_layout.svelte diff --git a/packages/builder/src/App.svelte b/packages/builder/src/App.svelte index 4ead618ba6..72d287e9cb 100644 --- a/packages/builder/src/App.svelte +++ b/packages/builder/src/App.svelte @@ -25,7 +25,11 @@ $basepath = "/_builder" + + + + diff --git a/packages/builder/src/builderStore/store/workflow/Workflow.js b/packages/builder/src/builderStore/store/workflow/Workflow.js index c8ae55dbf3..d50e16b5da 100644 --- a/packages/builder/src/builderStore/store/workflow/Workflow.js +++ b/packages/builder/src/builderStore/store/workflow/Workflow.js @@ -1,5 +1,4 @@ import mustache from "mustache" -// TODO: tidy up import import blockDefinitions from "components/workflow/WorkflowPanel/blockDefinitions" import { generate } from "shortid" @@ -13,12 +12,19 @@ export default class Workflow { } isEmpty() { - // return this.workflow.definition.next - return this.workflow.length > 0 + return this.workflow.definition.trigger.length === 0 } addBlock(block) { // Make sure to add trigger if doesn't exist + // if (this.isEmpty()) { + // this.workflow.definition.triggers.push({ + // id: generate(), + // ...block, + // }) + // return; + // } + this.workflow.definition.steps.push({ id: generate(), ...block, @@ -27,13 +33,10 @@ export default class Workflow { updateBlock(updatedBlock, id) { const { steps, trigger } = this.workflow.definition + // TODO: Account for trigger - // if the block is a trigger do X - - // if step const stepIdx = steps.findIndex(step => step.id === id) - // while (block.id !== id) block = block.next if (stepIdx < 0) throw new Error("Block not found.") steps.splice(stepIdx, 1, updatedBlock) @@ -41,6 +44,7 @@ export default class Workflow { deleteBlock(id) { const { steps, trigger } = this.workflow.definition + // TODO: Account for trigger const stepIdx = steps.findIndex(step => step.id === id) @@ -84,38 +88,4 @@ export default class Workflow { } }) } - - // static buildUiTree(block, tree = []) { - // if (!block) return tree - - // // The client side display definition for the block - // const definition = blockDefinitions[block.type][block.actionId] - // if (!definition) { - // throw new Error( - // `No block definition exists for the chosen block. Check there's an entry in the block definitions for ${block.actionId}` - // ) - // } - - // if (!definition.params) { - // throw new Error( - // `Blocks should always have parameters. Ensure that the block definition is correct for ${block.actionId}` - // ) - // } - - // const tagline = definition.tagline || "" - // const args = block.args || {} - - // // all the fields the workflow block needs to render in the UI - // tree.push({ - // id: block.id, - // type: block.type, - // params: block.params, - // args, - // heading: block.actionId, - // body: mustache.render(tagline, args), - // name: definition.name - // }) - - // return this.buildUiTree(block.next, tree) - // } } diff --git a/packages/builder/src/builderStore/store/workflow/index.js b/packages/builder/src/builderStore/store/workflow/index.js index 8b534aab5c..b41bb8d8f7 100644 --- a/packages/builder/src/builderStore/store/workflow/index.js +++ b/packages/builder/src/builderStore/store/workflow/index.js @@ -68,7 +68,6 @@ const workflowActions = store => ({ }, select: workflow => { store.update(state => { - // TODO: better naming state.currentWorkflow = new Workflow(workflow) state.selectedWorkflowBlock = null return state diff --git a/packages/builder/src/builderStore/store/workflow/tests/Workflow.js b/packages/builder/src/builderStore/store/workflow/tests/Workflow.js deleted file mode 100644 index a892d688b3..0000000000 --- a/packages/builder/src/builderStore/store/workflow/tests/Workflow.js +++ /dev/null @@ -1 +0,0 @@ -describe("Workflow Data Object", () => {}) diff --git a/packages/builder/src/builderStore/store/workflow/tests/Workflow.spec.js b/packages/builder/src/builderStore/store/workflow/tests/Workflow.spec.js new file mode 100644 index 0000000000..fd14404a5f --- /dev/null +++ b/packages/builder/src/builderStore/store/workflow/tests/Workflow.spec.js @@ -0,0 +1,57 @@ +import Workflow from "../Workflow"; +import TEST_WORKFLOW from "./testWorkflow"; + +const TEST_BLOCK = { + id: "VFWeZcIPx", + name: "Update UI State", + tagline: "Update {{path}} to {{value}}", + icon: "ri-refresh-line", + description: "Update your User Interface with some data.", + environment: "CLIENT", + params: { + path: "string", + value: "longText", + }, + args: { + path: "foo", + value: "started...", + }, + actionId: "SET_STATE", + type: "ACTION", +} + +describe("Workflow Data Object", () => { + let workflow + + beforeEach(() => { + workflow = new Workflow({ ...TEST_WORKFLOW }); + }); + + it("adds a workflow block to the workflow", () => { + workflow.addBlock(TEST_BLOCK); + expect(workflow.workflow.definition) + }) + + it("updates a workflow block with new attributes", () => { + const firstBlock = workflow.workflow.definition.steps[0]; + const updatedBlock = { + ...firstBlock, + name: "UPDATED" + }; + workflow.updateBlock(updatedBlock, firstBlock.id); + expect(workflow.workflow.definition.steps[0]).toEqual(updatedBlock) + }) + + it("deletes a workflow block successfully", () => { + const { steps } = workflow.workflow.definition + const originalLength = steps.length + + const lastBlock = steps[steps.length - 1]; + workflow.deleteBlock(lastBlock.id); + expect(workflow.workflow.definition.steps.length).toBeLessThan(originalLength); + }) + + it("builds a tree that gets rendered in the flowchart builder", () => { + expect(Workflow.buildUiTree(TEST_WORKFLOW.definition)).toMatchSnapshot(); + }) +}) diff --git a/packages/builder/src/builderStore/store/workflow/tests/__snapshots__/Workflow.spec.js.snap b/packages/builder/src/builderStore/store/workflow/tests/__snapshots__/Workflow.spec.js.snap new file mode 100644 index 0000000000..732764a082 --- /dev/null +++ b/packages/builder/src/builderStore/store/workflow/tests/__snapshots__/Workflow.spec.js.snap @@ -0,0 +1,49 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Workflow Data Object builds a tree that gets rendered in the flowchart builder 1`] = ` +Array [ + Object { + "args": Object { + "time": 3000, + }, + "body": "Delay for 3000 milliseconds", + "heading": "DELAY", + "id": "zJQcZUgDS", + "name": "Delay", + "params": Object { + "time": "number", + }, + "type": "LOGIC", + }, + Object { + "args": Object { + "path": "foo", + "value": "finished", + }, + "body": "Update foo to finished", + "heading": "SET_STATE", + "id": "3RSTO7BMB", + "name": "Update UI State", + "params": Object { + "path": "string", + "value": "longText", + }, + "type": "ACTION", + }, + Object { + "args": Object { + "path": "foo", + "value": "started...", + }, + "body": "Update foo to started...", + "heading": "SET_STATE", + "id": "VFWeZcIPx", + "name": "Update UI State", + "params": Object { + "path": "string", + "value": "longText", + }, + "type": "ACTION", + }, +] +`; diff --git a/packages/builder/src/builderStore/store/workflow/tests/testWorkflow.js b/packages/builder/src/builderStore/store/workflow/tests/testWorkflow.js new file mode 100644 index 0000000000..2ef6018bf0 --- /dev/null +++ b/packages/builder/src/builderStore/store/workflow/tests/testWorkflow.js @@ -0,0 +1,64 @@ +export default { + _id: "53b6148c65d1429c987e046852d11611", + _rev: "4-02c6659734934895812fa7be0215ee59", + name: "Test Workflow", + definition: { + trigger: {}, + steps: [ + { + id: "VFWeZcIPx", + name: "Update UI State", + tagline: "Update {{path}} to {{value}}", + icon: "ri-refresh-line", + description: "Update your User Interface with some data.", + environment: "CLIENT", + params: { + path: "string", + value: "longText", + }, + args: { + path: "foo", + value: "started...", + }, + actionId: "SET_STATE", + type: "ACTION", + }, + { + id: "zJQcZUgDS", + name: "Delay", + icon: "ri-time-fill", + tagline: "Delay for {{time}} milliseconds", + description: "Delay the workflow until an amount of time has passed.", + environment: "CLIENT", + params: { + time: "number", + }, + args: { + time: 3000, + }, + actionId: "DELAY", + type: "LOGIC", + }, + { + id: "3RSTO7BMB", + name: "Update UI State", + tagline: "Update {{path}} to {{value}}", + icon: "ri-refresh-line", + description: "Update your User Interface with some data.", + environment: "CLIENT", + params: { + path: "string", + value: "longText", + }, + args: { + path: "foo", + value: "finished", + }, + actionId: "SET_STATE", + type: "ACTION", + }, + ], + }, + type: "workflow", + live: true, +} diff --git a/packages/builder/src/components/workflow/WorkflowBuilder/WorkflowBuilder.svelte b/packages/builder/src/components/workflow/WorkflowBuilder/WorkflowBuilder.svelte index af3b108b7b..6399ab727a 100644 --- a/packages/builder/src/components/workflow/WorkflowBuilder/WorkflowBuilder.svelte +++ b/packages/builder/src/components/workflow/WorkflowBuilder/WorkflowBuilder.svelte @@ -2,14 +2,13 @@ import { onMount } from "svelte" import { workflowStore, backendUiStore } from "builderStore" import { notifier } from "@beyonk/svelte-notifications" - import Flowchart from "./svelte-flows/Flowchart.svelte" + import Flowchart from "./flowchart/Flowchart.svelte" import api from "builderStore/api" let selectedWorkflow let uiTree let instanceId = $backendUiStore.selectedDatabase._id - // TODO: better naming $: selectedWorkflow = $workflowStore.currentWorkflow $: workflowLive = selectedWorkflow && selectedWorkflow.workflow.live diff --git a/packages/builder/src/components/workflow/WorkflowBuilder/svelte-flows/Arrow.svelte b/packages/builder/src/components/workflow/WorkflowBuilder/flowchart/Arrow.svelte similarity index 100% rename from packages/builder/src/components/workflow/WorkflowBuilder/svelte-flows/Arrow.svelte rename to packages/builder/src/components/workflow/WorkflowBuilder/flowchart/Arrow.svelte diff --git a/packages/builder/src/components/workflow/WorkflowBuilder/svelte-flows/FlowChart.svelte b/packages/builder/src/components/workflow/WorkflowBuilder/flowchart/FlowChart.svelte similarity index 100% rename from packages/builder/src/components/workflow/WorkflowBuilder/svelte-flows/FlowChart.svelte rename to packages/builder/src/components/workflow/WorkflowBuilder/flowchart/FlowChart.svelte diff --git a/packages/builder/src/components/workflow/WorkflowBuilder/svelte-flows/FlowItem.svelte b/packages/builder/src/components/workflow/WorkflowBuilder/flowchart/FlowItem.svelte similarity index 100% rename from packages/builder/src/components/workflow/WorkflowBuilder/svelte-flows/FlowItem.svelte rename to packages/builder/src/components/workflow/WorkflowBuilder/flowchart/FlowItem.svelte diff --git a/packages/builder/src/components/workflow/WorkflowBuilder/svelte-flows/api.js b/packages/builder/src/components/workflow/WorkflowBuilder/flowchart/api.js similarity index 100% rename from packages/builder/src/components/workflow/WorkflowBuilder/svelte-flows/api.js rename to packages/builder/src/components/workflow/WorkflowBuilder/flowchart/api.js diff --git a/packages/builder/src/components/workflow/WorkflowBuilder/svelte-flows/index.js b/packages/builder/src/components/workflow/WorkflowBuilder/flowchart/index.js similarity index 100% rename from packages/builder/src/components/workflow/WorkflowBuilder/svelte-flows/index.js rename to packages/builder/src/components/workflow/WorkflowBuilder/flowchart/index.js diff --git a/packages/builder/src/pages/[application]/workflow/[workflow]/_layout.svelte b/packages/builder/src/pages/[application]/workflow/[workflow]/_layout.svelte new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/client/src/api/workflow/actions.js b/packages/client/src/api/workflow/actions.js index 44d863cd3f..795d34fad3 100644 --- a/packages/client/src/api/workflow/actions.js +++ b/packages/client/src/api/workflow/actions.js @@ -6,17 +6,17 @@ const delay = ms => new Promise(resolve => setTimeout(resolve, ms)) export default { SET_STATE: ({ context, args, id }) => { - // get props from the workflow context if required setState(...Object.values(args)) - // update the context with the data context = { ...context, [id]: args, } }, - NAVIGATE: ({ context, args, id }) => {}, + NAVIGATE: ({ context, args, id }) => { + // TODO client navigation + }, DELAY: async ({ context, args }) => await delay(args.time), - FILTER: (context, args) => { + FILTER: ({ context, args }) => { const { field, condition, value } = args switch (condition) { case "equals": diff --git a/packages/client/src/api/workflow/orchestrator.js b/packages/client/src/api/workflow/orchestrator.js index 1473b183e7..e69e41b918 100644 --- a/packages/client/src/api/workflow/orchestrator.js +++ b/packages/client/src/api/workflow/orchestrator.js @@ -43,7 +43,7 @@ export const clientStrategy = ({ api, instanceId }) => ({ // We don't want to render mustache templates on non-strings if (typeof argValue !== "string") continue - // Means that it's bound to state or workflow context + // Render the string with values from the workflow context and state mappedArgs[arg] = mustache.render(argValue, { context: this.context, state: get(appStore), @@ -82,8 +82,6 @@ export const clientStrategy = ({ api, instanceId }) => ({ [block.actionId]: response, } } - - console.log("workflowContext", this.context) } }, }) diff --git a/packages/client/src/createApp.js b/packages/client/src/createApp.js index 4655c0ebe7..d3fb51b124 100644 --- a/packages/client/src/createApp.js +++ b/packages/client/src/createApp.js @@ -61,7 +61,6 @@ export const createApp = ({ let rootTreeNode const pageStateManager = createStateManager({ - // store: writable({ _bbuser: user }), frontendDefinition, componentLibraries, onScreenSlotRendered, diff --git a/packages/client/src/index.js b/packages/client/src/index.js index 3e86f3ddec..95f722d445 100644 --- a/packages/client/src/index.js +++ b/packages/client/src/index.js @@ -11,7 +11,6 @@ export const loadBudibase = async opts => { const frontendDefinition = _window["##BUDIBASE_FRONTEND_DEFINITION##"] - // TODO: update const user = {} const componentLibraryModules = (opts && opts.componentLibraries) || {} diff --git a/packages/client/src/state/bbComponentApi.js b/packages/client/src/state/bbComponentApi.js index 34746058f7..9945d17b36 100644 --- a/packages/client/src/state/bbComponentApi.js +++ b/packages/client/src/state/bbComponentApi.js @@ -1,5 +1,4 @@ import { setState } from "./setState" -// import { isBound } from "./parseBinding" import { attachChildren } from "../render/attachChildren" import { getContext, setContext } from "./getSetContext" diff --git a/packages/client/src/state/eventHandlers.js b/packages/client/src/state/eventHandlers.js index f1de2592f8..88418ba1a3 100644 --- a/packages/client/src/state/eventHandlers.js +++ b/packages/client/src/state/eventHandlers.js @@ -1,7 +1,6 @@ import { setState } from "./setState" import { getState } from "./getState" import { isArray, isUndefined } from "lodash/fp" -import { appStore } from "./store" import { createApi } from "../api" diff --git a/packages/client/src/state/getState.js b/packages/client/src/state/getState.js index 4afe27236a..236b54c6dc 100644 --- a/packages/client/src/state/getState.js +++ b/packages/client/src/state/getState.js @@ -1,4 +1,3 @@ -// import { isUndefined, isObject } from "lodash/fp" import { get } from "svelte/store" import getOr from "lodash/fp/getOr" import { appStore } from "./store" diff --git a/packages/server/src/api/controllers/workflow/actions/SAVE_RECORD.js b/packages/server/src/api/controllers/workflow/actions/SAVE_RECORD.js index 24225f2287..20d70850a3 100644 --- a/packages/server/src/api/controllers/workflow/actions/SAVE_RECORD.js +++ b/packages/server/src/api/controllers/workflow/actions/SAVE_RECORD.js @@ -1,8 +1,6 @@ const recordController = require("../../record") module.exports = async function saveRecord(args) { - console.log("SAVING this record", args.record) - const ctx = { params: { instanceId: "inst_60dd510_700f7dc06735403e81d5af91072d7241", diff --git a/packages/server/src/events/index.js b/packages/server/src/events/index.js index 76dc231f15..5c386e97b6 100644 --- a/packages/server/src/events/index.js +++ b/packages/server/src/events/index.js @@ -19,7 +19,7 @@ emitter.on("record:save", async function(event) { ) for (let workflow of workflowsToTrigger) { - // SERVER SIDE STUFF!! + // TODO: server side workflow triggers } }) @@ -30,7 +30,7 @@ emitter.on("record:delete", async function(event) { ) for (let workflow of workflowsToTrigger) { - // SERVER SIDE STUFF!! + // TODO: server side workflow triggers } }) From 176906e7667eace650cf58781ef3975acbc60d27 Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Mon, 1 Jun 2020 16:22:13 +0100 Subject: [PATCH 21/27] server side workflows --- .../builderStore/store/workflow/Workflow.js | 37 ++-- .../src/builderStore/store/workflow/index.js | 8 +- .../EventsEditor/StateBindingCascader.svelte | 8 - .../PropertyCascader/PropertyCascader.svelte | 121 ----------- .../StateBindingOptions.svelte | 63 ------ .../userInterface/PropertyCascader/index.js | 1 - .../userInterface/StateBindingControl.svelte | 55 +++-- .../WorkflowPanel/BlockList/BlockList.svelte | 4 +- .../workflow/[workflow]/_layout.svelte | 3 + packages/client/src/api/workflow/index.js | 76 ++++++- .../client/src/api/workflow/orchestrator.js | 5 +- packages/server/package.json | 3 +- .../server/src/api/controllers/instance.js | 2 +- .../workflow/actions/SEND_EMAIL.js | 3 +- packages/server/src/events/index.js | 36 ++-- packages/server/src/events/workflow.js | 52 +++++ packages/server/yarn.lock | 195 +++++++++++++++++- 17 files changed, 394 insertions(+), 278 deletions(-) delete mode 100644 packages/builder/src/components/userInterface/PropertyCascader/PropertyCascader.svelte delete mode 100644 packages/builder/src/components/userInterface/PropertyCascader/StateBindingOptions.svelte delete mode 100644 packages/builder/src/components/userInterface/PropertyCascader/index.js create mode 100644 packages/server/src/events/workflow.js diff --git a/packages/builder/src/builderStore/store/workflow/Workflow.js b/packages/builder/src/builderStore/store/workflow/Workflow.js index d50e16b5da..e2bc5c1ee2 100644 --- a/packages/builder/src/builderStore/store/workflow/Workflow.js +++ b/packages/builder/src/builderStore/store/workflow/Workflow.js @@ -11,19 +11,16 @@ export default class Workflow { this.workflow = workflow } - isEmpty() { - return this.workflow.definition.trigger.length === 0 + hasTrigger() { + return this.workflow.definition.trigger } addBlock(block) { // Make sure to add trigger if doesn't exist - // if (this.isEmpty()) { - // this.workflow.definition.triggers.push({ - // id: generate(), - // ...block, - // }) - // return; - // } + if (!this.hasTrigger()) { + this.workflow.definition.trigger = { id: generate(), ...block } + return; + } this.workflow.definition.steps.push({ id: generate(), @@ -33,23 +30,27 @@ export default class Workflow { updateBlock(updatedBlock, id) { const { steps, trigger } = this.workflow.definition - // TODO: Account for trigger + + if (trigger && trigger.id === id) { + this.workflow.definition.trigger = null + return + } const stepIdx = steps.findIndex(step => step.id === id) - if (stepIdx < 0) throw new Error("Block not found.") - steps.splice(stepIdx, 1, updatedBlock) } deleteBlock(id) { const { steps, trigger } = this.workflow.definition - // TODO: Account for trigger + + if (trigger && trigger.id === id) { + this.workflow.definition.trigger = null + return + } const stepIdx = steps.findIndex(step => step.id === id) - if (stepIdx < 0) throw new Error("Block not found.") - steps.splice(stepIdx, 1) } @@ -59,7 +60,7 @@ export default class Workflow { } static buildUiTree(definition) { - return definition.steps.map(step => { + const steps = definition.steps.map(step => { // The client side display definition for the block const definition = blockDefinitions[step.type][step.actionId] if (!definition) { @@ -87,5 +88,9 @@ export default class Workflow { name: definition.name, } }) + + console.log(definition); + + return definition.trigger ? [definition.trigger, ...steps] : steps } } diff --git a/packages/builder/src/builderStore/store/workflow/index.js b/packages/builder/src/builderStore/store/workflow/index.js index b41bb8d8f7..cc403115b4 100644 --- a/packages/builder/src/builderStore/store/workflow/index.js +++ b/packages/builder/src/builderStore/store/workflow/index.js @@ -13,8 +13,12 @@ const workflowActions = store => ({ }) }, create: async ({ instanceId, name }) => { - // TODO: set these defaults in the backend - const workflow = { name, definition: { trigger: {}, steps: [] } } + const workflow = { + name, + definition: { + steps: [] + } + }; const CREATE_WORKFLOW_URL = `/api/${instanceId}/workflows` const response = await api.post(CREATE_WORKFLOW_URL, workflow) const json = await response.json() diff --git a/packages/builder/src/components/userInterface/EventsEditor/StateBindingCascader.svelte b/packages/builder/src/components/userInterface/EventsEditor/StateBindingCascader.svelte index 37681aa399..2f72d98672 100644 --- a/packages/builder/src/components/userInterface/EventsEditor/StateBindingCascader.svelte +++ b/packages/builder/src/components/userInterface/EventsEditor/StateBindingCascader.svelte @@ -10,7 +10,6 @@ allHandlers, } from "components/common/eventHandlers" import { store, workflowStore } from "builderStore" - import StateBindingOptions from "../PropertyCascader/StateBindingOptions.svelte" import { ArrowDownIcon } from "components/common/Icons/" export let parameter @@ -38,13 +37,6 @@
- {#if isOpen} - { - onChange(option) - isOpen = false - }} /> - {/if} {/if}
diff --git a/packages/builder/src/components/userInterface/PropertyCascader/PropertyCascader.svelte b/packages/builder/src/components/userInterface/PropertyCascader/PropertyCascader.svelte deleted file mode 100644 index dba1001f8f..0000000000 --- a/packages/builder/src/components/userInterface/PropertyCascader/PropertyCascader.svelte +++ /dev/null @@ -1,121 +0,0 @@ - - -
-
- { - setBindingFallback(e.target.value) - onChanged(e.target.value) - }} /> - -
- {#if isOpen} - { - onChanged(option) - isOpen = false - }} /> - {/if} -
- - diff --git a/packages/builder/src/components/userInterface/PropertyCascader/StateBindingOptions.svelte b/packages/builder/src/components/userInterface/PropertyCascader/StateBindingOptions.svelte deleted file mode 100644 index 06f8289a34..0000000000 --- a/packages/builder/src/components/userInterface/PropertyCascader/StateBindingOptions.svelte +++ /dev/null @@ -1,63 +0,0 @@ - - -
    - {#each options as option} -
  • onSelect(`${option.name}.`)}> - {option.name} - {option.description} -
  • - {/each} -
- - diff --git a/packages/builder/src/components/userInterface/PropertyCascader/index.js b/packages/builder/src/components/userInterface/PropertyCascader/index.js deleted file mode 100644 index 2a2573830a..0000000000 --- a/packages/builder/src/components/userInterface/PropertyCascader/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from "./PropertyCascader.svelte" diff --git a/packages/builder/src/components/userInterface/StateBindingControl.svelte b/packages/builder/src/components/userInterface/StateBindingControl.svelte index a89b805e5b..57a6bf13f9 100644 --- a/packages/builder/src/components/userInterface/StateBindingControl.svelte +++ b/packages/builder/src/components/userInterface/StateBindingControl.svelte @@ -2,7 +2,6 @@ import { backendUiStore } from "builderStore" import IconButton from "../common/IconButton.svelte" import Input from "../common/Input.svelte" - // import PropertyCascader from "./PropertyCascader" import Colorpicker from "../common/Colorpicker.svelte" export let value = "" @@ -22,34 +21,32 @@ size="19" on:click={() => onChanged(!value)} />
- {:else if type === 'models'} - - {:else if type === 'options' || type === 'models'} - - {:else} - + {:else if type === 'models'} + + {:else if type === 'options' || type === 'models'} + {/if}
diff --git a/packages/builder/src/components/workflow/WorkflowPanel/BlockList/BlockList.svelte b/packages/builder/src/components/workflow/WorkflowPanel/BlockList/BlockList.svelte index b173f30f15..523545a929 100644 --- a/packages/builder/src/components/workflow/WorkflowPanel/BlockList/BlockList.svelte +++ b/packages/builder/src/components/workflow/WorkflowPanel/BlockList/BlockList.svelte @@ -13,7 +13,7 @@ $: { if ( - !$workflowStore.currentWorkflow.isEmpty() && + $workflowStore.currentWorkflow.hasTrigger() && selectedTab === "TRIGGER" ) { selectedTab = "ACTION" @@ -23,7 +23,7 @@
- {#if $workflowStore.currentWorkflow.isEmpty()} + {#if !$workflowStore.currentWorkflow.hasTrigger()} + store.setCurrentPage($params.page) + \ No newline at end of file diff --git a/packages/client/src/api/workflow/index.js b/packages/client/src/api/workflow/index.js index 795dafca20..94cd6fb6f8 100644 --- a/packages/client/src/api/workflow/index.js +++ b/packages/client/src/api/workflow/index.js @@ -1,11 +1,73 @@ -import Orchestrator, { clientStrategy } from "./orchestrator" +import { get } from "svelte/store" +import mustache from "mustache" +import { appStore } from "../../state/store" +import Orchestrator from "./orchestrator"; +import clientActions from "./actions" -export const triggerWorkflow = api => ({ workflow }) => { - const workflowOrchestrator = new Orchestrator( - api, - "inst_ad75c7f_4f3e7d5d80a74b17a5187a18e2aba85e" - ) +// Execute a workflow from a running budibase app +export const clientStrategy = ({ api, instanceId }) => ({ + context: {}, + bindContextArgs: function(args) { + const mappedArgs = { ...args } + + // bind the workflow action args to the workflow context, if required + for (let arg in args) { + const argValue = args[arg] + + // We don't want to render mustache templates on non-strings + if (typeof argValue !== "string") continue + + // Render the string with values from the workflow context and state + mappedArgs[arg] = mustache.render(argValue, { + context: this.context, + state: get(appStore), + }) + } + + return mappedArgs + }, + run: async function(workflow) { + for (let block of workflow.steps) { + console.log("Executing workflow block", block) + + // This code gets run in the browser + if (block.environment === "CLIENT") { + const action = clientActions[block.actionId] + await action({ + context: this.context, + args: this.bindContextArgs(block.args), + id: block.id, + }) + } + + // this workflow block gets executed on the server + if (block.environment === "SERVER") { + const EXECUTE_WORKFLOW_URL = `/api/${instanceId}/workflows/action` + const response = await api.post({ + url: EXECUTE_WORKFLOW_URL, + body: { + action: block.actionId, + args: this.bindContextArgs(block.args, api), + }, + }) + + this.context = { + ...this.context, + [block.actionId]: response, + } + } + } + }, +}) + +export const triggerWorkflow = api => async ({ workflow }) => { + const instanceId = "inst_ad75c7f_4f3e7d5d80a74b17a5187a18e2aba85e"; + + const workflowOrchestrator = new Orchestrator(api, instanceId) workflowOrchestrator.strategy = clientStrategy - workflowOrchestrator.execute(workflow) + const EXECUTE_WORKFLOW_URL = `/api/${instanceId}/workflows/${workflow}` + const workflowDefinition = await api.get({ url: EXECUTE_WORKFLOW_URL }) + + workflowOrchestrator.execute(workflowDefinition) } diff --git a/packages/client/src/api/workflow/orchestrator.js b/packages/client/src/api/workflow/orchestrator.js index e69e41b918..df0520409a 100644 --- a/packages/client/src/api/workflow/orchestrator.js +++ b/packages/client/src/api/workflow/orchestrator.js @@ -20,10 +20,7 @@ export default class Orchestrator { this._strategy = strategy({ api: this.api, instanceId: this.instanceId }) } - async execute(workflowId) { - const EXECUTE_WORKFLOW_URL = `/api/${this.instanceId}/workflows/${workflowId}` - const workflow = await this.api.get({ url: EXECUTE_WORKFLOW_URL }) - + async execute(workflow) { if (workflow.live) { this._strategy.run(workflow.definition) } diff --git a/packages/server/package.json b/packages/server/package.json index 6e2d095655..52a7200fee 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -28,7 +28,7 @@ "test:watch": "jest -w", "initialise": "node ../cli/bin/budi init -b local -q", "budi": "node ../cli/bin/budi", - "dev:builder": "node ../cli/bin/budi run", + "dev:builder": "nodemon ../cli/bin/budi run", "electron": "electron src/electron.js", "build:electron": "electron-builder --dir", "publish:electron": "electron-builder -mwl --publish always", @@ -61,6 +61,7 @@ "koa-session": "^5.12.0", "koa-static": "^5.0.0", "lodash": "^4.17.13", + "mustache": "^4.0.1", "pino-pretty": "^4.0.0", "pouchdb": "^7.2.1", "pouchdb-all-dbs": "^1.0.2", diff --git a/packages/server/src/api/controllers/instance.js b/packages/server/src/api/controllers/instance.js index 4e6db897d5..fe40cf9fb4 100644 --- a/packages/server/src/api/controllers/instance.js +++ b/packages/server/src/api/controllers/instance.js @@ -32,7 +32,7 @@ exports.create = async function(ctx) { by_workflow_trigger: { map: function(doc) { if (doc.type === "workflow") { - const trigger = doc.definition.next + const trigger = doc.definition.trigger if (trigger) { emit([trigger.event], trigger) } diff --git a/packages/server/src/api/controllers/workflow/actions/SEND_EMAIL.js b/packages/server/src/api/controllers/workflow/actions/SEND_EMAIL.js index ef582781b8..b56e95defa 100644 --- a/packages/server/src/api/controllers/workflow/actions/SEND_EMAIL.js +++ b/packages/server/src/api/controllers/workflow/actions/SEND_EMAIL.js @@ -14,9 +14,10 @@ module.exports = async function sendEmail(args) { await sgMail.send(msg) return { success: true, - err, + ...args, } } catch (err) { + console.error(err) return { success: false, err, diff --git a/packages/server/src/events/index.js b/packages/server/src/events/index.js index 5c386e97b6..93b08bd1d8 100644 --- a/packages/server/src/events/index.js +++ b/packages/server/src/events/index.js @@ -1,37 +1,33 @@ const EventEmitter = require("events").EventEmitter const CouchDB = require("../db") +const { Orchestrator, serverStrategy } = require("./workflow"); const emitter = new EventEmitter() -async function determineWorkflowsToTrigger(instanceId, event) { - const db = new CouchDB(instanceId) +async function executeRelevantWorkflows(event, eventType) { + const db = new CouchDB(event.instanceId) const workflowsToTrigger = await db.query("database/by_workflow_trigger", { - key: [event], + key: [eventType], + include_docs: true }) - return workflowsToTrigger.rows + const workflows = workflowsToTrigger.rows.map(wf => wf.doc) + + // Create orchestrator + const workflowOrchestrator = new Orchestrator() + workflowOrchestrator.strategy = serverStrategy + + for (let workflow of workflows) { + workflowOrchestrator.execute(workflow) + } } emitter.on("record:save", async function(event) { - const workflowsToTrigger = await determineWorkflowsToTrigger( - instanceId, - "record:save" - ) - - for (let workflow of workflowsToTrigger) { - // TODO: server side workflow triggers - } + await executeRelevantWorkflows(event, "record:save"); }) emitter.on("record:delete", async function(event) { - const workflowsToTrigger = await determineWorkflowsToTrigger( - instanceId, - "record:delete" - ) - - for (let workflow of workflowsToTrigger) { - // TODO: server side workflow triggers - } + await executeRelevantWorkflows(event, "record:delete"); }) module.exports = emitter diff --git a/packages/server/src/events/workflow.js b/packages/server/src/events/workflow.js new file mode 100644 index 0000000000..f2796d91e2 --- /dev/null +++ b/packages/server/src/events/workflow.js @@ -0,0 +1,52 @@ +const mustache = require("mustache") + +/** + * The workflow orchestrator is a class responsible for executing workflows. + * It relies on the strategy pattern, which allows composable behaviour to be + * passed into its execute() function. This allows custom execution behaviour based + * on where the orchestrator is run. + * + */ +exports.Orchestrator = class Orchestrator { + set strategy(strategy) { + this._strategy = strategy() + } + + async execute(workflow) { + if (workflow.live) { + this._strategy.run(workflow.definition) + } + } +} + +exports.serverStrategy = () => ({ + context: {}, + bindContextArgs: function(args) { + const mappedArgs = { ...args } + + // bind the workflow action args to the workflow context, if required + for (let arg in args) { + const argValue = args[arg] + // We don't want to render mustache templates on non-strings + if (typeof argValue !== "string") continue + + mappedArgs[arg] = mustache.render(argValue, { context: this.context }) + } + + return mappedArgs + }, + run: async function(workflow) { + for (let block of workflow.steps) { + console.log("Executing workflow block", block) + if (block.type === "CLIENT") continue + + const action = require(`../api/controllers/workflow/actions/${block.actionId}`) + const response = await action(this.bindContextArgs(block.args)) + + this.context = { + ...this.context, + [block.id]: response, + } + } + } +}); diff --git a/packages/server/yarn.lock b/packages/server/yarn.lock index 3fc1f15622..62f4dc7d70 100644 --- a/packages/server/yarn.lock +++ b/packages/server/yarn.lock @@ -194,6 +194,20 @@ lodash "^4.17.13" to-fast-properties "^2.0.0" +"@budibase/client@^0.0.32": + version "0.0.32" + resolved "https://registry.yarnpkg.com/@budibase/client/-/client-0.0.32.tgz#76d9f147563a0bf939eae7f32ce75b2a527ba496" + integrity sha512-jmCCLn0CUoQbL6h623S5IqK6+GYLqX3WzUTZInSb1SCBOM3pI0eLP5HwTR6s7r42SfD0v9jTWRdyTnHiElNj8A== + dependencies: + "@nx-js/compiler-util" "^2.0.0" + bcryptjs "^2.4.3" + deep-equal "^2.0.1" + lodash "^4.17.15" + lunr "^2.3.5" + regexparam "^1.3.0" + shortid "^2.2.8" + svelte "^3.9.2" + "@budibase/core@^0.0.32": version "0.0.32" resolved "https://registry.yarnpkg.com/@budibase/core/-/core-0.0.32.tgz#c5d9ab869c5e9596a1ac337aaf041e795b1cc7fa" @@ -849,6 +863,11 @@ array-equal@^1.0.0: resolved "https://registry.yarnpkg.com/array-equal/-/array-equal-1.0.0.tgz#8c2a5ef2472fd9ea742b04c77a75093ba2757c93" integrity sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM= +array-filter@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/array-filter/-/array-filter-1.0.0.tgz#baf79e62e6ef4c2a4c0b831232daffec251f9d83" + integrity sha1-uveeYubvTCpMC4MSMtr/7CUfnYM= + array-unique@^0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" @@ -921,6 +940,13 @@ atomic-sleep@^1.0.0: resolved "https://registry.yarnpkg.com/atomic-sleep/-/atomic-sleep-1.0.0.tgz#eb85b77a601fc932cfe432c5acd364a9e2c9075b" integrity sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ== +available-typed-arrays@^1.0.0, available-typed-arrays@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.2.tgz#6b098ca9d8039079ee3f77f7b783c4480ba513f5" + integrity sha512-XWX3OX8Onv97LMk/ftVyBibpGwY5a8SmuxZPzeOxqmuEqUCOM9ZE+uIaD1VNJ5QnvU2UQusvmKbuM1FR8QWGfQ== + dependencies: + array-filter "^1.0.0" + aws-sign2@~0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" @@ -1643,6 +1669,26 @@ decompress-response@^3.3.0: dependencies: mimic-response "^1.0.0" +deep-equal@^2.0.1: + version "2.0.3" + resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-2.0.3.tgz#cad1c15277ad78a5c01c49c2dee0f54de8a6a7b0" + integrity sha512-Spqdl4H+ky45I9ByyJtXteOm9CaIrPmnIPmOhrkKGNYWeDgCvJ8jNYVCTjChxW4FqGuZnLHADc8EKRMX6+CgvA== + dependencies: + es-abstract "^1.17.5" + es-get-iterator "^1.1.0" + is-arguments "^1.0.4" + is-date-object "^1.0.2" + is-regex "^1.0.5" + isarray "^2.0.5" + object-is "^1.1.2" + object-keys "^1.1.1" + object.assign "^4.1.0" + regexp.prototype.flags "^1.3.0" + side-channel "^1.0.2" + which-boxed-primitive "^1.0.1" + which-collection "^1.0.1" + which-typed-array "^1.1.2" + deep-equal@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5" @@ -2001,7 +2047,7 @@ error-inject@^1.0.0: resolved "https://registry.yarnpkg.com/error-inject/-/error-inject-1.0.0.tgz#e2b3d91b54aed672f309d950d154850fa11d4f37" integrity sha1-4rPZG1Su1nLzCdlQ0VSFD6EdTzc= -es-abstract@^1.17.0-next.1, es-abstract@^1.17.2, es-abstract@^1.17.5: +es-abstract@^1.17.0-next.1, es-abstract@^1.17.2, es-abstract@^1.17.4, es-abstract@^1.17.5: version "1.17.5" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.5.tgz#d8c9d1d66c8981fb9200e2251d799eee92774ae9" integrity sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg== @@ -2018,6 +2064,19 @@ es-abstract@^1.17.0-next.1, es-abstract@^1.17.2, es-abstract@^1.17.5: string.prototype.trimleft "^2.1.1" string.prototype.trimright "^2.1.1" +es-get-iterator@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/es-get-iterator/-/es-get-iterator-1.1.0.tgz#bb98ad9d6d63b31aacdc8f89d5d0ee57bcb5b4c8" + integrity sha512-UfrmHuWQlNMTs35e1ypnvikg6jCz3SK8v8ImvmDsh36fCVUR1MqoFDiyn0/k52C8NqO3YsO8Oe0azeesNuqSsQ== + dependencies: + es-abstract "^1.17.4" + has-symbols "^1.0.1" + is-arguments "^1.0.4" + is-map "^2.0.1" + is-set "^2.0.1" + is-string "^1.0.5" + isarray "^2.0.5" + es-to-primitive@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" @@ -3010,11 +3069,21 @@ is-accessor-descriptor@^1.0.0: dependencies: kind-of "^6.0.0" +is-arguments@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.0.4.tgz#3faf966c7cba0ff437fb31f6250082fcf0448cf3" + integrity sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA== + is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= +is-bigint@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.0.tgz#73da8c33208d00f130e9b5e15d23eac9215601c4" + integrity sha512-t5mGUXC/xRheCK431ylNiSkGGpBp8bHENBcENTkDT6ppwPzEVxNGZRvgvmOEfbWkFhA7D2GEuE2mmQTr78sl2g== + is-binary-path@~2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" @@ -3022,6 +3091,11 @@ is-binary-path@~2.1.0: dependencies: binary-extensions "^2.0.0" +is-boolean-object@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.0.1.tgz#10edc0900dd127697a92f6f9807c7617d68ac48e" + integrity sha512-TqZuVwa/sppcrhUCAYkGBk7w0yxfQQnxq28fjkO53tnK9FQXmdwz2JS5+GjsWQ6RByES1K40nI+yDic5c9/aAQ== + is-buffer@^1.1.5: version "1.1.6" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" @@ -3058,7 +3132,7 @@ is-data-descriptor@^1.0.0: dependencies: kind-of "^6.0.0" -is-date-object@^1.0.1: +is-date-object@^1.0.1, is-date-object@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.2.tgz#bda736f2cd8fd06d32844e7743bfa7494c3bfd7e" integrity sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g== @@ -3133,11 +3207,21 @@ is-installed-globally@^0.3.1: global-dirs "^2.0.1" is-path-inside "^3.0.1" +is-map@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.1.tgz#520dafc4307bb8ebc33b813de5ce7c9400d644a1" + integrity sha512-T/S49scO8plUiAOA2DBTBG3JHpn1yiw0kRp6dgiZ0v2/6twi5eiB0rHtHFH9ZIrvlWc6+4O+m4zg5+Z833aXgw== + is-npm@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-4.0.0.tgz#c90dd8380696df87a7a6d823c20d0b12bbe3c84d" integrity sha512-96ECIfh9xtDDlPylNPXhzjsykHsMJZ18ASpaWzQyBr4YRTcVjUvzaHayDAES2oU/3KpljhHUjtSRNiDwi0F0ig== +is-number-object@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.4.tgz#36ac95e741cf18b283fc1ddf5e83da798e3ec197" + integrity sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw== + is-number@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" @@ -3174,11 +3258,21 @@ is-regex@^1.0.5: dependencies: has "^1.0.3" +is-set@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.1.tgz#d1604afdab1724986d30091575f54945da7e5f43" + integrity sha512-eJEzOtVyenDs1TMzSQ3kU3K+E0GUS9sno+F0OBT97xsgcJsF9nXMBtkT9/kut5JEpM7oL7X/0qxR17K3mcwIAA== + is-stream@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= +is-string@^1.0.4, is-string@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.5.tgz#40493ed198ef3ff477b8c7f92f644ec82a5cd3a6" + integrity sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ== + is-symbol@^1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.3.tgz#38e1014b9e6329be0de9d24a414fd7441ec61937" @@ -3195,11 +3289,31 @@ is-type-of@^1.0.0: is-class-hotfix "~0.0.6" isstream "~0.1.2" +is-typed-array@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.3.tgz#a4ff5a5e672e1a55f99c7f54e59597af5c1df04d" + integrity sha512-BSYUBOK/HJibQ30wWkWold5txYwMUXQct9YHAQJr8fSwvZoiglcqB0pd7vEN23+Tsi9IUEjztdOSzl4qLVYGTQ== + dependencies: + available-typed-arrays "^1.0.0" + es-abstract "^1.17.4" + foreach "^2.0.5" + has-symbols "^1.0.1" + is-typedarray@^1.0.0, is-typedarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= +is-weakmap@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-weakmap/-/is-weakmap-2.0.1.tgz#5008b59bdc43b698201d18f62b37b2ca243e8cf2" + integrity sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA== + +is-weakset@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-weakset/-/is-weakset-2.0.1.tgz#e9a0af88dbd751589f5e50d80f4c98b780884f83" + integrity sha512-pi4vhbhVHGLxohUw7PhGsueT4vRGFoXhP7+RGN0jKIv9+8PWYCQTqtADngrxOm2g46hoH0+g8uZZBzMrvVGDmw== + is-windows@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" @@ -3225,6 +3339,11 @@ isarray@1.0.0, isarray@~1.0.0: resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= +isarray@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" + integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== + isbinaryfile@^4.0.6: version "4.0.6" resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-4.0.6.tgz#edcb62b224e2b4710830b67498c8e4e5a4d2610b" @@ -4441,6 +4560,11 @@ ms@^2.1.1: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== +mustache@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/mustache/-/mustache-4.0.1.tgz#d99beb031701ad433338e7ea65e0489416c854a2" + integrity sha512-yL5VE97+OXn4+Er3THSmTdCFCtx5hHWzrolvH+JObZnUYwuaG7XV+Ch4fR2cIrcYI0tFHxS7iyFYl14bW8y2sA== + mute-stream@0.0.8: version "0.0.8" resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" @@ -4632,6 +4756,14 @@ object-inspect@^1.7.0: resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.7.0.tgz#f4f6bd181ad77f006b5ece60bd0b6f398ff74a67" integrity sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw== +object-is@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.2.tgz#c5d2e87ff9e119f78b7a088441519e2eec1573b6" + integrity sha512-5lHCz+0uufF6wZ7CRFWJN3hp8Jqblpgve06U5CMQ3f//6iDjPr2PEo9MWCjEssDsa+UZEL4PkFpr+BMop6aKzQ== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.5" + object-keys@^1.0.11, object-keys@^1.0.12, object-keys@^1.0.6, object-keys@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" @@ -5353,6 +5485,19 @@ regex-not@^1.0.0, regex-not@^1.0.2: extend-shallow "^3.0.2" safe-regex "^1.1.0" +regexp.prototype.flags@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz#7aba89b3c13a64509dabcf3ca8d9fbb9bdf5cb75" + integrity sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.0-next.1" + +regexparam@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/regexparam/-/regexparam-1.3.0.tgz#2fe42c93e32a40eff6235d635e0ffa344b92965f" + integrity sha512-6IQpFBv6e5vz1QAqI+V4k8P2e/3gRrqfCJ9FI+O1FLQTO+Uz6RXZEZOPmTJ6hlGj7gkERzY5BRCv09whKP96/g== + regexpp@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f" @@ -5681,6 +5826,14 @@ shortid@^2.2.8: dependencies: nanoid "^2.1.0" +side-channel@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.2.tgz#df5d1abadb4e4bf4af1cd8852bf132d2f7876947" + integrity sha512-7rL9YlPHg7Ancea1S96Pa8/QWb4BtXL/TZvS6B8XFetGBeuhAsfmUspK6DokBeZ64+Kj9TCNRD/30pVz1BvQNA== + dependencies: + es-abstract "^1.17.0-next.1" + object-inspect "^1.7.0" + signal-exit@^3.0.0, signal-exit@^3.0.2: version "3.0.3" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" @@ -6089,6 +6242,11 @@ supports-color@^7.1.0: dependencies: has-flag "^4.0.0" +svelte@^3.9.2: + version "3.23.0" + resolved "https://registry.yarnpkg.com/svelte/-/svelte-3.23.0.tgz#bbcd6887cf588c24a975b14467455abfff9acd3f" + integrity sha512-cnyd96bK/Nw5DnYuB1hzm5cl6+I1fpmdKOteA7KLzU9KGLsLmvWsSkSKbcntzODCLmSySN3HjcgTHRo6/rJNTw== + symbol-tree@^3.2.2: version "3.2.4" resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" @@ -6569,11 +6727,44 @@ whatwg-url@^7.0.0: tr46 "^1.0.1" webidl-conversions "^4.0.2" +which-boxed-primitive@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.1.tgz#cbe8f838ebe91ba2471bb69e9edbda67ab5a5ec1" + integrity sha512-7BT4TwISdDGBgaemWU0N0OU7FeAEJ9Oo2P1PHRm/FCWoEi2VLWC9b6xvxAA3C/NMpxg3HXVgi0sMmGbNUbNepQ== + dependencies: + is-bigint "^1.0.0" + is-boolean-object "^1.0.0" + is-number-object "^1.0.3" + is-string "^1.0.4" + is-symbol "^1.0.2" + +which-collection@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/which-collection/-/which-collection-1.0.1.tgz#70eab71ebbbd2aefaf32f917082fc62cdcb70906" + integrity sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A== + dependencies: + is-map "^2.0.1" + is-set "^2.0.1" + is-weakmap "^2.0.1" + is-weakset "^2.0.1" + which-module@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= +which-typed-array@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.2.tgz#e5f98e56bda93e3dac196b01d47c1156679c00b2" + integrity sha512-KT6okrd1tE6JdZAy3o2VhMoYPh3+J6EMZLyrxBQsZflI1QCZIxMrIYLkosd8Twf+YfknVIHmYQPgJt238p8dnQ== + dependencies: + available-typed-arrays "^1.0.2" + es-abstract "^1.17.5" + foreach "^2.0.5" + function-bind "^1.1.1" + has-symbols "^1.0.1" + is-typed-array "^1.1.3" + which@^1.2.9, which@^1.3.0: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" From b372f6b03dd650d72c61ef15f4daf2ba94ab2bd2 Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Mon, 1 Jun 2020 21:26:32 +0100 Subject: [PATCH 22/27] tidy up --- packages/builder/package.json | 1 - .../builderStore/store/workflow/Workflow.js | 17 +- .../src/builderStore/store/workflow/index.js | 12 +- .../builder/src/components/common/binding.js | 31 ---- .../src/components/common/eventHandlers.js | 9 +- .../modals/CreateEditModel/FieldView.svelte | 1 - .../AppPreview/iframeTemplate.js | 1 - .../EventsEditor/StateBindingCascader.svelte | 5 +- .../ParamInputs/ModelSelector.svelte | 1 - .../workflow/SetupPanel/SetupPanel.svelte | 138 +++++++++++---- .../SetupPanel/WorkflowBlockSetup.svelte | 2 + .../WorkflowList/WorkflowList.svelte | 1 - .../WorkflowPanel/blockDefinitions.js | 4 +- packages/client/src/api/workflow/actions.js | 8 +- packages/client/src/api/workflow/index.js | 10 +- packages/client/src/createApp.js | 2 - packages/client/src/render/screenRouter.js | 2 - packages/client/src/state/eventHandlers.js | 7 +- packages/client/src/state/parseBinding.js | 67 -------- packages/client/src/state/stateManager.js | 160 +----------------- .../client/tests/workflowOrchestrator.spec.js | 43 ----- packages/server/src/api/controllers/view.js | 3 +- .../src/api/controllers/workflow/index.js | 7 +- packages/server/src/api/routes/workflow.js | 4 +- packages/server/src/events/index.js | 10 +- packages/server/src/events/workflow.js | 5 +- 26 files changed, 156 insertions(+), 395 deletions(-) delete mode 100644 packages/builder/src/components/common/binding.js delete mode 100644 packages/client/src/state/parseBinding.js delete mode 100644 packages/client/tests/workflowOrchestrator.spec.js diff --git a/packages/builder/package.json b/packages/builder/package.json index 7e4e7b9cab..22ee0e5302 100644 --- a/packages/builder/package.json +++ b/packages/builder/package.json @@ -54,7 +54,6 @@ "safe-buffer": "^5.1.2", "shortid": "^2.2.8", "string_decoder": "^1.2.0", - "svelte-grid": "^1.10.8", "svelte-simple-modal": "^0.3.0", "uikit": "^3.1.7" }, diff --git a/packages/builder/src/builderStore/store/workflow/Workflow.js b/packages/builder/src/builderStore/store/workflow/Workflow.js index e2bc5c1ee2..d9c1ee249f 100644 --- a/packages/builder/src/builderStore/store/workflow/Workflow.js +++ b/packages/builder/src/builderStore/store/workflow/Workflow.js @@ -17,9 +17,9 @@ export default class Workflow { addBlock(block) { // Make sure to add trigger if doesn't exist - if (!this.hasTrigger()) { + if (!this.hasTrigger() && block.type === "TRIGGER") { this.workflow.definition.trigger = { id: generate(), ...block } - return; + return } this.workflow.definition.steps.push({ @@ -30,7 +30,7 @@ export default class Workflow { updateBlock(updatedBlock, id) { const { steps, trigger } = this.workflow.definition - + if (trigger && trigger.id === id) { this.workflow.definition.trigger = null return @@ -43,7 +43,7 @@ export default class Workflow { deleteBlock(id) { const { steps, trigger } = this.workflow.definition - + if (trigger && trigger.id === id) { this.workflow.definition.trigger = null return @@ -60,7 +60,10 @@ export default class Workflow { } static buildUiTree(definition) { - const steps = definition.steps.map(step => { + const steps = [] + if (definition.trigger) steps.push(definition.trigger) + + return [...steps, ...definition.steps].map(step => { // The client side display definition for the block const definition = blockDefinitions[step.type][step.actionId] if (!definition) { @@ -88,9 +91,5 @@ export default class Workflow { name: definition.name, } }) - - console.log(definition); - - return definition.trigger ? [definition.trigger, ...steps] : steps } } diff --git a/packages/builder/src/builderStore/store/workflow/index.js b/packages/builder/src/builderStore/store/workflow/index.js index cc403115b4..8d5e63b197 100644 --- a/packages/builder/src/builderStore/store/workflow/index.js +++ b/packages/builder/src/builderStore/store/workflow/index.js @@ -13,12 +13,12 @@ const workflowActions = store => ({ }) }, create: async ({ instanceId, name }) => { - const workflow = { - name, - definition: { - steps: [] - } - }; + const workflow = { + name, + definition: { + steps: [], + }, + } const CREATE_WORKFLOW_URL = `/api/${instanceId}/workflows` const response = await api.post(CREATE_WORKFLOW_URL, workflow) const json = await response.json() diff --git a/packages/builder/src/components/common/binding.js b/packages/builder/src/components/common/binding.js deleted file mode 100644 index 18c31e8040..0000000000 --- a/packages/builder/src/components/common/binding.js +++ /dev/null @@ -1,31 +0,0 @@ -// import { isString } from "lodash/fp" - -// import { -// BB_STATE_BINDINGPATH, -// BB_STATE_FALLBACK, -// BB_STATE_BINDINGSOURCE, -// isBound, -// parseBinding, -// } from "@budibase/client/src/state/parseBinding" - -// export const isBinding = isBound - -// export const setBinding = ({ path, fallback, source }, binding = {}) => { -// if (isNonEmptyString(path)) binding[BB_STATE_BINDINGPATH] = path -// if (isNonEmptyString(fallback)) binding[BB_STATE_FALLBACK] = fallback -// binding[BB_STATE_BINDINGSOURCE] = source || "store" -// return binding -// } - -// export const getBinding = val => { -// const binding = parseBinding(val) -// return binding -// ? binding -// : { -// path: "", -// source: "store", -// fallback: "", -// } -// } - -// const isNonEmptyString = s => isString(s) && s.length > 0 diff --git a/packages/builder/src/components/common/eventHandlers.js b/packages/builder/src/components/common/eventHandlers.js index 9dac01eb86..2e7a62ac3e 100644 --- a/packages/builder/src/components/common/eventHandlers.js +++ b/packages/builder/src/components/common/eventHandlers.js @@ -1,13 +1,8 @@ import { eventHandlers } from "../../../../client/src/state/eventHandlers" -import { writable } from "svelte/store" export { EVENT_TYPE_MEMBER_NAME } from "../../../../client/src/state/eventHandlers" -export const allHandlers = user => { - const store = writable({ - _bbuser: user, - }) - - const handlersObj = eventHandlers(store) +export const allHandlers = () => { + const handlersObj = eventHandlers() const handlers = Object.keys(handlersObj).map(name => ({ name, diff --git a/packages/builder/src/components/database/ModelDataTable/modals/CreateEditModel/FieldView.svelte b/packages/builder/src/components/database/ModelDataTable/modals/CreateEditModel/FieldView.svelte index 46f8bd9189..2c3443ae56 100644 --- a/packages/builder/src/components/database/ModelDataTable/modals/CreateEditModel/FieldView.svelte +++ b/packages/builder/src/components/database/ModelDataTable/modals/CreateEditModel/FieldView.svelte @@ -64,7 +64,6 @@ {:else if type === 'datetime'} - diff --git a/packages/builder/src/components/userInterface/AppPreview/iframeTemplate.js b/packages/builder/src/components/userInterface/AppPreview/iframeTemplate.js index da17bead3f..d3a94b6cf5 100644 --- a/packages/builder/src/components/userInterface/AppPreview/iframeTemplate.js +++ b/packages/builder/src/components/userInterface/AppPreview/iframeTemplate.js @@ -4,7 +4,6 @@ export default ({ selectedComponentType, selectedComponentId, frontendDefinition, - currentPageFunctions, }) => ` ${stylesheetLinks} diff --git a/packages/builder/src/components/userInterface/EventsEditor/StateBindingCascader.svelte b/packages/builder/src/components/userInterface/EventsEditor/StateBindingCascader.svelte index 2f72d98672..130b0f8269 100644 --- a/packages/builder/src/components/userInterface/EventsEditor/StateBindingCascader.svelte +++ b/packages/builder/src/components/userInterface/EventsEditor/StateBindingCascader.svelte @@ -6,8 +6,7 @@ import { find, map, keys, reduce, keyBy } from "lodash/fp" import { pipe } from "components/common/core" import { - EVENT_TYPE_MEMBER_NAME, - allHandlers, + EVENT_TYPE_MEMBER_NAME } from "components/common/eventHandlers" import { store, workflowStore } from "builderStore" import { ArrowDownIcon } from "components/common/Icons/" @@ -26,7 +25,7 @@ class="budibase__input" on:change={onChange} bind:value={parameter.value}> - {#each $workflowStore.workflows as workflow} + {#each $workflowStore.workflows.filter(wf => wf.live) as workflow} {/each} diff --git a/packages/builder/src/components/workflow/SetupPanel/ParamInputs/ModelSelector.svelte b/packages/builder/src/components/workflow/SetupPanel/ParamInputs/ModelSelector.svelte index 5f12f74919..1c39e19505 100644 --- a/packages/builder/src/components/workflow/SetupPanel/ParamInputs/ModelSelector.svelte +++ b/packages/builder/src/components/workflow/SetupPanel/ParamInputs/ModelSelector.svelte @@ -5,7 +5,6 @@
-
+ {#if selectedTab === 'TEST'} +
+ {#if testResult} + + {/if} + +
+ {/if} + {#if selectedTab === 'SETUP'} + {#if workflowBlock} + + + {:else if $workflowStore.currentWorkflow} +
+ +
+ +
+ +
+
+
+ + +
+ +
-
- - Some User Access Stuff Here -
-
- + + {/if} {/if}
@@ -78,21 +129,30 @@ font-weight: bold; display: flex; align-items: center; - justify-content: space-between; margin-bottom: 20px; } - header > span { + .selected { color: var(--font); } + .config-item { + padding: 20px; + background: var(--light-grey); + } + + header > span { + color: var(--dark-grey); + margin-right: 20px; + } + label { font-weight: 500; font-size: 14px; color: var(--font); } - .delete-workflow-button { + .workflow-button { font-family: Roboto; width: 100%; border: solid 1px #f2f2f2; @@ -102,4 +162,24 @@ font-size: 12px; font-weight: 500; } + + .test-result { + border: none; + width: 100%; + border-radius: 2px; + height: 32px; + font-size: 12px; + font-weight: 500; + color: var(--white); + text-align: center; + margin-bottom: 10px; + } + + .passed { + background: #84c991; + } + + .failed { + background: var(--coral); + } diff --git a/packages/builder/src/components/workflow/SetupPanel/WorkflowBlockSetup.svelte b/packages/builder/src/components/workflow/SetupPanel/WorkflowBlockSetup.svelte index 7bb16daa52..d629630099 100644 --- a/packages/builder/src/components/workflow/SetupPanel/WorkflowBlockSetup.svelte +++ b/packages/builder/src/components/workflow/SetupPanel/WorkflowBlockSetup.svelte @@ -76,5 +76,7 @@ textarea { min-height: 150px; + font-family: inherit; + padding: 5px; } diff --git a/packages/builder/src/components/workflow/WorkflowPanel/WorkflowList/WorkflowList.svelte b/packages/builder/src/components/workflow/WorkflowPanel/WorkflowList/WorkflowList.svelte index 9b03ee2f6d..ff13782114 100644 --- a/packages/builder/src/components/workflow/WorkflowPanel/WorkflowList/WorkflowList.svelte +++ b/packages/builder/src/components/workflow/WorkflowPanel/WorkflowList/WorkflowList.svelte @@ -28,7 +28,6 @@ async function saveWorkflow() { const workflow = $workflowStore.currentWorkflow.workflow - // TODO: Clean up args await workflowStore.actions.save({ instanceId: $backendUiStore.selectedDatabase._id, workflow, diff --git a/packages/builder/src/components/workflow/WorkflowPanel/blockDefinitions.js b/packages/builder/src/components/workflow/WorkflowPanel/blockDefinitions.js index 4e36353532..96da6a3910 100644 --- a/packages/builder/src/components/workflow/WorkflowPanel/blockDefinitions.js +++ b/packages/builder/src/components/workflow/WorkflowPanel/blockDefinitions.js @@ -22,17 +22,19 @@ const ACTION = { }, SAVE_RECORD: { name: "Save Record", + tagline: "Save a {{model.name}} record", icon: "ri-save-3-fill", description: "Save a record to your database.", environment: "SERVER", params: { - model: "string", + model: "model", }, }, DELETE_RECORD: { description: "Delete a record from your database.", icon: "ri-delete-bin-line", name: "Delete Record", + tagline: "Delete a {{model.name}} record", environment: "SERVER", params: { record: "string", diff --git a/packages/client/src/api/workflow/actions.js b/packages/client/src/api/workflow/actions.js index 795d34fad3..f7d4755fbf 100644 --- a/packages/client/src/api/workflow/actions.js +++ b/packages/client/src/api/workflow/actions.js @@ -1,6 +1,4 @@ -import { get } from "svelte/store" import { setState } from "../../state/setState" -import { appStore } from "../../state/store" const delay = ms => new Promise(resolve => setTimeout(resolve, ms)) @@ -12,11 +10,11 @@ export default { [id]: args, } }, - NAVIGATE: ({ context, args, id }) => { + NAVIGATE: () => { // TODO client navigation }, - DELAY: async ({ context, args }) => await delay(args.time), - FILTER: ({ context, args }) => { + DELAY: async ({ args }) => await delay(args.time), + FILTER: ({ args }) => { const { field, condition, value } = args switch (condition) { case "equals": diff --git a/packages/client/src/api/workflow/index.js b/packages/client/src/api/workflow/index.js index 94cd6fb6f8..6d6d12a97e 100644 --- a/packages/client/src/api/workflow/index.js +++ b/packages/client/src/api/workflow/index.js @@ -1,7 +1,7 @@ import { get } from "svelte/store" import mustache from "mustache" import { appStore } from "../../state/store" -import Orchestrator from "./orchestrator"; +import Orchestrator from "./orchestrator" import clientActions from "./actions" // Execute a workflow from a running budibase app @@ -28,8 +28,6 @@ export const clientStrategy = ({ api, instanceId }) => ({ }, run: async function(workflow) { for (let block of workflow.steps) { - console.log("Executing workflow block", block) - // This code gets run in the browser if (block.environment === "CLIENT") { const action = clientActions[block.actionId] @@ -60,13 +58,11 @@ export const clientStrategy = ({ api, instanceId }) => ({ }, }) -export const triggerWorkflow = api => async ({ workflow }) => { - const instanceId = "inst_ad75c7f_4f3e7d5d80a74b17a5187a18e2aba85e"; - +export const triggerWorkflow = api => async ({ workflow, instanceId }) => { const workflowOrchestrator = new Orchestrator(api, instanceId) workflowOrchestrator.strategy = clientStrategy - const EXECUTE_WORKFLOW_URL = `/api/${instanceId}/workflows/${workflow}` + const EXECUTE_WORKFLOW_URL = `/api/workflows/${workflow}` const workflowDefinition = await api.get({ url: EXECUTE_WORKFLOW_URL }) workflowOrchestrator.execute(workflowDefinition) diff --git a/packages/client/src/createApp.js b/packages/client/src/createApp.js index d3fb51b124..a80c1009c7 100644 --- a/packages/client/src/createApp.js +++ b/packages/client/src/createApp.js @@ -1,4 +1,3 @@ -import { writable } from "svelte/store" import { attachChildren } from "./render/attachChildren" import { createTreeNode } from "./render/prepareRenderComponent" import { screenRouter } from "./render/screenRouter" @@ -7,7 +6,6 @@ import { createStateManager } from "./state/stateManager" export const createApp = ({ componentLibraries, frontendDefinition, - user, window, }) => { let routeTo diff --git a/packages/client/src/render/screenRouter.js b/packages/client/src/render/screenRouter.js index 1325bbd20a..262ed65f66 100644 --- a/packages/client/src/render/screenRouter.js +++ b/packages/client/src/render/screenRouter.js @@ -1,8 +1,6 @@ import regexparam from "regexparam" import { routerStore } from "../state/store" -import { initRouteStore } from "../state/store" -// TODO: refactor export const screenRouter = ({ screens, onScreenSelected, appRootPath }) => { const makeRootedPath = url => { if (appRootPath) { diff --git a/packages/client/src/state/eventHandlers.js b/packages/client/src/state/eventHandlers.js index 88418ba1a3..b013956dd4 100644 --- a/packages/client/src/state/eventHandlers.js +++ b/packages/client/src/state/eventHandlers.js @@ -6,17 +6,12 @@ import { createApi } from "../api" export const EVENT_TYPE_MEMBER_NAME = "##eventHandlerType" -export const eventHandlers = (store, rootPath, routeTo) => { +export const eventHandlers = (rootPath, routeTo) => { const handler = (parameters, execute) => ({ execute, parameters, }) - let currentState - store.subscribe(state => { - currentState = state - }) - const api = createApi({ rootPath, setState, diff --git a/packages/client/src/state/parseBinding.js b/packages/client/src/state/parseBinding.js deleted file mode 100644 index 4ecf9887ef..0000000000 --- a/packages/client/src/state/parseBinding.js +++ /dev/null @@ -1,67 +0,0 @@ -// export const BB_STATE_BINDINGPATH = "##bbstate" -// export const BB_STATE_BINDINGSOURCE = "##bbsource" -// export const BB_STATE_FALLBACK = "##bbstatefallback" - -// export const isBound = prop => !!parseBinding(prop) - -// /** -// * -// * @param {object|string|number} prop - component property to parse for a dynamic state binding -// * @returns {object|boolean} -// */ -// export const parseBinding = prop => { -// if (!prop) return false - -// if (isBindingExpression(prop)) { -// return parseBindingExpression(prop) -// } - -// if (isAlreadyBinding(prop)) { -// return { -// path: prop.path, -// source: prop.source || "store", -// fallback: prop.fallback, -// } -// } - -// if (hasBindingObject(prop)) { -// return { -// path: prop[BB_STATE_BINDINGPATH], -// fallback: prop[BB_STATE_FALLBACK] || "", -// source: prop[BB_STATE_BINDINGSOURCE] || "store", -// } -// } -// } - -// export const isStoreBinding = binding => binding && binding.source === "store" -// export const isContextBinding = binding => -// binding && binding.source === "context" -// // export const isEventBinding = binding => binding && binding.source === "event" - -// const hasBindingObject = prop => -// typeof prop === "object" && prop[BB_STATE_BINDINGPATH] !== undefined - -// const isAlreadyBinding = prop => typeof prop === "object" && prop.path - -// const isBindingExpression = prop => -// typeof prop === "string" && -// (prop.startsWith("state.") || -// prop.startsWith("context.") || -// prop.startsWith("event.") || -// prop.startsWith("route.")) - -// const parseBindingExpression = prop => { -// let [source, ...rest] = prop.split(".") -// let path = rest.join(".") - -// if (source === "route") { -// source = "state" -// path = `##routeParams.${path}` -// } - -// return { -// fallback: "", // TODO: provide fallback support -// source, -// path, -// } -// } diff --git a/packages/client/src/state/stateManager.js b/packages/client/src/state/stateManager.js index b4597be5dd..8e777ca4b9 100644 --- a/packages/client/src/state/stateManager.js +++ b/packages/client/src/state/stateManager.js @@ -21,27 +21,16 @@ const isMetaProp = propName => propName === "_styles" export const createStateManager = ({ - // store, appRootPath, frontendDefinition, componentLibraries, onScreenSlotRendered, routeTo, }) => { - let handlerTypes = eventHandlers(appStore, appRootPath, routeTo) + let handlerTypes = eventHandlers(appRootPath, routeTo) let currentState - // any nodes that have props that are bound to the store - // let nodesBoundByProps = [] - - // any node whose children depend on code, that uses the store - // let nodesWithCodeBoundChildren = [] - const getCurrentState = () => currentState - // const registerBindings = _registerBindings( - // nodesBoundByProps, - // nodesWithCodeBoundChildren - // ) const bb = bbFactory({ store: appStore, @@ -53,131 +42,26 @@ export const createStateManager = ({ const setup = _setup({ handlerTypes, getCurrentState, bb, store: appStore }) - // TODO: remove - const unsubscribe = appStore.subscribe(state => { - console.log("store updated", state) - return state - }) - - // const unsubscribe = store.subscribe( - // onStoreStateUpdated({ - // setCurrentState: state => (currentState = state), - // getCurrentState, - // // nodesWithCodeBoundChildren, - // // nodesBoundByProps, - // componentLibraries, - // onScreenSlotRendered, - // setupState: setup, - // }) - // ) - return { setup, - destroy: () => unsubscribe(), + destroy: () => {}, getCurrentState, store: appStore, } } -const onStoreStateUpdated = ({ - setCurrentState, - getCurrentState, - componentLibraries, - onScreenSlotRendered, - setupState, -}) => state => { - // fire the state update event to re-render anything bound to this - // setCurrentState(state) - // setCurrentState(state) - // attachChildren({ - // componentLibraries, - // treeNode: createTreeNode(), - // onScreenSlotRendered, - // setupState, - // getCurrentState, - // })(document.querySelector("#app"), { hydrate: true, force: true }) - // // the original array gets changed by components' destroy() - // // so we make a clone and check if they are still in the original - // const nodesWithBoundChildren_clone = [...nodesWithCodeBoundChildren] - // for (let node of nodesWithBoundChildren_clone) { - // if (!nodesWithCodeBoundChildren.includes(node)) continue - // attachChildren({ - // componentLibraries, - // treeNode: node, - // onScreenSlotRendered, - // setupState, - // getCurrentState, - // })(node.rootElement, { hydrate: true, force: true }) - // } -} - -// const _registerBindings = (nodesBoundByProps, nodesWithCodeBoundChildren) => ( -// node, -// bindings -// ) => { -// if (bindings.length > 0) { -// node.bindings = bindings -// nodesBoundByProps.push(node) -// const onDestroy = () => { -// nodesBoundByProps = nodesBoundByProps.filter(n => n === node) -// node.onDestroy = node.onDestroy.filter(d => d === onDestroy) -// } -// node.onDestroy.push(onDestroy) -// } -// if ( -// node.props._children && -// node.props._children.filter(c => c._codeMeta && c._codeMeta.dependsOnStore) -// .length > 0 -// ) { -// nodesWithCodeBoundChildren.push(node) -// const onDestroy = () => { -// nodesWithCodeBoundChildren = nodesWithCodeBoundChildren.filter( -// n => n === node -// ) -// node.onDestroy = node.onDestroy.filter(d => d === onDestroy) -// } -// node.onDestroy.push(onDestroy) -// } -// } - -// const setNodeState = (storeState, node) => { -// if (!node.component) return -// const newProps = { ...node.bindings.initialProps } - -// for (let binding of node.bindings) { -// const val = getState(storeState, binding.path, binding.fallback) - -// if (val === undefined && newProps[binding.propName] !== undefined) { -// delete newProps[binding.propName] -// } - -// if (val !== undefined) { -// newProps[binding.propName] = val -// } -// } - -// node.component.$set(newProps) -// } - const _setup = ({ handlerTypes, getCurrentState, bb, store }) => node => { const props = node.props const context = node.context || {} const initialProps = { ...props } - // const storeBoundProps = [] const currentStoreState = get(appStore) - console.log("node", node) - - // console.log("node", node); - // console.log("nodeComponent", node.component); - for (let propName in props) { if (isMetaProp(propName)) continue const propValue = props[propName] - // const binding = parseBinding(propValue) - // TODO: better binding stuff + // A little bit of a hack - won't bind if the string doesn't start with {{ const isBound = typeof propValue === "string" && propValue.startsWith("{{") if (isBound) { @@ -191,27 +75,6 @@ const _setup = ({ handlerTypes, getCurrentState, bb, store }) => node => { } } - // if (isBound) binding.propName = propName - - // if (isBound && binding.source === "state") { - // storeBoundProps.push(binding) - - // initialProps[propName] = !currentStoreState - // ? binding.fallback - // : getState( - // currentStoreState, - // binding.path, - // binding.fallback, - // binding.source - // ) - // } - - // if (isBound && binding.source === "context") { - // initialProps[propName] = !context - // ? propValue - // : getState(context, binding.path, binding.fallback, binding.source) - // } - if (isEventType(propValue)) { const handlersInfos = [] for (let event of propValue) { @@ -228,21 +91,6 @@ const _setup = ({ handlerTypes, getCurrentState, bb, store }) => node => { state: getCurrentState(), context, }) - // const paramBinding = parseBinding(paramValue) - // if (!paramBinding) { - // resolvedParams[paramName] = () => paramValue - // continue - // } - - // let paramValueSource - - // if (paramBinding.source === "context") paramValueSource = context - // if (paramBinding.source === "state") - // paramValueSource = getCurrentState() - - // // The new dynamic event parameter bound to the relevant source - // resolvedParams[paramName] = () => - // getState(paramValueSource, paramBinding.path, paramBinding.fallback) } handlerInfo.parameters = resolvedParams @@ -262,8 +110,6 @@ const _setup = ({ handlerTypes, getCurrentState, bb, store }) => node => { } } - // registerBindings(node, storeBoundProps) - const setup = _setup({ handlerTypes, getCurrentState, bb, store }) initialProps._bb = bb(node, setup) diff --git a/packages/client/tests/workflowOrchestrator.spec.js b/packages/client/tests/workflowOrchestrator.spec.js deleted file mode 100644 index 4858e9f51b..0000000000 --- a/packages/client/tests/workflowOrchestrator.spec.js +++ /dev/null @@ -1,43 +0,0 @@ -const TEST_WORKFLOW = { - "_id": "8ebe79daf1c744c7ab204c0b964e309e", - "_rev": "37-94ae573300721c98267cc1d18822c94d", - "name": "Workflow", - "type": "workflow", - "definition": { - "next": { - "type": "CLIENT", - "actionId": "SET_STATE", - "args": { - "path": "myPath", - "value": "foo" - }, - "next": { - "type": "SERVER", - "actionId": "SAVE_RECORD", - "args": { - "record": { - "modelId": "f452a2b9c3a94251b9ea7be1e20e3b19", - "name": "workflowRecord" - }, - "next": { - "type": "CLIENT", - "actionId": "SET_STATE", - "args": { - "path": "myPath", - "value": "$context.SAVE_RECORD.record.name" - }, - } - } - } - } - } -}; - -describe("Workflow Orchestrator", () => { - it("executes a workflow", () => { - }); - - it("", () => { - - }); -}); \ No newline at end of file diff --git a/packages/server/src/api/controllers/view.js b/packages/server/src/api/controllers/view.js index 8456837c6a..2f15d41570 100644 --- a/packages/server/src/api/controllers/view.js +++ b/packages/server/src/api/controllers/view.js @@ -11,7 +11,8 @@ const controller = { if ( !name.startsWith("all") && name !== "by_type" && - name !== "by_username" + name !== "by_username" && + name !== "by_workflow_trigger" ) { response.push({ name, diff --git a/packages/server/src/api/controllers/workflow/index.js b/packages/server/src/api/controllers/workflow/index.js index 55173c28cb..4c5394ab2f 100644 --- a/packages/server/src/api/controllers/workflow/index.js +++ b/packages/server/src/api/controllers/workflow/index.js @@ -1,5 +1,5 @@ -const CouchDB = require("../../db") -const newid = require("../../db/newid") +const CouchDB = require("../../../db") +const newid = require("../../../db/newid") exports.create = async function(ctx) { const db = new CouchDB(ctx.params.instanceId) @@ -49,7 +49,7 @@ exports.fetch = async function(ctx) { } exports.find = async function(ctx) { - const db = new CouchDB(ctx.params.instanceId) + const db = new CouchDB(ctx.user.instanceId) ctx.body = await db.get(ctx.params.id) } @@ -61,7 +61,6 @@ exports.executeAction = async function(ctx) { exports.fetchActionScript = async function(ctx) { const workflowAction = require(`./actions/${ctx.action}`) - console.log(workflowAction) ctx.body = workflowAction } diff --git a/packages/server/src/api/routes/workflow.js b/packages/server/src/api/routes/workflow.js index b4d8920261..2420e4cb52 100644 --- a/packages/server/src/api/routes/workflow.js +++ b/packages/server/src/api/routes/workflow.js @@ -1,13 +1,13 @@ const Router = require("@koa/router") const controller = require("../controllers/workflow") const authorized = require("../../middleware/authorized") -const { BUILDER, EXECUTE_WORKFLOW } = require("../../utilities/accessLevels") +const { BUILDER } = require("../../utilities/accessLevels") const router = Router() router .get("/api/:instanceId/workflows", authorized(BUILDER), controller.fetch) - .get("/api/:instanceId/workflows/:id", authorized(BUILDER), controller.find) + .get("/api/workflows/:id", authorized(BUILDER), controller.find) .get( "/api/:instanceId/workflows/:id/:action", authorized(BUILDER), diff --git a/packages/server/src/events/index.js b/packages/server/src/events/index.js index 93b08bd1d8..019b866f9b 100644 --- a/packages/server/src/events/index.js +++ b/packages/server/src/events/index.js @@ -1,6 +1,6 @@ const EventEmitter = require("events").EventEmitter const CouchDB = require("../db") -const { Orchestrator, serverStrategy } = require("./workflow"); +const { Orchestrator, serverStrategy } = require("./workflow") const emitter = new EventEmitter() @@ -8,7 +8,7 @@ async function executeRelevantWorkflows(event, eventType) { const db = new CouchDB(event.instanceId) const workflowsToTrigger = await db.query("database/by_workflow_trigger", { key: [eventType], - include_docs: true + include_docs: true, }) const workflows = workflowsToTrigger.rows.map(wf => wf.doc) @@ -22,12 +22,12 @@ async function executeRelevantWorkflows(event, eventType) { } } -emitter.on("record:save", async function(event) { - await executeRelevantWorkflows(event, "record:save"); +emitter.on("action", async function(event) { + await executeRelevantWorkflows(event, "record:save") }) emitter.on("record:delete", async function(event) { - await executeRelevantWorkflows(event, "record:delete"); + await executeRelevantWorkflows(event, "record:delete") }) module.exports = emitter diff --git a/packages/server/src/events/workflow.js b/packages/server/src/events/workflow.js index f2796d91e2..6368274dff 100644 --- a/packages/server/src/events/workflow.js +++ b/packages/server/src/events/workflow.js @@ -37,7 +37,6 @@ exports.serverStrategy = () => ({ }, run: async function(workflow) { for (let block of workflow.steps) { - console.log("Executing workflow block", block) if (block.type === "CLIENT") continue const action = require(`../api/controllers/workflow/actions/${block.actionId}`) @@ -48,5 +47,5 @@ exports.serverStrategy = () => ({ [block.id]: response, } } - } -}); + }, +}) From a6602ba1fcae42d9d7d579e49d005dd6bd61644e Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Mon, 1 Jun 2020 21:31:55 +0100 Subject: [PATCH 23/27] lint + format --- .../store/workflow/tests/testWorkflow.js | 1 - .../EventsEditor/StateBindingCascader.svelte | 4 +- .../userInterface/StateBindingControl.svelte | 52 +-- .../workflow/SetupPanel/SetupPanel.svelte | 8 +- .../workflow/[workflow]/_layout.svelte | 2 +- packages/builder/tests/createProps.spec.js | 12 - packages/client/src/api/workflow/actions.js | 1 + packages/client/tests/bindingDom.spec.js | 363 ------------------ packages/client/tests/domControlFlow.spec.js | 74 ---- packages/server/src/api/controllers/view.js | 2 +- 10 files changed, 33 insertions(+), 486 deletions(-) delete mode 100644 packages/client/tests/bindingDom.spec.js delete mode 100644 packages/client/tests/domControlFlow.spec.js diff --git a/packages/builder/src/builderStore/store/workflow/tests/testWorkflow.js b/packages/builder/src/builderStore/store/workflow/tests/testWorkflow.js index 2ef6018bf0..90c4b17924 100644 --- a/packages/builder/src/builderStore/store/workflow/tests/testWorkflow.js +++ b/packages/builder/src/builderStore/store/workflow/tests/testWorkflow.js @@ -3,7 +3,6 @@ export default { _rev: "4-02c6659734934895812fa7be0215ee59", name: "Test Workflow", definition: { - trigger: {}, steps: [ { id: "VFWeZcIPx", diff --git a/packages/builder/src/components/userInterface/EventsEditor/StateBindingCascader.svelte b/packages/builder/src/components/userInterface/EventsEditor/StateBindingCascader.svelte index 130b0f8269..1edf5d45a0 100644 --- a/packages/builder/src/components/userInterface/EventsEditor/StateBindingCascader.svelte +++ b/packages/builder/src/components/userInterface/EventsEditor/StateBindingCascader.svelte @@ -5,9 +5,7 @@ import Input from "components/common/Input.svelte" import { find, map, keys, reduce, keyBy } from "lodash/fp" import { pipe } from "components/common/core" - import { - EVENT_TYPE_MEMBER_NAME - } from "components/common/eventHandlers" + import { EVENT_TYPE_MEMBER_NAME } from "components/common/eventHandlers" import { store, workflowStore } from "builderStore" import { ArrowDownIcon } from "components/common/Icons/" diff --git a/packages/builder/src/components/userInterface/StateBindingControl.svelte b/packages/builder/src/components/userInterface/StateBindingControl.svelte index 57a6bf13f9..0246fb71dc 100644 --- a/packages/builder/src/components/userInterface/StateBindingControl.svelte +++ b/packages/builder/src/components/userInterface/StateBindingControl.svelte @@ -21,32 +21,32 @@ size="19" on:click={() => onChanged(!value)} />
- {:else if type === 'models'} - - {:else if type === 'options' || type === 'models'} - + {:else if type === 'models'} + + {:else if type === 'options' || type === 'models'} + {/if}
diff --git a/packages/builder/src/components/workflow/SetupPanel/SetupPanel.svelte b/packages/builder/src/components/workflow/SetupPanel/SetupPanel.svelte index f7252a5707..1ae741b55a 100644 --- a/packages/builder/src/components/workflow/SetupPanel/SetupPanel.svelte +++ b/packages/builder/src/components/workflow/SetupPanel/SetupPanel.svelte @@ -1,5 +1,5 @@ \ No newline at end of file + diff --git a/packages/builder/tests/createProps.spec.js b/packages/builder/tests/createProps.spec.js index 269ab8156a..bd468ba41a 100644 --- a/packages/builder/tests/createProps.spec.js +++ b/packages/builder/tests/createProps.spec.js @@ -1,6 +1,5 @@ import { createProps } from "../src/components/userInterface/pagesParsing/createProps" import { keys, some } from "lodash/fp" -import { BB_STATE_BINDINGPATH } from "@budibase/client/src/state/parseBinding" import { stripStandardProps } from "./testData" describe("createDefaultProps", () => { @@ -94,17 +93,6 @@ describe("createDefaultProps", () => { expect(props.onClick).toEqual([]) }) - it("should create a object with empty state when prop def is 'state' ", () => { - const comp = getcomponent() - comp.props.data = "state" - - const { props, errors } = createProps(comp) - - expect(errors).toEqual([]) - expect(props.data[BB_STATE_BINDINGPATH]).toBeDefined() - expect(props.data[BB_STATE_BINDINGPATH]).toBe("") - }) - it("should create a object children array when children == true ", () => { const comp = getcomponent() comp.children = true diff --git a/packages/client/src/api/workflow/actions.js b/packages/client/src/api/workflow/actions.js index f7d4755fbf..794cb184bf 100644 --- a/packages/client/src/api/workflow/actions.js +++ b/packages/client/src/api/workflow/actions.js @@ -9,6 +9,7 @@ export default { ...context, [id]: args, } + return context }, NAVIGATE: () => { // TODO client navigation diff --git a/packages/client/tests/bindingDom.spec.js b/packages/client/tests/bindingDom.spec.js deleted file mode 100644 index 2013803d27..0000000000 --- a/packages/client/tests/bindingDom.spec.js +++ /dev/null @@ -1,363 +0,0 @@ -import { load, makePage, makeScreen } from "./testAppDef" -import { EVENT_TYPE_MEMBER_NAME } from "../src/state/eventHandlers" - -describe("initialiseApp (binding)", () => { - it("should populate root element prop from store value", async () => { - const { dom } = await load( - makePage({ - _component: "testlib/div", - className: { - "##bbstate": "divClassName", - "##bbsource": "state", - "##bbstatefallback": "default", - }, - }) - ) - - const rootDiv = dom.window.document.body.children[0] - expect(rootDiv.className.includes("default")).toBe(true) - }) - - it("should update root element from store", async () => { - const { dom, app } = await load( - makePage({ - _component: "testlib/div", - className: { - "##bbstate": "divClassName", - "##bbsource": "state", - "##bbstatefallback": "default", - }, - }) - ) - - app.pageStore().update(s => { - s.divClassName = "newvalue" - return s - }) - - const rootDiv = dom.window.document.body.children[0] - expect(rootDiv.className.includes("newvalue")).toBe(true) - }) - - it("should update root element from store, using binding expression", async () => { - const { dom, app } = await load( - makePage({ - _component: "testlib/div", - className: "state.divClassName", - }) - ) - - app.pageStore().update(s => { - s.divClassName = "newvalue" - return s - }) - - const rootDiv = dom.window.document.body.children[0] - expect(rootDiv.className.includes("newvalue")).toBe(true) - }) - - it("should populate child component with store value", async () => { - const { dom } = await load( - makePage({ - _component: "testlib/div", - _children: [ - { - _component: "testlib/h1", - text: { - "##bbstate": "headerOneText", - "##bbsource": "state", - "##bbstatefallback": "header one", - }, - }, - { - _component: "testlib/h1", - text: { - "##bbstate": "headerTwoText", - "##bbsource": "state", - "##bbstatefallback": "header two", - }, - }, - ], - }) - ) - - const rootDiv = dom.window.document.body.children[0] - - expect(rootDiv.children.length).toBe(2) - expect(rootDiv.children[0].tagName).toBe("H1") - expect(rootDiv.children[0].innerText).toBe("header one") - expect(rootDiv.children[1].tagName).toBe("H1") - expect(rootDiv.children[1].innerText).toBe("header two") - }) - - it("should populate child component with store value", async () => { - const { dom, app } = await load( - makePage({ - _component: "testlib/div", - _children: [ - { - _component: "testlib/h1", - text: { - "##bbstate": "headerOneText", - "##bbsource": "state", - "##bbstatefallback": "header one", - }, - }, - { - _component: "testlib/h1", - text: { - "##bbstate": "headerTwoText", - "##bbsource": "state", - "##bbstatefallback": "header two", - }, - }, - ], - }) - ) - - app.pageStore().update(s => { - s.headerOneText = "header 1 - new val" - s.headerTwoText = "header 2 - new val" - return s - }) - - const rootDiv = dom.window.document.body.children[0] - - expect(rootDiv.children.length).toBe(2) - expect(rootDiv.children[0].tagName).toBe("H1") - expect(rootDiv.children[0].innerText).toBe("header 1 - new val") - expect(rootDiv.children[1].tagName).toBe("H1") - expect(rootDiv.children[1].innerText).toBe("header 2 - new val") - }) - - it("should populate screen child with store value", async () => { - const { dom, app } = await load( - makePage({ - _component: "testlib/div", - _children: [ - { - _component: "##builtin/screenslot", - text: "header one", - }, - ], - }), - [ - makeScreen("/", { - _component: "testlib/div", - className: "screen-class", - _children: [ - { - _component: "testlib/h1", - text: { - "##bbstate": "headerOneText", - "##bbsource": "state", - "##bbstatefallback": "header one", - }, - }, - { - _component: "testlib/h1", - text: { - "##bbstate": "headerTwoText", - "##bbsource": "state", - "##bbstatefallback": "header two", - }, - }, - ], - }), - ] - ) - - app.screenStore().update(s => { - s.headerOneText = "header 1 - new val" - s.headerTwoText = "header 2 - new val" - return s - }) - - const rootDiv = dom.window.document.body.children[0] - expect(rootDiv.children.length).toBe(1) - - const screenRoot = rootDiv.children[0] - - expect(screenRoot.children.length).toBe(1) - expect(screenRoot.children[0].children.length).toBe(2) - expect(screenRoot.children[0].children[0].innerText).toBe( - "header 1 - new val" - ) - expect(screenRoot.children[0].children[1].innerText).toBe( - "header 2 - new val" - ) - }) - - it("should fire events", async () => { - const { dom, app } = await load( - makePage({ - _component: "testlib/button", - onClick: [ - event("Set State", { - path: "address", - value: "123 Main Street", - }), - ], - }) - ) - - const button = dom.window.document.body.children[0] - expect(button.tagName).toBe("BUTTON") - - let storeAddress - app.pageStore().subscribe(s => { - storeAddress = s.address - }) - button.dispatchEvent(new dom.window.Event("click")) - expect(storeAddress).toBe("123 Main Street") - }) - - it("should alter event parameters based on store values", async () => { - const { dom, app } = await load( - makePage({ - _component: "testlib/button", - onClick: [ - event("Set State", { - path: "address", - value: { - "##bbstate": "sourceaddress", - "##bbsource": "state", - "##bbstatefallback": "fallback address", - }, - }), - ], - }) - ) - - const button = dom.window.document.body.children[0] - expect(button.tagName).toBe("BUTTON") - - let storeAddress - app.pageStore().subscribe(s => { - storeAddress = s.address - }) - - button.dispatchEvent(new dom.window.Event("click")) - expect(storeAddress).toBe("fallback address") - - app.pageStore().update(s => { - s.sourceaddress = "new address" - return s - }) - - button.dispatchEvent(new dom.window.Event("click")) - expect(storeAddress).toBe("new address") - }) - - it("should take event parameters from context values", async () => { - const { dom, app } = await load( - makePage({ - _component: "testlib/button", - _id: "with_context", - onClick: [ - event("Set State", { - path: "address", - value: { - "##bbstate": "testKey", - "##bbsource": "context", - "##bbstatefallback": "fallback address", - }, - }), - ], - }) - ) - - const button = dom.window.document.body.children[0] - expect(button.tagName).toBe("BUTTON") - - let storeAddress - app.pageStore().subscribe(s => { - storeAddress = s.address - }) - - button.dispatchEvent(new dom.window.Event("click")) - expect(storeAddress).toBe("test value") - }) -}) - -it("should rerender components when their code is bound to the store ", async () => { - const { dom, app } = await load( - makePage({ - _component: "testlib/div", - _children: [ - { - _component: "testlib/div", - _id: "n_clones_based_on_store", - className: "child_div", - }, - ], - }) - ) - - const rootDiv = dom.window.document.body.children[0] - expect(rootDiv.tagName).toBe("DIV") - expect(rootDiv.children.length).toBe(0) - - app.pageStore().update(s => { - s.componentCount = 3 - return s - }) - - expect(rootDiv.children.length).toBe(3) - expect(rootDiv.children[0].className.includes("child_div")).toBe(true) - - app.pageStore().update(s => { - s.componentCount = 5 - return s - }) - - expect(rootDiv.children.length).toBe(5) - expect(rootDiv.children[0].className.includes("child_div")).toBe(true) - - app.pageStore().update(s => { - s.componentCount = 0 - return s - }) - - expect(rootDiv.children.length).toBe(0) -}) - -it("should be able to read value from context, passed fromm parent, through code", async () => { - const { dom, app } = await load( - makePage({ - _component: "testlib/div", - _children: [ - { - _component: "testlib/div", - _id: "n_clones_based_on_store", - className: { - "##bbstate": "index", - "##bbsource": "context", - "##bbstatefallback": "nothing", - }, - }, - ], - }) - ) - - const rootDiv = dom.window.document.body.children[0] - expect(rootDiv.tagName).toBe("DIV") - expect(rootDiv.children.length).toBe(0) - - app.pageStore().update(s => { - s.componentCount = 3 - return s - }) - - expect(rootDiv.children.length).toBe(3) - expect(rootDiv.children[0].className.includes("index_0")).toBe(true) - expect(rootDiv.children[1].className.includes("index_1")).toBe(true) - expect(rootDiv.children[2].className.includes("index_2")).toBe(true) -}) - -const event = (handlerType, parameters) => { - const e = {} - e[EVENT_TYPE_MEMBER_NAME] = handlerType - e.parameters = parameters - return e -} diff --git a/packages/client/tests/domControlFlow.spec.js b/packages/client/tests/domControlFlow.spec.js deleted file mode 100644 index 3c817d859d..0000000000 --- a/packages/client/tests/domControlFlow.spec.js +++ /dev/null @@ -1,74 +0,0 @@ -import { load, makePage } from "./testAppDef" - -describe("controlFlow", () => { - it("should display simple div, with always true render function", async () => { - const { dom } = await load( - makePage({ - _component: "testlib/div", - className: "my-test-class", - _id: "always_render", - }) - ) - - expect(dom.window.document.body.children.length).toBe(1) - const child = dom.window.document.body.children[0] - expect(child.className.includes("my-test-class")).toBeTruthy() - }) - - it("should not display div, with always false render function", async () => { - const { dom } = await load( - makePage({ - _component: "testlib/div", - className: "my-test-class", - _id: "never_render", - }) - ) - - expect(dom.window.document.body.children.length).toBe(0) - }) - - it("should display 3 divs in a looped render function", async () => { - const { dom } = await load( - makePage({ - _component: "testlib/div", - className: "my-test-class", - _id: "three_clones", - }) - ) - - expect(dom.window.document.body.children.length).toBe(3) - - const child0 = dom.window.document.body.children[0] - expect(child0.className.includes("my-test-class")).toBeTruthy() - - const child1 = dom.window.document.body.children[1] - expect(child1.className.includes("my-test-class")).toBeTruthy() - - const child2 = dom.window.document.body.children[2] - expect(child2.className.includes("my-test-class")).toBeTruthy() - }) - - it("should display 3 div, in a looped render, as children", async () => { - const { dom } = await load( - makePage({ - _component: "testlib/div", - _children: [ - { - _component: "testlib/div", - className: "my-test-class", - _id: "three_clones", - }, - ], - }) - ) - - expect(dom.window.document.body.children.length).toBe(1) - - const rootDiv = dom.window.document.body.children[0] - expect(rootDiv.children.length).toBe(3) - - expect(rootDiv.children[0].className.includes("my-test-class")).toBeTruthy() - expect(rootDiv.children[1].className.includes("my-test-class")).toBeTruthy() - expect(rootDiv.children[2].className.includes("my-test-class")).toBeTruthy() - }) -}) diff --git a/packages/server/src/api/controllers/view.js b/packages/server/src/api/controllers/view.js index 2f15d41570..9e847da358 100644 --- a/packages/server/src/api/controllers/view.js +++ b/packages/server/src/api/controllers/view.js @@ -11,7 +11,7 @@ const controller = { if ( !name.startsWith("all") && name !== "by_type" && - name !== "by_username" && + name !== "by_username" && name !== "by_workflow_trigger" ) { response.push({ From 9a2393f79b67f8df88ef6ac11577720f0ef39931 Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Mon, 1 Jun 2020 21:40:02 +0100 Subject: [PATCH 24/27] path fix --- .../components/workflow/WorkflowBuilder/WorkflowBuilder.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/builder/src/components/workflow/WorkflowBuilder/WorkflowBuilder.svelte b/packages/builder/src/components/workflow/WorkflowBuilder/WorkflowBuilder.svelte index 6399ab727a..506575f05d 100644 --- a/packages/builder/src/components/workflow/WorkflowBuilder/WorkflowBuilder.svelte +++ b/packages/builder/src/components/workflow/WorkflowBuilder/WorkflowBuilder.svelte @@ -2,7 +2,7 @@ import { onMount } from "svelte" import { workflowStore, backendUiStore } from "builderStore" import { notifier } from "@beyonk/svelte-notifications" - import Flowchart from "./flowchart/Flowchart.svelte" + import Flowchart from "./flowchart/FlowChart.svelte" import api from "builderStore/api" let selectedWorkflow From a9e77836cbc6527b075e8c8fcdb38a01eba91218 Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Mon, 1 Jun 2020 22:25:44 +0100 Subject: [PATCH 25/27] tests --- .../src/render/prepareRenderComponent.js | 5 +- packages/client/src/state/setState.js | 8 +- packages/client/src/state/stateManager.js | 3 +- .../src/api/routes/tests/record.spec.js | 104 +++--------------- .../server/src/api/routes/tests/view.spec.js | 74 ++----------- .../src/api/routes/tests/workflow.spec.js | 23 ---- 6 files changed, 27 insertions(+), 190 deletions(-) diff --git a/packages/client/src/render/prepareRenderComponent.js b/packages/client/src/render/prepareRenderComponent.js index 30af819221..6e8ae123d0 100644 --- a/packages/client/src/render/prepareRenderComponent.js +++ b/packages/client/src/render/prepareRenderComponent.js @@ -44,8 +44,9 @@ export const prepareRenderComponent = ({ const unsubscribe = appStore.subscribe(state => { const storeBoundProps = { ...initialProps._bb.props } for (let prop in storeBoundProps) { - if (typeof storeBoundProps[prop] === "string") { - storeBoundProps[prop] = mustache.render(storeBoundProps[prop], { + const propValue = storeBoundProps[prop] + if (typeof propValue === "string") { + storeBoundProps[prop] = mustache.render(propValue, { state, }) } diff --git a/packages/client/src/state/setState.js b/packages/client/src/state/setState.js index 3d3b2045c8..ad25b631d5 100644 --- a/packages/client/src/state/setState.js +++ b/packages/client/src/state/setState.js @@ -8,10 +8,4 @@ export const setState = (path, value) => { state = set(path, value, state) return state }) -} - -// export const setStateFromBinding = (store, binding, value) => { -// const parsedBinding = parseBinding(binding) -// if (!parsedBinding) return -// return setState(store, parsedBinding.path, value) -// } +} \ No newline at end of file diff --git a/packages/client/src/state/stateManager.js b/packages/client/src/state/stateManager.js index 8e777ca4b9..5d32f5a486 100644 --- a/packages/client/src/state/stateManager.js +++ b/packages/client/src/state/stateManager.js @@ -33,7 +33,7 @@ export const createStateManager = ({ const getCurrentState = () => currentState const bb = bbFactory({ - store: appStore, + store: appStore, getCurrentState, frontendDefinition, componentLibraries, @@ -65,6 +65,7 @@ const _setup = ({ handlerTypes, getCurrentState, bb, store }) => node => { const isBound = typeof propValue === "string" && propValue.startsWith("{{") if (isBound) { + console.log("NODE IS BOUND", node); initialProps[propName] = mustache.render(propValue, { state: currentStoreState, context, diff --git a/packages/server/src/api/routes/tests/record.spec.js b/packages/server/src/api/routes/tests/record.spec.js index 93543d742f..2c8c542715 100644 --- a/packages/server/src/api/routes/tests/record.spec.js +++ b/packages/server/src/api/routes/tests/record.spec.js @@ -5,13 +5,7 @@ const { createModel, supertest, defaultHeaders, - testPermissionsForEndpoint, - shouldReturn403WhenNoPermission, - shouldReturn200WithOnlyOnePermission, } = require("./couchTestUtils"); -const { - WRITE_MODEL, READ_MODEL -} = require("../../../utilities/accessLevels") describe("/records", () => { let request @@ -41,15 +35,15 @@ describe("/records", () => { } }) - const createRecord = async r => - await request - .post(`/api/${instance._id}/${model._id}/records`) - .send(r || record) - .set(defaultHeaders) - .expect('Content-Type', /json/) - .expect(200) + describe("save, load, update, delete", () => { - describe("save", () => { + const createRecord = async r => + await request + .post(`/api/${instance._id}/${model._id}/records`) + .send(r || record) + .set(defaultHeaders) + .expect('Content-Type', /json/) + .expect(200) it("returns a success message when the record is created", async () => { const res = await createRecord() @@ -58,18 +52,6 @@ describe("/records", () => { expect(res.body._rev).toBeDefined() }) - it("should apply authorization to endpoint", async () => { - await testPermissionsForEndpoint({ - request, - method: "POST", - url: `/api/${instance._id}/${model._id}/records`, - body: record, - instanceId: instance._id, - permissionName: WRITE_MODEL, - itemId: model._id, - }) - }) - it("updates a record successfully", async () => { const rec = await createRecord() const existing = rec.body @@ -89,9 +71,7 @@ describe("/records", () => { expect(res.res.statusMessage).toEqual(`${model.name} updated successfully.`) expect(res.body.name).toEqual("Updated Name") }) - }) - describe("find", () => { it("should load a record", async () => { const rec = await createRecord() const existing = rec.body @@ -110,31 +90,6 @@ describe("/records", () => { }) }) - it("load should return 404 when record does not exist", async () => { - await createRecord() - await request - .get(`/api/${instance._id}/${model._id}/records/not-a-valid-id`) - .set(defaultHeaders) - .expect('Content-Type', /json/) - .expect(404) - }) - - it("should apply authorization to endpoint", async () => { - const rec = await createRecord() - await testPermissionsForEndpoint({ - request, - method: "GET", - url: `/api/${instance._id}/${model._id}/records/${rec.body._id}`, - instanceId: instance._id, - permissionName: READ_MODEL, - itemId: model._id, - }) - }) - - }) - - describe("fetch", () => { - it("should list all records for given modelId", async () => { const newRecord = { modelId: model._id, @@ -155,47 +110,14 @@ describe("/records", () => { expect(res.body.find(r => r.name === record.name)).toBeDefined() }) - it("should apply authorization to endpoint", async () => { - await testPermissionsForEndpoint({ - request, - method: "GET", - url: `/api/${instance._id}/${model._id}/records`, - instanceId: instance._id, - permissionName: READ_MODEL, - itemId: model._id, - }) - }) - - }) - - describe("delete", () => { - - it("should remove a record", async () => { - const createRes = await createRecord() - + it("load should return 404 when record does not exist", async () => { + await createRecord() await request - .delete(`/api/${instance._id}/${model._id}/records/${createRes.body._id}/${createRes.body._rev}`) - .set(defaultHeaders) - .expect(200) - - await request - .get(`/api/${instance._id}/${model._id}/records/${createRes.body._id}`) + .get(`/api/${instance._id}/${model._id}/records/not-a-valid-id`) .set(defaultHeaders) + .expect('Content-Type', /json/) .expect(404) }) - - it("should apply authorization to endpoint", async () => { - const createRes = await createRecord() - await testPermissionsForEndpoint({ - request, - method: "DELETE", - url: `/api/${instance._id}/${model._id}/records/${createRes.body._id}/${createRes.body._rev}`, - instanceId: instance._id, - permissionName: WRITE_MODEL, - itemId: model._id, - }) - }) - }) describe("validate", () => { @@ -224,4 +146,4 @@ describe("/records", () => { }) }) -}) +}) \ No newline at end of file diff --git a/packages/server/src/api/routes/tests/view.spec.js b/packages/server/src/api/routes/tests/view.spec.js index c6231cf336..b8fd47972a 100644 --- a/packages/server/src/api/routes/tests/view.spec.js +++ b/packages/server/src/api/routes/tests/view.spec.js @@ -3,13 +3,9 @@ const { createApplication, createInstance, createModel, - createRecord, supertest, - defaultHeaders, - testPermissionsForEndpoint, - builderEndpointShouldBlockNormalUsers, + defaultHeaders } = require("./couchTestUtils") -const { READ_VIEW } = require("../../../utilities/accessLevels") describe("/views", () => { let request @@ -38,10 +34,11 @@ describe("/views", () => { .send({ name: "TestView", map: `function(doc) { - if (doc.type === 'record') { + if (doc.id) { emit(doc.name, doc._id); } }`, + reduce: `function(keys, values) { }` }) .set(defaultHeaders) .expect('Content-Type', /json/) @@ -54,28 +51,14 @@ describe("/views", () => { expect(res.res.statusMessage).toEqual("View TestView created successfully."); expect(res.body.name).toEqual("TestView"); }) - - it("should apply authorization to endpoint", async () => { - await builderEndpointShouldBlockNormalUsers({ - request, - method: "POST", - url: `/api/${instance._id}/views`, - body: { - name: "TestView", - map: `function(doc) { - if (doc.id) { - emit(doc.name, doc._id); - } - }`, - reduce: `function(keys, values) { }` - }, - instanceId: instance._id, - }) - }) }); describe("fetch", () => { + beforeEach(async () => { + model = await createModel(request, instance._id); + }); + it("should only return custom views", async () => { const view = await createView() const res = await request @@ -86,46 +69,5 @@ describe("/views", () => { expect(res.body.length).toBe(1) expect(res.body.find(v => v.name === view.body.name)).toBeDefined() }) - - it("should apply authorization to endpoint", async () => { - await builderEndpointShouldBlockNormalUsers({ - request, - method: "GET", - url: `/api/${instance._id}/views`, - instanceId: instance._id, - }) - }) }); - - describe("query", () => { - - beforeEach(async () => { - model = await createModel(request, instance._id); - }); - - it("should return records from custom view", async () => { - await createView() - const rec1 = await createRecord({ request, instanceId: instance._id, modelId: model._id }) - await createRecord({ request, instanceId: instance._id, modelId: model._id }) - const res = await request - .get(`/api/${instance._id}/views/TestView`) - .set(defaultHeaders) - .expect('Content-Type', /json/) - .expect(200) - expect(res.body.length).toBe(2) - expect(res.body.find(r => r._id === rec1._id)).toBeDefined() - }) - - it("should apply authorization to endpoint", async () => { - await createView() - await testPermissionsForEndpoint({ - request, - method: "GET", - url: `/api/${instance._id}/views/TestView`, - instanceId: instance._id, - permissionName: READ_VIEW, - itemId: "TestView", - }) - }) - }) -}); +}); \ No newline at end of file diff --git a/packages/server/src/api/routes/tests/workflow.spec.js b/packages/server/src/api/routes/tests/workflow.spec.js index 0e0ce6bfc6..1f345698bf 100644 --- a/packages/server/src/api/routes/tests/workflow.spec.js +++ b/packages/server/src/api/routes/tests/workflow.spec.js @@ -125,29 +125,6 @@ describe("/workflows", () => { }) }) - describe("find", () => { - it("returns a workflow when queried by ID", async () => { - await createWorkflow(); - const res = await request - .get(`/api/${instance._id}/workflows/${workflow.id}`) - .set(defaultHeaders) - .expect('Content-Type', /json/) - .expect(200) - - expect(res.body).toEqual(expect.objectContaining(TEST_WORKFLOW)); - }) - - it("should apply authorization to endpoint", async () => { - await createWorkflow(); - await builderEndpointShouldBlockNormalUsers({ - request, - method: "GET", - url: `/api/${instance._id}/workflows/${workflow.id}`, - instanceId: instance._id, - }) - }) - }) - describe("destroy", () => { it("deletes a workflow by its ID", async () => { await createWorkflow(); From 401b6689bc733463534ecfb2120bb3e7c9bb43e8 Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Mon, 1 Jun 2020 22:55:44 +0100 Subject: [PATCH 26/27] Record selector --- .../ParamInputs/ModelSelector.svelte | 1 + .../ParamInputs/RecordSelector.svelte | 33 +++++++++++++++++++ .../SetupPanel/WorkflowBlockSetup.svelte | 3 ++ .../BlockList/WorkflowBlock.svelte | 2 +- .../WorkflowPanel/blockDefinitions.js | 15 ++++++--- packages/client/src/state/setState.js | 2 +- packages/client/src/state/stateManager.js | 4 +-- 7 files changed, 52 insertions(+), 8 deletions(-) diff --git a/packages/builder/src/components/workflow/SetupPanel/ParamInputs/ModelSelector.svelte b/packages/builder/src/components/workflow/SetupPanel/ParamInputs/ModelSelector.svelte index 1c39e19505..29babefcd9 100644 --- a/packages/builder/src/components/workflow/SetupPanel/ParamInputs/ModelSelector.svelte +++ b/packages/builder/src/components/workflow/SetupPanel/ParamInputs/ModelSelector.svelte @@ -5,6 +5,7 @@
+
+ {#each $backendUiStore.models as model} + + {/each} + +
+
+ +{#if value.model} +
+ + {#each Object.keys(value.model.schema) as field} +
+ + +
+ {/each} +
+{/if} + + diff --git a/packages/builder/src/components/workflow/SetupPanel/WorkflowBlockSetup.svelte b/packages/builder/src/components/workflow/SetupPanel/WorkflowBlockSetup.svelte index d629630099..08bf2d72db 100644 --- a/packages/builder/src/components/workflow/SetupPanel/WorkflowBlockSetup.svelte +++ b/packages/builder/src/components/workflow/SetupPanel/WorkflowBlockSetup.svelte @@ -2,6 +2,7 @@ import { backendUiStore, store } from "builderStore" import ComponentSelector from "./ParamInputs/ComponentSelector.svelte" import ModelSelector from "./ParamInputs/ModelSelector.svelte" + import RecordSelector from "./ParamInputs/RecordSelector.svelte" export let workflowBlock @@ -51,6 +52,8 @@ bind:value={workflowBlock.args[parameter]} /> {:else if type === 'model'} + {:else if type === 'record'} + {:else if type === 'string'} {{model.name}}
record", + tagline: "Save a {{record.model.name}} record", icon: "ri-save-3-fill", description: "Save a record to your database.", environment: "SERVER", params: { - model: "model", + record: "record", + }, + args: { + record: {}, }, }, DELETE_RECORD: { description: "Delete a record from your database.", icon: "ri-delete-bin-line", name: "Delete Record", - tagline: "Delete a {{model.name}} record", + tagline: "Delete a {{record.model.name}} record", environment: "SERVER", params: { - record: "string", + record: "record", + }, + args: { + record: {}, }, }, FIND_RECORD: { description: "Find a record in your database.", + tagline: "Find a {{record.model.name}} record", icon: "ri-search-line", name: "Find Record", environment: "SERVER", diff --git a/packages/client/src/state/setState.js b/packages/client/src/state/setState.js index ad25b631d5..ac17b1a681 100644 --- a/packages/client/src/state/setState.js +++ b/packages/client/src/state/setState.js @@ -8,4 +8,4 @@ export const setState = (path, value) => { state = set(path, value, state) return state }) -} \ No newline at end of file +} diff --git a/packages/client/src/state/stateManager.js b/packages/client/src/state/stateManager.js index 5d32f5a486..5dd0d9957d 100644 --- a/packages/client/src/state/stateManager.js +++ b/packages/client/src/state/stateManager.js @@ -33,7 +33,7 @@ export const createStateManager = ({ const getCurrentState = () => currentState const bb = bbFactory({ - store: appStore, + store: appStore, getCurrentState, frontendDefinition, componentLibraries, @@ -65,7 +65,7 @@ const _setup = ({ handlerTypes, getCurrentState, bb, store }) => node => { const isBound = typeof propValue === "string" && propValue.startsWith("{{") if (isBound) { - console.log("NODE IS BOUND", node); + console.log("NODE IS BOUND", node) initialProps[propName] = mustache.render(propValue, { state: currentStoreState, context, From 3a983cea8a36bbe478042de6c1cb924b2293f589 Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Tue, 2 Jun 2020 11:08:53 +0100 Subject: [PATCH 27/27] tidy up, styling --- .../workflow/SetupPanel/SetupPanel.svelte | 44 ++++++++++--- .../WorkflowList/WorkflowList.svelte | 4 ++ .../WorkflowPanel/blockDefinitions.js | 20 +++--- packages/client/src/api/workflow/index.js | 8 +-- .../client/src/api/workflow/orchestrator.js | 66 +------------------ packages/client/src/state/stateManager.js | 1 - packages/server/src/api/controllers/record.js | 11 ++-- .../workflow/actions/CREATE_USER.js | 8 +-- .../controllers/workflow/actions/CUSTOM_JS.js | 9 --- .../workflow/actions/SAVE_RECORD.js | 21 ++++-- .../workflow/actions/SEND_EMAIL.js | 4 +- .../src/api/controllers/workflow/index.js | 8 ++- packages/server/src/api/routes/workflow.js | 2 +- packages/server/src/electron.js | 11 +++- 14 files changed, 98 insertions(+), 119 deletions(-) delete mode 100644 packages/server/src/api/controllers/workflow/actions/CUSTOM_JS.js diff --git a/packages/builder/src/components/workflow/SetupPanel/SetupPanel.svelte b/packages/builder/src/components/workflow/SetupPanel/SetupPanel.svelte index 1ae741b55a..676aa18d77 100644 --- a/packages/builder/src/components/workflow/SetupPanel/SetupPanel.svelte +++ b/packages/builder/src/components/workflow/SetupPanel/SetupPanel.svelte @@ -9,6 +9,17 @@ const { open, close } = getContext("simple-modal") + const ACCESS_LEVELS = [ + { + name: "Admin", + key: "ADMIN" + }, + { + name: "Power User", + key: "POWER_USER" + } + ]; + let selectedTab = "SETUP" let testResult @@ -92,16 +103,14 @@
- -
- -
+
+ {#each ACCESS_LEVELS as { name, key }} + + + + + {/each} +