Exemple #1
0
        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;
            }
        }
Exemple #4
0
        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);
        }
Exemple #7
0
        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,
                });
            }));
        }
Exemple #9
0
        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);
                }
            }
        }
Exemple #10
0
        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());
        }
Exemple #13
0
        // 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);
            }
        }
Exemple #15
0
        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));
        }
Exemple #17
0
        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);
        }
Exemple #18
0
        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));
            }
        }
Exemple #19
0
        //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));
        }
Exemple #22
0
        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);
        }
Exemple #23
0
        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));
        }
Exemple #24
0
        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."));
        }
Exemple #25
0
        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
                });
            }
        }
Exemple #26
0
        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));
        }
Exemple #27
0
        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);
            }
        }