public async Task GetCalculationMetadataForSpecification_GivenResponseIsSuccess_ReturnsCalculationMetadataValue() { //Arrange IEnumerable <CalculationMetadata> expectedCalculationMetadata = new List <CalculationMetadata>() { new CalculationMetadata() }; ApiResponse <IEnumerable <CalculationMetadata> > apiResponse = new ApiResponse <IEnumerable <CalculationMetadata> >(HttpStatusCode.OK, expectedCalculationMetadata); ICalculationsApiClient calculationsApiClient = CreateCalculationsApiClient(); calculationsApiClient .GetCalculationMetadataForSpecification(Arg.Is(specificationId)) .Returns(apiResponse); ILogger logger = CreateLogger(); CalculationsService calculationsService = CreateCalculationsService(calculationsApiClient, logger); //Act IEnumerable <CalculationMetadata> responseValue = await calculationsService.GetCalculationMetadataForSpecification(specificationId); //Assert responseValue .Should() .BeEquivalentTo(expectedCalculationMetadata); }
public async Task GetCalculationMetadataForSpecification_GivenFailedResponseFromCalculationsApi_ThrowsRetriableException() { //Arrange ApiResponse <IEnumerable <CalculationMetadata> > apiResponse = new ApiResponse <IEnumerable <CalculationMetadata> >(HttpStatusCode.NotFound); ICalculationsApiClient calculationsApiClient = CreateCalculationsApiClient(); calculationsApiClient .GetCalculationMetadataForSpecification(Arg.Is(specificationId)) .Returns(apiResponse); ILogger logger = CreateLogger(); string errorMessage = $"Failed to retrieve calculation metadata for specification id '{specificationId}'" + $" with status code '{apiResponse.StatusCode}'"; CalculationsService calculationsService = CreateCalculationsService(calculationsApiClient, logger); //Act Func <Task> test = async() => await calculationsService.GetCalculationMetadataForSpecification(specificationId); //Assert test .Should() .ThrowExactly <RetriableException>() .Which .Message .Should() .Be(errorMessage); logger .Received(1) .Error(Arg.Is(errorMessage)); }
public async Task <IEnumerable <CalculationMetadata> > GetCalculationMetadataForSpecification(string specificationId) { Guard.IsNullOrWhiteSpace(specificationId, nameof(specificationId)); ApiResponse <IEnumerable <CalculationMetadata> > apiResponse = await _calcsApiClientPolicy.ExecuteAsync( () => _calculationsApiClient.GetCalculationMetadataForSpecification(specificationId)); if (!apiResponse.StatusCode.IsSuccess()) { string errorMessage = $"Failed to retrieve calculation metadata for specification id '{specificationId}'" + $" with status code '{apiResponse.StatusCode}'"; _logger.Error(errorMessage); throw new RetriableException(errorMessage); } return(apiResponse.Content); }
public async Task <IEnumerable <string> > VerifyCalculationPrerequisites(SpecificationSummary specification) { List <string> validationErrors = new List <string>(); string specificationId = specification.Id; ApiResponse <IEnumerable <CalculationMetadata> > calculationsResponse = await _policy.ExecuteAsync(() => _calcsApiClient.GetCalculationMetadataForSpecification(specificationId)); if (calculationsResponse?.Content == null) { string errorMessage = $"Did locate any calculation metadata for specification {specificationId}. Unable to complete prerequisite checks"; _logger.Error(errorMessage); validationErrors.Add(errorMessage); return(validationErrors); } validationErrors.AddRange(calculationsResponse?.Content.Where(_ => _.PublishStatus != PublishStatus.Approved && _.CalculationType == CalculationType.Template) .Select(_ => $"Calculation {_.Name} must be approved but is {_.PublishStatus}")); foreach (var fundingStream in specification.FundingStreams) { ApiResponse <TemplateMapping> templateMappingResponse = await _calcsApiClient.GetTemplateMapping(specificationId, fundingStream.Id); foreach (TemplateMappingItem calcInError in templateMappingResponse.Content.TemplateMappingItems.Where(c => string.IsNullOrWhiteSpace(c.CalculationId))) { validationErrors.Add($"{calcInError.EntityType} {calcInError.Name} is not mapped to a calculation in CFS"); } } return(validationErrors); }
private void GivenTheCalculationsForTheSpecificationId(string specificationId, params CalculationMetadata[] calculations) { _calculationsApiClient.GetCalculationMetadataForSpecification(specificationId) .Returns(new ApiResponse <IEnumerable <CalculationMetadata> >(HttpStatusCode.OK, calculations)); }
private void ValidScenarioSetup(string fundingStreamId) { SpecificationSummary specificationSummary = new SpecificationSummary { Id = SpecificationId, TemplateIds = new Dictionary <string, string> { [fundingStreamId] = TemplateVersion } }; TemplateMetadataContents templateMetadataContents = new TemplateMetadataContents { RootFundingLines = new List <FundingLine> { new FundingLine { TemplateLineId = 123, Name = "FundingLine-1" }, new FundingLine { TemplateLineId = 234, Name = "FundingLine-2-withFundingLines", FundingLines = new List <FundingLine> { new FundingLine { TemplateLineId = 345, Name = "FundingLine-2-fl-1" }, new FundingLine { TemplateLineId = 456, Name = "FundingLine-2-fl-2", FundingLines = new List <FundingLine> { new FundingLine { TemplateLineId = 890, Name = "FundingLine-2-fl-2-fl-1" } } } } }, new FundingLine { TemplateLineId = 567, Name = "FundingLine-3-withCalculationsAndFundingLines", FundingLines = new List <FundingLine> { new FundingLine { TemplateLineId = 678, Name = "FundingLine-3-fl-1" } }, Calculations = new List <Calculation> { new Calculation { Name = "FundingLine-3-calc-1", TemplateCalculationId = 7, Type = Common.TemplateMetadata.Enums.CalculationType.Cash }, new Calculation { Name = "FundingLine-3-calc-2", TemplateCalculationId = 11, Calculations = new List <Calculation> { new Calculation { Name = "FundingLine-3-calc-2-calc-1", TemplateCalculationId = 8, Type = Common.TemplateMetadata.Enums.CalculationType.Cash } } }, new Calculation { Name = "FundingLine-3-calc-3", TemplateCalculationId = 10, Type = Common.TemplateMetadata.Enums.CalculationType.Cash } } }, new FundingLine { TemplateLineId = 789, Name = "FundingLine-4" }, } }; _specificationsService.GetSpecificationSummaryById(SpecificationId) .Returns(new OkObjectResult(specificationSummary)); _policiesApiClient.GetFundingTemplateContents(FundingStreamId, FundingPeriodId, TemplateVersion) .Returns(new ApiResponse <TemplateMetadataContents>(HttpStatusCode.OK, templateMetadataContents)); _calculationsApiClient.GetTemplateMapping(SpecificationId, FundingStreamId) .Returns(new ApiResponse <TemplateMapping>(HttpStatusCode.OK, new TemplateMapping { FundingStreamId = FundingStreamId, SpecificationId = SpecificationId, TemplateMappingItems = new List <TemplateMappingItem> { new TemplateMappingItem { TemplateId = 7, CalculationId = aValidCalculationId1 }, new TemplateMappingItem { TemplateId = 11, CalculationId = aValidCalculationId2 }, new TemplateMappingItem { TemplateId = 8, CalculationId = "CalculationIdForTemplateCalculationId2" }, new TemplateMappingItem { TemplateId = 10, CalculationId = aValidCalculationId3 } } })); _calculationsApiClient.GetCalculationMetadataForSpecification(SpecificationId) .Returns(new ApiResponse <IEnumerable <CalculationMetadata> >(HttpStatusCode.OK, new List <CalculationMetadata> { new CalculationMetadata { SpecificationId = SpecificationId, CalculationId = aValidCalculationId1, PublishStatus = CalculationExpectedPublishStatus }, new CalculationMetadata { SpecificationId = SpecificationId, CalculationId = aValidCalculationId2, PublishStatus = CalculationExpectedPublishStatus }, new CalculationMetadata { SpecificationId = SpecificationId, CalculationId = aValidCalculationId3, PublishStatus = CalculationExpectedPublishStatus } })); _graphApiClient.GetCircularDependencies(SpecificationId) .Returns(new ApiResponse <IEnumerable <Entity <Common.ApiClient.Graph.Models.Calculation> > >( HttpStatusCode.OK, new List <Entity <Common.ApiClient.Graph.Models.Calculation> >() { new Entity <Common.ApiClient.Graph.Models.Calculation>() { Node = new Common.ApiClient.Graph.Models.Calculation() { SpecificationId = SpecificationId, CalculationId = "CalculationIdForTemplateCalculationId2" } } })); }
public async Task <IActionResult> GetProviderResults(string providerId, string specificationId) { if (string.IsNullOrWhiteSpace(providerId)) { _logger.Error("No provider Id was provided to GetProviderResults"); return(new BadRequestObjectResult("Null or empty provider Id provided")); } if (string.IsNullOrWhiteSpace(specificationId)) { _logger.Error("No specification Id was provided to GetProviderResults"); return(new BadRequestObjectResult("Null or empty specification Id provided")); } ProviderResult providerResult = await _resultsRepositoryPolicy.ExecuteAsync(() => _resultsRepository.GetProviderResult(providerId, specificationId)); if (providerResult != null) { _logger.Information($"A result was found for provider id {providerId}, specification id {specificationId}"); ApiModels.ApiResponse <IEnumerable <CalcModels.CalculationMetadata> > calculationsResponse = await _calculationsApiClientPolicy.ExecuteAsync(() => _calculationsApiClient.GetCalculationMetadataForSpecification(specificationId)); if (calculationsResponse?.Content == null) { string error = $"Did not locate any calculation metadata for specification {specificationId}."; _logger.Error(error); return(new BadRequestObjectResult(error)); } IDictionary <string, CalcModels.CalculationMetadata> calculations = calculationsResponse.Content.ToDictionary(_ => _.CalculationId); ProviderResultResponse providerResultResponse = _mapper.Map <ProviderResultResponse>(providerResult); foreach (CalculationResultResponse calculationResult in providerResultResponse.CalculationResults) { calculationResult.CalculationValueType = _mapper.Map <CalculationValueType>(calculations[calculationResult.Calculation.Id].ValueType); } return(new OkObjectResult(providerResultResponse)); } _logger.Information($"A result was not found for provider id {providerId}, specification id {specificationId}"); return(new NotFoundResult()); }
public async Task <IActionResult> GetFundingStructureResultsForProviderAndSpecification( [FromRoute] string providerId, [FromRoute] string specificationId) { Guard.ArgumentNotNull(providerId, nameof(providerId)); Guard.ArgumentNotNull(specificationId, nameof(specificationId)); ApiResponse <SpecificationSummary> specificationResult = await _specsClient.GetSpecificationSummaryById(specificationId); IActionResult specificationErrorResult = specificationResult.IsSuccessOrReturnFailureResult("GetFundingStructureResultsForProviderAndSpecification"); if (specificationErrorResult != null) { return(specificationErrorResult); } SpecificationSummary specification = specificationResult.Content; // NOTE: This API is designed with the assumption that there is only a single funding stream per specification. // The return type will need to change to be funding stream specific if more than one is ever used Reference fundingStream = specification.FundingStreams.First(); string fundingStreamTemplateVersion = specification.TemplateIds[fundingStream.Id]; Task <ApiResponse <TemplateMetadataDistinctContents> > distictTemplateContentsRequest = _policiesClient.GetDistinctTemplateMetadataContents(fundingStream.Id, specification.FundingPeriod.Id, fundingStreamTemplateVersion); Task <ApiResponse <TemplateMapping> > templateMappingRequest = _calculationsClient.GetTemplateMapping(specificationId, fundingStream.Id); Task <ApiResponse <Common.ApiClient.Results.Models.ProviderResultResponse> > calculationEngineResultsRequest = _resultsClient.GetProviderResults(providerId, specificationId); Task <ApiResponse <IEnumerable <CalculationMetadata> > > calculationMetadataRequest = _calculationsClient.GetCalculationMetadataForSpecification(specificationId); await TaskHelper.WhenAllAndThrow(distictTemplateContentsRequest, templateMappingRequest, calculationEngineResultsRequest, calculationMetadataRequest); IActionResult distictTemplateContentsErrorResult = distictTemplateContentsRequest.Result.IsSuccessOrReturnFailureResult(nameof(TemplateMetadataDistinctContents)); IActionResult templateMappingErrorResult = templateMappingRequest.Result.IsSuccessOrReturnFailureResult(nameof(TemplateMapping)); IActionResult calculationEngineResultsErrorResult = calculationEngineResultsRequest.Result.IsSuccessOrReturnFailureResult(nameof(Common.ApiClient.Results.Models.ProviderResultResponse)); IActionResult calculationMetadataErrorResult = calculationMetadataRequest.Result.IsSuccessOrReturnFailureResult(nameof(CalculationMetadata)); if (distictTemplateContentsErrorResult != null) { return(distictTemplateContentsErrorResult); } if (templateMappingErrorResult != null) { return(templateMappingErrorResult); } if (calculationEngineResultsErrorResult != null) { return(calculationEngineResultsErrorResult); } if (calculationMetadataErrorResult != null) { return(calculationMetadataErrorResult); } IEnumerable <TemplateMetadataCalculation> templateCalculations = distictTemplateContentsRequest.Result.Content.Calculations; IEnumerable <TemplateMetadataFundingLine> templateFundingLines = distictTemplateContentsRequest.Result.Content.FundingLines; IEnumerable <TemplateMappingItem> templateMapping = templateMappingRequest.Result.Content.TemplateMappingItems; Dictionary <uint, TemplateCalculationResult> calculationResults = GenerateCalculationResults( templateMapping, templateCalculations, calculationEngineResultsRequest.Result.Content.CalculationResults, calculationMetadataRequest.Result.Content ); Dictionary <uint, FundingLineResult> fundingLineResults = GenerateFundingLineResults(templateFundingLines, calculationEngineResultsRequest.Result.Content.FundingLineResults); return(Ok(new ProviderResultForSpecification() { SpecificationId = specificationId, SpecificationName = specification.Name, FundingStreamName = fundingStream.Name, FundingStreamId = fundingStream.Id, CalculationResults = calculationResults, FundingLineResults = fundingLineResults, })); }
public async Task <IActionResult> GetFundingStructure(string fundingStreamId, string fundingPeriodId, string specificationId) { IActionResult specificationSummaryResult = await _specificationsService.GetSpecificationSummaryById(specificationId); if (!(specificationSummaryResult is OkObjectResult)) { return(specificationSummaryResult); } SpecificationSummary specificationSummary = (specificationSummaryResult as OkObjectResult).Value as SpecificationSummary; string templateVersion = specificationSummary.TemplateIds.ContainsKey(fundingStreamId) ? specificationSummary.TemplateIds[fundingStreamId] : null; if (templateVersion == null) { return(new InternalServerErrorResult( $"Specification contains no matching template version for funding stream '{fundingStreamId}'")); } ApiResponse <TemplateMetadataContents> templateMetadataContentsApiResponse = await _policiesResilience.ExecuteAsync(() => _policiesApiClient.GetFundingTemplateContents(fundingStreamId, fundingPeriodId, templateVersion)); IActionResult templateMetadataContentsApiResponseErrorResult = templateMetadataContentsApiResponse.IsSuccessOrReturnFailureResult("GetFundingTemplateContents"); if (templateMetadataContentsApiResponseErrorResult != null) { return(templateMetadataContentsApiResponseErrorResult); } ApiResponse <TemplateMapping> templateMappingResponse = await _calculationsResilience.ExecuteAsync(() => _calculationsApiClient.GetTemplateMapping(specificationId, fundingStreamId)); IActionResult templateMappingResponseErrorResult = templateMappingResponse.IsSuccessOrReturnFailureResult("GetTemplateMapping"); if (templateMappingResponseErrorResult != null) { return(templateMappingResponseErrorResult); } ApiResponse <IEnumerable <CalculationMetadata> > calculationMetadata = await _calculationsResilience.ExecuteAsync(() => _calculationsApiClient.GetCalculationMetadataForSpecification(specificationId)); IActionResult calculationMetadataErrorResult = calculationMetadata.IsSuccessOrReturnFailureResult("calculationMetadata"); if (calculationMetadataErrorResult != null) { return(calculationMetadataErrorResult); } List <string> calculationIdsWithError = new List <string>(); ApiResponse <IEnumerable <Entity <Calculation> > > getCircularDependenciesApiResponse = await _graphApiClient.GetCircularDependencies(specificationId); IActionResult circularDependenciesApiErrorResult = getCircularDependenciesApiResponse.IsSuccessOrReturnFailureResult("GetCircularDependencies"); if (circularDependenciesApiErrorResult == null) { calculationIdsWithError = getCircularDependenciesApiResponse.Content.Select(calcs => calcs.Node.CalculationId).ToList(); } List <FundingStructureItem> fundingStructures = new List <FundingStructureItem>(); RecursivelyAddFundingLineToFundingStructure( fundingStructures, templateMetadataContentsApiResponse.Content.RootFundingLines, templateMappingResponse.Content.TemplateMappingItems.ToList(), calculationMetadata.Content.ToList(), calculationIdsWithError); FundingStructure fundingStructure = new FundingStructure { Items = fundingStructures, LastModified = await GetFundingStructureTimeStamp(fundingStreamId, fundingPeriodId, specificationId) }; return(new OkObjectResult(fundingStructure)); }