Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Dynamic Workflows Low Code Style
namespace dynamicworkflows.Workflows
{
using Jint;
using Newtonsoft.Json;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.DurableTask;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Extensions.Logging;
public class DynamicStep<T, U>
{
public string Action { get; private set; }
public U param { get; private set; }
public string Fn { get; }
public DynamicStep(string action, U param)
{
Action = action;
this.param = param;
Fn = string.Empty;
}
[JsonConstructor]
public DynamicStep(string action, U param, string fn)
{
Action = action;
this.param = param;
Fn = fn;
}
}
public class DynamicResult<T>
{
public T Result { get; set; }
}
public static class ActionName
{
public const string Add = "Add";
public const string Subtract = "Subtract";
public const string Multiply = "Multiply";
public const string Divide = "Divide";
public const string Dynamic = "Dynamic";
}
public static class FlowMaker
{
[FunctionName("FlowMaker")]
public static async Task<double> Run([OrchestrationTrigger] IDurableOrchestrationContext context)
{
var steps = new List<DynamicStep<int, int>>
{
new DynamicStep<int, int>(ActionName.Add, 1),
new DynamicStep<int, int>(ActionName.Add, 2),
new DynamicStep<int, int>(ActionName.Add, 3),
new DynamicStep<int, int>(ActionName.Dynamic, 2, "(2 * r + 1)/p"), // <-- simulate loading for a datasource
};
var ctx = new DynamicFlowContext
{
Steps = steps
};
var result = await context.CallSubOrchestratorAsync<DynamicResult<double>>("DynamicOrchestrator", ctx);
return result.Result;
}
[FunctionName("DynamicOrchestrator")]
public static async Task<DynamicResult<double>> RunInnerOrchestrator(
[OrchestrationTrigger] IDurableOrchestrationContext ctx)
{
var input = ctx.GetInput<DynamicFlowContext>();
double state = 0;
foreach (var step in input.Steps)
{
state = await ctx.CallActivityAsync<double>(step.Action, new DynamicParam
{
Accumulator = state,
Parameter = step.param,
Fn = step.Fn,
});
}
return new DynamicResult<double>
{
Result = state
};
}
[FunctionName("Add")]
public static async Task<double> Add([ActivityTrigger] DynamicParam param, ILogger log)
{
return param.Accumulator + param.Parameter;
}
[FunctionName("Subtract")]
public static async Task<double> Subtract([ActivityTrigger] DynamicParam param, ILogger log)
{
return param.Accumulator - param.Parameter;
}
[FunctionName("Multiply")]
public static async Task<double> Multiply([ActivityTrigger] DynamicParam param, ILogger log)
{
return param.Accumulator * param.Parameter;
}
[FunctionName("Divide")]
public static async Task<double> Divide([ActivityTrigger] DynamicParam param, ILogger log)
{
return param.Accumulator / param.Parameter;
}
[FunctionName("Dynamic")]
public static async Task<double> DynamicCalculate([ActivityTrigger] DynamicParam param, ILogger log)
{
var func = new Engine()
.Execute($"function dyn(r, p){{ return {param.Fn} }}").GetValue("dyn");
var invoked = func.Invoke(param.Accumulator, param.Parameter);
double.TryParse(invoked.ToString(), out var result);
return result;
}
[FunctionName("FlowMaker_HttpStart")]
public static async Task<HttpResponseMessage> HttpStart(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = "start")]
HttpRequestMessage req,
[DurableClient] IDurableOrchestrationClient starter,
ILogger log)
{
// Function input comes from the request content.
string instanceId = await starter.StartNewAsync("FlowMaker");
log.LogInformation($"Started orchestration with ID = '{instanceId}'.");
return starter.CreateCheckStatusResponse(req, instanceId);
}
public class DynamicFlowContext
{
public List<DynamicStep<int, int>> Steps { get; set; }
}
public class DynamicParam
{
public double Accumulator { get; set; }
public int Parameter { get; set; }
public string Fn { get; set; }
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment