move workflow to array data structure
This commit is contained in:
parent
dc90e141f5
commit
a220822e3a
|
@ -152,7 +152,7 @@ export default {
|
|||
{
|
||||
find: "builderStore",
|
||||
replacement: path.resolve(projectRootDir, "src/builderStore"),
|
||||
}
|
||||
},
|
||||
],
|
||||
customResolver,
|
||||
}),
|
||||
|
|
|
@ -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({
|
||||
|
|
|
@ -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)
|
||||
// }
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -116,7 +116,7 @@
|
|||
stylesheetLinks,
|
||||
selectedComponentType,
|
||||
selectedComponentId,
|
||||
frontendDefinition: JSON.stringify(frontendDefinition)
|
||||
frontendDefinition: JSON.stringify(frontendDefinition),
|
||||
})} />
|
||||
{/if}
|
||||
</div>
|
||||
|
|
|
@ -17,14 +17,16 @@
|
|||
export let onChange
|
||||
|
||||
let isOpen = false
|
||||
|
||||
</script>
|
||||
|
||||
<div class="handler-option">
|
||||
<span>{parameter.name}</span>
|
||||
<div class="handler-input">
|
||||
{#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}
|
||||
<option value={workflow._id}>{workflow.name}</option>
|
||||
{/each}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<script>
|
||||
import { store, backendUiStore, workflowStore } from "builderStore"
|
||||
import { notifier } from '@beyonk/svelte-notifications'
|
||||
import { notifier } from "@beyonk/svelte-notifications"
|
||||
import api from "builderStore/api"
|
||||
import ActionButton from "components/common/ActionButton.svelte"
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<script>
|
||||
import { store } from "builderStore"
|
||||
import deepmerge from "deepmerge";
|
||||
import deepmerge from "deepmerge"
|
||||
|
||||
export let value
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
<script>
|
||||
import { backendUiStore } from "builderStore"
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<script>
|
||||
import { backendUiStore, store } from "builderStore"
|
||||
import ComponentSelector from "./ParamInputs/ComponentSelector.svelte";
|
||||
import ModelSelector from "./ParamInputs/ModelSelector.svelte";
|
||||
import ComponentSelector from "./ParamInputs/ComponentSelector.svelte"
|
||||
import ModelSelector from "./ParamInputs/ModelSelector.svelte"
|
||||
|
||||
export let workflowBlock
|
||||
|
||||
|
@ -12,9 +12,7 @@
|
|||
: []
|
||||
</script>
|
||||
|
||||
<label class="uk-form-label">
|
||||
{workflowBlock.type}: {workflowBlock.name}
|
||||
</label>
|
||||
<label class="uk-form-label">{workflowBlock.type}: {workflowBlock.name}</label>
|
||||
{#each workflowParams as [parameter, type]}
|
||||
<div class="uk-margin block-field">
|
||||
<label class="uk-form-label">{parameter}</label>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<script>
|
||||
import FlowItem from "./FlowItem.svelte"
|
||||
import Arrow from "./Arrow.svelte";
|
||||
import Arrow from "./Arrow.svelte"
|
||||
|
||||
export let blocks = []
|
||||
export let onSelect
|
||||
|
|
|
@ -12,7 +12,10 @@
|
|||
$: definitions = Object.entries(blockDefinitions[selectedTab])
|
||||
|
||||
$: {
|
||||
if (!$workflowStore.currentWorkflow.isEmpty() && selectedTab === "TRIGGER") {
|
||||
if (
|
||||
!$workflowStore.currentWorkflow.isEmpty() &&
|
||||
selectedTab === "TRIGGER"
|
||||
) {
|
||||
selectedTab = "ACTION"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<script>
|
||||
import { store, backendUiStore, workflowStore } from "builderStore"
|
||||
import { notifier } from '@beyonk/svelte-notifications'
|
||||
import { notifier } from "@beyonk/svelte-notifications"
|
||||
import api from "builderStore/api"
|
||||
import ActionButton from "components/common/ActionButton.svelte"
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<script>
|
||||
import Modal from "svelte-simple-modal"
|
||||
import { notifier } from "@beyonk/svelte-notifications";
|
||||
import { notifier } from "@beyonk/svelte-notifications"
|
||||
import { onMount, getContext } from "svelte"
|
||||
import { backendUiStore, workflowStore } from "builderStore"
|
||||
import api from "builderStore/api"
|
||||
|
@ -9,7 +9,8 @@
|
|||
const { open, close } = getContext("simple-modal")
|
||||
|
||||
$: currentWorkflowId =
|
||||
$workflowStore.currentWorkflow && $workflowStore.currentWorkflow.workflow._id
|
||||
$workflowStore.currentWorkflow &&
|
||||
$workflowStore.currentWorkflow.workflow._id
|
||||
|
||||
function newWorkflow() {
|
||||
open(
|
||||
|
@ -30,9 +31,9 @@
|
|||
// TODO: Clean up args
|
||||
await workflowStore.actions.save({
|
||||
instanceId: $backendUiStore.selectedDatabase._id,
|
||||
workflow
|
||||
workflow,
|
||||
})
|
||||
notifier.success(`Workflow ${workflow.name} saved.`);
|
||||
notifier.success(`Workflow ${workflow.name} saved.`)
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
@ -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: {
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
}
|
||||
},
|
||||
}
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
},
|
||||
})
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -39,7 +39,7 @@ export const loadBudibase = async opts => {
|
|||
componentLibraries: componentLibraryModules,
|
||||
frontendDefinition,
|
||||
user,
|
||||
window
|
||||
window,
|
||||
})
|
||||
|
||||
const route = _window.location
|
||||
|
|
|
@ -42,7 +42,7 @@ export const attachChildren = initialiseOpts => (htmlElement, options) => {
|
|||
parentNode: treeNode,
|
||||
ComponentConstructor,
|
||||
htmlElement,
|
||||
anchor
|
||||
anchor,
|
||||
})
|
||||
|
||||
for (let childNode of childNodesThisIteration) {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -51,7 +51,7 @@ export const bbFactory = ({
|
|||
componentLibraries,
|
||||
treeNode,
|
||||
onScreenSlotRendered,
|
||||
setupState
|
||||
setupState,
|
||||
}
|
||||
|
||||
return {
|
||||
|
|
|
@ -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),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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));
|
||||
return getOr(fallback, path, get(appStore))
|
||||
}
|
|
@ -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
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 }
|
||||
|
|
|
@ -37,8 +37,8 @@ exports.create = async function(ctx) {
|
|||
emit([trigger.event], trigger)
|
||||
}
|
||||
}
|
||||
}.toString()
|
||||
}
|
||||
}.toString(),
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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!!
|
||||
|
|
|
@ -17,7 +17,7 @@ const WORKFLOW_SCHEMA = {
|
|||
type: "object",
|
||||
properties: {
|
||||
triggers: { type: "array" },
|
||||
steps: { type: "array" }
|
||||
steps: { type: "array" },
|
||||
// next: {
|
||||
// type: "object",
|
||||
// properties: {
|
||||
|
|
Loading…
Reference in New Issue