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

RFC: Add IFileUploadValidator to mirror the pattern of the new validator classes #567

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

Conversation

ivarne
Copy link
Member

@ivarne ivarne commented Apr 7, 2024

Some service owners has asked for validation of uploaded files. Since we decided to use a new approach for the new validation classes, I think it would make sense to provide a new interface IFileUploadValidator to provide a way that follows the same patterns as the new validation interfaces to validate file uploads.

Description

IFileValidator and IFileAnalyser are registered in custom fields on the DataType object in applicationmetadata.json. All the other new validation interfaces uses a different pattern where the validator has a DataType/TaskId field to configure when it should be executed.

This PR:

  • Adds a new IFileUploadValidator interface to match the new validation interfaces (including language parameter)
  • Adds default implementation LegacyFileAnalyzerValidator that ensures IFileValidator/IFileAnalyser continue to work in a backwards compatible way.
  • Consolidates validation functionality into ValidationService/ValidationFactory.
/// <summary>
/// Interface for handling validation of files when uploaded.
/// A validation failiure will cause the upload to be rejected.
/// </summary>
public interface IFileUploadValidator
{
    /// <summary>
    /// The data type that this validator should run for. This is the id of the data type from applicationmetadata.json
    /// The string "*" is a special value that means that the validator should run for all data types that don't have app-logic.
    /// </summary>
    string DataType { get; }

    /// <summary>
    /// Returns the group id of the validator.
    /// The default is based on the FullName and DataType fields, and should not need customization
    /// </summary>
    string ValidationSource => $"{this.GetType().FullName}-{DataType}";

    /// <summary>
    /// Validating a an uploaded file to possibly prevent the upload.
    /// The file is considered valid if the list of issues has no entries where <see cref="ValidationIssue.Severity"/> is Error.
    /// </summary>
    Task<List<ValidationIssue>> Validate(Instance instance, DataType dataType, byte[] fileContent, string? filename, string? mimeType, string? language);
}

Related Issue(s)

  • #{issue number}

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 added area/validation Custom validations/validation messages feature Label Pull requests with new features. Used when generation releasenotes labels Apr 7, 2024
@ivarne ivarne changed the title Add IFileUploadValidator to mirror the pattern of the new validator classes RFC: Add IFileUploadValidator to mirror the pattern of the new validator classes Apr 7, 2024
@ivarne ivarne force-pushed the ivarne/fileUploadValidator branch from 685f204 to 86ba987 Compare April 7, 2024 21:09
Comment on lines +52 to +60
foreach (var validatorName in dataType.EnabledFileValidators)
{
var validator = _fileValidators.First(v => v.Id == validatorName);
var (success, issues) = await validator.Validate(dataType, fileAnalysisResults);
if (!success)
{
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(...)'.
@ivarne ivarne force-pushed the ivarne/fileUploadValidator branch from 86ba987 to e3cec69 Compare April 7, 2024 21:25
Copy link
Contributor

@martinothamar martinothamar left a comment

Choose a reason for hiding this comment

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

Pretty limited understanding of the whole context here, but approach looks good to me. I'm uncertain about removing public types and such though. Since it seemed like we agreed to follow semver pretty strictly?

src/Altinn.App.Api/Controllers/DataController.cs Outdated Show resolved Hide resolved
/// Implementation of <see cref="IFileUploadValidator"/> that uses the legacy
/// <see cref="IFileValidator"/> and <see cref="IFileAnalyser"/>.
/// </summary>
public class LegacyFileAnalyzerValidator : IFileUploadValidator
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this should be internal and sealed? This was meant to bridge the migration to the the new interfaces right, so we have some expectations from the behavior here?

Copy link
Member Author

Choose a reason for hiding this comment

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

I just followed previous practice. Having this internal would make it more awkward for users to unregister this implementation from the DI container, and they would have to do it in a way that does not cause compiler warnings if this implementation is removed. I don't have a strong opinion either way.

Copy link
Contributor

Choose a reason for hiding this comment

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

Should users be able to unregister this? I thought it was needed to still support the file analyzer abstraction

Copy link
Member Author

Choose a reason for hiding this comment

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

I don't see any reason why we should actively prevent users from unregistering anything and replace it with their own interface implementation.

I also can't find any realistic scenario when you'd actually need to unregister this service, but a contrived example might be if you had a badly behaved IFileAnalyser in a package that should not be constructed for uploads to all DataTypes, but only the ones where it is actually used. A quick fix would be to replace this implementation that inject IEnumerable<IFileAnalysr> with one that fetches from IServiceProvider only when it is actually required.

Copy link
Contributor

Choose a reason for hiding this comment

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

It doesn't prevent unregistering though. My impression from previous discussions was that we would be more intentional about type accessibility going forward, and that any modification to public types is a breaking change. That's what I was considering here when reviewing, and so if I consider the tradeoffs between the ergonomics of unregistering and including this as a public API I personally am more worried about adding another public type

"Error while running validator {validatorName} on {dataType} for instance {instanceId}",
v.GetType().Name,
dataType.Id,
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
.
"Start running validator {validatorName} on {dataType} for instance {instanceId}",
v.GetType().Name,
dataType.Id,
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
.
Copy link

sonarcloud bot commented Apr 25, 2024

Copy link

sonarcloud bot commented May 30, 2024

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/validation Custom validations/validation messages feature Label Pull requests with new features. Used when generation releasenotes
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants