Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 14 additions & 4 deletions src/Altinn.App.Core/Configuration/AppSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,19 @@ public class AppSettings
{
#pragma warning disable CA1707 // Identifiers should not contain underscores
/// <summary>
/// Constant for the location of json schema file
/// Constant for the suffix on json schema file names
/// </summary>
public const string JSON_SCHEMA_FILENAME = "schema.json";

/// <summary>
/// Constant for the location of validation configuration file
/// Constant for the suffix on validation file names
/// </summary>
public const string VALIDATION_CONFIG_FILENAME = "validation.json";

/// <summary>
/// Constant for the suffix on calculation file names
/// </summary>
public const string CALCULATION_CONFIG_FILENAME = "calculation.json";
#pragma warning restore CA1707 // Identifiers should not contain underscores

/// <summary>
Expand Down Expand Up @@ -95,15 +100,20 @@ public class AppSettings
public string RuleConfigurationJSONFileName { get; set; } = "RuleConfiguration.json";

/// <summary>
/// Gets or sets The JSON schema file name
/// Gets or sets the file names suffix for the json schema files
/// </summary>
public string JsonSchemaFileName { get; set; } = JSON_SCHEMA_FILENAME;

/// <summary>
/// Gets or sets The JSON schema file name
/// Gets or sets the file names suffix for the validation files
/// </summary>
public string ValidationConfigurationFileName { get; set; } = VALIDATION_CONFIG_FILENAME;

/// <summary>
/// Gets or sets the file names suffix for the calculation files
/// </summary>
public string CalculationConfigurationFileName { get; set; } = CALCULATION_CONFIG_FILENAME;

/// <summary>
/// Gets or sets the filename for application meta data
/// </summary>
Expand Down
2 changes: 2 additions & 0 deletions src/Altinn.App.Core/Extensions/ServiceCollectionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,8 @@
#pragma warning restore CS0618, CS0612 // Type or member is obsolete
services.TryAddTransient<IInstantiationProcessor, NullInstantiationProcessor>();
services.TryAddTransient<IInstantiationValidator, NullInstantiationValidator>();
services.TryAddTransient<IDataWriteProcessor, DataFieldValueCalculatorProcessor>();
services.TryAddTransient<DataFieldValueCalculator>();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find all IDataWriteProcessor registrations and usage
echo "=== IDataWriteProcessor registrations and references ==="
rg -n "IDataWriteProcessor" --type=cs -B2 -A2

echo ""
echo "=== GetAll<IDataWriteProcessor> usage ==="
rg -n "GetAll.*IDataWriteProcessor" --type=cs -B3 -A3

echo ""
echo "=== DataFieldValueCalculatorProcessor class definition ==="
rg -n "class DataFieldValueCalculatorProcessor" --type=cs -B2 -A10

Repository: Altinn/app-lib-dotnet

Length of output: 10062


Use AddTransient instead of TryAddTransient for IDataWriteProcessor.

The code uses GetAll<IDataWriteProcessor>() to retrieve all registered implementations (InternalPatchService.cs line 269), iterating over each in a loop. With TryAddTransient, if an app registers its own IDataWriteProcessor, the core's DataFieldValueCalculatorProcessor will be skipped entirely, preventing the default processor from executing alongside app-provided implementations.

Change line 185 to services.AddTransient<IDataWriteProcessor, DataFieldValueCalculatorProcessor>(); to ensure the core processor is always included.

Additionally, DataFieldValueCalculatorProcessor should be marked sealed per coding guidelines unless inheritance is a valid use-case.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Altinn.App.Core/Extensions/ServiceCollectionExtensions.cs` around lines
185 - 186, Replace the TryAddTransient registration for IDataWriteProcessor with
services.AddTransient<IDataWriteProcessor, DataFieldValueCalculatorProcessor>()
so the core DataFieldValueCalculatorProcessor is always registered alongside any
app-provided implementations (the code that enumerates
GetAll<IDataWriteProcessor>() in InternalPatchService should therefore see
both). Also mark the DataFieldValueCalculatorProcessor class as sealed (unless
there is an intended inheritance use-case) to follow the coding guideline.

services.TryAddTransient<IAppModel, DefaultAppModel>();
services.TryAddTransient<DataListsFactory>();
services.TryAddTransient<InstanceDataListsFactory>();
Expand Down Expand Up @@ -263,7 +265,7 @@
services.AddTransient<IEventHandlerResolver, EventHandlerResolver>();
services.TryAddSingleton<IEventSecretCodeProvider, KeyVaultEventSecretCodeProvider>();

// TODO: Event subs could be handled by the new automatic Maskinporten auth, once implemented.

Check warning on line 268 in src/Altinn.App.Core/Extensions/ServiceCollectionExtensions.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Complete the task associated to this 'TODO' comment.

See more on https://sonarcloud.io/project/issues?id=Altinn_app-lib-dotnet&issues=AZy-iQtShgA_KbzevodZ&open=AZy-iQtShgA_KbzevodZ&pullRequest=1683
// The event subscription client depends upon a Maskinporten message handler being
// added to the client during setup. As of now this needs to be done in the apps
// if subscription is to be added. This registration is to prevent the DI container
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
private readonly AppImplementationFactory _appImplementationFactory;

/// <summary>
/// Initializes a new instance of the <see cref="DataListsFactory"/> class.
/// Initializes a new instance of the <see cref="InstanceDataListsFactory"/> class.
/// </summary>
public InstanceDataListsFactory(IServiceProvider serviceProvider)
{
Expand All @@ -26,7 +26,7 @@
public IInstanceDataListProvider GetDataListProvider(string listId)
{
var instanceDataListProviders = _appImplementationFactory.GetAll<IInstanceDataListProvider>();
foreach (var instanceDataListProvider in instanceDataListProviders)

Check warning on line 29 in src/Altinn.App.Core/Features/DataLists/InstanceDataListsFactory.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Loops should be simplified using the "Where" LINQ method

See more on https://sonarcloud.io/project/issues?id=Altinn_app-lib-dotnet&issues=AZy-iQsxhgA_Kbzevoc5&open=AZy-iQsxhgA_Kbzevoc5&pullRequest=1683
{
if (instanceDataListProvider.Id.Equals(listId, StringComparison.OrdinalIgnoreCase))
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,331 @@
using System.Text.Json;
using Altinn.App.Core.Internal.App;
using Altinn.App.Core.Internal.Data;
using Altinn.App.Core.Internal.Expressions;
using Altinn.App.Core.Models;
using Altinn.App.Core.Models.Layout;
using Altinn.Platform.Storage.Interface.Models;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using ComponentContext = Altinn.App.Core.Models.Expressions.ComponentContext;

namespace Altinn.App.Core.Features.DataProcessing;

public class DataFieldValueCalculator

Check warning on line 14 in src/Altinn.App.Core/Features/DataProcessing/DataFieldValueCalculator.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Missing XML comment for publicly visible type or member 'DataFieldValueCalculator'

See more on https://sonarcloud.io/project/issues?id=Altinn_app-lib-dotnet&issues=AZy-iQs9hgA_KbzevodJ&open=AZy-iQs9hgA_KbzevodJ&pullRequest=1683
{
private static readonly JsonSerializerOptions _jsonSerializerOptions = new()
{
ReadCommentHandling = JsonCommentHandling.Skip,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
};

private readonly ILogger<DataFieldValueCalculator> _logger;
private readonly IAppResources _appResourceService;
private readonly ILayoutEvaluatorStateInitializer _layoutEvaluatorStateInitializer;
private readonly IDataElementAccessChecker _dataElementAccessChecker;

public DataFieldValueCalculator(
ILogger<DataFieldValueCalculator> logger,
ILayoutEvaluatorStateInitializer layoutEvaluatorStateInitializer,
IAppResources appResourceService,
IServiceProvider serviceProvider
)
{
_logger = logger;
_appResourceService = appResourceService;
_layoutEvaluatorStateInitializer = layoutEvaluatorStateInitializer;
_dataElementAccessChecker = serviceProvider.GetRequiredService<IDataElementAccessChecker>();
}

public async Task Calculate(IInstanceDataAccessor dataAccessor, string taskId)

Check warning on line 40 in src/Altinn.App.Core/Features/DataProcessing/DataFieldValueCalculator.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Missing XML comment for publicly visible type or member 'DataFieldValueCalculator.Calculate(IInstanceDataAccessor, string)'

See more on https://sonarcloud.io/project/issues?id=Altinn_app-lib-dotnet&issues=AZy-iQs9hgA_KbzevodL&open=AZy-iQs9hgA_KbzevodL&pullRequest=1683
{
foreach (var (dataType, dataElement) in dataAccessor.GetDataElementsWithFormDataForTask(taskId))
{
if (await _dataElementAccessChecker.CanRead(dataAccessor.Instance, dataType) is false)

Check warning on line 44 in src/Altinn.App.Core/Features/DataProcessing/DataFieldValueCalculator.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove the unnecessary Boolean literal(s).

See more on https://sonarcloud.io/project/issues?id=Altinn_app-lib-dotnet&issues=AZy-iQs9hgA_Kbzevoc6&open=AZy-iQs9hgA_Kbzevoc6&pullRequest=1683
{
continue;
}

var calculationConfig = _appResourceService.GetCalculationConfiguration(dataType.Id);
if (!string.IsNullOrEmpty(calculationConfig))
{
await CalculateFormData(dataAccessor, dataElement, taskId, calculationConfig);
}
}
}
Comment on lines +42 to +58
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add OpenTelemetry instrumentation for this feature path.

This new calculation engine currently logs, but lacks explicit tracing/metrics around calculation lifecycle and failures. Please add spans/counters (e.g., per data element, per calculation, failures, conversion failures).

As per coding guidelines "src/**/*.cs: Comprehensive telemetry instrumentation should be included in feature implementations using OpenTelemetry".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Altinn.App.Core/Features/DataProcessing/DataFieldValueCalculator.cs`
around lines 40 - 55, Wrap the Calculate lifecycle in OpenTelemetry
instrumentation: create or use an ActivitySource and a Meter and start a parent
Activity in Calculate (recording taskId and instance identifiers), then for each
loop iteration start a child Activity for the data element (use dataType.Id and
dataElement identifiers) and another nested Activity or span around the call to
CalculateFormData; record attributes/tags like taskId, dataType.Id,
calculationConfig and use Counter/Histogram instruments to emit metrics for
"calculations_started", "calculations_failed", "conversion_failures" and
duration; on any exception from _dataElementAccessChecker.CanRead,
_appResourceService.GetCalculationConfiguration or CalculateFormData catch/log
the exception, set the Activity status to error, increment the failures counter
and rethrow or handle accordingly, and ensure Activities are disposed in finally
blocks so tracing captures end times.


internal async Task CalculateFormData(
IInstanceDataAccessor dataAccessor,
DataElement dataElement,
string taskId,
string rawCalculationConfig
)
{
var evaluatorState = await _layoutEvaluatorStateInitializer.Init(dataAccessor, taskId);
var hiddenFields = await LayoutEvaluator.GetHiddenFieldsForRemoval(
evaluatorState,
evaluateRemoveWhenHidden: false
);
DataElementIdentifier dataElementIdentifier = dataElement;
var dataFieldCalculations = ParseDataFieldCalculationConfig(rawCalculationConfig, _logger);

foreach (var (baseField, calculations) in dataFieldCalculations)
{
var resolvedFields = await evaluatorState.GetResolvedKeys(
new DataReference() { Field = baseField, DataElementIdentifier = dataElementIdentifier },
true
);
foreach (var resolvedField in resolvedFields)
{
if (
hiddenFields.Exists(d =>
d.DataElementIdentifier == resolvedField.DataElementIdentifier
&& resolvedField.Field.StartsWith(d.Field, StringComparison.InvariantCulture)
)
Comment on lines +85 to +88
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

StartsWith filtering can skip unrelated fields.

resolvedField.Field.StartsWith(d.Field, ...) is too broad and can match sibling names. Use an exact-or-descendant path check with segment boundaries.

🐛 Proposed fix
-                if (
-                    hiddenFields.Exists(d =>
-                        d.DataElementIdentifier == resolvedField.DataElementIdentifier
-                        && resolvedField.Field.StartsWith(d.Field, StringComparison.InvariantCulture)
-                    )
-                )
+                if (
+                    hiddenFields.Exists(d =>
+                        d.DataElementIdentifier == resolvedField.DataElementIdentifier
+                        && IsSameOrDescendantField(resolvedField.Field, d.Field)
+                    )
+                )
                 {
                     continue;
                 }
private static bool IsSameOrDescendantField(string candidate, string hiddenField)
{
    if (candidate.Equals(hiddenField, StringComparison.Ordinal))
    {
        return true;
    }

    return candidate.StartsWith(hiddenField, StringComparison.Ordinal)
        && candidate.Length > hiddenField.Length
        && (candidate[hiddenField.Length] == '.' || candidate[hiddenField.Length] == '[');
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Altinn.App.Core/Features/DataProcessing/DataFieldValueCalculator.cs`
around lines 81 - 84, The StartsWith check used in the hiddenFields.Exists
predicate is too broad and can match sibling fields; replace that logic with an
exact-or-descendant check (create a helper like IsSameOrDescendantField) that
returns true if candidate.Equals(hiddenField, StringComparison.Ordinal) or if
candidate.StartsWith(hiddenField, StringComparison.Ordinal) AND candidate.Length
> hiddenField.Length AND the next character is either '.' or '['; update the
predicate that currently uses resolvedField.Field.StartsWith(...) to call this
helper so only exact matches or true descendants are considered.

)
{
continue;
}

var context = new ComponentContext(
evaluatorState,
component: null,
rowIndices: ExpressionHelper.GetRowIndices(resolvedField.Field),
dataElementIdentifier: resolvedField.DataElementIdentifier
);
var positionalArguments = new object[] { resolvedField.Field };
var formDataWrapper = await dataAccessor.GetFormDataWrapper(dataElement);
foreach (var calculation in calculations)
{
await RunCalculation(
formDataWrapper,
evaluatorState,
resolvedField,
context,
positionalArguments,
calculation
);
}
}
}
}

private async Task RunCalculation(
IFormDataWrapper formDataWrapper,
LayoutEvaluatorState evaluatorState,
DataReference resolvedField,
ComponentContext context,
object[] positionalArguments,
DataFieldCalculation calculation
)
{
try
{
var calculationResult = await ExpressionEvaluator.EvaluateExpressionToExpressionValue(
evaluatorState,
calculation.Condition,
context,
positionalArguments
);
if (!formDataWrapper.Set(resolvedField.Field, calculationResult))
{
_logger.LogWarning(
"Could not set calculated value for field {Field} in data element {DataElementId}. "
+ "This is because the type conversion failed.",
resolvedField.Field,
resolvedField.DataElementIdentifier.Id
);
}
}
catch (Exception e)

Check warning on line 140 in src/Altinn.App.Core/Features/DataProcessing/DataFieldValueCalculator.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Either log this exception and handle it, or rethrow it with some contextual information.

See more on https://sonarcloud.io/project/issues?id=Altinn_app-lib-dotnet&issues=AZy-iQs9hgA_Kbzevoc7&open=AZy-iQs9hgA_Kbzevoc7&pullRequest=1683
{
_logger.LogError(e, "Error while evaluating calculation for field {field}", resolvedField.Field);

Check warning on line 142 in src/Altinn.App.Core/Features/DataProcessing/DataFieldValueCalculator.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use PascalCase for named placeholders.

See more on https://sonarcloud.io/project/issues?id=Altinn_app-lib-dotnet&issues=AZy-iQs9hgA_Kbzevoc8&open=AZy-iQs9hgA_Kbzevoc8&pullRequest=1683
throw;
}
}

private Dictionary<string, List<DataFieldCalculation>> ParseDataFieldCalculationConfig(

Check failure on line 147 in src/Altinn.App.Core/Features/DataProcessing/DataFieldValueCalculator.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Refactor this method to reduce its Cognitive Complexity from 20 to the 15 allowed.

See more on https://sonarcloud.io/project/issues?id=Altinn_app-lib-dotnet&issues=AZy-iQs9hgA_Kbzevoc9&open=AZy-iQs9hgA_Kbzevoc9&pullRequest=1683
string rawCalculationConfig,
ILogger<DataFieldValueCalculator> logger
)
{
using var calculationConfigDocument = JsonDocument.Parse(rawCalculationConfig);
var calculationDefinitions = new Dictionary<string, RawDataFieldValueCalculation>();
var hasDefinitions = calculationConfigDocument.RootElement.TryGetProperty(
"definitions",
out JsonElement definitionsObject
);
if (hasDefinitions)
{
foreach (var definitionProperty in definitionsObject.EnumerateObject())
{
var resolvedDefinition = ResolveCalculationDefinition(
definitionProperty,
calculationDefinitions,
logger
);
if (resolvedDefinition == null)
{
logger.LogError("Calculation definition {name} could not be resolved", definitionProperty.Name);

Check warning on line 169 in src/Altinn.App.Core/Features/DataProcessing/DataFieldValueCalculator.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use PascalCase for named placeholders in the logging message template

See more on https://sonarcloud.io/project/issues?id=Altinn_app-lib-dotnet&issues=AZy-iQs9hgA_KbzevodN&open=AZy-iQs9hgA_KbzevodN&pullRequest=1683

Check warning on line 169 in src/Altinn.App.Core/Features/DataProcessing/DataFieldValueCalculator.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use PascalCase for named placeholders.

See more on https://sonarcloud.io/project/issues?id=Altinn_app-lib-dotnet&issues=AZy-iQs9hgA_Kbzevoc-&open=AZy-iQs9hgA_Kbzevoc-&pullRequest=1683
continue;
}
calculationDefinitions[definitionProperty.Name] = resolvedDefinition;
}
}

var dataFieldCalculations = new Dictionary<string, List<DataFieldCalculation>>();
var hasCalculations = calculationConfigDocument.RootElement.TryGetProperty(
"calculations",
out JsonElement calculationsObject
);
if (hasCalculations)
{
foreach (var calculationArray in calculationsObject.EnumerateObject())
{
var field = calculationArray.Name;
var calculations = calculationArray.Value;
foreach (var calculation in calculations.EnumerateArray())
{
if (!dataFieldCalculations.TryGetValue(field, out var dataFieldCalculation))
{
dataFieldCalculation = new List<DataFieldCalculation>();

Check warning on line 191 in src/Altinn.App.Core/Features/DataProcessing/DataFieldValueCalculator.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Collection initialization can be simplified

See more on https://sonarcloud.io/project/issues?id=Altinn_app-lib-dotnet&issues=AZy-iQs9hgA_KbzevodP&open=AZy-iQs9hgA_KbzevodP&pullRequest=1683
dataFieldCalculations[field] = dataFieldCalculation;
}
var resolvedDataFieldCalculation = ResolveDataFieldCalculation(
field,
calculation,
calculationDefinitions,
logger
);
if (resolvedDataFieldCalculation == null)
{
logger.LogError("Calculation for field {field} could not be resolved", field);

Check warning on line 202 in src/Altinn.App.Core/Features/DataProcessing/DataFieldValueCalculator.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use PascalCase for named placeholders in the logging message template

See more on https://sonarcloud.io/project/issues?id=Altinn_app-lib-dotnet&issues=AZy-iQs9hgA_KbzevodO&open=AZy-iQs9hgA_KbzevodO&pullRequest=1683
continue;
}
dataFieldCalculation.Add(resolvedDataFieldCalculation);
}
}
}
return dataFieldCalculations;
}

private DataFieldCalculation? ResolveDataFieldCalculation(

Check failure on line 212 in src/Altinn.App.Core/Features/DataProcessing/DataFieldValueCalculator.cs

View workflow job for this annotation

GitHub Actions / Run dotnet build and test (macos-latest)

Member 'ResolveDataFieldCalculation' does not access instance data and can be marked as static (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1822)

Check failure on line 212 in src/Altinn.App.Core/Features/DataProcessing/DataFieldValueCalculator.cs

View workflow job for this annotation

GitHub Actions / Run dotnet build and test (macos-latest)

Member 'ResolveDataFieldCalculation' does not access instance data and can be marked as static (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1822)

Check failure on line 212 in src/Altinn.App.Core/Features/DataProcessing/DataFieldValueCalculator.cs

View workflow job for this annotation

GitHub Actions / Analyze (csharp)

Member 'ResolveDataFieldCalculation' does not access instance data and can be marked as static (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1822)

Check failure on line 212 in src/Altinn.App.Core/Features/DataProcessing/DataFieldValueCalculator.cs

View workflow job for this annotation

GitHub Actions / Analyze (csharp)

Member 'ResolveDataFieldCalculation' does not access instance data and can be marked as static (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1822)
string field,
JsonElement definition,
Dictionary<string, RawDataFieldValueCalculation> resolvedDefinitions,
ILogger logger
)
{
var rawDataFieldValueCalculation = new RawDataFieldValueCalculation();

if (definition.ValueKind == JsonValueKind.String)
{
var stringReference = definition.GetString();
if (stringReference == null)
{
logger.LogError("Could not resolve null reference for calculation for field {field}", field);

Check warning on line 226 in src/Altinn.App.Core/Features/DataProcessing/DataFieldValueCalculator.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use PascalCase for named placeholders.

See more on https://sonarcloud.io/project/issues?id=Altinn_app-lib-dotnet&issues=AZy-iQs9hgA_KbzevodB&open=AZy-iQs9hgA_KbzevodB&pullRequest=1683
return null;
}

var reference = resolvedDefinitions.GetValueOrDefault(stringReference);
if (reference == null)
{
logger.LogError(
"Could not resolve reference {stringReference} for calculation for field {field}",

Check warning on line 234 in src/Altinn.App.Core/Features/DataProcessing/DataFieldValueCalculator.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use PascalCase for named placeholders.

See more on https://sonarcloud.io/project/issues?id=Altinn_app-lib-dotnet&issues=AZy-iQs9hgA_KbzevodC&open=AZy-iQs9hgA_KbzevodC&pullRequest=1683
stringReference,
field
);
return null;
}
rawDataFieldValueCalculation.Condition = reference.Condition;
}
else
{
var dataFieldCalculationDefinition = definition.Deserialize<RawDataFieldValueCalculation>(
_jsonSerializerOptions
);
if (dataFieldCalculationDefinition == null)
{
logger.LogError("Calculation for field {field} could not be parsed", field);
return null;
}

if (dataFieldCalculationDefinition.Ref != null)
{
var reference = resolvedDefinitions.GetValueOrDefault(dataFieldCalculationDefinition.Ref);
if (reference == null)
{
logger.LogError(
"Could not resolve reference {expressionDefinitionRef} for calculation for field {field}",
dataFieldCalculationDefinition.Ref,
field
);
return null;
}
rawDataFieldValueCalculation.Condition = reference.Condition;
}

if (dataFieldCalculationDefinition.Condition != null)
{
rawDataFieldValueCalculation.Condition = dataFieldCalculationDefinition.Condition;
}
}

if (rawDataFieldValueCalculation.Condition == null)
{
logger.LogError("Calculation for field {field} is missing condition", field);

Check warning on line 276 in src/Altinn.App.Core/Features/DataProcessing/DataFieldValueCalculator.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use PascalCase for named placeholders in the logging message template

See more on https://sonarcloud.io/project/issues?id=Altinn_app-lib-dotnet&issues=AZy-iQs9hgA_KbzevodU&open=AZy-iQs9hgA_KbzevodU&pullRequest=1683
return null;
}

var dataFieldCalculation = new DataFieldCalculation
{
Condition = rawDataFieldValueCalculation.Condition.Value,
};

return dataFieldCalculation;
}

private static RawDataFieldValueCalculation? ResolveCalculationDefinition(
JsonProperty definitionProperty,
Dictionary<string, RawDataFieldValueCalculation> resolvedDefinitions,
ILogger logger
)
{
var resolvedDefinition = new RawDataFieldValueCalculation();
var rawDefinition = definitionProperty.Value.Deserialize<RawDataFieldValueCalculation>(_jsonSerializerOptions);
if (rawDefinition == null)
{
logger.LogError("Calculation definition {name} could not be parsed", definitionProperty.Name);

Check warning on line 298 in src/Altinn.App.Core/Features/DataProcessing/DataFieldValueCalculator.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use PascalCase for named placeholders.

See more on https://sonarcloud.io/project/issues?id=Altinn_app-lib-dotnet&issues=AZy-iQs9hgA_KbzevodG&open=AZy-iQs9hgA_KbzevodG&pullRequest=1683
return null;
}

if (rawDefinition.Ref != null)
{
var reference = resolvedDefinitions.GetValueOrDefault(rawDefinition.Ref);
if (reference == null)
{
logger.LogError(
"Could not resolve reference {rawDefinitionRef} for calculation {name}",
rawDefinition.Ref,
definitionProperty.Name
);
return null;
}

resolvedDefinition.Condition = reference.Condition;
}

if (rawDefinition.Condition != null)
{
resolvedDefinition.Condition = rawDefinition.Condition;
}

if (resolvedDefinition.Condition == null)
{
logger.LogError("Calculation {name} is missing condition", definitionProperty.Name);

Check warning on line 325 in src/Altinn.App.Core/Features/DataProcessing/DataFieldValueCalculator.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use PascalCase for named placeholders.

See more on https://sonarcloud.io/project/issues?id=Altinn_app-lib-dotnet&issues=AZy-iQs9hgA_KbzevodI&open=AZy-iQs9hgA_KbzevodI&pullRequest=1683
return null;
}

return resolvedDefinition;
}
}
Loading
Loading