public async Task <IActionResult> GetJobById(string jobId, bool includeChildJobs) { Guard.IsNullOrWhiteSpace(jobId, nameof(jobId)); Job job = await _jobsRepositoryPolicy.ExecuteAsync(() => _jobRepository.GetJobById(jobId)); if (job == null) { return(new NotFoundResult()); } JobViewModel jobViewModel = _mapper.Map <JobViewModel>(job); IEnumerable <Job> childJobs = _jobsRepositoryNonAsyncPolicy.Execute(() => _jobRepository.GetChildJobsForParent(jobId)); if (!childJobs.IsNullOrEmpty()) { foreach (Job childJob in childJobs) { jobViewModel.ChildJobs.Add(_mapper.Map <JobViewModel>(childJob)); } } return(new OkObjectResult(jobViewModel)); }
public async Task <IActionResult> SaveDefinition(HttpRequest request) { string json = await request.GetRawBodyStringAsync(); string jsonFilename = request.GetJsonFileNameFromRequest(); if (string.IsNullOrEmpty(json)) { _logger.Error($"Null or empty json provided for file: {jsonFilename}"); return(new BadRequestObjectResult($"Invalid json was provided for file: {jsonFilename}")); } JobDefinition definition = null; try { definition = JsonConvert.DeserializeObject <JobDefinition>(json); } catch (Exception exception) { _logger.Error(exception, $"Invalid json was provided for file: {jsonFilename}"); return(new BadRequestObjectResult($"Invalid json was provided for file: {jsonFilename}")); } try { HttpStatusCode result = await _jobDefinitionsRepositoryPolicy.ExecuteAsync(() => _jobDefinitionsRepository.SaveJobDefinition(definition)); if (!result.IsSuccess()) { int statusCode = (int)result; _logger.Error($"Failed to save json file: {jsonFilename} to cosmos db with status {statusCode}"); return(new StatusCodeResult(statusCode)); } } catch (Exception exception) { _logger.Error(exception, $"Exception occurred writing json file: {jsonFilename} to cosmos db"); return(new InternalServerErrorResult($"Exception occurred writing json file: {jsonFilename} to cosmos db")); } await _cachePolicy.ExecuteAsync(() => _cacheProvider.RemoveAsync <List <JobDefinition> >(CacheKeys.JobDefinitions)); return(new NoContentResult()); }
public async Task GenerateCalculationResultsCsv(Message message) { string specificationId = message.GetUserProperty <string>("specification-id"); if (specificationId == null) { string error = "Specification id missing"; _logger.Error(error); throw new NonRetriableException(error); } IEnumerable <ProviderResult> results = await ProviderResultsBySpecificationId(specificationId); IEnumerable <ExpandoObject> resultsForOutput = ProcessProviderResultsForCsvOutput(results).ToList(); string csv = new CsvUtils().CreateCsvExpando(resultsForOutput); ICloudBlob blob = _blobClient.GetBlockBlobReference($"calculation-results-{specificationId}.csv"); using (MemoryStream memoryStream = new MemoryStream(Encoding.UTF8.GetBytes(csv))) { await _blobClientPolicy.ExecuteAsync(() => blob.UploadFromStreamAsync(memoryStream)); } }
public async Task <IActionResult> HasCalculationResults(string calculationId) { Guard.IsNullOrWhiteSpace(calculationId, nameof(calculationId)); Models.Calcs.Calculation calculation = await _calculationsRepositoryPolicy.ExecuteAsync(() => _calculationRepository.GetCalculationById(calculationId)); if (calculation == null) { _logger.Error($"Calculation could not be found for calculation id '{calculationId}'"); return(new NotFoundObjectResult($"Calculation could not be found for calculation id '{calculationId}'")); } ProviderResult providerResult = await _resultsRepositoryPolicy.ExecuteAsync(() => _resultsRepository.GetSingleProviderResultBySpecificationId(calculation.SpecificationId)); if (providerResult != null) { CalculationResult calculationResult = providerResult.CalculationResults?.FirstOrDefault(m => string.Equals(m.Calculation.Id, calculationId, StringComparison.InvariantCultureIgnoreCase)); if (calculationResult != null) { return(new OkObjectResult(true)); } } return(new OkObjectResult(false)); }
private async Task <Job> CreateJob(JobCreateModel job) { Job newJob = new Job() { JobDefinitionId = job.JobDefinitionId, InvokerUserId = job.InvokerUserId, InvokerUserDisplayName = job.InvokerUserDisplayName, ItemCount = job.ItemCount, SpecificationId = job.SpecificationId, Trigger = job.Trigger, ParentJobId = job.ParentJobId, CorrelationId = job.CorrelationId, Properties = job.Properties, MessageBody = job.MessageBody }; Job newJobResult = null; try { newJobResult = await _jobDefinitionsRepositoryPolicy.ExecuteAsync(() => _jobRepository.CreateJob(newJob)); } catch (Exception ex) { _logger.Error(ex, $"Failed to save new job with definition id {job.JobDefinitionId}"); } return(newJobResult); }
public async Task <SearchFeed <AllocationNotificationFeedIndex> > GetFeeds(int pageRef, int top = 500, IEnumerable <string> statuses = null) { if (pageRef < 1) { throw new ArgumentException("Page ref cannot be less than one", nameof(pageRef)); } if (top < 1) { top = 500; } int skip = (pageRef - 1) * top; IList <string> filter = new List <string>(); if (!statuses.IsNullOrEmpty()) { if (!statuses.Contains("All")) { foreach (string status in statuses) { filter.Add($"allocationStatus eq '{status}'"); } } } SearchResults <AllocationNotificationFeedIndex> searchResults = await _allocationNotificationsSearchRepositoryPolicy.ExecuteAsync( () => _allocationNotificationsSearchRepository.Search("", new SearchParameters { Skip = skip, Top = top, SearchMode = Microsoft.Azure.Search.Models.SearchMode.Any, IncludeTotalResultCount = true, Filter = filter.IsNullOrEmpty() ? "" : string.Join(" or ", filter), OrderBy = DefaultOrderBy.ToList(), QueryType = QueryType.Full })); return(new SearchFeed <AllocationNotificationFeedIndex> { PageRef = pageRef, Top = top, TotalCount = searchResults != null && searchResults.TotalCount.HasValue ? (int)searchResults?.TotalCount : 0, Entries = searchResults?.Results.Select(m => m.Result) }); }
private async Task <BuildProject> GenerateBuildProject(string specificationId) { BuildProject buildproject = new BuildProject { SpecificationId = specificationId, Id = Guid.NewGuid().ToString(), Name = specificationId, DatasetRelationships = new List <DatasetRelationshipSummary>(), Build = new Build() }; IEnumerable <DatasetSpecificationRelationshipViewModel> datasetRelationshipModels = await _datasetRepositoryPolicy.ExecuteAsync(() => _datasetRepository.GetCurrentRelationshipsBySpecificationId(specificationId)); if (!datasetRelationshipModels.IsNullOrEmpty()) { ConcurrentBag <DatasetDefinition> datasetDefinitions = new ConcurrentBag <DatasetDefinition>(); IList <Task> definitionTasks = new List <Task>(); IEnumerable <string> definitionIds = datasetRelationshipModels.Select(m => m.Definition?.Id); foreach (string definitionId in definitionIds) { Task task = Task.Run(async() => { DatasetDefinition datasetDefinition = await _datasetRepositoryPolicy.ExecuteAsync(() => _datasetRepository.GetDatasetDefinitionById(definitionId)); if (datasetDefinition != null) { datasetDefinitions.Add(datasetDefinition); } }); definitionTasks.Add(task); } await TaskHelper.WhenAllAndThrow(definitionTasks.ToArray()); foreach (DatasetSpecificationRelationshipViewModel datasetRelationshipModel in datasetRelationshipModels) { buildproject.DatasetRelationships.Add(new DatasetRelationshipSummary { DatasetDefinitionId = datasetRelationshipModel.Definition.Id, DatasetId = datasetRelationshipModel.DatasetId, Relationship = new Common.Models.Reference(datasetRelationshipModel.Id, datasetRelationshipModel.Name), DefinesScope = datasetRelationshipModel.IsProviderData, Id = datasetRelationshipModel.Id, Name = datasetRelationshipModel.Name, DatasetDefinition = datasetDefinitions.FirstOrDefault(m => m.Id == datasetRelationshipModel.Definition.Id) }); } } return(buildproject); }
public async Task ResetScenarioForFieldDefinitionChanges(IEnumerable <DatasetSpecificationRelationshipViewModel> relationships, string specificationId, IEnumerable <string> currentFieldDefinitionNames) { Guard.ArgumentNotNull(relationships, nameof(relationships)); Guard.IsNullOrWhiteSpace(specificationId, nameof(specificationId)); Guard.ArgumentNotNull(currentFieldDefinitionNames, nameof(currentFieldDefinitionNames)); IEnumerable <TestScenario> scenarios = await _scenariosRepositoryPolicy.ExecuteAsync(() => _scenariosRepository.GetTestScenariosBySpecificationId(specificationId)); if (scenarios.IsNullOrEmpty()) { _logger.Information($"No scenarios found for specification id '{specificationId}'"); return; } List <string> fieldIdentifiers = new List <string>(); foreach (DatasetSpecificationRelationshipViewModel datasetSpecificationRelationshipViewModel in relationships) { fieldIdentifiers.AddRange(currentFieldDefinitionNames.Select(m => $"dataset {datasetSpecificationRelationshipViewModel.Name} field {VisualBasicTypeGenerator.GenerateIdentifier(m)}")); } IEnumerable <TestScenario> scenariosToUpdate = scenarios.Where(m => SourceCodeHelpers.CodeContainsFullyQualifiedDatasetFieldIdentifier(m.Current.Gherkin.RemoveAllQuotes(), fieldIdentifiers)); if (scenariosToUpdate.IsNullOrEmpty()) { _logger.Information($"No test scenarios required resetting for specification id '{specificationId}'"); return; } const string reasonForCommenting = "The dataset definition referenced by this scenario/spec has been updated and subsequently the code has been commented out"; foreach (TestScenario scenario in scenariosToUpdate) { string gherkin = scenario.Current.Gherkin; string updatedGherkin = SourceCodeHelpers.CommentOutCode(gherkin, reasonForCommenting, commentSymbol: "#"); await SaveVersion(scenario, updatedGherkin); } }
public async Task <IActionResult> UpdateBuildProjectRelationships(HttpRequest request) { request.Query.TryGetValue("specificationId", out Microsoft.Extensions.Primitives.StringValues specId); string specificationId = specId.FirstOrDefault(); if (string.IsNullOrWhiteSpace(specificationId)) { _logger.Error("No specification Id was provided to UpdateBuildProjectRelationships"); return(new BadRequestObjectResult("Null or empty specification Id provided")); } string json = await request.GetRawBodyStringAsync(); DatasetRelationshipSummary relationship = JsonConvert.DeserializeObject <DatasetRelationshipSummary>(json); if (relationship == null) { _logger.Error("A null relationship message was provided to UpdateBuildProjectRelationships"); return(new BadRequestObjectResult("Null relationship provided")); } BuildProject buildProject = await GetBuildProjectForSpecificationId(specificationId); IEnumerable <Models.Calcs.Calculation> calculations = await _calculationsRepository.GetCalculationsBySpecificationId(specificationId); buildProject.Build = _sourceCodeService.Compile(buildProject, calculations ?? Enumerable.Empty <Models.Calcs.Calculation>()); if (!_featureToggle.IsDynamicBuildProjectEnabled()) { await _buildProjectsRepositoryPolicy.ExecuteAsync(() => _buildProjectsRepository.UpdateBuildProject(buildProject)); } await _sourceCodeService.SaveAssembly(buildProject); return(new OkObjectResult(buildProject)); }
public async Task OnSpecificationUpdate(Message message) { SpecificationVersionComparisonModel versionComparison = message.GetPayloadAsInstanceOf <SpecificationVersionComparisonModel>(); if (versionComparison == null || versionComparison.Current == null || versionComparison.Previous == null) { _logger.Error($"A null versionComparison was provided to users {nameof(OnSpecificationUpdate)}"); throw new InvalidModelException(nameof(SpecificationVersionComparisonModel), new[] { "Null or invalid model provided" }); } string specificationId = versionComparison.Id; if (string.IsNullOrWhiteSpace(specificationId)) { _logger.Error($"A null specificationId was provided to users {nameof(OnSpecificationUpdate)} in model"); throw new InvalidModelException(nameof(SpecificationVersionComparisonModel), new[] { "Null or invalid specificationId on model" }); } IEnumerable <string> previousFundingStreams = versionComparison.Previous.FundingStreams.OrderBy(c => c.Id).Select(f => f.Id); IEnumerable <string> currentFundingStreams = versionComparison.Current.FundingStreams.OrderBy(c => c.Id).Select(f => f.Id); if (!previousFundingStreams.SequenceEqual(currentFundingStreams)) { _logger.Information("Found changed funding streams for specification '{SpecificationId}' Previous: {PreviousFundingStreams} Current {CurrentFundingStreams}", specificationId, previousFundingStreams, currentFundingStreams); Dictionary <string, bool> userIds = new Dictionary <string, bool>(); IEnumerable <string> allFundingStreamIds = previousFundingStreams.Union(currentFundingStreams); foreach (string fundingStreamId in allFundingStreamIds) { IEnumerable <FundingStreamPermission> userPermissions = await _userRepositoryPolicy.ExecuteAsync(() => _userRepository.GetUsersWithFundingStreamPermissions(fundingStreamId)); foreach (FundingStreamPermission permission in userPermissions) { if (!userIds.ContainsKey(permission.UserId)) { userIds.Add(permission.UserId, true); } } } foreach (string userId in userIds.Keys) { _logger.Information("Clearing effective permissions for userId '{UserId}' for specification '{SpecificationId}'", userId, specificationId); await _cacheProviderPolicy.ExecuteAsync(() => _cacheProvider.DeleteHashKey <EffectiveSpecificationPermission>($"{CacheKeys.EffectivePermissions}:{userId}", specificationId)); } } else { _logger.Information("No funding streams have changed for specification '{SpecificationId}' which require effective permission clearing.", specificationId); } }
public async Task ProcessChanges(Message message) { Guard.ArgumentNotNull(message, nameof(message)); if (!_featureToggle.IsProcessDatasetDefinitionFieldChangesEnabled()) { return; } DatasetDefinitionChanges datasetDefinitionChanges = message.GetPayloadAsInstanceOf <DatasetDefinitionChanges>(); _logger.Information("Checking for dataset definition changes before proceeding"); if (datasetDefinitionChanges == null) { throw new NonRetriableException("Message does not contain a valid dataset definition change model"); } if (!datasetDefinitionChanges.HasChanges) { _logger.Information($"No dataset definition field changes for definition id '{datasetDefinitionChanges.Id}'"); return; } IEnumerable <FieldDefinitionChanges> fieldChanges = datasetDefinitionChanges.TableDefinitionChanges.SelectMany(m => m.FieldChanges); if (fieldChanges.IsNullOrEmpty()) { _logger.Information($"No dataset definition field changes for definition id '{datasetDefinitionChanges.Id}'"); return; } IEnumerable <string> relationshipSpecificationIds = await _datasetRepositoryPolicy.ExecuteAsync(() => _datasetRepository.GetRelationshipSpecificationIdsByDatasetDefinitionId(datasetDefinitionChanges.Id)); if (relationshipSpecificationIds.IsNullOrEmpty()) { _logger.Information($"No dataset definition specification relationships exists for definition id '{datasetDefinitionChanges.Id}'"); return; } await ProcessFieldChanges(datasetDefinitionChanges.Id, fieldChanges, relationshipSpecificationIds); }
private async Task <Job> SendInstructAllocationsToJobService(string specificationId, Reference user, Trigger trigger, string correlationId, bool generateAggregations = false) { JobCreateModel job = new JobCreateModel { InvokerUserDisplayName = user.Name, InvokerUserId = user.Id, JobDefinitionId = generateAggregations ? JobConstants.DefinitionNames.CreateInstructGenerateAggregationsAllocationJob : JobConstants.DefinitionNames.CreateInstructAllocationJob, SpecificationId = specificationId, Properties = new Dictionary <string, string> { { "specification-id", specificationId }, { "ignore-save-provider-results", "true" } }, Trigger = trigger, CorrelationId = correlationId }; return(await _jobsApiClientPolicy.ExecuteAsync(() => _jobsApiClient.CreateJob(job))); }
public async Task <IActionResult> GetFundingStreamPermissionsForUser(string userId, HttpRequest request) { if (string.IsNullOrWhiteSpace(userId)) { return(new BadRequestObjectResult($"{nameof(userId)} is null or empty")); } List <FundingStreamPermissionCurrent> results = new List <FundingStreamPermissionCurrent>(); IEnumerable <FundingStreamPermission> permissions = await _userRepositoryPolicy.ExecuteAsync(() => _userRepository.GetFundingStreamPermissions(userId)); if (permissions.AnyWithNullCheck()) { foreach (FundingStreamPermission permission in permissions) { results.Add(_mapper.Map <FundingStreamPermissionCurrent>(permission)); } } return(new OkObjectResult(results)); }
public async Task <IActionResult> AssignDatasourceVersionToRelationship(HttpRequest request) { string json = await request.GetRawBodyStringAsync(); AssignDatasourceModel model = JsonConvert.DeserializeObject <AssignDatasourceModel>(json); if (model == null) { _logger.Error("Null AssignDatasourceModel was provided to AssignDatasourceVersionToRelationship"); return(new BadRequestObjectResult("Null AssignDatasourceModel was provided")); } Dataset dataset = await _datasetRepository.GetDatasetByDatasetId(model.DatasetId); if (dataset == null) { _logger.Error($"Dataset not found for dataset id: {model.DatasetId}"); return(new StatusCodeResult(412)); } DefinitionSpecificationRelationship relationship = await _datasetRepository.GetDefinitionSpecificationRelationshipById(model.RelationshipId); if (relationship == null) { _logger.Error($"Relationship not found for relationship id: {model.RelationshipId}"); return(new StatusCodeResult(412)); } relationship.DatasetVersion = new DatasetRelationshipVersion { Id = model.DatasetId, Version = model.Version }; HttpStatusCode statusCode = await _datasetRepository.UpdateDefinitionSpecificationRelationship(relationship); if (!statusCode.IsSuccess()) { _logger.Error($"Failed to assign data source to relationship : {model.RelationshipId} with status code {statusCode.ToString()}"); return(new StatusCodeResult((int)statusCode)); } Reference user = request.GetUser(); Trigger trigger = new Trigger { EntityId = dataset.Id, EntityType = nameof(Dataset), Message = $"Mapping dataset: '{dataset.Id}'" }; string correlationId = request.GetCorrelationId(); JobCreateModel job = new JobCreateModel { InvokerUserDisplayName = user.Name, InvokerUserId = user.Id, JobDefinitionId = JobConstants.DefinitionNames.MapDatasetJob, MessageBody = JsonConvert.SerializeObject(dataset), Properties = new Dictionary <string, string> { { "specification-id", relationship.Specification.Id }, { "relationship-id", relationship.Id } }, SpecificationId = relationship.Specification.Id, Trigger = trigger, CorrelationId = correlationId }; await _jobsApiClientPolicy.ExecuteAsync(() => _jobsApiClient.CreateJob(job)); return(new NoContentResult()); }
public async Task ProcessDeadLetteredMessage(Message message) { Guard.ArgumentNotNull(message, nameof(message)); if (!message.UserProperties.ContainsKey("jobId")) { _logger.Error("Missing job id from dead lettered message"); return; } string jobId = message.UserProperties["jobId"].ToString(); Common.ApiClient.Jobs.Models.JobLogUpdateModel jobLogUpdateModel = new Common.ApiClient.Jobs.Models.JobLogUpdateModel { CompletedSuccessfully = false, Outcome = $"The job has exceeded its maximum retry count and failed to complete successfully" }; try { ApiResponse <Common.ApiClient.Jobs.Models.JobLog> jobLogResponse = await _jobsApiClientPolicy.ExecuteAsync(() => _jobsApiClient.AddJobLog(jobId, jobLogUpdateModel)); if (jobLogResponse == null || jobLogResponse.Content == null) { _logger.Error($"Failed to add a job log for job id '{jobId}'"); } else { _logger.Information($"A new job log was added to inform of a dead lettered message with job log id '{jobLogResponse.Content.Id}' on job with id '{jobId}'"); } } catch (Exception exception) { _logger.Error(exception, $"Failed to add a job log for job id '{jobId}'"); } }
public async Task <IActionResult> SaveVersion(HttpRequest request) { string json = await request.GetRawBodyStringAsync(); CreateNewTestScenarioVersion scenarioVersion = JsonConvert.DeserializeObject <CreateNewTestScenarioVersion>(json); 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; SpecificationSummary specification = await _specificationsRepository.GetSpecificationSummaryById(scenarioVersion.SpecificationId); if (specification == null) { _logger.Error($"Unable to find a specification for specification id : {scenarioVersion.SpecificationId}"); return(new StatusCodeResult(412)); } Reference user = request.GetUserOrDefault(); 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.ToString()}"); 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 <Models.Calcs.CalculationCurrentVersion> calculations = await _calcsRepositoryPolicy.ExecuteAsync(() => _calcsRepository.GetCurrentCalculationsBySpecificationId(specification.Id)); if (calculations.IsNullOrEmpty()) { _logger.Information($"No calculations found to test for specification id: '{specification.Id}'"); } else { string correlationId = request.GetCorrelationId(); try { Trigger trigger = new Trigger { EntityId = testScenario.Id, EntityType = nameof(TestScenario), Message = $"Saving test scenario: '{testScenario.Id}'" }; bool generateCalculationAggregations = SourceCodeHelpers.HasCalculationAggregateFunctionParameters(calculations.Select(m => m.SourceCode)); 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 ReIndexCalculationResults(Message message) { Guard.ArgumentNotNull(message, nameof(message)); Reference user = message.GetUserDetails(); _logger.Information($"{nameof(ReIndexCalculationResults)} initiated by: '{user.Name}'"); IEnumerable <SpecificationSummary> specifications = await _specificationsRepositoryPolicy.ExecuteAsync(() => _specificationsRepository.GetSpecificationSummaries()); foreach (SpecificationSummary specification in specifications) { await _resultsRepositoryPolicy.ExecuteAsync(() => _resultsRepository.ProviderResultsBatchProcessing(specification.Id, async(x) => { IList <ProviderCalculationResultsIndex> results = new List <ProviderCalculationResultsIndex>(); foreach (ProviderResult providerResult in x) { if (!providerResult.CalculationResults.IsNullOrEmpty()) { ProviderCalculationResultsIndex calculationResult = new ProviderCalculationResultsIndex { SpecificationId = providerResult.SpecificationId, SpecificationName = specification?.Name, ProviderId = providerResult.Provider?.Id, ProviderName = providerResult.Provider?.Name, ProviderType = providerResult.Provider?.ProviderType, ProviderSubType = providerResult.Provider?.ProviderSubType, LocalAuthority = providerResult.Provider?.Authority, LastUpdatedDate = providerResult.CreatedAt, UKPRN = providerResult.Provider?.UKPRN, URN = providerResult.Provider?.URN, UPIN = providerResult.Provider?.UPIN, EstablishmentNumber = providerResult.Provider?.EstablishmentNumber, OpenDate = providerResult.Provider?.DateOpened, CalculationId = providerResult.CalculationResults.Select(m => m.Calculation.Id).ToArraySafe(), CalculationName = providerResult.CalculationResults.Select(m => m.Calculation.Name).ToArraySafe(), CalculationResult = providerResult.CalculationResults.Select(m => m.Value.HasValue ? m.Value.ToString() : "null").ToArraySafe() }; if (_featureToggle.IsExceptionMessagesEnabled()) { calculationResult.CalculationException = providerResult.CalculationResults .Where(m => !string.IsNullOrWhiteSpace(m.ExceptionType)) .Select(e => e.Calculation.Id) .ToArraySafe(); calculationResult.CalculationExceptionType = providerResult.CalculationResults .Select(m => m.ExceptionType ?? string.Empty) .ToArraySafe(); calculationResult.CalculationExceptionMessage = providerResult.CalculationResults .Select(m => m.ExceptionMessage ?? string.Empty) .ToArraySafe(); } results.Add(calculationResult); } } IEnumerable <IndexError> errors = await _resultsSearchRepositoryPolicy.ExecuteAsync(() => _providerCalculationResultsSearchRepository.Index(results)); if (errors.Any()) { string errorMessage = $"Failed to index calculation provider result documents with errors: { string.Join(";", errors.Select(m => m.ErrorMessage)) }"; _logger.Error(errorMessage); throw new RetriableException(errorMessage); } })); } }
private async Task ProcessFieldChanges(string datasetDefinitionId, IEnumerable <FieldDefinitionChanges> fieldChanges, IEnumerable <string> relationshipSpecificationIds) { Guard.IsNullOrWhiteSpace(datasetDefinitionId, nameof(datasetDefinitionId)); Guard.ArgumentNotNull(fieldChanges, nameof(fieldChanges)); Guard.ArgumentNotNull(relationshipSpecificationIds, nameof(relationshipSpecificationIds)); IEnumerable <IGrouping <string, FieldDefinitionChanges> > groupedFieldChanges = fieldChanges.GroupBy(f => f.FieldDefinition.Id); IList <FieldDefinitionChanges> fieldDefinitionChanges = new List <FieldDefinitionChanges>(); bool shouldResetCalculation = false; foreach (IGrouping <string, FieldDefinitionChanges> grouping in groupedFieldChanges) { FieldDefinitionChanges fieldDefinitionChange = grouping.FirstOrDefault(m => m.ChangeTypes.Any( c => c == FieldDefinitionChangeType.FieldName) || m.RequiresRemap); if (fieldDefinitionChange != null) { fieldDefinitionChanges.Add(fieldDefinitionChange); } shouldResetCalculation = true; } if (!shouldResetCalculation) { return; } foreach (string specificationId in relationshipSpecificationIds) { IEnumerable <DatasetSpecificationRelationshipViewModel> relationships = await _datasetRepositoryPolicy.ExecuteAsync(() => _datasetRepository.GetCurrentRelationshipsBySpecificationIdAndDatasetDefinitionId(specificationId, datasetDefinitionId)); if (relationships.IsNullOrEmpty()) { throw new RetriableException($"No relationships found for specificationId '{specificationId}' and dataset definition id '{datasetDefinitionId}'"); } IEnumerable <Calculation> calculations = (await _calculationsRepositoryPolicy.ExecuteAsync(() => _calculationsRepository.GetCalculationsBySpecificationId(specificationId))).ToList(); IEnumerable <string> aggregateParameters = calculations.SelectMany(m => SourceCodeHelpers.GetDatasetAggregateFunctionParameters(m.Current.SourceCode)); HashSet <string> fieldNames = new HashSet <string>(); foreach (FieldDefinitionChanges changes in fieldDefinitionChanges) { //Check if only aggregable changes if (!changes.ChangeTypes.Contains(FieldDefinitionChangeType.FieldType) && !changes.ChangeTypes.Contains(FieldDefinitionChangeType.FieldName)) { foreach (DatasetSpecificationRelationshipViewModel datasetSpecificationRelationshipViewModel in relationships) { if (aggregateParameters.Contains($"Datasets.{VisualBasicTypeGenerator.GenerateIdentifier(datasetSpecificationRelationshipViewModel.Name)}.{VisualBasicTypeGenerator.GenerateIdentifier(changes.ExistingFieldDefinition.Name)}")) { fieldNames.Add(changes.ExistingFieldDefinition.Name); } } } else { fieldNames.Add(changes.ExistingFieldDefinition.Name); } } if (fieldNames.Any()) { await _calculationService.ResetCalculationForFieldDefinitionChanges(relationships, specificationId, fieldNames); } } }
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}'"); } }
public async Task <IActionResult> GetProviderById(HttpRequest request) { string providerId = request.GetParameter("providerId"); if (string.IsNullOrWhiteSpace(providerId)) { _logger.Error("No provider Id was provided to GetProviderById"); return(new BadRequestObjectResult("Null or empty provider Id provided")); } ProviderIndex provider = await _resultsRepositoryPolicy.ExecuteAsync(() => _searchRepository.SearchById(providerId, IdFieldOverride: "providerId")); if (provider == null) { return(new NotFoundResult()); } return(new OkObjectResult(provider)); }
public async Task <IActionResult> GetEffectivePermissionsForUser(string userId, string specificationId, HttpRequest request) { 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 { SpecificationSummary specification = await _specificationsRepositoryPolicy.ExecuteAsync(() => _specificationsRepository.GetSpecificationSummaryById(specificationId)); if (specification == null) { return(new PreconditionFailedResult("Specification not found")); } 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, CanPublishFunding = false, CanAdministerFundingStream = false, CanApproveSpecification = false, CanCreateQaTests = false, CanDeleteCalculations = false, CanDeleteSpecification = false, CanDeleteQaTests = false, CanEditQaTests = false, CanRefreshFunding = 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)); } }
public async Task <IActionResult> ReIndexCalculationProviderResults() { IEnumerable <DocumentEntity <ProviderResult> > providerResults = await _resultsRepositoryPolicy.ExecuteAsync(() => _resultsRepository.GetAllProviderResults()); IList <ProviderCalculationResultsIndex> searchItems = new List <ProviderCalculationResultsIndex>(); Dictionary <string, SpecificationSummary> specifications = new Dictionary <string, SpecificationSummary>(); foreach (DocumentEntity <ProviderResult> documentEntity in providerResults) { ProviderResult providerResult = documentEntity.Content; foreach (CalculationResult calculationResult in providerResult.CalculationResults) { SpecificationSummary specificationSummary = null; if (!specifications.ContainsKey(providerResult.SpecificationId)) { specificationSummary = await _specificationsRepositoryPolicy.ExecuteAsync(() => _specificationsRepository.GetSpecificationSummaryById(providerResult.SpecificationId)); if (specificationSummary == null) { throw new InvalidOperationException($"Specification Summary returned null for specification ID '{providerResult.SpecificationId}'"); } specifications.Add(providerResult.SpecificationId, specificationSummary); } else { specificationSummary = specifications[providerResult.SpecificationId]; } ProviderCalculationResultsIndex searchItem = new ProviderCalculationResultsIndex { SpecificationId = providerResult.SpecificationId, SpecificationName = specificationSummary?.Name, CalculationName = providerResult.CalculationResults.Select(x => x.Calculation.Name).ToArraySafe(), CalculationId = providerResult.CalculationResults.Select(x => x.Calculation.Id).ToArraySafe(), ProviderId = providerResult.Provider.Id, ProviderName = providerResult.Provider.Name, ProviderType = providerResult.Provider.ProviderType, ProviderSubType = providerResult.Provider.ProviderSubType, LocalAuthority = providerResult.Provider.Authority, LastUpdatedDate = documentEntity.UpdatedAt, UKPRN = providerResult.Provider.UKPRN, URN = providerResult.Provider.URN, UPIN = providerResult.Provider.UPIN, EstablishmentNumber = providerResult.Provider.EstablishmentNumber, OpenDate = providerResult.Provider.DateOpened, CalculationResult = providerResult.CalculationResults.Select(m => m.Value.HasValue ? m.Value.ToString() : "null").ToArraySafe() }; if (_featureToggle.IsExceptionMessagesEnabled()) { searchItem.CalculationException = providerResult.CalculationResults .Where(m => !string.IsNullOrWhiteSpace(m.ExceptionType)) .Select(e => e.Calculation.Id) .ToArraySafe(); searchItem.CalculationExceptionType = providerResult.CalculationResults .Select(m => m.ExceptionType ?? string.Empty) .ToArraySafe(); searchItem.CalculationExceptionMessage = providerResult.CalculationResults .Select(m => m.ExceptionMessage ?? string.Empty) .ToArraySafe(); } searchItems.Add(searchItem); } } const int partitionSize = 500; for (int i = 0; i < searchItems.Count; i += partitionSize) { IEnumerable <ProviderCalculationResultsIndex> partitionedResults = searchItems.Skip(i).Take(partitionSize); IEnumerable <IndexError> errors = await _resultsSearchRepositoryPolicy.ExecuteAsync(() => _calculationProviderResultsSearchRepository.Index(partitionedResults)); if (errors.Any()) { _logger.Error($"Failed to index calculation provider result documents with errors: { string.Join(";", errors.Select(m => m.ErrorMessage)) }"); return(new InternalServerErrorResult(null)); } } return(new NoContentResult()); }
public async Task <IActionResult> AddJobLog(string jobId, JobLogUpdateModel jobLogUpdateModel) { Guard.IsNullOrWhiteSpace(jobId, nameof(jobId)); Guard.ArgumentNotNull(jobLogUpdateModel, nameof(jobLogUpdateModel)); Job job = await _jobsRepositoryPolicy.ExecuteAsync(() => _jobRepository.GetJobById(jobId)); if (job == null) { _logger.Error($"A job could not be found for job id: '{jobId}'"); return(new NotFoundObjectResult($"A job could not be found for job id: '{jobId}'")); } bool saveJob = false; if (jobLogUpdateModel.CompletedSuccessfully.HasValue) { job.Completed = DateTimeOffset.UtcNow; job.RunningStatus = RunningStatus.Completed; job.CompletionStatus = jobLogUpdateModel.CompletedSuccessfully.Value ? CompletionStatus.Succeeded : CompletionStatus.Failed; job.Outcome = jobLogUpdateModel.Outcome; saveJob = true; } else { if (job.RunningStatus != RunningStatus.InProgress) { job.RunningStatus = RunningStatus.InProgress; saveJob = true; } } if (saveJob) { HttpStatusCode statusCode = await _jobsRepositoryPolicy.ExecuteAsync(() => _jobRepository.UpdateJob(job)); if (!statusCode.IsSuccess()) { _logger.Error($"Failed to update job id: '{jobId}' with status code '{(int)statusCode}'"); return(new InternalServerErrorResult($"Failed to update job id: '{jobId}' with status code '{(int)statusCode}'")); } } JobLog jobLog = new JobLog { Id = Guid.NewGuid().ToString(), JobId = jobId, ItemsProcessed = jobLogUpdateModel.ItemsProcessed, ItemsSucceeded = jobLogUpdateModel.ItemsSucceeded, ItemsFailed = jobLogUpdateModel.ItemsFailed, Outcome = jobLogUpdateModel.Outcome, CompletedSuccessfully = jobLogUpdateModel.CompletedSuccessfully, Timestamp = DateTimeOffset.UtcNow }; HttpStatusCode createJobLogStatus = await _jobsRepositoryPolicy.ExecuteAsync(() => _jobRepository.CreateJobLog(jobLog)); if (!createJobLogStatus.IsSuccess()) { _logger.Error($"Failed to create a job log for job id: '{jobId}'"); throw new Exception($"Failed to create a job log for job id: '{jobId}'"); } await SendJobLogNotification(job, jobLog); return(new OkObjectResult(jobLog)); }
public async Task <HttpStatusCode> SaveTestProviderResults(IEnumerable <TestScenarioResult> testResults, IEnumerable <ProviderResult> providerResults) { Guard.ArgumentNotNull(testResults, nameof(testResults)); if (!testResults.Any()) { return(HttpStatusCode.NotModified); } Stopwatch stopwatch = Stopwatch.StartNew(); Task <HttpStatusCode> repoUpdateTask = _testResultsPolicy.ExecuteAsync(() => _testResultsRepository.SaveTestProviderResults(testResults)); IEnumerable <TestScenarioResultIndex> searchIndexItems = _mapper.Map <IEnumerable <TestScenarioResultIndex> >(testResults); foreach (TestScenarioResultIndex testScenarioResult in searchIndexItems) { ProviderResult providerResult = providerResults.FirstOrDefault(m => m.Provider.Id == testScenarioResult.ProviderId); if (providerResult != null) { testScenarioResult.EstablishmentNumber = providerResult.Provider.EstablishmentNumber; testScenarioResult.UKPRN = providerResult.Provider.UKPRN; testScenarioResult.UPIN = providerResult.Provider.UPIN; testScenarioResult.URN = providerResult.Provider.URN; testScenarioResult.LocalAuthority = providerResult.Provider.Authority; testScenarioResult.ProviderType = providerResult.Provider.ProviderType; testScenarioResult.ProviderSubType = providerResult.Provider.ProviderSubType; testScenarioResult.OpenDate = providerResult.Provider.DateOpened; } } Task <IEnumerable <IndexError> > searchUpdateTask = _testResultsSearchPolicy.ExecuteAsync(() => _searchRepository.Index(searchIndexItems)); await TaskHelper.WhenAllAndThrow(searchUpdateTask, repoUpdateTask); IEnumerable <IndexError> indexErrors = searchUpdateTask.Result; HttpStatusCode repositoryUpdateStatusCode = repoUpdateTask.Result; stopwatch.Stop(); if (!indexErrors.Any() && (repoUpdateTask.Result == HttpStatusCode.Created || repoUpdateTask.Result == HttpStatusCode.NotModified)) { _telemetry.TrackEvent("UpdateTestScenario", new Dictionary <string, string>() { { "SpecificationId", testResults.First().Specification.Id } }, new Dictionary <string, double>() { { "update-testscenario-elapsedMilliseconds", stopwatch.ElapsedMilliseconds }, { "update-testscenario-recordsUpdated", testResults.Count() }, } ); return(HttpStatusCode.Created); } foreach (IndexError indexError in indexErrors) { _logger.Error($"SaveTestProviderResults index error {{key}}: {indexError.ErrorMessage}", indexError.Key); } if (repositoryUpdateStatusCode == default(HttpStatusCode)) { _logger.Error("SaveTestProviderResults repository failed with no response code"); } else { _logger.Error("SaveTestProviderResults repository failed with response code: {repositoryUpdateStatusCode}", repositoryUpdateStatusCode); } return(HttpStatusCode.InternalServerError); }
public async Task <IActionResult> UpdatePermissionForUser(string userId, string fundingStreamId, HttpRequest request) { if (string.IsNullOrWhiteSpace(userId)) { return(new BadRequestObjectResult($"{nameof(userId)} is empty or null")); } if (string.IsNullOrWhiteSpace(fundingStreamId)) { return(new BadRequestObjectResult($"{nameof(fundingStreamId)} is empty or null")); } Guard.ArgumentNotNull(request, nameof(request)); User user = await _userRepositoryPolicy.ExecuteAsync(() => _userRepository.GetUserById(userId)); if (user == null) { return(new PreconditionFailedResult("userId not found")); } string json = await request.GetRawBodyStringAsync(); FundingStreamPermissionUpdateModel updateModel = JsonConvert.DeserializeObject <FundingStreamPermissionUpdateModel>(json); FundingStreamPermission existingPermissions = await _userRepositoryPolicy.ExecuteAsync(() => _userRepository.GetFundingStreamPermission(userId, fundingStreamId)); FundingStreamPermission newPermissions = _mapper.Map <FundingStreamPermissionUpdateModel, FundingStreamPermission>(updateModel); newPermissions.FundingStreamId = fundingStreamId; newPermissions.UserId = userId; if (existingPermissions == null || !existingPermissions.HasSamePermissions(newPermissions)) { HttpStatusCode saveResult = await _userRepositoryPolicy.ExecuteAsync(() => _userRepository.UpdateFundingStreamPermission(newPermissions)); if (saveResult != HttpStatusCode.OK && saveResult != HttpStatusCode.Created) { return(new InternalServerErrorResult($"Saving funding stream permission to repository returned '{saveResult}'")); } Reference author = request.GetUserOrDefault(); FundingStreamPermissionVersion version = new FundingStreamPermissionVersion() { Author = author, Permission = newPermissions, PublishStatus = Models.Versioning.PublishStatus.Updated, Date = DateTimeOffset.Now, UserId = userId, }; version.Version = await _fundingStreamPermissionVersionRepositoryPolicy.ExecuteAsync(() => _fundingStreamPermissionVersionRepository.GetNextVersionNumber(version, partitionKeyId: userId)); await _fundingStreamPermissionVersionRepositoryPolicy.ExecuteAsync(() => _fundingStreamPermissionVersionRepository.SaveVersion(version, userId)); await ClearEffectivePermissionsForUser(userId); } return(new OkObjectResult(_mapper.Map <FundingStreamPermissionCurrent>(newPermissions))); }