Пример #1
0
        public async Task CreateAdditionalCalculation_GivenCreateJobReturnsNull_ReturnsInternalServerError()
        {
            //Arrange
            CalculationCreateModel model = CreateCalculationCreateModel();

            Reference author = CreateAuthor();

            ICalculationsRepository calculationsRepository = CreateCalculationsRepository();

            calculationsRepository
            .CreateDraftCalculation(Arg.Any <Calculation>())
            .Returns(HttpStatusCode.OK);

            IVersionRepository <CalculationVersion> versionRepository = CreateCalculationVersionRepository();

            ISearchRepository <CalculationIndex> searchRepository = CreateSearchRepository();

            IJobManagement jobManagement = CreateJobManagement();

            jobManagement
            .QueueJob(Arg.Any <JobCreateModel>())
            .Returns((Job)null);

            ISpecificationsApiClient specificationsApiClient = CreateSpecificationsApiClient();

            specificationsApiClient
            .GetSpecificationSummaryById(Arg.Is(SpecificationId))
            .Returns(new ApiResponse <SpecificationSummary>(
                         HttpStatusCode.OK,
                         new SpecificationSummary {
                Id = SpecificationId
            }
                         ));

            ILogger logger = CreateLogger();

            CalculationService calculationService = CreateCalculationService(
                calculationsRepository: calculationsRepository,
                calculationVersionRepository: versionRepository,
                searchRepository: searchRepository,
                jobManagement: jobManagement,
                logger: logger,
                specificationsApiClient: specificationsApiClient);

            //Act
            IActionResult result = await calculationService.CreateAdditionalCalculation(SpecificationId, model, author, CorrelationId);

            //Assert
            result
            .Should()
            .BeOfType <InternalServerErrorResult>()
            .Which
            .Value
            .Should()
            .Be($"Failed to create job of type '{JobConstants.DefinitionNames.CreateInstructAllocationJob}' on specification '{SpecificationId}'");

            logger
            .Received(1)
            .Error(Arg.Is($"Failed to create job of type '{JobConstants.DefinitionNames.CreateInstructAllocationJob}' on specification '{SpecificationId}'"));
        }
Пример #2
0
        public async Task CreateAdditionalCalculation_GivenValidationFails_ReturnsBadRequest()
        {
            //Arrange
            string correlationId = "any-id";

            CalculationCreateModel model = new CalculationCreateModel();
            Reference author             = new Reference();

            ValidationResult validationResult = new ValidationResult(new[] {
                new ValidationFailure("prop1", "oh no an error!!!")
            });

            IValidator <CalculationCreateModel> validator = CreateCalculationCreateModelValidator(validationResult);

            ISpecificationsApiClient specificationsApiClient = CreateSpecificationsApiClient();

            specificationsApiClient
            .GetSpecificationSummaryById(Arg.Is(SpecificationId))
            .Returns(new ApiResponse <SpecificationSummary>(
                         HttpStatusCode.OK,
                         new SpecificationSummary {
                Id = SpecificationId
            }
                         ));

            CalculationService calculationService = CreateCalculationService(calculationCreateModelValidator: validator, specificationsApiClient: specificationsApiClient);

            //Act
            IActionResult result = await calculationService.CreateAdditionalCalculation(SpecificationId, model, author, correlationId);

            //Assert
            result
            .Should()
            .BeAssignableTo <BadRequestObjectResult>();
        }
Пример #3
0
        public async Task IsCalculationNameValid_WhenCalculationDoesNotExist_ThenReturnsOkResult()
        {
            // Arrange
            string specificationId = "spec1";

            ISpecificationsApiClient specificationsApiClient = CreateSpecificationsApiClient();

            specificationsApiClient
            .GetSpecificationSummaryById(Arg.Is(specificationId))
            .Returns(new Common.ApiClient.Models.ApiResponse <SpecModel.SpecificationSummary>(HttpStatusCode.OK, new SpecModel.SpecificationSummary {
                Id = specificationId
            }));

            ICalculationsRepository calculationsRepository = CreateCalculationsRepository();

            calculationsRepository
            .GetCalculationsBySpecificationId(Arg.Is(specificationId))
            .Returns(new List <Calculation>());

            CalculationService service = CreateCalculationService(specificationsApiClient: specificationsApiClient, calculationsRepository: calculationsRepository);

            // Act
            IActionResult result = await service.IsCalculationNameValid(specificationId, "calc1", null);

            // Assert
            result
            .Should()
            .BeOfType <OkResult>();
        }
Пример #4
0
        public async Task ValidateAsync_WhenSpecificationCanNotBeFound_ValidIsFalse()
        {
            //Arrange
            CalculationCreateModel model = CreateModel();

            ISpecificationsApiClient specificationsApiClient = CreateSpecificationsApiClient();

            specificationsApiClient
            .GetSpecificationSummaryById(Arg.Is(model.SpecificationId))
            .Returns(new Common.ApiClient.Models.ApiResponse <SpecModel.SpecificationSummary>(HttpStatusCode.OK, null));

            CalculationCreateModelValidator validator = CreateValidator(specificationsApiClient: specificationsApiClient);

            //Act
            ValidationResult result = await validator.ValidateAsync(model);

            //Assert
            result
            .IsValid
            .Should()
            .BeFalse();

            result.Errors
            .Should()
            .Contain(_ => _.ErrorMessage == "Failed to find specification for provided specification id.");
        }
Пример #5
0
        public async Task ValidateAsync_WhenSpecificationDoesNotContainFundingStreamValidIsFalse()
        {
            //Arrange
            CalculationCreateModel model = CreateModel();

            SpecModel.SpecificationSummary specificationSummary = new SpecModel.SpecificationSummary
            {
                FundingStreams = new[] { new Reference() }
            };

            ISpecificationsApiClient specificationsApiClient = CreateSpecificationsApiClient();

            specificationsApiClient
            .GetSpecificationSummaryById(Arg.Is(model.SpecificationId))
            .Returns(new Common.ApiClient.Models.ApiResponse <SpecModel.SpecificationSummary>(HttpStatusCode.OK, specificationSummary));

            CalculationCreateModelValidator validator = CreateValidator(specificationsApiClient: specificationsApiClient);

            //Act
            ValidationResult result = await validator.ValidateAsync(model);

            //Assert
            result
            .IsValid
            .Should()
            .BeFalse();

            result.Errors
            .Should()
            .Contain(_ => _.ErrorMessage == "The funding stream id provided is not associated with the provided specification.");
        }
        public async Task <ApiSpecificationSummary> GetSpecificationSummaryById(string specificationId)
        {
            Guard.IsNullOrWhiteSpace(specificationId, nameof(specificationId));

            ApiResponse <ApiSpecificationSummary> specificationSummaryResponse =
                await _resiliencePolicy.ExecuteAsync(() => _specifications.GetSpecificationSummaryById(specificationId));

            return(specificationSummaryResponse?.Content);
        }
        public async Task <IActionResult> GetPreviousProfilesForSpecificationForProviderForFundingLine(
            string specificationId,
            string providerId,
            string fundingStreamId,
            string fundingLineCode)
        {
            Guard.ArgumentNotNull(fundingStreamId, nameof(fundingStreamId));
            Guard.ArgumentNotNull(specificationId, nameof(specificationId));
            Guard.ArgumentNotNull(providerId, nameof(providerId));
            Guard.ArgumentNotNull(fundingLineCode, nameof(fundingLineCode));

            ApiResponse <IEnumerable <FundingLineChange> > fundingLineApiResponse = await _publishingApiClient
                                                                                    .GetPreviousProfilesForSpecificationForProviderForFundingLine(
                specificationId,
                providerId,
                fundingStreamId,
                fundingLineCode);

            IActionResult fundingLineErrorResult =
                fundingLineApiResponse.IsSuccessOrReturnFailureResult(nameof(PublishedProviderVersion));

            if (fundingLineErrorResult != null)
            {
                return(fundingLineErrorResult);
            }

            ApiResponse <ProviderVersionSearchResult> providerResponse =
                await _providersApiClient.GetCurrentProviderForFundingStream(fundingStreamId, providerId);

            IActionResult providerErrorResult =
                providerResponse.IsSuccessOrReturnFailureResult(nameof(ProviderVersionSearchResult));

            if (providerErrorResult != null)
            {
                return(providerErrorResult);
            }

            ApiResponse <SpecificationSummary> specificationResponse = await _specificationsApiClient.GetSpecificationSummaryById(specificationId);

            IActionResult specificationErrorResult =
                specificationResponse.IsSuccessOrReturnFailureResult(nameof(SpecificationSummary));

            if (specificationErrorResult != null)
            {
                return(specificationErrorResult);
            }

            SpecificationSummary specification = specificationResponse.Content;

            return(Ok(new FundingLineChangesViewModel
            {
                ProviderName = providerResponse.Content.Name,
                SpecificationName = specification.Name,
                FundingPeriodName = specification.FundingPeriod.Name,
                FundingLineChanges = fundingLineApiResponse.Content
            }));
        }
Пример #8
0
        public async Task ValidateAsync_WhenSourceCodeDoesNotCompile__ValidIsFalse()
        {
            //Arrange
            CalculationCreateModel model = CreateModel();

            model.CalculationType = CalculationType.Additional;

            ICalculationsRepository calculationsRepository = CreateCalculationRepository();

            calculationsRepository
            .GetCalculationsBySpecificationIdAndCalculationName(Arg.Is(model.SpecificationId), Arg.Is(model.Name))
            .Returns((Calculation)null);

            SpecModel.SpecificationSummary specificationSummary = new SpecModel.SpecificationSummary
            {
                Name           = "spec name",
                FundingStreams = new[] { new Reference(model.FundingStreamId, "funding stream name") }
            };

            ISpecificationsApiClient specificationsApiClient = CreateSpecificationsApiClient();

            specificationsApiClient
            .GetSpecificationSummaryById(Arg.Is(model.SpecificationId))
            .Returns(new Common.ApiClient.Models.ApiResponse <SpecModel.SpecificationSummary>(HttpStatusCode.OK, specificationSummary));

            PreviewResponse previewResponse = new PreviewResponse
            {
                CompilerOutput = new Build
                {
                    CompilerMessages = new List <CompilerMessage>
                    {
                        new CompilerMessage {
                            Message = "Failed"
                        }
                    }
                }
            };

            IPreviewService previewService = CreatePreviewService(previewResponse);

            CalculationCreateModelValidator validator = CreateValidator(
                calculationsRepository, previewService: previewService, specificationsApiClient: specificationsApiClient);

            //Act
            ValidationResult result = await validator.ValidateAsync(model);

            //Assert
            result
            .IsValid
            .Should()
            .BeFalse();

            result.Errors
            .Should()
            .Contain(_ => _.ErrorMessage == "There are errors in the source code provided");
        }
        private void AndSpecification()
        {
            _specificationSummary = NewSpecificationSummary(_ => _.WithId(SpecificationId)
                                                            .WithFundingStreamIds(new[] { FundingStreamId })
                                                            .WithFundingPeriodId(FundingPeriodId)
                                                            .WithTemplateIds((FundingStreamId, "1.0")));

            _specificationsApiClient.GetSpecificationSummaryById(SpecificationId)
            .Returns(new ApiResponse <SpecificationSummary>(HttpStatusCode.OK, _specificationSummary));
        }
Пример #10
0
        private async Task <SpecificationSummary> GetSpecificationSummary(string specificationId)
        {
            ApiResponse <SpecificationSummary> specificationQuery =
                await _specificationsApiPolicy.ExecuteAsync(() => _specificationsApiClient.GetSpecificationSummaryById(specificationId));

            if (specificationQuery == null || specificationQuery.StatusCode != HttpStatusCode.OK || specificationQuery.Content == null)
            {
                throw new InvalidOperationException("Specification summary is null");
            }

            return(specificationQuery.Content);
        }
 private void AndTheSpecificationIsReturned()
 {
     _specificationApiClient.GetSpecificationSummaryById(Arg.Is(_specificationId))
     .Returns(new ApiResponse <SpecificationSummary>
              (
                  HttpStatusCode.OK,
                  new SpecificationSummary
     {
         Id            = _specificationId,
         FundingPeriod = new Reference(_fundingPeriodId, "")
     }
              ));;
 }
Пример #12
0
        public async Task CreateAdditionalCalculation_GivenSavingDraftCalcFails_ReturnsInternalServerErrorResult()
        {
            //Arrange
            string correlationId = "any-id";

            CalculationCreateModel model = CreateCalculationCreateModel();

            Reference author = CreateAuthor();

            ICalculationsRepository calculationsRepository = CreateCalculationsRepository();

            calculationsRepository
            .CreateDraftCalculation(Arg.Any <Calculation>())
            .Returns(HttpStatusCode.BadRequest);

            ISpecificationsApiClient specificationsApiClient = CreateSpecificationsApiClient();

            specificationsApiClient
            .GetSpecificationSummaryById(Arg.Is(SpecificationId))
            .Returns(new ApiResponse <SpecificationSummary>(
                         HttpStatusCode.OK,
                         new SpecificationSummary {
                Id = SpecificationId
            }
                         ));

            ILogger logger = CreateLogger();

            CalculationService calculationService = CreateCalculationService(logger: logger, calculationsRepository: calculationsRepository, specificationsApiClient: specificationsApiClient);

            string errorMessage = $"There was problem creating a new calculation with name {CalculationName} in Cosmos Db with status code 400";

            //Act
            IActionResult result = await calculationService.CreateAdditionalCalculation(SpecificationId, model, author, correlationId);

            //Assert
            result
            .Should()
            .BeAssignableTo <InternalServerErrorResult>()
            .Which
            .Value
            .Should()
            .Be(errorMessage);

            logger
            .Received(1)
            .Error(Arg.Is(errorMessage));
        }
        public async Task <IActionResult> ReIndex()
        {
            IEnumerable <DocumentEntity <TestScenario> > testScenarios = await _scenariosRepository.GetAllTestScenarios();

            List <ScenarioIndex> testScenarioIndexes = new List <ScenarioIndex>();

            Dictionary <string, SpecModel.SpecificationSummary> specifications = new Dictionary <string, SpecModel.SpecificationSummary>();

            foreach (DocumentEntity <TestScenario> entity in testScenarios)
            {
                TestScenario testScenario = entity.Content;

                SpecModel.SpecificationSummary specificationSummary = null;
                if (!specifications.ContainsKey(testScenario.SpecificationId))
                {
                    Common.ApiClient.Models.ApiResponse <SpecModel.SpecificationSummary> specificationApiResponse =
                        await _specificationsApiClientPolicy.ExecuteAsync(() => _specificationsApiClient.GetSpecificationSummaryById(testScenario.SpecificationId));

                    specificationSummary = specificationApiResponse.Content;

                    specifications.Add(testScenario.SpecificationId, specificationSummary);
                }
                else
                {
                    specificationSummary = specifications[testScenario.SpecificationId];
                }

                testScenarioIndexes.Add(new ScenarioIndex()
                {
                    Id                 = testScenario.Id,
                    Name               = testScenario.Name,
                    Description        = testScenario.Current.Description,
                    LastUpdatedDate    = entity.UpdatedAt,
                    FundingStreamIds   = testScenario.Current?.FundingStreamIds.ToArray(),
                    FundingStreamNames = specificationSummary.FundingStreams.Select(s => s.Name).ToArray(),
                    FundingPeriodId    = testScenario.Current?.FundingPeriodId,
                    FundingPeriodName  = specificationSummary.FundingPeriod.Name,
                    SpecificationId    = testScenario.SpecificationId,
                    SpecificationName  = specificationSummary.Name,
                    Status             = Enum.GetName(typeof(PublishStatus), testScenario.Current.PublishStatus),
                });
            }

            await _searchRepository.Index(testScenarioIndexes);

            return(new OkObjectResult($"Updated {testScenarioIndexes.Count} records"));
        }
        public async Task EditCalculationStatus_GivenNewStatusButUpdatingDbReturnsBadRequest_ReturnsStatusCode400()
        {
            //Arrange
            EditStatusModel CalculationEditStatusModel = new EditStatusModel
            {
                PublishStatus = PublishStatus.Approved
            };

            ILogger logger = CreateLogger();

            Calculation calculation = CreateCalculation();

            ICalculationsRepository CalculationsRepository = CreateCalculationsRepository();

            CalculationsRepository
            .GetCalculationById(Arg.Is(CalculationId))
            .Returns(calculation);

            CalculationsRepository
            .UpdateCalculation(Arg.Any <Calculation>())
            .Returns(HttpStatusCode.BadRequest);

            SpecModel.SpecificationSummary specificationSummary = new SpecModel.SpecificationSummary();

            ISpecificationsApiClient specificationsApiClient = CreateSpecificationsApiClient();

            specificationsApiClient
            .GetSpecificationSummaryById(Arg.Is(calculation.SpecificationId))
            .Returns(new Common.ApiClient.Models.ApiResponse <SpecModel.SpecificationSummary>(HttpStatusCode.OK, specificationSummary));


            CalculationService service = CreateCalculationService(
                logger: logger, calculationsRepository: CalculationsRepository, specificationsApiClient: specificationsApiClient);

            //Act
            IActionResult result = await service.UpdateCalculationStatus(CalculationId, CalculationEditStatusModel);

            //Arrange
            result
            .Should()
            .BeAssignableTo <StatusCodeResult>()
            .Which
            .StatusCode
            .Should()
            .Be(400);
        }
Пример #15
0
        public async Task GetEffectivePermissionsForUser_WhenNotFoundInCacheButSpecificationNotFound_ThenPreconditionFailedResultReturned()
        {
            // Arrange
            ICacheProvider cacheProvider = CreateCacheProvider();
            EffectiveSpecificationPermission cachedPermission = null;

            cacheProvider
            .GetHashValue <EffectiveSpecificationPermission>(Arg.Is($"{CacheKeys.EffectivePermissions}:{UserId}"), Arg.Is(SpecificationId))
            .Returns(cachedPermission);

            ISpecificationsApiClient specificationsApiClient = CreateSpecificationsApiClient();

            SpecModel.SpecificationSummary specificationSummary = null;
            specificationsApiClient
            .GetSpecificationSummaryById(Arg.Is(SpecificationId))
            .Returns(new ApiResponse <SpecModel.SpecificationSummary>(HttpStatusCode.OK, specificationSummary));


            FundingStreamPermissionService service = CreateService(
                specificationsApiClient: specificationsApiClient,
                cacheProvider: cacheProvider);


            // Act
            IActionResult result = await service.GetEffectivePermissionsForUser(UserId, SpecificationId);

            // Assert
            result
            .Should()
            .BeOfType <PreconditionFailedResult>()
            .Which
            .Value
            .Should()
            .Be("Specification not found");

            await cacheProvider
            .Received(1)
            .GetHashValue <EffectiveSpecificationPermission>(Arg.Is($"{CacheKeys.EffectivePermissions}:{UserId}"), Arg.Is(SpecificationId));

            await cacheProvider
            .Received(0)
            .SetHashValue(
                Arg.Is($"{CacheKeys.EffectivePermissions}:{UserId}"),
                Arg.Is(SpecificationId),
                Arg.Any <EffectiveSpecificationPermission>());
        }
Пример #16
0
        public override async Task Process(Message message)
        {
            Guard.ArgumentNotNull(message, nameof(message));

            string specificationId = UserPropertyFrom(message, "specification-id");

            IEnumerable <Calculation> calculations =
                (await _calculationRepositoryPolicy.ExecuteAsync(() =>
                                                                 _calculationsRepository.GetCalculationsBySpecificationId(specificationId)))
                .ToArraySafe();

            if (calculations.IsNullOrEmpty())
            {
                string calcNotFoundMessage = $"No calculations found for specification id: {specificationId}";

                _logger.Information(calcNotFoundMessage);

                return;
            }

            foreach (Calculation calculation in calculations)
            {
                calculation.Current.PublishStatus = Models.Versioning.PublishStatus.Approved;
            }

            ApiResponse <SpecModel.SpecificationSummary> specificationApiResponse =
                await _specificationsApiClientPolicy.ExecuteAsync(() => _specificationsApiClient.GetSpecificationSummaryById(specificationId));

            if (!specificationApiResponse.StatusCode.IsSuccess() || specificationApiResponse.Content == null)
            {
                throw new NonRetriableException("Specification not found");
            }

            SpecModel.SpecificationSummary specificationSummary = specificationApiResponse.Content;

            await _calculationRepositoryPolicy.ExecuteAsync(() => _calculationsRepository.UpdateCalculations(calculations));

            await UpdateSearch(calculations, specificationSummary);

            IEnumerable <Task> tasks = calculations.Select(_ => UpdateCalculationInCache(_.ToResponseModel()));

            await TaskHelper.WhenAllAndThrow(tasks.ToArraySafe());

            await UpdateCalculationsInCache(specificationSummary);
        }
Пример #17
0
        public async Task IsCalculationNameValid_WhenSameCalculation_ThenReturnsOkResult()
        {
            // Arrange
            string specificationId = "spec1";
            string calcName        = "calc1";
            string calcSpecId      = "calc-1";

            ISpecificationsApiClient specificationsApiClient = CreateSpecificationsApiClient();

            specificationsApiClient
            .GetSpecificationSummaryById(Arg.Is(specificationId))
            .Returns(new Common.ApiClient.Models.ApiResponse <SpecModel.SpecificationSummary>(HttpStatusCode.OK, new SpecModel.SpecificationSummary {
                Id = specificationId
            }));

            List <Calculation> existingCalcs = new List <Calculation>
            {
                new Calculation
                {
                    Current = new CalculationVersion
                    {
                        Name           = calcName,
                        SourceCodeName = calcName
                    },

                    Id = "calc-1"
                }
            };

            ICalculationsRepository calculationsRepository = CreateCalculationsRepository();

            calculationsRepository
            .GetCalculationsBySpecificationId(Arg.Is(specificationId))
            .Returns(existingCalcs);

            CalculationService service = CreateCalculationService(specificationsApiClient: specificationsApiClient, calculationsRepository: calculationsRepository);

            // Act
            IActionResult result = await service.IsCalculationNameValid(specificationId, calcName, calcSpecId);

            // Assert
            result
            .Should()
            .BeOfType <OkResult>();
        }
        public async Task <IActionResult> GetSpecificationById(string specificationId)
        {
            Guard.IsNullOrWhiteSpace(specificationId, nameof(specificationId));

            ApiResponse <SpecificationSummary> apiResponse = await _specificationsApiClient.GetSpecificationSummaryById(specificationId);

            if (apiResponse.StatusCode == HttpStatusCode.OK)
            {
                return(Ok(apiResponse.Content));
            }

            if (apiResponse.StatusCode == HttpStatusCode.BadRequest)
            {
                return(new BadRequestResult());
            }

            return(new StatusCodeResult((int)apiResponse.StatusCode));
        }
Пример #19
0
        public async Task ValidateAsync_WhenValidModel_ValidIsTrue()
        {
            //Arrange
            CalculationCreateModel model = CreateModel();


            ICalculationsRepository calculationsRepository = CreateCalculationRepository();

            calculationsRepository
            .GetCalculationsBySpecificationIdAndCalculationName(Arg.Is(model.SpecificationId), Arg.Is(model.Name))
            .Returns((Calculation)null);

            SpecModel.SpecificationSummary specificationSummary = new SpecModel.SpecificationSummary
            {
                Name           = "spec name",
                FundingStreams = new[] { new Reference(model.FundingStreamId, "funding stream name") }
            };

            ISpecificationsApiClient specificationsApiClient = CreateSpecificationsApiClient();

            specificationsApiClient
            .GetSpecificationSummaryById(Arg.Is(model.SpecificationId))
            .Returns(new Common.ApiClient.Models.ApiResponse <SpecModel.SpecificationSummary>(HttpStatusCode.OK, specificationSummary));

            CalculationCreateModelValidator validator = CreateValidator(
                calculationsRepository, specificationsApiClient: specificationsApiClient);

            //Act
            ValidationResult result = await validator.ValidateAsync(model);

            //Assert
            result
            .IsValid
            .Should()
            .BeTrue();

            model.SpecificationName
            .Should()
            .Be("spec name");

            model.FundingStreamName
            .Should()
            .Be("funding stream name");
        }
        public async Task <IActionResult> GetSources(string specificationId)
        {
            Guard.IsNullOrWhiteSpace(specificationId, nameof(specificationId));

            ApiResponse <SpecificationSummary> specificationResponse = await _specificationsApiClient.GetSpecificationSummaryById(specificationId);

            if (specificationResponse.StatusCode != HttpStatusCode.OK)
            {
                return(new StatusCodeResult((int)specificationResponse.StatusCode));
            }

            SpecificationDatasetRelationshipsViewModel viewModel = await PopulateViewModel(specificationResponse.Content);

            if (viewModel == null)
            {
                return(new StatusCodeResult(500));
            }

            return(new OkObjectResult(viewModel));
        }
Пример #21
0
        private static ISpecificationsApiClient CreateSpecificationsApiClient()
        {
            ISpecificationsApiClient specificationsApiClient = Substitute.For <ISpecificationsApiClient>();

            SpecModel.SpecificationSummary specificationSummary = new SpecModel.SpecificationSummary()
            {
                TemplateIds = new Dictionary <string, string> {
                    { "fs-1", "2.2" }
                },
                FundingStreams = new List <Reference>()
                {
                    new Reference("fs-1", "PE and Sports"),
                },
            };

            specificationsApiClient
            .GetSpecificationSummaryById(Arg.Any <string>())
            .Returns(new ApiResponse <SpecModel.SpecificationSummary>(HttpStatusCode.OK, specificationSummary));

            return(specificationsApiClient);
        }
Пример #22
0
        public async Task IsCalculationNameValid_WhenSpecificationDoesNotExist_ThenReturnsNotFoundResult()
        {
            // Arrange
            string specificationId = "spec1";

            ISpecificationsApiClient specificationsApiClient = CreateSpecificationsApiClient();

            specificationsApiClient
            .GetSpecificationSummaryById(Arg.Is(specificationId))
            .Returns(new Common.ApiClient.Models.ApiResponse <SpecModel.SpecificationSummary>(HttpStatusCode.OK, null));

            CalculationService service = CreateCalculationService(specificationsApiClient: specificationsApiClient);

            // Act
            IActionResult result = await service.IsCalculationNameValid(specificationId, "calc1", null);

            // Assert
            result
            .Should()
            .BeOfType <NotFoundResult>();
        }
        public async Task EditCalculationStatus_GivenNewStatusButNoSpecSummaryFound_ReturnsPreConditionFailed()
        {
            //Arrange
            EditStatusModel CalculationEditStatusModel = new EditStatusModel
            {
                PublishStatus = PublishStatus.Approved
            };

            ILogger logger = CreateLogger();

            Calculation calculation = CreateCalculation();

            ICalculationsRepository CalculationsRepository = CreateCalculationsRepository();

            CalculationsRepository
            .GetCalculationById(Arg.Is(CalculationId))
            .Returns(calculation);

            ISpecificationsApiClient specificationsApiClient = CreateSpecificationsApiClient();

            specificationsApiClient
            .GetSpecificationSummaryById(Arg.Is(calculation.SpecificationId))
            .Returns(new Common.ApiClient.Models.ApiResponse <SpecModel.SpecificationSummary>(HttpStatusCode.OK, null));

            CalculationService service = CreateCalculationService(
                logger: logger, calculationsRepository: CalculationsRepository, specificationsApiClient: specificationsApiClient);

            //Act
            IActionResult result = await service.UpdateCalculationStatus(CalculationId, CalculationEditStatusModel);

            //Arrange
            result
            .Should()
            .BeAssignableTo <PreconditionFailedResult>()
            .Which
            .Value
            .Should()
            .Be("Specification not found");
        }
Пример #24
0
        public async Task ValidateAsync_WhenFundingStreamIdEmptyForAdditionalCalcs_ValidIsTrue()
        {
            //Arrange
            CalculationCreateModel model = CreateModel(CalculationType.Additional);

            model.FundingStreamId = string.Empty;

            ISpecificationsApiClient specificationsApiClient = CreateSpecificationsApiClient();

            specificationsApiClient
            .GetSpecificationSummaryById(Arg.Is(model.SpecificationId))
            .Returns(new Common.ApiClient.Models.ApiResponse <SpecModel.SpecificationSummary>(HttpStatusCode.OK, new SpecModel.SpecificationSummary()));

            CalculationCreateModelValidator validator = CreateValidator(specificationsApiClient: specificationsApiClient);

            //Act
            ValidationResult result = await validator.ValidateAsync(model);

            //Assert
            result
            .IsValid
            .Should()
            .BeTrue();
        }
 private void GivenTheApiResponseContentForTheSpecificationId(ApiSpecificationSummary specificationSummary,
                                                              string specificationId)
 {
     _specifications.GetSpecificationSummaryById(specificationId)
     .Returns(new ApiResponse <ApiSpecificationSummary>(HttpStatusCode.OK, specificationSummary));
 }
        public async Task <IActionResult> GetEffectivePermissionsForUser(string userId, string specificationId)
        {
            if (string.IsNullOrWhiteSpace(userId))
            {
                return(new BadRequestObjectResult($"{nameof(userId)} is empty or null"));
            }

            if (string.IsNullOrWhiteSpace(specificationId))
            {
                return(new BadRequestObjectResult($"{nameof(specificationId)} is empty or null"));
            }

            EffectiveSpecificationPermission cachedPermissions = await _cacheProviderPolicy.ExecuteAsync(() => _cacheProvider.GetHashValue <EffectiveSpecificationPermission>($"{CacheKeys.EffectivePermissions}:{userId}", specificationId));

            if (cachedPermissions != null)
            {
                return(new OkObjectResult(cachedPermissions));
            }
            else
            {
                ApiResponse <SpecModel.SpecificationSummary> specificationApiResponse =
                    await _specificationsApiClientPolicy.ExecuteAsync(() => _specificationsApiClient.GetSpecificationSummaryById(specificationId));

                if (!specificationApiResponse.StatusCode.IsSuccess() || specificationApiResponse.Content == null)
                {
                    return(new PreconditionFailedResult("Specification not found"));
                }

                SpecModel.SpecificationSummary specification = specificationApiResponse.Content;

                List <FundingStreamPermission> permissionsForUser = new List <FundingStreamPermission>();
                foreach (Reference fundingStream in specification.FundingStreams)
                {
                    FundingStreamPermission permission = await _userRepositoryPolicy.ExecuteAsync(() => _userRepository.GetFundingStreamPermission(userId, fundingStream.Id));

                    if (permission != null)
                    {
                        permissionsForUser.Add(permission);
                    }
                    else
                    {
                        // Add permission for this funding stream with no permissions - used further down to calculate permissions (required for pessimistic permissions)
                        permissionsForUser.Add(new FundingStreamPermission
                        {
                            UserId                       = userId,
                            FundingStreamId              = fundingStream.Id,
                            CanApproveFunding            = false,
                            CanChooseFunding             = false,
                            CanCreateSpecification       = false,
                            CanEditCalculations          = false,
                            CanEditSpecification         = false,
                            CanMapDatasets               = false,
                            CanReleaseFunding            = false,
                            CanAdministerFundingStream   = false,
                            CanApproveSpecification      = false,
                            CanCreateQaTests             = false,
                            CanDeleteCalculations        = false,
                            CanDeleteSpecification       = false,
                            CanDeleteQaTests             = false,
                            CanEditQaTests               = false,
                            CanRefreshFunding            = false,
                            CanCreateTemplates           = false,
                            CanEditTemplates             = false,
                            CanDeleteTemplates           = false,
                            CanApproveTemplates          = false,
                            CanCreateProfilePattern      = false,
                            CanEditProfilePattern        = false,
                            CanDeleteProfilePattern      = false,
                            CanAssignProfilePattern      = false,
                            CanApplyCustomProfilePattern = false,
                            CanApproveCalculations       = false,
                            CanApproveAnyCalculations    = false,
                            CanApproveAllCalculations    = false
                        });
                    }
                }

                EffectiveSpecificationPermission specificationPermissions = GeneratePermissions(permissionsForUser, specificationId, userId);

                string userPermissionHashKey = $"{CacheKeys.EffectivePermissions}:{userId}";

                // Does the hash set for this user already exist - used to determine the timeout for the hash set below
                bool existingHashSetExists = await _cacheProviderPolicy.ExecuteAsync(() => _cacheProvider.HashSetExists(userPermissionHashKey));

                // Cache effective permissions for the specification / user
                await _cacheProviderPolicy.ExecuteAsync(() => _cacheProvider.SetHashValue(userPermissionHashKey, specificationId, specificationPermissions));

                // If the hash set does not exist, then set an expiry for the whole hash set. This stops the users permissions being stored indefinitely
                if (!existingHashSetExists)
                {
                    await _cacheProviderPolicy.ExecuteAsync(() => _cacheProvider.SetHashExpiry(userPermissionHashKey, DateTime.UtcNow.AddHours(12)));
                }

                return(new OkObjectResult(specificationPermissions));
            }
        }
Пример #27
0
        public async Task <IActionResult> SaveVersion(CreateNewTestScenarioVersion scenarioVersion, Reference user, string correlationId)
        {
            if (scenarioVersion == null)
            {
                _logger.Error("A null scenario version was provided");

                return(new BadRequestObjectResult("Null or empty calculation Id provided"));
            }

            BadRequestObjectResult validationResult = (await _createNewTestScenarioVersionValidator.ValidateAsync(scenarioVersion)).PopulateModelState();

            if (validationResult != null)
            {
                return(validationResult);
            }

            TestScenario testScenario = null;

            if (!string.IsNullOrEmpty(scenarioVersion.Id))
            {
                testScenario = await _scenariosRepository.GetTestScenarioById(scenarioVersion.Id);
            }

            bool saveAsVersion = true;

            Common.ApiClient.Models.ApiResponse <SpecModel.SpecificationSummary> specificationApiResponse =
                await _specificationsApiClientPolicy.ExecuteAsync(() => _specificationsApiClient.GetSpecificationSummaryById(scenarioVersion.SpecificationId));

            if (!specificationApiResponse.StatusCode.IsSuccess() || specificationApiResponse.Content == null)
            {
                _logger.Error($"Unable to find a specification for specification id : {scenarioVersion.SpecificationId}");
                return(new StatusCodeResult(412));
            }

            SpecModel.SpecificationSummary specification = specificationApiResponse.Content;

            if (testScenario == null)
            {
                string Id = Guid.NewGuid().ToString();

                testScenario = new TestScenario
                {
                    Id = Id,
                    SpecificationId = specification.Id,
                    Name            = scenarioVersion.Name,
                    Current         = new TestScenarioVersion
                    {
                        Date             = DateTimeOffset.Now.ToLocalTime(),
                        TestScenarioId   = Id,
                        PublishStatus    = PublishStatus.Draft,
                        Version          = 1,
                        Author           = user,
                        Gherkin          = scenarioVersion.Scenario,
                        Description      = scenarioVersion.Description,
                        FundingPeriodId  = specification.FundingPeriod.Id,
                        FundingStreamIds = specification.FundingStreams.Select(s => s.Id).ToArraySafe(),
                    }
                };
            }
            else
            {
                testScenario.Name = scenarioVersion.Name;

                saveAsVersion = !string.Equals(scenarioVersion.Scenario, testScenario.Current.Gherkin) ||
                                scenarioVersion.Description != testScenario.Current.Description;

                TestScenarioVersion newVersion = testScenario.Current.Clone() as TestScenarioVersion;

                if (saveAsVersion == true)
                {
                    newVersion.Author           = user;
                    newVersion.Gherkin          = scenarioVersion.Scenario;
                    newVersion.Description      = scenarioVersion.Description;
                    newVersion.FundingStreamIds = specification.FundingStreams.Select(s => s.Id).ToArraySafe();
                    newVersion.FundingPeriodId  = specification.FundingPeriod.Id;

                    newVersion = await _versionRepository.CreateVersion(newVersion, testScenario.Current);

                    testScenario.Current = newVersion;
                }
            }

            HttpStatusCode statusCode = await _scenariosRepository.SaveTestScenario(testScenario);

            if (!statusCode.IsSuccess())
            {
                _logger.Error($"Failed to save test scenario with status code: {statusCode}");

                return(new StatusCodeResult((int)statusCode));
            }

            await _versionRepository.SaveVersion(testScenario.Current);

            ScenarioIndex scenarioIndex = CreateScenarioIndexFromScenario(testScenario, specification);

            await _searchRepository.Index(new List <ScenarioIndex> {
                scenarioIndex
            });

            await _cacheProvider.RemoveAsync <List <TestScenario> >($"{CacheKeys.TestScenarios}{testScenario.SpecificationId}");

            await _cacheProvider.RemoveAsync <GherkinParseResult>($"{CacheKeys.GherkinParseResult}{testScenario.Id}");

            IEnumerable <Common.ApiClient.Calcs.Models.Calculation> calculations = await _calcsRepositoryPolicy.ExecuteAsync(() => _calcsRepository.GetCurrentCalculationsBySpecificationId(specification.Id));

            if (calculations.IsNullOrEmpty())
            {
                _logger.Information($"No calculations found to test for specification id: '{specification.Id}'");
            }
            else
            {
                try
                {
                    JobsModels.Trigger trigger = new JobsModels.Trigger
                    {
                        EntityId   = testScenario.Id,
                        EntityType = nameof(TestScenario),
                        Message    = $"Saving test scenario: '{testScenario.Id}'"
                    };

                    bool generateCalculationAggregations = SourceCodeHelpers.HasCalculationAggregateFunctionParameters(calculations.Select(m => m.SourceCode));

                    JobsModels.Job job = await SendInstructAllocationsToJobService(specification.Id, user, trigger, correlationId, generateCalculationAggregations);

                    _logger.Information($"New job of type '{job.JobDefinitionId}' created with id: '{job.Id}'");
                }
                catch (Exception ex)
                {
                    _logger.Error(ex, $"Failed to create job of type '{JobConstants.DefinitionNames.CreateInstructAllocationJob}' on specification '{specification.Id}'");

                    return(new InternalServerErrorResult($"An error occurred attempting to execute calculations prior to running tests on specification '{specification.Id}'"));
                }
            }

            CurrentTestScenario testScenarioResult = await _scenariosRepository.GetCurrentTestScenarioById(testScenario.Id);

            return(new OkObjectResult(testScenarioResult));
        }
        public async Task PreviewCalculationResult_GivenCachedAggregateValuesExist_CalculateProviderResultsCallReceived()
        {
            IAllocationModel allocationModel = Substitute.For <IAllocationModel>();

            _calculationEngine
            .GenerateAllocationModel(Arg.Any <Assembly>())
            .Returns(allocationModel);

            ProviderVersionSearchResult providerVersionSearchResult = new ProviderVersionSearchResult
            {
                UKPRN = providerId
            };

            IEnumerable <string> dataDefinitionRelationshipIds = new List <string>();

            SpecificationSummary specificationSummary = new SpecificationSummary
            {
                DataDefinitionRelationshipIds = dataDefinitionRelationshipIds,
                ProviderVersionId             = providerVersionId
            };

            _specificationsApiClient
            .GetSpecificationSummaryById(Arg.Is(specificationId))
            .Returns(new ApiResponse <SpecificationSummary>(HttpStatusCode.OK, specificationSummary));

            _providersApiClient
            .GetProviderByIdFromProviderVersion(Arg.Is(providerVersionId), Arg.Is(providerId))
            .Returns(new ApiResponse <ProviderVersionSearchResult>(HttpStatusCode.OK, providerVersionSearchResult));

            CalculationSummaryModel previewCalculationSummaryModel = new CalculationSummaryModel();

            IEnumerable <CalculationSummaryModel> calculationSummaryModels = new List <CalculationSummaryModel>
            {
                new CalculationSummaryModel(),
                new CalculationSummaryModel()
            };

            List <CalculationSummaryModel> expectedCalculationSummaryModels = calculationSummaryModels.ToList();

            expectedCalculationSummaryModels.Add(previewCalculationSummaryModel);

            _calculationsRepository
            .GetCalculationSummariesForSpecification(Arg.Is(specificationId))
            .Returns(calculationSummaryModels);

            Dictionary <string, ProviderSourceDataset> sourceDatasets = new Dictionary <string, ProviderSourceDataset>();

            Dictionary <string, Dictionary <string, ProviderSourceDataset> > providerSourceDatasets = new Dictionary <string, Dictionary <string, ProviderSourceDataset> >
            {
                { providerId, sourceDatasets }
            };

            _providerSourceDatasetsRepository
            .GetProviderSourceDatasetsByProviderIdsAndRelationshipIds(
                Arg.Is(specificationId),
                Arg.Is <IEnumerable <string> >(_ => _ != null && _.Count() == 1 && _.FirstOrDefault() == providerId),
                Arg.Is <IEnumerable <string> >(_ => _ != null && _.SequenceEqual(dataDefinitionRelationshipIds)))
            .Returns(providerSourceDatasets);

            IEnumerable <CalculationAggregation> calculationAggregations = new List <CalculationAggregation>();

            _calculationAggregationService
            .BuildAggregations(Arg.Is <BuildAggregationRequest>(_ => _ != null && _.SpecificationId == specificationId))
            .Returns(calculationAggregations);

            PreviewCalculationRequest previewCalculationRequest = new PreviewCalculationRequest
            {
                AssemblyContent = MockData.GetMockAssembly(),
                PreviewCalculationSummaryModel = previewCalculationSummaryModel
            };

            IActionResult actionResult =
                await _calculationEnginePreviewService.PreviewCalculationResult(specificationId, providerId, previewCalculationRequest);

            _calculationEngine
            .Received(1)
            .CalculateProviderResults(
                Arg.Is(allocationModel),
                specificationId,
                Arg.Is <IEnumerable <CalculationSummaryModel> >(_ => _.SequenceEqual(expectedCalculationSummaryModels)),
                Arg.Is <ProviderSummary>(_ => _.UKPRN == providerId),
                Arg.Is <Dictionary <string, ProviderSourceDataset> >(_ => _.SequenceEqual(sourceDatasets)),
                Arg.Is <IEnumerable <CalculationAggregation> >(_ => _.SequenceEqual(calculationAggregations)));
        }
Пример #29
0
        public override async Task Process(Message message)
        {
            Guard.ArgumentNotNull(message, nameof(message));

            string specificationId = UserPropertyFrom(message, "specification-id");
            string fundingStreamId = UserPropertyFrom(message, "fundingstream-id");
            string templateVersion = UserPropertyFrom(message, "template-version");

            string    correlationId = message.GetCorrelationId();
            Reference author        = message.GetUserDetails();

            TemplateMapping templateMapping = await _calculationsRepositoryPolicy.ExecuteAsync(
                () => _calculationsRepository.GetTemplateMapping(specificationId, fundingStreamId));

            if (templateMapping == null)
            {
                LogAndThrowException(
                    $"Did not locate Template Mapping for funding stream id {fundingStreamId} and specification id {specificationId}");
            }

            ApiResponse <SpecificationSummary> specificationApiResponse =
                await _specificationsApiClientPolicy.ExecuteAsync(() => _specificationsApiClient.GetSpecificationSummaryById(specificationId));

            if (!specificationApiResponse.StatusCode.IsSuccess() || specificationApiResponse.Content == null)
            {
                LogAndThrowException(
                    $"Did not locate specification : {specificationId}");
            }

            SpecificationSummary specificationSummary = specificationApiResponse.Content;

            ApiResponse <TemplateMetadataContents> templateContentsResponse = await _policiesResiliencePolicy.ExecuteAsync(
                () => _policiesApiClient.GetFundingTemplateContents(fundingStreamId, specificationSummary.FundingPeriod.Id, templateVersion));

            TemplateMetadataContents templateMetadataContents = templateContentsResponse?.Content;

            if (templateMetadataContents == null)
            {
                LogAndThrowException(
                    $"Did not locate Template Metadata Contents for funding stream id {fundingStreamId}, funding period id {specificationSummary.FundingPeriod.Id} and template version {templateVersion}");
            }

            TemplateMappingItem[] mappingsWithoutCalculations = templateMapping.TemplateMappingItems.Where(_ => _.CalculationId.IsNullOrWhitespace())
                                                                .ToArray();

            FundingLine[] flattenedFundingLines = templateMetadataContents.RootFundingLines.Flatten(_ => _.FundingLines).ToArray();

            IDictionary <uint, Calculation> uniqueTemplateCalculations = flattenedFundingLines
                                                                         .SelectMany(_ => _.Calculations.Flatten(cal => cal.Calculations))
                                                                         .GroupBy(x => x.TemplateCalculationId)
                                                                         .Select(x => x.FirstOrDefault())
                                                                         .ToDictionary(_ => _.TemplateCalculationId);

            TemplateMappingItem[] mappingsWithCalculations = templateMapping.TemplateMappingItems.Where(_ => !_.CalculationId.IsNullOrWhitespace())
                                                             .ToArray();

            int startingItemCount = 1;

            await NotifyProgress(startingItemCount);

            await EnsureAllExistingCalculationsModified(mappingsWithCalculations,
                                                        specificationSummary,
                                                        correlationId,
                                                        author,
                                                        uniqueTemplateCalculations,
                                                        startingItemCount);

            TemplateMappingItem[] newMappingsWithCalculations = await EnsureAllRequiredCalculationsExist(mappingsWithoutCalculations,
                                                                                                         mappingsWithCalculations,
                                                                                                         fundingStreamId,
                                                                                                         specificationSummary,
                                                                                                         author,
                                                                                                         correlationId,
                                                                                                         startingItemCount += mappingsWithCalculations.Length,
                                                                                                         uniqueTemplateCalculations);

            // refresh template mapping
            await RefreshTemplateMapping(specificationId, fundingStreamId, templateMapping);

            await InitiateCalculationRun(specificationId, author, correlationId);

            await QueueUpdateCodeContextJob(specificationId);
        }
Пример #30
0
        public CalculationCreateModelValidator(
            ICalculationsRepository calculationRepository,
            IPreviewService previewService,
            ISpecificationsApiClient specificationsApiClient,
            ICalcsResiliencePolicies calcsResiliencePolicies)
        {
            Guard.ArgumentNotNull(calculationRepository, nameof(calculationRepository));
            Guard.ArgumentNotNull(previewService, nameof(previewService));
            Guard.ArgumentNotNull(specificationsApiClient, nameof(specificationsApiClient));
            Guard.ArgumentNotNull(calcsResiliencePolicies, nameof(calcsResiliencePolicies));
            Guard.ArgumentNotNull(calcsResiliencePolicies?.SpecificationsApiClient, nameof(calcsResiliencePolicies.SpecificationsApiClient));

            _calculationRepository         = calculationRepository;
            _previewService                = previewService;
            _specificationsApiClient       = specificationsApiClient;
            _specificationsApiClientPolicy = calcsResiliencePolicies.SpecificationsApiClient;

            CascadeMode = CascadeMode.StopOnFirstFailure;

            RuleFor(model => model.SpecificationId)
            .NotEmpty()
            .NotNull()
            .WithMessage("Null or empty specification id provided.");

            RuleFor(model => model.ValueType)
            .NotNull()
            .WithMessage("Null value type was provided.");

            RuleFor(model => model.Name)
            .Custom((name, context) =>
            {
                CalculationCreateModel calculationCreateModel = context.ParentContext.InstanceToValidate as CalculationCreateModel;
                if (string.IsNullOrWhiteSpace(calculationCreateModel.Name))
                {
                    context.AddFailure("Null or empty calculation name provided.");
                }
                else
                {
                    if (!string.IsNullOrWhiteSpace(calculationCreateModel.SpecificationId))
                    {
                        Calculation calculation = _calculationRepository.GetCalculationsBySpecificationIdAndCalculationName(calculationCreateModel.SpecificationId, calculationCreateModel.Name).Result;

                        if (calculation != null)
                        {
                            context.AddFailure($"A calculation already exists with the name: '{calculationCreateModel.Name}' for this specification");
                        }
                    }
                }
            });

            RuleFor(model => model.SourceCode)
            .Custom((sc, context) =>
            {
                CalculationCreateModel calculationCreateModel = context.ParentContext.InstanceToValidate as CalculationCreateModel;
                if (string.IsNullOrWhiteSpace(calculationCreateModel.SourceCode))
                {
                    context.AddFailure("Null or empty source code provided.");
                }
                else
                {
                    if (calculationCreateModel.CalculationType == CalculationType.Additional)
                    {
                        PreviewRequest previewRequest = new PreviewRequest
                        {
                            SpecificationId = calculationCreateModel.SpecificationId,
                            CalculationId   = calculationCreateModel.Id,
                            Name            = calculationCreateModel.Name,
                            SourceCode      = calculationCreateModel.SourceCode
                        };

                        IActionResult result = _previewService.Compile(previewRequest).Result;

                        OkObjectResult okObjectResult = result as OkObjectResult;

                        PreviewResponse response = okObjectResult.Value as PreviewResponse;

                        if (response != null)
                        {
                            if (!response.CompilerOutput.CompilerMessages.IsNullOrEmpty())
                            {
                                context.AddFailure("There are errors in the source code provided");
                            }
                        }
                    }
                }
            });

            RuleFor(model => model.FundingStreamId)
            .Custom((fs, context) =>
            {
                CalculationCreateModel calculationCreateModel = context.ParentContext.InstanceToValidate as CalculationCreateModel;

                //only validate funding stream id for template calcs
                var isTemplateCalculation = calculationCreateModel.CalculationType == CalculationType.Template;

                if (isTemplateCalculation &&
                    string.IsNullOrWhiteSpace(calculationCreateModel.FundingStreamId))
                {
                    context.AddFailure("Null or empty funding stream id provided.");
                }
                else
                {
                    ApiResponse <SpecModel.SpecificationSummary> specificationApiResponse = _specificationsApiClientPolicy.ExecuteAsync(() => _specificationsApiClient.GetSpecificationSummaryById(calculationCreateModel.SpecificationId)).GetAwaiter().GetResult();

                    if (specificationApiResponse == null || !specificationApiResponse.StatusCode.IsSuccess() || specificationApiResponse.Content == null)
                    {
                        context.AddFailure("Failed to find specification for provided specification id.");
                    }
                    else
                    {
                        SpecModel.SpecificationSummary specificationSummary = specificationApiResponse.Content;

                        //I don't want to have to fetch the spec summary again outside of this method to get the name and funding stream so we set them on input model here
                        calculationCreateModel.SpecificationName = specificationSummary.Name;

                        //only validate funding stream ids for template calcs
                        if (!isTemplateCalculation)
                        {
                            return;
                        }

                        Reference fundingStream = specificationSummary.FundingStreams.FirstOrDefault(m => m.Id == calculationCreateModel.FundingStreamId);

                        if (fundingStream == null)
                        {
                            context.AddFailure("The funding stream id provided is not associated with the provided specification.");
                        }
                        else
                        {
                            calculationCreateModel.FundingStreamName = fundingStream.Name;
                        }
                    }
                }
            });
        }