public async Task ValidateFundingTemplate_GivenTemplateWIthValidSchemaVersionButTemplateDoesNotValidateAgainstTheSchema_ReturnsValidationResultWithErrors1() { //Arrange const string schemaVersion = "1.0"; string fundingTemplate = CreateJsonFile("CalculateFunding.Services.Policy.Resources.LogicalModelTemplate.json"); string fundingSchema = CreateJsonFile("CalculateFunding.Services.Policy.Resources.LogicalModel.json"); string blobName = $"{fundingSchemaFolder}/{schemaVersion}.json"; IFundingSchemaRepository fundingSchemaRepository = CreateFundingSchemaRepository(); fundingSchemaRepository .SchemaVersionExists(Arg.Is(blobName)) .Returns(true); fundingSchemaRepository .GetFundingSchemaVersion(Arg.Is(blobName)) .Returns(fundingSchema); FundingTemplateValidationService fundingTemplateValidationService = CreateFundingTemplateValidationService(fundingSchemaRepository: fundingSchemaRepository); //Act FundingTemplateValidationResult result = await fundingTemplateValidationService.ValidateFundingTemplate(fundingTemplate, "fsid", "fpid", "1.0"); //Assert result .Errors .Should() .HaveCount(8); result .IsValid .Should() .BeFalse(); }
public async Task ValidateFundingTemplate_GivenTemplateThatDoesNotContainASchemaVersionValue_ReturnsValidationResultWithErrors() { //Arrange string fundingTemplate = "{ \"schemaVersion\" : \"\"}"; FundingTemplateValidationService fundingTemplateValidationService = CreateFundingTemplateValidationService(); //Act FundingTemplateValidationResult result = await fundingTemplateValidationService.ValidateFundingTemplate(fundingTemplate, "fsid", "fpid", "1.0"); //Assert result .Errors .Should() .HaveCount(1); result .Errors[0] .ErrorMessage .Should() .Be("Missing schema version from funding template."); result .IsValid .Should() .BeFalse(); }
public async Task <FundingTemplateValidationResult> ValidateFundingTemplate(string fundingTemplate, string fundingStreamId, string fundingPeriodId, string templateVersion = null) { Guard.IsNullOrWhiteSpace(fundingTemplate, nameof(fundingTemplate)); FundingTemplateValidationResult fundingTemplateValidationResult = new FundingTemplateValidationResult() { FundingStreamId = fundingStreamId, FundingPeriodId = fundingPeriodId, TemplateVersion = templateVersion }; JObject parsedFundingTemplate; try { parsedFundingTemplate = JObject.Parse(fundingTemplate); } catch (JsonReaderException jre) { fundingTemplateValidationResult.Errors.Add(new ValidationFailure("", jre.Message)); return(fundingTemplateValidationResult); } if (parsedFundingTemplate["schemaVersion"] == null || string.IsNullOrWhiteSpace(parsedFundingTemplate["schemaVersion"].Value <string>())) { fundingTemplateValidationResult.Errors.Add(new ValidationFailure("", "Missing schema version from funding template.")); return(fundingTemplateValidationResult); } string schemaVersion = parsedFundingTemplate["schemaVersion"].Value <string>(); fundingTemplateValidationResult.SchemaVersion = schemaVersion; string blobName = $"{fundingSchemaFolder}/{schemaVersion}.json"; bool schemaExists = await _fundingSchemaRepositoryPolicy.ExecuteAsync(() => _fundingSchemaRepository.SchemaVersionExists(blobName)); if (!schemaExists) { fundingTemplateValidationResult.Errors.Add(new ValidationFailure("", $"A valid schema could not be found for schema version '{schemaVersion}'.")); return(fundingTemplateValidationResult); } await ValidateAgainstSchema(blobName, parsedFundingTemplate, fundingTemplateValidationResult); if (!fundingTemplateValidationResult.IsValid) { return(fundingTemplateValidationResult); } await ValidateFundingStream(fundingTemplateValidationResult); await ValidateFundingPeriod(fundingTemplateValidationResult); return(fundingTemplateValidationResult); }
public async Task SaveFundingTemplate_GivenInvalidTemplateDueToProfilePeriods_BadRequest() { //Arrange string template = CreateJsonFile("CalculateFunding.Services.Policy.Resources.LogicalModelTemplate.json"); string fundingStreamId = "PES"; string templateVersion = "1.5"; string fundingPeriodId = "AY-2020"; FundingTemplateValidationResult validationResult = new FundingTemplateValidationResult { TemplateVersion = templateVersion, FundingStreamId = fundingStreamId, SchemaVersion = "1.0", }; ITemplateMetadataResolver templateMetadataResolver = CreateMetadataResolver("1.0"); IFundingTemplateValidationService fundingTemplateValidationService = CreateFundingTemplateValidationService(); fundingTemplateValidationService .ValidateFundingTemplate(Arg.Is(template), Arg.Is(fundingStreamId), Arg.Is(fundingPeriodId), Arg.Is(templateVersion)) .Returns(validationResult); IFundingTemplateRepository fundingTemplateRepository = CreateFundingTemplateRepository(); ILogger logger = CreateLogger(); ICacheProvider cacheProvider = CreateCacheProvider(); FundingTemplateService fundingTemplateService = CreateFundingTemplateService( logger, fundingTemplateValidationService: fundingTemplateValidationService, fundingTemplateRepository: fundingTemplateRepository, cacheProvider: cacheProvider, templateMetadataResolver: templateMetadataResolver); //Act IActionResult result = await fundingTemplateService.SaveFundingTemplate(createdAtActionName, createdAtControllerName, template, fundingStreamId, fundingPeriodId, templateVersion); //Assert result .Should() .BeAssignableTo <BadRequestObjectResult>(); BadRequestObjectResult badRequestObjectResult = result as BadRequestObjectResult; SerializableError validationResults = badRequestObjectResult.Value as SerializableError; validationResults .Count() .Should() .Be(1); ((string[])validationResults["DistributionPeriods"])[0] .Should() .Be("Funding line : 'Total funding line' has values for the distribution periods"); }
public async Task <IActionResult> SaveFundingTemplate(string actionName, string controllerName, string template, string fundingStreamId, string fundingPeriodId, string templateVersion) { Guard.IsNullOrWhiteSpace(actionName, nameof(actionName)); Guard.IsNullOrWhiteSpace(controllerName, nameof(controllerName)); Guard.IsNullOrWhiteSpace(fundingStreamId, nameof(fundingStreamId)); Guard.IsNullOrWhiteSpace(fundingPeriodId, nameof(fundingPeriodId)); Guard.IsNullOrWhiteSpace(templateVersion, nameof(templateVersion)); //Already checked for null above when getting body if (template.Trim() == string.Empty) { return(new BadRequestObjectResult("Null or empty funding template was provided.")); } FundingTemplateValidationResult validationResult = await _fundingTemplateValidationService.ValidateFundingTemplate(template, fundingStreamId, fundingPeriodId, templateVersion); if (!validationResult.IsValid) { return(validationResult.AsBadRequest()); } ITemplateMetadataGenerator templateMetadataGenerator = _templateMetadataResolver.GetService(validationResult.SchemaVersion); ValidationResult validationGeneratorResult = templateMetadataGenerator.Validate(template); if (!validationGeneratorResult.IsValid) { return(validationGeneratorResult.PopulateModelState()); } string blobName = GetBlobNameFor(validationResult.FundingStreamId, validationResult.FundingPeriodId, validationResult.TemplateVersion); try { byte[] templateFileBytes = Encoding.UTF8.GetBytes(template); await SaveFundingTemplateVersion(blobName, templateFileBytes); string cacheKey = $"{CacheKeys.FundingTemplatePrefix}{validationResult.FundingStreamId}-{validationResult.FundingPeriodId}-{validationResult.TemplateVersion}".ToLowerInvariant(); await _cacheProviderPolicy.ExecuteAsync(() => _cacheProvider.RemoveAsync <string>(cacheKey)); await _cacheProviderPolicy.ExecuteAsync(() => _cacheProvider.RemoveAsync <FundingTemplateContents>($"{CacheKeys.FundingTemplateContents}{validationResult.FundingStreamId}:{validationResult.FundingPeriodId}:{validationResult.TemplateVersion}".ToLowerInvariant())); await _cacheProviderPolicy.ExecuteAsync(() => _cacheProvider.RemoveAsync <TemplateMetadataContents>($"{CacheKeys.FundingTemplateContentMetadata}{validationResult.FundingStreamId}:{validationResult.FundingPeriodId}:{validationResult.TemplateVersion}".ToLowerInvariant())); await _cacheProviderPolicy.ExecuteAsync(() => _cacheProvider.RemoveAsync <TemplateMetadataDistinctContents>($"{CacheKeys.FundingTemplateContentMetadataDistinct}{validationResult.FundingStreamId}:{validationResult.FundingPeriodId}:{validationResult.TemplateVersion}".ToLowerInvariant())); return(new CreatedAtActionResult(actionName, controllerName, new { fundingStreamId = validationResult.FundingStreamId, fundingPeriodId = validationResult.FundingPeriodId, templateVersion = validationResult.TemplateVersion }, string.Empty)); } catch (Exception ex) { _logger.Error(ex, $"Failed to save funding template '{blobName}' to blob storage"); return(new InternalServerErrorResult("Error occurred uploading funding template")); } }
public async Task ValidateFundingTemplate_GivenTemplateWithValidFundingPeriodIdButNoFundingPeriodExists_ReturnsValidationResultWithErrors() { //Arrange const string schemaVersion = "1.0"; FundingStream fundingStream = new FundingStream(); JsonSchema schema = JsonSchema.FromType <TestTemplate_schema_1_0>(); TestTemplate_schema_1_0 testClassWithFunding = new TestTemplate_schema_1_0 { SchemaVersion = schemaVersion, Funding = new { templateVersion = "2.1", fundingStream = new { code = "PES" }, fundingPeriod = new { id = "AY-2020" } } }; string fundingTemplate = JsonConvert.SerializeObject(testClassWithFunding); string fundingSchema = schema.AsJson(); string blobName = $"{fundingSchemaFolder}/{schemaVersion}.json"; IFundingSchemaRepository fundingSchemaRepository = CreateFundingSchemaRepository(); fundingSchemaRepository .SchemaVersionExists(Arg.Is(blobName)) .Returns(true); fundingSchemaRepository .GetFundingSchemaVersion(Arg.Is(blobName)) .Returns(fundingSchema); IPolicyRepository policyRepository = CreatePolicyRepository(); policyRepository .GetFundingStreamById(Arg.Is("PES")) .Returns(fundingStream); FundingTemplateValidationService fundingTemplateValidationService = CreateFundingTemplateValidationService( fundingSchemaRepository: fundingSchemaRepository, policyRepository: policyRepository); //Act FundingTemplateValidationResult result = await fundingTemplateValidationService.ValidateFundingTemplate(fundingTemplate, "PES", "AY-2020", "2.1"); //Assert result .IsValid .Should() .BeFalse(); result .Errors[0] .ErrorMessage .Should() .Be("A funding period could not be found for funding period id 'AY-2020'"); }
private async Task ValidateFundingPeriod(FundingTemplateValidationResult fundingTemplateValidationResult) { if (!string.IsNullOrWhiteSpace(fundingTemplateValidationResult.FundingPeriodId)) { FundingPeriod fundingPeriod = await _policyRepositoryPolicy.ExecuteAsync(() => _policyRepository.GetFundingPeriodById(fundingTemplateValidationResult.FundingPeriodId)); if (fundingPeriod == null) { fundingTemplateValidationResult.Errors .Add(new ValidationFailure("", $"A funding period could not be found for funding period id '{fundingTemplateValidationResult.FundingPeriodId}'")); } } }
private async Task ValidateFundingStream(FundingTemplateValidationResult fundingTemplateValidationResult) { if (!fundingTemplateValidationResult.FundingStreamId.IsNullOrEmpty()) { FundingStream fundingStream = await _policyRepositoryPolicy.ExecuteAsync(() => _policyRepository.GetFundingStreamById(fundingTemplateValidationResult.FundingStreamId)); if (fundingStream == null) { fundingTemplateValidationResult.Errors .Add(new ValidationFailure("", $"A funding stream could not be found for funding stream id '{fundingTemplateValidationResult.FundingStreamId}'")); } } }
public async Task SaveFundingTemplate_GivenTemplateButDidNotValidate_ReturnsBadRequest() { //Arrange const string template = "a template"; string fundingStreamId = NewRandomString(); string templateVersion = NewRandomString(); string fundingPeriodId = NewRandomString(); FundingTemplateValidationResult validationResult = new FundingTemplateValidationResult(); validationResult.Errors.Add(new ValidationFailure("prop1", "an error")); validationResult.Errors.Add(new ValidationFailure("prop2", "another error")); IFundingTemplateValidationService fundingTemplateValidationService = CreateFundingTemplateValidationService(); fundingTemplateValidationService .ValidateFundingTemplate(Arg.Is(template), Arg.Is(fundingStreamId), Arg.Is(fundingPeriodId), Arg.Is(templateVersion)) .Returns(validationResult); FundingTemplateService fundingTemplateService = CreateFundingTemplateService(fundingTemplateValidationService: fundingTemplateValidationService); //Act IActionResult result = await fundingTemplateService.SaveFundingTemplate(createdAtActionName, createdAtControllerName, template, fundingStreamId, fundingPeriodId, templateVersion); //Assert result .Should() .BeAssignableTo <BadRequestObjectResult>(); BadRequestObjectResult badRequestObjectResult = result as BadRequestObjectResult; badRequestObjectResult .Value .Should() .BeOfType <SerializableError>(); SerializableError modelState = badRequestObjectResult.Value as SerializableError; modelState .Should() .NotBeNull(); modelState .Values .Should() .HaveCount(2); }
private async Task ValidateAgainstSchema(string blobName, JObject parsedFundingTemplate, FundingTemplateValidationResult fundingTemplateValidationResult) { string fundingSchemaJson = await _fundingSchemaRepositoryPolicy.ExecuteAsync(() => _fundingSchemaRepository.GetFundingSchemaVersion(blobName)); JsonSchema fundingSchema = await JsonSchema.FromJsonAsync(fundingSchemaJson); ICollection <ValidationError> validationMessages = fundingSchema.Validate(parsedFundingTemplate); if (validationMessages.AnyWithNullCheck()) { foreach (ValidationError message in validationMessages) { fundingTemplateValidationResult.Errors.Add(new ValidationFailure(message.Property, message.ToString())); } } }
public async Task ValidateFundingTemplate_GivenTemplateWithInvalidJson_ReturnsValidationResultWithErrors() { //Arrange string fundingTemplate = "invalid json schema"; FundingTemplateValidationService fundingTemplateValidationService = CreateFundingTemplateValidationService(); //Act FundingTemplateValidationResult result = await fundingTemplateValidationService.ValidateFundingTemplate(fundingTemplate, "fsid", "fpid", "1.0"); //Assert result .Errors .Should() .HaveCount(1); result .IsValid .Should() .BeFalse(); }
public async Task ValidateFundingTemplate_GivenTemplateWIthValidSchemaVersionButSchemaDoesNotExist_ReturnsValidationResultWithErrors() { //Arrange const string schemaVersion = "1.0"; string fundingTemplate = $"{{ \"schemaVersion\" : \"{schemaVersion}\"}}"; string blobName = $"{fundingSchemaFolder}/{schemaVersion}.json"; IFundingSchemaRepository fundingSchemaRepository = CreateFundingSchemaRepository(); fundingSchemaRepository .SchemaVersionExists(Arg.Is(blobName)) .Returns(false); FundingTemplateValidationService fundingTemplateValidationService = CreateFundingTemplateValidationService(fundingSchemaRepository: fundingSchemaRepository); //Act FundingTemplateValidationResult result = await fundingTemplateValidationService.ValidateFundingTemplate(fundingTemplate, "fsid", "fpid", "1.0"); //Assert result .Errors .Should() .HaveCount(1); result .Errors[0] .ErrorMessage .Should() .Be($"A valid schema could not be found for schema version '{schemaVersion}'."); result .IsValid .Should() .BeFalse(); }
public async Task ValidateFundingTemplate_Schema_1_1_GivenTemplateIsValidAndValuesExtracted_ReturnsValidationResultWithNoErrors() { //Arrange const string schemaVersion = "1.1"; FundingStream fundingStream = new FundingStream(); JsonSchema schema = JsonSchema.FromType <TestTemplate_schema_1_1>(); var template = new TestTemplate_schema_1_1 { SchemaVersion = schemaVersion, FundingStreamTemplate = new { templateVersion = "56.4", fundingStream = new { code = "XXX" }, fundingPeriod = new { id = "AY-2020" } } }; string fundingTemplate = JsonConvert.SerializeObject(template); string fundingSchema = schema.AsJson(); string blobName = $"{fundingSchemaFolder}/{schemaVersion}.json"; IFundingSchemaRepository fundingSchemaRepository = CreateFundingSchemaRepository(); fundingSchemaRepository .SchemaVersionExists(Arg.Is(blobName)) .Returns(true); fundingSchemaRepository .GetFundingSchemaVersion(Arg.Is(blobName)) .Returns(fundingSchema); IPolicyRepository policyRepository = CreatePolicyRepository(); policyRepository .GetFundingStreamById(Arg.Is("XXX")) .Returns(fundingStream); policyRepository .GetFundingPeriodById(Arg.Is("AY-2020")) .Returns(new FundingPeriod()); FundingTemplateValidationService fundingTemplateValidationService = CreateFundingTemplateValidationService( fundingSchemaRepository: fundingSchemaRepository, policyRepository: policyRepository); //Act FundingTemplateValidationResult result = await fundingTemplateValidationService.ValidateFundingTemplate(fundingTemplate, "XXX", "AY-2020", "56.4"); //Assert result .Errors .Should() .BeEmpty(); result .TemplateVersion .Should() .Be("56.4"); result .SchemaVersion .Should() .Be("1.1"); result .FundingStreamId .Should() .Be("XXX"); result .FundingPeriodId .Should() .Be("AY-2020"); result .IsValid .Should() .BeTrue(); }
public async Task SaveFundingTemplate_GivenValidTemplateAndSaves_InvalidatesCacheReturnsCreatedAtActionResult() { //Arrange string template = CreateJsonFile("CalculateFunding.Services.Policy.Resources.LogicalModelTemplateNoProfilePeriods.json"); string fundingStreamId = "PES"; string templateVersion = "1.5"; string fundingPeriodId = "AY-2020"; FundingTemplateValidationResult validationResult = new FundingTemplateValidationResult { TemplateVersion = templateVersion, FundingStreamId = fundingStreamId, SchemaVersion = "1.0", FundingPeriodId = fundingPeriodId }; string cacheKey = $"{CacheKeys.FundingTemplatePrefix}{validationResult.FundingStreamId}-{validationResult.FundingPeriodId}-{validationResult.TemplateVersion}".ToLowerInvariant(); ITemplateMetadataResolver templateMetadataResolver = CreateMetadataResolver("1.0"); IFundingTemplateValidationService fundingTemplateValidationService = CreateFundingTemplateValidationService(); fundingTemplateValidationService .ValidateFundingTemplate(Arg.Is(template), Arg.Is(fundingStreamId), Arg.Is(fundingPeriodId), Arg.Is(templateVersion)) .Returns(validationResult); IFundingTemplateRepository fundingTemplateRepository = CreateFundingTemplateRepository(); ILogger logger = CreateLogger(); ICacheProvider cacheProvider = CreateCacheProvider(); FundingTemplateService fundingTemplateService = CreateFundingTemplateService( logger, fundingTemplateValidationService: fundingTemplateValidationService, fundingTemplateRepository: fundingTemplateRepository, cacheProvider: cacheProvider, templateMetadataResolver: templateMetadataResolver); //Act IActionResult result = await fundingTemplateService.SaveFundingTemplate(createdAtActionName, createdAtControllerName, template, fundingStreamId, fundingPeriodId, templateVersion); //Assert result .Should() .BeAssignableTo <CreatedAtActionResult>(); CreatedAtActionResult actionResult = result as CreatedAtActionResult; actionResult .ActionName .Should() .Be(createdAtActionName); actionResult .ControllerName .Should() .Be(createdAtControllerName); actionResult .RouteValues["fundingStreamId"].ToString() .Should() .Be("PES"); actionResult .RouteValues["fundingPeriodId"].ToString() .Should() .Be("AY-2020"); actionResult .RouteValues["templateVersion"].ToString() .Should() .Be("1.5"); await cacheProvider .Received(1) .RemoveAsync <string>(Arg.Is(cacheKey)); await cacheProvider .Received(1) .RemoveAsync <FundingTemplateContents>(Arg.Is($"{CacheKeys.FundingTemplateContents}pes:ay-2020:1.5")); await cacheProvider .Received(1) .RemoveAsync <TemplateMetadataContents>($"{CacheKeys.FundingTemplateContentMetadata}pes:ay-2020:1.5"); await cacheProvider .Received(1) .RemoveAsync <TemplateMetadataDistinctContents>($"{CacheKeys.FundingTemplateContentMetadataDistinct}pes:ay-2020:1.5"); }
public async Task SaveFundingTemplate_GivenValidTemplateButFailedToSaveToBlobStorage_ReturnsInternalServerError() { //Arrange const string template = "a template"; string fundingStreamId = NewRandomString(); string templateVersion = NewRandomString(); string fundingPeriodId = NewRandomString(); FundingTemplateValidationResult validationResult = new FundingTemplateValidationResult { TemplateVersion = "1.8", FundingStreamId = "PES", SchemaVersion = "1.0", FundingPeriodId = "AY-2020" }; string blobName = $"{validationResult.FundingStreamId}/{validationResult.FundingPeriodId}/{validationResult.TemplateVersion}.json"; IFundingTemplateValidationService fundingTemplateValidationService = CreateFundingTemplateValidationService(); fundingTemplateValidationService .ValidateFundingTemplate(Arg.Is(template), Arg.Is(fundingStreamId), Arg.Is(fundingPeriodId), Arg.Is(templateVersion)) .Returns(validationResult); IFundingTemplateRepository fundingTemplateRepository = CreateFundingTemplateRepository(); fundingTemplateRepository .When(x => x.SaveFundingTemplateVersion(Arg.Is(blobName), Arg.Any <byte[]>())) .Do(x => { throw new Exception(); }); ILogger logger = CreateLogger(); ITemplateMetadataGenerator templateMetadataGenerator = CreateMetadataGenerator(); templateMetadataGenerator.Validate(Arg.Is <string>(template)) .Returns(new FluentValidation.Results.ValidationResult()); ITemplateMetadataResolver templateMetadataResolver = CreateMetadataResolver("1.0", templateMetadataGenerator); FundingTemplateService fundingTemplateService = CreateFundingTemplateService( logger, fundingTemplateValidationService: fundingTemplateValidationService, fundingTemplateRepository: fundingTemplateRepository, templateMetadataResolver: templateMetadataResolver); //Act IActionResult result = await fundingTemplateService.SaveFundingTemplate(createdAtActionName, createdAtControllerName, template, fundingStreamId, fundingPeriodId, templateVersion); //Assert result .Should() .BeAssignableTo <InternalServerErrorResult>() .Which .Value .Should() .Be("Error occurred uploading funding template"); logger .Received(1) .Error(Arg.Any <NonRetriableException>(), Arg.Is($"Failed to save funding template '{blobName}' to blob storage")); }