Example #1
0
        public void SetUp()
        {
            _approveSpecificationFundingJobs = Substitute.For <ICreateApproveAllFundingJobs>();
            _approveProviderFundingJobs      = Substitute.For <ICreateApproveBatchFundingJobs>();
            _cacheProvider = Substitute.For <ICacheProvider>();
            _specificationFundingStatusService = Substitute.For <ISpecificationFundingStatusService>();
            _prerequisiteCheckerLocator        = Substitute.For <IPrerequisiteCheckerLocator>();
            _prerequisiteChecker = Substitute.For <IPrerequisiteChecker>();
            _providerService     = Substitute.For <IProviderService>();
            _publishedFunding    = Substitute.For <IPublishedFundingRepository>();

            _service = new SpecificationPublishingService(
                SpecificationIdValidator,
                ProviderIdsValidator,
                _providerService,
                Specifications,
                ResiliencePolicies,
                _cacheProvider,
                Jobs,
                _approveSpecificationFundingJobs,
                _approveProviderFundingJobs,
                _specificationFundingStatusService,
                FundingConfigurationService,
                _prerequisiteCheckerLocator,
                _publishedFunding);

            _approveProvidersRequest = BuildApproveProvidersRequest(_ => _.WithProviders(ProviderIds));
        }
Example #2
0
 public CommandHandler(IPrerequisiteChecker prerequisiteChecker,
                       ISystemService systemService,
                       ICommand[] commands)
 {
     _commands            = commands.OrderByDescending(x => x.GetCommandWords().Length).ToArray();
     _prerequisiteChecker = prerequisiteChecker;
     _systemService       = systemService;
 }
 public CommandHandler(IPrerequisiteChecker prerequisiteChecker,
                       ISystemService systemService,
                       ICommand[] commands)
 {
     _commands = commands.OrderByDescending(x => x.GetCommandWords().Length).ToArray();
     _prerequisiteChecker = prerequisiteChecker;
     _systemService = systemService;
 }
        public async Task <IActionResult> CreateRefreshFundingJob(string specificationId,
                                                                  Reference user,
                                                                  string correlationId)
        {
            ValidationResult validationResult = SpecificationIdValidator.Validate(specificationId);

            if (!validationResult.IsValid)
            {
                return(validationResult.AsBadRequest());
            }

            ApiResponse <ApiSpecificationSummary> specificationIdResponse =
                await ResiliencePolicy.ExecuteAsync(() => Specifications.GetSpecificationSummaryById(specificationId));

            ApiSpecificationSummary specificationSummary = specificationIdResponse.Content;

            if (specificationSummary == null)
            {
                return(new NotFoundResult());
            }

            SpecificationFundingStatus chooseCheck = await _specificationFundingStatusService.CheckChooseForFundingStatus(specificationSummary);

            if (chooseCheck == SpecificationFundingStatus.SharesAlreadyChosenFundingStream)
            {
                return(new ConflictResult());
            }

            IDictionary <string, Provider> scopedProviders = await _providerService.GetScopedProvidersForSpecification(specificationSummary.Id, specificationSummary.ProviderVersionId);

            // Check prerequisites for this specification to be chosen/refreshed
            IPrerequisiteChecker prerequisiteChecker = _prerequisiteCheckerLocator.GetPreReqChecker(PrerequisiteCheckerType.Refresh);

            try
            {
                await prerequisiteChecker.PerformChecks(
                    specificationSummary,
                    null,
                    Array.Empty <PublishedProvider>(),
                    scopedProviders?.Values);
            }
            catch (JobPrereqFailedException ex)
            {
                return(new BadRequestObjectResult(new [] { $"Prerequisite check for refresh failed {ex.Message}" }.ToModelStateDictionary()));
            }

            ApiJob refreshFundingJob = await _refreshFundingJobs.CreateJob(specificationId, user, correlationId);

            Guard.ArgumentNotNull(refreshFundingJob, nameof(refreshFundingJob), "Failed to create RefreshFundingJob");

            JobCreationResponse jobCreationResponse = new JobCreationResponse()
            {
                JobId = refreshFundingJob.Id,
            };

            return(new OkObjectResult(jobCreationResponse));
        }
        private async Task PerformPrerequisiteChecks(string specificationId, string jobId, PrerequisiteCheckerType prerequisiteCheckerType)
        {
            _logger.Information($"Verifying prerequisites for {prerequisiteCheckerType}");

            IPrerequisiteChecker prerequisiteChecker = _prerequisiteCheckerLocator.GetPreReqChecker(prerequisiteCheckerType);

            try
            {
                await prerequisiteChecker.PerformChecks(specificationId, jobId, null, null);
            }
            catch (JobPrereqFailedException ex)
            {
                throw new NonRetriableException(ex.Message, ex);
            }

            _logger.Information($"Prerequisites for {prerequisiteCheckerType} passed");
        }
        public async Task <IActionResult> ValidateSpecificationForRefresh(string specificationId)
        {
            List <string> prereqErrors = new List <string>();

            ValidationResult validationResult = SpecificationIdValidator.Validate(specificationId);

            if (!validationResult.IsValid)
            {
                return(validationResult.AsBadRequest());
            }

            ApiResponse <ApiSpecificationSummary> specificationIdResponse =
                await ResiliencePolicy.ExecuteAsync(() => Specifications.GetSpecificationSummaryById(specificationId));

            ApiSpecificationSummary specificationSummary = specificationIdResponse.Content;

            if (specificationSummary == null)
            {
                return(new NotFoundResult());
            }

            IDictionary <string, Provider> scopedProviders = await _providerService.GetScopedProvidersForSpecification(specificationSummary.Id, specificationSummary.ProviderVersionId);

            IPrerequisiteChecker prerequisiteChecker = _prerequisiteCheckerLocator.GetPreReqChecker(PrerequisiteCheckerType.Refresh);

            try
            {
                await prerequisiteChecker.PerformChecks(
                    specificationSummary,
                    null,
                    Array.Empty <PublishedProvider>(),
                    scopedProviders?.Values);
            }
            catch (JobPrereqFailedException ex)
            {
                return(new BadRequestObjectResult(ex.Errors.ToArray().ToModelStateDictionary()));
            }

            return(new NoContentResult());
        }
Example #7
0
        public override async Task Process(Message message)
        {
            Guard.ArgumentNotNull(message, nameof(message));

            Reference author = message.GetUserDetails();

            string specificationId = message.UserProperties["specification-id"] as string;

            SpecificationSummary specification = await _specificationService.GetSpecificationSummaryById(specificationId);

            if (specification == null)
            {
                throw new NonRetriableException($"Could not find specification with id '{specificationId}'");
            }

            // Get scoped providers for this specification
            IDictionary <string, Provider> scopedProviders = await _providerService.GetScopedProvidersForSpecification(specification.Id, specification.ProviderVersionId);

            if (!scopedProviders.IsNullOrEmpty())
            {
                _logger.Information($"Found {scopedProviders.Count} scoped providers for refresh");
            }
            else
            {
                _logger.Information("No scoped providers found for refresh");
            }

            // Get existing published providers for this specification
            _logger.Information("Looking up existing published providers from cosmos for refresh job");

            IDictionary <string, List <PublishedProvider> > existingPublishedProvidersByFundingStream = new Dictionary <string, List <PublishedProvider> >();

            foreach (Reference fundingStream in specification.FundingStreams)
            {
                List <PublishedProvider> publishedProviders = (await _publishingResiliencePolicy.ExecuteAsync(() =>
                                                                                                              _publishedFundingDataService.GetCurrentPublishedProviders(fundingStream.Id, specification.FundingPeriod.Id))).ToList();

                existingPublishedProvidersByFundingStream.Add(fundingStream.Id, publishedProviders);
                _logger.Information($"Found {publishedProviders.Count} existing published providers for funding stream {fundingStream.Id} from cosmos for refresh job");
            }

            _logger.Information("Verifying prerequisites for funding refresh");

            // Check prerequisites for this specification to be chosen/refreshed
            IPrerequisiteChecker prerequisiteChecker = _prerequisiteCheckerLocator.GetPreReqChecker(PrerequisiteCheckerType.Refresh);

            try
            {
                await prerequisiteChecker.PerformChecks(specification, Job.Id, existingPublishedProvidersByFundingStream.SelectMany(x => x.Value), scopedProviders?.Values);
            }
            catch (JobPrereqFailedException ex)
            {
                throw new NonRetriableException(ex.Message, ex);
            }
            _logger.Information("Prerequisites for refresh passed");

            // Get calculation results for specification
            _logger.Information("Looking up calculation results");

            IDictionary <string, ProviderCalculationResult> allCalculationResults;

            try
            {
                allCalculationResults = await _calculationResultsService.GetCalculationResultsBySpecificationId(specificationId, scopedProviders.Keys);
            }
            catch (Exception ex)
            {
                _logger.Error(ex, "Exception during calculation result lookup");
                throw;
            }

            _logger.Information($"Found calculation results for {allCalculationResults?.Count} providers from cosmos for refresh job");

            string correlationId = message.GetUserProperty <string>(SfaCorrelationId);

            try
            {
                foreach (Reference fundingStream in specification.FundingStreams)
                {
                    _logger.Information($"Starting to refresh funding for '{fundingStream.Id}'");

                    await RefreshFundingStream(fundingStream,
                                               specification,
                                               scopedProviders,
                                               allCalculationResults,
                                               Job.Id,
                                               author,
                                               correlationId,
                                               existingPublishedProvidersByFundingStream[fundingStream.Id],
                                               specification.FundingPeriod.Id);

                    _logger.Information($"Finished processing refresh funding for '{fundingStream.Id}'");
                }
            }
            finally
            {
                _logger.Information("Starting to clear variation snapshots");
                _variationService.ClearSnapshots();
                _logger.Information("Finished clearing variation snapshots");
            }
        }
        private async Task PublishFundingStream(Reference fundingStream,
                                                SpecificationSummary specification,
                                                string jobId,
                                                Reference author,
                                                string correlationId,
                                                PrerequisiteCheckerType prerequisiteCheckerType,
                                                string[] batchPublishedProviderIds = null)
        {
            _logger.Information($"Processing Publish Funding for {fundingStream.Id} in specification {specification.Id}");

            if (!specification.TemplateIds.ContainsKey(fundingStream.Id) || string.IsNullOrWhiteSpace(specification.TemplateIds[fundingStream.Id]))
            {
                _logger.Information($"Skipped publishing {fundingStream.Id} as no template exists");

                return;
            }

            // we always need to get every provider in scope whether it is released or otherwise so that we always genarate the contents
            // this is just in case an error has occurred during a release so we never get a case where we don't get blobs generated for the published providers
            (IDictionary <string, PublishedProvider> publishedProvidersForFundingStream,
             IDictionary <string, PublishedProvider> scopedPublishedProviders) = await _providerService.GetPublishedProviders(fundingStream,
                                                                                                                              specification);

            IDictionary <string, PublishedProvider> publishedProvidersByPublishedProviderId = publishedProvidersForFundingStream.Values.ToDictionary(_ => _.PublishedProviderId);

            IEnumerable <PublishedProvider> selectedPublishedProviders =
                batchPublishedProviderIds.IsNullOrEmpty() ?
                publishedProvidersForFundingStream.Values :
                batchPublishedProviderIds.Where(_ => publishedProvidersByPublishedProviderId.ContainsKey(_)).Select(_ => publishedProvidersByPublishedProviderId[_]);

            AddInitialPublishVariationReasons(selectedPublishedProviders);

            _logger.Information($"Verifying prerequisites for funding publish");

            IPrerequisiteChecker prerequisiteChecker = _prerequisiteCheckerLocator.GetPreReqChecker(prerequisiteCheckerType);

            try
            {
                await prerequisiteChecker.PerformChecks(specification, jobId, selectedPublishedProviders?.ToList());
            }
            catch (JobPrereqFailedException ex)
            {
                throw new NonRetriableException(ex.Message, ex);
            }

            _logger.Information("Prerequisites for publish passed");

            TemplateMapping templateMapping = await GetTemplateMapping(fundingStream, specification.Id);

            PublishedFundingInput publishedFundingInput = await _publishedFundingService.GeneratePublishedFundingInput(publishedProvidersForFundingStream,
                                                                                                                       scopedPublishedProviders?.Values.Select(_ => _.Current.Provider),
                                                                                                                       fundingStream,
                                                                                                                       specification,
                                                                                                                       batchPublishedProviderIds.IsNullOrEmpty()?null : selectedPublishedProviders);

            using Transaction transaction = _transactionFactory.NewTransaction <PublishService>();
            try
            {
                // if any error occurs while updating or indexing then we need to re-index all published providers and persist published funding for consistency
                transaction.Enroll(async() =>
                {
                    await _publishedProviderVersionService.CreateReIndexJob(author, correlationId, specification.Id, jobId);
                    await _createPublishIntegrityJob.CreateJob(specification.Id,
                                                               author,
                                                               correlationId,
                                                               batchPublishedProviderIds.IsNullOrEmpty() ? null : new Dictionary <string, string>
                    {
                        { "providers-batch", JsonExtensions.AsJson(selectedPublishedProviders.Select(_ => _.PublishedProviderId)) }
                    },
                                                               parentJobId: jobId);
                });

                await SavePublishedProvidersAsReleased(jobId, author, selectedPublishedProviders, correlationId);

                ICollection <PublishedProvider> publishedProviders = publishedProvidersForFundingStream?.Values;

                _logger.Information($"Generating published funding");
                IEnumerable <(PublishedFunding PublishedFunding, PublishedFundingVersion PublishedFundingVersion)> publishedFundingToSave =
                    _publishedFundingGenerator.GeneratePublishedFunding(publishedFundingInput, publishedProviders).ToList();
                _logger.Information($"A total of {publishedFundingToSave.Count()} published funding versions created to save.");

                foreach ((PublishedFunding PublishedFunding, PublishedFundingVersion PublishedFundingVersion)publishedFundingItems in publishedFundingToSave)
                {
                    PropagateProviderVariationReasons(publishedFundingItems.PublishedFundingVersion, publishedProviders);
                }

                // if any error occurs while updating then we still need to run the indexer to be consistent
                transaction.Enroll(async() =>
                {
                    await _publishedIndexSearchResiliencePolicy.ExecuteAsync(() => _publishedFundingSearchRepository.RunIndexer());
                });

                // Save a version of published funding and set this version to current
                _logger.Information($"Saving published funding");
                await _publishedFundingStatusUpdateService.UpdatePublishedFundingStatus(publishedFundingToSave, author, PublishedFundingStatus.Released, jobId, correlationId);

                _logger.Information($"Finished saving published funding");

                // Save contents to blob storage and search for the feed
                _logger.Information($"Saving published funding contents");
                await _publishedFundingContentsPersistanceService.SavePublishedFundingContents(publishedFundingToSave.Select(_ => _.PublishedFundingVersion),
                                                                                               publishedFundingInput.TemplateMetadataContents);

                _logger.Information($"Finished saving published funding contents");

                if (!selectedPublishedProviders.IsNullOrEmpty())
                {
                    // Generate contents JSON for provider and save to blob storage
                    IPublishedProviderContentsGenerator generator = _publishedProviderContentsGeneratorResolver.GetService(publishedFundingInput.TemplateMetadataContents.SchemaVersion);
                    await _publishedProviderContentsPersistanceService.SavePublishedProviderContents(publishedFundingInput.TemplateMetadataContents, templateMapping,
                                                                                                     selectedPublishedProviders, generator);
                }

                transaction.Complete();
            }
            catch (Exception ex)
            {
                await transaction.Compensate();

                throw;
            }
        }