public async Task <IActionResult> UpdateBuildProjectRelationships(string specificationId, DatasetRelationshipSummary relationship) { if (string.IsNullOrWhiteSpace(specificationId)) { _logger.Error("No specification Id was provided to UpdateBuildProjectRelationships"); return(new BadRequestObjectResult("Null or empty specification Id provided")); } 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 <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 <IEnumerable <TypeInformation> > BuildCodeContextForSpecification(string specificationId) { Guard.IsNullOrWhiteSpace(specificationId, nameof(specificationId)); _logger.Information("Generating code context for {specificationId}", specificationId); BuildProject buildProject = await _buildProjects.GetBuildProjectForSpecificationId(specificationId); IEnumerable <Calculation> calculations = await _calculationsResilience.ExecuteAsync(() => _calculations.GetCalculationsBySpecificationId(specificationId)); buildProject.Build = _compiler.Compile(buildProject, calculations ?? Enumerable.Empty <Calculation>()); Guard.ArgumentNotNull(buildProject.Build, nameof(buildProject.Build)); return(await _compiler.GetTypeInformation(buildProject)); }
public async Task EditCalculationStatus_GivenNewStatusOfUpdated_UpdatesSearchReturnsOK() { //Arrange EditStatusModel CalculationEditStatusModel = new EditStatusModel { PublishStatus = PublishStatus.Updated }; string json = JsonConvert.SerializeObject(CalculationEditStatusModel); byte[] byteArray = Encoding.UTF8.GetBytes(json); MemoryStream stream = new MemoryStream(byteArray); HttpContext context = Substitute.For <HttpContext>(); HttpRequest request = Substitute.For <HttpRequest>(); IQueryCollection queryStringValues = new QueryCollection(new Dictionary <string, StringValues> { { "calculationId", new StringValues(CalculationId) }, }); request .Query .Returns(queryStringValues); request .Body .Returns(stream); request .HttpContext .Returns(context); ILogger logger = CreateLogger(); Calculation calculation = CreateCalculation(); calculation.Current.PublishStatus = PublishStatus.Approved; CalculationVersion calculationVersion = calculation.Current.Clone() as CalculationVersion; calculationVersion.PublishStatus = PublishStatus.Updated; ICalculationsRepository CalculationsRepository = CreateCalculationsRepository(); CalculationsRepository .GetCalculationById(Arg.Is(CalculationId)) .Returns(calculation); CalculationsRepository .UpdateCalculation(Arg.Any <Calculation>()) .Returns(HttpStatusCode.OK); ISearchRepository <CalculationIndex> searchRepository = CreateSearchRepository(); Models.Specs.SpecificationSummary specificationSummary = new Models.Specs.SpecificationSummary(); ISpecificationRepository specificationRepository = CreateSpecificationRepository(); specificationRepository .GetSpecificationSummaryById(Arg.Is(calculation.SpecificationId)) .Returns(specificationSummary); IVersionRepository <CalculationVersion> versionRepository = CreateCalculationVersionRepository(); versionRepository .CreateVersion(Arg.Any <CalculationVersion>(), Arg.Any <CalculationVersion>()) .Returns(calculationVersion); Build build = new Build { SourceFiles = new List <SourceFile> { new SourceFile() } }; BuildProject buildProject = new BuildProject(); IBuildProjectsService buildProjectsService = CreateBuildProjectsService(); buildProjectsService .GetBuildProjectForSpecificationId(Arg.Is(calculation.SpecificationId)) .Returns(buildProject); ISourceCodeService sourceCodeService = CreateSourceCodeService(); sourceCodeService .Compile(Arg.Any <BuildProject>(), Arg.Any <IEnumerable <Models.Calcs.Calculation> >(), Arg.Any <CompilerOptions>()) .Returns(build); CalculationService service = CreateCalculationService( logger: logger, calculationsRepository: CalculationsRepository, searchRepository: searchRepository, specificationRepository: specificationRepository, calculationVersionRepository: versionRepository, sourceCodeService: sourceCodeService, buildProjectsService: buildProjectsService); //Act IActionResult result = await service.UpdateCalculationStatus(request); //Arrange result .Should() .BeOfType <OkObjectResult>() .Which .Value .Should() .BeOfType <PublishStatusResultModel>() .Which .PublishStatus .Should() .Be(PublishStatus.Updated); calculation .Current .PublishStatus .Should() .Be(PublishStatus.Updated); await searchRepository .Received(1) .Index(Arg.Is <IEnumerable <CalculationIndex> >(m => m.First().Status == "Updated")); }
public async Task ResetCalculationForFieldDefinitionChanges_GivenCalculationRequiresReset_UpdatesCalculationsAndDeletesAssembly() { //Arrange const string specificationId = "spec-id"; IEnumerable <DatasetSpecificationRelationshipViewModel> relationships = new[] { new DatasetSpecificationRelationshipViewModel { Name = "Test Name" } }; IEnumerable <string> currentFieldDefinitionNames = new[] { "Test Field" }; ILogger logger = CreateLogger(); CalculationVersion calculationVersion = new CalculationVersion { SourceCode = "return Datasets.TestName.TestField", Date = DateTimeOffset.Now }; IEnumerable <Calculation> calculations = new[] { new Calculation { Current = calculationVersion, SpecificationId = specificationId, CalculationSpecification = new Reference("calc-spac-id", "calc spec name"), FundingPeriod = new Reference("fp1", "fp 1"), FundingStream = new Reference("fs1", "fs 1"), Policies = new List <Reference> { new Reference { Id = "policy-1", Name = "policy 1" } } } }; IEnumerable <Models.Specs.Calculation> calculationSpecifications = new[] { new Models.Specs.Calculation { Id = "calc-spec-id" } }; BuildProject buildProject = new BuildProject(); Build build = new Build { SourceFiles = new List <SourceFile>() }; Models.Specs.SpecificationSummary specificationSummary = new Models.Specs.SpecificationSummary(); ICalculationsRepository calculationsRepository = CreateCalculationsRepository(); calculationsRepository .GetCalculationsBySpecificationId(Arg.Is(specificationId)) .Returns(calculations); calculationsRepository .UpdateCalculation(Arg.Any <Calculation>()) .Returns(HttpStatusCode.OK); ISpecificationRepository specificationRepository = CreateSpecificationRepository(); specificationRepository .GetCalculationSpecificationsForSpecification(Arg.Is(specificationId)) .Returns(calculationSpecifications); specificationRepository .GetSpecificationSummaryById(Arg.Is(specificationId)) .Returns(specificationSummary); IBuildProjectsService buildProjectsService = CreateBuildProjectsService(); buildProjectsService .GetBuildProjectForSpecificationId(Arg.Is(specificationId)) .Returns(buildProject); ISourceCodeService sourceCodeService = CreateSourceCodeService(); sourceCodeService .Compile(Arg.Is(buildProject), Arg.Any <IEnumerable <Calculation> >(), Arg.Any <CompilerOptions>()) .Returns(build); IVersionRepository <CalculationVersion> calculationVersionRepository = CreateCalculationVersionRepository(); calculationVersionRepository .CreateVersion(Arg.Any <CalculationVersion>(), Arg.Any <CalculationVersion>()) .Returns(calculationVersion); ICacheProvider cacheProvider = CreateCacheProvider(); CalculationService calculationService = CreateCalculationService( logger: logger, calculationsRepository: calculationsRepository, specificationRepository: specificationRepository, buildProjectsService: buildProjectsService, sourceCodeService: sourceCodeService, calculationVersionRepository: calculationVersionRepository, cacheProvider: cacheProvider); //Act await calculationService.ResetCalculationForFieldDefinitionChanges(relationships, specificationId, currentFieldDefinitionNames); //Assert await sourceCodeService .Received(1) .DeleteAssembly(Arg.Is(specificationId)); await cacheProvider .Received(1) .RemoveAsync <List <DatasetSchemaRelationshipModel> >(Arg.Is($"{CacheKeys.DatasetRelationshipFieldsForSpecification}{specificationId}")); }
private async Task <IActionResult> GenerateAndCompile(BuildProject buildProject, Calculation calculationToPreview, IEnumerable <Calculation> calculations, CompilerOptions compilerOptions, PreviewRequest previewRequest) { PreviewProviderCalculationResponseModel previewProviderCalculation = null; Build compilerOutput = _sourceCodeService.Compile(buildProject, calculations, compilerOptions); if (compilerOutput.SourceFiles != null) { await _sourceCodeService.SaveSourceFiles(compilerOutput.SourceFiles, buildProject.SpecificationId, SourceCodeType.Preview); } if (compilerOutput.Success) { _logger.Information($"Build compiled successfully for calculation id {calculationToPreview.Id}"); string calculationIdentifier = $"{_typeIdentifierGenerator.GenerateIdentifier(calculationToPreview.Namespace)}.{_typeIdentifierGenerator.GenerateIdentifier(calculationToPreview.Name)}"; IDictionary <string, string> functions = _sourceCodeService.GetCalculationFunctions(compilerOutput.SourceFiles); IDictionary <string, string> calculationIdentifierMap = calculations .Select(_ => new { Identifier = $"{_typeIdentifierGenerator.GenerateIdentifier(_.Namespace)}.{_typeIdentifierGenerator.GenerateIdentifier(_.Name)}", CalcName = _.Name }) .ToDictionary(d => d.Identifier, d => d.CalcName); if (!functions.ContainsKey(calculationIdentifier)) { compilerOutput.Success = false; compilerOutput.CompilerMessages.Add(new CompilerMessage { Message = $"{calculationIdentifier} is not an aggregable field", Severity = Severity.Error }); } else { if (previewRequest != null) { if (!SourceCodeHelpers.HasReturn(previewRequest.SourceCode)) { compilerOutput.Success = false; compilerOutput.CompilerMessages.Add(new CompilerMessage { Message = $"{calculationIdentifier} must have a return statement so that a calculation result will be returned", Severity = Severity.Error }); } else { IEnumerable <string> aggregateParameters = SourceCodeHelpers.GetCalculationAggregateFunctionParameters(previewRequest.SourceCode); bool continueChecking = true; if (!aggregateParameters.IsNullOrEmpty()) { foreach (string aggregateParameter in aggregateParameters) { if (!functions.ContainsKey(aggregateParameter)) { compilerOutput.Success = false; compilerOutput.CompilerMessages.Add(new CompilerMessage { Message = $"{aggregateParameter} is not an aggregable field", Severity = Severity.Error }); continueChecking = false; break; } if (calculationIdentifierMap.ContainsKey(aggregateParameter)) { Calculation calculation = calculations.SingleOrDefault(_ => _.Name == calculationIdentifierMap[aggregateParameter]); if (calculation.Current.DataType != CalculationDataType.Decimal) { compilerOutput.Success = false; compilerOutput.CompilerMessages.Add(new CompilerMessage { Message = $"Only decimal fields can be used on aggregation. {aggregateParameter} has data type of {calculation.Current.DataType}", Severity = Severity.Error }); continueChecking = false; break; } } } if (continueChecking) { if (SourceCodeHelpers.IsCalcReferencedInAnAggregate(functions, calculationIdentifier)) { compilerOutput.Success = false; compilerOutput.CompilerMessages.Add(new CompilerMessage { Message = $"{calculationIdentifier} is already referenced in an aggregation that would cause nesting", Severity = Severity.Error }); } else if (SourceCodeHelpers.CheckSourceForExistingCalculationAggregates(functions, previewRequest.SourceCode)) { compilerOutput.Success = false; compilerOutput.CompilerMessages.Add(new CompilerMessage { Message = $"{calculationIdentifier} cannot reference another calc that is being aggregated", Severity = Severity.Error }); } } } } } } } else { _logger.Information($"Build did not compile successfully for calculation id {calculationToPreview.Id}"); } LogMessages(compilerOutput, buildProject, calculationToPreview); if (!string.IsNullOrEmpty(previewRequest.ProviderId)) { CalculationSummaryModel calculationSummaryModel = calculationToPreview.ToSummaryModel(); CalcEngineModels.CalculationSummaryModel model = _mapper.Map <CalcEngineModels.CalculationSummaryModel>(calculationSummaryModel); CalcEngineModels.PreviewCalculationRequest previewCalculationRequest = new CalcEngineModels.PreviewCalculationRequest { AssemblyContent = compilerOutput.Assembly, PreviewCalculationSummaryModel = model }; ApiResponse <CalcEngineProviderResult> previewCalcResultApiResponse = await _calcEngineApiClientPolicy.ExecuteAsync( () => _calcEngineApiClient.PreviewCalculationResults( previewRequest.SpecificationId, previewRequest.ProviderId, previewCalculationRequest)); if (previewCalcResultApiResponse.StatusCode.IsSuccess()) { CalcEngineProviderResult calcEngineProviderResult = previewCalcResultApiResponse.Content; previewProviderCalculation = new PreviewProviderCalculationResponseModel { ProviderName = calcEngineProviderResult.Provider.Name, CalculationResult = _mapper.Map <CalculationResult>( calcEngineProviderResult.CalculationResults.SingleOrDefault(_ => _.Calculation?.Id == calculationToPreview.Id)), }; } } return(new OkObjectResult(new PreviewResponse { Calculation = calculationToPreview.ToResponseModel(), CompilerOutput = compilerOutput, PreviewProviderCalculation = previewProviderCalculation })); }
public async Task CreateCalculation_GivenValidCalculationWithNullFundingStream_AndSavesLogs() { //Arrange Calculation calculation = CreateCalculation(); calculation.FundingStream = null; IEnumerable <Calculation> calculations = new[] { calculation }; IEnumerable <Models.Specs.Calculation> calculationSpecifications = new[] { new Models.Specs.Calculation { Id = calculation.CalculationSpecification.Id } }; string json = JsonConvert.SerializeObject(calculation); Message message = new Message(Encoding.UTF8.GetBytes(json)); message.UserProperties.Add("user-id", UserId); message.UserProperties.Add("user-name", Username); ICalculationsRepository repository = CreateCalculationsRepository(); repository .CreateDraftCalculation(Arg.Any <Calculation>()) .Returns(HttpStatusCode.Created); repository .GetCalculationsBySpecificationId(Arg.Is("any-spec-id")) .Returns(calculations); Models.Specs.SpecificationSummary specificationSummary = new Models.Specs.SpecificationSummary() { Id = calculation.SpecificationId, Name = "Test Spec Name", }; ISpecificationRepository specificationRepository = CreateSpecificationRepository(); specificationRepository .GetSpecificationSummaryById(Arg.Is(calculation.SpecificationId)) .Returns(specificationSummary); specificationRepository .GetCalculationSpecificationsForSpecification(Arg.Is(calculation.SpecificationId)) .Returns(calculationSpecifications); ILogger logger = CreateLogger(); ISearchRepository <CalculationIndex> searchRepository = CreateSearchRepository(); Build build = new Build { SourceFiles = new List <SourceFile> { new SourceFile() } }; ISourceCodeService sourceCodeService = CreateSourceCodeService(); sourceCodeService .Compile(Arg.Any <BuildProject>(), Arg.Any <IEnumerable <Models.Calcs.Calculation> >(), Arg.Any <CompilerOptions>()) .Returns(build); CalculationService service = CreateCalculationService( calculationsRepository: repository, logger: logger, searchRepository: searchRepository, specificationRepository: specificationRepository, sourceCodeService: sourceCodeService); //Act await service.CreateCalculation(message); //Assert logger .Received(1) .Information($"Calculation with id: {calculation.Id} was successfully saved to Cosmos Db"); await repository .Received(1) .CreateDraftCalculation(Arg.Is <Calculation>(m => m.Id == CalculationId && m.Current.PublishStatus == PublishStatus.Draft && m.Current.Author.Id == UserId && m.Current.Author.Name == Username && m.Current.Date.Date == DateTimeOffset.Now.Date && m.Current.Version == 1 && m.Current.DecimalPlaces == 6 )); await searchRepository .Received(1) .Index(Arg.Is <List <CalculationIndex> >( m => m.First().Id == CalculationId && m.First().Name == "Test Calc Name" && m.First().CalculationSpecificationId == "any-calc-id" && m.First().CalculationSpecificationName == "Test Calc Name" && m.First().SpecificationId == "any-spec-id" && m.First().SpecificationName == "Test Spec Name" && m.First().FundingPeriodId == "18/19" && m.First().FundingPeriodName == "2018/2019" && m.First().FundingStreamId == string.Empty && m.First().FundingStreamName == "No funding stream set" && m.First().AllocationLineId == "test-alloc-id" && m.First().AllocationLineName == "test-alloc-name" && m.First().PolicySpecificationIds.First() == "policy-id" && m.First().PolicySpecificationNames.First() == "policy-name" )); }
public async Task UpdateCalculationCodeOnCalculationChange_WhenCalculationsFoundReferencingCalculationToBeUpdated_ThenSourceCodeUpdated() { // Arrange ICalculationsRepository calculationsRepository = CreateCalculationsRepository(); ISpecificationsApiClient specificationsApiClient = CreateSpecificationsApiClient(); IVersionRepository <CalculationVersion> versionRepository = CreateCalculationVersionRepository(); IBuildProjectsService buildProjectsService = CreateBuildProjectsService(); ISourceCodeService sourceCodeService = CreateSourceCodeService(); CalculationService service = CreateCalculationService(calculationsRepository: calculationsRepository, specificationsApiClient: specificationsApiClient, calculationVersionRepository: versionRepository, buildProjectsService: buildProjectsService, sourceCodeService: sourceCodeService); const string specificationId = "specId"; const string calculationId = "updatedCalc"; const string originalCodeUpdate = @"Dim test as OriginalNameOptions? OriginalNameOptions.enumName Return Calculations.OriginalName()"; const string newCodeUpdated = @"Dim test as CalculationToUpdateOptions? CalculationToUpdateOptions.enumName Return Calculations.CalculationToUpdate()"; const string originalCodeIgnore = "Return 10"; const string fundingStreamId = "fundingstreamid"; CalculationVersionComparisonModel comparison = new CalculationVersionComparisonModel() { CalculationId = calculationId, CurrentName = "Calculation To Update", PreviousName = "Original Name", SpecificationId = specificationId, CalculationDataType = CalculationDataType.Enum, Namespace = "Calculations" }; Reference user = new Reference("userId", "User Name"); List <Calculation> calculations = new List <Calculation> { new Calculation { Id = calculationId, SpecificationId = specificationId, FundingStreamId = fundingStreamId, Current = new CalculationVersion { SourceCode = originalCodeIgnore, Name = "Calculation to Update", CalculationType = CalculationType.Template, Description = "Calculation Description", DataType = CalculationDataType.Enum } }, new Calculation { Id = "referenceCalc", SpecificationId = specificationId, FundingStreamId = fundingStreamId, Current = new CalculationVersion { SourceCode = originalCodeUpdate, Name = "Calling Calculation To Update", CalculationType = CalculationType.Template, Description = "Calculation Description" } } }; calculationsRepository .GetCalculationsBySpecificationId(Arg.Is(specificationId)) .Returns(calculations); calculationsRepository .UpdateCalculation(Arg.Any <Calculation>()) .Returns(HttpStatusCode.OK); calculationsRepository .GetCalculationById(Arg.Is(calculations[0].Id)) .Returns(calculations[0]); SpecModel.SpecificationSummary specification = new SpecModel.SpecificationSummary() { Id = specificationId, Name = "Specification Name", FundingStreams = new [] { new Reference(fundingStreamId, "fundingStreamName"), } }; specificationsApiClient .GetSpecificationSummaryById(Arg.Is(specificationId)) .Returns(new Common.ApiClient.Models.ApiResponse <SpecModel.SpecificationSummary>(HttpStatusCode.OK, specification)); CalculationVersion calculationVersion = new CalculationVersion { SourceCode = newCodeUpdated, Version = 2 }; versionRepository .CreateVersion(Arg.Is <CalculationVersion>(_ => _.SourceCode == newCodeUpdated), Arg.Any <CalculationVersion>()) .Returns(calculationVersion); buildProjectsService .GetBuildProjectForSpecificationId(specificationId) .Returns(new BuildProject()); sourceCodeService .Compile(Arg.Any <BuildProject>(), Arg.Any <IEnumerable <Calculation> >(), Arg.Any <CompilerOptions>()) .Returns(new Build()); // Act IEnumerable <Calculation> updatedCalculations = await service.UpdateCalculationCodeOnCalculationChange(comparison, user); // Assert updatedCalculations .Should() .HaveCount(1); Calculation calculation = updatedCalculations.Single(); calculation.Current.SourceCode .Should() .Be(newCodeUpdated); calculation.Current.Version .Should() .Be(2); calculation.Id .Should() .Be("referenceCalc"); }
public async Task EditCalculationStatus_GivenNewStatusOfUpdated_UpdatesSearchReturnsOK() { //Arrange EditStatusModel CalculationEditStatusModel = new EditStatusModel { PublishStatus = PublishStatus.Updated }; ILogger logger = CreateLogger(); Calculation calculation = CreateCalculation(); calculation.Current.PublishStatus = PublishStatus.Approved; CalculationVersion calculationVersion = calculation.Current.Clone() as CalculationVersion; calculationVersion.PublishStatus = PublishStatus.Updated; ICalculationsRepository CalculationsRepository = CreateCalculationsRepository(); CalculationsRepository .GetCalculationById(Arg.Is(CalculationId)) .Returns(calculation); CalculationsRepository .UpdateCalculation(Arg.Any <Calculation>()) .Returns(HttpStatusCode.OK); ISearchRepository <CalculationIndex> searchRepository = CreateSearchRepository(); SpecModel.SpecificationSummary specificationSummary = new SpecModel.SpecificationSummary() { Id = calculation.SpecificationId, Name = "spec name", FundingStreams = new[] { new Reference(calculation.FundingStreamId, "funding stream name") } }; ISpecificationsApiClient specificationsApiClient = CreateSpecificationsApiClient(); specificationsApiClient .GetSpecificationSummaryById(Arg.Is(calculation.SpecificationId)) .Returns(new Common.ApiClient.Models.ApiResponse <SpecModel.SpecificationSummary>(HttpStatusCode.OK, specificationSummary)); IVersionRepository <CalculationVersion> versionRepository = CreateCalculationVersionRepository(); versionRepository .CreateVersion(Arg.Any <CalculationVersion>(), Arg.Any <CalculationVersion>()) .Returns(calculationVersion); Build build = new Build { SourceFiles = new List <SourceFile> { new SourceFile() } }; BuildProject buildProject = new BuildProject(); IBuildProjectsService buildProjectsService = CreateBuildProjectsService(); buildProjectsService .GetBuildProjectForSpecificationId(Arg.Is(calculation.SpecificationId)) .Returns(buildProject); ISourceCodeService sourceCodeService = CreateSourceCodeService(); sourceCodeService .Compile(Arg.Any <BuildProject>(), Arg.Any <IEnumerable <Models.Calcs.Calculation> >(), Arg.Any <CompilerOptions>()) .Returns(build); CalculationService service = CreateCalculationService( logger: logger, calculationsRepository: CalculationsRepository, searchRepository: searchRepository, specificationsApiClient: specificationsApiClient, calculationVersionRepository: versionRepository, sourceCodeService: sourceCodeService, buildProjectsService: buildProjectsService); //Act IActionResult result = await service.UpdateCalculationStatus(CalculationId, CalculationEditStatusModel); //Arrange result .Should() .BeOfType <OkObjectResult>() .Which .Value .Should() .BeOfType <PublishStatusResultModel>() .Which .PublishStatus .Should() .Be(PublishStatus.Updated); calculation .Current .PublishStatus .Should() .Be(PublishStatus.Updated); }
private async Task <IActionResult> GenerateAndCompile(BuildProject buildProject, Calculation calculationToPreview, IEnumerable <Calculation> calculations, CompilerOptions compilerOptions, PreviewRequest previewRequest) { Build compilerOutput = _sourceCodeService.Compile(buildProject, calculations, compilerOptions); compilerOutput = FilterDoubleToDecimalErrors(compilerOutput); await _sourceCodeService.SaveSourceFiles(compilerOutput.SourceFiles, buildProject.SpecificationId, SourceCodeType.Preview); if (compilerOutput.Success) { _logger.Information($"Build compiled successfully for calculation id {calculationToPreview.Id}"); string calculationIdentifier = VisualBasicTypeGenerator.GenerateIdentifier(calculationToPreview.Name); IDictionary <string, string> functions = _sourceCodeService.GetCalculationFunctions(compilerOutput.SourceFiles); if (!functions.ContainsKey(calculationIdentifier)) { compilerOutput.Success = false; compilerOutput.CompilerMessages.Add(new CompilerMessage { Message = $"{calculationIdentifier} is not an aggregable field", Severity = Severity.Error }); } else { if (previewRequest != null) { IEnumerable <string> aggregateParameters = SourceCodeHelpers.GetCalculationAggregateFunctionParameters(previewRequest.SourceCode); bool continueChecking = true; if (!aggregateParameters.IsNullOrEmpty()) { foreach (string aggregateParameter in aggregateParameters) { if (!functions.ContainsKey(aggregateParameter)) { compilerOutput.Success = false; compilerOutput.CompilerMessages.Add(new CompilerMessage { Message = $"{aggregateParameter} is not an aggregable field", Severity = Severity.Error }); continueChecking = false; break; } } if (continueChecking) { if (SourceCodeHelpers.IsCalcReferencedInAnAggregate(functions, calculationIdentifier)) { compilerOutput.Success = false; compilerOutput.CompilerMessages.Add(new CompilerMessage { Message = $"{calculationIdentifier} is already referenced in an aggregation that would cause nesting", Severity = Severity.Error }); } else if (SourceCodeHelpers.CheckSourceForExistingCalculationAggregates(functions, previewRequest.SourceCode)) { compilerOutput.Success = false; compilerOutput.CompilerMessages.Add(new CompilerMessage { Message = $"{calculationIdentifier} cannot reference another calc that is being aggregated", Severity = Severity.Error }); } } } } } //Forcing to compile for calc runs only compilerOptions.OptionStrictEnabled = false; Build nonPreviewCompilerOutput = _sourceCodeService.Compile(buildProject, calculations, compilerOptions); if (nonPreviewCompilerOutput.Success) { await _sourceCodeService.SaveSourceFiles(nonPreviewCompilerOutput.SourceFiles, buildProject.SpecificationId, SourceCodeType.Release); } } else { _logger.Information($"Build did not compile successfully for calculation id {calculationToPreview.Id}"); } CheckCircularReference(calculationToPreview, compilerOutput); LogMessages(compilerOutput, buildProject, calculationToPreview); return(new OkObjectResult(new PreviewResponse { Calculation = calculationToPreview, CompilerOutput = compilerOutput })); }