diff --git a/Directory.Build.props b/Directory.Build.props index 15e97ba3..5537b276 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,5 +1,5 @@ - 4.0.51-beta.4 + 4.0.52-beta.4 \ No newline at end of file diff --git a/Source/FunctionMonkey.Compiler.Core/Templates/AzureFunctions/signalrbindingexpressionnegotiate.csharp.handlebars b/Source/FunctionMonkey.Compiler.Core/Templates/AzureFunctions/signalrbindingexpressionnegotiate.csharp.handlebars index 5b262597..0ff5ffe0 100644 --- a/Source/FunctionMonkey.Compiler.Core/Templates/AzureFunctions/signalrbindingexpressionnegotiate.csharp.handlebars +++ b/Source/FunctionMonkey.Compiler.Core/Templates/AzureFunctions/signalrbindingexpressionnegotiate.csharp.handlebars @@ -37,6 +37,7 @@ namespace {{Namespace}} ) { log.LogInformation("HTTP trigger function {{Name}} processed a request."); + FunctionMonkey.PluginFunctions pluginFunctions = FunctionMonkey.Runtime.PluginFunctions["{{Name}}"]; FunctionMonkey.Runtime.FunctionProvidedLogger.Value = log; @@ -68,20 +69,17 @@ namespace {{Namespace}} { return new UnauthorizedResult(); } - var tokenValidator = (FunctionMonkey.Abstractions.ITokenValidator) - FunctionMonkey.Runtime.ServiceProvider.GetService(typeof({{TokenValidatorTypeName}})); - principal = await tokenValidator.ValidateAsync(authorizationHeader); + + principal = await pluginFunctions.ValidateToken(authorizationHeader); if (principal == null) { return new UnauthorizedResult(); } - contextSetter.SetHttpContext(principal, requestUrl, headerDictionary); + contextSetter.SetHttpContext(principal, requestUrl, headerDictionary); {{/if}} {{#if AuthorizesClaims}} - var claimsPrincipalAuthorization = ({{ClaimsPrincipalAuthorizationTypeName}}) - FunctionMonkey.Runtime.ServiceProvider.GetService(typeof({{ClaimsPrincipalAuthorizationTypeName}})); - var claimsPrincipalAuthorizationResult = await claimsPrincipalAuthorization.IsAuthorized(principal, req.Method, requestUrl); + var claimsPrincipalAuthorizationResult = await pluginFunctions.IsAuthorized(principal, req.Method, requestUrl); if (!claimsPrincipalAuthorizationResult) { return new UnauthorizedResult(); diff --git a/Source/FunctionMonkey.Compiler.Core/Templates/AzureFunctions/signalrcommandnegotiate.csharp.handlebars b/Source/FunctionMonkey.Compiler.Core/Templates/AzureFunctions/signalrcommandnegotiate.csharp.handlebars index be4cce77..2fd6d47c 100644 --- a/Source/FunctionMonkey.Compiler.Core/Templates/AzureFunctions/signalrcommandnegotiate.csharp.handlebars +++ b/Source/FunctionMonkey.Compiler.Core/Templates/AzureFunctions/signalrcommandnegotiate.csharp.handlebars @@ -42,6 +42,7 @@ namespace {{Namespace}} ) { log.LogInformation("HTTP trigger function {{Name}} processed a request."); + FunctionMonkey.PluginFunctions pluginFunctions = FunctionMonkey.Runtime.PluginFunctions["{{Name}}"]; FunctionMonkey.Runtime.FunctionProvidedLogger.Value = log; @@ -73,39 +74,27 @@ namespace {{Namespace}} { return new UnauthorizedResult(); } - var tokenValidator = (FunctionMonkey.Abstractions.ITokenValidator) - FunctionMonkey.Runtime.ServiceProvider.GetService(typeof({{TokenValidatorTypeName}})); - principal = await tokenValidator.ValidateAsync(authorizationHeader); + + principal = await pluginFunctions.ValidateToken(authorizationHeader); if (principal == null) { return new UnauthorizedResult(); } - contextSetter.SetHttpContext(principal, requestUrl, headerDictionary); + contextSetter.SetHttpContext(principal, requestUrl, headerDictionary); {{/if}} {{#if AuthorizesClaims}} - var claimsPrincipalAuthorization = ({{ClaimsPrincipalAuthorizationTypeName}}) - FunctionMonkey.Runtime.ServiceProvider.GetService(typeof({{ClaimsPrincipalAuthorizationTypeName}})); - var claimsPrincipalAuthorizationResult = await claimsPrincipalAuthorization.IsAuthorized(principal, req.Method, requestUrl); + var claimsPrincipalAuthorizationResult = await pluginFunctions.IsAuthorized(principal, req.Method, requestUrl); if (!claimsPrincipalAuthorizationResult) { return new UnauthorizedResult(); } {{/if}} - {{#if SerializerNamingStrategyTypeName}} - var serializerNamingStrategy = new {{SerializerNamingStrategyTypeName}}(); - var deserializerNamingStrategy = new {{DeserializerNamingStrategyTypeName}}(); - FunctionMonkey.Abstractions.ISerializer serializer = new FunctionMonkey.Serialization.NamingStrategyJsonSerializer(deserializerNamingStrategy, serializerNamingStrategy); - {{else}} - var serializer = (FunctionMonkey.Abstractions.ISerializer) - FunctionMonkey.Runtime.ServiceProvider.GetService(typeof({{CommandDeseriaizerTypeName}})); - {{/if}} - - {{CommandTypeName}} command; + {{{CommandTypeName}}} command; string contentType = req.ContentType?.ToLower() ?? "application/json"; {{#if IsStreamCommand}} - command = new {{CommandTypeName}}() { + command = new {{{CommandTypeName}}}() { Stream = req.Body }; {{else}} @@ -119,16 +108,34 @@ namespace {{Namespace}} if (!System.String.IsNullOrWhiteSpace(requestBody)) { - command = serializer.Deserialize<{{CommandTypeName}}>(requestBody); + try + { + command = ({{{CommandTypeName}}})pluginFunctions.Deserialize(requestBody, true); // true is to enforce security properties + } + catch(FunctionMonkey.DeserializationException dex) + { + if (dex.LineNumber != -1) + { + System.Text.StringBuilder sbError = new System.Text.StringBuilder("Invalid type in message body at line "); + sbError.Append(dex.LineNumber); + sbError.Append(" for path "); + sbError.Append(dex.Path); + return CreateBadParameterResponse(sbError.ToString()); + } + else + { + return CreateBadParameterResponse(dex.Message); + } + } } else { - command = new {{CommandTypeName}}(); + command = CreateNewCommand(); } } else { - command = new {{CommandTypeName}}(); + command = CreateNewCommand(); } {{/if}} @@ -136,174 +143,327 @@ namespace {{Namespace}} command.{{Name}} = req.Form; {{/each}} + {{#if UsesImmutableTypes}} + Dictionary imtPropertyValues = new Dictionary(); + {{/if}} + Microsoft.Extensions.Primitives.StringValues queryParameterValues; - string method = req.Method.ToUpper(); - if (method == "GET" || method=="DELETE") - { - {{#each QueryParameters}} - {{#unless IsFormCollection}} - if (req.Query.TryGetValue("{{Name}}", out queryParameterValues)) + {{#each QueryParameters}} + {{#unless IsFormCollection}} + if (req.Query.TryGetValue("{{Name}}", out queryParameterValues)) + { + + {{#if IsCollection}} + {{#if IsFSharpList}} + var collection = {{{CollectionInstanceTypeName}}}.Empty; + {{else if IsCollectionArray}} + var collection = new {{{DiscreteTypeName}}}[queryParameterValues.Count]; + int queryParameterArrayIndex = 0; + {{else}} + var collection = new {{{CollectionInstanceTypeName}}}(); + {{/if}} + {{/if}} + foreach(string queryParameterValue in queryParameterValues) { + {{#if IsFSharpOptionType}} + {{{DiscreteTypeName}}} parsedValue = {{{DiscreteTypeName}}}.None; + {{else}} + {{{DiscreteTypeName}}} parsedValue = default({{{DiscreteTypeName}}}); + {{/if}} + {{#if IsString}} - command.{{Name}} = queryParameterValues[0]; + parsedValue = queryParameterValue; {{else if IsEnum}} { - {{{TypeName}}} result; - if (System.Enum.TryParse<{{TypeName}}>(queryParameterValues[0], out result)) + if (!System.Enum.TryParse<{{DiscreteTypeName}}>(queryParameterValue, out parsedValue)) { - command.{{Name}} = result; + return CreateBadParameterResponse("Invalid type for query parameter {{{Name}}}"); } } + {{else if IsFSharpOptionType}} + {{#if FSharpOptionInnerTypeIsString}} + parsedValue = new Microsoft.FSharp.Core.FSharpOption(queryParameterValue); + {{else}} + if (!String.IsNullOrEmpty(queryParameterValue)) + { + if ({{{FSharpOptionInnerTypeName}}}.TryParse(queryParameterValue, out var qcandidate{{@index}})) + { + parsedValue = new Microsoft.FSharp.Core.FSharpOption<{{{FSharpOptionInnerTypeName}}}>(qcandidate{{@index}}); + } + else + { + return CreateBadParameterResponse("Invalid type for query parameter {{{Name}}}"); + } + } + {{/if}} {{else if IsNullable}} { {{#if IsNullableTypeHasTryParseMethod}} - { - {{{TypeName}}} nullableValue; - if(TryParseNullable(queryParameterValues[0], {{NullableType}}.TryParse, out nullableValue)) + if (queryParameterValue != null) { - command.{{Name}} = nullableValue; + if(!TryParseNullable(queryParameterValue, {{NullableType}}.TryParse, out parsedValue)) + { + return CreateBadParameterResponse("Invalid type for query parameter {{{Name}}}"); + } } - } {{/if}} } {{else}} - {{TypeName}}.TryParse(queryParameterValues[0], out var candidate); - command.{{Name}} = candidate; + if (!{{{DiscreteTypeName}}}.TryParse(queryParameterValue, out parsedValue)) + { + return CreateBadParameterResponse("Invalid type for query parameter {{{Name}}}"); + } + {{/if}} + + {{#if IsCollection}} + {{#if IsFSharpList}} + collection = new {{{CollectionInstanceTypeName}}}(parsedValue, collection); + {{else if IsCollectionArray}} + collection[queryParameterArrayIndex] = parsedValue; + queryParameterArrayIndex++; + {{else}} + collection.Add(parsedValue); + {{/if}} + {{else}} + {{#if ../UsesImmutableTypes}} + imtPropertyValues["{{Name}}"] = parsedValue; + {{else}} + command.{{Name}} = parsedValue; + {{/if}} + break; {{/if}} } - {{/unless}} - {{/each}} - } - + + {{#if IsCollection}} + {{#if ../UsesImmutableTypes}} + imtPropertyValues["{{Name}}"] = collection; + {{else}} + command.{{Name}} = collection; + {{/if}} + {{/if}} + } + {{/unless}} + {{/each}} + {{#if HeaderBindingConfiguration}} - string headerName; - {{#each QueryParameters}} - {{#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; + {{#unless IsFormCollection}} + string headerName; + {{#each QueryParameters}} + {{#if HasHeaderMapping}} + if (req.Headers.TryGetValue("{{{mappedHeaderNameForProperty ../HeaderBindingConfiguration}}}", out var stringValues{{@index}})) + { + string headerValue = stringValues{{@index}}.FirstOrDefault(); + {{#if IsString}} + {{#if ../UsesImmutableTypes}} + imtPropertyValues["{{Name}}"] = headerValue; + {{else}} + command.{{Name}} = headerValue; + {{/if}} + {{else if IsEnum}} + { + {{{TypeName}}} result; + if (System.Enum.TryParse<{{TypeName}}>(headerValue, out result)) + { + {{#if ../UsesImmutableTypes}} + imtPropertyValues["{{Name}}"] = result; + {{else}} + command.{{Name}} = result; + {{/if}} + } + } + {{else if IsFSharpOptionType}} + {{#if FSharpOptionInnerTypeIsString}} + imtPropertyValues["{{Name}}"] = new Microsoft.FSharp.Core.FSharpOption(headerValue); + {{else}} + if (!String.IsNullOrEmpty(headerValue)) + { + if ({{{FSharpOptionInnerTypeName}}}.TryParse(headerValue, out var hcandidate{{@index}})) + { + imtPropertyValues["{{Name}}"] = new Microsoft.FSharp.Core.FSharpOption<{{{FSharpOptionInnerTypeName}}}>(hcandidate{{@index}}); + } + else + { + return CreateBadParameterResponse("Invalid type for header parameter {{{Name}}}"); + } + } + {{/if}} + {{else if IsNullable}} + {{#if IsNullableTypeHasTryParseMethod}} + { + {{{TypeName}}} nullableValue; + if(TryParseNullable(headerValue, {{{NullableType}}}.TryParse, out nullableValue)) + { + {{#if ../UsesImmutableTypes}} + imtPropertyValues["{{Name}}"] = nullableValue; + {{else}} + command.{{Name}} = nullableValue; + {{/if}} + } + } + {{/if}} + {{else}} + {{{TypeName}}}.TryParse(headerValue, out var hcandidate{{@index}}); + {{#if ../UsesImmutableTypes}} + imtPropertyValues["{{Name}}"] = hcandidate{{@index}}; + {{else}} + command.{{Name}} = hcandidate{{@index}}; + {{/if}} + {{/if}} + } {{/if}} - } - } - {{/unless}} - {{/each}} + {{/each}} + {{/unless}} {{/if}} {{#each RouteParameters}} {{#if IsString}} + {{#if ../UsesImmutableTypes}} + imtPropertyValues["{{Name}}"] = {{RouteName}}; + {{else}} command.{{Name}} = {{RouteName}}; + {{/if}} {{else if IsEnum}} + { + {{{RouteTypeName}}} result; + if (System.Enum.TryParse<{{{RouteTypeName}}}>({{RouteName}}, out result)) { - {{{RouteTypeName}}} result; - if (System.Enum.TryParse<{{RouteTypeName}}>({{RouteName}}, out result)) - { + {{#if ../UsesImmutableTypes}} + imtPropertyValues["{{Name}}"] = result; + {{else}} command.{{Name}} = result; + {{/if}} + } + else + { + return CreateBadParameterResponse("Invalid type for route parameter {{{RouteName}}}"); + } + } + {{else if IsFSharpOptionType}} + {{#if FSharpOptionInnerTypeIsString}} + imtPropertyValues["{{Name}}"] = new Microsoft.FSharp.Core.FSharpOption({{RouteName}}); + {{else}} + if (!String.IsNullOrEmpty({{RouteName}})) + { + if ({{{FSharpOptionInnerTypeName}}}.TryParse({{RouteName}}, out var rcandidate{{@index}})) + { + imtPropertyValues["{{Name}}"] = new Microsoft.FSharp.Core.FSharpOption<{{{FSharpOptionInnerTypeName}}}>(rcandidate{{@index}}); } else { - return CreateResponse(400, "Invalid type for parameter {{RouteName}}", serializer); + return CreateBadParameterResponse("Invalid type for route parameter {{{RouteName}}}"); } } + {{/if}} {{else if IsNullable}} - { + { {{#if IsNullableTypeHasTryParseMethod}} + { + {{{RouteTypeName}}} nullableValue; + if ({{RouteName}} != null) { - {{{RouteTypeName}}} nullableValue; - if ({{RouteName}} != null) + if(TryParseNullable({{RouteName}}, {{{NullableType}}}.TryParse, out nullableValue)) { - if(TryParseNullable({{RouteName}}, {{NullableType}}.TryParse, out nullableValue)) - { + {{#if ../UsesImmutableTypes}} + imtPropertyValues["{{Name}}"] = nullableValue; + {{else}} command.{{Name}} = nullableValue; - } - else - { - return CreateResponse(400, "Invalid type for parameter {{RouteName}}", serializer); - } + {{/if}} + } + else + { + return CreateBadParameterResponse("Invalid type for route parameter {{{RouteName}}}"); } } - {{/if}} } + {{/if}} + } + {{else if IsDiscriminatedUnion}} + { + return CreateBadParameterResponse("Unions not yet supported for route parameters"); + //{{{DiscriminatedUnionUnderlyingTypeName}}} discriminatedUnionValue = pluginFunctions.ParseFromString<{{{RouteTypeName}}}>({{RouteName}})); + } {{else}} - if ({{TypeName}}.TryParse({{RouteName}}, out var candidate)) - { - command.{{Name}} = candidate; - } - else - { - return CreateResponse(400, "Invalid type for parameter {{RouteName}}", serializer); - } + if ({{{TypeName}}}.TryParse({{RouteName}}, out var rcandidate{{@index}})) + { + {{#if ../UsesImmutableTypes}} + imtPropertyValues["{{Name}}"] = rcandidate{{@index}}; + {{else}} + command.{{Name}} = rcandidate{{@index}}; + {{/if}} + } + else + { + return CreateBadParameterResponse("Invalid type for route parameter {{{RouteName}}}"); + } {{/if}} {{/each}} - {{#if HasCommandTransformer}} - FunctionMonkey.Abstractions.ICommandTransformer transformer = (FunctionMonkey.Abstractions.ICommandTransformer)FunctionMonkey.Runtime.ServiceProvider.GetService(typeof({{{CommandTransformerTypeName}}})); - command = transformer.Transform(command); + {{#if UsesImmutableTypes}} + command = MergeCommandWithParameters(imtPropertyValues, command); {{/if}} {{#if ValidatesToken}} - var claimsBinder = (FunctionMonkey.Abstractions.ICommandClaimsBinder) - FunctionMonkey.Runtime.ServiceProvider.GetService(typeof(FunctionMonkey.Abstractions.ICommandClaimsBinder)); - var claimsBinderTask = claimsBinder.BindAsync(principal, command); - if (claimsBinderTask == null) - { - claimsBinder.Bind(principal, command); - } - else - { - await claimsBinderTask; - } + {{#unless CommandTypeIsUnit}} + command = ({{{CommandTypeName}}})(await pluginFunctions.BindClaims(principal, command)); + {{/unless}} {{/if}} - {{#if HasHttpResponseHandler}} - var responseHandler = ({{HttpResponseHandlerType}})FunctionMonkey.Runtime.ServiceProvider.GetService(typeof({{HttpResponseHandlerType}})); + {{#if HasCommandTransformer}} + FunctionMonkey.Abstractions.ICommandTransformer transformer = (FunctionMonkey.Abstractions.ICommandTransformer)FunctionMonkey.Runtime.ServiceProvider.GetService(typeof({{{CommandTransformerTypeName}}})); + command = transformer.Transform(command); {{/if}} {{#if IsUsingValidator}} - var validator = (FunctionMonkey.Abstractions.Validation.IValidator) - FunctionMonkey.Runtime.ServiceProvider.GetService(typeof(FunctionMonkey.Abstractions.Validation.IValidator)); - var validationResult = validator.Validate(command); - if (!validationResult.IsValid) + {{#unless CommandTypeIsUnit}} + var validationResult = pluginFunctions.Validate(command); + if (!pluginFunctions.IsValid(validationResult)) { {{#if HasHttpResponseHandler}} - var validatorResponseTask = responseHandler.CreateValidationFailureResponse(command, validationResult); + var validatorResponseTask = pluginFunctions.CreateValidationFailureResponse(command, validationResult); var handledValidationResponse = validatorResponseTask != null ? (await validatorResponseTask) : null; - return handledValidationResponse ?? CreateResponse(400, validationResult, serializer); + return handledValidationResponse ?? CreateResponse(400, validationResult, pluginFunctions); {{else}} - return CreateResponse(400, validationResult, serializer); + return CreateResponse(400, validationResult, pluginFunctions); {{/if}} } + {{/unless}} {{/if}} try { - System.Threading.CancellationTokenSource tokenSource = System.Threading.CancellationTokenSource.CreateLinkedTokenSource(req.HttpContext.RequestAborted, cancellationToken); - {{{CommandResultTypeName}}} result = await FunctionMonkey.Runtime.Mediator.RequestAsync<{{{CommandResultTypeName}}}>(command, cancellationToken); + {{#if IsFunctionalFunction}} + Func<{{{CommandTypeName}}}, Task<{{{CommandResultTypeName}}}>> handler = (Func<{{{CommandTypeName}}}, Task<{{{CommandResultTypeName}}}>>)pluginFunctions.Handler; + {{#if FunctionHandlerIsAsync}} + var result = await handler(command); + {{else}} + var result = handler(command); + {{/if}} + {{else}} + System.Threading.CancellationTokenSource tokenSource = System.Threading.CancellationTokenSource.CreateLinkedTokenSource(req.HttpContext.RequestAborted, cancellationToken); + {{#if CommandHasResult}} + {{{CommandResultTypeName}}} result = await FunctionMonkey.Runtime.Mediator.RequestAsync<{{{CommandResultTypeName}}}>(command, tokenSource.Token); + {{else}} + await FunctionMonkey.Runtime.Mediator.SendAsync(command, tokenSource.Token); + {{/if}} + {{/if}} {{#if HasHttpResponseHandler}} {{#if IsValidationResult}} FunctionMonkey.Commanding.Abstractions.Validation.ValidationResult validationResult = (FunctionMonkey.Commanding.Abstractions.Validation.ValidationResult)result; if (!validationResult.IsValid) { - var validatorResponseTask = responseHandler.CreateValidationFailureResponse(command, validationResult); + var validatorResponseTask = pluginFunctions.CreateValidationFailureResponse(command, validationResult); var handledValidationResponse = validatorResponseTask != null ? (await responseTask) : null; - return handledValidationResponse ?? CreateResponse(400, validationResult, serializer); + return handledValidationResponse ?? CreateResponse(400, validationResult, pluginFunctions); } else { - var responseTask = responseHandler.CreateResponse(command, result); + var responseTask = pluginFunctions.CreateResponseForResult(command, result); var handledResponse = responseTask != null ? (await responseTask) : null; return handledResponse ?? CreateSignalRResponse(result); } {{else}} - var responseTask = responseHandler.CreateResponse(command, result); + var responseTask = pluginFunctions.CreateResponseForResult(command, result); var handledResponse = responseTask != null ? (await responseTask) : null; return handledResponse ?? CreateSignalRResponse(result); {{/if}} @@ -312,7 +472,7 @@ namespace {{Namespace}} FunctionMonkey.Commanding.Abstractions.Validation.ValidationResult validationResult = (FunctionMonkey.Commanding.Abstractions.Validation.ValidationResult)result.Result; if (!validationResult.IsValid) { - return CreateResponse(400, validationResult, serializer); + return CreateResponse(400, validationResult, pluginFunctions); } {{/if}} return CreateSignalRResponse(result); @@ -321,12 +481,12 @@ namespace {{Namespace}} catch(System.Exception ex) { {{#if HasHttpResponseHandler}} - var responseTask = responseHandler.CreateResponseFromException(command, ex); + var responseTask = pluginFunctions.CreateResponseFromException(command, ex); var handledResponse = responseTask != null ? (await responseTask) : null; - return handledResponse ?? CreateResponse(500, "Unexpected error", serializer); + return handledResponse ?? CreateResponse(500, "Unexpected error", pluginFunctions); {{else}} log.LogError(ex, $"Error occurred executing command {command.GetType().Name}"); - return CreateResponse(500, "Unexpected error", serializer); + return CreateResponse(500, "Unexpected error", pluginFunctions); {{/if}} } {{/if}} @@ -345,15 +505,24 @@ namespace {{Namespace}} return new OkObjectResult(info); } - public static IActionResult CreateResponse(int code, object content, FunctionMonkey.Abstractions.ISerializer serializer) + public static IActionResult CreateResponse(int code, object content, FunctionMonkey.PluginFunctions pluginFunctions) { ContentResult result = new ContentResult(); - result.Content = serializer.Serialize(content, true); + result.Content = pluginFunctions.Serialize(content, true); result.ContentType = "application/json"; result.StatusCode = code; return result; } + public static IActionResult CreateBadParameterResponse(string message) + { + ContentResult result = new ContentResult(); + result.Content = message; + result.ContentType = "text/plain"; + result.StatusCode = 400; + return result; + } + private static string GetRequestUrl(HttpRequest request) { string str1 = request.Host.Value; @@ -362,7 +531,7 @@ namespace {{Namespace}} string str4 = request.QueryString.Value; return new System.Text.StringBuilder(request.Scheme.Length + "://".Length + str1.Length + str2.Length + str3.Length + str4.Length).Append(request.Scheme).Append("://").Append(str1).Append(str2).Append(str3).Append(str4).ToString(); } - + public delegate System.Boolean TryDelegate(System.String input, out T result); private static System.Boolean TryParseNullable( @@ -376,5 +545,42 @@ namespace {{Namespace}} return success; } + + private static {{{CommandTypeName}}} CreateNewCommand() + { + {{#if UsesImmutableTypes}} + {{#if CommandTypeIsUnit}} + return null; + {{else}} + return new {{{CommandTypeName}}}( + {{#each ImmutableTypeConstructorParameters}} + {{#if IsFSharpOptionType}} + Microsoft.FSharp.Core.FSharpOption<{{{FSharpOptionInnerTypeName}}}>.None + {{else}} + default({{{TypeName}}}) + {{/if}} + {{#unless @last}},{{/unless}} + {{/each}} + ); + {{/if}} + {{else}} + return new {{{CommandTypeName}}}(); + {{/if}} + } + + {{#if UsesImmutableTypes}} + private static {{{CommandTypeName}}} MergeCommandWithParameters(Dictionary values, {{{CommandTypeName}}} originalCommand) + { + {{#if CommandTypeIsUnit}} + return null; + {{else}} + return new {{{CommandTypeName}}}( + {{#each ImmutableTypeConstructorParameters}} + values.TryGetValue("{{Name}}", out object uncastValue{{@index}}) ? ({{{TypeName}}})uncastValue{{@index}} : originalCommand.{{Name}}{{#unless @last}},{{/unless}} + {{/each}} + ); + {{/if}} + } + {{/if}} } } diff --git a/Source/FunctionMonkey.Compiler/FunctionMonkey.Compiler.nuspec b/Source/FunctionMonkey.Compiler/FunctionMonkey.Compiler.nuspec index 70ff1911..379dbe3d 100644 --- a/Source/FunctionMonkey.Compiler/FunctionMonkey.Compiler.nuspec +++ b/Source/FunctionMonkey.Compiler/FunctionMonkey.Compiler.nuspec @@ -2,7 +2,7 @@ FunctionMonkey.Compiler - 4.0.51-beta.4 + 4.0.52-beta.4 James Randall Generates Azure Functions from command registrations https://raw.githubusercontent.com/JamesRandall/FunctionMonkey/master/LICENSE