diff --git a/src/Microsoft.NET.Sdk.Functions.Generator/MethodInfoExtensions.cs b/src/Microsoft.NET.Sdk.Functions.Generator/MethodInfoExtensions.cs
index 98b5804b..88e9cc57 100644
--- a/src/Microsoft.NET.Sdk.Functions.Generator/MethodInfoExtensions.cs
+++ b/src/Microsoft.NET.Sdk.Functions.Generator/MethodInfoExtensions.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Mono.Cecil;
@@ -59,15 +60,20 @@ public static JObject ManualTriggerBinding(this MethodDefinition method)
/// object that represents the passed in .
public static FunctionJsonSchema ToFunctionJson(this MethodDefinition method, string assemblyPath)
{
+ // For every SDK parameter, convert it to a FunctionJson bindings.
+ // Every parameter can potentially contain more than 1 attribute that will be converted into a binding object.
+ var bindingsFromParameters = method.HasNoAutomaticTriggerAttribute() ? new[] { method.ManualTriggerBinding() } : method.Parameters
+ .Select(p => p.ToFunctionJsonBindings())
+ .SelectMany(i => i);
+
+ // Get binding if a return attribute is used.
+ // Ex: [return: Queue("myqueue-items-a", Connection = "MyStorageConnStr")]
+ var returnBindings = GetOutputBindingsFromReturnAttribute(method);
+ var allBindings = bindingsFromParameters.Concat(returnBindings).ToArray();
+
return new FunctionJsonSchema
{
- // For every SDK parameter, convert it to a FunctionJson bindings.
- // Every parameter can potentially contain more than 1 attribute that will be converted into a binding object.
- Bindings = method.HasNoAutomaticTriggerAttribute() ? new[] { method.ManualTriggerBinding() } : method.Parameters
- .Where(p => p.IsWebJobSdkTriggerParameter())
- .Select(p => p.ToFunctionJsonBindings())
- .SelectMany(i => i)
- .ToArray(),
+ Bindings = allBindings,
// Entry point is the fully qualified name of the function
EntryPoint = $"{method.DeclaringType.FullName}.{method.Name}",
ScriptFile = assemblyPath,
@@ -77,6 +83,36 @@ public static FunctionJsonSchema ToFunctionJson(this MethodDefinition method, st
};
}
+ ///
+ /// Gets bindings from return expression used with a binding expression.
+ /// Ex:
+ /// [FunctionName("HttpTriggerWriteToQueue1")]
+ /// [return: Queue("myqueue-items-a", Connection = "MyStorageConnStra")]
+ /// public static string Run([HttpTrigger] HttpRequestMessage request) => "foo";
+ ///
+ private static JObject[] GetOutputBindingsFromReturnAttribute(MethodDefinition method)
+ {
+ if (method.MethodReturnType == null)
+ {
+ return Array.Empty();
+ }
+
+ var outputBindings = new List();
+ foreach (var attribute in method.MethodReturnType.CustomAttributes.Where(a=>a.IsWebJobsAttribute()))
+ {
+ var bindingJObject = attribute.ToReflection().ToJObject();
+
+ // return binding must have the direction attribute set to out.
+ // https://github.com/Azure/azure-functions-host/blob/dev/src/WebJobs.Script/Utility.cs#L561
+ bindingJObject["name"] = "$return";
+ bindingJObject["direction"] = "out";
+
+ outputBindings.Add(bindingJObject);
+ }
+
+ return outputBindings.ToArray();
+ }
+
///
/// Gets a function name from a
///
diff --git a/test/Microsoft.NET.Sdk.Functions.Generator.Tests/FunctionJsonConverterTests.cs b/test/Microsoft.NET.Sdk.Functions.Generator.Tests/FunctionJsonConverterTests.cs
index e7f5db9e..deaea93f 100644
--- a/test/Microsoft.NET.Sdk.Functions.Generator.Tests/FunctionJsonConverterTests.cs
+++ b/test/Microsoft.NET.Sdk.Functions.Generator.Tests/FunctionJsonConverterTests.cs
@@ -1,4 +1,6 @@
using System;
+using System.Collections;
+using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
@@ -8,6 +10,7 @@
using Microsoft.Azure.EventHubs;
using Microsoft.Azure.WebJobs;
using Microsoft.WindowsAzure.Storage.Queue;
+using Newtonsoft.Json.Linq;
using Xunit;
namespace Microsoft.NET.Sdk.Functions.Test
@@ -24,6 +27,26 @@ public class FunctionsClass
[FunctionName("MyHttpTrigger")]
public static void Run1([HttpTrigger] HttpRequestMessage request) { }
+ [FunctionName("HttpTriggerQueueReturn")]
+ [return: Queue("myqueue-items-a", Connection = "MyStorageConnStrA")]
+ public static string HttpTriggerQueueReturn([HttpTrigger] HttpRequestMessage request) => "foo";
+
+ [FunctionName("HttpTriggerQueueOutParam")]
+ public static void HttpTriggerQueueOutParam([HttpTrigger] HttpRequestMessage request,
+ [Queue("myqueue-items-b", Connection = "MyStorageConnStrB")] out string msg)
+ {
+ msg = "foo";
+ }
+
+ [FunctionName("HttpTriggerMultipleOutputs")]
+ public static void HttpTriggerMultipleOutputs([HttpTrigger] HttpRequestMessage request,
+ [Blob("binding-metric-test/sample-text.txt", Connection = "MyStorageConnStrC")] out string myBlob,
+ [Queue("myqueue-items-c", Connection = "MyStorageConnStrC")] IAsyncCollector qCollector)
+ {
+ myBlob = "foo-blob";
+ qCollector.AddAsync("foo-queue");
+ }
+
[FunctionName("MyBlobTrigger")]
public static void Run2([BlobTrigger("blob.txt")] string blobContent) { }
@@ -46,24 +69,237 @@ public static void Run7(string input) { }
public static void Run8() { }
}
+ public class BindingAssertionItem
+ {
+ public string FunctionName { set; get; }
+
+ public Dictionary[] Bindings { set; get; }
+ }
+
+ public class BindingTestData : IEnumerable