move workflow to array data structure

This commit is contained in:
Martin McKeaveney 2020-06-01 10:41:28 +01:00
parent dc90e141f5
commit a220822e3a
36 changed files with 257 additions and 232 deletions

View File

@ -152,7 +152,7 @@ export default {
{ {
find: "builderStore", find: "builderStore",
replacement: path.resolve(projectRootDir, "src/builderStore"), replacement: path.resolve(projectRootDir, "src/builderStore"),
} },
], ],
customResolver, customResolver,
}), }),

View File

@ -7,8 +7,7 @@
import AppNotification, { import AppNotification, {
showAppNotification, showAppNotification,
} from "components/common/AppNotification.svelte" } from "components/common/AppNotification.svelte"
import { NotificationDisplay } from '@beyonk/svelte-notifications' import { NotificationDisplay } from "@beyonk/svelte-notifications"
function showErrorBanner() { function showErrorBanner() {
showAppNotification({ showAppNotification({

View File

@ -13,82 +13,109 @@ export default class Workflow {
} }
isEmpty() { isEmpty() {
return !this.workflow.definition.next // return this.workflow.definition.next
return this.workflow.length > 0
} }
addBlock(block) { addBlock(block) {
let node = this.workflow.definition // Make sure to add trigger if doesn't exist
while (node.next) node = node.next this.workflow.definition.steps.push({
node.next = {
id: generate(), id: generate(),
...block ...block,
} })
} }
updateBlock(updatedBlock, id) { updateBlock(updatedBlock, id) {
let block = this.workflow.definition const { steps, trigger } = this.workflow.definition
while (block.id !== id) block = block.next // if the block is a trigger do X
if (!block) throw new Error("Block not found.")
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) { deleteBlock(id) {
let previous = null const { steps, trigger } = this.workflow.definition
let block = this.workflow.definition
// iterate through the blocks const stepIdx = steps.findIndex(step => step.id === id)
while (block.id !== id) {
previous = block
block = block.next
}
// delete the block matching your id if (stepIdx < 0) throw new Error("Block not found.")
if (!block.next) {
delete previous.next
} else {
previous.next = block.next
}
steps.splice(stepIdx, 1)
} }
createUiTree() { createUiTree() {
if (!this.workflow.definition.next) return [] if (!this.workflow.definition) return []
return Workflow.buildUiTree(this.workflow.definition.next) return Workflow.buildUiTree(this.workflow.definition)
} }
static buildUiTree(block, tree = []) { static buildUiTree(definition) {
if (!block) return tree return definition.steps.map(step => {
// The client side display definition for the block // The client side display definition for the block
const definition = blockDefinitions[block.type][block.actionId] const definition = blockDefinitions[step.type][step.actionId]
if (!definition) { if (!definition) {
throw new Error( throw new Error(
`No block definition exists for the chosen block. Check there's an entry in the block definitions for ${block.actionId}` `No block definition exists for the chosen block. Check there's an entry in the block definitions for ${step.actionId}`
) )
} }
if (!definition.params) { if (!definition.params) {
throw new Error( throw new Error(
`Blocks should always have parameters. Ensure that the block definition is correct for ${block.actionId}` `Blocks should always have parameters. Ensure that the block definition is correct for ${step.actionId}`
) )
} }
const tagline = definition.tagline || "" const tagline = definition.tagline || ""
const args = block.args || {} const args = step.args || {}
// all the fields the workflow block needs to render in the UI return {
tree.push({ id: step.id,
id: block.id, type: step.type,
type: block.type, params: step.params,
params: block.params,
args, args,
heading: block.actionId, heading: step.actionId,
body: mustache.render(tagline, args), body: mustache.render(tagline, args),
name: definition.name 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)
// }
} }

View File

@ -13,7 +13,8 @@ const workflowActions = store => ({
}) })
}, },
create: async ({ instanceId, name }) => { 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 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

@ -116,7 +116,7 @@
stylesheetLinks, stylesheetLinks,
selectedComponentType, selectedComponentType,
selectedComponentId, selectedComponentId,
frontendDefinition: JSON.stringify(frontendDefinition) frontendDefinition: JSON.stringify(frontendDefinition),
})} /> })} />
{/if} {/if}
</div> </div>

View File

@ -17,14 +17,16 @@
export let onChange export let onChange
let isOpen = false let isOpen = false
</script> </script>
<div class="handler-option"> <div class="handler-option">
<span>{parameter.name}</span> <span>{parameter.name}</span>
<div class="handler-input"> <div class="handler-input">
{#if parameter.name === 'workflow'} {#if parameter.name === 'workflow'}
<select class="budibase__input" on:change={onChange} bind:value={parameter.value}> <select
class="budibase__input"
on:change={onChange}
bind:value={parameter.value}>
{#each $workflowStore.workflows as workflow} {#each $workflowStore.workflows as workflow}
<option value={workflow._id}>{workflow.name}</option> <option value={workflow._id}>{workflow.name}</option>
{/each} {/each}

View File

@ -1,6 +1,6 @@
<script> <script>
import { store, backendUiStore, workflowStore } from "builderStore" import { store, backendUiStore, workflowStore } from "builderStore"
import { notifier } from '@beyonk/svelte-notifications' import { notifier } from "@beyonk/svelte-notifications"
import api from "builderStore/api" import api from "builderStore/api"
import ActionButton from "components/common/ActionButton.svelte" import ActionButton from "components/common/ActionButton.svelte"

View File

@ -1,6 +1,6 @@
<script> <script>
import { store } from "builderStore" import { store } from "builderStore"
import deepmerge from "deepmerge"; import deepmerge from "deepmerge"
export let value export let value

View File

@ -1,4 +1,3 @@
<script> <script>
import { backendUiStore } from "builderStore" import { backendUiStore } from "builderStore"

View File

@ -1,7 +1,7 @@
<script> <script>
import { backendUiStore, store } from "builderStore" import { backendUiStore, store } from "builderStore"
import ComponentSelector from "./ParamInputs/ComponentSelector.svelte"; import ComponentSelector from "./ParamInputs/ComponentSelector.svelte"
import ModelSelector from "./ParamInputs/ModelSelector.svelte"; import ModelSelector from "./ParamInputs/ModelSelector.svelte"
export let workflowBlock export let workflowBlock
@ -12,9 +12,7 @@
: [] : []
</script> </script>
<label class="uk-form-label"> <label class="uk-form-label">{workflowBlock.type}: {workflowBlock.name}</label>
{workflowBlock.type}: {workflowBlock.name}
</label>
{#each workflowParams as [parameter, type]} {#each workflowParams as [parameter, type]}
<div class="uk-margin block-field"> <div class="uk-margin block-field">
<label class="uk-form-label">{parameter}</label> <label class="uk-form-label">{parameter}</label>

View File

@ -1,6 +1,6 @@
<script> <script>
import FlowItem from "./FlowItem.svelte" import FlowItem from "./FlowItem.svelte"
import Arrow from "./Arrow.svelte"; import Arrow from "./Arrow.svelte"
export let blocks = [] export let blocks = []
export let onSelect export let onSelect

View File

@ -12,7 +12,10 @@
$: definitions = Object.entries(blockDefinitions[selectedTab]) $: definitions = Object.entries(blockDefinitions[selectedTab])
$: { $: {
if (!$workflowStore.currentWorkflow.isEmpty() && selectedTab === "TRIGGER") { if (
!$workflowStore.currentWorkflow.isEmpty() &&
selectedTab === "TRIGGER"
) {
selectedTab = "ACTION" selectedTab = "ACTION"
} }
} }

View File

@ -1,6 +1,6 @@
<script> <script>
import { store, backendUiStore, workflowStore } from "builderStore" import { store, backendUiStore, workflowStore } from "builderStore"
import { notifier } from '@beyonk/svelte-notifications' import { notifier } from "@beyonk/svelte-notifications"
import api from "builderStore/api" import api from "builderStore/api"
import ActionButton from "components/common/ActionButton.svelte" import ActionButton from "components/common/ActionButton.svelte"

View File

@ -1,6 +1,6 @@
<script> <script>
import Modal from "svelte-simple-modal" import Modal from "svelte-simple-modal"
import { notifier } from "@beyonk/svelte-notifications"; import { notifier } from "@beyonk/svelte-notifications"
import { onMount, getContext } from "svelte" import { onMount, getContext } from "svelte"
import { backendUiStore, workflowStore } from "builderStore" import { backendUiStore, workflowStore } from "builderStore"
import api from "builderStore/api" import api from "builderStore/api"
@ -9,7 +9,8 @@
const { open, close } = getContext("simple-modal") const { open, close } = getContext("simple-modal")
$: currentWorkflowId = $: currentWorkflowId =
$workflowStore.currentWorkflow && $workflowStore.currentWorkflow.workflow._id $workflowStore.currentWorkflow &&
$workflowStore.currentWorkflow.workflow._id
function newWorkflow() { function newWorkflow() {
open( open(
@ -30,9 +31,9 @@
// TODO: Clean up args // TODO: Clean up args
await workflowStore.actions.save({ await workflowStore.actions.save({
instanceId: $backendUiStore.selectedDatabase._id, instanceId: $backendUiStore.selectedDatabase._id,
workflow workflow,
}) })
notifier.success(`Workflow ${workflow.name} saved.`); notifier.success(`Workflow ${workflow.name} saved.`)
} }
</script> </script>

View File

@ -94,7 +94,7 @@ const TRIGGER = {
description: "Fired when a record is deleted from your database.", description: "Fired when a record is deleted from your database.",
environment: "SERVER", environment: "SERVER",
params: { params: {
model: "model" model: "model",
}, },
}, },
// CLICK: { // CLICK: {
@ -138,10 +138,8 @@ const LOGIC = {
environment: "CLIENT", environment: "CLIENT",
params: { params: {
filter: "string", filter: "string",
condition: [ condition: ["equals"],
"equals", value: "string",
],
value: "string"
}, },
}, },
DELAY: { DELAY: {

View File

@ -1,3 +1,3 @@
export { default as WorkflowBuilder } from "./WorkflowBuilder/WorkflowBuilder.svelte"; export { default as WorkflowBuilder } from "./WorkflowBuilder/WorkflowBuilder.svelte"
export { default as SetupPanel } from "./SetupPanel/SetupPanel.svelte"; export { default as SetupPanel } from "./SetupPanel/SetupPanel.svelte"
export { default as WorkflowPanel } from "./WorkflowPanel/WorkflowPanel.svelte"; export { default as WorkflowPanel } from "./WorkflowPanel/WorkflowPanel.svelte"

View File

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

View File

@ -3,7 +3,7 @@ import Orchestrator, { clientStrategy } from "./orchestrator"
export const triggerWorkflow = api => ({ workflow }) => { export const triggerWorkflow = api => ({ workflow }) => {
const workflowOrchestrator = new Orchestrator( const workflowOrchestrator = new Orchestrator(
api, api,
"inst_60dd510_700f7dc06735403e81d5af91072d7241" "inst_ad75c7f_4f3e7d5d80a74b17a5187a18e2aba85e"
) )
workflowOrchestrator.strategy = clientStrategy workflowOrchestrator.strategy = clientStrategy

View File

@ -1,7 +1,7 @@
import { get } from "svelte/store"; import { get } from "svelte/store"
import { setState } from "../../state/setState"; import mustache from "mustache"
import mustache from "mustache"; import { appStore } from "../../state/store"
import { appStore } from "../../state/store"; import clientActions from "./actions"
/** /**
* The workflow orchestrator is a class responsible for executing workflows. * The workflow orchestrator is a class responsible for executing workflows.
@ -17,7 +17,7 @@ export default class Orchestrator {
} }
set strategy(strategy) { set strategy(strategy) {
this._strategy = strategy({ api: this.api, instanceId: this.instanceId }); this._strategy = strategy({ api: this.api, instanceId: this.instanceId })
} }
async execute(workflowId) { async execute(workflowId) {
@ -32,64 +32,38 @@ export default class Orchestrator {
// Execute a workflow from a running budibase app // Execute a workflow from a running budibase app
export const clientStrategy = ({ api, instanceId }) => ({ export const clientStrategy = ({ api, instanceId }) => ({
delay: ms => new Promise(resolve => setTimeout(resolve, ms)),
context: {}, context: {},
bindContextArgs: function(args) { bindContextArgs: function(args) {
const mappedArgs = { ...args } const mappedArgs = { ...args }
console.log("original args", args)
// bind the workflow action args to the workflow context, if required // bind the workflow action args to the workflow context, if required
for (let arg in args) { for (let arg in args) {
const argValue = args[arg] 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 // Means that it's bound to state or workflow context
console.log(argValue, get(appStore));
mappedArgs[arg] = mustache.render(argValue, { mappedArgs[arg] = mustache.render(argValue, {
context: this.context, context: this.context,
state: get(appStore) state: get(appStore),
}); })
} }
console.log(mappedArgs)
return mappedArgs return mappedArgs
}, },
run: async function(workflow) { 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)
if (!block) return
// This code gets run in the browser // This code gets run in the browser
if (block.environment === "CLIENT") { if (block.environment === "CLIENT") {
if (block.actionId === "SET_STATE") { const action = clientActions[block.actionId]
// get props from the workflow context if required await action({
setState(...Object.values(this.bindContextArgs(block.args))) context: this.context,
// update the context with the data args: this.bindContextArgs(block.args),
this.context = { id: block.id,
...this.context, })
SET_STATE: block.args,
}
}
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;
}
}
} }
// this workflow block gets executed on the server // this workflow block gets executed on the server
@ -110,7 +84,6 @@ export const clientStrategy = ({ api, instanceId }) => ({
} }
console.log("workflowContext", this.context) console.log("workflowContext", this.context)
}
await this.run(workflow.next)
}, },
}) })

View File

@ -8,7 +8,7 @@ export const createApp = ({
componentLibraries, componentLibraries,
frontendDefinition, frontendDefinition,
user, user,
window window,
}) => { }) => {
let routeTo let routeTo
let currentUrl let currentUrl
@ -38,7 +38,7 @@ export const createApp = ({
routeTo = screenRouter({ routeTo = screenRouter({
screens: frontendDefinition.screens, screens: frontendDefinition.screens,
onScreenSelected, onScreenSelected,
appRootPath: frontendDefinition.appRootPath appRootPath: frontendDefinition.appRootPath,
}) })
const fallbackPath = window.location.pathname.replace( const fallbackPath = window.location.pathname.replace(
frontendDefinition.appRootPath, frontendDefinition.appRootPath,

View File

@ -39,7 +39,7 @@ export const loadBudibase = async opts => {
componentLibraries: componentLibraryModules, componentLibraries: componentLibraryModules,
frontendDefinition, frontendDefinition,
user, user,
window window,
}) })
const route = _window.location const route = _window.location

View File

@ -42,7 +42,7 @@ export const attachChildren = initialiseOpts => (htmlElement, options) => {
parentNode: treeNode, parentNode: treeNode,
ComponentConstructor, ComponentConstructor,
htmlElement, htmlElement,
anchor anchor,
}) })
for (let childNode of childNodesThisIteration) { for (let childNode of childNodesThisIteration) {

View File

@ -1,12 +1,12 @@
import { appStore } from "../state/store" import { appStore } from "../state/store"
import mustache from "mustache"; import mustache from "mustache"
export const prepareRenderComponent = ({ export const prepareRenderComponent = ({
ComponentConstructor, ComponentConstructor,
htmlElement, htmlElement,
anchor, anchor,
props, props,
parentNode parentNode,
}) => { }) => {
const parentContext = (parentNode && parentNode.context) || {} const parentContext = (parentNode && parentNode.context) || {}
@ -42,14 +42,16 @@ export const prepareRenderComponent = ({
// make this node listen to the store // make this node listen to the store
if (thisNode.stateBound) { if (thisNode.stateBound) {
const unsubscribe = appStore.subscribe(state => { const unsubscribe = appStore.subscribe(state => {
const storeBoundProps = { ...initialProps._bb.props }; const storeBoundProps = { ...initialProps._bb.props }
for (let prop in storeBoundProps) { for (let prop in storeBoundProps) {
if (typeof storeBoundProps[prop] === "string") { 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 thisNode.unsubscribe = unsubscribe
} }
} }

View File

@ -1,5 +1,5 @@
import regexparam from "regexparam" import regexparam from "regexparam"
import { routerStore } from "../state/store"; import { routerStore } from "../state/store"
import { initRouteStore } from "../state/store" import { initRouteStore } from "../state/store"
// TODO: refactor // TODO: refactor
@ -43,8 +43,8 @@ export const screenRouter = ({ screens, onScreenSelected, appRootPath }) => {
} }
routerStore.update(state => { routerStore.update(state => {
state["##routeParams"] = params; state["##routeParams"] = params
return state; return state
}) })
const screenIndex = current !== -1 ? current : fallback const screenIndex = current !== -1 ? current : fallback

View File

@ -51,7 +51,7 @@ export const bbFactory = ({
componentLibraries, componentLibraries,
treeNode, treeNode,
onScreenSlotRendered, onScreenSlotRendered,
setupState setupState,
} }
return { return {

View File

@ -1,7 +1,7 @@
import { setState } from "./setState" import { setState } from "./setState"
import { getState } from "./getState" import { getState } from "./getState"
import { isArray, isUndefined } from "lodash/fp" import { isArray, isUndefined } from "lodash/fp"
import { appStore } from "./store"; import { appStore } from "./store"
import { createApi } from "../api" import { createApi } from "../api"
@ -21,7 +21,7 @@ export const eventHandlers = (store, rootPath, routeTo) => {
const api = createApi({ const api = createApi({
rootPath, rootPath,
setState, setState,
getState: (path, fallback) => getState(path, fallback) getState: (path, fallback) => getState(path, fallback),
}) })
const setStateHandler = ({ path, value }) => setState(path, value) const setStateHandler = ({ path, value }) => setState(path, value)
@ -29,7 +29,7 @@ export const eventHandlers = (store, rootPath, routeTo) => {
return { return {
"Set State": handler(["path", "value"], setStateHandler), "Set State": handler(["path", "value"], setStateHandler),
"Navigate To": handler(["url"], param => routeTo(param && param.url)), "Navigate To": handler(["url"], param => routeTo(param && param.url)),
"Trigger Workflow": handler(["workflow"], api.triggerWorkflow) "Trigger Workflow": handler(["workflow"], api.triggerWorkflow),
} }
} }

View File

@ -1,10 +1,10 @@
// import { isUndefined, isObject } from "lodash/fp" // import { isUndefined, isObject } from "lodash/fp"
import { get } from "svelte/store"; import { get } from "svelte/store"
import getOr from "lodash/fp/getOr"; import getOr from "lodash/fp/getOr"
import { appStore } from "./store"; import { appStore } from "./store"
export const getState = (path, fallback) => { export const getState = (path, fallback) => {
if (!path || path.length === 0) return fallback if (!path || path.length === 0) return fallback
return getOr(fallback, path, get(appStore)); return getOr(fallback, path, get(appStore))
} }

View File

@ -1,11 +1,11 @@
import set from "lodash/fp/set"; import set from "lodash/fp/set"
import { appStore } from "./store"; import { appStore } from "./store"
export const setState = (path, value) => { export const setState = (path, value) => {
if (!path || path.length === 0) return if (!path || path.length === 0) return
appStore.update(state => { appStore.update(state => {
state = set(path, value, state); state = set(path, value, state)
return state return state
}) })
} }

View File

@ -5,8 +5,8 @@ import {
} from "./eventHandlers" } from "./eventHandlers"
import { bbFactory } from "./bbComponentApi" import { bbFactory } from "./bbComponentApi"
import mustache from "mustache" import mustache from "mustache"
import { get } from "svelte/store"; import { get } from "svelte/store"
import { appStore } from "./store"; import { appStore } from "./store"
const doNothing = () => {} const doNothing = () => {}
doNothing.isPlaceholder = true doNothing.isPlaceholder = true
@ -55,9 +55,9 @@ export const createStateManager = ({
// TODO: remove // TODO: remove
const unsubscribe = appStore.subscribe(state => { const unsubscribe = appStore.subscribe(state => {
console.log("store updated", state); console.log("store updated", state)
return state; return state
}); })
// const unsubscribe = store.subscribe( // const unsubscribe = store.subscribe(
// onStoreStateUpdated({ // onStoreStateUpdated({
@ -84,11 +84,10 @@ const onStoreStateUpdated = ({
getCurrentState, getCurrentState,
componentLibraries, componentLibraries,
onScreenSlotRendered, onScreenSlotRendered,
setupState setupState,
}) => state => { }) => state => {
// fire the state update event to re-render anything bound to this // fire the state update event to re-render anything bound to this
// setCurrentState(state) // setCurrentState(state)
// setCurrentState(state) // setCurrentState(state)
// attachChildren({ // attachChildren({
// componentLibraries, // componentLibraries,
@ -97,7 +96,6 @@ const onStoreStateUpdated = ({
// setupState, // setupState,
// getCurrentState, // getCurrentState,
// })(document.querySelector("#app"), { hydrate: true, force: true }) // })(document.querySelector("#app"), { hydrate: true, force: true })
// // the original array gets changed by components' destroy() // // the original array gets changed by components' destroy()
// // so we make a clone and check if they are still in the original // // so we make a clone and check if they are still in the original
// const nodesWithBoundChildren_clone = [...nodesWithCodeBoundChildren] // const nodesWithBoundChildren_clone = [...nodesWithCodeBoundChildren]
@ -161,19 +159,14 @@ const onStoreStateUpdated = ({
// node.component.$set(newProps) // node.component.$set(newProps)
// } // }
const _setup = ({ const _setup = ({ handlerTypes, getCurrentState, bb, store }) => node => {
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 storeBoundProps = []
const currentStoreState = get(appStore) const currentStoreState = get(appStore)
console.log("node", node); console.log("node", node)
// console.log("node", node); // console.log("node", node);
// console.log("nodeComponent", node.component); // console.log("nodeComponent", node.component);
@ -185,12 +178,12 @@ const _setup = ({
// const binding = parseBinding(propValue) // const binding = parseBinding(propValue)
// TODO: better binding stuff // TODO: better binding stuff
const isBound = typeof propValue === "string" && propValue.startsWith("{{"); const isBound = typeof propValue === "string" && propValue.startsWith("{{")
if (isBound) { if (isBound) {
initialProps[propName] = mustache.render(propValue, { initialProps[propName] = mustache.render(propValue, {
state: currentStoreState, state: currentStoreState,
context context,
}) })
if (!node.stateBound) { if (!node.stateBound) {
@ -230,7 +223,8 @@ const _setup = ({
const resolvedParams = {} const resolvedParams = {}
for (let paramName in handlerInfo.parameters) { for (let paramName in handlerInfo.parameters) {
const paramValue = handlerInfo.parameters[paramName] const paramValue = handlerInfo.parameters[paramName]
resolvedParams[paramName] = () => mustache.render(paramValue, { resolvedParams[paramName] = () =>
mustache.render(paramValue, {
state: getCurrentState(), state: getCurrentState(),
context, context,
}) })

View File

@ -1,16 +1,9 @@
import { writable } from "svelte/store"; import { writable } from "svelte/store"
const appStore = writable({}); const appStore = writable({})
appStore.actions = { appStore.actions = {}
}; const routerStore = writable({})
routerStore.actions = {}
const routerStore = writable({}); export { appStore, routerStore }
routerStore.actions = {
}
export {
appStore,
routerStore
}

View File

@ -37,8 +37,8 @@ exports.create = async function(ctx) {
emit([trigger.event], trigger) emit([trigger.event], trigger)
} }
} }
}.toString() }.toString(),
} },
}, },
}) })

View File

@ -47,7 +47,7 @@ exports.save = async function(ctx) {
ctx.eventEmitter.emit(`record:save`, { ctx.eventEmitter.emit(`record:save`, {
record, record,
instanceId: ctx.params.instanceId instanceId: ctx.params.instanceId,
}) })
ctx.body = record ctx.body = record
ctx.status = 200 ctx.status = 200

View File

@ -8,19 +8,19 @@ module.exports = async function createUser(user) {
instanceId: "inst_60dd510_700f7dc06735403e81d5af91072d7241", instanceId: "inst_60dd510_700f7dc06735403e81d5af91072d7241",
}, },
request: { request: {
body: user body: user,
}, },
} }
try { try {
const response = await userController.create(ctx) const response = await userController.create(ctx)
return { return {
user: response user: response,
} }
} catch (err) { } catch (err) {
console.error(err); console.error(err)
return { return {
user: null user: null,
} }
} }
} }

View File

@ -1,27 +1,33 @@
const EventEmitter = require("events").EventEmitter const EventEmitter = require("events").EventEmitter
const CouchDB = require("../db"); const CouchDB = require("../db")
const emitter = new EventEmitter() const emitter = new EventEmitter()
function determineWorkflowsToTrigger(instanceId, event) { async function determineWorkflowsToTrigger(instanceId, event) {
const db = new CouchDB(instanceId); const db = new CouchDB(instanceId)
const workflowsToTrigger = await db.query("database/by_workflow_trigger", { 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) { 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) { for (let workflow of workflowsToTrigger) {
// SERVER SIDE STUFF!! // SERVER SIDE STUFF!!
} }
}) })
emitter.on("record:delete", function(event) { emitter.on("record:delete", async function(event) {
const workflowsToTrigger = await determineWorkflowsToTrigger(instanceId, "record:delete") const workflowsToTrigger = await determineWorkflowsToTrigger(
instanceId,
"record:delete"
)
for (let workflow of workflowsToTrigger) { for (let workflow of workflowsToTrigger) {
// SERVER SIDE STUFF!! // SERVER SIDE STUFF!!

View File

@ -17,7 +17,7 @@ const WORKFLOW_SCHEMA = {
type: "object", type: "object",
properties: { properties: {
triggers: { type: "array" }, triggers: { type: "array" },
steps: { type: "array" } steps: { type: "array" },
// next: { // next: {
// type: "object", // type: "object",
// properties: { // properties: {