diff --git a/packages/builder/package.json b/packages/builder/package.json index 086d22382a..0926b82fb0 100644 --- a/packages/builder/package.json +++ b/packages/builder/package.json @@ -47,9 +47,11 @@ "lodash": "^4.17.13", "logrocket": "^1.0.6", "lunr": "^2.3.5", + "mustache": "^4.0.1", "safe-buffer": "^5.1.2", "shortid": "^2.2.8", "string_decoder": "^1.2.0", + "svelte-grid": "^1.10.8", "svelte-simple-modal": "^0.3.0", "uikit": "^3.1.7" }, diff --git a/packages/builder/src/builderStore/api.js b/packages/builder/src/builderStore/api.js index c9327a98f2..04dcce6cb9 100644 --- a/packages/builder/src/builderStore/api.js +++ b/packages/builder/src/builderStore/api.js @@ -25,5 +25,5 @@ export default { get, patch, delete: del, - put + put, } diff --git a/packages/builder/src/builderStore/index.js b/packages/builder/src/builderStore/index.js index dd6784c135..2af3a66667 100644 --- a/packages/builder/src/builderStore/index.js +++ b/packages/builder/src/builderStore/index.js @@ -1,6 +1,6 @@ import { getStore } from "./store" import { getBackendUiStore } from "./store/backend" -import { getWorkflowStore } from "./store/workflow" +import { getWorkflowStore } from "./store/workflow/" import LogRocket from "logrocket" export const store = getStore() diff --git a/packages/builder/src/builderStore/store/workflow.js b/packages/builder/src/builderStore/store/workflow.js deleted file mode 100644 index f1eb8b93f5..0000000000 --- a/packages/builder/src/builderStore/store/workflow.js +++ /dev/null @@ -1,55 +0,0 @@ -import { writable } from "svelte/store" -import api from "../api" - -const workflowActions = store => ({ - fetch: async instanceId => { - const WORKFLOWS_URL = `/api/${instanceId}/workflows`; - const workflowResponse = await api.get(WORKFLOWS_URL); - const json = await workflowResponse.json(); - store.update(state => { - state.workflows = json - return state - }) - }, - create: async ({ instanceId, name }) => { - const workflow = { name } - const CREATE_WORKFLOW_URL = `/api/${instanceId}/workflows`; - const response = await api.post(CREATE_WORKFLOW_URL, workflow) - const json = await response.json(); - store.update(state => { - state.workflows = state.workflows.concat(json.workflow) - state.selectedWorkflowId = json.workflow._id - return state - }) - }, - update: async ({ instanceId, workflow }) => { - const UPDATE_WORKFLOW_URL = `/api/${instanceId}/workflows`; - const response = await api.put(UPDATE_WORKFLOW_URL, workflow) - const json = await response.json(); - store.update(state => { - const existingIdx = state.workflows.findIndex(existing => existing._id === workflow._id); - state.workflows.splice(existingIdx, 1, json.workflow); - state.workflows = state.workflows - return state - }) - }, - select: workflow => { - store.update(state => { - state.selectedWorkflowId = workflow._id - state.selectedWorkflowBlock = null - return state; - }) - } -}); - -export const getWorkflowStore = () => { - const INITIAL_WORKFLOW_STATE = { - workflows: [] - } - - const store = writable(INITIAL_WORKFLOW_STATE) - - store.actions = workflowActions(store); - - return store -} \ No newline at end of file diff --git a/packages/builder/src/builderStore/store/workflow/Workflow.js b/packages/builder/src/builderStore/store/workflow/Workflow.js new file mode 100644 index 0000000000..b3e50466f7 --- /dev/null +++ b/packages/builder/src/builderStore/store/workflow/Workflow.js @@ -0,0 +1,79 @@ +import mustache from "mustache" +// TODO: tidy up import +import blockDefinitions from "../../../pages/[application]/workflow/WorkflowPanel/blockDefinitions" + +/** + * Class responsible for the traversing of the workflow definition. + * Workflow definitions are stored in linked lists. + */ +export default class Workflow { + constructor(workflow) { + this.workflow = workflow + } + + addBlock(block) { + let node = this.workflow.definition + while (node.next) node = node.next + node.next = block + } + + updateBlock(updatedBlock, id) { + let block = this.workflow.definition + + while (block.id !== id) block = block.next + if (!block) throw new Error("Block not found.") + + block = updatedBlock + } + + deleteBlock(id) { + let previous = null + let block = this.workflow.definition + + // iterate through the blocks + while (block.id !== id) { + previous = block + block = block.next + } + + // delete the block found + previous.next = block.next || {} + } + + createUiTree() { + if (!this.workflow.definition.next) return [] + return Workflow.buildUiTree(this.workflow.definition.next) + } + + static buildUiTree(block, tree = []) { + if (!block) return tree + + // The client side display definition for the block + const definition = blockDefinitions[block.type][block.actionId] + if (!definition) { + throw new Error( + `No block definition exists for the chosen block. Check there's an entry in the block definitions for ${block.actionId}` + ) + } + + if (!definition.params) { + throw new Error( + `Blocks should always have parameters. Ensure that the block definition is correct for ${block.actionId}` + ) + } + + const tagline = definition.tagline || "" + const args = block.args || {} + + tree.push({ + id: block.id, + type: block.type, + params: block.params, + args, + heading: block.actionId, + body: mustache.render(tagline, args), + }) + + return this.buildUiTree(block.next, tree) + } +} diff --git a/packages/builder/src/builderStore/store/workflow/index.js b/packages/builder/src/builderStore/store/workflow/index.js new file mode 100644 index 0000000000..fcdc299271 --- /dev/null +++ b/packages/builder/src/builderStore/store/workflow/index.js @@ -0,0 +1,102 @@ +import { writable } from "svelte/store" +import api from "../../api" +import Workflow from "./Workflow" + +const workflowActions = store => ({ + fetch: async instanceId => { + const WORKFLOWS_URL = `/api/${instanceId}/workflows` + const workflowResponse = await api.get(WORKFLOWS_URL) + const json = await workflowResponse.json() + store.update(state => { + state.workflows = json + return state + }) + }, + create: async ({ instanceId, name }) => { + const workflow = { name, definition: {} } + const CREATE_WORKFLOW_URL = `/api/${instanceId}/workflows` + const response = await api.post(CREATE_WORKFLOW_URL, workflow) + const json = await response.json() + store.update(state => { + state.workflows = state.workflows.concat(json.workflow) + state.currentWorkflow = new Workflow(json.workflow) + return state + }) + }, + save: async ({ instanceId, workflow }) => { + const UPDATE_WORKFLOW_URL = `/api/${instanceId}/workflows` + const response = await api.put(UPDATE_WORKFLOW_URL, workflow) + const json = await response.json() + store.update(state => { + const existingIdx = state.workflows.findIndex( + existing => existing._id === workflow._id + ) + state.workflows.splice(existingIdx, 1, json.workflow) + state.workflows = state.workflows + state.currentWorkflow = new Workflow(json.workflow) + return state + }) + }, + update: async ({ instanceId, workflow }) => { + const UPDATE_WORKFLOW_URL = `/api/${instanceId}/workflows` + const response = await api.put(UPDATE_WORKFLOW_URL, workflow) + const json = await response.json() + store.update(state => { + const existingIdx = state.workflows.findIndex( + existing => existing._id === workflow._id + ) + state.workflows.splice(existingIdx, 1, json.workflow) + state.workflows = state.workflows + return state + }) + }, + delete: async ({ instanceId, workflow }) => { + const { _id, _rev } = workflow + const DELETE_WORKFLOW_URL = `/api/${instanceId}/workflows/${_id}/${_rev}` + await api.delete(DELETE_WORKFLOW_URL) + + store.update(state => { + const existingIdx = state.workflows.findIndex( + existing => existing._id === _id + ) + state.workflows.splice(existingIdx, 1) + state.workflows = state.workflows + state.currentWorkflow = null + return state + }) + }, + select: workflow => { + store.update(state => { + // TODO: better naming + state.currentWorkflow = new Workflow(workflow) + state.selectedWorkflowBlock = null + return state + }) + }, + addBlockToWorkflow: block => { + store.update(state => { + state.currentWorkflow.addBlock(block) + state.selectedWorkflowBlock = block + return state + }) + }, + deleteWorkflowBlock: block => { + store.update(state => { + state.currentWorkflow.deleteBlock(block._id) + state.selectedWorkflowBlock = null + return state + }) + }, +}) + +export const getWorkflowStore = () => { + const INITIAL_WORKFLOW_STATE = { + workflows: [], + } + + const store = writable(INITIAL_WORKFLOW_STATE) + + store.actions = workflowActions(store) + + return store +} diff --git a/packages/builder/src/builderStore/store/workflow/tests/Workflow.js b/packages/builder/src/builderStore/store/workflow/tests/Workflow.js new file mode 100644 index 0000000000..a892d688b3 --- /dev/null +++ b/packages/builder/src/builderStore/store/workflow/tests/Workflow.js @@ -0,0 +1 @@ +describe("Workflow Data Object", () => {}) diff --git a/packages/builder/src/components/database/ModelDataTable/modals/CreateUser.svelte b/packages/builder/src/components/database/ModelDataTable/modals/CreateUser.svelte index 77e0850b87..e81659f716 100644 --- a/packages/builder/src/components/database/ModelDataTable/modals/CreateUser.svelte +++ b/packages/builder/src/components/database/ModelDataTable/modals/CreateUser.svelte @@ -34,7 +34,7 @@
+ Are you sure you want to delete this workflow? This action can't be undone. +
+