I’ve had Function Monkey on my to-look-at radar for a little while now so I thought I’d finally get round to looking at it in this post.
As a writing experiment I’m going to “live write” my experience of using it for the first time.
Function Monkey is essentially a tool/library for building an Azure Functions app in a different way from the usual function-methods-with-attributes.
Let’s go!
Setting Up A Project
So the first thing I need to do is create a new Azure Functions project in Visual Studio – I’m choosing to create an empty functions app.
Next, I’m installing the NuGet packages: FunctionMonkey and FunctionMonkey.Compiler
Defining a Command
Function Monkey uses commands to represent “things the functions can do”.
For example to create a command that represent the addition of 2 numbers I’m adding the following:
using AzureFromTheTrenches.Commanding.Abstractions;
namespace FunctionApp1
{
public class AddNumbers : ICommand<int>
{
public int FirstNumber { get; set; }
public int SecondNumber { get; set; }
}
}
The ICommand<int> says that when this command executes it will return an int.
Handling a Command
Now I’ve defined a command, I need to be able to execute it so I need to create a “command handler”, so I’m adding a new class:
internal class AddNumbersHandler : ICommandHandler<AddNumbers, int>
{
public Task<int> ExecuteAsync(AddNumbers command, int previousResult)
{
return Task.FromResult(command.FirstNumber + command.SecondNumber);
}
}
I now need a wire to make the handler execute.
Function App Configuration
In function apps you normally create a class and decorate methods with attributes to create functions. With Function Monkey it looks like there’s a fluent API instead, so I’m adding a new class to wire everything up:
public class FunctionAppConfiguration : IFunctionAppConfiguration
{
public void Build(IFunctionHostBuilder builder)
{
builder
.Setup((serviceCollection, commandRegistry) =>
{
commandRegistry.Register<AddNumbersHandler>();
})
.Functions(functions => functions
.HttpRoute("v1/AddNumbers", route => route
.HttpFunction<AddNumbers>()
)
);
}
}
This code is wiring up the command and handler that we just created and setting up an HTTP-triggered function.
I’m not sure at first glance how I feel about the readability of this as I’m looking at it, though it is an interesting way to wire things up, I believe you can also wire up dependency injection and other things here which would be nice.
Testing It Out
Ok, let’s run the app and see what happens:
Application started. Press Ctrl+C to shut down.
Http Functions:
AddNumbers: [GET] http://localhost:7071/api/v1/AddNumbers
[31/01/2020 4:49:07 AM] Host lock lease acquired by instance ID '0000000000000000000000006B155899'.
So it looks like the HTTP-triggered function has been created in the functions runtime with the route “v1/AddNumbers”.
It’s not immediately obvious what HTTP request to make, it’s exposing a GET so let’s try and make a GET request with some JSON to represent the command properties FirstNumber and SecondNumber.
This is the JSON I’m going to try (I’m going to use the Postman app to send the request):
{
"FirstNumber" : 1,
"SecondNumber" : 1
}
Sending this JSON results in the value 2 being returned so this part where JSON maps to the command properties is nice and intuitive.
There’s also probably an easy way to restrict this API to POSTs. Without looking at the docs I’m going to see if it’s easy to find/intuitive to modify the code where the function is defined…
So the HTTP method has an overload that takes an params array of HttpMethod, so I just changed the config to:
public class FunctionAppConfiguration : IFunctionAppConfiguration
{
public void Build(IFunctionHostBuilder builder)
{
builder
.Setup((serviceCollection, commandRegistry) =>
{
commandRegistry.Register<AddNumbersHandler>();
})
.Functions(functions => functions
.HttpRoute("v1/AddNumbers", route => route
.HttpFunction<AddNumbers>(HttpMethod.Post)
)
);
}
}
Let’s run the app again:
Application started. Press Ctrl+C to shut down.
Http Functions:
AddNumbers: [POST] http://localhost:7071/api/v1/AddNumbers
[31/01/2020 7:00:38 AM] Host lock lease acquired by instance ID '0000000000000000000000006B155899'.
Now as we can see the URL accepts POSTS.
This is a library that I think warrants further inspection. Let me know in the comments if you’d like to see more posts on this.
One thing to bear in mind when using these kinds of tools is that they do introduce another dependency to the application.
SHARE: