public async Task ValidateAsync_Error() { var command = new ProviderProjectCreateCommand(null, new Project()); var message = new ProviderCommandMessage(command, "http://localhost/callback"); var result = await message.ValidateAsync().ConfigureAwait(false); Assert.False(result.IsValid); }
public void Validate_Error() { var command = new ProviderProjectCreateCommand(null, new Project()); var message = new ProviderCommandMessage(command, "http://localhost/callback"); var result = message.Validate(); Assert.False(result.IsValid); }
public void Validate_Success() { var command = new ProviderProjectCreateCommand(new User(), new Project()); var message = new ProviderCommandMessage(command, "http://localhost/callback"); var result = message.Validate(); Assert.True(result.IsValid); }
protected async Task <ICommandResult> SendCommandAsync(IProviderCommand providerCommand, bool waitForCallbackResult = false, TimeSpan?waitForCallbackResultTimeout = default) { if (providerCommand is null) { throw new ArgumentNullException(nameof(providerCommand)); } await providerCommand .ValidateAsync(throwOnValidationError : true) .ConfigureAwait(false); var message = new ProviderCommandMessage( providerCommand, OrchestratorService.Instance.GetCallbackUrl(providerCommand)); Logger.LogInformation($"{nameof(SendCommandAsync)}:\n{JsonConvert.SerializeObject(providerCommand, Formatting.Indented)}"); var response = await ProviderService.BaseUrl .AppendPathSegment("api/command") .PostJsonAsync(message) .ConfigureAwait(false); var commandResult = await response.Content .ReadAsAsync <ICommandResult>() .ConfigureAwait(false); if (waitForCallbackResult) { commandResult = await GetCommandResultAsync(providerCommand.CommandId, waitForCallbackResult, waitForCallbackResultTimeout.GetValueOrDefault(commandResult.Timeout)) .ConfigureAwait(false); } else { ProviderServicePrincipalId = commandResult is ProviderRegisterCommandResult providerRegisterCommandResult ? providerRegisterCommandResult.Result.PrincipalId : ProviderServicePrincipalId; } return(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); }
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); }