public static async Task <object> Status( [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "Voting/{instanceId}")] HttpRequestMessage requestMessage, [DurableClient] IDurableOrchestrationClient durableClient, [DurableClient] IDurableEntityClient entityClient, string instanceId) { var status = await durableClient.GetStatusAsync(instanceId); var state = await entityClient.ReadEntityStateAsync <VotingEntity>( new EntityId(nameof(VotingEntity), instanceId)); return(new { status.InstanceId, status.RuntimeStatus, status.Output, State = state.EntityState }); }
public async Task <IActionResult> StatusInformation( [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req, [DurableClient] IDurableOrchestrationClient client) { var taskItem = await GetTaskItem(req); var taskId = _service.TaskId(taskItem); if (string.IsNullOrWhiteSpace(taskId)) { return(new OkObjectResult("orchestratorId missing")); } DurableOrchestrationStatus status = await client.GetStatusAsync(taskId, true, false, false); return(new OkObjectResult(status)); }
private async Task StartInstance(IDurableOrchestrationClient context, Order order, string instanceId, ILogger log) { try { var reportStatus = await context.GetStatusAsync(instanceId); string runningStatus = reportStatus == null ? "NULL" : reportStatus.RuntimeStatus.ToString(); //log.LogInformation($"Instance running status: '{runningStatus}'."); if (reportStatus == null || reportStatus.RuntimeStatus != OrchestrationRuntimeStatus.Running) { await context.StartNewAsync("OrderPlacedOrchestrator", instanceId, order); } } catch (Exception ex) { throw ex; } }
public async Task <HttpResponseMessage> HttpStart( [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "orchestrators/{orchestratorName}/{instanceId}")] HttpRequestMessage req, [DurableClient(TaskHub = "%PostCallCleanupDurableTaskHubName%")] IDurableOrchestrationClient starter, string orchestratorName, string instanceId, ILogger log) { try { // Check if an instance with the specified ID already exists. var instance = await starter.GetStatusAsync(instanceId); if (instance == null || !(instance.RuntimeStatus == OrchestrationRuntimeStatus.Pending || instance.RuntimeStatus == OrchestrationRuntimeStatus.Running || instance.RuntimeStatus == OrchestrationRuntimeStatus.ContinuedAsNew)) { // An instance with the specified ID doesn't exist, create one. await starter.StartNewAsync(orchestratorName, instanceId); log.LogInformation($"Started orchestration '{orchestratorName}' with ID = '{instanceId}'."); return(starter.CreateCheckStatusResponse(req, instanceId)); } else { log.LogInformation($"Orchestration '{orchestratorName}' with ID = '{instanceId}' already exists."); // An instance with the specified ID exists, don't create one. return(req.CreateErrorResponse( HttpStatusCode.Conflict, $"An instance with ID '{instanceId}' already exists.")); } } catch (Exception ex) { log.LogError(ex, "PostCallCleanupEternalOrchestrator_HttpStart: An error occurred while starting orchestrator"); return(req.CreateErrorResponse( HttpStatusCode.InternalServerError, $"{ex.Message}")); } }
public static async Task IotHubScaleInit( [TimerTrigger("0 0 * * * *")] TimerInfo myTimer, [DurableClient] IDurableOrchestrationClient starter, ILogger log) { log.LogInformation($"Timer trigger started at: {DateTime.Now}"); // check and see if a named instance of the orchestrator is already running var existingInstance = await starter.GetStatusAsync(IotHubScaleOrchestratorInstanceId); if (existingInstance == null) { log.LogInformation(String.Format("No instnace of job is running, starting new instance...", IotHubScaleOrchestratorInstanceId)); await starter.StartNewAsync(nameof(IotHubScaleOrchestrator), IotHubScaleOrchestratorInstanceId); } else { log.LogInformation(String.Format("Another instance already running, nothing to do...")); } }
public async Task <IActionResult> GetSchedule( [HttpTrigger(AuthorizationLevel.Function, "get", Route = "orchestrators/{orchestratorName}/schedules/{instanceId}")] HttpRequest req, [DurableClient] IDurableOrchestrationClient starter, string orchestratorName, string instanceId, ILogger log) { var status = await starter.GetStatusAsync(instanceId); var result = new ContentResult() { Content = JsonConvert.SerializeObject(status, this._settings), StatusCode = (int)HttpStatusCode.OK, ContentType = "application/json" }; log.LogInformation($"Retrieved the schedule for '{instanceId}'."); return(result); }
public async Task <IActionResult> Run( [HttpTrigger(AuthorizationLevel.Anonymous, methods: "post", Route = "slackapproval")] HttpRequestMessage req, [DurableClient] IDurableOrchestrationClient orchestrationClient, ILogger log) { var formData = await req.Content.ReadAsFormDataAsync(); string payload = formData.Get("payload"); dynamic response = JsonConvert.DeserializeObject(payload); string callbackId = response.callback_id; string responseUrl = response.response_url; string[] callbackIdParts = callbackId.Split('#'); string approvalType = callbackIdParts[0]; log.LogInformation($"Received a Slack Response with callbackid {callbackId}"); string instanceId = callbackIdParts[1]; bool isApproved = false; log.LogInformation($"instaceId:'{instanceId}', response:'{response.actions[0].value}'"); var status = await orchestrationClient.GetStatusAsync(instanceId); log.LogInformation($"Orchestration status: '{status}'"); if (status.RuntimeStatus == OrchestrationRuntimeStatus.Running || status.RuntimeStatus == OrchestrationRuntimeStatus.Pending) { string selection = response.actions[0].value; isApproved = selection == "Approve"; await orchestrationClient.RaiseEventAsync(instanceId, "ReceiveApprovalResponse", isApproved); httpClient.BaseAddress = new Uri(responseUrl); var responseMessage = Environment.GetEnvironmentVariable("Slack:ResponseMessage", EnvironmentVariableTarget.Process); var content = new StringContent(responseMessage, UnicodeEncoding.UTF8, "application/json"); var result = await httpClient.PostAsync(responseUrl, content); return(new OkObjectResult($"Thanks for your selection! Your selection was *'{selection}'*")); } else { return(new OkObjectResult($"The approval request has expired!")); } }
public Task <JsonResult> GetJobStatus( [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "jobs")] HttpRequest req, [HttpQuery(Required = true)] HttpParam <string> jobId, [HttpHeader(Name = "Authorization")] HttpParam <string> authorization, [DurableClient] IDurableOrchestrationClient orchestrationClient, CancellationToken cancellationToken) { return(exceptionFilter.FilterExceptions(async() => { var(userId, _, _) = await auth.ValidateUser(authorization, cancellationToken); log.LogInformation("Fetching status of job {jobId} for user {userId}", jobId, userId); var status = await orchestrationClient.GetStatusAsync(jobId); log.LogInformation("Fetched status of job {jobId} for user {userId}", jobId, userId); return new JsonResult(new JobStatus { Status = status.RuntimeStatus, }); })); }
public async Task LiveDataFunctionServiceBus( [ServiceBusTrigger("%ServiceBusQueueName%", Connection = "ServiceBusConnectionString")] LiveSyncMessage message, [DurableClient] IDurableOrchestrationClient client, ILogger log) { //todo: UniqueId might be used here if we need some kind of identification, but should include connectorType then. if (message.Uri != null) { var instanceId = message.Uri.ToBase64OrNull(); var existingInstance = await client.GetStatusAsync(instanceId); if (existingInstance == null || existingInstance.RuntimeStatus == OrchestrationRuntimeStatus.Completed || existingInstance.RuntimeStatus == OrchestrationRuntimeStatus.Failed || existingInstance.RuntimeStatus == OrchestrationRuntimeStatus.Terminated) { log.LogInformation($"{nameof(LiveDataFunction)} was triggered."); await client.StartNewAsync("ScheduledLiveMonitoring", instanceId, message); } } }
public async Task <IActionResult> CIHook( [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = null)] HttpRequest req, [OrchestrationClient] IDurableOrchestrationClient starter, ILogger log) { string pullRequestId = req.Query["pullRequestId"]; string projectKey = req.Query["ProjectKey"]; string commitId = req.Query["commitId"]; log.LogInformation($"PullRequestId: {pullRequestId} ProjectKey: {projectKey}"); var cIContext = new CIContext() { PullRequestId = pullRequestId, ProjectKey = projectKey, CommitId = commitId }; var instanceId = await starter.StartNewAsync(nameof(CreatePRReviewDecorator), cIContext); DurableOrchestrationStatus status = await starter.GetStatusAsync(instanceId, false, false); return((ActionResult) new OkObjectResult(status)); }
public static async Task <IActionResult> Run( [HttpTrigger(AuthorizationLevel.Function, "post", Route = "onpaymentreceived")] HttpRequest req, [DurableClient] IDurableOrchestrationClient starter, ILogger log) { log.LogInformation($"Payment received on {DateTime.UtcNow.ToAEST()}"); var requestBody = await new StreamReader(req.Body).ReadToEndAsync(); var data = JsonConvert.DeserializeObject <PaymentReceived>(requestBody); log.LogInformation($"Payment data: {requestBody}"); var instanceId = await starter.StartNewAsync("PaymentReceivedProcessor", data); var status = await starter.GetStatusAsync(instanceId, showHistory : false); if (status.RuntimeStatus == OrchestrationRuntimeStatus.Failed) { log.LogError($"Processing payment failed"); } return(new OkObjectResult("Payment processed succesfully")); }
public static async Task <IActionResult> Callback( [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "api/callback/{id}/{status}")] HttpRequestMessage req, [DurableClient] IDurableOrchestrationClient client, ILogger log, string id, string status) { var instanceStatus = await client.GetStatusAsync(id); try { await client.RaiseEventAsync(id, "signeeevent", status); } catch (Exception e) { log.LogWarning(e, $"Raise event failed, status for instance: {instanceStatus.RuntimeStatus}"); return(new NoContentResult()); } return(new AcceptedResult()); }
// TODO: Make this a built-in API public static async Task <DurableOrchestrationStatus> WaitForStartAsync( this IDurableOrchestrationClient client, string instanceId, CancellationToken cancellationToken) { while (!cancellationToken.IsCancellationRequested) { DurableOrchestrationStatus status = await client.GetStatusAsync(instanceId); if (status != null && status.RuntimeStatus != OrchestrationRuntimeStatus.Pending) { return(status); } await Task.Delay(TimeSpan.FromMilliseconds(500)); } cancellationToken.ThrowIfCancellationRequested(); // Code should never reach here return(null !); }
public static async Task <bool> TryStartSingletonAsync(this IDurableOrchestrationClient starter, string orchestrationName, string instanceId, object input) { var inactiveStatuses = new OrchestrationRuntimeStatus[] { OrchestrationRuntimeStatus.Terminated, OrchestrationRuntimeStatus.Failed, OrchestrationRuntimeStatus.Canceled, OrchestrationRuntimeStatus.Completed }; var instance = await starter.GetStatusAsync(instanceId); if (instance == null || inactiveStatuses.Contains(instance.RuntimeStatus)) { await starter.StartNewAsync(orchestrationName, instanceId, input); return(true); } else { return(false); } }
public static async Task <HttpResponseMessage> HttpStartSingle( [HttpTrigger(AuthorizationLevel.Function, methods: "post", Route = "orchestrators/{functionName}/{instanceId}")] HttpRequestMessage req, [DurableClient] IDurableOrchestrationClient starter, string functionName, string instanceId, ILogger log) { // Check if an instance with the specified ID already exists. var existingInstance = await starter.GetStatusAsync(instanceId); if (existingInstance == null) { // An instance with the specified ID doesn't exist, create one. // Get the id of the last tweet treated previously, as specified in the http request. string lastTweetId = req.RequestUri.ParseQueryString()["lastTweetId"]; if (lastTweetId == null) { return(new HttpResponseMessage(HttpStatusCode.BadRequest) { Content = new StringContent("Please pass lastTweetId in the query string."), }); } await starter.StartNewAsync(functionName, instanceId, lastTweetId); log.LogInformation($"Started orchestration with ID = '{instanceId}'."); return(starter.CreateCheckStatusResponse(req, instanceId)); } else { // An instance with the specified ID exists, don't create one. return(new HttpResponseMessage(HttpStatusCode.Conflict) { Content = new StringContent($"An instance with ID '{instanceId}' already exists."), }); } }
public static async Task <HttpResponseMessage> ResetDeviceStatus( [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = "v1/devices/{deviceId}/status/reset")] HttpRequestMessage req, string deviceId, [DurableClient] IDurableOrchestrationClient starter, ILogger log) { var instanceId = deviceId; var existingInstance = await starter.GetStatusAsync(instanceId); if (existingInstance != null) { await starter.TerminateAsync(instanceId, "reset command called."); //await starter.PurgeInstanceHistoryAsync(instanceId); // これを呼ぶとInstanceの情報すべてが削除される。この場合StartNewAsyncとWaitForCompletionOrCreateCheckStatusResponseAsyncの必要はない await starter.StartNewAsync( "UpdateDeviceStatus", instanceId, new InputData { State = existingInstance?.CustomStatus?.ToObject <DeviceState>(), Input = new[] { new { Type = "terminate" } } }); var output = await starter.WaitForCompletionOrCreateCheckStatusResponseAsync( req, instanceId, TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(1)); } return(req.CreateResponse(HttpStatusCode.OK)); }
private async Task RunNextTaskIfComputeIsAvailable() { if (Status != ComputeSetStatus.Running) { if (Status == ComputeSetStatus.NotStarted) { Entity.Current.StartNewOrchestration(nameof(Orchestrators.StartComputeSetOrchestrator), Entity.Current.EntityKey); Status = ComputeSetStatus.Starting; } return; } var computeIsAvailable = CurrentRequest == null; if (!computeIsAvailable) { return; } if (!InstanceIdQueue.TryDequeue(out var instanceId)) { // no pending tasks return; } // get the task hierarchy from the input of the orchestrator // (so the entity only needs to keep a queue of orchestration ids) var taskOrchestrator = await client.GetStatusAsync(instanceId); var taskHierarchy = taskOrchestrator.Input.ToObject <TaskHierarchy>(); CurrentRequest = new TaskExecutionRequest { TaskOrchestratorInstanceId = instanceId, Tasks = taskHierarchy }; Entity.Current.StartNewOrchestration(nameof(Orchestrators.TaskExecutionOrchestrator), CurrentRequest); }
public static async Task IotHubScaleInit( [TimerTrigger("5 * * * * *")] TimerInfo myTimer, [DurableClient] IDurableOrchestrationClient starter, ILogger log) { // Function input comes from the request content. string instanceId = await starter.StartNewAsync("IotHubScaleOrchestratorName", null); log.LogInformation($"C# Timer trigger function executed at: {DateTime.Now}"); //await starter.StartNewAsync(IotHubScaleOrchestratorName, IotHubScaleOrchestratorInstanceId, input: null); var existingInstance = await starter.GetStatusAsync(IotHubScaleWorkerName); if (existingInstance == null) { log.LogInformation(String.Format("{0} job not running, starting new instance...", IotHubScaleOrchestratorInstanceId)); await starter.StartNewAsync("IotHubScaleOrchestratorName", null); } else { log.LogInformation(String.Format("An instance of {0} job is already running, nothing to do...", IotHubScaleOrchestratorInstanceId)); } }
//TODO: Implement Get Trip Demo Instances, Restart Trip Demo Instances and Terminate Trip Demo Instances if Persist to table storage if persist instances is activated /** PRIVATE **/ private static async Task StartInstance(IDurableOrchestrationClient context, TripDemoState state, string instanceId, ILogger log) { try { var reportStatus = await context.GetStatusAsync(instanceId); string runningStatus = reportStatus == null ? "NULL" : reportStatus.RuntimeStatus.ToString(); log.LogInformation($"Instance running status: '{runningStatus}'."); if (reportStatus == null || reportStatus.RuntimeStatus != OrchestrationRuntimeStatus.Running) { await context.StartNewAsync("O_DemoTrip", instanceId, state); log.LogInformation($"Started a new trip demo = '{instanceId}'."); // TODO: Persist to table storage if persist instances is activated } } catch (Exception ex) { throw ex; } }
public static async Task <HttpResponseMessage> HttpStart( [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = "orchestrators/{functionName}/{instanceId}")] HttpRequestMessage req, [DurableClient] IDurableOrchestrationClient starter, string functionName, string instanceId, ILogger log) { var existingInstance = await starter.GetStatusAsync(instanceId); if (existingInstance == null) { await starter.StartNewAsync(functionName, instanceId); log.LogInformation($"Started orchestration with ID = '{instanceId}'."); return(starter.CreateCheckStatusResponse(req, instanceId)); } return(new HttpResponseMessage(HttpStatusCode.Conflict) { Content = new StringContent($"An instance with ID '{instanceId}' already exists."), }); }
public static async Task <HttpResponseMessage> HttpStart( [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] HttpRequestMessage req, [DurableClient] IDurableOrchestrationClient starter, ILogger log) { OrchestrationStatusQueryCondition status = new OrchestrationStatusQueryCondition(); List <Microsoft.Azure.WebJobs.Extensions.DurableTask.OrchestrationRuntimeStatus> sL = new List <Microsoft.Azure.WebJobs.Extensions.DurableTask.OrchestrationRuntimeStatus>(); sL.Add(OrchestrationRuntimeStatus.Running); sL.Add(OrchestrationRuntimeStatus.Completed); status.RuntimeStatus = sL; string instanceId; // Function input comes from the request content. if (string.IsNullOrEmpty(_testMessage)) { instanceId = await starter.StartNewAsync("DurableFunctionsOrchestrationTicTacToe", null); _testMessage = instanceId; } else { instanceId = await starter.StartNewAsync <string>("DurableFunctionsOrchestrationTicTacToe", _testMessage, null); } var openInstances = await starter.GetStatusAsync(instanceId, true, false, false); log.LogInformation($"This is number of running instances: '{openInstances.RuntimeStatus}'"); log.LogInformation($"Started orchestration with ID = '{instanceId}'."); log.LogInformation($"Test Mesage = '{_testMessage}'."); return(starter.CreateCheckStatusResponse(req, instanceId)); }
public async Task <IActionResult> Run( [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req, [DurableClient] IDurableOrchestrationClient client, ILogger log) { var taskItem = await GetTaskItem(req); if (taskItem == null) { return(new OkObjectResult($"TaskItem argument not valid")); } var orchestratorInstanceId = _service.TaskId(taskItem); if (string.IsNullOrWhiteSpace(orchestratorInstanceId.ToString())) { return(new OkObjectResult("orchestratorId missing")); } DurableOrchestrationStatus status = await client.GetStatusAsync(orchestratorInstanceId); // avoid runtime error, from staring the same orchestration more than once. if (status != null && status.RuntimeStatus == OrchestrationRuntimeStatus.Running) { return(new OkObjectResult(status)); } string instanceId = await client.StartNewAsync <TaskItem>(nameof(StartOrchestrator), orchestratorInstanceId, taskItem); log.LogInformation($"Started orchestration with ID = '{instanceId}'."); var result = client.CreateCheckStatusResponse(req, instanceId); return(result); }
public async Task <HttpResponseMessage> ProcessSlackApproval( [HttpTrigger(AuthorizationLevel.Anonymous, "post")] HttpRequestMessage req, [DurableClient] IDurableOrchestrationClient orchestrationClient, ILogger log) { // Get approval response from HTTP body var slackResponse = JsonSerializer.Deserialize <ApprovalResponse>(await req.Content.ReadAsStringAsync()); // Get status based on orchestration ID var status = await orchestrationClient.GetStatusAsync(slackResponse.OrchestrationInstanceID); if (status.RuntimeStatus == OrchestrationRuntimeStatus.Running || status.RuntimeStatus == OrchestrationRuntimeStatus.Pending) { log.LogInformation("Received Slack response in time, raising event"); // Raise an event for the given orchestration await orchestrationClient.RaiseEventAsync(slackResponse.OrchestrationInstanceID, ReceiveApprovalResponseEvent, slackResponse.Approved); return(new HttpResponseMessage(HttpStatusCode.OK)); } return(new HttpResponseMessage(HttpStatusCode.BadRequest)); }
public static async Task <IActionResult> Run( [HttpTrigger(AuthorizationLevel.Anonymous, methods: "get", Route = "status/{instanceId}")] HttpRequest req, [DurableClient] IDurableOrchestrationClient orchestrationClient, string instanceId, ILogger logger) { var status = await orchestrationClient.GetStatusAsync(instanceId); if (status != null) { if (status.RuntimeStatus == OrchestrationRuntimeStatus.Running || status.RuntimeStatus == OrchestrationRuntimeStatus.Pending) { string checkStatusLocacion = string.Format("{0}://{1}/api/status/{2}", req.Scheme, req.Host, instanceId); string message = $"The current status is {status.RuntimeStatus}. Check status later : GET {checkStatusLocacion}"; ActionResult response = new AcceptedResult(checkStatusLocacion, message); req.HttpContext.Response.Headers.Add("retry-after", "20"); return(response); } else if (status.RuntimeStatus == OrchestrationRuntimeStatus.Completed) { return(new OkObjectResult($"The update process is completed with '{instanceId}', Function output '{status.Output}'")); } } return(new NotFoundObjectResult($"Instance Id - '{instanceId}' not found.")); }
public async Task <IActionResult> Run( [HttpTrigger(AuthorizationLevel.Function, "get", Route = "teamHealth/{teamId}")] HttpRequest req, [DurableClient] IDurableOrchestrationClient starter, string teamId, ILogger log) { try { var connection = await _scheduleConnectorService.GetConnectionAsync(teamId).ConfigureAwait(false); var date = DateTime.Today; if (req.Query.ContainsKey("date")) { DateTime.TryParse(req.Query["date"], out date); } var weekStartDate = date.StartOfWeek(_options.StartDayOfWeek); var employees = await _wfmDataService.GetEmployeesAsync(connection.TeamId, connection.WfmBuId, weekStartDate).ConfigureAwait(false); await UpdateEmployeesWithTeamsData(connection.TeamId, employees).ConfigureAwait(false); var cachedShifts = await GetCachedShiftsAsync(connection, weekStartDate).ConfigureAwait(false); var jobIds = cachedShifts.SelectMany(s => s.Jobs).Select(j => j.WfmJobId).Distinct().ToList(); var jobs = await GetJobsAsync(connection, jobIds).ConfigureAwait(false); ExpandIds(cachedShifts, employees, jobs); var missingUsers = employees.Where(e => string.IsNullOrEmpty(e.TeamsEmployeeId)).ToList(); var missingShifts = await GetMissingShiftsAsync(connection, weekStartDate, cachedShifts).ConfigureAwait(false); jobIds = missingShifts.SelectMany(s => s.Jobs).Select(j => j.WfmJobId).Distinct().ToList(); jobs = await GetJobsAsync(connection, jobIds).ConfigureAwait(false); ExpandIds(missingShifts, employees, jobs); var mappedUsers = await GetMappedUsersAsync(connection.TeamId).ConfigureAwait(false); var teamHealthResponseModel = new TeamHealthResponseModel { TeamId = connection.TeamId, WeekStartDate = weekStartDate.AsDateString(), EmployeeCacheOrchestratorStatus = await starter.GetStatusAsync(string.Format(EmployeeCacheOrchestrator.InstanceIdPattern, connection.TeamId)).ConfigureAwait(false), EmployeeTokenRefreshOrchestratorStatus = await starter.GetStatusAsync(string.Format(EmployeeTokenRefreshOrchestrator.InstanceIdPattern, connection.TeamId)).ConfigureAwait(false), TeamOrchestratorStatus = await starter.GetStatusAsync(string.Format(ShiftsOrchestrator.InstanceIdPattern, connection.TeamId)).ConfigureAwait(false), MappedUsers = mappedUsers, MissingUsers = missingUsers, MissingShifts = missingShifts, CachedShifts = cachedShifts }; if (_featureOptions.EnableOpenShiftSync) { teamHealthResponseModel.OpenShiftsOrchestratorStatus = await starter.GetStatusAsync(string.Format(OpenShiftsOrchestrator.InstanceIdPattern, connection.TeamId)).ConfigureAwait(false); } if (_featureOptions.EnableTimeOffSync) { teamHealthResponseModel.TimeOffOrchestratorStatus = await starter.GetStatusAsync(string.Format(TimeOffOrchestrator.InstanceIdPattern, connection.TeamId)).ConfigureAwait(false); } if (_featureOptions.EnableAvailabilitySync) { teamHealthResponseModel.AvailabilityOrchestratorStatus = await starter.GetStatusAsync(string.Format(AvailabilityOrchestrator.InstanceIdPattern, connection.TeamId)).ConfigureAwait(false); } // N.B. the following block returns the JSON in a ContentResult rather than in the // rather more concise JsonResult because to return the Json with the settings // required adding a package dependency to Microsoft.AspNetCore.Mvc.NewtonsoftJson // as per https://github.com/Azure/azure-functions-core-tools/issues/1907 which then // caused an issue with incompatible dependencies and a significant issue with // deserializing json in HttpRequestExtensions var json = JsonConvert.SerializeObject(teamHealthResponseModel, Formatting.Indented); return(new ContentResult { Content = json, ContentType = "application/json", StatusCode = (int)HttpStatusCode.OK }); } catch (Exception ex) { log.LogError(ex, "Team health failed!"); return(new ContentResult { Content = $"Unexpected exception: {ex.Message}", StatusCode = StatusCodes.Status500InternalServerError }); } }
public static async Task <HttpResponseMessage> HttpStart( [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] HttpRequestMessage req, [DurableClient] IDurableOrchestrationClient starter, ILogger log) { var payloadFromEventGrid = JToken.ReadFrom(new JsonTextReader(new StreamReader(await req.Content.ReadAsStreamAsync()))); dynamic eventGridSoleItem = (payloadFromEventGrid as JArray)?.SingleOrDefault(); if (eventGridSoleItem == null) { return(req.CreateErrorResponse(HttpStatusCode.BadRequest, $@"Expecting only one item in the Event Grid message")); } if (eventGridSoleItem.eventType == @"Microsoft.EventGrid.SubscriptionValidationEvent") { //log.Verbose(@"Event Grid Validation event received."); return(new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent($"{{ \"validationResponse\" : \"{((dynamic)payloadFromEventGrid)[0].data.validationCode}\" }}") }); } string filename = ParseEventGridPayload(eventGridSoleItem, log); string orderid = filename.Substring(0, filename.IndexOf('-')); if (string.IsNullOrEmpty(filename)) { // The request either wasn't valid (filename couldn't be parsed) or not applicable (put in to a folder other than /inbound) return(new HttpResponseMessage(HttpStatusCode.NoContent)); //req.CreateCompatibleResponse(HttpStatusCode.NoContent); } var instanceForPrefix = await starter.GetStatusAsync(orderid); if (instanceForPrefix == null) { //starter.Log(log, $@"New instance needed for prefix '{orderid}'. Starting..."); var retval = await starter.StartNewAsync(@"EnsureAllFiles", orderid, filename); //starter.Log(log, $@"Started. {retval}"); log.LogDebug($"No existing instance. Starting new instance for {orderid}"); } else { //starter.Log(log, $@"Instance already waiting. Current status: {instanceForPrefix.RuntimeStatus}. Firing 'newfile' event..."); // if (instanceForPrefix.RuntimeStatus != OrchestrationRuntimeStatus.Running) // { // if (instanceForPrefix.RuntimeStatus != OrchestrationRuntimeStatus.Terminated) // { // await starter.TerminateAsync(orderid, @"bounce"); // } // var retval = await starter.StartNewAsync(@"EnsureAllFiles", orderid, filename); // //starter.Log(log, $@"Restarted listener for {orderid}. {retval}"); // } // else // { log.LogDebug($"Found existing instance for {orderid}."); log.LogDebug($"Current state is {instanceForPrefix.RuntimeStatus.ToString()}"); await starter.RaiseEventAsync(orderid, @"newfile", filename); // } } return(starter.CreateCheckStatusResponse(req, orderid)); }
public virtual async Task <string> runAction(IDurableOrchestrationClient starter, ILogger log) { var entLookup = Environment.GetEnvironmentVariable("LCU-ENTERPRISE-LOOKUP"); try { var listInstanceQuery = new OrchestrationStatusQueryCondition() { PageSize = 1000, RuntimeStatus = new[] { OrchestrationRuntimeStatus.Running, OrchestrationRuntimeStatus.Pending } }; var instances = await starter.ListInstancesAsync(listInstanceQuery, new System.Threading.CancellationToken()); var running = new HashSet <string>(instances.DurableOrchestrationState.Select(instance => instance.InstanceId)); var terminateTasks = running.Select(instanceId => { return(starter.TerminateAsync(instanceId, "Cleanup")); }); await Task.WhenAll(terminateTasks); while (running.Count > 0) { var results = await Task.WhenAll(instances.DurableOrchestrationState.Select(instance => starter.GetStatusAsync(instance.InstanceId))); foreach (var status in results) { // Remove any terminated or completed instances from the hashset if (status != null && status.RuntimeStatus != OrchestrationRuntimeStatus.Pending && status.RuntimeStatus != OrchestrationRuntimeStatus.Running) { running.Remove(status.InstanceId); } } await Task.Delay(TimeSpan.FromMilliseconds(1000)); } var instanceId = await starter.StartAction("RenewCertificatesOrchestration", new StateDetails() { EnterpriseLookup = entLookup }, new ExecuteActionRequest() { Arguments = new { EnterpriseLookup = entLookup }.JSONConvert <MetadataModel>() }, log); return(instanceId); } catch (Exception ex) { return(null); } }
private async Task <HttpResponseMessage> HandleGetStatusRequestAsync( HttpRequestMessage request, string instanceId) { IDurableOrchestrationClient client = this.GetClient(request); var queryNameValuePairs = request.GetQueryNameValuePairs(); var showHistory = GetBooleanQueryParameterValue(queryNameValuePairs, ShowHistoryParameter, defaultValue: false); var showHistoryOutput = GetBooleanQueryParameterValue(queryNameValuePairs, ShowHistoryOutputParameter, defaultValue: false); bool showInput = GetBooleanQueryParameterValue(queryNameValuePairs, ShowInputParameter, defaultValue: true); bool returnInternalServerErrorOnFailure = GetBooleanQueryParameterValue(queryNameValuePairs, ReturnInternalServerErrorOnFailure, defaultValue: false); var status = await client.GetStatusAsync(instanceId, showHistory, showHistoryOutput, showInput); if (status == null) { return(request.CreateResponse(HttpStatusCode.NotFound)); } HttpStatusCode statusCode; Uri location; switch (status.RuntimeStatus) { // The orchestration is running - return 202 w/Location header case OrchestrationRuntimeStatus.Running: case OrchestrationRuntimeStatus.Pending: case OrchestrationRuntimeStatus.ContinuedAsNew: statusCode = HttpStatusCode.Accepted; location = request.RequestUri; break; // The orchestration has failed - return 500 w/out Location header case OrchestrationRuntimeStatus.Failed: if (returnInternalServerErrorOnFailure) { statusCode = HttpStatusCode.InternalServerError; } else { statusCode = HttpStatusCode.OK; } location = null; break; // The orchestration is not running - return 200 w/out Location header case OrchestrationRuntimeStatus.Canceled: case OrchestrationRuntimeStatus.Terminated: case OrchestrationRuntimeStatus.Completed: statusCode = HttpStatusCode.OK; location = null; break; default: this.logger.LogError($"Unknown runtime state '{status.RuntimeStatus}'."); statusCode = HttpStatusCode.InternalServerError; location = null; break; } var response = request.CreateResponse( statusCode, this.ConvertFrom(status)); if (location != null) { response.Headers.Location = location; } if (statusCode == HttpStatusCode.Accepted) { // Ask for 5 seconds before retry. Some clients will otherwise retry in a tight loop. response.Headers.RetryAfter = new RetryConditionHeaderValue(TimeSpan.FromSeconds(5)); } return(response); }
public async Task RunAsync( [ServiceBusTrigger("%membershipQueueName%", Connection = "differenceQueueConnection", IsSessionsEnabled = true)] Message message, [DurableClient] IDurableOrchestrationClient starter, IMessageSession messageSession) { await _loggingRepository.LogMessageAsync(new LogMessage { Message = nameof(StarterFunction) + " function started" }); var messageDetails = _messageService.GetMessageProperties(message); var graphRequest = new GraphUpdaterFunctionRequest() { Message = Encoding.UTF8.GetString(messageDetails.Body), MessageSessionId = messageDetails.SessionId, MessageLockToken = messageDetails.LockToken }; var groupMembership = JsonConvert.DeserializeObject <GroupMembership>(graphRequest.Message); SetSessionTracker(messageDetails, groupMembership); var source = new CancellationTokenSource(); var renew = RenewMessages(starter, messageSession, source, messageDetails.MessageId); var instanceId = await starter.StartNewAsync(nameof(OrchestratorFunction), graphRequest); var completedGroupMembershipMessages = default(List <GroupMembershipMessage>); var isLastMessage = false; var orchestratorRuntimeStatusCodesWorthRetrying = new OrchestrationRuntimeStatus[] { OrchestrationRuntimeStatus.ContinuedAsNew, OrchestrationRuntimeStatus.Running, OrchestrationRuntimeStatus.Pending }; var result = default(DurableOrchestrationStatus); /*Understanding the Retry policy * We have a lot of sub-second sync execution so the first query would ensure we cater to those queries * We also have a lot of syncs that take less than 10 seconds. Having a exponetial backoff 1.25^1 would mean we would be waiting 90 seconds per sync instead of 10 seconds. * Hence the logic to ensure retryAttempt 1 is done after 10 seconds. Following this we go back to the exponetial backoff. */ var retryPolicy = Policy .HandleResult <DurableOrchestrationStatus>(status => orchestratorRuntimeStatusCodesWorthRetrying.Contains(status.RuntimeStatus)) .WaitAndRetryAsync( MAX_RETRY_ATTEMPTS, retryAttempt => { if (retryAttempt == 1) { return(TimeSpan.FromSeconds(FIRST_RETRY_DELAY_IN_SECONDS)); } else { return(TimeSpan.FromMinutes(Math.Pow(1.25, retryAttempt - 1))); } } ); await retryPolicy.ExecuteAsync(async() => { result = await starter.GetStatusAsync(instanceId); return(result); }); if (result.RuntimeStatus == OrchestrationRuntimeStatus.Failed || result.RuntimeStatus == OrchestrationRuntimeStatus.Terminated) { await _loggingRepository.LogMessageAsync(new LogMessage { Message = $"Error: Status of instance {result.InstanceId} is {result.RuntimeStatus}. The error message is : {result.Output}" }); // stop renewing the message session source.Cancel(); } else { await _loggingRepository.LogMessageAsync(new LogMessage { Message = $"Instance processing completed for {result.InstanceId}" }); var orchestratorResponseOutput = JsonConvert.DeserializeObject <GroupMembershipMessageResponse>(result.Output.ToString()); completedGroupMembershipMessages = orchestratorResponseOutput.CompletedGroupMembershipMessages; isLastMessage = orchestratorResponseOutput.ShouldCompleteMessage; } if (isLastMessage) { var completedLockTokens = completedGroupMembershipMessages.Select(x => x.LockToken); await messageSession.CompleteAsync(completedLockTokens); await messageSession.CloseAsync(); source.Cancel(); } await _loggingRepository.LogMessageAsync(new LogMessage { Message = nameof(StarterFunction) + " function completed" }); }
public async Task Run( [TimerTrigger("0 */5 * * * *")] TimerInfo timerInfo, [DurableClient] IDurableOrchestrationClient starter) { if (starter is null) { throw new ArgumentNullException(nameof(starter)); } var allGlobalXSettings = await _mediator.Send(new GlobalXOrgSettingsQuery() { DocumentSyncEnabled = true }); var allExceptions = new List <Exception>(); foreach (var globalXSettings in allGlobalXSettings) { var lastDocumentSyncInstant = Instant.FromDateTimeUtc(globalXSettings.LastDocumentSyncUtc); try { var validator = new GlobalXOrgSettings.Validator(); validator.ValidateAndThrow(globalXSettings); } catch (ValidationException vex) { allExceptions.Add(vex); _logger.LogError(vex, $"Error encountered processing documents for org key'{globalXSettings?.ActionstepOrgKey}'. Settings are invalid."); } try { var documentsQuery = new DocumentsRequest() { UserId = globalXSettings.GlobalXAdminId, After = lastDocumentSyncInstant.WithOffset(Offset.Zero), Statuses = { DocumentStatus.Complete } }; var thisSyncTime = _clock.GetCurrentInstant(); await foreach (var documentWithoutVersions in _globalXService.GetDocuments(documentsQuery)) { if (!documentWithoutVersions.DocumentId.HasValue) { _logger.LogError("Error encountered processing document. Response was missing DocumentId for" + " org '{ActionstepOrgKey}', GlobalX Admin ID: '{GlobalXAdminId}'", globalXSettings?.ActionstepOrgKey, globalXSettings?.GlobalXAdminId); continue; } Document documentWithVersions; try { documentWithVersions = await _globalXService.GetDocument(documentWithoutVersions.DocumentId.Value, globalXSettings.GlobalXAdminId); } catch (Exception ex) { _logger.LogError(ex, "Error encountered retrieving document version information for DocumentId '{DocumentId}'" + " org '{ActionstepOrgKey}', GlobalX Admin ID: '{GlobalXAdminId}'", documentWithoutVersions.DocumentId.Value, globalXSettings?.ActionstepOrgKey, globalXSettings?.GlobalXAdminId); continue; } foreach (var documentVersion in documentWithVersions.DocumentVersions) { try { var documentVersionId = documentVersion.DocumentVersionId.ToString(); // Only process "Complete" documents if (documentVersion.StatusDescription != DocumentStatus.Complete.ToString()) { _logger.LogInformation("Skipping document version '{DocumentVersionId}' and statys '{Status}', because it's status is not {RequiredDocumentStatus}.", documentVersionId, documentVersion.StatusDescription, DocumentStatus.Complete.ToString()); continue; } // Only process PDFs if (documentVersion.MimeType != _mimeTypePdf) { _logger.LogInformation("Skipping document version '{DocumentVersionId}' and Mime type '{DocumentMimeType}', as it does not have the mime type {RequiredMimeType}.", documentVersionId, documentVersion.MimeType, _mimeTypePdf); continue; } // Only process document versions updated since the last sync if (documentVersion.Timestamp.Value.ToInstant() < lastDocumentSyncInstant) { _logger.LogInformation("Skipping document version '{DocumentVersionId}' with Timestamp '{Timestamp}', because it's timestamp is before the last sync job time of 'LastDocumentSync' which means that this document version should already have been processed.", documentVersionId, documentVersion.Timestamp.Value, lastDocumentSyncInstant); continue; } var instanceId = GlobalXDocumentSyncOrchestrator.InstancePrefix + documentVersionId; var existingInstance = await starter.GetStatusAsync(instanceId); if (existingInstance is null) { _logger.LogDebug("About to start '{OrchestratorName}' for document version '{DocumentVersionId}'.", nameof(GlobalXDocumentSyncOrchestrator), documentVersionId, documentVersion.Timestamp.Value, lastDocumentSyncInstant); await starter.StartNewAsync( orchestratorFunctionName : nameof(GlobalXDocumentSyncOrchestrator), instanceId : instanceId, input : new CopyDocumentVersionToActionstepCommand() { GlobalXUserId = globalXSettings.GlobalXAdminId, ActionstepUserId = globalXSettings.ActionstepSyncUserId, ActionstepOrgKey = globalXSettings.ActionstepOrgKey, MinimumMatterIdToSync = globalXSettings.MinimumMatterIdToSync, // Use document without versions because we only care about the single version being // processed by this orchestrator. Any remaining versions are unnecessary. Document = documentWithoutVersions, DocumentVersion = documentVersion }); } else { _logger.LogInformation("Orchestration '{OrchestratorName}' for document version '{DocumentVersionId}' is already running, so does not need to be started", nameof(GlobalXDocumentSyncOrchestrator), documentVersionId); } } catch (Exception ex) { allExceptions.Add(ex); _logger.LogError(ex, "Error encountered processing document version '{DocumentVersionId}' for" + " org '{ActionstepOrgKey}', GlobalX Admin ID: '{GlobalXAdminId}'", documentVersion?.DocumentVersionId, globalXSettings?.ActionstepOrgKey, globalXSettings?.GlobalXAdminId); } } } await _mediator.Send(new SetLastDocumentSyncTimeCommand(globalXSettings.ActionstepOrgKey, thisSyncTime)); } catch (Exception ex) { allExceptions.Add(ex); _logger.LogError(ex, "Error encountered while retrieving document versions for" + " org '{ActionstepOrgKey}', GlobalX Admin ID: '{GlobalXAdminId}'", globalXSettings?.ActionstepOrgKey, globalXSettings?.GlobalXAdminId); } } if (allExceptions.Count > 0) { // If there were any failures, throwing ensures that the TimerJob shows up as failed. throw new AggregateException("One or more failures encountered while processing GlobalX documents.", allExceptions); } }