Skip to content

Commit

Permalink
Merge pull request #363 from FirelyTeam/bugfix/342-exception-when-try…
Browse files Browse the repository at this point in the history
…ing-to-validate-profile-with-minvaluequantitymaxvaluequantity

Added quantity support to min/max comparison
  • Loading branch information
mmsmits authored Sep 24, 2024
2 parents 239668b + bc1a220 commit 77b1cc8
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 16 deletions.
57 changes: 43 additions & 14 deletions src/Firely.Fhir.Validation/Impl/MinMaxValueValidator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,23 +73,38 @@ public MinMaxValueValidator(ITypedElement limit, ValidationMode minMaxType)
Limit = limit ?? throw new ArgumentNullException(nameof(limit), $"{nameof(limit)} cannot be null");
MinMaxType = minMaxType;

if (Any.TryConvert(Limit.Value, out _minMaxAnyValue!) == false)
throw new IncorrectElementDefinitionException($"Cannot convert the limit value ({Limit.Value}) to a comparable primitive.");
if (limit.InstanceType == "Quantity") //Quantity is the only non primitive that can be used as min/max value;
{

var quantity = limit.ParseQuantity()?.ToQuantity()!; // first parse to a Hl7.Model Qunatity, which we convert to a Hl7.Fhir.ElementModel.Types Quantity
if (quantity is not null)
{
_minMaxAnyValue = quantity!;
}

else
throw new IncorrectElementDefinitionException($"Cannot convert the limit value ({limit.Value}) to a quantity for comparison.");
}
else
{
if (Any.TryConvert(Limit.Value, out _minMaxAnyValue!) == false)
throw new IncorrectElementDefinitionException($"Cannot convert the limit value ({Limit.Value}) to a comparable primitive.");

// Min/max are only defined for ordered types
if (!isOrderedType(_minMaxAnyValue))
throw new IncorrectElementDefinitionException($"{Limit.Name} was given in ElementDefinition, but type '{Limit.InstanceType}' is not an ordered type");

static bool isOrderedType(Any value) => value is ICqlOrderable;
}

_comparisonOutcome = MinMaxType == ValidationMode.MinValue ? -1 : 1;
_comparisonLabel = _comparisonOutcome == -1 ? "smaller than" :
_comparisonOutcome == 0 ? "equal to" :
"larger than";
_comparisonIssue = _comparisonOutcome == -1 ? Issue.CONTENT_ELEMENT_PRIMITIVE_VALUE_TOO_SMALL :
Issue.CONTENT_ELEMENT_PRIMITIVE_VALUE_TOO_LARGE;

Issue.CONTENT_ELEMENT_PRIMITIVE_VALUE_TOO_LARGE;
_minMaxLabel = $"{MinMaxType.GetLiteral().Uncapitalize()}";

// Min/max are only defined for ordered types
if (!isOrderedType(_minMaxAnyValue))
throw new IncorrectElementDefinitionException($"{Limit.Name} was given in ElementDefinition, but type '{Limit.InstanceType}' is not an ordered type");

static bool isOrderedType(Any value) => value is ICqlOrderable;
}

/// <inheritdoc cref="MinMaxValueValidator(ITypedElement, ValidationMode)"/>
Expand All @@ -98,10 +113,24 @@ public MinMaxValueValidator(long limit, ValidationMode minMaxType) : this(Elemen
/// <inheritdoc/>
ResultReport IValidatable.Validate(IScopedNode input, ValidationSettings _, ValidationState s)
{
if (!Any.TryConvert(input.Value, out var instanceValue))
Any instanceValue;
if (input.InstanceType == "Quantity")
{
var quantity = input.ParseQuantity()?.ToQuantity()!; // first parse to a Hl7.Model Qunatity, which we convert to a Hl7.Fhir.ElementModel.Types Quantity
if (quantity is not null)
{
instanceValue = quantity;
}
else
{
return new IssueAssertion(Issue.CONTENT_ELEMENT_PRIMITIVE_VALUE_NOT_COMPARABLE,
$"Value '{input.Value ?? input}' cannot be compared with {_minMaxAnyValue})").AsResult(s);
}
}
else if (!Any.TryConvert(input.Value, out instanceValue!))
{
return new IssueAssertion(Issue.CONTENT_ELEMENT_PRIMITIVE_VALUE_NOT_COMPARABLE,
$"Value '{input.Value}' cannot be compared with {Limit.Value})").AsResult(s);
$"Value '{input.Value}' cannot be compared with {_minMaxAnyValue})").AsResult(s);
}

try
Expand All @@ -113,17 +142,17 @@ ResultReport IValidatable.Validate(IScopedNode input, ValidationSettings _, Vali
(false, true) => 1,
_ => 0
};

if (intResult == _comparisonOutcome)
{
return new IssueAssertion(_comparisonIssue, $"Value '{input.Value}' is {_comparisonLabel} {Limit.Value})")
return new IssueAssertion(_comparisonIssue, $"Value '{instanceValue}' is {_comparisonLabel} {_minMaxAnyValue})")
.AsResult(s);
}
}
catch (ArgumentException)
{
return new IssueAssertion(Issue.CONTENT_ELEMENT_PRIMITIVE_VALUE_NOT_COMPARABLE,
$"Value '{input.Value}' cannot be compared with {Limit.Value})")
$"Value '{instanceValue}' cannot be compared with {_minMaxAnyValue})")
.AsResult(s);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@

using FluentAssertions;
using Hl7.Fhir.ElementModel;
using Hl7.Fhir.ElementModel.Types;
using Hl7.Fhir.Model;
using Hl7.Fhir.Support;
using Microsoft.VisualStudio.TestTools.UnitTesting;
Expand Down Expand Up @@ -116,6 +115,42 @@ public void InvalidConstructors()
action.Should().Throw<IncorrectElementDefinitionException>();
}

[TestMethod]
public void QuantityComparison()
{
var maxValue = new Hl7.Fhir.Model.Quantity
{
Value = 4,
Unit = "kg",
System = "http://unitsofmeasure.org",
Code = "kg"
}.ToTypedElement(ModelInfo.ModelInspector);

var assertion = new MinMaxValueValidator(maxValue, MinMaxValueValidator.ValidationMode.MaxValue);

assertion.Should().NotBeNull();
assertion.Limit.Should().BeAssignableTo<ITypedElement>();

var quantityCorrect = new Quantity
{
Value = 3,
Unit = "kg",
System = "http://unitsofmeasure.org",
Code = "kg"
}.ToTypedElement(ModelInfo.ModelInspector);

base.BasicValidatorTestcases(assertion, quantityCorrect, true, null, "");

var quantityIncorrect = new Quantity
{
Value = 5,
Unit = "kg",
System = "http://unitsofmeasure.org",
Code = "kg"
}.ToTypedElement(ModelInfo.ModelInspector);
base.BasicValidatorTestcases(assertion, quantityIncorrect, false, Issue.CONTENT_ELEMENT_PRIMITIVE_VALUE_TOO_LARGE, "");
}

[TestMethod]
public void CorrectConstructor()
{
Expand Down

0 comments on commit 77b1cc8

Please sign in to comment.