diff --git a/Exemplars/StandardFunctions/CosmosMonitorExample.cs b/Exemplars/StandardFunctions/CosmosMonitorExample.cs new file mode 100644 index 00000000..cfc24676 --- /dev/null +++ b/Exemplars/StandardFunctions/CosmosMonitorExample.cs @@ -0,0 +1,120 @@ +using System; +using System.Threading.Tasks; +using Microsoft.Azure.WebJobs; +using Microsoft.Extensions.Logging; +using Microsoft.Azure.Documents; +using System.Collections.Generic; +using Microsoft.Azure.Documents.Client; +using Microsoft.Azure.Documents.ChangeFeedProcessor.PartitionManagement; +using Microsoft.Azure.Documents.ChangeFeedProcessor; +using System.Data.Common; +using System.Threading; + +namespace FunctionApp2 +{ + public static class MonitorTrigger + { + // Just a normal trigger + [FunctionName("Trigger")] + public static void Trigger([CosmosDBTrigger( + databaseName: "%MonitoredDatabase%", + collectionName: "%MonitoredCollection%", + ConnectionStringSetting = "CosmosDB", + LeaseCollectionPrefix = "%MonitoredDatabaseLeasePrefix%", + LeaseCollectionName = "leases", + CreateLeaseCollectionIfNotExists = true)] IReadOnlyList input, ILogger log) + { + if (input != null && input.Count > 0) + { + log.LogInformation("Documents modified " + input.Count); + + // We add a delay to simulate some work being doing + Thread.Sleep(1000); + } + } + + [FunctionName("Monitor")] + public static async Task Monitor( + [TimerTrigger("*/1 * * * * *", RunOnStartup = true)] TimerInfo timer, // Timer will trigger every 1 second, adjust CRON expression + [CosmosDB("%MonitoredDatabase%", "%MonitoredCollection%", ConnectionStringSetting = "CosmosDB")] DocumentClient monitoredCollectionClient, + [CosmosDB("%MonitoredDatabase%", "leases", ConnectionStringSetting = "CosmosDB")] DocumentClient leaseCollectionClient, + ILogger log) + { + var estimator = GetRemainingWorkEstimator(monitoredCollectionClient, leaseCollectionClient); + var remainingWork = await estimator.GetEstimatedRemainingWork(); + // Send custom metric to App Insight + log.LogInformation(remainingWork.ToString()); + log.LogMetric("RemainingWork", remainingWork); + } + + // Make it Lazy to reuse between calls + private static Lazy remainingWorkEstimator; + + public static IRemainingWorkEstimator GetRemainingWorkEstimator( + DocumentClient monitoredCollectionClient, + DocumentClient leaseCollectionClient) + { + if (remainingWorkEstimator == null) + { + // Pull the Connection string from the environment, Environment.GetEnvironmentVariable will read the local.settings.json file or the deployed Function App configuration + CosmosDBConnectionString cosmosDBConnectionString = new CosmosDBConnectionString(Environment.GetEnvironmentVariable("CosmosDB")); + remainingWorkEstimator = new Lazy(() => + { + var builder = new ChangeFeedProcessorBuilder() + .WithHostName("monitor") // Can be a random name + .WithProcessorOptions(new ChangeFeedProcessorOptions() + { + LeasePrefix = Environment.GetEnvironmentVariable("MonitoredDatabaseLeasePrefix") + }) + .WithFeedCollection(new DocumentCollectionInfo() + { + Uri = cosmosDBConnectionString.ServiceEndpoint, + MasterKey = cosmosDBConnectionString.AuthKey, + CollectionName = Environment.GetEnvironmentVariable("MonitoredCollection"), + DatabaseName = Environment.GetEnvironmentVariable("MonitoredDatabase") + }) + .WithLeaseCollection(new DocumentCollectionInfo() + { + Uri = cosmosDBConnectionString.ServiceEndpoint, + MasterKey = cosmosDBConnectionString.AuthKey, + CollectionName = "leases", + DatabaseName = Environment.GetEnvironmentVariable("MonitoredDatabase") + }) + .WithFeedDocumentClient(monitoredCollectionClient) + .WithLeaseDocumentClient(leaseCollectionClient); + + return builder.BuildEstimatorAsync().Result; + + }); + } + + return remainingWorkEstimator.Value; + } + + private class CosmosDBConnectionString + { + public CosmosDBConnectionString(string connectionString) + { + // Use this generic builder to parse the connection string + DbConnectionStringBuilder builder = new DbConnectionStringBuilder + { + ConnectionString = connectionString + }; + + if (builder.TryGetValue("AccountKey", out object key)) + { + AuthKey = key.ToString(); + } + + if (builder.TryGetValue("AccountEndpoint", out object uri)) + { + ServiceEndpoint = new Uri(uri.ToString()); + } + } + + public Uri ServiceEndpoint { get; set; } + + public string AuthKey { get; set; } + } + } +} \ No newline at end of file diff --git a/Exemplars/StandardFunctions/StandardFunctions.csproj b/Exemplars/StandardFunctions/StandardFunctions.csproj index ce6db30d..27dfce21 100644 --- a/Exemplars/StandardFunctions/StandardFunctions.csproj +++ b/Exemplars/StandardFunctions/StandardFunctions.csproj @@ -8,7 +8,7 @@ - + diff --git a/Samples/DocumentationSamples/GettingStartedSample/GettingStartedSample.csproj b/Samples/DocumentationSamples/GettingStartedSample/GettingStartedSample.csproj index ff4f4b52..4631610b 100644 --- a/Samples/DocumentationSamples/GettingStartedSample/GettingStartedSample.csproj +++ b/Samples/DocumentationSamples/GettingStartedSample/GettingStartedSample.csproj @@ -7,7 +7,7 @@ - + diff --git a/Samples/DocumentationSamples/RestApiSample/RestApiSample.csproj b/Samples/DocumentationSamples/RestApiSample/RestApiSample.csproj index 8dea4fef..ae80724c 100644 --- a/Samples/DocumentationSamples/RestApiSample/RestApiSample.csproj +++ b/Samples/DocumentationSamples/RestApiSample/RestApiSample.csproj @@ -6,7 +6,7 @@ - + diff --git a/Samples/DocumentationSamples/ServiceBusSample/ServiceBusSample.csproj b/Samples/DocumentationSamples/ServiceBusSample/ServiceBusSample.csproj index f3a7277e..f6e49a29 100644 --- a/Samples/DocumentationSamples/ServiceBusSample/ServiceBusSample.csproj +++ b/Samples/DocumentationSamples/ServiceBusSample/ServiceBusSample.csproj @@ -7,7 +7,7 @@ - + diff --git a/Samples/Scratch/SwaggerBuildOut/FunctionAppConfiguration.cs b/Samples/Scratch/SwaggerBuildOut/FunctionAppConfiguration.cs index e751deea..5a3135de 100644 --- a/Samples/Scratch/SwaggerBuildOut/FunctionAppConfiguration.cs +++ b/Samples/Scratch/SwaggerBuildOut/FunctionAppConfiguration.cs @@ -52,11 +52,11 @@ public void Build(IFunctionHostBuilder builder) .HttpFunction(HttpMethod.Post) ) .OpenApiDescription("A route description") - /*.CosmosDb("CosmosConnection", cosmos => cosmos + .CosmosDb("CosmosConnection", cosmos => cosmos .ChangeFeedFunction("Items", "ToDoList", leaseCollectionPrefix:"fn1")//, convertToPascalCase:true) //.ChangeFeedFunction("Items", "ToDoList") //.ChangeFeedFunction("Items", "ToDoList", leaseCollectionPrefix:"fn2") - )*/ + ) .HttpRoute("/Add", route => route .HttpFunction(AuthorizationTypeEnum.Anonymous,HttpMethod.Post) .OpenApiDescription("Adds two numbers together") diff --git a/Samples/Scratch/SwaggerBuildOut/SwaggerBuildOut.csproj b/Samples/Scratch/SwaggerBuildOut/SwaggerBuildOut.csproj index c42adcf6..25fcc4b4 100644 --- a/Samples/Scratch/SwaggerBuildOut/SwaggerBuildOut.csproj +++ b/Samples/Scratch/SwaggerBuildOut/SwaggerBuildOut.csproj @@ -16,7 +16,7 @@ - + diff --git a/Source/FunctionMonkey.Abstractions/Builders/ICosmosDbFunctionBuilder.cs b/Source/FunctionMonkey.Abstractions/Builders/ICosmosDbFunctionBuilder.cs index 23990fd7..770a9075 100644 --- a/Source/FunctionMonkey.Abstractions/Builders/ICosmosDbFunctionBuilder.cs +++ b/Source/FunctionMonkey.Abstractions/Builders/ICosmosDbFunctionBuilder.cs @@ -35,6 +35,8 @@ public interface ICosmosDbFunctionBuilder /// When set, it defines, in milliseconds, the renew interval for all leases for partitions currently held by an instance. Default is 17000 (17 seconds). /// When set, it defines, in milliseconds, the interval between lease checkpoints. Default is always after a successful Function call. /// Defines the amount of Request Units to assign when the leases collection is created. This setting is only used When createLeaseCollectionIfNotExists is set to true. This parameter is automatically set when the binding is created using the portal. + /// If true (default value) this will create a timer function that will output a remaining work estimate to the log - the metric name will be of the form {functionName}RemainingWork + /// The frequency that the monitor timer runs - defaults to once per every 5 seconds /// ICosmosDbFunctionOptionBuilder ChangeFeedFunction(string collectionName, string databaseName, @@ -50,7 +52,9 @@ ICosmosDbFunctionOptionBuilder ChangeFeedFunction(string collectionNam int? leaseExpirationInterval=null, int? leaseRenewInterval=null, int? checkpointFrequency=null, - int? leasesCollectionThroughput=null + int? leasesCollectionThroughput=null, + bool trackRemainingWork=false, + string remainingWorkCronExpression = "*/5 * * * * *" ) where TCommand : ICommand; @@ -85,6 +89,8 @@ ICosmosDbFunctionOptionBuilder ChangeFeedFunction(string collectionNam /// When set, it defines, in milliseconds, the renew interval for all leases for partitions currently held by an instance. Default is 17000 (17 seconds). /// When set, it defines, in milliseconds, the interval between lease checkpoints. Default is always after a successful Function call. /// Defines the amount of Request Units to assign when the leases collection is created. This setting is only used When createLeaseCollectionIfNotExists is set to true. This parameter is automatically set when the binding is created using the portal. + /// If true (default value) this will create a timer function that will output a remaining work estimate to the log - the metric name will be of the form {functionName}RemainingWork + /// The frequency that the monitor timer runs - defaults to once per every 5 seconds /// ICosmosDbFunctionOptionBuilder ChangeFeedFunction(string collectionName, string databaseName, @@ -100,7 +106,9 @@ ICosmosDbFunctionOptionBuilder ChangeFeedFunction netstandard2.0 - 0.20.0-beta000 + 0.21.5-beta000 James Randall https://raw.githubusercontent.com/JamesRandall/AzureFromTheTrenches.Commanding/master/LICENSE https://commanding.azurefromthetrenches.com/ https://github.com/JamesRandall/FunctionMonkey.git - 0.20.0.0 - 0.20.0.0 + 0.21.5.0 + 0.21.5.0 diff --git a/Source/FunctionMonkey.Commanding.Abstractions/FunctionMonkey.Commanding.Abstractions.csproj b/Source/FunctionMonkey.Commanding.Abstractions/FunctionMonkey.Commanding.Abstractions.csproj index f7e0ded8..bb89e692 100644 --- a/Source/FunctionMonkey.Commanding.Abstractions/FunctionMonkey.Commanding.Abstractions.csproj +++ b/Source/FunctionMonkey.Commanding.Abstractions/FunctionMonkey.Commanding.Abstractions.csproj @@ -2,14 +2,14 @@ netstandard2.0 - 0.20.0-beta000 + 0.21.5-beta000 James Randall James Randall https://raw.githubusercontent.com/JamesRandall/AzureFromTheTrenches.Commanding/master/LICENSE https://commanding.azurefromthetrenches.com/ https://github.com/JamesRandall/FunctionMonkey.git - 0.20.0.0 - 0.20.0.0 + 0.21.5.0 + 0.21.5.0 diff --git a/Source/FunctionMonkey.Commanding.Cosmos.Abstractions/FunctionMonkey.Commanding.Cosmos.Abstractions.csproj b/Source/FunctionMonkey.Commanding.Cosmos.Abstractions/FunctionMonkey.Commanding.Cosmos.Abstractions.csproj index 0778b96d..46b62205 100644 --- a/Source/FunctionMonkey.Commanding.Cosmos.Abstractions/FunctionMonkey.Commanding.Cosmos.Abstractions.csproj +++ b/Source/FunctionMonkey.Commanding.Cosmos.Abstractions/FunctionMonkey.Commanding.Cosmos.Abstractions.csproj @@ -2,9 +2,9 @@ netstandard2.0 - 0.20.0-beta000 - 0.20.0.0 - 0.20.0.0 + 0.21.5-beta000 + 0.21.5.0 + 0.21.5.0 diff --git a/Source/FunctionMonkey.Compiler/FunctionMonkey.Compiler.csproj b/Source/FunctionMonkey.Compiler/FunctionMonkey.Compiler.csproj index 9c2259de..10016549 100644 --- a/Source/FunctionMonkey.Compiler/FunctionMonkey.Compiler.csproj +++ b/Source/FunctionMonkey.Compiler/FunctionMonkey.Compiler.csproj @@ -3,7 +3,7 @@ Exe netcoreapp2.1 - 0.20.0-beta000 + 0.21.5-beta000 FunctionMonkey.Compiler FunctionMonkey.Compiler @@ -144,6 +144,7 @@ + @@ -296,6 +297,7 @@ + @@ -358,8 +360,8 @@ $(MSBuildProjectDirectory)/bin/$(Configuration)/publish/ $(IntermediatePackDir)$(TargetFramework)/ publishDir=$([MSBuild]::NormalizeDirectory($(IntermediatePackDir))) - 0.20.0.0 - 0.20.0.0 + 0.21.5.0 + 0.21.5.0 diff --git a/Source/FunctionMonkey.Compiler/FunctionMonkey.Compiler.nuspec b/Source/FunctionMonkey.Compiler/FunctionMonkey.Compiler.nuspec index 46b07e59..a413892b 100644 --- a/Source/FunctionMonkey.Compiler/FunctionMonkey.Compiler.nuspec +++ b/Source/FunctionMonkey.Compiler/FunctionMonkey.Compiler.nuspec @@ -2,7 +2,7 @@ FunctionMonkey.Compiler - 0.20.0-beta000 + 0.21.5-beta000 James Randall Generates Azure Functions from command registrations https://raw.githubusercontent.com/JamesRandall/AzureFromTheTrenches.Commanding/master/LICENSE diff --git a/Source/FunctionMonkey.Compiler/Implementation/AssemblyCompiler.cs b/Source/FunctionMonkey.Compiler/Implementation/AssemblyCompiler.cs index e697679d..11f7405a 100644 --- a/Source/FunctionMonkey.Compiler/Implementation/AssemblyCompiler.cs +++ b/Source/FunctionMonkey.Compiler/Implementation/AssemblyCompiler.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Data.Common; using System.Diagnostics; using System.IO; using System.Linq; @@ -15,6 +16,7 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.Azure.Documents; +using Microsoft.Azure.Documents.ChangeFeedProcessor; using Microsoft.Azure.ServiceBus; using Microsoft.Azure.WebJobs; using Microsoft.CodeAnalysis; @@ -265,7 +267,10 @@ private static IReadOnlyCollection BuildCandidateReferenceList(IReadOnly typeof(StringValues).GetTypeInfo().Assembly.Location, typeof(ExecutionContext).GetTypeInfo().Assembly.Location, typeof(Document).GetTypeInfo().Assembly.Location, - typeof(Message).GetTypeInfo().Assembly.Location + typeof(Message).GetTypeInfo().Assembly.Location, + typeof(ChangeFeedProcessorBuilder).Assembly.Location, + typeof(TimerInfo).Assembly.Location, + typeof(DbConnectionStringBuilder).Assembly.Location }; if (target == FunctionCompiler.TargetEnum.NETCore21) diff --git a/Source/FunctionMonkey.Compiler/Implementation/JsonCompiler.cs b/Source/FunctionMonkey.Compiler/Implementation/JsonCompiler.cs index 03e6a9b7..d076a907 100644 --- a/Source/FunctionMonkey.Compiler/Implementation/JsonCompiler.cs +++ b/Source/FunctionMonkey.Compiler/Implementation/JsonCompiler.cs @@ -33,6 +33,26 @@ public void Compile(IReadOnlyCollection functionDefi string json = template(functionDefinition); WriteFunctionTemplate(outputBinaryFolder, functionDefinition.Name, json); + + if (functionDefinition is CosmosDbFunctionDefinition cosmosDbFunctionDefinition) + { + if (cosmosDbFunctionDefinition.TrackRemainingWork) + { + TimerFunctionDefinition cosmosMonitorDefinition = new TimerFunctionDefinition(functionDefinition.CommandType) + { + AssemblyName = cosmosDbFunctionDefinition.AssemblyName, + CommandDeserializerType = null, + CommandType = null, + CronExpression = cosmosDbFunctionDefinition.RemainingWorkCronExpression, + FunctionClassTypeName = $"{functionDefinition.Namespace}.Monitor{functionDefinition.Name}" + }; + string timerTemplateSource = _templateProvider.GetJsonTemplate(cosmosMonitorDefinition); + Func timerTemplate = Handlebars.Compile(timerTemplateSource); + + string timerJson = timerTemplate(cosmosMonitorDefinition); + WriteFunctionTemplate(outputBinaryFolder, $"Monitor{functionDefinition.Name}", timerJson); + } + } } if (openApiOutputModel != null && openApiOutputModel.IsConfiguredForUserInterface) diff --git a/Source/FunctionMonkey.Compiler/Templates/cosmosdb.csharp.handlebars b/Source/FunctionMonkey.Compiler/Templates/cosmosdb.csharp.handlebars index 02047da1..93d701b5 100644 --- a/Source/FunctionMonkey.Compiler/Templates/cosmosdb.csharp.handlebars +++ b/Source/FunctionMonkey.Compiler/Templates/cosmosdb.csharp.handlebars @@ -1,6 +1,9 @@ using System; using System.IO; using System.Collections.Generic; +using Microsoft.Azure.Documents.Client; +using Microsoft.Azure.Documents.ChangeFeedProcessor.PartitionManagement; +using Microsoft.Azure.Documents.ChangeFeedProcessor; using System.Text; using System.Threading.Tasks; using Microsoft.Azure.WebJobs; @@ -8,6 +11,8 @@ using Microsoft.Azure.Documents; using Microsoft.Extensions.Logging; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; +using System.Data.Common; +using System.Threading; namespace {{Namespace}} { @@ -31,7 +36,7 @@ namespace {{Namespace}} )] IReadOnlyList input, ILogger log, - ExecutionContext executionContext) + Microsoft.Azure.WebJobs.ExecutionContext executionContext) { log.LogInformation("Cosmos change feed trigger function {{Name}} processed a request."); FunctionMonkey.Runtime.FunctionProvidedLogger.Value = log; @@ -86,4 +91,93 @@ namespace {{Namespace}} {{/if}} } } + +{{#if TrackRemainingWork}} + public static class Monitor{{Name}} + { + [FunctionName("Monitor{{Name}}")] + public static async Task Run( + [TimerTrigger("{{RemainingWorkCronExpression}}", RunOnStartup = true)] TimerInfo myTimer, + [CosmosDB("{{DatabaseName}}", "{{CollectionName}}", ConnectionStringSetting = "{{ConnectionStringName}}")] DocumentClient monitoredCollectionClient, + [CosmosDB("{{DatabaseName}}", "{{LeaseCollectionName}}", ConnectionStringSetting = "{{ConnectionStringName}}")] DocumentClient leaseCollectionClient, + ILogger log) + { + var estimator = GetRemainingWorkEstimator(monitoredCollectionClient, leaseCollectionClient); + var remainingWork = await estimator.GetEstimatedRemainingWork(); + // Send custom metric to App Insight + log.LogInformation(remainingWork.ToString()); + log.LogMetric("{{Name}}:RemainingWork", remainingWork); + } + + private static Lazy _remainingWorkEstimator; + + private static IRemainingWorkEstimator GetRemainingWorkEstimator( + DocumentClient monitoredCollectionClient, + DocumentClient leaseCollectionClient) + { + if (_remainingWorkEstimator == null) + { + // Pull the Connection string from the environment, Environment.GetEnvironmentVariable will read the local.settings.json file or the deployed Function App configuration + CosmosDBConnectionString cosmosDBConnectionString = new CosmosDBConnectionString(Environment.GetEnvironmentVariable("{{ConnectionStringName}}")); + CosmosDBConnectionString leaseConnectionString = new CosmosDBConnectionString(Environment.GetEnvironmentVariable("{{LeaseConnectionStringName}}")); + _remainingWorkEstimator = new Lazy(() => + { + var builder = new ChangeFeedProcessorBuilder() + .WithHostName("Monitor{{Name}}") // Can be a random name + .WithProcessorOptions(new ChangeFeedProcessorOptions() + { + LeasePrefix = "{{LeaseCollectionPrefix}}" + }) + .WithFeedCollection(new DocumentCollectionInfo() + { + Uri = cosmosDBConnectionString.ServiceEndpoint, + MasterKey = cosmosDBConnectionString.AuthKey, + CollectionName = "{{CollectionName}}", + DatabaseName = "{{DatabaseName}}" + }) + .WithLeaseCollection(new DocumentCollectionInfo() + { + Uri = leaseConnectionString.ServiceEndpoint, + MasterKey = leaseConnectionString.AuthKey, + CollectionName = "{{LeaseCollectionName}}", + DatabaseName = "{{DatabaseName}}" + }) + .WithFeedDocumentClient(monitoredCollectionClient) + .WithLeaseDocumentClient(leaseCollectionClient); + + return builder.BuildEstimatorAsync().Result; + + }); + } + + return _remainingWorkEstimator.Value; + } + + private class CosmosDBConnectionString + { + public CosmosDBConnectionString(string connectionString) + { + // Use this generic builder to parse the connection string + DbConnectionStringBuilder builder = new DbConnectionStringBuilder + { + ConnectionString = connectionString + }; + + if (builder.TryGetValue("AccountKey", out object key)) + { + AuthKey = key.ToString(); + } + + if (builder.TryGetValue("AccountEndpoint", out object uri)) + { + ServiceEndpoint = new Uri(uri.ToString()); + } + } + + public Uri ServiceEndpoint { get; set; } + + public string AuthKey { get; set; } + } + } +{{/if}} } diff --git a/Source/FunctionMonkey.Compiler/Templates/cosmosdbMonitor.json.handlebars b/Source/FunctionMonkey.Compiler/Templates/cosmosdbMonitor.json.handlebars new file mode 100644 index 00000000..46166f3d --- /dev/null +++ b/Source/FunctionMonkey.Compiler/Templates/cosmosdbMonitor.json.handlebars @@ -0,0 +1,16 @@ +{ + "generatedBy": "Microsoft.NET.Sdk.Functions-1.0.19", + "configurationSource": "attributes", + "bindings": [ + { + "type": "timerTrigger", + "schedule": "{{CronExpression}}", + "useMonitor": true, + "runOnStartup": false, + "name": "myTimer" + } + ], + "disabled": false, + "scriptFile": "../bin/{{AssemblyName}}", + "entryPoint": "{{FunctionClassTypeName}}.Run" +} diff --git a/Source/FunctionMonkey.Compiler/Templates/http.csharp.handlebars b/Source/FunctionMonkey.Compiler/Templates/http.csharp.handlebars index ccbb765f..628ae8ea 100644 --- a/Source/FunctionMonkey.Compiler/Templates/http.csharp.handlebars +++ b/Source/FunctionMonkey.Compiler/Templates/http.csharp.handlebars @@ -155,27 +155,25 @@ namespace {{Namespace}} } {{#if HeaderBindingConfiguration}} - {{#if HeaderBindingConfiguration.Enabled}} - string headerName; - {{#each PossibleBindingProperties}} - {{#unless IsFormCollection}} - headerName = "{{{mappedHeaderNameForProperty ../HeaderBindingConfiguration}}}"; - if (!string.IsNullOrWhiteSpace(headerName)) - { - if (req.Headers.TryGetValue(headerName, out var stringValues)) - { - string headerValue = stringValues.FirstOrDefault(); - {{#if IsString}} - command.{{Name}} = headerValue; - {{else}} - {{TypeName}}.TryParse(headerValue, out var candidate); - command.{{Name}} = candidate; - {{/if}} - } - } - {{/unless}} - {{/each}} - {{/if}} + string headerName; + {{#each PossibleBindingProperties}} + {{#unless IsFormCollection}} + headerName = "{{{mappedHeaderNameForProperty ../HeaderBindingConfiguration}}}"; + if (!string.IsNullOrWhiteSpace(headerName)) + { + if (req.Headers.TryGetValue(headerName, out var stringValues)) + { + string headerValue = stringValues.FirstOrDefault(); + {{#if IsString}} + command.{{Name}} = headerValue; + {{else}} + {{TypeName}}.TryParse(headerValue, out var candidate); + command.{{Name}} = candidate; + {{/if}} + } + } + {{/unless}} + {{/each}} {{/if}} {{#each RouteParameters}} diff --git a/Source/FunctionMonkey.FluentValidation/FunctionMonkey.FluentValidation.csproj b/Source/FunctionMonkey.FluentValidation/FunctionMonkey.FluentValidation.csproj index 010b44d5..6d080334 100644 --- a/Source/FunctionMonkey.FluentValidation/FunctionMonkey.FluentValidation.csproj +++ b/Source/FunctionMonkey.FluentValidation/FunctionMonkey.FluentValidation.csproj @@ -2,14 +2,14 @@ netstandard2.0 - 0.20.0-beta000 + 0.21.5-beta000 James Randall James Randall https://raw.githubusercontent.com/JamesRandall/AzureFromTheTrenches.Commanding/master/LICENSE https://commanding.azurefromthetrenches.com/ https://github.com/JamesRandall/FunctionMonkey.git - 0.20.0.0 - 0.20.0.0 + 0.21.5.0 + 0.21.5.0 diff --git a/Source/FunctionMonkey/Builders/CosmosDbFunctionBuilder.cs b/Source/FunctionMonkey/Builders/CosmosDbFunctionBuilder.cs index acbcc3f5..b9376e25 100644 --- a/Source/FunctionMonkey/Builders/CosmosDbFunctionBuilder.cs +++ b/Source/FunctionMonkey/Builders/CosmosDbFunctionBuilder.cs @@ -1,8 +1,5 @@ -using System; -using System.Collections.Generic; -using System.Text; +using System.Collections.Generic; using AzureFromTheTrenches.Commanding.Abstractions; -using FunctionMonkey.Abstractions; using FunctionMonkey.Abstractions.Builders; using FunctionMonkey.Commanding.Cosmos.Abstractions; using FunctionMonkey.Extensions; @@ -40,7 +37,9 @@ public ICosmosDbFunctionOptionBuilder ChangeFeedFunction( int? leaseExpirationInterval = null, int? leaseRenewInterval = null, int? checkpointFrequency = null, - int? leasesCollectionThroughput = null + int? leasesCollectionThroughput = null, + bool trackRemainingWork = false, + string remainingWorkCronExpression = "*/5 * * * * *" ) where TCommand : ICommand { CosmosDbFunctionDefinition definition = new CosmosDbFunctionDefinition(typeof(TCommand)) @@ -61,7 +60,9 @@ public ICosmosDbFunctionOptionBuilder ChangeFeedFunction( LeaseExpirationInterval = leaseExpirationInterval, LeaseRenewInterval = leaseRenewInterval, CheckpointFrequency = checkpointFrequency, - LeasesCollectionThroughput = leasesCollectionThroughput + LeasesCollectionThroughput = leasesCollectionThroughput, + TrackRemainingWork = trackRemainingWork, + RemainingWorkCronExpression = remainingWorkCronExpression }; _functionDefinitions.Add(definition); return new CosmosDbFunctionOptionBuilder(this, definition); @@ -82,7 +83,10 @@ public ICosmosDbFunctionOptionBuilder ChangeFeedFunction(string collec bool createLeaseCollectionIfNotExists = false, bool startFromBeginning = false, bool convertToPascalCase = false, string leaseCollectionPrefix = null, int? maxItemsPerInvocation = null, int? feedPollDelay = null, int? leaseAcquireInterval = null, int? leaseExpirationInterval = null, int? leaseRenewInterval = null, - int? checkpointFrequency = null, int? leasesCollectionThroughput = null) where TCommand : ICommand + int? checkpointFrequency = null, int? leasesCollectionThroughput = null, + bool trackRemainingWork = true, + string remainingWorkCronExpression = "*/1 * * * * *") where TCommand : ICommand { return _underlyingBuilder.ChangeFeedFunction(collectionName, databaseName, leaseCollectionName, leaseDatabaseName, createLeaseCollectionIfNotExists, startFromBeginning, convertToPascalCase, leaseCollectionPrefix, maxItemsPerInvocation, feedPollDelay, leaseAcquireInterval, leaseExpirationInterval, leaseRenewInterval, - checkpointFrequency, leasesCollectionThroughput); + checkpointFrequency, leasesCollectionThroughput, trackRemainingWork, remainingWorkCronExpression); } public ICosmosDbFunctionOptionBuilder ChangeFeedFunction(string collectionName, string databaseName, @@ -40,14 +42,16 @@ public ICosmosDbFunctionOptionBuilder ChangeFeedFunction(collectionName, databaseName, leaseCollectionName, leaseDatabaseName, createLeaseCollectionIfNotExists, startFromBeginning, convertToPascalCase, leaseCollectionPrefix, maxItemsPerInvocation, feedPollDelay, leaseAcquireInterval, leaseExpirationInterval, leaseRenewInterval, - checkpointFrequency, leasesCollectionThroughput); + checkpointFrequency, leasesCollectionThroughput, trackRemainingWork, remainingWorkCronExpression); } public ICosmosDbFunctionOptionBuilder Options(Action options) diff --git a/Source/FunctionMonkey/FunctionMonkey.csproj b/Source/FunctionMonkey/FunctionMonkey.csproj index 2b990daa..915a7cdf 100644 --- a/Source/FunctionMonkey/FunctionMonkey.csproj +++ b/Source/FunctionMonkey/FunctionMonkey.csproj @@ -2,7 +2,7 @@ netstandard2.0 - 0.20.0-beta000 + 0.21.5-beta000 false James Randall @@ -10,8 +10,8 @@ https://commanding.azurefromthetrenches.com/ https://github.com/JamesRandall/FunctionMonkey.git true - 0.20.0.0 - 0.20.0.0 + 0.21.5.0 + 0.21.5.0 diff --git a/Source/FunctionMonkey/Model/CosmosDbFunctionDefinition.cs b/Source/FunctionMonkey/Model/CosmosDbFunctionDefinition.cs index 977a0de6..0ae1be9e 100644 --- a/Source/FunctionMonkey/Model/CosmosDbFunctionDefinition.cs +++ b/Source/FunctionMonkey/Model/CosmosDbFunctionDefinition.cs @@ -8,6 +8,7 @@ public class CosmosDbFunctionDefinition : AbstractFunctionDefinition { public CosmosDbFunctionDefinition(Type commandType) : base("CosmosFn", commandType) { + TrackRemainingWork = true; } public string DatabaseName { get; set; } @@ -36,6 +37,8 @@ public CosmosDbFunctionDefinition(Type commandType) : base("CosmosFn", commandTy public string LeaseCollectionPrefix { get; set; } + public bool TrackRemainingWork { get; set; } + public int? MaxItemsPerInvocation { get; set; } public int? FeedPollDelay { get; set; } @@ -46,5 +49,6 @@ public CosmosDbFunctionDefinition(Type commandType) : base("CosmosFn", commandTy public int? LeasesCollectionThroughput { get; set; } public string ErrorHandlerTypeName { get; set; } public Type ErrorHandlerType { get; set; } + public string RemainingWorkCronExpression { get; set; } } } diff --git a/Tests/FunctionMonkey.Tests.Integration/FunctionMonkey.Tests.Integration.csproj b/Tests/FunctionMonkey.Tests.Integration/FunctionMonkey.Tests.Integration.csproj index b7df3421..1cd5df07 100644 --- a/Tests/FunctionMonkey.Tests.Integration/FunctionMonkey.Tests.Integration.csproj +++ b/Tests/FunctionMonkey.Tests.Integration/FunctionMonkey.Tests.Integration.csproj @@ -7,7 +7,7 @@ - +