mirror of https://github.com/Budibase/budibase.git
12 changed files with 237 additions and 37 deletions
@ -0,0 +1,16 @@ |
|||
import Orchestrator, { clientStrategy } from "./orchestrator"; |
|||
|
|||
|
|||
export const triggerWorkflow = api => ({ workflow }) => { |
|||
console.log(workflow); |
|||
const workflowOrchestrator = new Orchestrator( |
|||
api, |
|||
"inst_60dd510_700f7dc06735403e81d5af91072d7241" |
|||
); |
|||
workflowOrchestrator.strategy = clientStrategy |
|||
|
|||
workflowOrchestrator.execute(workflow); |
|||
|
|||
// hit the API and get the workflow data back
|
|||
|
|||
} |
|||
@ -0,0 +1,103 @@ |
|||
import get from "lodash/fp/get"; |
|||
|
|||
/** |
|||
* The workflow orhestrator is a class responsible for executing workflows. |
|||
* It relies on the strategy pattern, which allows composable behaviour to be |
|||
* passed into its execute() function. This allows custom execution behaviour based |
|||
* on where the orchestrator is run. |
|||
* |
|||
*/ |
|||
export default class Orchestrator { |
|||
constructor(api, instanceId) { |
|||
this.api = api |
|||
this.instanceId = instanceId |
|||
} |
|||
|
|||
set strategy(strategy) { |
|||
this._strategy = strategy |
|||
} |
|||
|
|||
async execute(workflowId) { |
|||
const EXECUTE_WORKFLOW_URL = `/api/${this.instanceId}/workflows/${workflowId}`; |
|||
const workflow = await this.api.get({ url: EXECUTE_WORKFLOW_URL }) |
|||
this._strategy.run({ |
|||
workflow: workflow.definition, |
|||
api: this.api, |
|||
instanceId: this.instanceId |
|||
}); |
|||
} |
|||
} |
|||
|
|||
// Execute a workflow from a running budibase app
|
|||
export const clientStrategy = { |
|||
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]; |
|||
// Means that it's bound to state or workflow context
|
|||
if (argValue.startsWith("$")) { |
|||
// if value is bound to workflow context.
|
|||
if (argValue.startsWith("$context")) { |
|||
const path = argValue.replace("$context.", ""); |
|||
// pass in the value from context
|
|||
mappedArgs[arg] = get(path, this.context); |
|||
} |
|||
|
|||
// if the value is bound to state
|
|||
if (argValue.startsWith("$state")) { |
|||
const path = argValue.match("$context.", ""); |
|||
// pass in the value from context
|
|||
mappedArgs[arg] = get(path, this.context); |
|||
} |
|||
} |
|||
} |
|||
return Object.values(mappedArgs); |
|||
}, |
|||
run: async function({ workflow, api, instanceId }) { |
|||
const block = workflow.next; |
|||
|
|||
console.log("Executing workflow block", block); |
|||
|
|||
if (!block) return; |
|||
|
|||
// This code gets run in the browser
|
|||
if (block.type === "CLIENT") { |
|||
if (block.actionId === "SET_STATE") { |
|||
// get props from the workflow context if required
|
|||
api.setState(...this.bindContextArgs(block.args)) |
|||
// update the context with the data
|
|||
this.context = { |
|||
...this.context, |
|||
SET_STATE: block.args |
|||
} |
|||
} |
|||
}; |
|||
|
|||
// this workflow block gets executed on the server
|
|||
if (block.type === "SERVER") { |
|||
const EXECUTE_WORKFLOW_URL = `/api/${instanceId}/workflows/action` |
|||
const response = await api.post({ |
|||
url: EXECUTE_WORKFLOW_URL, |
|||
body: { |
|||
action: block.actionId, |
|||
args: block.args |
|||
} |
|||
}); |
|||
|
|||
this.context = { |
|||
...this.context, |
|||
[block.actionId]: response |
|||
} |
|||
} |
|||
|
|||
console.log("workflowContext", this.context) |
|||
|
|||
// TODO: clean this up, don't pass all those args
|
|||
this.run({ workflow: workflow.next, instanceId, api }); |
|||
} |
|||
} |
|||
@ -1 +0,0 @@ |
|||
export const ERROR = "##error_message" |
|||
@ -0,0 +1,43 @@ |
|||
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("", () => { |
|||
|
|||
}); |
|||
}); |
|||
@ -0,0 +1,9 @@ |
|||
export default async function () { |
|||
const response = await fetch("www.google.com"); |
|||
console.log(response); |
|||
console.log("CUSTOM ACTION"); |
|||
return { |
|||
message: "CUSTOM_WORKFLOW_SCRIPT", |
|||
response |
|||
} |
|||
} |
|||
@ -0,0 +1,20 @@ |
|||
const recordController = require("../../record"); |
|||
|
|||
module.exports = async function saveRecord(args) { |
|||
console.log("SAVING this record", args.record); |
|||
|
|||
const ctx = { |
|||
params: { |
|||
instanceId: "inst_60dd510_700f7dc06735403e81d5af91072d7241", |
|||
}, |
|||
request: { |
|||
body: args.record |
|||
} |
|||
} |
|||
|
|||
await recordController.save(ctx); |
|||
|
|||
return { |
|||
record: ctx.body |
|||
} |
|||
} |
|||
Loading…
Reference in new issue