Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/multi data model with validation #730

Open
wants to merge 68 commits into
base: main
Choose a base branch
from

Conversation

ivarne
Copy link
Member

@ivarne ivarne commented Aug 19, 2024

Work on supporting multiple data models and subforms in backend validations with expressions.

Related Issue(s)

Main changes

The main goal for this PR is to add

New single interface for validation IValidator

With changes in one data element affects the validity of another element, the current hierarchy of validators and only IFormDataValidator supporting incremental validation break down. Adaptors for the old interfaces has been provided so no changes to current apps are required, but they might want to update in order to support incremental validation and read instance data with the IInstanceDataAccessor.

New interface IInstanceDataAccessor

When validation has dependencies across data models, we need a cached way to access the content of data elements that does not require us to download the same data element multiple times for the same request. The implementation is not available from DI, but always passed as an argument in order to eliminate risk of it being reused cross requests because of Singleton services or other unintentionally shared global state.

New DataElementId struct to ensure type safety for a wrapped Guid

36 places in the updated code need to reference the Guid of a data element, and adding type safety is an important step to ensure correctness. An implicit cast is provided from DataElement to simplify usage

New ModelBinding struct in expressions engine

To support the new syntax for referring to a non default data element in components and expressions

{
    "dataModelBindings": {"field": "path.to.data", "dataType": "default"}
    "hidden": ["equals", null, ["dataModel", "path.to.other", "prefill"]]
}

When handling references to a specific fields (eg. for removal) we needed to also add DataReference to combine a Field (path) with a specific DataElementId

New PATCH api endpoint that allows frontend to update multiple data models at the same time

Saving and triggering incremental validation is expensive and some apps might want to batch multiple changes to reduce cost. The code in frontend would also be simplified if it could do a single request, instead of multiple.

ValidationIssueWithSource class for external apis

Previously we had a single class ValidationIssue that included a Source property. Service owners sometimes tries to set this in their validators (because it shows up in intellisense), but as we override it based on IValidator.ValidationSource in our ValidationService it would be good if it wasn't there to confuse. This PR creates a new type ValidationIssueWithSource for external apis (where the source is included) and marks it [Obsolete] in the type returned in the validator interfaces.

Respect new app logic settings for data elements DisallowUserCreate and DisallowUserDelete

This means that users can not create or delete this types. Typically used for prefill, when you want only the org token to be able to modify the number of data elements on an instance.

Minor changes

Used IL code to generate real C# classes based on the json data models in shared tests. Previously different code was used to access properties of the test code as in production, which is not really good for tests.

Verification

  • Your code builds clean without any errors or warnings
  • Manual testing done (required)
  • Relevant automated test added (if you find this hard, leave it and we'll help out)
  • All tests run green

Documentation

  • User documentation is updated with a separate linked PR in altinn-studio-docs. (if applicable)

@ivarne ivarne force-pushed the feat/multi-data-model-with-validation branch from c2bfbae to f7b91e0 Compare August 19, 2024 12:22
@ivarne ivarne force-pushed the feat/multi-data-model-with-validation branch from 1703585 to 9007d42 Compare August 22, 2024 10:05
* Unused usings
* Mark members static

Much easier to see theese issues before sonar cloud runs
Make it easier to get 0 sonar cloud warnings by making its issues warnings
Copy link
Member Author

@ivarne ivarne left a comment

Choose a reason for hiding this comment

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

Æsj, svar på kommentarer grupperte seg sammen til en review og ble ikke sendt

src/Altinn.App.Core/Features/Telemetry.InterfaceFactory.cs Outdated Show resolved Hide resolved
/// <summary>
/// Initialize LayoutEvaluatorState with given Instance, data object and layoutSetId
/// </summary>
public virtual Task<LayoutEvaluatorState> Init(
[Obsolete("Use the overload with ILayoutEvaluatorStateInitializer instead")]
public async Task<LayoutEvaluatorState> Init(
Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, we should.

Some validators can neither run on every request for efficeincy,
nor has a sensible way to implement HasRelevantChanges.

Theese validators need to only run on process/next,
or when explicitly requested in a call to /validate

Also added NoIncrementalUpdates (with [JsonIgnore(WhenWritingDefault)]) to
ValidationIssueWithSource, so that FE can distinguish
what issues won't be fixed with incremental updates.

/validate now supports a boolean to run either only incremental, only non
incremnetal or all validators (default)
* Use IInstanceDataMutator for actions
* Return List instead of dictionary for models and validation issues in MultipleDataPatch for improved swagger doc
* New IDataWriteProcessor interface with IInstanceDataMutator and change list to mimik IValidator
* IValidator gets ShouldRunForTask(string taskId) so that
    * Required validators only runs for tasks with layout.
    * Expression validators only runs for tasks with datatypes that has expressions validation files
* Revert DataElementIdentifier to a simple wrapper for Guid (that caches the string version)
Add IInstanceDataModifier and use it for a new dataProcessing interface and extend the UserActionContext to support modifying data elements.
src/Altinn.App.Api/Controllers/ActionsController.cs Dismissed Show dismissed Hide dismissed
Comment on lines +80 to +91
foreach (var dataElement in formDataElementsForTask)
{
var issues = await ValidateFormData(
instance,
dataElement,
instanceDataAccessor,
validationConfig,
taskId,
language
);
validationIssues.AddRange(issues);
}

Check notice

Code scanning / CodeQL

Missed opportunity to use Select Note

This foreach loop immediately
maps its iteration variable to another variable
- consider mapping the sequence explicitly using '.Select(...)'.
"Error while running validator {validatorName} on task {taskId} in instance {instanceId}",
validator.GetType().Name,
taskId,
instance.Id

Check failure

Code scanning / CodeQL

Log entries created from user input High

This log entry depends on a
user-provided value
.
This log entry depends on a
user-provided value
.
This log entry depends on a
user-provided value
.
This log entry depends on a
user-provided value
.
This log entry depends on a
user-provided value
.
This log entry depends on a
user-provided value
.

Copilot Autofix AI 40 minutes ago

To fix the problem, we need to sanitize the instance.Id before logging it. Since instance.Id is a GUID, we can ensure it does not contain any special characters or new lines by converting it to a string and replacing any new line characters. This approach will prevent log forging by ensuring that the logged value is safe.

Suggested changeset 1
src/Altinn.App.Core/Internal/Validation/ValidationService.cs

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/src/Altinn.App.Core/Internal/Validation/ValidationService.cs b/src/Altinn.App.Core/Internal/Validation/ValidationService.cs
--- a/src/Altinn.App.Core/Internal/Validation/ValidationService.cs
+++ b/src/Altinn.App.Core/Internal/Validation/ValidationService.cs
@@ -147,6 +147,6 @@
                     e,
-                    "Error while running validator {validatorName} on task {taskId} in instance {instanceId}",
+                    "Error while running validator {validatorName} on task {taskId} in instance {sanitizedInstanceId}",
                     validator.GetType().Name,
                     taskId,
-                    instance.Id
+                    instance.Id.Replace(Environment.NewLine, "")
                 );
EOF
@@ -147,6 +147,6 @@
e,
"Error while running validator {validatorName} on task {taskId} in instance {instanceId}",
"Error while running validator {validatorName} on task {taskId} in instance {sanitizedInstanceId}",
validator.GetType().Name,
taskId,
instance.Id
instance.Id.Replace(Environment.NewLine, "")
);
Copilot is powered by AI and may make mistakes. Always verify output.
Copy link

sonarcloud bot commented Oct 3, 2024

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants