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)); }
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()); }
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; } }