public async Task Execute(
        [OrchestrationTrigger] IDurableOrchestrationContext orchestratorContext,
        ILogger log)
    {
        if (orchestratorContext is null)
        {
            throw new ArgumentNullException(nameof(orchestratorContext));
        }

        if (log is null)
        {
            throw new ArgumentNullException(nameof(log));
        }

        var command       = orchestratorContext.GetInput <ICommand>();
        var commandResult = command.CreateResult();

        try
        {
            await orchestratorContext
            .AuditAsync(command, commandResult)
            .ConfigureAwait(true);

            var commandHandler = commandHandlers
                                 .SingleOrDefault(ch => ch.Orchestration && ch.CanHandle(command));

            if (commandHandler is null)
            {
                throw new NullReferenceException($"Could not find orchestration handler for command '{command.GetType()}'");
            }

            await orchestratorContext
            .AuditAsync(command, commandResult)
            .ConfigureAwait(true);

            commandResult = await commandHandler
                            .HandleAsync(command, new CommandCollector(orchestratorContext, command), orchestratorContext, log)
                            .ConfigureAwait(true);

            if (commandResult is null)
            {
                throw new NullReferenceException($"Command handler '{commandHandler.GetType()}' returned NULL result");
            }
        }
        catch (Exception exc)
        {
            commandResult ??= command.CreateResult();
            commandResult.Errors.Add(exc);
        }
        finally
        {
            await orchestratorContext
            .AuditAsync(command, commandResult)
            .ConfigureAwait(true);

            orchestratorContext.SetOutput(commandResult);
        }
    }
        public static async Task RunOrchestrator(
            [OrchestrationTrigger] IDurableOrchestrationContext functionContext,
            ILogger log)
        {
            if (functionContext is null)
            {
                throw new ArgumentNullException(nameof(functionContext));
            }

            var command       = functionContext.GetInput <ICommand>();
            var commandResult = command.CreateResult();
            var commandLog    = functionContext.CreateReplaySafeLogger(log ?? NullLogger.Instance);

            try
            {
                await functionContext
                .AuditAsync(command, commandResult)
                .ConfigureAwait(true);

                var commandOrchestration = await functionContext
                                           .CallActivityWithRetryAsync <string>(nameof(OrchestratorCommandDispatchActivity), command)
                                           .ConfigureAwait(true);

                commandResult = await functionContext
                                .CallSubOrchestratorWithRetryAsync <ICommandResult>(commandOrchestration, command.CommandId.ToString(), command)
                                .ConfigureAwait(true);
            }
            catch (Exception exc)
            {
                commandLog.LogError(exc, $"Processing command '{command.GetType().FullName}' ({command.CommandId}) Failed >>> {exc.Message}");

                commandResult ??= command.CreateResult();
                commandResult.Errors.Add(exc);
            }
            finally
            {
                if (commandResult?.RuntimeStatus.IsUnknown() ?? false)
                {
                    commandResult = await functionContext
                                    .CallActivityWithRetryAsync <ICommandResult>(nameof(CommandResultAugmentActivity), commandResult)
                                    .ConfigureAwait(true);
                }

                await functionContext
                .AuditAsync(command, commandResult)
                .ConfigureAwait(true);

                functionContext.SetOutput(commandResult);
            }
        }
Esempio n. 3
0
        private static async Task <ICommandResult> SwitchCommandAsync(IDurableOrchestrationContext functionContext, ProviderDocument provider, IProviderCommand command, ICommandResult commandResult, ILogger log)
        {
            try
            {
                await functionContext
                .AuditAsync(command, commandResult, provider)
                .ConfigureAwait(true);

                functionContext.SetCustomStatus($"Switching command", log);

                var project = await functionContext
                              .GetProjectAsync(command.ProjectId, allowUnsafe : true)
                              .ConfigureAwait(true);

                functionContext.ContinueAsNew((
                                                  new ProviderProjectUpdateCommand
                                                  (
                                                      command.BaseApi,
                                                      command.User as Model.Data.User,
                                                      project.PopulateExternalModel(),
                                                      command.CommandId),
                                                  provider
                                                  )
                                              );
            }
            catch (Exception exc)
            {
                functionContext.SetCustomStatus($"Switching command failed: {exc.Message}", log, exc);

                commandResult ??= command.CreateResult();
                commandResult.Errors.Add(exc);
            }

            return(commandResult);
        }
        public static async Task Run(
            [OrchestrationTrigger] IDurableOrchestrationContext functionContext,
            [DurableClient] IDurableClient durableClient,
            ILogger log)
        {
            if (functionContext is null)
            {
                throw new ArgumentNullException(nameof(functionContext));
            }

            if (durableClient is null)
            {
                throw new ArgumentNullException(nameof(durableClient));
            }

            var commandMessage = functionContext.GetInput <ProviderCommandMessage>()
                                 ?? throw new ArgumentException("Command message is null", nameof(functionContext));

            var command = commandMessage.Command
                          ?? throw new ArgumentException("Command message does not contain a command", nameof(functionContext));

            var commandResult = command.CreateResult();
            var commandLog    = functionContext.CreateReplaySafeLogger(log ?? NullLogger.Instance);

            try
            {
                await functionContext
                .AuditAsync(command, commandResult)
                .ConfigureAwait(true);

                var commandOrchestrationName = await functionContext
                                               .CallActivityWithRetryAsync <string>(nameof(ProviderCommandDispatchActivity), command)
                                               .ConfigureAwait(true);

                var commandOrchestrationInstanceId = CommandHandler.GetCommandOrchestrationInstanceId(command);

                if (string.IsNullOrEmpty(commandOrchestrationName))
                {
                    commandLog
                    .LogInformation($"Dispatching command '{command.GetType().FullName}' ({command.CommandId}) >>> FALLBACK ({commandOrchestrationInstanceId})");

                    commandResult = await functionContext
                                    .CallSubOrchestratorWithRetryAsync <ICommandResult>(nameof(ProviderCommandFallbackOrchestration), commandOrchestrationInstanceId, command)
                                    .ConfigureAwait(true);
                }
                else
                {
                    commandLog
                    .LogInformation($"Dispatching command '{command.GetType().FullName}' ({commandMessage.CommandId}) >>> {commandOrchestrationName} ({commandOrchestrationInstanceId})");

                    commandResult = await functionContext
                                    .CallSubOrchestratorWithRetryAsync <ICommandResult>(commandOrchestrationName, commandOrchestrationInstanceId, command)
                                    .ConfigureAwait(true);
                }

                commandResult = await functionContext
                                .CallActivityWithRetryAsync <ICommandResult>(nameof(ProviderCommandResultAugmentActivity), (commandResult, commandOrchestrationInstanceId))
                                .ConfigureAwait(true);
            }
            catch (Exception exc)
            {
                commandLog.LogError(exc, $"Processing command '{command.GetType().FullName}' ({command.CommandId}) Failed >>> {exc.Message}");

                commandResult ??= command.CreateResult();
                commandResult.Errors.Add(exc);
            }
            finally
            {
                try
                {
                    await functionContext
                    .CallActivityWithRetryAsync(nameof(ProviderCommandResultSendActivity), (commandResult, commandMessage.CallbackUrl))
                    .ConfigureAwait(true);
                }
                catch (Exception exc)
                {
                    commandLog.LogError(exc, $"Sending result for command '{command.GetType().FullName}' ({command.CommandId}) Failed >>> {exc.Message}");

                    commandResult ??= command.CreateResult();
                    commandResult.Errors.Add(exc);
                }
                finally
                {
                    await functionContext
                    .AuditAsync(command, commandResult)
                    .ConfigureAwait(true);

                    functionContext.SetOutput(commandResult);
                }
            }
        }
        public static async Task RunOrchestrator(
            [OrchestrationTrigger] IDurableOrchestrationContext orchestrationContext,
            ILogger log)
        {
            if (orchestrationContext is null)
            {
                throw new ArgumentNullException(nameof(orchestrationContext));
            }

            var command       = orchestrationContext.GetInput <ICommand>();
            var commandResult = command.CreateResult();
            var commandLog    = orchestrationContext.CreateReplaySafeLogger(log ?? NullLogger.Instance);

            try
            {
                orchestrationContext.SetCustomStatus("Auditing command", log);

                await orchestrationContext
                .AuditAsync(command, commandResult)
                .ConfigureAwait(true);

                orchestrationContext.SetCustomStatus("Processing command", log);

                commandResult = await orchestrationContext
                                .CallSubOrchestratorWithRetryAsync <ICommandResult>(OrchestratorCommandOrchestrationHandler.GetCommandOrchestrationName(command), command.CommandId.ToString(), command)
                                .ConfigureAwait(true);
            }
            catch (Exception exc)
            {
                commandResult ??= command.CreateResult();
                commandResult.Errors.Add(exc);
            }
            finally
            {
                try
                {
                    orchestrationContext.SetCustomStatus("Augmenting command result", log);

                    commandResult = await orchestrationContext
                                    .CallActivityWithRetryAsync <ICommandResult>(nameof(CommandResultAugmentActivity), commandResult)
                                    .ConfigureAwait(true);
                }
                catch (Exception exc)
                {
                    commandResult ??= command.CreateResult();
                    commandResult.Errors.Add(exc);
                }

                orchestrationContext.SetCustomStatus("Auditing command result", log);

                await orchestrationContext
                .AuditAsync(command, commandResult)
                .ConfigureAwait(true);

                var commandException = commandResult.Errors?.ToException();

                if (commandException is null)
                {
                    orchestrationContext.SetCustomStatus($"Command succeeded", log);
                }
                else
                {
                    orchestrationContext.SetCustomStatus($"Command failed: {commandException.Message}", log, commandException);
                }

                orchestrationContext.SetOutput(commandResult);
            }
        }
        public static async Task <ICommandResult> RunOrchestrator(
            [OrchestrationTrigger] IDurableOrchestrationContext functionContext,
            ILogger log)
        {
            if (functionContext is null)
            {
                throw new ArgumentNullException(nameof(functionContext));
            }

            var(command, provider) = functionContext.GetInput <(IProviderCommand, Provider)>();

            var commandResult   = command.CreateResult();
            var commandMessage  = default(ICommandMessage);
            var commandCallback = default(string);

            try
            {
                functionContext.SetCustomStatus($"Acquire callback url for command '{command.CommandId}'", log);

                commandCallback = await functionContext
                                  .CallActivityWithRetryAsync <string>(nameof(CallbackAcquireActivity), (functionContext.InstanceId, command))
                                  .ConfigureAwait(true);

                commandMessage = new ProviderCommandMessage(command, commandCallback);

                functionContext.SetCustomStatus($"Prepare sending command '{command.CommandId}'", log);

                await RegisterProviderAsync(functionContext, command, provider)
                .ConfigureAwait(true);

                await EnableProviderAsync(functionContext, command, provider)
                .ConfigureAwait(true);

                command = await AugmentCommandAsync(functionContext, provider, command)
                          .ConfigureAwait(true);

                await functionContext
                .AuditAsync(provider, command, commandResult)
                .ConfigureAwait(true);

                try
                {
                    functionContext.SetCustomStatus($"Sending command '{command.CommandId}'", log);

                    commandResult = await functionContext
                                    .CallActivityWithRetryAsync <ICommandResult>(nameof(CommandSendActivity), (provider, commandMessage))
                                    .ConfigureAwait(true);
                }
                catch (RetryCanceledException)
                {
                    commandResult = await functionContext
                                    .CallActivityWithRetryAsync <ICommandResult>(nameof(CommandResultActivity), (provider, commandMessage))
                                    .ConfigureAwait(true);
                }
                finally
                {
                    await functionContext
                    .AuditAsync(provider, command, commandResult)
                    .ConfigureAwait(true);
                }

                if (commandResult.RuntimeStatus.IsActive())
                {
                    var commandTimeout = (commandResult.Timeout > TimeSpan.Zero && commandResult.Timeout < CommandResult.MaximumTimeout)
                        ? commandResult.Timeout         // use the timeout reported back by the provider
                        : CommandResult.MaximumTimeout; // use the defined maximum timeout

                    functionContext.SetCustomStatus($"Waiting for command ({command.CommandId}) result on {commandCallback} for {commandTimeout}", log);

                    commandResult = await functionContext
                                    .WaitForExternalEvent <ICommandResult>(command.CommandId.ToString(), commandTimeout, null)
                                    .ConfigureAwait(true);

                    if (commandResult is null)
                    {
                        commandResult = await functionContext
                                        .CallActivityWithRetryAsync <ICommandResult>(nameof(CommandResultActivity), (provider, commandMessage))
                                        .ConfigureAwait(true);

                        throw new TimeoutException($"Provider '{provider.Id}' ran into timeout ({commandTimeout})");
                    }
                }
            }
            catch (Exception exc)
            {
                functionContext.SetCustomStatus($"Sending command '{command.CommandId}' failed: {exc.Message}", log, exc);

                // ensure we always have a command result
                // to add our exception so we won't break
                // our command auditing in the finally block

                commandResult ??= command.CreateResult();
                commandResult.Errors.Add(exc);

                // re-throw the exception to inform the
                // outer orchestration that some bad happened

                throw;
            }
            finally
            {
                if (!string.IsNullOrEmpty(commandCallback))
                {
                    functionContext.SetCustomStatus($"Invalidating callback url for command '{command.CommandId}'", log);

                    await functionContext
                    .CallActivityWithRetryAsync(nameof(CallbackInvalidateActivity), functionContext.InstanceId)
                    .ConfigureAwait(true);
                }

                await functionContext
                .AuditAsync(provider, command, commandResult)
                .ConfigureAwait(true);

                await ProcessOutputAsync(functionContext, provider, command, commandResult)
                .ConfigureAwait(true);
            }

            return(commandResult);
        }
Esempio n. 7
0
        private static async Task <ICommandResult> ProcessCommandAsync(IDurableOrchestrationContext functionContext, ProviderDocument provider, IProviderCommand command, ICommandResult commandResult, ILogger log)
        {
            var commandMessage  = default(ICommandMessage);
            var commandCallback = default(string);

            try
            {
                functionContext.SetCustomStatus($"Acquire callback url", log);

                commandCallback = await functionContext
                                  .CallActivityWithRetryAsync <string>(nameof(CallbackUrlGetActivity), (functionContext.InstanceId, command))
                                  .ConfigureAwait(true);

                commandMessage = new ProviderCommandMessage(command, commandCallback);

                if (!(command is ProviderRegisterCommand || provider.Registered.HasValue))
                {
                    log.LogInformation($"Register provider {provider.Id} for command {command.CommandId}");

                    await functionContext
                    .RegisterProviderAsync(provider, true)
                    .ConfigureAwait(true);
                }

                if (!string.IsNullOrEmpty(command.ProjectId) && provider.PrincipalId.HasValue)
                {
                    log.LogInformation($"Enable provider {provider.Id} for command {command.CommandId}");

                    await functionContext
                    .CallActivityWithRetryAsync(nameof(ProjectResourcesAccessActivity), (command.ProjectId, provider.PrincipalId.Value))
                    .ConfigureAwait(true);
                }

                functionContext.SetCustomStatus($"Augmenting command", log);

                command = await AugmentCommandAsync(functionContext, provider, command)
                          .ConfigureAwait(true);

                await functionContext
                .AuditAsync(command, commandResult, provider)
                .ConfigureAwait(true);

                try
                {
                    functionContext.SetCustomStatus($"Sending command", log);

                    commandResult = await functionContext
                                    .CallActivityWithRetryAsync <ICommandResult>(nameof(CommandSendActivity), (provider, commandMessage))
                                    .ConfigureAwait(true);
                }
                catch (RetryCanceledException)
                {
                    commandResult = await functionContext
                                    .CallActivityWithRetryAsync <ICommandResult>(nameof(CommandResultFetchActivity), (provider, commandMessage))
                                    .ConfigureAwait(true);
                }
                finally
                {
                    await functionContext
                    .AuditAsync(command, commandResult, provider)
                    .ConfigureAwait(true);
                }

                if (commandResult.RuntimeStatus.IsActive())
                {
                    var commandTimeout = (commandResult.Timeout > TimeSpan.Zero && commandResult.Timeout < CommandResult.MaximumTimeout)
                        ? commandResult.Timeout         // use the timeout reported back by the provider
                        : CommandResult.MaximumTimeout; // use the defined maximum timeout

                    functionContext.SetCustomStatus($"Waiting for command result", log);

                    commandResult = await functionContext
                                    .WaitForExternalEvent <ICommandResult>(command.CommandId.ToString(), commandTimeout, null)
                                    .ConfigureAwait(true);

                    if (commandResult is null)
                    {
                        // provider ran into a timeout
                        // lets give our provider a last
                        // chance to return a command result

                        commandResult = await functionContext
                                        .CallActivityWithRetryAsync <ICommandResult>(nameof(CommandResultFetchActivity), (provider, commandMessage))
                                        .ConfigureAwait(true);

                        if (commandResult.RuntimeStatus.IsActive())
                        {
                            // the last change result still doesn't report a final runtime status
                            // escalate the timeout by throwing an appropriate exception

                            throw new TimeoutException($"Provider '{provider.Id}' ran into timeout ({commandTimeout})");
                        }
                    }
                }
            }
            catch (Exception exc)
            {
                functionContext.SetCustomStatus($"Sending command failed: {exc.Message}", log, exc);

                commandResult ??= command.CreateResult();
                commandResult.Errors.Add(exc.AsSerializable());
            }
            finally
            {
                await functionContext
                .AuditAsync(command, commandResult, provider)
                .ConfigureAwait(true);

                await ProcessOutputAsync(functionContext, provider, command, commandResult)
                .ConfigureAwait(true);
            }

            return(commandResult);
        }
Esempio n. 8
0
        public static async Task Run(
            [OrchestrationTrigger] IDurableOrchestrationContext functionContext,
            [DurableClient] IDurableClient durableClient,
            ILogger log)
        {
            if (functionContext is null)
            {
                throw new ArgumentNullException(nameof(functionContext));
            }

            if (durableClient is null)
            {
                throw new ArgumentNullException(nameof(durableClient));
            }

            var commandMessage = functionContext.GetInput <ProviderCommandMessage>()
                                 ?? throw new ArgumentException("Command message is null", nameof(functionContext));

            var command = commandMessage.Command
                          ?? throw new ArgumentException("Command message does not contain a command", nameof(functionContext));

            var commandResult = command.CreateResult();
            var commandLog    = functionContext.CreateReplaySafeLogger(log ?? NullLogger.Instance);

            try
            {
                await functionContext
                .AuditAsync(command, commandResult)
                .ConfigureAwait(true);

                var commandOrchestrationName = await functionContext
                                               .CallActivityWithRetryAsync <string>(nameof(ProviderCommandDispatchActivity), command)
                                               .ConfigureAwait(true);

                var commandOrchestrationInstanceId = CommandHandler.GetCommandOrchestrationInstanceId(command);

                if (string.IsNullOrEmpty(commandOrchestrationName))
                {
                    commandLog
                    .LogInformation($"Dispatching command '{command.GetType().FullName}' ({command.CommandId}) >>> FALLBACK ({commandOrchestrationInstanceId})");

                    commandResult = await functionContext
                                    .CallSubOrchestratorWithRetryAsync <ICommandResult>(nameof(ProviderCommandFallbackOrchestration), commandOrchestrationInstanceId, command)
                                    .ConfigureAwait(true);
                }
                else
                {
                    commandLog
                    .LogInformation($"Dispatching command '{command.GetType().FullName}' ({commandMessage.CommandId}) >>> {commandOrchestrationName} ({commandOrchestrationInstanceId})");

                    commandResult = await functionContext
                                    .CallSubOrchestratorWithRetryAsync <ICommandResult>(commandOrchestrationName, commandOrchestrationInstanceId, command)
                                    .ConfigureAwait(true);
                }

                do
                {
                    // there is a chance that the suborchestration used to agument the command result
                    // doesn't reflect the final runtime status (completed / failed / canceled) because
                    // of timing issues in the durable functions runtime. to void a none final runtime
                    // status reported back to the orchestrator we loop / wait for this runtime status.

                    commandResult = await functionContext
                                    .CallActivityWithRetryAsync <ICommandResult>(nameof(ProviderCommandResultAugmentActivity), (commandResult, commandOrchestrationInstanceId))
                                    .ConfigureAwait(true);
                }while (commandResult.RuntimeStatus.IsActive());
            }
            catch (Exception exc)
            {
                commandLog.LogError(exc, $"Processing command '{command.GetType().FullName}' ({command.CommandId}) Failed >>> {exc.Message}");

                commandResult ??= command.CreateResult();
                commandResult.Errors.Add(exc);
            }
            finally
            {
                try
                {
                    await functionContext
                    .CallActivityWithRetryAsync(nameof(ProviderCommandResultSendActivity), (commandResult, commandMessage.CallbackUrl))
                    .ConfigureAwait(true);
                }
                catch (Exception exc)
                {
                    commandLog.LogError(exc, $"Sending result for command '{command.GetType().FullName}' ({command.CommandId}) Failed >>> {exc.Message}");

                    commandResult ??= command.CreateResult();
                    commandResult.Errors.Add(exc);
                }
                finally
                {
                    await functionContext
                    .AuditAsync(command, commandResult)
                    .ConfigureAwait(true);

                    functionContext.SetOutput(commandResult);
                }
            }
        }