Beispiel #1
0
        public void CheckSourceForExistingCalculationAggregates_GivenCalcContainsCalcReferenceThatIncludesAvgInTheNameButNoOtherAggregateRefrences_ReturnsFalse()
        {
            //Arrange
            Dictionary <string, string> functions = new Dictionary <string, string>
            {
                { "Calc1", "Return Calc2() + Min( Calc6)" },
                { "Calc2", "Return 1" },
                { "Calc3", "Return Sum(Calc2) + 5" },
                { "Calc4", "Return Sum(Calc2) + Avg(Calc2) + Min( Calc3)" },
                { "Calc5", "Return CalcAvg1()" },
                { "Calc6", "Return Calc5()" },
                { "Calc7", "Return Calc6()" }
            };

            string calcToCheck = "Return CalcAvg1() = Calc6()";

            //Act
            bool result = SourceCodeHelpers.CheckSourceForExistingCalculationAggregates(functions, calcToCheck);

            //Assert
            result
            .Should()
            .BeFalse();
        }
Beispiel #2
0
        public void CheckSourceForExistingCalculationAggregates_GivenCalcIsReferencedInAnAggregateWithDeepNestingAndFurtherLevelsAndIncludeWhitespaceInAggregateCall_ReturnsTrue()
        {
            //Arrange
            Dictionary <string, string> functions = new Dictionary <string, string>
            {
                { "Calc1", "Return Calc2() + Min( Calc6)" },
                { "Calc2", "Return 1" },
                { "Calc3", "Return Sum(Calc2) + 5" },
                { "Calc4", "Return Sum(Calc2) + Avg(Calc2) + Min( Calc3)" },
                { "Calc5", "Return Calc1()" },
                { "Calc6", "Return Calc5()" },
                { "Calc7", "Return Calc6()" }
            };

            string calcToCheck = "Return Sum(Calc2) + Avg(Calc2) + Sum(Calc3)";

            //Act
            bool result = SourceCodeHelpers.CheckSourceForExistingCalculationAggregates(functions, calcToCheck);

            //Assert
            result
            .Should()
            .BeTrue();
        }
Beispiel #3
0
        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
            }));
        }
Beispiel #4
0
        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
            }));
        }