Azure Function Integration

◷ Reading Time: 7 minutes

Overview

Here is a breakdown of what we have to do execute FlexRule services using Azure Function Apps.

  1. Add the FlexRule package and your FlexRule Runtime License file to your project.
  2. Add a reference to FlexRule.Runtime in your project via Nuget.
  3. Read the incoming request with input parameters.
  4. Create a RunTime Engine and execute your service.
  5. Pass the outputs back from your Function App in your preferred format.
  6. Test your function with Postman.

The steps in this page use Microsoft Visual Studio 2019 but you can also use the Function Apps web editor.

1. Add the Package File and Runtime License File

Add the FlexRule Designer Package file to the project. e.g. AgeTitle3.frdp

Add your FlexRule RunTime License to the project.

2. Add a reference to FlexRule.Runtime

In your project, add a reference to the latest version of FlexRule.Runtime.

You can use Nuget Package Manager to add this reference.

Add the following using statements to your function class.

using FlexRule;
using FlexRule.Runner.Commands;

3. Read the Incoming Request

Start with an empty function which takes an HttpRequest.

[FunctionName("ExecuteService")]
public static async Task<List<FlexRule.Json>> Run([HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req, ILogger log)
{
	log.LogInformation("C# HTTP trigger function processed a request.");


}

Read the body of the request.

var requestBody = await new StreamReader(req.Body).ReadToEndAsync();

Call the RunCommand method. We’ll create this method in the next step.

  • "AgeTitle3.frdp" is the path to the package file we want to use.
  • requestBody is the value we just read above.
  • log is the ILogger that has been passed to the Run method.
var result = RunCommand("AgeTitle3.frdp", requestBody, log);

4. Create a RunTime Engine and Execute Your Service

Add the following 2 methods to your function class.

Note that the paths to the package, license, and runtime need to correspond to your deployment.

public static RunLogicResponse RunCommand(string packagePath, string inputString, ILogger log)
{
	var inputs = (IDictionary<string, object>)Json.Parse(inputString);

	var path = $"{Directory.GetCurrentDirectory()}\\{packagePath}";
	var package = File.ReadAllBytes(path);
	var license = File.ReadAllText("flexrule.license.lic");
	var runtimeFolder = $"{Directory.GetCurrentDirectory()}\\bin";

	var runCommand = new RunLogicCommand
	{
		Runtime = runtimeFolder,
		LogicContent = package,
		InputValues = inputs,
		Cache = true,
	};
	var logger = new StringBuilder();
	var output = new StringBuilder();
	var h = new CommandHandler(OutputFactory.CreateString(output), OutputFactory.CreateString(logger));

	try
	{
		h.Execute(new InitLicenseCommand(license));
		var result = h.Execute(runCommand);

		if(result == null)
			throw new FlexRuleException(logger.ToString());

		return result;
	}
	catch (Exception ex)
	{
		log.Log(LogLevel.Error, ex.Message, ex);
	}

	return null;
}

5. Pass the Outputs Back in the Function’s Response

By calling RunCommand() we should receive back a RunLogicResponse object which gets assigned to result.

var result = RunCommand("AgeTitle3.frdp", requestBody, log);

There are two important variables you’ll need to note.

  • result.OutParameters has a list of keys that the service should output.
  • result.Result.Context.VariableContainer is a Dictionary that holds the output keys and values.

You can use the following snippet to obtain the expected outputs from the service as a list of FlexRule.Json objects. You can then return this list to the client calling your function or manipulate it further.

result.OutParameters.Select(outParam => 
result.Result.Context.VariableContainer[outParam] as FlexRule.Json).ToList();

Your completed Run() method should look like this:

[FunctionName("ExecuteService")]
public static async Task<List<FlexRule.Json>> Run([HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req, ILogger log)
{
	log.LogInformation("C# HTTP trigger function processed a request.");

	// Read the request body
	var requestBody = await new StreamReader(req.Body).ReadToEndAsync();

	// Create a RunTime Engine and run the service
	var result = RunCommand("AgeTitle3.frdp", requestBody, log);

	// Format the outputs and return them as Json
	return result.OutParameters.Select(outParam => result.Result.Context.VariableContainer[outParam] as FlexRule.Json).ToList();
}

6. Test your Function with Postman

The FlexRule logic in this example takes a person’s age and outputs a word to describe whether they are young or old. The Title key in the input is currently null and the service execution will assign an output to this value.

  {
    "person":{
      Age: 23,
      Title: null
    }
  }

Run your Function.

In Postman enter the address to your function.
(Postman is a tool to test Rest API endpoints – more details and download here)

In the Body tab, choose raw and enter the input for your service execution.

Click Send.

You should receive a response with your service execution completed and the outputs from the execution.

Optional: Custom DLL Files and FlexRule Extensions

If your business logic uses custom DLL files or FlexRule Extensions:

  • Include the DLL files when you create a package in FlexRule Designer.
  • Add the Extension parameter to your RunLogicCommand (from step 4).

For example, if you were using the FlexRule Azure Storage Extension, you would use the following snippet.

	var runCommand = new RunLogicCommand
	{
		Runtime = runtimeFolder,
		LogicContent = package,
		InputValues = inputs,
		Cache = true,
		Extension = new[] { "FlexRule.Extensions.Storage.AzureStorage.dll" },
	};

Optional: Custom Execution Handling

If you want more control over the execution, you can use a different RunCommand() method which will only give you the output values without the additional functionality required to make this a production-ready solution.

In this scenario you will need to manually implement your own:

  • Loading of modules from the package file.
  • Loading of custom DLL files required by your service.
  • Caching of the FlexRule Runtime Engine creation.
  • Event handling.
  • Logging of error messages.
private static object RunCommand(string packagePath, string requestBody, ILogger log)
{
	try
	{
		// Initialise license
		UserLicense.Key = File.ReadAllText("flexrule.license.lic");

		// Create engine
		IRuntimeEngine engine;
		using (var stream = File.OpenRead(packagePath))
		{
			var loader = new PackageLoader(stream);
			var module = loader.Modules.First();
			engine = RuntimeEngine.FromRuleSet(module.Ruleset, module.Entry);
		}

		// Parse inputs and execute
		var inputs = (IDictionary<string, object>) Json.Parse(requestBody);
		var result = engine.Run(inputs);

		// Get the outputs as Json
		var outVariables = engine.Descriptions.ListOutVariableName();
		var outputs = outVariables
			.Select(outVariable => result.Context.VariableContainer[outVariable] as FlexRule.Json).ToList();

		return outputs;
	}
	catch (Exception ex)
	{
		log.Log(LogLevel.Error, ex.Message, ex);
		return ex.Message;
	}
}

Updated on May 15, 2020

Article Attachments

Was this article helpful?

Related Articles