public async Task <CheckApprovalResult> BetterHandlingValidateReportLineItem([ActivityTrigger] CheckApprovalRequest checkApprovalRequest)
        {
            var validator = _expenseValidatorFactory.Create(checkApprovalRequest.ExpenseCategory);

            var approved = await validator.IsApproved(checkApprovalRequest.EmployeeId, checkApprovalRequest.Amount);

            return(new CheckApprovalResult {
                LineItemId = checkApprovalRequest.LineItemId, IsApproved = approved
            });
        }
        public async Task BetterHandlingOrchestrator(
            [OrchestrationTrigger] IDurableOrchestrationContext context)
        {
            var report = context.GetInput <ExpenseReport>();

            var lineItemTasks = new List <Task <CheckApprovalResult> >();

            foreach (var lineItem in report.LineItems)
            {
                var approvalRequest = new CheckApprovalRequest
                {
                    LineItemId      = lineItem.Id,
                    EmployeeId      = report.EmployeeId,
                    Amount          = lineItem.Amount,
                    ExpenseCategory = lineItem.Category
                };
                lineItemTasks.Add(context.CallActivityAsync <CheckApprovalResult>(nameof(BetterHandlingValidateReportLineItem), approvalRequest));
            }

            await Task.WhenAll(lineItemTasks);

            foreach (var lineItem in report.LineItems)
            {
                lineItem.Approved = lineItemTasks.FirstOrDefault(task => task.Result.LineItemId == lineItem.Id)?.Result.IsApproved ?? false;
            }

            try
            {
                await context.CallActivityAsync(nameof(SaveReport), report);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Failed to save validated report");
            }

            var retryOptions = new RetryOptions(TimeSpan.FromSeconds(5), 3);
            await context.CallActivityWithRetryAsync(nameof(NotifyUserReportFinished), retryOptions, report.EmployeeId);
        }
        public async Task AdvancedHandlingOrchestrator(
            [OrchestrationTrigger] IDurableOrchestrationContext context)
        {
            try
            {
                var report = context.GetInput <ExpenseReport>();

                var lineItemTasks = new List <Task <CheckApprovalResult> >();
                foreach (var lineItem in report.LineItems)
                {
                    var approvalRequest = new CheckApprovalRequest
                    {
                        LineItemId      = lineItem.Id,
                        EmployeeId      = report.EmployeeId,
                        Amount          = lineItem.Amount,
                        ExpenseCategory = lineItem.Category
                    };
                    lineItemTasks.Add(context.CallActivityAsync <CheckApprovalResult>(nameof(AdvancedHandlingValidateReportLineItem), approvalRequest));
                }

                await Task.WhenAll(lineItemTasks);

                foreach (var lineItem in report.LineItems)
                {
                    lineItem.Approved = lineItemTasks.FirstOrDefault(task => task.Result.LineItemId == lineItem.Id)?.Result.IsApproved ?? false;
                }

                await context.CallActivityAsync(nameof(SaveExpenseReport), report);

                await context.CallActivityAsync(nameof(SendApprovalRequestNotification), report.Id);

                using (var timeoutCts = new CancellationTokenSource())
                {
                    var expiration   = context.CurrentUtcDateTime.AddHours(24);
                    var timeoutTask  = context.CreateTimer(expiration, timeoutCts.Token);
                    var approvedTask = context.WaitForExternalEvent <string>(Constants.Events.Approval);

                    var resultTask = await Task.WhenAny(timeoutTask, approvedTask);

                    if (resultTask == timeoutTask)
                    {
                        await context.CallActivityAsync(nameof(SendEscalationNotification), report.Id);
                    }
                    else
                    {
                        await context.CallActivityAsync(nameof(SendReportReceivedResponseNotification), (report.Id, approvedTask.Result));
                    }

                    if (!timeoutTask.IsCompleted)
                    {
                        // the function wont exit unless this is complete or cancelled
                        timeoutCts.Cancel();
                    }
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, $"Error occurred during orchestration {context.InstanceId}");

                // In a real application we'd probably want to send some sort of notification here
            }
        }