diff --git a/endtoendtests/Azure.Functions.Java.Tests.E2E/Azure.Functions.Java.Tests.E2E/Constants.cs b/endtoendtests/Azure.Functions.Java.Tests.E2E/Azure.Functions.Java.Tests.E2E/Constants.cs index a6aec7a6..7fed4d3b 100644 --- a/endtoendtests/Azure.Functions.Java.Tests.E2E/Azure.Functions.Java.Tests.E2E/Constants.cs +++ b/endtoendtests/Azure.Functions.Java.Tests.E2E/Azure.Functions.Java.Tests.E2E/Constants.cs @@ -37,10 +37,26 @@ public static class Constants // EventHubs public static string OutputEventHubQueueName = "test-eventhuboutput-java"; public static string InputEventHubName = "test-input-java"; + public static string OutputJsonEventHubQueueName = "test-eventhuboutputjson-java"; public static string InputJsonEventHubName = "test-inputjson-java"; + public static string OutputOneEventHubQueueName = "test-eventhuboutputone-java"; public static string InputCardinalityOneEventHubName = "test-inputOne-java"; + + public static string OutputBinaryOneQueueName = "test-binary-output-cardinality-one-java"; + public static string InputBinaryOneEventHubQueueName = "test-binary-input-cardinality-one-java"; + + public static string OutputBinaryManyQueueName = "test-binary-output-cardinality-many-list-java"; + public static string InputBinaryManyEventHubQueueName = "test-binary-input-cardinality-many-list-java"; + + public static string OutputBinaryArrayManyQueueName = "test-binary-output-cardinality-many-array-java"; + public static string InputBinaryManyArrayEventHubQueueName = "test-binary-input-cardinality-many-array-java"; + + // Settings + public static string EventHubsConnectionStringSenderSetting = Environment.GetEnvironmentVariable("AzureWebJobsEventHubSender"); + public static string EventHubsConnectionStringSenderSetting2 = Environment.GetEnvironmentVariable("AzureWebJobsEventHubSender_2"); + public static string EventHubsConnectionStringSetting = Environment.GetEnvironmentVariable("AzureWebJobsEventHubSender"); // Xunit Fixtures and Collections diff --git a/endtoendtests/Azure.Functions.Java.Tests.E2E/Azure.Functions.Java.Tests.E2E/EventHubsEndToEndTests.cs b/endtoendtests/Azure.Functions.Java.Tests.E2E/Azure.Functions.Java.Tests.E2E/EventHubsEndToEndTests.cs index 6e1ec187..55e59eec 100644 --- a/endtoendtests/Azure.Functions.Java.Tests.E2E/Azure.Functions.Java.Tests.E2E/EventHubsEndToEndTests.cs +++ b/endtoendtests/Azure.Functions.Java.Tests.E2E/Azure.Functions.Java.Tests.E2E/EventHubsEndToEndTests.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Reflection.Metadata; using System.Threading.Tasks; using Xunit; @@ -29,7 +30,7 @@ public async Task EventHubTriggerAndOutputJSON_Succeeds() await SetupQueue(Constants.OutputJsonEventHubQueueName); // Need to setup EventHubs: test-inputjson-java and test-outputjson-java - await EventHubsHelpers.SendJSONMessagesAsync(expectedEventId); + await EventHubsHelpers.SendJSONMessagesAsync(expectedEventId, Constants.EventHubsConnectionStringSenderSetting); //Verify var queueMessage = await StorageHelpers.ReadFromQueue(Constants.OutputJsonEventHubQueueName); @@ -52,7 +53,7 @@ public async Task EventHubTriggerAndOutputString_Succeeds() await SetupQueue(Constants.OutputEventHubQueueName); // Need to setup EventHubs: test-input-java and test-output-java - await EventHubsHelpers.SendMessagesAsync(expectedEventId, Constants.InputEventHubName); + await EventHubsHelpers.SendMessagesAsync(expectedEventId, Constants.InputEventHubName, Constants.EventHubsConnectionStringSenderSetting); //Verify var queueMessage = await StorageHelpers.ReadFromQueue(Constants.OutputEventHubQueueName); @@ -74,7 +75,7 @@ public async Task EventHubTriggerCardinalityOne_Succeeds() await SetupQueue(Constants.OutputOneEventHubQueueName); // Need to setup EventHubs: test-inputOne-java and test-outputone-java - await EventHubsHelpers.SendMessagesAsync(expectedEventId, Constants.InputCardinalityOneEventHubName); + await EventHubsHelpers.SendMessagesAsync(expectedEventId, Constants.InputCardinalityOneEventHubName, Constants.EventHubsConnectionStringSenderSetting); //Verify IEnumerable queueMessages = await StorageHelpers.ReadMessagesFromQueue(Constants.OutputOneEventHubQueueName); @@ -87,6 +88,72 @@ public async Task EventHubTriggerCardinalityOne_Succeeds() } } + /* + [Fact] + public async Task EventHubTriggerAndOutputBinaryListMany_Succeeds() + { + string expectedEventId = Guid.NewGuid().ToString(); + try + { + await SetupQueue(Constants.OutputBinaryManyQueueName); + + await EventHubsHelpers.SendMessagesAsync(expectedEventId, Constants.InputBinaryManyEventHubQueueName, Constants.EventHubsConnectionStringSenderSetting2); + + //Verify + var queueMessage = await StorageHelpers.ReadFromQueue(Constants.OutputBinaryManyQueueName); + Assert.Contains(expectedEventId, queueMessage); + } + finally + { + //Clear queue + await StorageHelpers.ClearQueue(Constants.OutputEventHubQueueName); + } + } + */ + [Fact] + public async Task EventHubTriggerAndOutputBinaryOne_Succeeds() + { + string expectedEventId = Guid.NewGuid().ToString(); + try + { + await SetupQueue(Constants.OutputBinaryOneQueueName); + + await EventHubsHelpers.SendMessagesAsync(expectedEventId, Constants.InputBinaryOneEventHubQueueName, Constants.EventHubsConnectionStringSenderSetting2); + + //Verify + var queueMessage = await StorageHelpers.ReadFromQueue(Constants.OutputBinaryOneQueueName); + Assert.Contains(expectedEventId, queueMessage); + } + finally + { + //Clear queue + await StorageHelpers.ClearQueue(Constants.OutputEventHubQueueName); + } + } + /* + [Fact] + public async Task EventHubTriggerAndOutputBinaryArrayMany_Succeeds() + { + string expectedEventId = Guid.NewGuid().ToString(); + try + { + await SetupQueue(Constants.OutputBinaryArrayManyQueueName); + + await EventHubsHelpers.SendMessagesAsync(expectedEventId, Constants.InputBinaryManyArrayEventHubQueueName, Constants.EventHubsConnectionStringSenderSetting2); + + //Verify + var queueMessage = await StorageHelpers.ReadFromQueue(Constants.OutputBinaryArrayManyQueueName); + Assert.Contains(expectedEventId, queueMessage); + } + finally + { + //Clear queue + await StorageHelpers.ClearQueue(Constants.OutputEventHubQueueName); + } + } + */ + + private static async Task SetupQueue(string queueName) { //Clear queue diff --git a/endtoendtests/Azure.Functions.Java.Tests.E2E/Azure.Functions.Java.Tests.E2E/Helpers/EventHubsHelpers.cs b/endtoendtests/Azure.Functions.Java.Tests.E2E/Azure.Functions.Java.Tests.E2E/Helpers/EventHubsHelpers.cs index d73d7b2c..e1f350e9 100644 --- a/endtoendtests/Azure.Functions.Java.Tests.E2E/Azure.Functions.Java.Tests.E2E/Helpers/EventHubsHelpers.cs +++ b/endtoendtests/Azure.Functions.Java.Tests.E2E/Azure.Functions.Java.Tests.E2E/Helpers/EventHubsHelpers.cs @@ -12,7 +12,7 @@ namespace Azure.Functions.Java.Tests.E2E { public class EventHubsHelpers { - public static async Task SendJSONMessagesAsync(string eventId) + public static async Task SendJSONMessagesAsync(string eventId, string connectionString) { // write 3 events List events = new List(); @@ -29,13 +29,13 @@ public static async Task SendJSONMessagesAsync(string eventId) events.Add(evt); } - EventHubsConnectionStringBuilder builder = new EventHubsConnectionStringBuilder(Constants.EventHubsConnectionStringSetting); + EventHubsConnectionStringBuilder builder = new EventHubsConnectionStringBuilder(connectionString); builder.EntityPath = Constants.InputJsonEventHubName; EventHubClient eventHubClient = EventHubClient.CreateFromConnectionString(builder.ToString()); await eventHubClient.SendAsync(events); } - public static async Task SendMessagesAsync(string eventId, string evenHubName) + public static async Task SendMessagesAsync(string eventId, string evenHubName, string connectionString) { // write 3 events List events = new List(); @@ -48,7 +48,7 @@ public static async Task SendMessagesAsync(string eventId, string evenHubName) events.Add(evt); } - EventHubsConnectionStringBuilder builder = new EventHubsConnectionStringBuilder(Constants.EventHubsConnectionStringSetting); + EventHubsConnectionStringBuilder builder = new EventHubsConnectionStringBuilder(connectionString); builder.EntityPath = evenHubName; EventHubClient eventHubClient = EventHubClient.CreateFromConnectionString(builder.ToString()); await eventHubClient.SendAsync(events); diff --git a/endtoendtests/local.settings.json b/endtoendtests/local.settings.json index 25a28dff..920e6ce9 100644 --- a/endtoendtests/local.settings.json +++ b/endtoendtests/local.settings.json @@ -4,6 +4,7 @@ "AzureWebJobsServiceBus": "", "AzureWebJobsEventHubReceiver":"", "AzureWebJobsEventHubSender":"", + "AzureWebJobsEventHubSender_2":"", "AzureWebJobsEventHubPath":"", "CosmosDBDatabaseName":"", "CosmosDBCollectionName":"", diff --git a/endtoendtests/src/main/java/com/microsoft/azure/functions/endtoend/EventHubTriggerTests.java b/endtoendtests/src/main/java/com/microsoft/azure/functions/endtoend/EventHubTriggerTests.java index a2e9efdb..3f1f7840 100644 --- a/endtoendtests/src/main/java/com/microsoft/azure/functions/endtoend/EventHubTriggerTests.java +++ b/endtoendtests/src/main/java/com/microsoft/azure/functions/endtoend/EventHubTriggerTests.java @@ -47,7 +47,7 @@ public void EventHubTriggerCardinalityOne( /** * This function verifies the above functions */ - @FunctionName("TestEventHubOutputJson") + @FunctionName("EventHubOutputJson") public void TestEventHubOutputJson( @EventHubTrigger(name = "message", eventHubName = "test-outputjson-java", connection = "AzureWebJobsEventHubSender") String message, @QueueOutput(name = "output", queueName = "test-eventhuboutputjson-java", connection = "AzureWebJobsStorage") OutputBinding output, @@ -57,7 +57,7 @@ public void TestEventHubOutputJson( output.setValue(message); } - @FunctionName("TestEventHubOutput") + @FunctionName("EventHubOutput") public void TestEventHubOutput( @EventHubTrigger(name = "message", eventHubName = "test-output-java", connection = "AzureWebJobsEventHubSender", cardinality = Cardinality.ONE) String message, @QueueOutput(name = "output", queueName = "test-eventhuboutput-java", connection = "AzureWebJobsStorage") OutputBinding output, @@ -67,7 +67,7 @@ public void TestEventHubOutput( output.setValue(message); } - @FunctionName("TestEventHubOutputInputOne") + @FunctionName("EventHubOutputInputOne") public void TestEventHubOutputInputOne( @EventHubTrigger(name = "message", eventHubName = "test-outputone-java", connection = "AzureWebJobsEventHubSender", cardinality = Cardinality.ONE) String message, @QueueOutput(name = "output", queueName = "test-eventhuboutputone-java", connection = "AzureWebJobsStorage") OutputBinding output, @@ -77,11 +77,40 @@ public void TestEventHubOutputInputOne( output.setValue(message); } + @FunctionName("EventHubTriggerAndOutputBinaryCardinalityManyListBinary") + public void EventHubTriggerAndOutputBinaryCardinalityManyListBinary( + @EventHubTrigger(name = "messages", eventHubName = "test-binary-input-cardinality-many-list-java", connection = "AzureWebJobsEventHubSender_2", dataType = "binary", cardinality = Cardinality.MANY) List messages, + @QueueOutput(name = "output", queueName = "test-binary-output-cardinality-many-list-java", connection = "AzureWebJobsStorage") OutputBinding output, + final ExecutionContext context + ) { + context.getLogger().info("Java Event Hub trigger received " + messages.size() +" messages"); + output.setValue(messages.get(0)); + } + + @FunctionName("EventHubTriggerAndOutputBinaryCardinalityOne") + public void EventHubTriggerAndOutputBinaryCardinalityOne( + @EventHubTrigger(name = "message", eventHubName = "test-binary-input-cardinality-one-java", connection = "AzureWebJobsEventHubSender_2", dataType = "binary", cardinality = Cardinality.ONE) byte[] message, + @QueueOutput(name = "output", queueName = "test-binary-output-cardinality-one-java",connection = "AzureWebJobsStorage") OutputBinding output, + final ExecutionContext context + ) { + context.getLogger().info("Java Event Hub trigger received message" + message); + output.setValue(message); + } + + @FunctionName("EventHubTriggerAndOutputBinaryCardinalityManyArrayBinary") + public void EventHubTriggerAndOutputBinaryCardinalityManyArrayBinary( + @EventHubTrigger(name = "messages", eventHubName = "test-binary-input-cardinality-many-array-java", connection = "AzureWebJobsEventHubSender_2", dataType = "binary", cardinality = Cardinality.MANY) byte[][] messages, + @QueueOutput(name = "output", queueName = "test-binary-output-cardinality-many-array-java", connection = "AzureWebJobsStorage") OutputBinding output, + final ExecutionContext context + ) { + context.getLogger().info("Java Event Hub trigger received " + messages.length +" messages"); + output.setValue(messages[0]); + } + public static class SystemProperty { public String SequenceNumber; public String Offset; public String PartitionKey; public String EnqueuedTimeUtc; } - } diff --git a/src/main/azure-functions-language-worker-protobuf/README.md b/src/main/azure-functions-language-worker-protobuf/README.md index a490b960..0f0f2341 100644 --- a/src/main/azure-functions-language-worker-protobuf/README.md +++ b/src/main/azure-functions-language-worker-protobuf/README.md @@ -26,19 +26,30 @@ From within the Azure Functions language worker repo: 1. Define remote branch for cleaner git commands - `git remote add proto-file https://github.com/azure/azure-functions-language-worker-protobuf.git` - `git fetch proto-file` -2. Merge updates - - `git merge -s subtree proto-file/ --squash --allow-unrelated-histories` - - You can also merge with an explicit path to subtree: `git merge -X subtree= --squash proto-file/ --allow-unrelated-histories` -3. Finalize with commit - - `git commit -m "Updated subtree from https://github.com/azure/azure-functions-language-worker-protobuf. Branch: . Commit: "` +2. Pull a specific release tag + - `git fetch proto-file refs/tags/` + - Example: `git fetch proto-file refs/tags/v1.1.0-protofile` +3. Merge updates + - Merge with an explicit path to subtree: `git merge -X subtree= --squash --allow-unrelated-histories --strategy-option theirs` + - Example: `git merge -X subtree=src/WebJobs.Script.Grpc/azure-functions-language-worker-protobuf --squash v1.1.0-protofile --allow-unrelated-histories --strategy-option theirs` +4. Finalize with commit + - `git commit -m "Updated subtree from https://github.com/azure/azure-functions-language-worker-protobuf. Tag: . Commit: "` - `git push` - + +## Releasing a Language Worker Protobuf version + +1. Draft a release in the GitHub UI + - Be sure to inculde details of the release +2. Create a release version, following semantic versioning guidelines ([semver.org](https://semver.org/)) +3. Tag the version with the pattern: `v..

-protofile` (example: `v1.1.0-protofile`) +3. Merge `dev` to `master` + ## Consuming FunctionRPC.proto *Note: Update versionNumber before running following commands* ## CSharp ``` -set NUGET_PATH=%UserProfile%\.nuget\packages +set NUGET_PATH="%UserProfile%\.nuget\packages" set GRPC_TOOLS_PATH=%NUGET_PATH%\grpc.tools\\tools\windows_x86 set PROTO_PATH=.\azure-functions-language-worker-protobuf\src\proto set PROTO=.\azure-functions-language-worker-protobuf\src\proto\FunctionRpc.proto diff --git a/src/main/azure-functions-language-worker-protobuf/src/proto/FunctionRpc.proto b/src/main/azure-functions-language-worker-protobuf/src/proto/FunctionRpc.proto index 44857b8b..2d54cf94 100644 --- a/src/main/azure-functions-language-worker-protobuf/src/proto/FunctionRpc.proto +++ b/src/main/azure-functions-language-worker-protobuf/src/proto/FunctionRpc.proto @@ -10,6 +10,8 @@ option go_package ="github.com/Azure/azure-functions-go-worker/internal/rpc"; package AzureFunctionsRpcMessages; import "google/protobuf/duration.proto"; +import "identity/ClaimsIdentityRpc.proto"; +import "shared/NullableTypes.proto"; // Interface exported by the server. service FunctionRpc { @@ -201,6 +203,9 @@ message FunctionLoadRequest { // Metadata for the request RpcFunctionMetadata metadata = 2; + + // A flag indicating if managed dependency is enabled or not + bool managed_dependency_enabled = 3; } // Worker tells host result of reload @@ -211,6 +216,9 @@ message FunctionLoadResponse { // Result of load operation StatusResult result = 2; // TODO: return type expected? + + // Result of load operation + bool is_dependency_downloaded = 3; } // Information on how a Function should be loaded and its bindings @@ -229,6 +237,9 @@ message RpcFunctionMetadata { // Bindings info map bindings = 6; + + // Is set to true for proxy + bool is_proxy = 7; } // Host requests worker to invoke a Function @@ -278,11 +289,35 @@ message TypedData { bytes bytes = 3; bytes stream = 4; RpcHttp http = 5; - sint64 int = 6; + sint64 int = 6; double double = 7; + CollectionBytes collection_bytes = 8; + CollectionString collection_string = 9; + CollectionDouble collection_double = 10; + CollectionSInt64 collection_sint64 = 11; } } +// Used to encapsulate collection string +message CollectionString { + repeated string string = 1; +} + +// Used to encapsulate collection bytes +message CollectionBytes { + repeated bytes bytes = 1; +} + +// Used to encapsulate collection double +message CollectionDouble { + repeated double double = 1; +} + +// Used to encapsulate collection sint64 +message CollectionSInt64 { + repeated sint64 sint64 = 1; +} + // Used to describe a given binding on invocation message ParameterBinding { // Name for the binding @@ -368,6 +403,44 @@ message RpcException { string message = 2; } +// Http cookie type. Note that only name and value are used for Http requests +message RpcHttpCookie { + // Enum that lets servers require that a cookie shouoldn't be sent with cross-site requests + enum SameSite { + None = 0; + Lax = 1; + Strict = 2; + } + + // Cookie name + string name = 1; + + // Cookie value + string value = 2; + + // Specifies allowed hosts to receive the cookie + NullableString domain = 3; + + // Specifies URL path that must exist in the requested URL + NullableString path = 4; + + // Sets the cookie to expire at a specific date instead of when the client closes. + // It is generally recommended that you use "Max-Age" over "Expires". + NullableTimestamp expires = 5; + + // Sets the cookie to only be sent with an encrypted request + NullableBool secure = 6; + + // Sets the cookie to be inaccessible to JavaScript's Document.cookie API + NullableBool http_only = 7; + + // Allows servers to assert that a cookie ought not to be sent along with cross-site requests + SameSite same_site = 8; + + // Number of seconds until the cookie expires. A zero or negative number will expire the cookie immediately. + NullableDouble max_age = 9; +} + // TODO - solidify this or remove it message RpcHttp { string method = 1; @@ -379,4 +452,6 @@ message RpcHttp { map query = 15; bool enable_content_negotiation= 16; TypedData rawBody = 17; + repeated RpcClaimsIdentity identities = 18; + repeated RpcHttpCookie cookies = 19; } diff --git a/src/main/azure-functions-language-worker-protobuf/src/proto/identity/ClaimsIdentityRpc.proto b/src/main/azure-functions-language-worker-protobuf/src/proto/identity/ClaimsIdentityRpc.proto new file mode 100644 index 00000000..c3945bb8 --- /dev/null +++ b/src/main/azure-functions-language-worker-protobuf/src/proto/identity/ClaimsIdentityRpc.proto @@ -0,0 +1,26 @@ +syntax = "proto3"; +// protobuf vscode extension: https://marketplace.visualstudio.com/items?itemName=zxh404.vscode-proto3 + +option java_package = "com.microsoft.azure.functions.rpc.messages"; + +import "shared/NullableTypes.proto"; + +// Light-weight representation of a .NET System.Security.Claims.ClaimsIdentity object. +// This is the same serialization as found in EasyAuth, and needs to be kept in sync with +// its ClaimsIdentitySlim definition, as seen in the WebJobs extension: +// https://github.com/Azure/azure-webjobs-sdk-extensions/blob/dev/src/WebJobs.Extensions.Http/ClaimsIdentitySlim.cs +message RpcClaimsIdentity { + NullableString authentication_type = 1; + NullableString name_claim_type = 2; + NullableString role_claim_type = 3; + repeated RpcClaim claims = 4; +} + +// Light-weight representation of a .NET System.Security.Claims.Claim object. +// This is the same serialization as found in EasyAuth, and needs to be kept in sync with +// its ClaimSlim definition, as seen in the WebJobs extension: +// https://github.com/Azure/azure-webjobs-sdk-extensions/blob/dev/src/WebJobs.Extensions.Http/ClaimSlim.cs +message RpcClaim { + string value = 1; + string type = 2; +} diff --git a/src/main/azure-functions-language-worker-protobuf/src/proto/shared/NullableTypes.proto b/src/main/azure-functions-language-worker-protobuf/src/proto/shared/NullableTypes.proto new file mode 100644 index 00000000..4fb47650 --- /dev/null +++ b/src/main/azure-functions-language-worker-protobuf/src/proto/shared/NullableTypes.proto @@ -0,0 +1,30 @@ +syntax = "proto3"; +// protobuf vscode extension: https://marketplace.visualstudio.com/items?itemName=zxh404.vscode-proto3 + +option java_package = "com.microsoft.azure.functions.rpc.messages"; + +import "google/protobuf/timestamp.proto"; + +message NullableString { + oneof string { + string value = 1; + } +} + +message NullableDouble { + oneof double { + double value = 1; + } +} + +message NullableBool { + oneof bool { + bool value = 1; + } +} + +message NullableTimestamp { + oneof timestamp { + google.protobuf.Timestamp value = 1; + } +} diff --git a/src/main/java/com/microsoft/azure/functions/worker/binding/BindingDataStore.java b/src/main/java/com/microsoft/azure/functions/worker/binding/BindingDataStore.java index 5c3e8237..7539747a 100644 --- a/src/main/java/com/microsoft/azure/functions/worker/binding/BindingDataStore.java +++ b/src/main/java/com/microsoft/azure/functions/worker/binding/BindingDataStore.java @@ -1,6 +1,5 @@ package com.microsoft.azure.functions.worker.binding; -import java.io.IOException; import java.lang.reflect.*; import java.util.*; @@ -35,7 +34,7 @@ public void addParameterSources(List parameters) { } public void addTriggerMetadataSource(Map metadata) { - for (Map.Entry entry : metadata.entrySet()) + for (Map.Entry entry : metadata.entrySet()) { DataSource inputValue = rpcSourceFromTypedData(entry.getKey(), entry.getValue()); this.metadataSources.put(entry.getKey(), inputValue); @@ -66,6 +65,10 @@ static DataSource rpcSourceFromTypedData(String name, TypedData data) { case BYTES: return new RpcByteArrayDataSource(name, data.getBytes()); case JSON: return new RpcJsonDataSource(name, data.getJson()); case HTTP: return new RpcHttpRequestDataSource(name, data.getHttp()); + case COLLECTION_STRING: return new RpcCollectionStringDataSource(name, data.getCollectionString()); + case COLLECTION_DOUBLE: return new RpcCollectionDoubleDataSource(name, data.getCollectionDouble()); + case COLLECTION_BYTES: return new RpcCollectionByteArrayDataSource(name, data.getCollectionBytes()); + case COLLECTION_SINT64: return new RpcCollectionLongDataSource(name, data.getCollectionSint64()); case DATA_NOT_SET: return new RpcEmptyDataSource(name); default: throw new UnsupportedOperationException("Input data type \"" + data.getDataCase() + "\" is not supported"); } diff --git a/src/main/java/com/microsoft/azure/functions/worker/binding/RpcCollectionByteArrayDataSource.java b/src/main/java/com/microsoft/azure/functions/worker/binding/RpcCollectionByteArrayDataSource.java new file mode 100644 index 00000000..408361c4 --- /dev/null +++ b/src/main/java/com/microsoft/azure/functions/worker/binding/RpcCollectionByteArrayDataSource.java @@ -0,0 +1,54 @@ +package com.microsoft.azure.functions.worker.binding; + +import com.google.protobuf.ByteString; +import com.microsoft.azure.functions.rpc.messages.CollectionBytes; +import org.apache.commons.lang3.ArrayUtils; + +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +public final class RpcCollectionByteArrayDataSource extends DataSource> { + public RpcCollectionByteArrayDataSource(String name, CollectionBytes value) { + super(name, value.getBytesList(), COLLECTION_DATA_OPERATIONS); + } + private static final DataOperations, Object> COLLECTION_DATA_OPERATIONS = new DataOperations<>(); + + public static Object convertToByteList(List sourceValue, Type targetType) { + if(targetType == List.class) { + return sourceValue.stream().map(element -> element.toByteArray()).collect(Collectors.toCollection(ArrayList::new)); + } + else if(targetType instanceof ParameterizedType){ + Type targetActualType = ((ParameterizedType) targetType).getActualTypeArguments()[0]; + if (targetActualType == byte[].class) { + return sourceValue.stream().map(element -> element.toByteArray()).collect(Collectors.toCollection(ArrayList::new)); + } else if (targetActualType == Byte[].class) { + return sourceValue.stream().map(element -> (ArrayUtils.toObject(element.toByteArray()))).collect(Collectors.toList()); + } + throw new UnsupportedOperationException("Input data type \"" + targetActualType + "\" is not supported"); + } + throw new UnsupportedOperationException("Input data type \"" + targetType + "\" is not supported"); + } + + public static Object convertToByteListDefault(List sourceValue, Type targetType) { + return sourceValue.stream().map(element -> element.toByteArray()).collect(Collectors.toCollection(ArrayList::new)); + } + + public static Object convertToBytesArray(List sourceValue, Type targetType) { + return sourceValue.stream().map(element -> element.toByteArray()).collect(Collectors.toCollection(ArrayList::new)).toArray(new byte[0][]); + } + + public static Object convertToBytesObjectArray(List sourceValue, Type targetType) { + return sourceValue.stream().map(element -> (ArrayUtils.toObject(element.toByteArray()))).collect(Collectors.toCollection(ArrayList::new)).toArray(new Byte[0][]); + } + + static { + COLLECTION_DATA_OPERATIONS.addGenericOperation(List.class, (v, t) -> convertToByteList(v, t)); + COLLECTION_DATA_OPERATIONS.addGenericOperation(byte[][].class, (v, t) -> convertToBytesArray(v, t)); + COLLECTION_DATA_OPERATIONS.addGenericOperation(Byte[][].class, (v, t) -> convertToBytesObjectArray(v, t)); + COLLECTION_DATA_OPERATIONS.addGenericOperation(String.class, (v, t) -> convertToByteListDefault(v, t)); + } +} + diff --git a/src/main/java/com/microsoft/azure/functions/worker/binding/RpcCollectionDoubleDataSource.java b/src/main/java/com/microsoft/azure/functions/worker/binding/RpcCollectionDoubleDataSource.java new file mode 100644 index 00000000..71ada30e --- /dev/null +++ b/src/main/java/com/microsoft/azure/functions/worker/binding/RpcCollectionDoubleDataSource.java @@ -0,0 +1,49 @@ +package com.microsoft.azure.functions.worker.binding; + +import com.microsoft.azure.functions.rpc.messages.CollectionDouble; + +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.List; + +public final class RpcCollectionDoubleDataSource extends DataSource> { + public RpcCollectionDoubleDataSource(String name, CollectionDouble value) { + super(name, value.getDoubleList(), COLLECTION_DATA_OPERATIONS); + } + private static final DataOperations, Object> COLLECTION_DATA_OPERATIONS = new DataOperations<>(); + + public static Object convertToDoubleListDefault(List sourceValue, Type targetType) { + return new ArrayList<>(sourceValue); + } + + public static Object convertToDoubleList(List sourceValue, Type targetType) { + if(targetType == List.class) { + return new ArrayList<>(sourceValue); + } + else if(targetType instanceof ParameterizedType) { + Type targetActualType = ((ParameterizedType) targetType).getActualTypeArguments()[0]; + if (targetActualType == Double.class) { + return new ArrayList<>(sourceValue); + } + throw new UnsupportedOperationException("Input data type \"" + targetActualType + "\" is not supported"); + } + throw new UnsupportedOperationException("Input data type \"" + targetType + "\" is not supported"); + } + + public static Object convertToDoubleObjectArray(List sourceValue, Type targetType) { + return new ArrayList<>(sourceValue).toArray(new Double[0]); + } + + public static Object convertToDoubleArray(List sourceValue, Type targetType) { + return sourceValue.stream().mapToDouble(Double::doubleValue).toArray(); + } + + static { + COLLECTION_DATA_OPERATIONS.addGenericOperation(List.class, (v, t) -> convertToDoubleList(v, t)); + COLLECTION_DATA_OPERATIONS.addGenericOperation(Double[].class, (v, t) -> convertToDoubleObjectArray(v, t)); + COLLECTION_DATA_OPERATIONS.addGenericOperation(double[].class, (v, t) -> convertToDoubleArray(v, t)); + COLLECTION_DATA_OPERATIONS.addGenericOperation(String.class, (v, t) -> convertToDoubleListDefault(v, t)); + } +} + diff --git a/src/main/java/com/microsoft/azure/functions/worker/binding/RpcCollectionLongDataSource.java b/src/main/java/com/microsoft/azure/functions/worker/binding/RpcCollectionLongDataSource.java new file mode 100644 index 00000000..d22ec2b5 --- /dev/null +++ b/src/main/java/com/microsoft/azure/functions/worker/binding/RpcCollectionLongDataSource.java @@ -0,0 +1,49 @@ +package com.microsoft.azure.functions.worker.binding; + +import com.microsoft.azure.functions.rpc.messages.CollectionSInt64; + +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.List; + +public final class RpcCollectionLongDataSource extends DataSource> { + public RpcCollectionLongDataSource(String name, CollectionSInt64 value) { + super(name, value.getSint64List(), COLLECTION_DATA_OPERATIONS); + } + private static final DataOperations, Object> COLLECTION_DATA_OPERATIONS = new DataOperations<>(); + + public static Object convertToLongList(List sourceValue, Type targetType) { + if(targetType == List.class) { + return new ArrayList<>(sourceValue); + } + else if(targetType instanceof ParameterizedType) { + Type targetActualType = ((ParameterizedType) targetType).getActualTypeArguments()[0]; + if (targetActualType == Long.class) { + return new ArrayList<>(sourceValue); + } + throw new UnsupportedOperationException("Input data type \"" + targetActualType + "\" is not supported"); + } + throw new UnsupportedOperationException("Input data type \"" + targetType + "\" is not supported"); + } + + public static Object convertToLongListDefault(List sourceValue, Type targetType) { + return new ArrayList<>(sourceValue); + } + + public static Object convertToLongObjectArray(List sourceValue, Type targetType) { + return new ArrayList<>(sourceValue).toArray(new Long[0]); + } + + public static Object convertToLongArray(List sourceValue, Type targetType) { + return sourceValue.stream().mapToLong(Long::longValue).toArray(); + } + + static { + COLLECTION_DATA_OPERATIONS.addGenericOperation(List.class, (v, t) -> convertToLongList(v, t)); + COLLECTION_DATA_OPERATIONS.addGenericOperation(Long[].class, (v, t) -> convertToLongObjectArray(v, t)); + COLLECTION_DATA_OPERATIONS.addGenericOperation(long[].class, (v, t) -> convertToLongArray(v, t)); + COLLECTION_DATA_OPERATIONS.addGenericOperation(String.class, (v, t) -> convertToLongListDefault(v, t)); + } +} + diff --git a/src/main/java/com/microsoft/azure/functions/worker/binding/RpcCollectionStringDataSource.java b/src/main/java/com/microsoft/azure/functions/worker/binding/RpcCollectionStringDataSource.java new file mode 100644 index 00000000..bb4708f8 --- /dev/null +++ b/src/main/java/com/microsoft/azure/functions/worker/binding/RpcCollectionStringDataSource.java @@ -0,0 +1,44 @@ +package com.microsoft.azure.functions.worker.binding; + +import com.microsoft.azure.functions.rpc.messages.CollectionString; + +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.List; + +public final class RpcCollectionStringDataSource extends DataSource> { + public RpcCollectionStringDataSource(String name, CollectionString value) { + super(name, value.getStringList(), COLLECTION_DATA_OPERATIONS); + } + private static final DataOperations, Object> COLLECTION_DATA_OPERATIONS = new DataOperations<>(); + + public static Object convertToStringList(List sourceValue, Type targetType) { + if(targetType == List.class) { + return new ArrayList<>(sourceValue); + } + else if(targetType instanceof ParameterizedType) { + Type targetActualType = ((ParameterizedType) targetType).getActualTypeArguments()[0]; + if (targetActualType == String.class) { + return new ArrayList<>(sourceValue); + } + throw new UnsupportedOperationException("Input data type \"" + targetActualType + "\" is not supported"); + } + throw new UnsupportedOperationException("Input data type \"" + targetType + "\" is not supported"); + } + + public static Object convertToStringArray(List sourceValue, Type targetType) { + return new ArrayList<>(sourceValue).toArray(new String[0]); + } + + public static Object convertToStringListDefault(List sourceValue, Type targetType) { + return new ArrayList<>(sourceValue); + } + + static { + COLLECTION_DATA_OPERATIONS.addGenericOperation(List.class, (v, t) -> convertToStringList(v, t)); + COLLECTION_DATA_OPERATIONS.addGenericOperation(String[].class, (v, t) -> convertToStringArray(v, t)); + COLLECTION_DATA_OPERATIONS.addGenericOperation(String.class, (v, t) -> convertToStringListDefault(v, t)); + } +} + diff --git a/src/main/java/com/microsoft/azure/functions/worker/handler/WorkerInitRequestHandler.java b/src/main/java/com/microsoft/azure/functions/worker/handler/WorkerInitRequestHandler.java index eb896390..993860b8 100644 --- a/src/main/java/com/microsoft/azure/functions/worker/handler/WorkerInitRequestHandler.java +++ b/src/main/java/com/microsoft/azure/functions/worker/handler/WorkerInitRequestHandler.java @@ -14,6 +14,7 @@ public WorkerInitRequestHandler() { @Override String execute(WorkerInitRequest request, WorkerInitResponse.Builder response) { response.setWorkerVersion(Application.version()); + response.putCapabilities("TypedDataCollection", "TypedDataCollection"); return "Worker initialized"; } } diff --git a/src/test/java/com/microsoft/azure/functions/worker/binding/tests/RpcCollectionByteArrayDataSourceTest.java b/src/test/java/com/microsoft/azure/functions/worker/binding/tests/RpcCollectionByteArrayDataSourceTest.java new file mode 100644 index 00000000..e29c1f2d --- /dev/null +++ b/src/test/java/com/microsoft/azure/functions/worker/binding/tests/RpcCollectionByteArrayDataSourceTest.java @@ -0,0 +1,167 @@ +package com.microsoft.azure.functions.worker.binding.tests; + +import com.google.protobuf.ByteString; +import com.microsoft.azure.functions.worker.binding.BindingData; +import com.microsoft.azure.functions.worker.binding.RpcCollectionByteArrayDataSource; +import com.microsoft.azure.functions.rpc.messages.CollectionBytes; +import com.microsoft.azure.functions.rpc.messages.CollectionBytes.Builder; + +import org.apache.commons.lang3.ArrayUtils; +import org.junit.Test; + +import java.lang.invoke.WrongMethodTypeException; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +import static org.junit.Assert.assertEquals; + +public class RpcCollectionByteArrayDataSourceTest { + @Test + public void rpcByteArrayDataSource_To_byte_Array() { + String sourceKey = "sourceKey"; + String expectedString = "Example String"; + byte[] expctedStingBytes = expectedString.getBytes(); + ByteString inputByteString = ByteString.copyFrom(expctedStingBytes); + + List input = new ArrayList(); + input.add(inputByteString); + + Builder a = CollectionBytes.newBuilder(); + a.addAllBytes(input); + + CollectionBytes typedDataCollectionBytes = a.build(); + + RpcCollectionByteArrayDataSource stringData = new RpcCollectionByteArrayDataSource(sourceKey, typedDataCollectionBytes); + + Optional actualBindingData = stringData.computeByName(sourceKey, byte[][].class); + BindingData actualArg = actualBindingData.orElseThrow(WrongMethodTypeException::new); + byte[][] actualBytes = (byte[][]) actualArg.getValue(); + String actualString = new String(actualBytes[0]); + assertEquals(actualString, expectedString); + } + + @Test + public void rpcByteArrayDataSource_To_Byte_Array() { + String sourceKey = "sourceKey"; + String expectedString = "Example String"; + byte[] expctedStingBytes = expectedString.getBytes(); + ByteString inputByteString = ByteString.copyFrom(expctedStingBytes); + + List input = new ArrayList(); + input.add(inputByteString); + + Builder a = CollectionBytes.newBuilder(); + a.addAllBytes(input); + + CollectionBytes typedDataCollectionBytes = a.build(); + + RpcCollectionByteArrayDataSource stringData = new RpcCollectionByteArrayDataSource(sourceKey, typedDataCollectionBytes); + + Optional actualBindingData = stringData.computeByName(sourceKey, Byte[][].class); + BindingData actualArg = actualBindingData.orElseThrow(WrongMethodTypeException::new); + Byte[][] actualBytes = (Byte[][]) actualArg.getValue(); + String actualString = new String(ArrayUtils.toPrimitive(actualBytes[0])); + assertEquals(actualString, expectedString); + } + + @Test + public void rpcByteArrayDataSource_default_To_List_byte() { + String sourceKey = "sourceKey"; + String expectedString = "Example String"; + byte[] expctedStingBytes = expectedString.getBytes(); + ByteString inputByteString = ByteString.copyFrom(expctedStingBytes); + + List input = new ArrayList(); + input.add(inputByteString); + + Builder a = CollectionBytes.newBuilder(); + a.addAllBytes(input); + + CollectionBytes typedDataCollectionBytes = a.build(); + + RpcCollectionByteArrayDataSource stringData = new RpcCollectionByteArrayDataSource(sourceKey, typedDataCollectionBytes); + + Optional actualBindingData = stringData.computeByName(sourceKey, String.class); + BindingData actualArg = actualBindingData.orElseThrow(WrongMethodTypeException::new); + List actualBytes = (List) actualArg.getValue(); + String actualString = new String(actualBytes.get(0)); + assertEquals(actualString, expectedString); + } + + @Test + public void rpcByteArrayDataSource_To_List_byte() { + String sourceKey = "sourceKey"; + String expectedString = "Example String"; + byte[] expctedStingBytes = expectedString.getBytes(); + ByteString inputByteString = ByteString.copyFrom(expctedStingBytes); + + List input = new ArrayList(); + input.add(inputByteString); + + Builder a = CollectionBytes.newBuilder(); + a.addAllBytes(input); + + CollectionBytes typedDataCollectionBytes = a.build(); + + RpcCollectionByteArrayDataSource stringData = new RpcCollectionByteArrayDataSource(sourceKey, typedDataCollectionBytes); + + Optional actualBindingData = stringData.computeByName(sourceKey, Utility.getActualType(byte[].class)); + BindingData actualArg = actualBindingData.orElseThrow(WrongMethodTypeException::new); + List actualBytes = (List) actualArg.getValue(); + String actualString = new String(actualBytes.get(0)); + assertEquals(actualString, expectedString); + } + + @Test + public void rpcByteArrayDataSource_To_List_Byte() { + String sourceKey = "sourceKey"; + String expectedString = "Example String"; + byte[] expctedStingBytes = expectedString.getBytes(); + ByteString inputByteString = ByteString.copyFrom(expctedStingBytes); + + List input = new ArrayList(); + input.add(inputByteString); + + Builder a = CollectionBytes.newBuilder(); + a.addAllBytes(input); + + CollectionBytes typedDataCollectionBytes = a.build(); + + RpcCollectionByteArrayDataSource stringData = new RpcCollectionByteArrayDataSource(sourceKey, typedDataCollectionBytes); + + Optional actualBindingData = stringData.computeByName(sourceKey, Utility.getActualType(Byte[].class)); + BindingData actualArg = actualBindingData.orElseThrow(WrongMethodTypeException::new); + List actualBytes = (List) actualArg.getValue(); + String actualString = new String(ArrayUtils.toPrimitive(actualBytes.get(0))); + assertEquals(actualString, expectedString); + } + + @Test + public void rpcByteArrayDataSource_No_Generic_To_List_byte() { + String sourceKey = "sourceKey"; + String expectedString = "Example String"; + byte[] expctedStingBytes = expectedString.getBytes(); + ByteString inputByteString = ByteString.copyFrom(expctedStingBytes); + + List input = new ArrayList(); + input.add(inputByteString); + + Builder a = CollectionBytes.newBuilder(); + a.addAllBytes(input); + + CollectionBytes typedDataCollectionBytes = a.build(); + + RpcCollectionByteArrayDataSource stringData = new RpcCollectionByteArrayDataSource(sourceKey, typedDataCollectionBytes); + + Optional actualBindingData = stringData.computeByName(sourceKey, List.class); + BindingData actualArg = actualBindingData.orElseThrow(WrongMethodTypeException::new); + List actualBytes = (List) actualArg.getValue(); + String actualString = new String(actualBytes.get(0)); + assertEquals(actualString, expectedString); + } + + + + +} diff --git a/src/test/java/com/microsoft/azure/functions/worker/binding/tests/RpcCollectionDoubleDataSourceTest.java b/src/test/java/com/microsoft/azure/functions/worker/binding/tests/RpcCollectionDoubleDataSourceTest.java new file mode 100644 index 00000000..e18b4a44 --- /dev/null +++ b/src/test/java/com/microsoft/azure/functions/worker/binding/tests/RpcCollectionDoubleDataSourceTest.java @@ -0,0 +1,127 @@ + +package com.microsoft.azure.functions.worker.binding.tests; + +import com.microsoft.azure.functions.rpc.messages.CollectionDouble; +import com.microsoft.azure.functions.worker.binding.BindingData; +import com.microsoft.azure.functions.worker.binding.RpcCollectionDoubleDataSource; +import org.junit.Test; + +import java.lang.invoke.WrongMethodTypeException; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +import static org.junit.Assert.assertEquals; + +public class RpcCollectionDoubleDataSourceTest { + @Test + public void rpcCollectionDoubleDataSource_To_Double_Object_Array() { + String sourceKey = "sourceKey"; + Double expectedDouble = 1.1; + + List input = new ArrayList(); + input.add(expectedDouble); + + CollectionDouble.Builder a = CollectionDouble.newBuilder(); + a.addAllDouble(input); + + CollectionDouble typedDataCollectionDouble = a.build(); + + RpcCollectionDoubleDataSource data = new RpcCollectionDoubleDataSource(sourceKey, typedDataCollectionDouble); + + Optional actualBindingData = data.computeByName(sourceKey, Double[].class); + BindingData actualArg = actualBindingData.orElseThrow(WrongMethodTypeException::new); + Double[] actualDoublegArray = (Double[]) actualArg.getValue(); + Double actualDouble = actualDoublegArray[0]; + assertEquals(actualDouble, expectedDouble); + } + + @Test + public void rpcCollectionDoubleDataSource_To_double_Array() { + String sourceKey = "sourceKey"; + double expectedDouble = 1.1; + + List input = new ArrayList(); + input.add(expectedDouble); + + CollectionDouble.Builder a = CollectionDouble.newBuilder(); + a.addAllDouble(input); + + CollectionDouble typedDataCollectionDouble = a.build(); + + RpcCollectionDoubleDataSource data = new RpcCollectionDoubleDataSource(sourceKey, typedDataCollectionDouble); + + Optional actualBindingData = data.computeByName(sourceKey, double[].class); + BindingData actualArg = actualBindingData.orElseThrow(WrongMethodTypeException::new); + double[] actualDoubleArray = (double[]) actualArg.getValue(); + double actualDouble = actualDoubleArray[0]; + assertEquals("" + actualDouble, "" + expectedDouble); + } + + @Test + public void rpcCollectionDoubleDataSource_default_To_List_Double() { + String sourceKey = "sourceKey"; + Double expectedDouble = 1.1; + + List input = new ArrayList(); + input.add(expectedDouble); + + CollectionDouble.Builder a = CollectionDouble.newBuilder(); + a.addAllDouble(input); + + CollectionDouble typedDataCollectionDouble = a.build(); + + RpcCollectionDoubleDataSource data = new RpcCollectionDoubleDataSource(sourceKey, typedDataCollectionDouble); + + Optional actualBindingData = data.computeByName(sourceKey, String.class); + BindingData actualArg = actualBindingData.orElseThrow(WrongMethodTypeException::new); + List actualDoubleList = (List) actualArg.getValue(); + Double actualLong = actualDoubleList.get(0); + assertEquals(actualLong, expectedDouble); + } + + @Test + public void rpcCollectionDoubleDataSource_To_List_Double() { + String sourceKey = "sourceKey"; + Double expectedDouble = 1.1; + + List input = new ArrayList(); + input.add(expectedDouble); + + CollectionDouble.Builder a = CollectionDouble.newBuilder(); + a.addAllDouble(input); + + CollectionDouble typedDataCollectionDouble = a.build(); + + RpcCollectionDoubleDataSource data = new RpcCollectionDoubleDataSource(sourceKey, typedDataCollectionDouble); + + Optional actualBindingData = data.computeByName(sourceKey, Utility.getActualType(Double.class)); + BindingData actualArg = actualBindingData.orElseThrow(WrongMethodTypeException::new); + List actualDoubleList = (List) actualArg.getValue(); + Double actualLong = actualDoubleList.get(0); + assertEquals(actualLong, expectedDouble); + } + + @Test + public void rpcCollectionDoubleDataSource_No_Generic_To_List_Long() { + String sourceKey = "sourceKey"; + Double expectedDouble = 1.1; + + List input = new ArrayList(); + input.add(expectedDouble); + + CollectionDouble.Builder a = CollectionDouble.newBuilder(); + a.addAllDouble(input); + + CollectionDouble typedDataCollectionDouble = a.build(); + + RpcCollectionDoubleDataSource data = new RpcCollectionDoubleDataSource(sourceKey, typedDataCollectionDouble); + + Optional actualBindingData = data.computeByName(sourceKey, List.class); + BindingData actualArg = actualBindingData.orElseThrow(WrongMethodTypeException::new); + List actualDoubleList = (List) actualArg.getValue(); + Double actualLong = actualDoubleList.get(0); + assertEquals(actualLong, expectedDouble); + } +} + diff --git a/src/test/java/com/microsoft/azure/functions/worker/binding/tests/RpcCollectionLongDataSourceTest.java b/src/test/java/com/microsoft/azure/functions/worker/binding/tests/RpcCollectionLongDataSourceTest.java new file mode 100644 index 00000000..a16d547f --- /dev/null +++ b/src/test/java/com/microsoft/azure/functions/worker/binding/tests/RpcCollectionLongDataSourceTest.java @@ -0,0 +1,128 @@ +package com.microsoft.azure.functions.worker.binding.tests; + +import com.microsoft.azure.functions.rpc.messages.CollectionSInt64; +import com.microsoft.azure.functions.rpc.messages.CollectionSInt64.Builder; +import com.microsoft.azure.functions.worker.binding.BindingData; +import com.microsoft.azure.functions.worker.binding.RpcCollectionLongDataSource; +import org.junit.Test; + +import java.lang.invoke.WrongMethodTypeException; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +import static org.junit.Assert.assertEquals; + +public class RpcCollectionLongDataSourceTest{ + @Test + public void rpcCollectionSInt64DataSource_To_Long_Object_Array() { + String sourceKey = "sourceKey"; + Long expectedLong = 1L; + + List input = new ArrayList(); + input.add(expectedLong); + + Builder a = CollectionSInt64.newBuilder(); + a.addAllSint64(input); + + CollectionSInt64 typedDataCollectionLong = a.build(); + + RpcCollectionLongDataSource stringData = new RpcCollectionLongDataSource(sourceKey, typedDataCollectionLong); + + Optional actualBindingData = stringData.computeByName(sourceKey, Long[].class); + BindingData actualArg = actualBindingData.orElseThrow(WrongMethodTypeException::new); + Long[] actualLongArray = (Long[]) actualArg.getValue(); + Long actualLong = actualLongArray[0]; + assertEquals(actualLong, expectedLong); + } + + @Test + public void rpcCollectionDoubleDataSource_To_long_Array() { + String sourceKey = "sourceKey"; + Long expectedLong = 1L; + + List input = new ArrayList(); + input.add(expectedLong); + + Builder a = CollectionSInt64.newBuilder(); + a.addAllSint64(input); + + CollectionSInt64 typedDataCollectionLong = a.build(); + + RpcCollectionLongDataSource stringData = new RpcCollectionLongDataSource(sourceKey, typedDataCollectionLong); + + Optional actualBindingData = stringData.computeByName(sourceKey, long[].class); + BindingData actualArg = actualBindingData.orElseThrow(WrongMethodTypeException::new); + long[] actualLongArray = (long[]) actualArg.getValue(); + Long actualLong = actualLongArray[0]; + assertEquals(actualLong, expectedLong); + } + + @Test + public void rpcCollectionSInt64DataSource_default_To_List_Long() { + String sourceKey = "sourceKey"; + Long expectedLong = 1L; + + List input = new ArrayList(); + input.add(expectedLong); + + Builder a = CollectionSInt64.newBuilder(); + a.addAllSint64(input); + + CollectionSInt64 typedDataCollectionLong = a.build(); + + RpcCollectionLongDataSource stringData = new RpcCollectionLongDataSource(sourceKey, typedDataCollectionLong); + + Optional actualBindingData = stringData.computeByName(sourceKey, String.class); + BindingData actualArg = actualBindingData.orElseThrow(WrongMethodTypeException::new); + List actualLongList = (List) actualArg.getValue(); + Long actualLong = actualLongList.get(0); + assertEquals(actualLong, expectedLong); + } + + @Test + public void rpcCollectionSInt64DataSource_To_List_Long() { + String sourceKey = "sourceKey"; + Long expectedLong = 1L; + + List input = new ArrayList(); + input.add(expectedLong); + + Builder a = CollectionSInt64.newBuilder(); + a.addAllSint64(input); + + CollectionSInt64 typedDataCollectionLong = a.build(); + + RpcCollectionLongDataSource stringData = new RpcCollectionLongDataSource(sourceKey, typedDataCollectionLong); + + + Optional actualBindingData = stringData.computeByName(sourceKey, Utility.getActualType(Long.class)); + BindingData actualArg = actualBindingData.orElseThrow(WrongMethodTypeException::new); + List actualLongList = (List) actualArg.getValue(); + Long actualLong = actualLongList.get(0); + assertEquals(actualLong, expectedLong); + } + + @Test + public void rpcCollectionSInt64DataSource_No_Generic_To_List_Long() { + String sourceKey = "sourceKey"; + Long expectedLong = 1L; + + List input = new ArrayList(); + input.add(expectedLong); + + Builder a = CollectionSInt64.newBuilder(); + a.addAllSint64(input); + + CollectionSInt64 typedDataCollectionLong = a.build(); + + RpcCollectionLongDataSource stringData = new RpcCollectionLongDataSource(sourceKey, typedDataCollectionLong); + + + Optional actualBindingData = stringData.computeByName(sourceKey, List.class); + BindingData actualArg = actualBindingData.orElseThrow(WrongMethodTypeException::new); + List actualLongList = (List) actualArg.getValue(); + Long actualLong = actualLongList.get(0); + assertEquals(actualLong, expectedLong); + } +} diff --git a/src/test/java/com/microsoft/azure/functions/worker/binding/tests/RpcCollectionStringDataSourceTest.java b/src/test/java/com/microsoft/azure/functions/worker/binding/tests/RpcCollectionStringDataSourceTest.java new file mode 100644 index 00000000..17a6e07b --- /dev/null +++ b/src/test/java/com/microsoft/azure/functions/worker/binding/tests/RpcCollectionStringDataSourceTest.java @@ -0,0 +1,108 @@ +package com.microsoft.azure.functions.worker.binding.tests; + +import com.microsoft.azure.functions.rpc.messages.CollectionString.Builder; +import com.microsoft.azure.functions.rpc.messages.CollectionString; +import com.microsoft.azure.functions.worker.binding.BindingData; +import com.microsoft.azure.functions.worker.binding.RpcCollectionStringDataSource; +import org.junit.Test; + +import java.lang.invoke.WrongMethodTypeException; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +import static org.junit.Assert.assertEquals; + +public class RpcCollectionStringDataSourceTest { + @Test + public void rpcStringCollectionDataSource_To_string_Array() { + String sourceKey = "sourceKey"; + String expectedString = "Example String"; + + List input = new ArrayList(); + input.add(expectedString); + + Builder a = CollectionString.newBuilder(); + a.addAllString(input); + + CollectionString typedDataCollectionString = a.build(); + + RpcCollectionStringDataSource stringData = new RpcCollectionStringDataSource(sourceKey, typedDataCollectionString); + + Optional actualBindingData = stringData.computeByName(sourceKey, String[].class); + BindingData actualArg = actualBindingData.orElseThrow(WrongMethodTypeException::new); + String[] actualStringArray = (String[]) actualArg.getValue(); + String actualString = actualStringArray[0]; + assertEquals(actualString, expectedString); + } + + @Test + public void rpcStringCollectionDataSource_default_To_List_string() { + String sourceKey = "sourceKey"; + String expectedString = "Example String"; + + List input = new ArrayList(); + input.add(expectedString); + + Builder a = CollectionString.newBuilder(); + a.addAllString(input); + + CollectionString typedDataCollectionString = a.build(); + + RpcCollectionStringDataSource stringData = new RpcCollectionStringDataSource(sourceKey, typedDataCollectionString); + + Optional actualBindingData = stringData.computeByName(sourceKey, String.class); + BindingData actualArg = actualBindingData.orElseThrow(WrongMethodTypeException::new); + List actualStringList = (List) actualArg.getValue(); + String actualString = actualStringList.get(0); + assertEquals(actualString, expectedString); + } + + @Test + public void rpcStringCollectionDataSource_To_List_string() { + String sourceKey = "sourceKey"; + String expectedString = "Example String"; + + List input = new ArrayList(); + input.add(expectedString); + + Builder a = CollectionString.newBuilder(); + a.addAllString(input); + + CollectionString typedDataCollectionString = a.build(); + + RpcCollectionStringDataSource stringData = new RpcCollectionStringDataSource(sourceKey, typedDataCollectionString); + + Optional actualBindingData = stringData.computeByName(sourceKey, Utility.getActualType(String.class)); + BindingData actualArg = actualBindingData.orElseThrow(WrongMethodTypeException::new); + List actualStringList = (List) actualArg.getValue(); + String actualString = actualStringList.get(0); + assertEquals(actualString, expectedString); + } + + @Test + public void rpcStringCollectionDataSource_No_Generic_To_List_String() { + String sourceKey = "sourceKey"; + String expectedString = "Example String"; + + List input = new ArrayList(); + input.add(expectedString); + + Builder a = CollectionString.newBuilder(); + a.addAllString(input); + + CollectionString typedDataCollectionString = a.build(); + + RpcCollectionStringDataSource stringData = new RpcCollectionStringDataSource(sourceKey, typedDataCollectionString); + + Optional actualBindingData = stringData.computeByName(sourceKey, List.class); + BindingData actualArg = actualBindingData.orElseThrow(WrongMethodTypeException::new); + List actualStringList = (List) actualArg.getValue(); + String actualString = actualStringList.get(0); + assertEquals(actualString, expectedString); + } + + + + +} diff --git a/src/test/java/com/microsoft/azure/functions/worker/binding/tests/Utility.java b/src/test/java/com/microsoft/azure/functions/worker/binding/tests/Utility.java new file mode 100644 index 00000000..703faeed --- /dev/null +++ b/src/test/java/com/microsoft/azure/functions/worker/binding/tests/Utility.java @@ -0,0 +1,12 @@ +package com.microsoft.azure.functions.worker.binding.tests; + +import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl; + +import java.lang.reflect.Type; +import java.util.List; + +public class Utility { + public static Type getActualType(Class clazz) { + return ParameterizedTypeImpl.make(List.class, new Type[]{clazz}, null); + } +}