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

min validator bug fix follow up #3065

Merged
merged 32 commits into from
Oct 6, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
979c95f
nit- extra line
esmadau Sep 26, 2023
79f4446
nit: rename to HasCoreTagError and fix formatting
esmadau Sep 26, 2023
7de48fb
nit: docs
esmadau Sep 26, 2023
489f330
validate method static so does not need leniency bool
esmadau Sep 26, 2023
faadf55
start logging when we fail or warn
esmadau Sep 26, 2023
1223c59
fix nit
esmadau Sep 26, 2023
872ce53
nit: enumerate values to avoid lookup
esmadau Sep 26, 2023
65b95d4
use enum to set validation style
esmadau Sep 27, 2023
85dc7fd
use leniency by default
esmadau Sep 27, 2023
f147a46
check for null before trimming
esmadau Sep 27, 2023
8497e45
Update src/Microsoft.Health.Dicom.Core/Features/Store/StoreService.cs
esmadau Sep 28, 2023
4cb7cb1
make RequiredCoreTags private, expose checks via static method
esmadau Sep 29, 2023
24d19f7
nit: rm commas
esmadau Sep 29, 2023
b21f14c
Use common string validator to sanitize
esmadau Sep 29, 2023
b0ce322
rename class
esmadau Sep 29, 2023
539eedb
move validationStyle to its own class
esmadau Sep 29, 2023
bbfd397
rm newline
esmadau Sep 29, 2023
f2bcdb4
rename from styles to levels
esmadau Sep 29, 2023
d0f00fd
Merge branch 'users/esmadau/vs-minvalidator-followup' of https://gith…
esmadau Sep 29, 2023
08b3df0
fix nits
esmadau Oct 2, 2023
e5d445d
use base class for validation, but force each validator to tell it ho…
esmadau Oct 2, 2023
bb4e36c
Merge branch 'main' into users/esmadau/vs-minvalidator-followup
esmadau Oct 5, 2023
a802f83
use virtual getValue on string validator and add string validator tes…
esmadau Oct 6, 2023
e6c2be4
by default allow null and empty on string validation, but only after …
esmadau Oct 6, 2023
4a67944
fix nit: swap vs and value args
esmadau Oct 6, 2023
d6f7545
getValue to GetValueOrDefault with no return
esmadau Oct 6, 2023
ba5747e
use property AllowNullOrEmpty
esmadau Oct 6, 2023
fb840ca
rm out
esmadau Oct 6, 2023
21e28f1
uid validation also allows null or empty, added tests
esmadau Oct 6, 2023
df3ca69
added tests, add back in ability to override check for null or empty …
esmadau Oct 6, 2023
e0e8527
added tests
esmadau Oct 6, 2023
00ae3cc
add null/empty check back in to validate long string as it is used el…
esmadau Oct 6, 2023
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
Original file line number Diff line number Diff line change
Expand Up @@ -264,12 +264,12 @@ private void DropInvalidMetadata(StoreValidationResult storeValidatorResult, Dic
var identifier = dicomDataset.ToInstanceIdentifier(partition);
foreach ((DicomTag tag, StoreErrorResult result) in storeValidatorResult.InvalidTagErrors)
{
if (!StoreDatasetValidator.IsCoreTag(error.Key))
if (!StoreDatasetValidator.IsCoreTag(tag))
{
// drop invalid metadata if not a core tag
dicomDataset.Remove(error.Key);
dicomDataset.Remove(tag);

string message = error.Value.Error;
string message = result.Error;
_telemetryClient.ForwardLogTrace(
$"{message}. This attribute will not be present when retrieving study, series, or instance metadata resources, nor can it be used in searches." +
" However, it will still be present when retrieving study, series, or instance resources.",
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,18 @@
using System;
using System.Globalization;
using FellowOakDicom;
using FellowOakDicom.IO.Buffer;
using Microsoft.Health.Dicom.Core.Exceptions;
using Microsoft.Health.Dicom.Core.Extensions;

namespace Microsoft.Health.Dicom.Core.Features.Validation;

internal class DateValidation : IElementValidation
internal class DateValidation : StringElementValidation
{
private const string DateFormatDA = "yyyyMMdd";

public void Validate(DicomElement dicomElement, ValidationLevel validationLevel = ValidationLevel.Strict)
protected override void ValidateStringElement(string name, string value, DicomVR vr, IByteBuffer buffer)
{
string name = dicomElement.Tag.GetFriendlyName();
string value = BaseStringSanitizer.Sanitize(dicomElement.GetFirstValueOrDefault<string>(), validationLevel);
if (string.IsNullOrEmpty(value))
{
return;
Expand All @@ -29,4 +28,10 @@ public void Validate(DicomElement dicomElement, ValidationLevel validationLevel
throw new ElementValidationException(name, DicomVR.DA, ValidationErrorCode.DateIsInvalid);
}
}

protected override bool GetValue(DicomElement dicomElement, out string value)
{
value = dicomElement.GetFirstValueOrDefault<string>();
return string.IsNullOrEmpty(value);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@
using System.Globalization;
using EnsureThat;
using FellowOakDicom;
using FellowOakDicom.IO.Buffer;
using Microsoft.Health.Dicom.Core.Exceptions;
using Microsoft.Health.Dicom.Core.Extensions;

namespace Microsoft.Health.Dicom.Core.Features.Validation;

internal class ElementMaxLengthValidation : IElementValidation
internal class ElementMaxLengthValidation : StringElementValidation
{
public ElementMaxLengthValidation(int maxLength)
{
Expand All @@ -22,10 +23,9 @@ public ElementMaxLengthValidation(int maxLength)

public int MaxLength { get; }

public void Validate(DicomElement dicomElement, ValidationLevel validationLevel = ValidationLevel.Strict)
protected override void ValidateStringElement(string name, string value, DicomVR vr, IByteBuffer buffer)
{
string value = BaseStringSanitizer.Sanitize(dicomElement.GetFirstValueOrDefault<string>(), validationLevel);
Validate(value, MaxLength, dicomElement.Tag.GetFriendlyName(), dicomElement.ValueRepresentation);
Validate(value, MaxLength, name, vr);
}

public static void Validate(string value, int maxLength, string name, DicomVR vr)
Expand All @@ -41,4 +41,10 @@ public static void Validate(string value, int maxLength, string name, DicomVR vr
string.Format(CultureInfo.CurrentCulture, DicomCoreResource.ErrorMessageExceedMaxLength, maxLength));
}
}

protected override bool GetValue(DicomElement dicomElement, out string value)
{
value = dicomElement.GetFirstValueOrDefault<string>();
return string.IsNullOrEmpty(value);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
// -------------------------------------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
Expand All @@ -13,7 +14,7 @@

namespace Microsoft.Health.Dicom.Core.Features.Validation;

internal class ElementRequiredLengthValidation : IElementValidation
internal class ElementRequiredLengthValidation : StringElementValidation
{
private static readonly HashSet<DicomVR> StringVrs = new HashSet<DicomVR>()
{
Expand All @@ -37,17 +38,15 @@ public ElementRequiredLengthValidation(int expectedLength)
ExpectedLength = expectedLength;
}

public void Validate(DicomElement dicomElement, ValidationLevel validationLevel = ValidationLevel.Strict)
protected override void ValidateStringElement(string name, string value, DicomVR vr, IByteBuffer buffer)
{
DicomVR vr = dicomElement.ValueRepresentation;
if (TryGetAsString(dicomElement, out string value))
if (!String.IsNullOrEmpty(value))
{
value = BaseStringSanitizer.Sanitize(value, validationLevel);
ValidateStringLength(vr, dicomElement.Tag.GetFriendlyName(), value);
ValidateStringLength(vr, name, value);
}
else
{
ValidateByteBufferLength(vr, dicomElement.Tag.GetFriendlyName(), dicomElement.Buffer);
ValidateByteBufferLength(vr, name, buffer);
}
}

Expand All @@ -64,7 +63,7 @@ private void ValidateByteBufferLength(DicomVR dicomVR, string name, IByteBuffer
}
}

private static bool TryGetAsString(DicomElement dicomElement, out string value)
protected override bool GetValue(DicomElement dicomElement, out string value)
{
value = string.Empty;
if (StringVrs.Contains(dicomElement.ValueRepresentation))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,36 +5,40 @@

using System;
using FellowOakDicom;
using FellowOakDicom.IO.Buffer;
using Microsoft.Health.Dicom.Core.Exceptions;
using Microsoft.Health.Dicom.Core.Extensions;

namespace Microsoft.Health.Dicom.Core.Features.Validation;

internal class EncodedStringElementValidation : IElementValidation
internal class EncodedStringElementValidation : StringElementValidation
{
public void Validate(DicomElement dicomElement, ValidationLevel validationLevel = ValidationLevel.Strict)
protected override void ValidateStringElement(string name, string value, DicomVR vr, IByteBuffer buffer)
{
DicomVR vr = dicomElement.ValueRepresentation;
switch (vr.Code)
{
case DicomVRCode.DT:
Validate(dicomElement, DicomValidation.ValidateDT, ValidationErrorCode.DateTimeIsInvalid, validationLevel);
Validate(name, value, vr, buffer, DicomValidation.ValidateDT, ValidationErrorCode.DateTimeIsInvalid);
break;
case DicomVRCode.IS:
Validate(dicomElement, DicomValidation.ValidateIS, ValidationErrorCode.IntegerStringIsInvalid, validationLevel);
Validate(name, value, vr, buffer, DicomValidation.ValidateIS, ValidationErrorCode.IntegerStringIsInvalid);
break;
case DicomVRCode.TM:
Validate(dicomElement, DicomValidation.ValidateTM, ValidationErrorCode.TimeIsInvalid, validationLevel);
Validate(name, value, vr, buffer, DicomValidation.ValidateTM, ValidationErrorCode.TimeIsInvalid);
break;
default:
throw new ArgumentOutOfRangeException(nameof(dicomElement));
throw new ArgumentOutOfRangeException(nameof(name));
};
}

private static void Validate(DicomElement element, Action<string> validate, ValidationErrorCode errorCode, ValidationLevel validationLevel = ValidationLevel.Strict)
protected override bool GetValue(DicomElement dicomElement, out string value)
{
string value = BaseStringSanitizer.Sanitize(element.GetFirstValueOrDefault<string>(), validationLevel);
value = dicomElement.GetFirstValueOrDefault<string>();
return string.IsNullOrEmpty(value);
}

private static void Validate(string name, string value, DicomVR vr, IByteBuffer buffer, Action<string> validate, ValidationErrorCode errorCode)
{
if (string.IsNullOrEmpty(value))
{
return;
Expand All @@ -46,7 +50,7 @@ private static void Validate(DicomElement element, Action<string> validate, Vali
}
catch (DicomValidationException)
{
throw new ElementValidationException(element.Tag.GetFriendlyName(), element.ValueRepresentation, errorCode);
throw new ElementValidationException(name, vr, errorCode);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

using System;
using FellowOakDicom;
using FellowOakDicom.IO.Buffer;
using Microsoft.Health.Dicom.Core.Exceptions;
using Microsoft.Health.Dicom.Core.Extensions;

Expand All @@ -13,12 +14,10 @@ namespace Microsoft.Health.Dicom.Core.Features.Validation;
/// <summary>
/// Validate Dicom VR LO
/// </summary>
internal class LongStringValidation : IElementValidation
internal class LongStringValidation : StringElementValidation
{
public void Validate(DicomElement dicomElement, ValidationLevel validationLevel = ValidationLevel.Strict)
protected override void ValidateStringElement(string name, string value, DicomVR vr, IByteBuffer buffer)
{
string value = BaseStringSanitizer.Sanitize(dicomElement.GetFirstValueOrDefault<string>(), validationLevel);
string name = dicomElement.Tag.GetFriendlyName();
Validate(value, name);
}

Expand All @@ -36,5 +35,11 @@ public static void Validate(string value, string name)
throw new ElementValidationException(name, DicomVR.LO, ValidationErrorCode.InvalidCharacters);
}
}

protected override bool GetValue(DicomElement dicomElement, out string value)
{
value = dicomElement.GetFirstValueOrDefault<string>();
return string.IsNullOrEmpty(value);
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,16 @@

using System.Linq;
using FellowOakDicom;
using FellowOakDicom.IO.Buffer;
using Microsoft.Health.Dicom.Core.Exceptions;
using Microsoft.Health.Dicom.Core.Extensions;

namespace Microsoft.Health.Dicom.Core.Features.Validation;

internal class PersonNameValidation : IElementValidation
internal class PersonNameValidation : StringElementValidation
{
public void Validate(DicomElement dicomElement, ValidationLevel validationLevel = ValidationLevel.Default)
protected override void ValidateStringElement(string name, string value, DicomVR vr, IByteBuffer buffer)
{
string name = dicomElement.Tag.GetFriendlyName();
DicomVR vr = dicomElement.ValueRepresentation;
string value = BaseStringSanitizer.Sanitize(dicomElement.GetFirstValueOrDefault<string>(), validationLevel);
if (string.IsNullOrEmpty(value))
{
// empty values allowed
Expand All @@ -33,7 +31,7 @@ public void Validate(DicomElement dicomElement, ValidationLevel validationLevel
{
try
{
ElementMaxLengthValidation.Validate(group, 64, name, dicomElement.ValueRepresentation);
ElementMaxLengthValidation.Validate(group, 64, name, vr);
}
catch (ElementValidationException ex) when (ex.ErrorCode == ValidationErrorCode.ExceedMaxLength)
{
Expand All @@ -52,4 +50,10 @@ public void Validate(DicomElement dicomElement, ValidationLevel validationLevel
throw new ElementValidationException(name, DicomVR.PN, ValidationErrorCode.PersonNameExceedMaxComponents);
}
}

protected override bool GetValue(DicomElement dicomElement, out string value)
{
value = dicomElement.GetFirstValueOrDefault<string>();
return string.IsNullOrEmpty(value);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// -------------------------------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
// -------------------------------------------------------------------------------------------------

using EnsureThat;
using FellowOakDicom;
using FellowOakDicom.IO.Buffer;
using Microsoft.Health.Dicom.Core.Extensions;

namespace Microsoft.Health.Dicom.Core.Features.Validation;


internal abstract class StringElementValidation : IElementValidation
{
public void Validate(DicomElement dicomElement, ValidationLevel validationLevel = ValidationLevel.Default)
{
EnsureArg.IsNotNull(dicomElement, nameof(dicomElement));

string name = dicomElement.Tag.GetFriendlyName();
GetValue(dicomElement, out string value);
if (!string.IsNullOrEmpty(value) && validationLevel == ValidationLevel.Default)
esmadau marked this conversation as resolved.
Show resolved Hide resolved
value = value.TrimEnd('\0');

ValidateStringElement(name, value, dicomElement.ValueRepresentation, dicomElement.Buffer);
}

protected abstract void ValidateStringElement(string name, string value, DicomVR vr, IByteBuffer buffer);
esmadau marked this conversation as resolved.
Show resolved Hide resolved

protected abstract bool GetValue(DicomElement dicomElement, out string value);
esmadau marked this conversation as resolved.
Show resolved Hide resolved
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,18 @@

using System.Text.RegularExpressions;
using FellowOakDicom;
using FellowOakDicom.IO.Buffer;
using Microsoft.Health.Dicom.Core.Exceptions;
using Microsoft.Health.Dicom.Core.Extensions;

namespace Microsoft.Health.Dicom.Core.Features.Validation;

internal class UidValidation : IElementValidation
internal class UidValidation : StringElementValidation
{
private static readonly Regex ValidIdentifierCharactersFormat = new Regex("^[0-9\\.]*[0-9]$", RegexOptions.Compiled);

public void Validate(DicomElement dicomElement, ValidationLevel validationLevel = ValidationLevel.Strict)
protected override void ValidateStringElement(string name, string value, DicomVR vr, IByteBuffer buffer)
{
string value = BaseStringSanitizer.Sanitize(dicomElement.GetFirstValueOrDefault<string>(), validationLevel);
string name = dicomElement.Tag.GetFriendlyName();
Validate(value, name, allowEmpty: true);
}

Expand Down Expand Up @@ -51,4 +50,10 @@ public static void Validate(string value, string name, bool allowEmpty = false)
throw new InvalidIdentifierException(name);
}
}

protected override bool GetValue(DicomElement dicomElement, out string value)
{
value = dicomElement.GetFirstValueOrDefault<string>();
return string.IsNullOrEmpty(value);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ public enum ValidationLevel
/// perform all validation after.
/// </summary>
Default,
esmadau marked this conversation as resolved.
Show resolved Hide resolved

/// <summary>
/// Strict validation currently fails when a string value has null padding
/// </summary>
Expand Down