/// <summary>
        /// Converts an execution request core model to an API model
        /// </summary>
        /// <param name="coreModel"></param>
        /// <returns></returns>
        public static ExecutionRequestApiModel ToApiModel(this ExecutionRequest coreModel)
        {
            var apiModel = new ExecutionRequestApiModel
            {
                CreatedDateTimeUtc          = coreModel.CreatedDateTimeUtc,
                ExecutionId                 = coreModel.ExecutionId,
                ExecutionModelName          = coreModel.ExecutionModelName,
                ExecutionParameters         = coreModel.ExecutionParameters,
                ExecutionProfileName        = coreModel.ExecutionProfileName,
                ExecutionTimeoutDateTimeUtc = coreModel.ExecutionTimeoutDateTimeUtc,
                ExecutionTimeoutDuration    = coreModel.ExecutionTimeoutDuration,
                Executor               = coreModel.Executor.ToApiModel(),
                ExecutorProperties     = coreModel.ExecutorProperties,
                ExtensionId            = coreModel.ExtensionId,
                ExtensionSettings      = coreModel.ExtensionSettings,
                ExtensionVersionId     = coreModel.ExtensionVersionId,
                GetExecutionStatusUrl  = coreModel.GetExecutionStatusUrl,
                IsValidationSupported  = coreModel.IsValidationSupported,
                LastUpdatedDateTimeUtc = coreModel.LastUpdatedDateTimeUtc,
                ObjectProviderName     = coreModel.ObjectProviderName,
                Priority               = coreModel.Priority,
                PutExecutionStatusUrl  = coreModel.UpdateExecutionStatusUrl,
                SignatureRsaKeyXml     = coreModel.SignatureRsaKeyXml,
                StatusUpdateKey        = coreModel.StatusUpdateKey,
                SupportedServices      = coreModel.SupportedServices,
                ValidateOnly           = coreModel.ValidateOnly
            };

            ApplyInputObjectsToApiModel(coreModel, apiModel);
            ApplyOutputObjectsToApiModel(coreModel, apiModel);

            return(apiModel);
        }
Ejemplo n.º 2
0
        public async Task BuildExecutionRequestContextAsync_ValidateOnlyButExtensionVersionDoesNotSupportValidation_ShouldGenerateError()
        {
            var mockExtensionRepository = new Mock <IExtensionRepository>();
            var erContextBuilder        = new ExecutionRequestContextBuilder(mockExtensionRepository.Object);

            var execRequestApiModel = new ExecutionRequestApiModel
            {
                ExtensionId        = DefaultExtensionId,
                ExtensionVersionId = DefaultExtensionVersionId,
                ValidateOnly       = true
            };

            var extension = CreateDefaultExtension();

            extension.ExtensionVersions.First().SupportsValidation = false;

            mockExtensionRepository
            .Setup(r => r.GetExtensionAsync(DefaultExtensionId))
            .Returns(Task.FromResult(extension));

            var erContext = await erContextBuilder.BuildExecutionRequestContextAsync(execRequestApiModel);

            erContext.ValidationErrors.Should().HaveCount(1);
            erContext.ValidationErrors.First().Should().Contain(ErrorCodes.ExtensionVersionDoesNotSupportValidation);
        }
Ejemplo n.º 3
0
        public async Task <IActionResult> GetServiceConfigurationAsync([FromBody] ExecutionRequestApiModel execRequestApiModel)
        {
            // Validate the execution request API model...

            var errors = execRequestApiModel.ValidateApiModel().ToList();

            // If we ran into any validation errors, respond with [400 Bad Request] + detailed error description...

            if (errors.Any())
            {
                return(BadRequest($"[{errors.Count}] errors occurred while attempting to process your request: {string.Join(' ', errors)}"));
            }

            // Get any service context(s) from the execution service provider...

            var execRequest       = execRequestApiModel.ToCoreModel();
            var execServiceConfig = await execServiceProvider.GetServiceConfigurationAsync(execRequest);

            if (execServiceConfig == null)
            {
                // If there are no available services, respond with [204 No Content]...

                return(NoContent());
            }
            else
            {
                // If there are available services, respond with [200 OK] + service context(s) for execution...

                return(Ok(execServiceConfig));
            }
        }
Ejemplo n.º 4
0
        public async Task <IActionResult> CreateNewExecutionAsync([FromBody, Required] ExecutionRequestApiModel apiExecRequest)
        {
            // Build/validate the execution request context...

            var erContext = await erContextBuilder.BuildExecutionRequestContextAsync(apiExecRequest);

            // If there were any validation errors, respond with [400 Bad Request] + detailed error description...

            if (erContext.ValidationErrors.Any())
            {
                return(BadRequest($"[{erContext.ValidationErrors.Count}] error(s) were encountered while attempting to fulfill " +
                                  $"your request. -- {erContext.ValidationErrors.ToSpaceSeparatedString()}"));
            }

            // Create a core execution model from the execution request context...

            erContext.Execution = await ToExecutionAsync(erContext);

            if (erContext.ExtensionVersion.InputObjects.Any())
            {
                // If the extension version defines any input objects, required or otherwise...
                // Update the status to indicate that we're waiting for the user to provide input objects...

                erContext.Execution.Status = ExecutionStatus.PendingInputObjects;

                // Persist the updated execution status...

                await UpdateExecutionAsync(erContext.Execution);

                // Respond with [202 Accepted] + instructions for providing the input object(s)...

                return(Accepted(await CreateToContinueApiModelAsync(erContext, "Please provide the following objects before continuing.")));
            }
            else
            {
                if (erContext.ExecutionProfile.ExecutionMode == ExecutionMode.Direct)
                {
                    // If the selected [executionMode] is [direct] (that is, the client will be executing the extension directly), we're basically done.
                    // For more information on direct execution, see /doc/architecture/direct-execution.md.
                    // Return a scoped, time-limited, digitally-signed execution token.

                    return(Ok(await CreateDirectExecutionRequestAsync(erContext)));
                }
                else
                {
                    // If the selected [executionMode] is [gateway] (that is, we'll be processing the execution request on behalf of the client),
                    // dispatch the execution request to the standard execution pipeline...
                    // For more information on the execution pipeline, see /doc/architecture/execution-pipeline.md.

                    return(await ToExecutionRequestRoutedResultAsync(await ExecuteAsync(erContext)));
                }
            }
        }
Ejemplo n.º 5
0
        public async Task BuildExecutionRequestContextAsync_GivenMultipleMistakes_ShouldGenerateMultipleErrors()
        {
            var mockExtensionRepository = new Mock <IExtensionRepository>();
            var erContextBuilder        = new ExecutionRequestContextBuilder(mockExtensionRepository.Object);

            var execRequestApiModel = new ExecutionRequestApiModel
            {
                Priority = "Not a real priority."
            };

            var extension = CreateDefaultExtension();

            mockExtensionRepository
            .Setup(r => r.GetExtensionAsync(DefaultExtensionId))
            .Returns(Task.FromResult(extension));

            var erContext = await erContextBuilder.BuildExecutionRequestContextAsync(execRequestApiModel);

            erContext.ValidationErrors.Should().HaveCount(3);
        }
Ejemplo n.º 6
0
        public async Task BuildExecutionRequestContextAsync_NoExtensionVersionIdProvided_ShouldGenerateError()
        {
            var mockExtensionRepository = new Mock <IExtensionRepository>();
            var erContextBuilder        = new ExecutionRequestContextBuilder(mockExtensionRepository.Object);

            var execRequestApiModel = new ExecutionRequestApiModel
            {
                ExtensionId = DefaultExtensionId
            };

            var extension = CreateDefaultExtension();

            mockExtensionRepository
            .Setup(r => r.GetExtensionAsync(DefaultExtensionId))
            .Returns(Task.FromResult(extension));

            var erContext = await erContextBuilder.BuildExecutionRequestContextAsync(execRequestApiModel);

            erContext.ValidationErrors.Should().HaveCount(1);
            erContext.ValidationErrors.First().Should().Contain(ErrorCodes.ExtensionVersionIdNotProvided);
        }
Ejemplo n.º 7
0
        public async Task BuildExecutionRequestContextAsync_ExecutionProfileDoesNotSupportPriority_ShouldGenerateError()
        {
            var mockExtensionRepository = new Mock <IExtensionRepository>();
            var erContextBuilder        = new ExecutionRequestContextBuilder(mockExtensionRepository.Object);

            var execRequestApiModel = new ExecutionRequestApiModel
            {
                ExtensionId        = DefaultExtensionId,
                ExtensionVersionId = DefaultExtensionVersionId,
                Priority           = ExecutionPriority.Low.ToString()
            };

            var extension = CreateDefaultExtension();

            mockExtensionRepository
            .Setup(r => r.GetExtensionAsync(DefaultExtensionId))
            .Returns(Task.FromResult(extension));

            var erContext = await erContextBuilder.BuildExecutionRequestContextAsync(execRequestApiModel);

            erContext.ValidationErrors.Should().HaveCount(1);
            erContext.ValidationErrors.First().Should().Contain(ErrorCodes.PriorityNotSupported);
        }
Ejemplo n.º 8
0
        public async Task <IActionResult> ExecuteAsync([FromBody] ExecutionRequestApiModel execRequestApiModel)
        {
            // Validate the provided execution request...

            var errors = execRequestApiModel.ValidateApiModel().ToList();

            // If the request is invalid, respond with [400 Bad Request] + detailed error description...

            if (errors.Any())
            {
                return(BadRequest($"[{errors.Count}] errors occurred while attempting to process your request: {string.Join(' ', errors)}"));
            }

            // Convert the execution request API model to a core model and hand it off to the execution pipeline.
            // For more information on the execution pipeline, see /doc/architecture/execution-pipeline.md.

            var execRequest = execRequestApiModel.ToCoreModel();
            var execContext = await execRequestRouter.RouteRequestAsync(execRequest, CancellationToken.None);

            // At this point, even if the execution itself failed, we did our job.
            // Respond with [200 OK] + an execution update...

            return(Ok(execContext.ToApiModel()));
        }
        /// <summary>
        /// Validates an execution request API model
        /// </summary>
        /// <param name="apiModel"></param>
        /// <returns></returns>
        public static IEnumerable <string> ValidateApiModel(this ExecutionRequestApiModel apiModel)
        {
            if (string.IsNullOrEmpty(apiModel.ExecutionId))
            {
                yield return("[executionId] is required.");
            }

            if (string.IsNullOrEmpty(apiModel.ExtensionId))
            {
                yield return("[extensionId] is required.");
            }

            if (string.IsNullOrEmpty(apiModel.ExtensionVersionId))
            {
                yield return("[extensionVersionId] is required.");
            }

            if (string.IsNullOrEmpty(apiModel.ExecutionModelName))
            {
                yield return("[executionModelName] is required.");
            }

            if (string.IsNullOrEmpty(apiModel.ObjectProviderName))
            {
                yield return("[objectProviderName] is required.");
            }

            if (string.IsNullOrEmpty(apiModel.StatusUpdateKey))
            {
                yield return("[statusUpdateKey] is required.");
            }

            if (string.IsNullOrEmpty(apiModel.GetExecutionStatusUrl))
            {
                yield return("[getExecutionStatusUrl] is required.");
            }

            if (string.IsNullOrEmpty(apiModel.PutExecutionStatusUrl))
            {
                yield return("[putExecutionStatusUrl] is required.");
            }

            if (string.IsNullOrEmpty(apiModel.SignatureRsaKeyXml))
            {
                yield return("[signatureRsaKeyXml] is required.");
            }

            if (apiModel.CreatedDateTimeUtc == default)
            {
                yield return("[createdDateTimeUtc] is required.");
            }

            if (apiModel.LastUpdatedDateTimeUtc == default)
            {
                yield return("[lastUpdatedDateTimeUtc] is required.");
            }

            if (apiModel.Executor == null)
            {
                yield return("[executor] is required.");
            }
            else
            {
                foreach (var executorError in apiModel.Executor.ValidateApiModel())
                {
                    yield return($"[executor]: {executorError}");
                }
            }

            if (apiModel.Priority == ExecutionPriority.Undefined)
            {
                yield return("[priority] is required; valid priorities are [1] (low), [2] (normal), and [3] (high).");
            }
        }
Ejemplo n.º 10
0
        private static ExecutionRequest ApplyOutputObjectsToCoreModel(ExecutionRequest coreModel, ExecutionRequestApiModel apiModel)
        {
            foreach (var objectName in apiModel.OutputObjects.Keys)
            {
                var ooApiModel = apiModel.OutputObjects[objectName];

                coreModel.OutputObjects.Add(objectName, ooApiModel.ToCoreModel(objectName));
            }

            return(coreModel);
        }
Ejemplo n.º 11
0
        private static ExecutionRequest ApplyInputObjectsToCoreModel(ExecutionRequest coreModel, ExecutionRequestApiModel apiModel)
        {
            foreach (var objectName in apiModel.InputObjects.Keys)
            {
                var ioApiModel = apiModel.InputObjects[objectName];

                coreModel.InputObjects.Add(objectName, ioApiModel.ToCoreModel(objectName));

                if (ioApiModel.IsProvided)
                {
                    coreModel.ProvidedInputObjects.Add(objectName);
                }
            }

            return(coreModel);
        }
Ejemplo n.º 12
0
        private static ExecutionRequestApiModel ApplyInputObjectsToApiModel(ExecutionRequest coreModel, ExecutionRequestApiModel apiModel)
        {
            foreach (var objectName in coreModel.InputObjects.Keys)
            {
                var ioCoreModel = coreModel.InputObjects[objectName];
                var ioApiModel  = ioCoreModel.ToApiModel();

                ioApiModel.IsProvided = coreModel.ProvidedInputObjects.Contains(objectName);

                apiModel.InputObjects.Add(objectName, ioApiModel);
            }

            return(apiModel);
        }
        public async Task <ExecutionRequestContext <ExecutionRequestApiModel> > BuildExecutionRequestContextAsync(ExecutionRequestApiModel apiExecRequest)
        {
            // Create a new execution request context...

            var erContext = new ExecutionRequestContext <ExecutionRequestApiModel>(apiExecRequest);

            // Did they provide an extension ID?

            if (string.IsNullOrEmpty(apiExecRequest.ExtensionId))
            {
                erContext.ValidationErrors.Add($"[{ErrorCodes.ExtensionIdNotProvided}]: [extensionId] is required.");
            }

            // Did they provide an extension version ID?

            if (string.IsNullOrEmpty(apiExecRequest.ExtensionVersionId))
            {
                erContext.ValidationErrors.Add($"[{ErrorCodes.ExtensionVersionIdNotProvided}]: [extensionVersionId] is required.");
            }

            // Is the execution priority that they provided valid?

            if (Enum.TryParse <ExecutionPriority>(apiExecRequest.Priority, out var execPriority) == false)
            {
                erContext.ValidationErrors.Add($"[{ErrorCodes.InvalidPriority}]: [{apiExecRequest.Priority}] is not a valid [priority]; " +
                                               $"valid priorities are [{ExecutionPriority.Low}], [{ExecutionPriority.Normal}], " +
                                               $"and [{ExecutionPriority.High}]; if no [priority] is explicitly provided, " +
                                               $"[{ExecutionPriority.Normal}] is automatically selected.");
            }

            if (string.IsNullOrEmpty(apiExecRequest.ExtensionId) == false)
            {
                // Get the extension information...

                erContext.Extension = await extensionRepository.GetExtensionAsync(apiExecRequest.ExtensionId);

                // Were we able to find the extension and is it active?

                if (erContext.Extension == null)
                {
                    erContext.ValidationErrors.Add($"[{ErrorCodes.ExtensionNotFound}]: Extension [{apiExecRequest.ExtensionId}] not found.");
                }
                else if (erContext.Extension.IsActive == false)
                {
                    erContext.ValidationErrors.Add($"[{ErrorCodes.ExtensionDisabled}]: Extension [{apiExecRequest.ExtensionId}] is currently unavailable.");
                }
                else if (string.IsNullOrEmpty(apiExecRequest.ExtensionVersionId) == false)
                {
                    // Get the extension version information...

                    erContext.ExtensionVersion = erContext.Extension.GetExtensionVersion(apiExecRequest.ExtensionVersionId);

                    // Were we able to find the extension version and is it active?

                    if (erContext.ExtensionVersion == null)
                    {
                        erContext.ValidationErrors.Add($"[{ErrorCodes.ExtensionVersionNotFound}]: Extension [{apiExecRequest.ExtensionId}] version " +
                                                       $"[{apiExecRequest.ExtensionVersionId}] not found.");
                    }
                    else if (erContext.ExtensionVersion.IsActive == false)
                    {
                        erContext.ValidationErrors.Add($"[{ErrorCodes.ExtensionVersionDisabled}]: Extension [{apiExecRequest.ExtensionId}] version " +
                                                       $"[{apiExecRequest.ExtensionVersionId}] is currently unavailable.");
                    }
                    else
                    {
                        // If the user has only requested validation (not execution), does the extension version support validation?

                        if (apiExecRequest.ValidateOnly && erContext.ExtensionVersion.SupportsValidation == false)
                        {
                            erContext.ValidationErrors.Add($"[{ErrorCodes.ExtensionVersionDoesNotSupportValidation}]: " +
                                                           $"Extension [{apiExecRequest.ExtensionId}] version " +
                                                           $"[{apiExecRequest.ExtensionVersionId}] does not support validation.");
                        }

                        // Get the execution profile information...

                        erContext.ExecutionProfile = erContext.ExtensionVersion.GetExecutionProfile(apiExecRequest.ProfileName);

                        // Were we able to find the execution profile information?

                        if (erContext.ExecutionProfile == null)
                        {
                            erContext.ValidationErrors.Add($"[{ErrorCodes.ExecutionProfileNotFound}]: " +
                                                           $"Execution profile [{apiExecRequest.ProfileName}] not found.");
                        }
                        else
                        {
                            // And, finally, is the specified priority supported by the execution profile?

                            if (erContext.ExecutionProfile.SupportedPriorities.HasFlag(execPriority) == false)
                            {
                                erContext.ValidationErrors.Add($"[{ErrorCodes.PriorityNotSupported}]: " +
                                                               $"Execution profile [{apiExecRequest.ProfileName}] does not support " +
                                                               $"[{execPriority}]-priority execution.");
                            }
                        }
                    }
                }
            }

            return(erContext);
        }