This commit is contained in:
Martin McKeaveney 2020-06-01 21:26:32 +01:00
parent 398f200661
commit aeae4f50e2
26 changed files with 156 additions and 395 deletions

View File

@ -54,7 +54,6 @@
"safe-buffer": "^5.1.2", "safe-buffer": "^5.1.2",
"shortid": "^2.2.8", "shortid": "^2.2.8",
"string_decoder": "^1.2.0", "string_decoder": "^1.2.0",
"svelte-grid": "^1.10.8",
"svelte-simple-modal": "^0.3.0", "svelte-simple-modal": "^0.3.0",
"uikit": "^3.1.7" "uikit": "^3.1.7"
}, },

View File

@ -17,9 +17,9 @@ export default class Workflow {
addBlock(block) { addBlock(block) {
// Make sure to add trigger if doesn't exist // 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 } this.workflow.definition.trigger = { id: generate(), ...block }
return; return
} }
this.workflow.definition.steps.push({ this.workflow.definition.steps.push({
@ -60,7 +60,10 @@ export default class Workflow {
} }
static buildUiTree(definition) { 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 // The client side display definition for the block
const definition = blockDefinitions[step.type][step.actionId] const definition = blockDefinitions[step.type][step.actionId]
if (!definition) { if (!definition) {
@ -88,9 +91,5 @@ export default class Workflow {
name: definition.name, name: definition.name,
} }
}) })
console.log(definition);
return definition.trigger ? [definition.trigger, ...steps] : steps
} }
} }

View File

@ -16,9 +16,9 @@ const workflowActions = store => ({
const workflow = { const workflow = {
name, name,
definition: { definition: {
steps: [] steps: [],
},
} }
};
const CREATE_WORKFLOW_URL = `/api/${instanceId}/workflows` const CREATE_WORKFLOW_URL = `/api/${instanceId}/workflows`
const response = await api.post(CREATE_WORKFLOW_URL, workflow) const response = await api.post(CREATE_WORKFLOW_URL, workflow)
const json = await response.json() const json = await response.json()

View File

@ -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

View File

@ -1,13 +1,8 @@
import { eventHandlers } from "../../../../client/src/state/eventHandlers" import { eventHandlers } from "../../../../client/src/state/eventHandlers"
import { writable } from "svelte/store"
export { EVENT_TYPE_MEMBER_NAME } from "../../../../client/src/state/eventHandlers" export { EVENT_TYPE_MEMBER_NAME } from "../../../../client/src/state/eventHandlers"
export const allHandlers = user => { export const allHandlers = () => {
const store = writable({ const handlersObj = eventHandlers()
_bbuser: user,
})
const handlersObj = eventHandlers(store)
const handlers = Object.keys(handlersObj).map(name => ({ const handlers = Object.keys(handlersObj).map(name => ({
name, name,

View File

@ -64,7 +64,6 @@
<NumberBox label="Max Length" bind:value={constraints.length.maximum} /> <NumberBox label="Max Length" bind:value={constraints.length.maximum} />
<ValuesList label="Categories" bind:values={constraints.inclusion} /> <ValuesList label="Categories" bind:values={constraints.inclusion} />
{:else if type === 'datetime'} {:else if type === 'datetime'}
<!-- TODO: revisit and fix with JSON schema -->
<DatePicker <DatePicker
label="Min Value" label="Min Value"
bind:value={constraints.datetime.earliest} /> bind:value={constraints.datetime.earliest} />

View File

@ -4,7 +4,6 @@ export default ({
selectedComponentType, selectedComponentType,
selectedComponentId, selectedComponentId,
frontendDefinition, frontendDefinition,
currentPageFunctions,
}) => `<html> }) => `<html>
<head> <head>
${stylesheetLinks} ${stylesheetLinks}

View File

@ -6,8 +6,7 @@
import { find, map, keys, reduce, keyBy } from "lodash/fp" import { find, map, keys, reduce, keyBy } from "lodash/fp"
import { pipe } from "components/common/core" import { pipe } from "components/common/core"
import { import {
EVENT_TYPE_MEMBER_NAME, EVENT_TYPE_MEMBER_NAME
allHandlers,
} from "components/common/eventHandlers" } from "components/common/eventHandlers"
import { store, workflowStore } from "builderStore" import { store, workflowStore } from "builderStore"
import { ArrowDownIcon } from "components/common/Icons/" import { ArrowDownIcon } from "components/common/Icons/"
@ -26,7 +25,7 @@
class="budibase__input" class="budibase__input"
on:change={onChange} on:change={onChange}
bind:value={parameter.value}> bind:value={parameter.value}>
{#each $workflowStore.workflows as workflow} {#each $workflowStore.workflows.filter(wf => wf.live) as workflow}
<option value={workflow._id}>{workflow.name}</option> <option value={workflow._id}>{workflow.name}</option>
{/each} {/each}
</select> </select>

View File

@ -5,7 +5,6 @@
</script> </script>
<div class="uk-margin block-field"> <div class="uk-margin block-field">
<label class="uk-form-label">Page</label>
<div class="uk-form-controls"> <div class="uk-form-controls">
<select class="budibase__input" bind:value> <select class="budibase__input" bind:value>
{#each $backendUiStore.models as model} {#each $backendUiStore.models as model}

View File

@ -1,4 +1,5 @@
<script> <script>
import { fade } from 'svelte/transition';
import { onMount, getContext } from "svelte" import { onMount, getContext } from "svelte"
import { backendUiStore, workflowStore } from "builderStore" import { backendUiStore, workflowStore } from "builderStore"
import { notifier } from "@beyonk/svelte-notifications" import { notifier } from "@beyonk/svelte-notifications"
@ -8,6 +9,9 @@
const { open, close } = getContext("simple-modal") const { open, close } = getContext("simple-modal")
let selectedTab = "SETUP"
let testResult
$: workflow = $: workflow =
$workflowStore.currentWorkflow && $workflowStore.currentWorkflow.workflow $workflowStore.currentWorkflow && $workflowStore.currentWorkflow.workflow
$: workflowBlock = $workflowStore.selectedWorkflowBlock $: workflowBlock = $workflowStore.selectedWorkflowBlock
@ -26,23 +30,58 @@
workflowStore.actions.deleteWorkflowBlock(workflowBlock) workflowStore.actions.deleteWorkflowBlock(workflowBlock)
notifier.info("Workflow block deleted.") notifier.info("Workflow block deleted.")
} }
function testWorkflow() {
testResult = "PASSED"
}
</script> </script>
<section> <section>
<header> <header>
<span>Setup</span> <span
class="hoverable"
class:selected={selectedTab === 'SETUP'}
on:click={() => {
selectedTab = 'SETUP'
testResult = null
}}>
Setup
</span>
{#if !workflowBlock}
<span
class="hoverable"
class:selected={selectedTab === 'TEST'}
on:click={() => (selectedTab = 'TEST')}>
Test
</span>
{/if}
</header> </header>
{#if selectedTab === 'TEST'}
<div class="uk-margin config-item">
{#if testResult}
<button
transition:fade
class:passed={testResult === 'PASSED'}
class:failed={testResult === 'FAILED'}
class="test-result">
{testResult}
</button>
{/if}
<button class="workflow-button hoverable" on:click={testWorkflow}>
Test
</button>
</div>
{/if}
{#if selectedTab === 'SETUP'}
{#if workflowBlock} {#if workflowBlock}
<WorkflowBlockSetup {workflowBlock} /> <WorkflowBlockSetup {workflowBlock} />
<button <button class="workflow-button hoverable" on:click={deleteWorkflowBlock}>
class="delete-workflow-button hoverable"
on:click={deleteWorkflowBlock}>
Delete Block Delete Block
</button> </button>
{:else if $workflowStore.currentWorkflow} {:else if $workflowStore.currentWorkflow}
<div class="panel-body"> <div class="panel-body">
<label class="uk-form-label">Workflow: {workflow.name}</label> <label class="uk-form-label">Workflow: {workflow.name}</label>
<div class="uk-margin"> <div class="uk-margin config-item">
<label class="uk-form-label">Name</label> <label class="uk-form-label">Name</label>
<div class="uk-form-controls"> <div class="uk-form-controls">
<input <input
@ -51,15 +90,27 @@
bind:value={workflow.name} /> bind:value={workflow.name} />
</div> </div>
</div> </div>
<div class="uk-margin"> <div class="uk-margin config-item">
<label class="uk-form-label">User Access</label> <label class="uk-form-label">User Access</label>
Some User Access Stuff Here <label>
<input class="uk-checkbox" type="checkbox" name="radio1" />
Admin
</label>
<br />
<label>
<input class="uk-checkbox" type="checkbox" name="radio1" />
Power User
</label>
<br />
</div> </div>
</div> </div>
<button class="delete-workflow-button hoverable" on:click={deleteWorkflow}> <button
class="workflow-button hoverable"
on:click={deleteWorkflow}>
Delete Workflow Delete Workflow
</button> </button>
{/if} {/if}
{/if}
</section> </section>
<style> <style>
@ -78,21 +129,30 @@
font-weight: bold; font-weight: bold;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between;
margin-bottom: 20px; margin-bottom: 20px;
} }
header > span { .selected {
color: var(--font); color: var(--font);
} }
.config-item {
padding: 20px;
background: var(--light-grey);
}
header > span {
color: var(--dark-grey);
margin-right: 20px;
}
label { label {
font-weight: 500; font-weight: 500;
font-size: 14px; font-size: 14px;
color: var(--font); color: var(--font);
} }
.delete-workflow-button { .workflow-button {
font-family: Roboto; font-family: Roboto;
width: 100%; width: 100%;
border: solid 1px #f2f2f2; border: solid 1px #f2f2f2;
@ -102,4 +162,24 @@
font-size: 12px; font-size: 12px;
font-weight: 500; 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);
}
</style> </style>

View File

@ -76,5 +76,7 @@
textarea { textarea {
min-height: 150px; min-height: 150px;
font-family: inherit;
padding: 5px;
} }
</style> </style>

View File

@ -28,7 +28,6 @@
async function saveWorkflow() { async function saveWorkflow() {
const workflow = $workflowStore.currentWorkflow.workflow const workflow = $workflowStore.currentWorkflow.workflow
// TODO: Clean up args
await workflowStore.actions.save({ await workflowStore.actions.save({
instanceId: $backendUiStore.selectedDatabase._id, instanceId: $backendUiStore.selectedDatabase._id,
workflow, workflow,

View File

@ -22,17 +22,19 @@ const ACTION = {
}, },
SAVE_RECORD: { SAVE_RECORD: {
name: "Save Record", name: "Save Record",
tagline: "Save a <b>{{model.name}}</b> record",
icon: "ri-save-3-fill", icon: "ri-save-3-fill",
description: "Save a record to your database.", description: "Save a record to your database.",
environment: "SERVER", environment: "SERVER",
params: { params: {
model: "string", model: "model",
}, },
}, },
DELETE_RECORD: { DELETE_RECORD: {
description: "Delete a record from your database.", description: "Delete a record from your database.",
icon: "ri-delete-bin-line", icon: "ri-delete-bin-line",
name: "Delete Record", name: "Delete Record",
tagline: "Delete a <b>{{model.name}}</b> record",
environment: "SERVER", environment: "SERVER",
params: { params: {
record: "string", record: "string",

View File

@ -1,6 +1,4 @@
import { get } from "svelte/store"
import { setState } from "../../state/setState" import { setState } from "../../state/setState"
import { appStore } from "../../state/store"
const delay = ms => new Promise(resolve => setTimeout(resolve, ms)) const delay = ms => new Promise(resolve => setTimeout(resolve, ms))
@ -12,11 +10,11 @@ export default {
[id]: args, [id]: args,
} }
}, },
NAVIGATE: ({ context, args, id }) => { NAVIGATE: () => {
// TODO client navigation // TODO client navigation
}, },
DELAY: async ({ context, args }) => await delay(args.time), DELAY: async ({ args }) => await delay(args.time),
FILTER: ({ context, args }) => { FILTER: ({ args }) => {
const { field, condition, value } = args const { field, condition, value } = args
switch (condition) { switch (condition) {
case "equals": case "equals":

View File

@ -1,7 +1,7 @@
import { get } from "svelte/store" import { get } from "svelte/store"
import mustache from "mustache" import mustache from "mustache"
import { appStore } from "../../state/store" import { appStore } from "../../state/store"
import Orchestrator from "./orchestrator"; import Orchestrator from "./orchestrator"
import clientActions from "./actions" import clientActions from "./actions"
// Execute a workflow from a running budibase app // Execute a workflow from a running budibase app
@ -28,8 +28,6 @@ export const clientStrategy = ({ api, instanceId }) => ({
}, },
run: async function(workflow) { run: async function(workflow) {
for (let block of workflow.steps) { for (let block of workflow.steps) {
console.log("Executing workflow block", block)
// This code gets run in the browser // This code gets run in the browser
if (block.environment === "CLIENT") { if (block.environment === "CLIENT") {
const action = clientActions[block.actionId] const action = clientActions[block.actionId]
@ -60,13 +58,11 @@ export const clientStrategy = ({ api, instanceId }) => ({
}, },
}) })
export const triggerWorkflow = api => async ({ workflow }) => { export const triggerWorkflow = api => async ({ workflow, instanceId }) => {
const instanceId = "inst_ad75c7f_4f3e7d5d80a74b17a5187a18e2aba85e";
const workflowOrchestrator = new Orchestrator(api, instanceId) const workflowOrchestrator = new Orchestrator(api, instanceId)
workflowOrchestrator.strategy = clientStrategy 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 }) const workflowDefinition = await api.get({ url: EXECUTE_WORKFLOW_URL })
workflowOrchestrator.execute(workflowDefinition) workflowOrchestrator.execute(workflowDefinition)

View File

@ -1,4 +1,3 @@
import { writable } from "svelte/store"
import { attachChildren } from "./render/attachChildren" import { attachChildren } from "./render/attachChildren"
import { createTreeNode } from "./render/prepareRenderComponent" import { createTreeNode } from "./render/prepareRenderComponent"
import { screenRouter } from "./render/screenRouter" import { screenRouter } from "./render/screenRouter"
@ -7,7 +6,6 @@ import { createStateManager } from "./state/stateManager"
export const createApp = ({ export const createApp = ({
componentLibraries, componentLibraries,
frontendDefinition, frontendDefinition,
user,
window, window,
}) => { }) => {
let routeTo let routeTo

View File

@ -1,8 +1,6 @@
import regexparam from "regexparam" import regexparam from "regexparam"
import { routerStore } from "../state/store" import { routerStore } from "../state/store"
import { initRouteStore } from "../state/store"
// TODO: refactor
export const screenRouter = ({ screens, onScreenSelected, appRootPath }) => { export const screenRouter = ({ screens, onScreenSelected, appRootPath }) => {
const makeRootedPath = url => { const makeRootedPath = url => {
if (appRootPath) { if (appRootPath) {

View File

@ -6,17 +6,12 @@ import { createApi } from "../api"
export const EVENT_TYPE_MEMBER_NAME = "##eventHandlerType" export const EVENT_TYPE_MEMBER_NAME = "##eventHandlerType"
export const eventHandlers = (store, rootPath, routeTo) => { export const eventHandlers = (rootPath, routeTo) => {
const handler = (parameters, execute) => ({ const handler = (parameters, execute) => ({
execute, execute,
parameters, parameters,
}) })
let currentState
store.subscribe(state => {
currentState = state
})
const api = createApi({ const api = createApi({
rootPath, rootPath,
setState, setState,

View File

@ -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,
// }
// }

View File

@ -21,27 +21,16 @@ const isMetaProp = propName =>
propName === "_styles" propName === "_styles"
export const createStateManager = ({ export const createStateManager = ({
// store,
appRootPath, appRootPath,
frontendDefinition, frontendDefinition,
componentLibraries, componentLibraries,
onScreenSlotRendered, onScreenSlotRendered,
routeTo, routeTo,
}) => { }) => {
let handlerTypes = eventHandlers(appStore, appRootPath, routeTo) let handlerTypes = eventHandlers(appRootPath, routeTo)
let currentState 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 getCurrentState = () => currentState
// const registerBindings = _registerBindings(
// nodesBoundByProps,
// nodesWithCodeBoundChildren
// )
const bb = bbFactory({ const bb = bbFactory({
store: appStore, store: appStore,
@ -53,131 +42,26 @@ export const createStateManager = ({
const setup = _setup({ handlerTypes, getCurrentState, bb, store: appStore }) 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 { return {
setup, setup,
destroy: () => unsubscribe(), destroy: () => {},
getCurrentState, getCurrentState,
store: appStore, 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 _setup = ({ handlerTypes, getCurrentState, bb, store }) => node => {
const props = node.props const props = node.props
const context = node.context || {} const context = node.context || {}
const initialProps = { ...props } const initialProps = { ...props }
// const storeBoundProps = []
const currentStoreState = get(appStore) const currentStoreState = get(appStore)
console.log("node", node)
// console.log("node", node);
// console.log("nodeComponent", node.component);
for (let propName in props) { for (let propName in props) {
if (isMetaProp(propName)) continue if (isMetaProp(propName)) continue
const propValue = props[propName] const propValue = props[propName]
// const binding = parseBinding(propValue) // A little bit of a hack - won't bind if the string doesn't start with {{
// TODO: better binding stuff
const isBound = typeof propValue === "string" && propValue.startsWith("{{") const isBound = typeof propValue === "string" && propValue.startsWith("{{")
if (isBound) { 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)) { if (isEventType(propValue)) {
const handlersInfos = [] const handlersInfos = []
for (let event of propValue) { for (let event of propValue) {
@ -228,21 +91,6 @@ const _setup = ({ handlerTypes, getCurrentState, bb, store }) => node => {
state: getCurrentState(), state: getCurrentState(),
context, 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 handlerInfo.parameters = resolvedParams
@ -262,8 +110,6 @@ const _setup = ({ handlerTypes, getCurrentState, bb, store }) => node => {
} }
} }
// registerBindings(node, storeBoundProps)
const setup = _setup({ handlerTypes, getCurrentState, bb, store }) const setup = _setup({ handlerTypes, getCurrentState, bb, store })
initialProps._bb = bb(node, setup) initialProps._bb = bb(node, setup)

View File

@ -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("", () => {
});
});

View File

@ -11,7 +11,8 @@ const controller = {
if ( if (
!name.startsWith("all") && !name.startsWith("all") &&
name !== "by_type" && name !== "by_type" &&
name !== "by_username" name !== "by_username" &&
name !== "by_workflow_trigger"
) { ) {
response.push({ response.push({
name, name,

View File

@ -1,5 +1,5 @@
const CouchDB = require("../../db") const CouchDB = require("../../../db")
const newid = require("../../db/newid") const newid = require("../../../db/newid")
exports.create = async function(ctx) { exports.create = async function(ctx) {
const db = new CouchDB(ctx.params.instanceId) const db = new CouchDB(ctx.params.instanceId)
@ -49,7 +49,7 @@ exports.fetch = async function(ctx) {
} }
exports.find = 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) ctx.body = await db.get(ctx.params.id)
} }
@ -61,7 +61,6 @@ exports.executeAction = async function(ctx) {
exports.fetchActionScript = async function(ctx) { exports.fetchActionScript = async function(ctx) {
const workflowAction = require(`./actions/${ctx.action}`) const workflowAction = require(`./actions/${ctx.action}`)
console.log(workflowAction)
ctx.body = workflowAction ctx.body = workflowAction
} }

View File

@ -1,13 +1,13 @@
const Router = require("@koa/router") const Router = require("@koa/router")
const controller = require("../controllers/workflow") const controller = require("../controllers/workflow")
const authorized = require("../../middleware/authorized") const authorized = require("../../middleware/authorized")
const { BUILDER, EXECUTE_WORKFLOW } = require("../../utilities/accessLevels") const { BUILDER } = require("../../utilities/accessLevels")
const router = Router() const router = Router()
router router
.get("/api/:instanceId/workflows", authorized(BUILDER), controller.fetch) .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( .get(
"/api/:instanceId/workflows/:id/:action", "/api/:instanceId/workflows/:id/:action",
authorized(BUILDER), authorized(BUILDER),

View File

@ -1,6 +1,6 @@
const EventEmitter = require("events").EventEmitter const EventEmitter = require("events").EventEmitter
const CouchDB = require("../db") const CouchDB = require("../db")
const { Orchestrator, serverStrategy } = require("./workflow"); const { Orchestrator, serverStrategy } = require("./workflow")
const emitter = new EventEmitter() const emitter = new EventEmitter()
@ -8,7 +8,7 @@ async function executeRelevantWorkflows(event, eventType) {
const db = new CouchDB(event.instanceId) const db = new CouchDB(event.instanceId)
const workflowsToTrigger = await db.query("database/by_workflow_trigger", { const workflowsToTrigger = await db.query("database/by_workflow_trigger", {
key: [eventType], key: [eventType],
include_docs: true include_docs: true,
}) })
const workflows = workflowsToTrigger.rows.map(wf => wf.doc) const workflows = workflowsToTrigger.rows.map(wf => wf.doc)
@ -22,12 +22,12 @@ async function executeRelevantWorkflows(event, eventType) {
} }
} }
emitter.on("record:save", async function(event) { emitter.on("action", async function(event) {
await executeRelevantWorkflows(event, "record:save"); await executeRelevantWorkflows(event, "record:save")
}) })
emitter.on("record:delete", async function(event) { emitter.on("record:delete", async function(event) {
await executeRelevantWorkflows(event, "record:delete"); await executeRelevantWorkflows(event, "record:delete")
}) })
module.exports = emitter module.exports = emitter

View File

@ -37,7 +37,6 @@ exports.serverStrategy = () => ({
}, },
run: async function(workflow) { run: async function(workflow) {
for (let block of workflow.steps) { for (let block of workflow.steps) {
console.log("Executing workflow block", block)
if (block.type === "CLIENT") continue if (block.type === "CLIENT") continue
const action = require(`../api/controllers/workflow/actions/${block.actionId}`) const action = require(`../api/controllers/workflow/actions/${block.actionId}`)
@ -48,5 +47,5 @@ exports.serverStrategy = () => ({
[block.id]: response, [block.id]: response,
} }
} }
} },
}); })