From 7a95d597d5cbcf77cbc51918733f97a4435ad148 Mon Sep 17 00:00:00 2001 From: David Justo Date: Mon, 22 Nov 2021 15:17:26 -0800 Subject: [PATCH] Allow DF Activity Functions to have multiple output bindings (#702) --- release_notes.md | 1 + src/RequestProcessor.cs | 2 +- .../DurableEndToEndTests.cs | 50 +++++++++++++++++++ .../function.json | 16 ++++++ .../DurableActivityWritesToQueue/run.ps1 | 7 +++ .../E2E/TestFunctionApp/DurableClient/run.ps1 | 2 +- .../function.json | 9 ++++ .../DurableOrchestratorWriteToQueue/run.ps1 | 5 ++ 8 files changed, 90 insertions(+), 2 deletions(-) create mode 100644 release_notes.md create mode 100644 test/E2E/TestFunctionApp/DurableActivityWritesToQueue/function.json create mode 100644 test/E2E/TestFunctionApp/DurableActivityWritesToQueue/run.ps1 create mode 100644 test/E2E/TestFunctionApp/DurableOrchestratorWriteToQueue/function.json create mode 100644 test/E2E/TestFunctionApp/DurableOrchestratorWriteToQueue/run.ps1 diff --git a/release_notes.md b/release_notes.md new file mode 100644 index 00000000..c636147d --- /dev/null +++ b/release_notes.md @@ -0,0 +1 @@ +* Bug fix: Activity Functions can now use output bindings (https://github.com/Azure/azure-functions-powershell-worker/issues/646) \ No newline at end of file diff --git a/src/RequestProcessor.cs b/src/RequestProcessor.cs index 005478bf..0e6041e8 100644 --- a/src/RequestProcessor.cs +++ b/src/RequestProcessor.cs @@ -456,7 +456,7 @@ private static TraceContext GetTraceContext(AzFunctionInfo functionInfo, Invocat /// private static void BindOutputFromResult(InvocationResponse response, AzFunctionInfo functionInfo, IDictionary results) { - if (functionInfo.DurableFunctionInfo.Type == DurableFunctionType.None) // TODO: but let activity functions have output bindings, too + if (functionInfo.DurableFunctionInfo.Type != DurableFunctionType.OrchestrationFunction) { // Set out binding data and return response to be sent back to host foreach (var (bindingName, bindingInfo) in functionInfo.OutputBindings) diff --git a/test/E2E/Azure.Functions.PowerShellWorker.E2E/Azure.Functions.PowerShellWorker.E2E/DurableEndToEndTests.cs b/test/E2E/Azure.Functions.PowerShellWorker.E2E/Azure.Functions.PowerShellWorker.E2E/DurableEndToEndTests.cs index 45d9aa18..ddc20c5e 100644 --- a/test/E2E/Azure.Functions.PowerShellWorker.E2E/Azure.Functions.PowerShellWorker.E2E/DurableEndToEndTests.cs +++ b/test/E2E/Azure.Functions.PowerShellWorker.E2E/Azure.Functions.PowerShellWorker.E2E/DurableEndToEndTests.cs @@ -147,6 +147,56 @@ public async Task LegacyDurableCommandNamesStillWork() } } + [Fact] + public async Task ActivityCanHaveQueueBinding() + { + const string queueName = "outqueue"; + await StorageHelpers.ClearQueue(queueName); + var initialResponse = await Utilities.GetHttpTriggerResponse("DurableClient", queryString: "?FunctionName=DurableOrchestratorWriteToQueue"); + Assert.Equal(HttpStatusCode.Accepted, initialResponse.StatusCode); + + var initialResponseBody = await initialResponse.Content.ReadAsStringAsync(); + dynamic initialResponseBodyObject = JsonConvert.DeserializeObject(initialResponseBody); + var statusQueryGetUri = (string)initialResponseBodyObject.statusQueryGetUri; + + var startTime = DateTime.UtcNow; + + using var httpClient = new HttpClient(); + + while (true) + { + var statusResponse = await httpClient.GetAsync(statusQueryGetUri); + switch (statusResponse.StatusCode) + { + case HttpStatusCode.Accepted: + { + if (DateTime.UtcNow > startTime + _orchestrationCompletionTimeout) + { + Assert.True(false, $"The orchestration has not completed after {_orchestrationCompletionTimeout}"); + } + + await Task.Delay(TimeSpan.FromSeconds(2)); + break; + } + + case HttpStatusCode.OK: + { + var statusResponseBody = await GetResponseBodyAsync(statusResponse); + Assert.Equal("Completed", (string)statusResponseBody.runtimeStatus); + + var queueMessage = await StorageHelpers.ReadFromQueue(queueName); + Assert.Equal("QueueData", queueMessage); + return; + } + + default: + Assert.True(false, $"Unexpected orchestration status code: {statusResponse.StatusCode}"); + break; + } + } + } + + [Fact] public async Task ActivityExceptionIsPropagatedThroughOrchestrator() { diff --git a/test/E2E/TestFunctionApp/DurableActivityWritesToQueue/function.json b/test/E2E/TestFunctionApp/DurableActivityWritesToQueue/function.json new file mode 100644 index 00000000..993a5ff4 --- /dev/null +++ b/test/E2E/TestFunctionApp/DurableActivityWritesToQueue/function.json @@ -0,0 +1,16 @@ +{ + "bindings": [ + { + "name": "name", + "type": "activityTrigger", + "direction": "in" + }, + { + "type": "queue", + "direction": "out", + "name": "outputQueueItem", + "queueName": "outqueue", + "connection": "AzureWebJobsStorage" + } + ] +} diff --git a/test/E2E/TestFunctionApp/DurableActivityWritesToQueue/run.ps1 b/test/E2E/TestFunctionApp/DurableActivityWritesToQueue/run.ps1 new file mode 100644 index 00000000..f8f1c7c3 --- /dev/null +++ b/test/E2E/TestFunctionApp/DurableActivityWritesToQueue/run.ps1 @@ -0,0 +1,7 @@ +param($name) + +Write-Information "Pushing to outputQueueItem output binding" +Push-OutputBinding -Name outputQueueItem -Value $name +Write-Information "Done" + +"Hello $name!" \ No newline at end of file diff --git a/test/E2E/TestFunctionApp/DurableClient/run.ps1 b/test/E2E/TestFunctionApp/DurableClient/run.ps1 index c7ba7f37..b3dca416 100644 --- a/test/E2E/TestFunctionApp/DurableClient/run.ps1 +++ b/test/E2E/TestFunctionApp/DurableClient/run.ps1 @@ -16,7 +16,7 @@ Push-OutputBinding -Name Response -Value $Response $Status = Get-DurableStatus -InstanceId $InstanceId Write-Host "Orchestration $InstanceId status: $($Status | ConvertTo-Json)" -if ($Status.runtimeStatus -notin 'Pending', 'Running', 'Failed') { +if ($Status.runtimeStatus -notin 'Pending', 'Running', 'Failed', 'Completed') { throw "Unexpected orchestration $InstanceId runtime status: $($Status.runtimeStatus)" } diff --git a/test/E2E/TestFunctionApp/DurableOrchestratorWriteToQueue/function.json b/test/E2E/TestFunctionApp/DurableOrchestratorWriteToQueue/function.json new file mode 100644 index 00000000..0c950e30 --- /dev/null +++ b/test/E2E/TestFunctionApp/DurableOrchestratorWriteToQueue/function.json @@ -0,0 +1,9 @@ +{ + "bindings": [ + { + "name": "Context", + "type": "orchestrationTrigger", + "direction": "in" + } + ] +} diff --git a/test/E2E/TestFunctionApp/DurableOrchestratorWriteToQueue/run.ps1 b/test/E2E/TestFunctionApp/DurableOrchestratorWriteToQueue/run.ps1 new file mode 100644 index 00000000..609bc0d7 --- /dev/null +++ b/test/E2E/TestFunctionApp/DurableOrchestratorWriteToQueue/run.ps1 @@ -0,0 +1,5 @@ +using namespace System.Net + +param($Context) + +Invoke-DurableActivity -FunctionName 'DurableActivityWritesToQueue' -Input 'QueueData' \ No newline at end of file