Skip to content
Open
Show file tree
Hide file tree
Changes from all 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,13 +182,15 @@
#pragma warning restore CS0618, CS0612 // Type or member is obsolete
services.TryAddTransient<IInstantiationProcessor, NullInstantiationProcessor>();
services.TryAddTransient<IInstantiationValidator, NullInstantiationValidator>();
services.TryAddTransient<DataFieldValueCalculator>();
services.TryAddTransient<IAppModel, DefaultAppModel>();
services.TryAddTransient<DataListsFactory>();
services.TryAddTransient<InstanceDataListsFactory>();
services.TryAddTransient<IDataElementAccessChecker, DataElementAccessChecker>();
services.TryAddTransient<IDataListsService, DataListsService>();
services.TryAddTransient<ILayoutEvaluatorStateInitializer, LayoutEvaluatorStateInitializer>();
services.TryAddTransient<LayoutEvaluatorStateInitializer>();
services.AddTransient<IDataWriteProcessor, DataFieldValueCalculatorProcessor>();
services.AddSingleton<IAuthenticationTokenResolver, AuthenticationTokenResolver>();
services.AddTransient<IDataService, DataService>();
services.AddSingleton<ModelSerializationService>();
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,231 @@
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.Logging;
using ComponentContext = Altinn.App.Core.Models.Expressions.ComponentContext;

namespace Altinn.App.Core.Features.DataProcessing;

internal sealed class DataFieldValueCalculator
{
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;
private readonly Telemetry? _telemetry;

public DataFieldValueCalculator(
ILogger<DataFieldValueCalculator> logger,
ILayoutEvaluatorStateInitializer layoutEvaluatorStateInitializer,
IAppResources appResourceService,
IDataElementAccessChecker dataElementAccessChecker,
Telemetry? telemetry
)
{
_logger = logger;
_appResourceService = appResourceService;
_layoutEvaluatorStateInitializer = layoutEvaluatorStateInitializer;
_dataElementAccessChecker = dataElementAccessChecker;
_telemetry = telemetry;
}

public async Task Calculate(IInstanceDataAccessor dataAccessor, string taskId)
{
using var activity = _telemetry?.StartCalculateActivity(dataAccessor.Instance.Id, taskId);
foreach (var (dataType, dataElement) in dataAccessor.GetDataElementsWithFormDataForTask(taskId))
{
if (await _dataElementAccessChecker.CanRead(dataAccessor.Instance, dataType) is false)

Check warning on line 47 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);
}
}
}

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);
var formDataWrapper = await dataAccessor.GetFormDataWrapper(dataElement);

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 };
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 143 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);
throw;
}
}

private Dictionary<string, List<DataFieldCalculation>> ParseDataFieldCalculationConfig(
string rawCalculationConfig,
ILogger<DataFieldValueCalculator> logger
)
{
using var calculationConfigDocument = JsonDocument.Parse(rawCalculationConfig);

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>();
dataFieldCalculations[field] = dataFieldCalculation;
}
var resolvedDataFieldCalculation = ResolveDataFieldCalculation(field, calculation, logger);
if (resolvedDataFieldCalculation == null)
{
logger.LogError("Calculation for field {Field} could not be resolved", field);
continue;
}
dataFieldCalculation.Add(resolvedDataFieldCalculation);
}
}
}
return dataFieldCalculations;
}

private DataFieldCalculation? ResolveDataFieldCalculation(string field, JsonElement definition, ILogger logger)

Check failure on line 188 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 188 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 188 in src/Altinn.App.Core/Features/DataProcessing/DataFieldValueCalculator.cs

View workflow job for this annotation

GitHub Actions / Run dotnet build and test (ubuntu-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 188 in src/Altinn.App.Core/Features/DataProcessing/DataFieldValueCalculator.cs

View workflow job for this annotation

GitHub Actions / Run dotnet build and test (ubuntu-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 188 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 188 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 188 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)
{
Comment on lines +188 to +189
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 | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, let's read the file to see the method implementation
head -n 250 src/Altinn.App.Core/Features/DataProcessing/DataFieldValueCalculator.cs | tail -n +180

Repository: Altinn/app-lib-dotnet

Length of output: 1807


🏁 Script executed:

# Check if _jsonSerializerOptions is static or instance field
rg "_jsonSerializerOptions" src/Altinn.App.Core/Features/DataProcessing/DataFieldValueCalculator.cs -A 2 -B 2

Repository: Altinn/app-lib-dotnet

Length of output: 491


🏁 Script executed:

# Search for all calls to ResolveDataFieldCalculation
rg "ResolveDataFieldCalculation" src/Altinn.App.Core/Features/DataProcessing/DataFieldValueCalculator.cs

Repository: Altinn/app-lib-dotnet

Length of output: 293


Make ResolveDataFieldCalculation static.

The method only uses its parameters and the static _jsonSerializerOptions field—no instance members are accessed. CA1822 correctly flags this in CI.

🛠️ Proposed fix
-    private DataFieldCalculation? ResolveDataFieldCalculation(string field, JsonElement definition, ILogger logger)
+    private static DataFieldCalculation? ResolveDataFieldCalculation(string field, JsonElement definition, ILogger logger)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
private DataFieldCalculation? ResolveDataFieldCalculation(string field, JsonElement definition, ILogger logger)
{
private static DataFieldCalculation? ResolveDataFieldCalculation(string field, JsonElement definition, ILogger logger)
{
🧰 Tools
🪛 GitHub Actions: Build and Test on windows, macos and ubuntu

[error] 188-188: CA1822: Member 'ResolveDataFieldCalculation' does not access instance data and can be marked as static.

🪛 GitHub Actions: CodeQL

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

🪛 GitHub Check: Analyze (csharp)

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


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


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

🪛 GitHub Check: Run dotnet build and test (macos-latest)

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


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

🤖 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 187 - 188, ResolveDataFieldCalculation can be made static because
it only uses its parameters and the static field _jsonSerializerOptions; update
the method signature of ResolveDataFieldCalculation to add the static modifier
and verify any callers still compile (no instance state is used), ensuring
references to ResolveDataFieldCalculation and the static _jsonSerializerOptions
remain valid.

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);
return null;
}
}
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.Condition != null)
{
rawDataFieldValueCalculation.Condition = dataFieldCalculationDefinition.Condition;
}
}

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

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

return dataFieldCalculation;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using Altinn.App.Core.Models;

namespace Altinn.App.Core.Features.DataProcessing;

/// <summary>
/// Processing data fields values that is calculated by expressions provided in [modelName].calculation.json.
/// </summary>
internal sealed class DataFieldValueCalculatorProcessor : IDataWriteProcessor
{
private readonly DataFieldValueCalculator _dataFieldValueCalculator;

/// <summary>
/// Initializes a new instance of the <see cref="DataFieldValueCalculatorProcessor"/> class.
/// </summary>
/// <param name="dataFieldValueCalculator"></param>
public DataFieldValueCalculatorProcessor(DataFieldValueCalculator dataFieldValueCalculator)
{
_dataFieldValueCalculator = dataFieldValueCalculator;
}

/// <summary>
/// Processes data write operations on properties in the data model.
/// </summary>
/// <param name="instanceDataMutator">Object to fetch data elements not included in changes</param>
/// <param name="taskId">The current task ID</param>
/// <param name="changes">Not used in this context</param>
/// <param name="language">Not used in this context</param>
public async Task ProcessDataWrite(
IInstanceDataMutator instanceDataMutator,
string taskId,
DataElementChanges changes,
string? language
)
{
await _dataFieldValueCalculator.Calculate(instanceDataMutator, taskId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public abstract class GenericDataProcessor<TModel> : IDataProcessor

/// <summary>
/// Do changes to the model before it is written to storage, and report back to frontend.
/// Tyipically used to add calculated values to the model.
/// Typically used to add calculated values to the model.
/// </summary>
public abstract Task ProcessDataWrite(
Instance instance,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ partial class Telemetry
internal Activity? StartGetValidationConfigurationActivity() =>
ActivitySource.StartActivity($"{Prefix}.GetValidationConfiguration");

internal Activity? StartGetCalculationConfigurationActivity() =>
ActivitySource.StartActivity($"{Prefix}.GetCalculationConfiguration");

internal Activity? StartGetLayoutModelActivity() => ActivitySource.StartActivity($"{Prefix}.GetLayoutModel");

internal Activity? StartGetClassRefActivity() => ActivitySource.StartActivity($"{Prefix}.GetClassRef");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using System.Diagnostics;
using static Altinn.App.Core.Features.Telemetry.DataFieldValueCalculator;

namespace Altinn.App.Core.Features;

partial class Telemetry
{
internal Activity? StartCalculateActivity(string instanceId, string taskId)
{
var activity = ActivitySource.StartActivity($"{Prefix}.Calculate");
activity?.SetInstanceId(instanceId);
activity?.SetTaskId(taskId);
return activity;
}

internal static class DataFieldValueCalculator
{
internal const string Prefix = "DataFieldValueCalculator";
}
}
Loading
Loading