// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 using System; using System.Collections.Generic; using System.Linq; using Microsoft.Extensions.DependencyInjection; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Newtonsoft.Json.Serialization; namespace AWS.Deploy.Common.Recipes.Validation { /// /// Factory that builds the validators for a given option or recipe /// public interface IValidatorFactory { /// /// Builds the validators that apply to the given option /// /// Option to validate /// Applies a filter to the list of validators /// Array of validators for the given option IOptionSettingItemValidator[] BuildValidators(OptionSettingItem optionSettingItem, Func? filter = null); /// /// Builds the validators that apply to the given recipe /// /// Recipe to validate /// Array of validators for the given recipe IRecipeValidator[] BuildValidators(RecipeDefinition recipeDefinition); } /// /// Builds and instances. /// public class ValidatorFactory : IValidatorFactory { private readonly IServiceProvider _serviceProvider; public ValidatorFactory(IServiceProvider serviceProvider) { _serviceProvider = serviceProvider; } private static readonly Dictionary _optionSettingItemValidatorTypeMapping = new() { { OptionSettingItemValidatorList.Range, typeof(RangeValidator) }, { OptionSettingItemValidatorList.Regex, typeof(RegexValidator) }, { OptionSettingItemValidatorList.Required, typeof(RequiredValidator) }, { OptionSettingItemValidatorList.DirectoryExists, typeof(DirectoryExistsValidator) }, { OptionSettingItemValidatorList.DockerBuildArgs, typeof(DockerBuildArgsValidator) }, { OptionSettingItemValidatorList.DotnetPublishArgs, typeof(DotnetPublishArgsValidator) }, { OptionSettingItemValidatorList.ExistingResource, typeof(ExistingResourceValidator) }, { OptionSettingItemValidatorList.FileExists, typeof(FileExistsValidator) }, { OptionSettingItemValidatorList.StringLength, typeof(StringLengthValidator) }, { OptionSettingItemValidatorList.InstanceType, typeof(LinuxInstanceTypeValidator) }, { OptionSettingItemValidatorList.WindowsInstanceType, typeof(WindowsInstanceTypeValidator) }, { OptionSettingItemValidatorList.SubnetsInVpc, typeof(SubnetsInVpcValidator) }, { OptionSettingItemValidatorList.SecurityGroupsInVpc, typeof(SecurityGroupsInVpcValidator) }, { OptionSettingItemValidatorList.Uri, typeof(UriValidator) }, { OptionSettingItemValidatorList.Comparison, typeof(ComparisonValidator) }, { OptionSettingItemValidatorList.VPCSubnetsInDifferentAZs, typeof(VPCSubnetsInDifferentAZsValidator) }, { OptionSettingItemValidatorList.VpcExists, typeof(VpcExistsValidator) } }; private static readonly Dictionary _recipeValidatorTypeMapping = new() { { RecipeValidatorList.FargateTaskSizeCpuMemoryLimits, typeof(FargateTaskCpuMemorySizeValidator) }, { RecipeValidatorList.ValidDockerfilePath, typeof(DockerfilePathValidator) } }; public IOptionSettingItemValidator[] BuildValidators(OptionSettingItem optionSettingItem, Func? filter = null) { return optionSettingItem.Validators .Where(validator => filter != null ? filter(validator) : true) .Select(v => Activate(v.ValidatorType, v.Configuration, _optionSettingItemValidatorTypeMapping)) .OfType() .ToArray(); } public IRecipeValidator[] BuildValidators(RecipeDefinition recipeDefinition) { return recipeDefinition.Validators .Select(v => Activate(v.ValidatorType, v.Configuration, _recipeValidatorTypeMapping)) .OfType() .ToArray(); } private object? Activate(TValidatorList validatorType, object? configuration, Dictionary typeMappings) where TValidatorList : struct { if (null == configuration) { var validatorInstance = ActivatorUtilities.CreateInstance(_serviceProvider, typeMappings[validatorType]); if (validatorInstance == null) throw new InvalidValidatorTypeException(DeployToolErrorCode.UnableToCreateValidatorInstance, $"Could not create an instance of validator type {validatorType}"); return validatorInstance; } if (configuration is JObject jObject) { var validatorInstance = JsonConvert.DeserializeObject( JsonConvert.SerializeObject(jObject), typeMappings[validatorType], new JsonSerializerSettings { ContractResolver = new ServiceContractResolver(_serviceProvider) }); if (validatorInstance == null) throw new InvalidValidatorTypeException(DeployToolErrorCode.UnableToCreateValidatorInstance, $"Could not create an instance of validator type {validatorType}"); return validatorInstance; } return configuration; } } /// /// Custom contract resolver that can inject services from an IServiceProvider /// into the constructor of the type that is being deserialized from Json /// public class ServiceContractResolver : DefaultContractResolver { private readonly IServiceProvider _serviceProvider; public ServiceContractResolver(IServiceProvider serviceProvider) { _serviceProvider = serviceProvider; } protected override JsonObjectContract CreateObjectContract(Type objectType) { var contract = base.CreateObjectContract(objectType); contract.DefaultCreator = () => ActivatorUtilities.CreateInstance(_serviceProvider, objectType); return contract; } } }