Skip to content

Commit

Permalink
Bug fix for stream blob triggers, more docs
Browse files Browse the repository at this point in the history
  • Loading branch information
JamesRandall committed Jul 5, 2018
1 parent 47a4610 commit 8982f25
Show file tree
Hide file tree
Showing 10 changed files with 168 additions and 24 deletions.
6 changes: 6 additions & 0 deletions Message.xml
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="us-ascii"?>
<Message xmlns="http://schemas.microsoft.com/servicebusexplorer">
<Date>05 July 2018 18:11:24</Date>
<Content><![CDATA[<?xml version="1.0" encoding="utf-8"?>
<message>Hi mate, how are you?</message>]]></Content>
</Message>
5 changes: 5 additions & 0 deletions Properties.xml
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="us-ascii"?>
<Properties xmlns="http://schemas.microsoft.com/servicebusexplorer">
<Property Key="MachineName" Type="String" Value="DESKTOP-JB77CF0" />
<Property Key="UserName" Type="String" Value="JamesRandall" />
</Properties>
9 changes: 5 additions & 4 deletions Samples/Scratch/SwaggerBuildOut/Commands/HelloWorldCommand.cs
@@ -1,13 +1,14 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using AzureFromTheTrenches.Commanding.Abstractions;
using FunctionMonkey.Commanding.Abstractions;
using SwaggerBuildOut.Commands.Responses;

namespace SwaggerBuildOut.Commands
{
public class HelloWorldCommand : ICommand<Message>
public class HelloWorldCommand : ICommand<Message>, IStreamCommand
{
public Stream Stream { get; set; }

public string Name { get; set; }
}
}
2 changes: 1 addition & 1 deletion Samples/Scratch/SwaggerBuildOut/ExampleTimer.cs
Expand Up @@ -7,7 +7,7 @@ namespace SwaggerBuildOut
public static class ExampleTimer
{
[FunctionName("ExampleTimer")]
public static void Run([TimerTrigger("0 */5 * * * *")]TimerInfo myTimer, TraceWriter log)
public static void Run([TimerTrigger("0 */5 * * * *")]TimerInfo myTimer, TraceWriter log, [BlobTrigger()])
{
log.Info($"C# Timer trigger function executed at: {DateTime.Now}");
}
Expand Down
8 changes: 5 additions & 3 deletions Samples/Scratch/SwaggerBuildOut/FunctionAppConfiguration.cs
Expand Up @@ -28,13 +28,15 @@ public void Build(IFunctionHostBuilder builder)
.OpenApiDescription("Says hello world")
)
.OpenApiDescription("A route description")*/
.HttpRoute("/Add", route => route
/*.HttpRoute("/Add", route => route
.HttpFunction<AddCommand>(AuthorizationTypeEnum.Anonymous,HttpMethod.Post)
.OpenApiDescription("Adds two numbers together")
.OpenApiResponse(400, "Some sort of error")
)
.OpenApiName("HelloWorld")
.Timer<HelloWorldCommand, HelloWorldTimerCommandFactory>("*/5 * * * * *")
.OpenApiName("HelloWorld")*/
//.Timer<HelloWorldCommand, HelloWorldTimerCommandFactory>("*/5 * * * * *")
.Storage("StorageConnectionString", storage => storage
.BlobFunction<HelloWorldCommand>("triggertest/{name}"))
);
}
}
Expand Down
@@ -1,4 +1,5 @@
using System.Threading.Tasks;
using System.IO;
using System.Threading.Tasks;
using AzureFromTheTrenches.Commanding.Abstractions;
using SwaggerBuildOut.Commands;
using SwaggerBuildOut.Commands.Responses;
Expand All @@ -9,10 +10,14 @@ internal class HelloWorldCommandHandler : ICommandHandler<HelloWorldCommand, Mes
{
public Task<Message> ExecuteAsync(HelloWorldCommand command, Message previousResult)
{
return Task.FromResult(new Message
using (StreamReader reader = new StreamReader(command.Stream))
{
Text = $"Hello {command.Name}"
});
string json = reader.ReadToEnd();
return Task.FromResult(new Message
{
Text = $"JSON payload\n{json}"
});
}
}
}
}
6 changes: 3 additions & 3 deletions Source/FunctionMonkey.Compiler/FunctionMonkey.Compiler.csproj
Expand Up @@ -3,7 +3,7 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.0</TargetFramework>
<Version>0.10.0-beta000</Version>
<Version>0.10.1-beta000</Version>
<AssemblyName>FunctionMonkey.Compiler</AssemblyName>
<PackageId>FunctionMonkey.Compiler</PackageId>
</PropertyGroup>
Expand Down Expand Up @@ -126,8 +126,8 @@
<IntermediatePackDir>$(MSBuildProjectDirectory)/bin/$(Configuration)/publish/</IntermediatePackDir>
<PublishDir>$(IntermediatePackDir)$(TargetFramework)/</PublishDir>
<NuspecProperties>publishDir=$([MSBuild]::NormalizeDirectory($(IntermediatePackDir)))</NuspecProperties>
<AssemblyVersion>0.10.0.0</AssemblyVersion>
<FileVersion>0.10.0.0</FileVersion>
<AssemblyVersion>0.10.1.0</AssemblyVersion>
<FileVersion>0.10.1.0</FileVersion>
<PackageReleaseNotes></PackageReleaseNotes>
</PropertyGroup>

Expand Down
Expand Up @@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2012/06/nuspec.xsd">
<metadata>
<id>FunctionMonkey.Compiler</id>
<version>0.10.0-beta000</version>
<version>0.10.1-beta000</version>
<authors>James Randall</authors>
<description>Generates Azure Functions from command registrations</description>
<licenseUrl>https://raw.githubusercontent.com/JamesRandall/AzureFromTheTrenches.Commanding/master/LICENSE</licenseUrl>
Expand Down
Expand Up @@ -18,14 +18,8 @@ namespace {{Namespace}}
IDictionary<string, string> metadata,
ILogger log)
{
log.LogInformation("Service bus queue trigger function {{Name}} processed a request.");
log.LogInformation("Blob stream function {{Name}} processed a request.");

string json;
using(StreamReader reader = new StreamReader(stream))
{
json = reader.ReadToEnd();
}

{{CommandTypeName}} command = new {{CommandTypeName}} {
Stream = stream,
Name = name
Expand Down
133 changes: 132 additions & 1 deletion docfx/guides/storage/blobs.md
@@ -1,3 +1,134 @@
# Blobs

Docs coming soon
Function Monkey supports two approaches for responding to blob storage events:

1. Deserializing directly into a command from JSON - useful for small blobs who's format is known in advance
2. Assigning a stream to a command that can be picked up in the handler - useful for blobs who's format is not known or that are very large and require careful processing

## Deserializing Directly Into a Command

First begin by creating an empty Azure Functions v2 project and then install the core nuget packages for Function Monkey:

Install-Package FunctionMonkey -pre
Install-Package FunctionMonkey.Compiler -pre

Now create a folder in the solution called commands and create a class called HelloWorldCommand:

public class HelloWorldCommand : ICommand
{
public string UserName { get; set; }
}

Next create a folder in the solution called Handlers and create a class called HelloWorldCommandHandler:

internal class HelloWorldCommandHandler : ICommandHandler<HelloWorldCommand>
{
public Task ExecuteAsync(HelloWorldCommand command)
{
Debug.WriteLine($"Hello {command.UserName}");
return Task.CompletedTask;
}
}

And now we'll create our function app configuration in the root of the project that registers the command handler and registers the command with a storage account and blob container:

public class FunctionAppConfiguration : IFunctionAppConfiguration
{
private const string ContainerName = "myblobcontainer";
private const string StorageConnectionName = "storageConnection";

public void Build(IFunctionHostBuilder builder)
{
builder
.Setup((serviceCollection, commandRegistry) =>
commandRegistry.Register<HelloWorldCommandHandler>()
)
.Functions(functions => functions
.Storage(StorageConnectionName, storage => storage
.BlobFunction<HelloWorldCommand>($"{ContainerName}/{name}")
)
);
}
}

Finally we need to create an entry in local.settings.json for the Azure Storage connection string:

{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"AzureWebJobsDashboard": "UseDevelopmentStorage=true",
"storageConnection": "<connection string>"
}
}

And that's it! If you run this sample you should find that adding a blob to the container triggers the function. Note that Azure Functions blob triggers do not work with the storage emulator, you need to use a real storage account.

## Using Streams

First begin by creating an empty Azure Functions v2 project and then install the core nuget packages for Function Monkey:

Install-Package FunctionMonkey -pre
Install-Package FunctionMonkey.Compiler -pre

Now create a folder in the solution called commands and create a class called HelloWorldCommand - in addition to implementing the _ICommand_ interface we also must implement the _IStreamCommand_:

public class HelloWorldCommand : ICommand, IStreamCommand
{
public Stream Stream { get; set; }

public string Name { get; set; }
}

Next create a folder in the solution called Handlers and create a class called HelloWorldCommandHandler:

internal class HelloWorldCommandHandler : ICommandHandler<HelloWorldCommand, Message>
{
public Task<Message> ExecuteAsync(HelloWorldCommand command, Message previousResult)
{
using (StreamReader reader = new StreamReader(command.Stream))
{
string json = reader.ReadToEnd();
return Task.FromResult(new Message
{
Text = $"JSON payload\n{json}"
});
}
}
}

Our handler will be provided an open stream to the blob in the _Stream_ property and the name of the blob in the _Name_ property of the command.

And now we'll create our function app configuration in the root of the project that registers the command handler and registers the command with a storage account and blob container:

public class FunctionAppConfiguration : IFunctionAppConfiguration
{
private const string ContainerName = "myblobcontainer";
private const string StorageConnectionName = "storageConnection";

public void Build(IFunctionHostBuilder builder)
{
builder
.Setup((serviceCollection, commandRegistry) =>
commandRegistry.Register<HelloWorldCommandHandler>()
)
.Functions(functions => functions
.Storage(StorageConnectionName, storage => storage
.BlobFunction<HelloWorldCommand>($"{ContainerName}/{name}")
)
);
}
}

Finally we need to create an entry in local.settings.json for the Azure Storage connection string:

{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"AzureWebJobsDashboard": "UseDevelopmentStorage=true",
"storageConnection": "<connection string>"
}
}

And that's it! If you run this sample you should find that adding a blob to the container triggers the function and you're given an open stream. Note that Azure Functions blob triggers do not work with the storage emulator, you need to use a real storage account.

0 comments on commit 8982f25

Please sign in to comment.