public async Task GetJobDetails_ReturnsJobViewModel() { IJobsApiClient jobsApiClient = Substitute.For <IJobsApiClient>(); JobManagementResiliencePolicies policies = new JobManagementResiliencePolicies { JobsApiClient = Policy.NoOpAsync() }; IMessengerService messengerService = Substitute.For <IMessengerService>(); ILogger logger = Substitute.For <ILogger>(); JobViewModel jvm = new JobViewModel { CompletionStatus = null }; ApiResponse <JobViewModel> jobApiResponse = new ApiResponse <JobViewModel>(HttpStatusCode.OK, jvm); jobsApiClient .GetJobById(Arg.Any <string>()) .Returns(jobApiResponse); JobManagement jobManagement = new JobManagement(jobsApiClient, logger, policies, messengerService); string jobId = "3456"; //Act JobViewModel viewModel = await jobManagement.GetJobById(jobId); //Assert viewModel .Should() .Be(jvm); }
public async Task CreateAllocationLineResultStatusUpdateJobs_GivenJobIdButJobResponseIsNotFound_LogsAndDoesNotProcess() { //Arrange Message message = new Message(); message.UserProperties.Add("jobId", jobId); ILogger logger = CreateLogger(); ApiResponse <JobViewModel> apiResponse = new ApiResponse <JobViewModel>(HttpStatusCode.NotFound); IJobsApiClient jobsApiClient = CreateJobsApiClient(); jobsApiClient .GetJobById(jobId) .Returns(apiResponse); PublishedResultsService publishedResultsService = CreateResultsService(logger, jobsApiClient: jobsApiClient); //Act await publishedResultsService.CreateAllocationLineResultStatusUpdateJobs(message); //Assert logger .Received(1) .Error(Arg.Is($"Could not find the job with id: '{jobId}'")); await jobsApiClient .DidNotReceive() .AddJobLog(Arg.Is(jobId), Arg.Any <JobLogUpdateModel>()); }
public void CreateAllocationLineResultStatusUpdateJobs_GivenJobFoundButModelNotFoundInCache_LogsAndThrowsException() { //Arrange const string cacheKey = "cache-key"; Message message = new Message(); message.UserProperties.Add("jobId", jobId); ILogger logger = CreateLogger(); JobViewModel job = new JobViewModel { Id = jobId, Properties = new Dictionary <string, string> { { "cache-key", cacheKey } } }; ApiResponse <JobViewModel> apiResponse = new ApiResponse <JobViewModel>(HttpStatusCode.OK, job); IJobsApiClient jobsApiClient = CreateJobsApiClient(); jobsApiClient .GetJobById(jobId) .Returns(apiResponse); ICacheProvider cacheProvider = CreateCacheProvider(); cacheProvider .GetAsync <UpdatePublishedAllocationLineResultStatusModel>(Arg.Is(cacheKey)) .Returns((UpdatePublishedAllocationLineResultStatusModel)null); PublishedResultsService publishedResultsService = CreateResultsService(logger, jobsApiClient: jobsApiClient, cacheProvider: cacheProvider); //Act Func <Task> test = async() => await publishedResultsService.CreateAllocationLineResultStatusUpdateJobs(message); //Assert test .Should() .ThrowExactly <Exception>() .Which .Message .Should() .Be($"Could not find the update model in cache with cache key: '{cacheKey}'"); logger .Received(1) .Error(Arg.Is($"Could not find the update model in cache with cache key: '{cacheKey}'")); }
public async Task RetrieveJobAndCheckCanBeProcessed_ApiReturnsIncomplete_ReturnsCorrectly() { //Arrange IJobsApiClient jobsApiClient = Substitute.For <IJobsApiClient>(); JobManagementResiliencePolicies policies = new JobManagementResiliencePolicies { JobsApiClient = Policy.NoOpAsync() }; IMessengerService messengerService = Substitute.For <IMessengerService>(); ILogger logger = Substitute.For <ILogger>(); JobViewModel jvm = new JobViewModel { CompletionStatus = null }; ApiResponse <JobViewModel> jobApiResponse = new ApiResponse <JobViewModel>(HttpStatusCode.OK, jvm); jobsApiClient .GetJobById(Arg.Any <string>()) .Returns(jobApiResponse); JobManagement jobManagement = new JobManagement(jobsApiClient, logger, policies, messengerService); string jobId = "3456"; //Act JobViewModel viewModel = await jobManagement.RetrieveJobAndCheckCanBeProcessed(jobId); //Assert await jobsApiClient .Received(1) .GetJobById(jobId); viewModel .Should() .Be(jvm); }
public async Task CreateAllocationLineResultStatusUpdateJobs_GivenJobFoundButAlreadyCompleted_LogsAndReturnsDoesNotAddJobLog() { //Arrange Message message = new Message(); message.UserProperties.Add("jobId", jobId); ILogger logger = CreateLogger(); JobViewModel job = new JobViewModel { Id = jobId, CompletionStatus = CompletionStatus.Cancelled }; ApiResponse <JobViewModel> apiResponse = new ApiResponse <JobViewModel>(HttpStatusCode.OK, job); IJobsApiClient jobsApiClient = CreateJobsApiClient(); jobsApiClient .GetJobById(jobId) .Returns(apiResponse); PublishedResultsService publishedResultsService = CreateResultsService(logger, jobsApiClient: jobsApiClient); //Act await publishedResultsService.CreateAllocationLineResultStatusUpdateJobs(message); //Assert logger .Received(1) .Information(Arg.Is($"Received job with id: '{job.Id}' is already in a completed state with status {job.CompletionStatus.ToString()}")); await jobsApiClient .DidNotReceive() .AddJobLog(Arg.Any <string>(), Arg.Any <JobLogUpdateModel>()); }
public async Task RetrieveJobAndCheckCanBeProcessed_FailsWithJobAlreadyCompleted_LogsAndErrors(ApiResponse <JobViewModel> jobApiResponse, string jobId, string errorMessage, LogEventLevel logEventLevel) { //Arrange IJobsApiClient jobsApiClient = Substitute.For <IJobsApiClient>(); IMessengerService messengerService = Substitute.For <IMessengerService>(); JobManagementResiliencePolicies policies = new JobManagementResiliencePolicies { JobsApiClient = Policy.NoOpAsync() }; ILogger logger = Substitute.For <ILogger>(); jobsApiClient .GetJobById(Arg.Any <string>()) .Returns(jobApiResponse); JobManagement jobManagement = new JobManagement(jobsApiClient, logger, policies, messengerService); Func <Task> test = async() => await jobManagement.RetrieveJobAndCheckCanBeProcessed(jobId); test .Should().Throw <JobAlreadyCompletedException>() .Which .Message .Should().Be(errorMessage); await jobsApiClient .Received(1) .GetJobById(jobId); logger .Received(1) .Write(logEventLevel, errorMessage); }
public async Task <JobViewModel> RetrieveJobAndCheckCanBeProcessed(string jobId) { ApiResponse <JobViewModel> response = await _jobsApiClientPolicy.ExecuteAsync(() => _jobsApiClient.GetJobById(jobId)); if (response?.Content == null) { string error = $"Could not find the job with id: '{jobId}'"; _logger.Write(LogEventLevel.Error, error); throw new JobNotFoundException(error, jobId); } JobViewModel job = response.Content; if (job.CompletionStatus.HasValue) { string error = $"Received job with id: '{jobId}' is already in a completed state with status {job.CompletionStatus}"; _logger.Write(LogEventLevel.Information, error); throw new JobAlreadyCompletedException(error, job); } return(job); }
public void CreateAllocationLineResultStatusUpdateJobs_GivenUpateModelWith10ProvidersAndMaxPartitionSizeOf2ButOnlyThreeJobsCreatedFromFive_ThrowsException() { //Arrange const string cacheKey = "cache-key"; Message message = new Message(); message.UserProperties.Add("jobId", jobId); ILogger logger = CreateLogger(); JobViewModel job = new JobViewModel { Id = jobId, SpecificationId = specificationId, InvokerUserDisplayName = "user-name", InvokerUserId = "user-id", CorrelationId = "coorelation-id", Properties = new Dictionary <string, string> { { "cache-key", cacheKey } }, JobDefinitionId = JobConstants.DefinitionNames.CreateInstructAllocationLineResultStatusUpdateJob }; UpdatePublishedAllocationLineResultStatusModel updateModel = new UpdatePublishedAllocationLineResultStatusModel { Status = AllocationLineStatus.Approved, Providers = CreateProviderAllocationLineResults() }; ApiResponse <JobViewModel> apiResponse = new ApiResponse <JobViewModel>(HttpStatusCode.OK, job); IJobsApiClient jobsApiClient = CreateJobsApiClient(); jobsApiClient .GetJobById(jobId) .Returns(apiResponse); IEnumerable <Job> newJobs = new[] { new Job(), new Job(), new Job() }; jobsApiClient .CreateJobs(Arg.Any <IEnumerable <JobCreateModel> >()) .Returns(newJobs); ICacheProvider cacheProvider = CreateCacheProvider(); cacheProvider .GetAsync <UpdatePublishedAllocationLineResultStatusModel>(Arg.Is(cacheKey)) .Returns(updateModel); IPublishedProviderResultsSettings settings = CreatePublishedProviderResultsSettings(); settings .UpdateAllocationLineResultStatusBatchCount .Returns(2); PublishedResultsService publishedResultsService = CreateResultsService(logger, jobsApiClient: jobsApiClient, cacheProvider: cacheProvider, publishedProviderResultsSettings: settings); //Act Func <Task> test = async() => await publishedResultsService.CreateAllocationLineResultStatusUpdateJobs(message); //Assert test .Should() .ThrowExactly <Exception>() .Which .Message .Should() .Be($"Only 3 jobs were created from 5 childJobs for parent job: '{job.Id}'"); logger .Received(1) .Error(Arg.Is($"Only 3 jobs were created from 5 childJobs for parent job: '{job.Id}'")); }
public async Task CreateAllocationLineResultStatusUpdateJobs_GivenUpateModelWith10ProvidersAndMaxPartitionSizeOf2_CreatesFiveChildJobs() { //Arrange const string cacheKey = "cache-key"; Message message = new Message(); message.UserProperties.Add("jobId", jobId); ILogger logger = CreateLogger(); JobViewModel job = new JobViewModel { Id = jobId, SpecificationId = specificationId, InvokerUserDisplayName = "user-name", InvokerUserId = "user-id", CorrelationId = "coorelation-id", Properties = new Dictionary <string, string> { { "cache-key", cacheKey } }, JobDefinitionId = JobConstants.DefinitionNames.CreateInstructAllocationLineResultStatusUpdateJob }; UpdatePublishedAllocationLineResultStatusModel updateModel = new UpdatePublishedAllocationLineResultStatusModel { Status = AllocationLineStatus.Approved, Providers = CreateProviderAllocationLineResults() }; ApiResponse <JobViewModel> apiResponse = new ApiResponse <JobViewModel>(HttpStatusCode.OK, job); IJobsApiClient jobsApiClient = CreateJobsApiClient(); jobsApiClient .GetJobById(jobId) .Returns(apiResponse); IEnumerable <Job> newJobs = new[] { new Job(), new Job(), new Job(), new Job(), new Job(), }; jobsApiClient .CreateJobs(Arg.Any <IEnumerable <JobCreateModel> >()) .Returns(newJobs); ICacheProvider cacheProvider = CreateCacheProvider(); cacheProvider .GetAsync <UpdatePublishedAllocationLineResultStatusModel>(Arg.Is(cacheKey)) .Returns(updateModel); IPublishedProviderResultsSettings settings = CreatePublishedProviderResultsSettings(); settings .UpdateAllocationLineResultStatusBatchCount .Returns(2); PublishedResultsService publishedResultsService = CreateResultsService(logger, jobsApiClient: jobsApiClient, cacheProvider: cacheProvider, publishedProviderResultsSettings: settings); //Act await publishedResultsService.CreateAllocationLineResultStatusUpdateJobs(message); //Assert await jobsApiClient .Received(1) .CreateJobs(Arg.Is <IEnumerable <JobCreateModel> >(m => m.Count() == 5)); await cacheProvider .Received(1) .RemoveAsync <UpdatePublishedAllocationLineResultStatusModel>(Arg.Is(cacheKey)); }
public async Task CreateAllocationLineResultStatusUpdateJobs_GivenUpateModelWith10ProvidersAndDefaultMaxPartitionSize_CreatesOneChildJob() { //Arrange const string cacheKey = "cache-key"; Message message = new Message(); message.UserProperties.Add("jobId", jobId); ILogger logger = CreateLogger(); JobViewModel job = new JobViewModel { Id = jobId, SpecificationId = specificationId, InvokerUserDisplayName = "user-name", InvokerUserId = "user-id", CorrelationId = "coorelation-id", Properties = new Dictionary <string, string> { { "cache-key", cacheKey } }, JobDefinitionId = JobConstants.DefinitionNames.CreateInstructAllocationLineResultStatusUpdateJob }; UpdatePublishedAllocationLineResultStatusModel updateModel = new UpdatePublishedAllocationLineResultStatusModel { Status = AllocationLineStatus.Approved, Providers = CreateProviderAllocationLineResults() }; ApiResponse <JobViewModel> apiResponse = new ApiResponse <JobViewModel>(HttpStatusCode.OK, job); IJobsApiClient jobsApiClient = CreateJobsApiClient(); jobsApiClient .GetJobById(jobId) .Returns(apiResponse); IEnumerable <Job> newJobs = new[] { new Job() }; jobsApiClient .CreateJobs(Arg.Any <IEnumerable <JobCreateModel> >()) .Returns(newJobs); ICacheProvider cacheProvider = CreateCacheProvider(); cacheProvider .GetAsync <UpdatePublishedAllocationLineResultStatusModel>(Arg.Is(cacheKey)) .Returns(updateModel); PublishedResultsService publishedResultsService = CreateResultsService(logger, jobsApiClient: jobsApiClient, cacheProvider: cacheProvider); //Act await publishedResultsService.CreateAllocationLineResultStatusUpdateJobs(message); //Assert await jobsApiClient .Received(1) .CreateJobs(Arg.Is <IEnumerable <JobCreateModel> >( m => m.First().Trigger.EntityId == jobId && m.First().Trigger.EntityType == "Job" && m.First().Trigger.Message == $"Triggered by parent job" && m.First().SpecificationId == specificationId && m.First().ParentJobId == jobId && m.First().InvokerUserId == job.InvokerUserId && m.First().InvokerUserDisplayName == job.InvokerUserDisplayName && m.First().CorrelationId == job.CorrelationId && !string.IsNullOrWhiteSpace(m.First().MessageBody))); await cacheProvider .Received(1) .RemoveAsync <UpdatePublishedAllocationLineResultStatusModel>(Arg.Is(cacheKey)); }
public async Task UpdateAllocations(Message message) { Guard.ArgumentNotNull(message, nameof(message)); JobViewModel job = null; if (!message.UserProperties.ContainsKey("jobId")) { _logger.Error("Missing parent job id to instruct generating allocations"); return; } string jobId = message.UserProperties["jobId"].ToString(); ApiResponse <JobViewModel> response = await _jobsApiClientPolicy.ExecuteAsync(() => _jobsApiClient.GetJobById(jobId)); if (response == null || response.Content == null) { _logger.Error($"Could not find the parent job with job id: '{jobId}'"); throw new Exception($"Could not find the parent job with job id: '{jobId}'"); } job = response.Content; if (job.CompletionStatus.HasValue) { _logger.Information($"Received job with id: '{job.Id}' is already in a completed state with status {job.CompletionStatus.ToString()}"); return; } await _jobsApiClientPolicy.ExecuteAsync(() => _jobsApiClient.AddJobLog(jobId, new Common.ApiClient.Jobs.Models.JobLogUpdateModel())); IDictionary <string, string> properties = message.BuildMessageProperties(); string specificationId = message.UserProperties["specification-id"].ToString(); BuildProject buildProject = await GetBuildProjectForSpecificationId(specificationId); if (message.UserProperties.ContainsKey("ignore-save-provider-results")) { properties.Add("ignore-save-provider-results", "true"); } string cacheKey = ""; if (message.UserProperties.ContainsKey("provider-cache-key")) { cacheKey = message.UserProperties["provider-cache-key"].ToString(); } else { cacheKey = $"{CacheKeys.ScopedProviderSummariesPrefix}{specificationId}"; } bool summariesExist = await _cacheProvider.KeyExists <ProviderSummary>(cacheKey); long totalCount = await _cacheProvider.ListLengthAsync <ProviderSummary>(cacheKey); bool refreshCachedScopedProviders = false; if (summariesExist) { IEnumerable <string> scopedProviderIds = await _providerResultsRepository.GetScopedProviderIds(specificationId); if (scopedProviderIds.Count() != totalCount) { refreshCachedScopedProviders = true; } else { IEnumerable <ProviderSummary> cachedScopedSummaries = await _cacheProvider.ListRangeAsync <ProviderSummary>(cacheKey, 0, (int)totalCount); IEnumerable <string> differences = scopedProviderIds.Except(cachedScopedSummaries.Select(m => m.Id)); refreshCachedScopedProviders = differences.AnyWithNullCheck(); } } if (!summariesExist || refreshCachedScopedProviders) { totalCount = await _providerResultsRepository.PopulateProviderSummariesForSpecification(specificationId); } const string providerSummariesPartitionIndex = "provider-summaries-partition-index"; const string providerSummariesPartitionSize = "provider-summaries-partition-size"; properties.Add(providerSummariesPartitionSize, _engineSettings.MaxPartitionSize.ToString()); properties.Add("provider-cache-key", cacheKey); properties.Add("specification-id", specificationId); IList <IDictionary <string, string> > allJobProperties = new List <IDictionary <string, string> >(); for (int partitionIndex = 0; partitionIndex < totalCount; partitionIndex += _engineSettings.MaxPartitionSize) { if (properties.ContainsKey(providerSummariesPartitionIndex)) { properties[providerSummariesPartitionIndex] = partitionIndex.ToString(); } else { properties.Add(providerSummariesPartitionIndex, partitionIndex.ToString()); } IDictionary <string, string> jobProperties = new Dictionary <string, string>(); foreach (KeyValuePair <string, string> item in properties) { jobProperties.Add(item.Key, item.Value); } allJobProperties.Add(jobProperties); } await _specificationsRepository.UpdateCalculationLastUpdatedDate(specificationId); try { if (!allJobProperties.Any()) { _logger.Information($"No scoped providers set for specification '{specificationId}'"); JobLogUpdateModel jobCompletedLog = new JobLogUpdateModel { CompletedSuccessfully = true, Outcome = "Calculations not run as no scoped providers set for specification" }; await _jobsApiClientPolicy.ExecuteAsync(() => _jobsApiClient.AddJobLog(job.Id, jobCompletedLog)); return; } IEnumerable <Job> newJobs = await CreateGenerateAllocationJobs(job, allJobProperties); int newJobsCount = newJobs.Count(); int batchCount = allJobProperties.Count(); if (newJobsCount != batchCount) { throw new Exception($"Only {newJobsCount} child jobs from {batchCount} were created with parent id: '{job.Id}'"); } else { _logger.Information($"{newJobsCount} child jobs were created for parent id: '{job.Id}'"); } } catch (Exception ex) { _logger.Error(ex.Message); throw new Exception($"Failed to create child jobs for parent job: '{job.Id}'"); } }
private async Task <JobViewModel> AddStartingProcessJobLog(string jobId) { if (string.IsNullOrWhiteSpace(jobId)) { _logger.Error($"No jobId given."); throw new NonRetriableException("No Job Id given"); } ApiResponse <JobViewModel> jobResponse = await _jobsApiClientPolicy.ExecuteAsync(() => _jobsApiClient.GetJobById(jobId)); if (jobResponse == null || jobResponse.Content == null) { _logger.Error($"Could not find the parent job with job id: '{jobId}'"); throw new NonRetriableException($"Could not find the parent job with job id: '{jobId}'"); } JobViewModel job = jobResponse.Content; if (job.CompletionStatus.HasValue) { _logger.Information($"Received job with id: '{job.Id}' is already in a completed state with status {job.CompletionStatus.ToString()}"); return(null); } await _jobsApiClientPolicy.ExecuteAsync(() => _jobsApiClient.AddJobLog(jobId, new JobLogUpdateModel())); return(job); }