Skip to content

Commit

Permalink
Improve Observation Value Merge Type Mismatch Errors (#187)
Browse files Browse the repository at this point in the history
* Add additional error information on existing type when merge value detect type mismatch.
* Add additional check to fail observation save if the observation found on the initial get doesn't contain the desired identifier.
* Update failing tests to properly mock data to pass the new identifier check.
  • Loading branch information
dustinburson authored Apr 22, 2022
1 parent 62d1eca commit a134481
Show file tree
Hide file tree
Showing 7 changed files with 31 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ namespace Microsoft.Health.Fhir.Ingest.Template
public abstract class FhirValueProcessor<TValueType, TInValue, TOutValue> : IFhirValueProcessor<TInValue, TOutValue>
where TValueType : FhirValueType
{
private const string NullValueText = "null";
private static readonly Type SupportedType = typeof(TValueType);

public Type SupportedValueType => SupportedType;
Expand Down Expand Up @@ -40,5 +41,10 @@ protected TValueType CastTemplate(FhirValueType template)
protected abstract TOutValue CreateValueImpl(TValueType template, TInValue inValue);

protected abstract TOutValue MergeValueImpl(TValueType template, TInValue inValue, TOutValue existingValue);

protected static string GetValueTypeName(object value)
{
return value?.GetType()?.Name ?? NullValueText;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ protected override DataType MergeValueImpl(CodeableConceptFhirValueType template
{
if (!(existingValue is CodeableConcept))
{
throw new NotSupportedException($"Element {nameof(existingValue)} expected to be of type {typeof(CodeableConcept)}.");
throw new NotSupportedException($"Element {nameof(existingValue)} expected to be of type {typeof(CodeableConcept)}. Actual type is {GetValueTypeName(existingValue)}.");
}

// Currently no way to merge codeable concepts. Just replace.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ protected override DataType MergeValueImpl(QuantityFhirValueType template, IObse
{
if (!(existingValue is Quantity))
{
throw new NotSupportedException($"Element {nameof(existingValue)} expected to be of type {typeof(Quantity)}.");
throw new NotSupportedException($"Element {nameof(existingValue)} expected to be of type {typeof(Quantity)}. Actual type is {GetValueTypeName(existingValue)}.");
}

// Only a single value, just replace.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ protected override DataType MergeValueImpl(SampledDataFhirValueType template, IO

if (!(existingValue is SampledData sampledData))
{
throw new NotSupportedException($"Element {nameof(existingValue)} expected to be of type {typeof(SampledData)}.");
throw new NotSupportedException($"Element {nameof(existingValue)} expected to be of type {typeof(SampledData)}. Actual type is {GetValueTypeName(existingValue)}.");
}

if (sampledData.Dimensions > 1)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ protected override DataType MergeValueImpl(StringFhirValueType template, IObserv
{
if (!(existingValue is FhirString))
{
throw new NotSupportedException($"Element {nameof(existingValue)} expected to be of type {typeof(FhirString)}.");
throw new NotSupportedException($"Element {nameof(existingValue)} expected to be of type {typeof(FhirString)}. Actual type is {GetValueTypeName(existingValue)}.");
}

// Only a single value, just replace.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,12 @@ public virtual async Task<string> SaveObservationAsync(ILookupTemplate<IFhirTemp
if (!_observationCache.TryGetValue(cacheKey, out Model.Observation existingObservation))
{
existingObservation = await GetObservationFromServerAsync(identifier).ConfigureAwait(false);

// Discovered an issue where FHIR Service is only matching on first 128 characters of the identifier. This is a temporary measure to prevent merging of different observations until a fix is available.
if (existingObservation != null && !existingObservation.Identifier.Exists(i => i.IsExactly(identifier)))
{
throw new NotSupportedException("FHIR Service returned matching observation but expected identifier was not present.");
}
}

var policyResult = await Policy<(Model.Observation observation, ResourceOperation operationType)>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,14 +113,7 @@ public async void GivenFoundObservation_WhenSaveObservationAsync_ThenUpdateInvok
Device = new ResourceReference(@"Device/123"),
Subject = new ResourceReference(@"Patient/abc"),
Status = ObservationStatus.Final,
Identifier = new List<Identifier>
{
new Identifier
{
System = "Test",
Value = "id",
},
},
Identifier = BuildIdentifiers(),
Effective = new Period(new FhirDateTime(DateTimeOffset.Now.AddHours(-1)), new FhirDateTime(DateTimeOffset.Now)),
};

Expand Down Expand Up @@ -170,7 +163,7 @@ public async void GivenFoundObservation_WhenSaveObservationAsync_ThenUpdateInvok
[Fact]
public async void GivenFoundObservationAndConflictOnSave_WhenSaveObservationAsync_ThenGetAndUpdateInvoked_Test()
{
var foundObservation1 = new Observation { Id = "1" };
var foundObservation1 = new Observation { Id = "1", Identifier = BuildIdentifiers() };
var foundBundle1 = new Bundle
{
Entry = new List<Bundle.EntryComponent>
Expand Down Expand Up @@ -350,7 +343,7 @@ public async Task GivenCachedObservationUnchanged_WhenGenerateObservation_ThenCa
[Fact]
public async void GivenFoundObservationUnchanged_WhenSaveObservationAsync_ThenUpdateInvoked_Test()
{
var foundObservation = new Observation { Id = "1" };
var foundObservation = new Observation { Id = "1", Identifier = BuildIdentifiers() };
var foundBundle = new Bundle
{
Entry = new List<Bundle.EntryComponent>
Expand Down Expand Up @@ -398,6 +391,18 @@ private static IDictionary<ResourceType, string> BuildIdCollection()
return lookup;
}

private static List<Identifier> BuildIdentifiers()
{
return new List<Identifier>
{
new Identifier
{
System = "https://azure.microsoft.com/en-us/services/iomt-fhir-connector/",
Value = "patientId.deviceId..",
},
};
}

private static Observation ThrowConflictException()
{
throw new FhirException(new FhirResponse<OperationOutcome>(new HttpResponseMessage(HttpStatusCode.Conflict), new OperationOutcome()));
Expand Down

0 comments on commit a134481

Please sign in to comment.