public async Task OpenCircuit( [OrchestrationTrigger] IDurableOrchestrationContext context, ILogger log) { if (!context.IsReplaying) { log.LogInformation("Disabling function app to open circuit"); } var resourceId = context.GetInput <string>(); var stopFunctionRequest = new DurableHttpRequest( HttpMethod.Post, new Uri($"https://management.azure.com{resourceId}/stop?api-version=2016-08-01"), tokenSource: new ManagedIdentityTokenSource("https://management.core.windows.net")); var restartResponse = await context.CallHttpAsync(stopFunctionRequest); if (restartResponse.StatusCode != HttpStatusCode.OK) { throw new ArgumentException($"Failed to stop Function App: {restartResponse.StatusCode}: {restartResponse.Content}"); } if (!context.IsReplaying) { log.LogInformation("Function disabled"); } }
public static DurableHttpRequest ConvertTestRequestToDurableHttpRequest(TestDurableHttpRequest testRequest) { Dictionary <string, StringValues> testHeaders = new Dictionary <string, StringValues>(StringComparer.OrdinalIgnoreCase); if (testRequest.Headers != null) { foreach (KeyValuePair <string, string> header in testRequest.Headers) { StringValues stringValues; if (testHeaders.TryGetValue(header.Key, out stringValues)) { stringValues.Append(header.Value); testHeaders[header.Key] = stringValues; } else { stringValues = new StringValues(header.Value); testHeaders[header.Key] = stringValues; } } } DurableHttpRequest durableHttpRequest = new DurableHttpRequest( method: testRequest.HttpMethod, uri: new Uri(testRequest.Uri), headers: testHeaders, content: testRequest.Content, tokenSource: testRequest.TokenSource, asynchronousPatternEnabled: testRequest.AsynchronousPatternEnabled); return(durableHttpRequest); }
public static async Task <string> RunOrchestrator( [OrchestrationTrigger] IDurableOrchestrationContext context, ILogger log) { string endpoint = context.GetInput <object>().ToString(); var request = new DurableHttpRequest(HttpMethod.Get, new Uri(endpoint)); DurableHttpResponse endpointResponse = await context.CallHttpAsync(request); if (endpointResponse.StatusCode != HttpStatusCode.OK) { throw new ArgumentException($"Failed to contact endpoint: {endpointResponse.StatusCode}: {endpointResponse.Content}"); } log.LogInformation($"Information retrieved from endpoint = {endpointResponse.Content}."); string[] words = endpointResponse.Content.Split(" "); log.LogInformation($"Words count = {words.Count()}."); var entityId = new EntityId("OrchestrationSample_Counter", "charCounter"); context.SignalEntity(entityId, "reset"); foreach (string word in words) { context.SignalEntity(entityId, "Add", word); } await context.CallActivityAsync("OrchestrationSample_LogBlob", endpoint.Replace("/", "bar")); int count = await context.CallEntityAsync <int>(entityId, "Get"); return($"Endpoint: {endpoint} has the total of {count} chars"); }
public static async Task <DurableHttpResponse> CallHttpAsyncOrchestrator([OrchestrationTrigger] IDurableOrchestrationContext ctx) { TestDurableHttpRequest testRequest = ctx.GetInput <TestDurableHttpRequest>(); DurableHttpRequest durableHttpRequest = ConvertTestRequestToDurableHttpRequest(testRequest); DurableHttpResponse response = await ctx.CallHttpAsync(durableHttpRequest); return(response); }
public async Task CallHttpActionOrchestrationWithManagedIdentityOptions() { DurableHttpRequest request = null; // Mock the CallHttpAsync API so we can capture the request and return a fixed response. var contextMock = new Mock <IDurableOrchestrationContext>(); contextMock .Setup(ctx => ctx.CallHttpAsync(It.IsAny <DurableHttpRequest>())) .Callback <DurableHttpRequest>(req => request = req) .Returns(Task.FromResult(new DurableHttpResponse(System.Net.HttpStatusCode.OK))); var shim = new OutOfProcOrchestrationShim(contextMock.Object); var executionJson = @" { ""isDone"": false, ""actions"": [ [{ ""actionType"": ""CallHttp"", ""httpRequest"": { ""method"": ""GET"", ""uri"": ""https://example.com"", ""tokenSource"": { ""kind"": ""AzureManagedIdentity"", ""resource"": ""https://management.core.windows.net/.default"", ""options"": { ""authorityhost"": ""https://login.microsoftonline.com/"", ""tenantid"": ""example_tenant_id"" } } } }] ] }"; // Feed the out-of-proc execution result JSON to the out-of-proc shim. var jsonObject = JObject.Parse(executionJson); OrchestrationInvocationResult result = new OrchestrationInvocationResult() { ReturnValue = jsonObject, }; bool moreWork = await shim.ScheduleDurableTaskEvents(result); Assert.NotNull(request); Assert.Equal(HttpMethod.Get, request.Method); Assert.Equal(new Uri("https://example.com"), request.Uri); Assert.Null(request.Content); Assert.NotNull(request.TokenSource); ManagedIdentityTokenSource tokenSource = Assert.IsType <ManagedIdentityTokenSource>(request.TokenSource); Assert.Equal("https://management.core.windows.net/.default", tokenSource.Resource); Assert.Equal("https://login.microsoftonline.com/", tokenSource.Options.AuthorityHost.ToString()); Assert.Equal("example_tenant_id", tokenSource.Options.TenantId); }
internal static Task <DurableHttpResponse> PostData(IDurableOrchestrationContext context, string url, object body, string authToken) { var authHeaders = new Dictionary <string, StringValues> { ["Authorization"] = new StringValues($"Bearer {authToken}") }; var request = new DurableHttpRequest( HttpMethod.Post, new Uri(url), authHeaders, JsonConvert.SerializeObject(body)); return(context.CallHttpAsync(request)); }
public static async Task RunOrchestrator( [OrchestrationTrigger] IDurableOrchestrationContext context) { ResourceInfo vmInfo = context.GetInput <ResourceInfo>(); string apiVersion = vmInfo.ApiVersion ?? "2018-06-01"; string subscriptionId = vmInfo.SubscriptionId; string resourceGroup = vmInfo.ResourceGroup; // Implicitly uses the Azure AD identity of the current app to make an HTTP call to Azure Resource Manager var managedIdentity = new ManagedIdentityTokenSource("https://management.core.windows.net"); // List all of the VMs in my subscription and add them to a list. DurableHttpRequest request = new DurableHttpRequest( HttpMethod.Get, new Uri($"https://management.azure.com/subscriptions/{subscriptionId}/providers/Microsoft.Compute/virtualMachines?api-version=2018-06-01"), tokenSource: managedIdentity); DurableHttpResponse response = await context.CallHttpAsync(request); if (response.StatusCode != HttpStatusCode.OK) { throw new ArgumentException($"Failed to list VMs: {response.StatusCode}: {response.Content}"); } // Deserializes content to just get the names of the VMs in the subscription JObject jObject = JsonConvert.DeserializeObject <JObject>(response.Content); var vmNamesList = new List <string>(); foreach (JToken value in jObject["value"]) { string vmName = value["name"].ToString(); vmNamesList.Add(vmName); } // Restart all of the VMs in my subscription foreach (string vmName in vmNamesList) { var restartRequest = new DurableHttpRequest( HttpMethod.Post, new Uri($"https://management.azure.com/subscriptions/{subscriptionId}/resourceGroups/{resourceGroup}/providers/Microsoft.Compute/virtualMachines/{vmName}/restart?api-version={apiVersion}"), tokenSource: managedIdentity); DurableHttpResponse restartResponse = await context.CallHttpAsync(restartRequest); if (restartResponse.StatusCode != HttpStatusCode.OK) { throw new ArgumentException($"Failed to restart VM: {restartResponse.StatusCode}: {restartResponse.Content}"); } } }
public static async Task <List <string> > RunOrchestrator( [OrchestrationTrigger] IDurableOrchestrationContext context) { // param into orchestrator OrchestratorInput oi = context.GetInput <OrchestratorInput>(); // call activity function await context.CallActivityAsync <List <string> >(nameof(MyActivityFunction), oi.Url); // call durable entity List <string> result = await ContactDurableEntity(context); // call http var request = new DurableHttpRequest(HttpMethod.Post, new Uri(oi.Url), content: "myContent"); DurableHttpResponse response = await context.CallHttpAsync(request); return(result); }
public async Task CallHttpActionOrchestrationWithManagedIdentity() { DurableHttpRequest request = null; // Mock the CallHttpAsync API so we can capture the request and return a fixed response. var contextMock = new Mock <IDurableOrchestrationContext>(); contextMock .Setup(ctx => ctx.CallHttpAsync(It.IsAny <DurableHttpRequest>())) .Callback <DurableHttpRequest>(req => request = req) .Returns(Task.FromResult(new DurableHttpResponse(System.Net.HttpStatusCode.OK))); var shim = new OutOfProcOrchestrationShim(contextMock.Object); var executionJson = @" { ""isDone"": false, ""actions"": [ [{ ""actionType"": ""CallHttp"", ""httpRequest"": { ""method"": ""GET"", ""uri"": ""https://example.com"", ""tokenSource"": { ""kind"": ""AzureManagedIdentity"", ""resource"": ""https://management.core.windows.net"" } } }] ] }"; // Feed the out-of-proc execution result JSON to the out-of-proc shim. var jsonObject = JObject.Parse(executionJson); await shim.ExecuteAsync(jsonObject); Assert.NotNull(request); Assert.Equal(HttpMethod.Get, request.Method); Assert.Equal(new Uri("https://example.com"), request.Uri); Assert.Null(request.Content); Assert.NotNull(request.TokenSource); ManagedIdentityTokenSource tokenSource = Assert.IsType <ManagedIdentityTokenSource>(request.TokenSource); Assert.Equal("https://management.core.windows.net", tokenSource.Resource); }
public static async Task SingleClientOnboardingOrchestration( [OrchestrationTrigger] IDurableOrchestrationContext createAccountContext ) { AccountOpeningData payload = createAccountContext.GetInput <AccountOpeningData>(); if (null != payload) { string newAccountNumber = await createAccountContext.CallActivityAsync <string>(nameof( CreateRandomAccountNumber), null); string jsonContent = JsonConvert.SerializeObject(payload); var newAccountRequest = new DurableHttpRequest( HttpMethod.Post, new Uri($"https://retailbankazurefunctionapp.azurewebsites.net/api/OpenAccount/{newAccountNumber}?code=9IcflcYh904F3Dcbp5IGCPuDrqelVJniQK5Ck1ZcVdacOd3Mx4ShWQ=="), content: jsonContent); newAccountRequest.Headers.Add(@"Content-Type", @"application/json"); DurableHttpResponse restartResponse = await createAccountContext.CallHttpAsync(newAccountRequest); } }
public async Task CallHttpActionOrchestration() { DurableHttpRequest request = null; // Mock the CallHttpAsync API so we can capture the request and return a fixed response. var contextMock = new Mock <IDurableOrchestrationContext>(); contextMock .Setup(ctx => ctx.CallHttpAsync(It.IsAny <DurableHttpRequest>())) .Callback <DurableHttpRequest>(req => request = req) .Returns(Task.FromResult(new DurableHttpResponse(System.Net.HttpStatusCode.OK))); var shim = new OutOfProcOrchestrationShim(contextMock.Object); var executionJson = @" { ""isDone"": false, ""actions"": [ [{ ""actionType"": ""CallHttp"", ""httpRequest"": { ""method"": ""POST"", ""uri"": ""https://example.com"", ""headers"": { ""Content-Type"": ""application/json"", ""Accept"": [""application/json"",""application/xml""], ""x-ms-foo"": [] }, ""content"": ""5"" } }] ] }"; // Feed the out-of-proc execution result JSON to the out-of-proc shim. var jsonObject = JObject.Parse(executionJson); OrchestrationInvocationResult result = new OrchestrationInvocationResult() { ReturnValue = jsonObject, }; bool moreWork = await shim.ScheduleDurableTaskEvents(result); // The request should not have completed because one additional replay is needed // to handle the result of CallHttpAsync. However, this test doesn't care about // completing the orchestration - we just need to validate the CallHttpAsync call. Assert.True(moreWork); Assert.NotNull(request); Assert.Equal(HttpMethod.Post, request.Method); Assert.Equal(new Uri("https://example.com"), request.Uri); Assert.Equal("5", request.Content); Assert.Equal(3, request.Headers.Count); Assert.True(request.Headers.TryGetValue("Content-Type", out StringValues contentTypeValues)); Assert.Single(contentTypeValues); Assert.Equal("application/json", contentTypeValues[0]); Assert.True(request.Headers.TryGetValue("Accept", out StringValues acceptValues)); Assert.Equal(2, acceptValues.Count); Assert.Equal("application/json", acceptValues[0]); Assert.Equal("application/xml", acceptValues[1]); Assert.True(request.Headers.TryGetValue("x-ms-foo", out StringValues customHeaderValues)); Assert.Empty(customHeaderValues); }
public async Task OpenCircuit( [OrchestrationTrigger] IDurableOrchestrationContext context, ILogger log) { if (context == null) { throw new ArgumentNullException(nameof(context)); } try { var failureRequest = context.GetInput <FailureRequest>(); var resourceId = failureRequest.ResourceId; if (!context.IsReplaying) { log.LogInformation("Disabling function app ({resourceId}) to open circuit.", resourceId); } var managedIdentity = new ManagedIdentityTokenSource("https://management.azure.com/.default"); // Note: Configurable resourceId works only for other Azure App Service applications. var stopFunctionRequest = new DurableHttpRequest( HttpMethod.Post, new Uri($"https://management.azure.com{resourceId}/stop?api-version=2019-08-01"), tokenSource: managedIdentity); // Note: Use of ConfigureAwait can break orchestration tasks (prevents from completing; instance remains in a 'running' state). // See https://github.com/Azure/azure-functions-durable-extension/issues/995 and // https://docs.microsoft.com/en-us/azure/azure-functions/durable/durable-functions-code-constraints #pragma warning disable CA2007 // Consider calling ConfigureAwait on the awaited task DurableHttpResponse restartResponse = await context.CallHttpAsync(stopFunctionRequest); #pragma warning restore CA2007 // Consider calling ConfigureAwait on the awaited task if (restartResponse.StatusCode != HttpStatusCode.OK) { if (!context.IsReplaying) { log.LogError("Failed to stop Function App: {restartResponse.StatusCode}: {restartResponse.Content}", restartResponse.StatusCode, restartResponse.Content); } throw new ApplicationException($"Failed to stop Function App: {restartResponse.StatusCode}: {restartResponse.Content}"); } if (restartResponse.StatusCode == HttpStatusCode.OK) { if (!context.IsReplaying) { // Logging as a warning to aide in noticing this messages, as stopping of an Azure Function // may be an action worthy of human intervention. log.LogWarning("Successfully STOPPED Azure Function with Resource ID {resourceId}.", resourceId); } } return; } catch (Exception e) { log.LogError(e, "Unexpected error '{msg}' while processing.", e.Message); throw; } }
public async Task <DurableHttpResponse> CallHttpAsync(DurableHttpRequest req) { throw new NotImplementedException(); }
public Task <DurableHttpResponse> CallHttpAsync(DurableHttpRequest req) { var response = _client.CallHttpHandler?.Invoke(req) ?? new DurableHttpResponse(HttpStatusCode.OK); return(Task.FromResult(response)); }
public static async Task <LoanApplicationResult> Orchestrate( [OrchestrationTrigger] IDurableOrchestrationContext context, [SignalR(HubName = "dashboard")] IAsyncCollector <SignalRMessage> dashboardMessages, ILogger logger) { var loanApplication = context.GetInput <LoanApplication>(); var agencyTasks = new List <Task <CreditAgencyResult> >(); var agencies = new List <CreditAgencyRequest>(); var results = new CreditAgencyResult[] {}; logger.LogWarning($"Status of application for {loanApplication.Applicant.ToString()} for {loanApplication.LoanAmount}: Checking with agencies."); // start the process and perform initial validation bool loanPreApproved = await context.CallActivityAsync <bool>(nameof(Receive), loanApplication); // fan out and check the credit agencies agencies.AddRange(new CreditAgencyRequest[] { new CreditAgencyRequest { AgencyName = "Contoso, Ltd.", AgencyId = "contoso", Application = loanApplication }, new CreditAgencyRequest { AgencyName = "Fabrikam, Inc.", AgencyId = "fabrikam", Application = loanApplication }, new CreditAgencyRequest { AgencyName = "Woodgrove Bank", AgencyId = "woodgrove", Application = loanApplication }, }); foreach (var agency in agencies) { agencyTasks.Add(context.CallActivityAsync <CreditAgencyResult>(nameof(CheckCreditAgency), agency)); } await context.CallActivityAsync(nameof(SendDashboardMessage), new SignalRMessage { Target = "agencyCheckPhaseStarted", Arguments = new object[] { } }); // wait for all the agencies to return their results results = await Task.WhenAll(agencyTasks); await context.CallActivityAsync(nameof(SendDashboardMessage), new SignalRMessage { Target = "agencyCheckPhaseCompleted", Arguments = new object[] { !(results.Any(x => x.IsApproved == false)) } }); var loanApplicationResult = new LoanApplicationResult { IsApproved = loanPreApproved && !(results.Any(x => x.IsApproved == false)), Application = loanApplication }; logger.LogWarning($"Agency checks result with {loanApplicationResult.IsApproved} for loan amount of {loanApplication.LoanAmount.Amount} to customer {loanApplication.Applicant.ToString()}"); foreach (var agencyResult in results) { loanApplicationResult.AgencyResults.Add(new AgencyCheckResult { AgencyName = agencyResult.AgencyId, IsApproved = agencyResult.IsApproved }); } // send the loan for final human validation logger.LogInformation($"Sending loan application for {loanApplicationResult.Application.Applicant.FirstName} {loanApplicationResult.Application.Applicant.LastName} for approval"); var json = System.Text.Json.JsonSerializer.Serialize <LoanApplicationResult>(loanApplicationResult, new JsonSerializerOptions { WriteIndented = true }); logger.LogInformation(json); var url = Environment.GetEnvironmentVariable("LoanOfficerServiceUrl"); logger.LogInformation(url); var request = new DurableHttpRequest( HttpMethod.Post, new Uri(url), headers: new Dictionary <string, StringValues> { { "Content-Type", "application/json" } }, content: json, asynchronousPatternEnabled: false ); DurableHttpResponse restartResponse = await context.CallHttpAsync(request); logger.LogInformation($"Status code returned: {restartResponse.StatusCode}"); if (restartResponse.StatusCode == HttpStatusCode.Accepted) { await context.CallActivityAsync(nameof(SendDashboardMessage), new SignalRMessage { Target = "loanApplicationComplete", Arguments = new object[] { loanApplicationResult } }); } return(loanApplicationResult); }