workflow DAO tests
This commit is contained in:
parent
a220822e3a
commit
2e42f8033e
|
@ -25,7 +25,11 @@
|
||||||
$basepath = "/_builder"
|
$basepath = "/_builder"
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<AppNotification />
|
||||||
|
|
||||||
|
<!-- svelte-notifications -->
|
||||||
<NotificationDisplay />
|
<NotificationDisplay />
|
||||||
|
|
||||||
<Modal>
|
<Modal>
|
||||||
<Router {routes} />
|
<Router {routes} />
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import mustache from "mustache"
|
import mustache from "mustache"
|
||||||
// TODO: tidy up import
|
|
||||||
import blockDefinitions from "components/workflow/WorkflowPanel/blockDefinitions"
|
import blockDefinitions from "components/workflow/WorkflowPanel/blockDefinitions"
|
||||||
import { generate } from "shortid"
|
import { generate } from "shortid"
|
||||||
|
|
||||||
|
@ -13,12 +12,19 @@ export default class Workflow {
|
||||||
}
|
}
|
||||||
|
|
||||||
isEmpty() {
|
isEmpty() {
|
||||||
// return this.workflow.definition.next
|
return this.workflow.definition.trigger.length === 0
|
||||||
return this.workflow.length > 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
addBlock(block) {
|
addBlock(block) {
|
||||||
// Make sure to add trigger if doesn't exist
|
// 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({
|
this.workflow.definition.steps.push({
|
||||||
id: generate(),
|
id: generate(),
|
||||||
...block,
|
...block,
|
||||||
|
@ -27,13 +33,10 @@ export default class Workflow {
|
||||||
|
|
||||||
updateBlock(updatedBlock, id) {
|
updateBlock(updatedBlock, id) {
|
||||||
const { steps, trigger } = this.workflow.definition
|
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)
|
const stepIdx = steps.findIndex(step => step.id === id)
|
||||||
|
|
||||||
// while (block.id !== id) block = block.next
|
|
||||||
if (stepIdx < 0) throw new Error("Block not found.")
|
if (stepIdx < 0) throw new Error("Block not found.")
|
||||||
|
|
||||||
steps.splice(stepIdx, 1, updatedBlock)
|
steps.splice(stepIdx, 1, updatedBlock)
|
||||||
|
@ -41,6 +44,7 @@ export default class Workflow {
|
||||||
|
|
||||||
deleteBlock(id) {
|
deleteBlock(id) {
|
||||||
const { steps, trigger } = this.workflow.definition
|
const { steps, trigger } = this.workflow.definition
|
||||||
|
// TODO: Account for trigger
|
||||||
|
|
||||||
const stepIdx = steps.findIndex(step => step.id === id)
|
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)
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,7 +68,6 @@ const workflowActions = store => ({
|
||||||
},
|
},
|
||||||
select: workflow => {
|
select: workflow => {
|
||||||
store.update(state => {
|
store.update(state => {
|
||||||
// TODO: better naming
|
|
||||||
state.currentWorkflow = new Workflow(workflow)
|
state.currentWorkflow = new Workflow(workflow)
|
||||||
state.selectedWorkflowBlock = null
|
state.selectedWorkflowBlock = null
|
||||||
return state
|
return state
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
describe("Workflow Data Object", () => {})
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
import Workflow from "../Workflow";
|
||||||
|
import TEST_WORKFLOW from "./testWorkflow";
|
||||||
|
|
||||||
|
const TEST_BLOCK = {
|
||||||
|
id: "VFWeZcIPx",
|
||||||
|
name: "Update UI State",
|
||||||
|
tagline: "Update <b>{{path}}</b> to <b>{{value}}</b>",
|
||||||
|
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();
|
||||||
|
})
|
||||||
|
})
|
|
@ -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 <b>3000</b> milliseconds",
|
||||||
|
"heading": "DELAY",
|
||||||
|
"id": "zJQcZUgDS",
|
||||||
|
"name": "Delay",
|
||||||
|
"params": Object {
|
||||||
|
"time": "number",
|
||||||
|
},
|
||||||
|
"type": "LOGIC",
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"args": Object {
|
||||||
|
"path": "foo",
|
||||||
|
"value": "finished",
|
||||||
|
},
|
||||||
|
"body": "Update <b>foo</b> to <b>finished</b>",
|
||||||
|
"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 <b>foo</b> to <b>started...</b>",
|
||||||
|
"heading": "SET_STATE",
|
||||||
|
"id": "VFWeZcIPx",
|
||||||
|
"name": "Update UI State",
|
||||||
|
"params": Object {
|
||||||
|
"path": "string",
|
||||||
|
"value": "longText",
|
||||||
|
},
|
||||||
|
"type": "ACTION",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
|
@ -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 <b>{{path}}</b> to <b>{{value}}</b>",
|
||||||
|
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 <b>{{time}}</b> 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 <b>{{path}}</b> to <b>{{value}}</b>",
|
||||||
|
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,
|
||||||
|
}
|
|
@ -2,14 +2,13 @@
|
||||||
import { onMount } from "svelte"
|
import { onMount } from "svelte"
|
||||||
import { workflowStore, backendUiStore } from "builderStore"
|
import { workflowStore, backendUiStore } from "builderStore"
|
||||||
import { notifier } from "@beyonk/svelte-notifications"
|
import { notifier } from "@beyonk/svelte-notifications"
|
||||||
import Flowchart from "./svelte-flows/Flowchart.svelte"
|
import Flowchart from "./flowchart/Flowchart.svelte"
|
||||||
import api from "builderStore/api"
|
import api from "builderStore/api"
|
||||||
|
|
||||||
let selectedWorkflow
|
let selectedWorkflow
|
||||||
let uiTree
|
let uiTree
|
||||||
let instanceId = $backendUiStore.selectedDatabase._id
|
let instanceId = $backendUiStore.selectedDatabase._id
|
||||||
|
|
||||||
// TODO: better naming
|
|
||||||
$: selectedWorkflow = $workflowStore.currentWorkflow
|
$: selectedWorkflow = $workflowStore.currentWorkflow
|
||||||
|
|
||||||
$: workflowLive = selectedWorkflow && selectedWorkflow.workflow.live
|
$: workflowLive = selectedWorkflow && selectedWorkflow.workflow.live
|
||||||
|
|
Before Width: | Height: | Size: 241 B After Width: | Height: | Size: 241 B |
|
@ -6,17 +6,17 @@ const delay = ms => new Promise(resolve => setTimeout(resolve, ms))
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
SET_STATE: ({ context, args, id }) => {
|
SET_STATE: ({ context, args, id }) => {
|
||||||
// get props from the workflow context if required
|
|
||||||
setState(...Object.values(args))
|
setState(...Object.values(args))
|
||||||
// update the context with the data
|
|
||||||
context = {
|
context = {
|
||||||
...context,
|
...context,
|
||||||
[id]: args,
|
[id]: args,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
NAVIGATE: ({ context, args, id }) => {},
|
NAVIGATE: ({ context, args, id }) => {
|
||||||
|
// TODO client navigation
|
||||||
|
},
|
||||||
DELAY: async ({ context, args }) => await delay(args.time),
|
DELAY: async ({ context, args }) => await delay(args.time),
|
||||||
FILTER: (context, args) => {
|
FILTER: ({ context, args }) => {
|
||||||
const { field, condition, value } = args
|
const { field, condition, value } = args
|
||||||
switch (condition) {
|
switch (condition) {
|
||||||
case "equals":
|
case "equals":
|
||||||
|
|
|
@ -43,7 +43,7 @@ export const clientStrategy = ({ api, instanceId }) => ({
|
||||||
// We don't want to render mustache templates on non-strings
|
// We don't want to render mustache templates on non-strings
|
||||||
if (typeof argValue !== "string") continue
|
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, {
|
mappedArgs[arg] = mustache.render(argValue, {
|
||||||
context: this.context,
|
context: this.context,
|
||||||
state: get(appStore),
|
state: get(appStore),
|
||||||
|
@ -82,8 +82,6 @@ export const clientStrategy = ({ api, instanceId }) => ({
|
||||||
[block.actionId]: response,
|
[block.actionId]: response,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("workflowContext", this.context)
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
|
@ -61,7 +61,6 @@ export const createApp = ({
|
||||||
|
|
||||||
let rootTreeNode
|
let rootTreeNode
|
||||||
const pageStateManager = createStateManager({
|
const pageStateManager = createStateManager({
|
||||||
// store: writable({ _bbuser: user }),
|
|
||||||
frontendDefinition,
|
frontendDefinition,
|
||||||
componentLibraries,
|
componentLibraries,
|
||||||
onScreenSlotRendered,
|
onScreenSlotRendered,
|
||||||
|
|
|
@ -11,7 +11,6 @@ export const loadBudibase = async opts => {
|
||||||
|
|
||||||
const frontendDefinition = _window["##BUDIBASE_FRONTEND_DEFINITION##"]
|
const frontendDefinition = _window["##BUDIBASE_FRONTEND_DEFINITION##"]
|
||||||
|
|
||||||
// TODO: update
|
|
||||||
const user = {}
|
const user = {}
|
||||||
|
|
||||||
const componentLibraryModules = (opts && opts.componentLibraries) || {}
|
const componentLibraryModules = (opts && opts.componentLibraries) || {}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import { setState } from "./setState"
|
import { setState } from "./setState"
|
||||||
// import { isBound } from "./parseBinding"
|
|
||||||
import { attachChildren } from "../render/attachChildren"
|
import { attachChildren } from "../render/attachChildren"
|
||||||
import { getContext, setContext } from "./getSetContext"
|
import { getContext, setContext } from "./getSetContext"
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
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 { createApi } from "../api"
|
import { createApi } from "../api"
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
// 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"
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
const recordController = require("../../record")
|
const recordController = require("../../record")
|
||||||
|
|
||||||
module.exports = async function saveRecord(args) {
|
module.exports = async function saveRecord(args) {
|
||||||
console.log("SAVING this record", args.record)
|
|
||||||
|
|
||||||
const ctx = {
|
const ctx = {
|
||||||
params: {
|
params: {
|
||||||
instanceId: "inst_60dd510_700f7dc06735403e81d5af91072d7241",
|
instanceId: "inst_60dd510_700f7dc06735403e81d5af91072d7241",
|
||||||
|
|
|
@ -19,7 +19,7 @@ emitter.on("record:save", async function(event) {
|
||||||
)
|
)
|
||||||
|
|
||||||
for (let workflow of workflowsToTrigger) {
|
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) {
|
for (let workflow of workflowsToTrigger) {
|
||||||
// SERVER SIDE STUFF!!
|
// TODO: server side workflow triggers
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue