Skip to content

Commit 7738a9e

Browse files
authored
Merge pull request #2819 from FirelyTeam/bugfix/2786-binary-data-content-issues
Either set binary.data or binary.content when parsing binaries in the FhirClient
2 parents 6544788 + f617372 commit 7738a9e

File tree

3 files changed

+35
-21
lines changed

3 files changed

+35
-21
lines changed

src/Hl7.Fhir.Base/Rest/BaseFhirClient.cs

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
using Hl7.Fhir.Introspection;
1212
using Hl7.Fhir.Model;
1313
using Hl7.Fhir.Serialization;
14+
using Hl7.Fhir.Specification;
1415
using Hl7.Fhir.Utility;
1516
using System;
1617
using System.Collections.Generic;
@@ -860,9 +861,9 @@ private static ResourceIdentity verifyResourceIdentity(Uri location, bool needId
860861
if (msg is null && entryComponent is null) throw new ArgumentException("Either msg or entryComponent should be set");
861862
// Validate the response and throw the appropriate exceptions. Also, if we have *not* verified the FHIR version
862863
// of the server, add a suggestion about this in the (legacy) parsing exception.
863-
var suggestedVersionOnParseError = !Settings.VerifyFhirVersion ? fhirVersion : null;
864+
864865
(LastResult, LastBody, LastBodyAsText, LastBodyAsResource, var issue) =
865-
await ValidateResponse(responseMessage, expect, getSerializationEngine(), suggestedVersionOnParseError, useBinaryProtocol)
866+
await ValidateResponse(responseMessage, expect, getSerializationEngine(), !Settings.VerifyFhirVersion, fhirVersion, useBinaryProtocol)
866867
.ConfigureAwait(false);
867868

868869
// If an error occurred while trying to interpret and validate the response, we will bail out now.
@@ -921,17 +922,20 @@ static string unexpectedBodyTypeForMessage(HttpRequestMessage msg, Resource resu
921922
/// <exception cref="FhirOperationException">The body content type could not be handled or the response status indicated failure, or we received an unexpected success status.</exception>
922923
/// <exception cref="FormatException">Thrown when the original ITypedElement-based parsers are used and a parse exception occurred.</exception>
923924
/// <exception cref="DeserializationFailedException">Thrown when a newer parsers is used and a parse exception occurred.</exception>
924-
/// <seealso cref="HttpContentParsers.ExtractResponseData(HttpResponseMessage, IFhirSerializationEngine, bool)"/>
925+
/// <seealso cref="HttpContentParsers.ExtractResponseData(HttpResponseMessage, IFhirSerializationEngine, bool, FhirRelease)"/>
925926
internal static async Task<ResponseData> ValidateResponse(
926927
HttpResponseMessage responseMessage,
927928
IEnumerable<HttpStatusCode> expect,
928929
IFhirSerializationEngine engine,
929-
string? suggestedVersionOnParseError,
930+
bool suggestVersionOnParsingError,
931+
string fhirVersion,
930932
bool useBinaryProtocol)
931933
{
932-
var responseData = (await responseMessage.ExtractResponseData(engine, useBinaryProtocol).ConfigureAwait(false))
934+
var fhirRelease = FhirReleaseParser.Parse(fhirVersion);
935+
936+
var responseData = (await responseMessage.ExtractResponseData(engine, useBinaryProtocol, fhirRelease).ConfigureAwait(false))
933937
.TranslateUnsupportedBodyTypeException(responseMessage.StatusCode)
934-
.TranslateLegacyParserException(suggestedVersionOnParseError);
938+
.TranslateLegacyParserException(suggestVersionOnParsingError ? fhirVersion : null);
935939

936940
// If extracting the data caused an issue, return it immediately
937941
if (responseData.Issue is not null)

src/Hl7.Fhir.Base/Rest/HttpContentParsers.cs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
using Hl7.Fhir.ElementModel;
1212
using Hl7.Fhir.Model;
1313
using Hl7.Fhir.Serialization;
14+
using Hl7.Fhir.Specification;
1415
using Hl7.Fhir.Utility;
1516
using System;
1617
using System.Linq;
@@ -147,7 +148,7 @@ internal record ResponseData(Bundle.ResponseComponent Response, byte[]? BodyData
147148
/// <remarks>If the status of the response indicates failure, this function will be lenient and return the body data
148149
/// instead of throwing an <see cref="UnsupportedBodyTypeException"/> when the content type or content itself is not recognizable as FHIR. This improves
149150
/// the chances of capturing diagnostic (non-FHIR) bodies returned by the server when an operation fails.</remarks>
150-
internal static async Task<ResponseData> ExtractResponseData(this HttpResponseMessage message, IFhirSerializationEngine ser, bool expectBinaryProtocol)
151+
internal static async Task<ResponseData> ExtractResponseData(this HttpResponseMessage message, IFhirSerializationEngine ser, bool expectBinaryProtocol, FhirRelease fhirRelease)
151152
{
152153
var component = message.ExtractResponseComponent();
153154
var data = await message.Content.ReadAsByteArrayAsync().ConfigureAwait(false);
@@ -159,14 +160,14 @@ internal static async Task<ResponseData> ExtractResponseData(this HttpResponseMe
159160
// If this is not binary data, try to capture the body as text
160161
if (!expectBinaryProtocol)
161162
result = result with { BodyText = await message.Content.GetBodyAsString().ConfigureAwait(false) };
162-
163+
163164
var usesFhirFormat = ContentType.GetResourceFormatFromContentType(message.Content.GetContentType()) != ResourceFormat.Unknown;
164165

165166
try
166167
{
167168
var resource = message switch
168169
{
169-
{ IsSuccessStatusCode: true } when expectBinaryProtocol && !usesFhirFormat => await ReadBinaryDataFromMessage(message).ConfigureAwait(false),
170+
{ IsSuccessStatusCode: true } when expectBinaryProtocol && !usesFhirFormat => await ReadBinaryDataFromMessage(message, fhirRelease).ConfigureAwait(false),
170171
{ IsSuccessStatusCode: true } => await ReadResourceFromMessage(message.Content, ser).ConfigureAwait(false),
171172
{ IsSuccessStatusCode: false } => await ReadOutcomeFromMessage(message.Content, ser).ConfigureAwait(false),
172173
};
@@ -179,7 +180,7 @@ internal static async Task<ResponseData> ExtractResponseData(this HttpResponseMe
179180
}
180181

181182
// Sets the Resource.ResourceBase to the location given in the RequestUri of the response message.
182-
if (result.BodyResource is not null && (message.Headers.Location?.OriginalString ?? message.GetRequestUri()?.OriginalString) is {} location)
183+
if (result.BodyResource is not null && (message.Headers.Location?.OriginalString ?? message.GetRequestUri()?.OriginalString) is { } location)
183184
{
184185
var ri = new ResourceIdentity(location);
185186
result.BodyResource.ResourceBase = ri.HasBaseUri && ri.Form == ResourceIdentityForm.AbsoluteRestUrl
@@ -196,11 +197,15 @@ internal static async Task<ResponseData> ExtractResponseData(this HttpResponseMe
196197
/// </summary>
197198
/// <returns>A <see cref="Binary"/> resource containing the streamed binary data. The resource's
198199
/// metadata will be retrieved from the appropriate HTTP headers.</returns>
199-
public static async Task<Binary> ReadBinaryDataFromMessage(this HttpResponseMessage message)
200+
public static async Task<Binary> ReadBinaryDataFromMessage(this HttpResponseMessage message, FhirRelease fhirRelease)
200201
{
201202
var result = new Binary();
202203

203-
result.Data = result.Content = await message.Content.ReadAsByteArrayAsync().ConfigureAwait(false);
204+
if (fhirRelease == FhirRelease.STU3)
205+
result.Content = await message.Content.ReadAsByteArrayAsync().ConfigureAwait(false);
206+
else
207+
result.Data = await message.Content.ReadAsByteArrayAsync().ConfigureAwait(false);
208+
204209
result.ContentType = message.Content.GetContentType();
205210
result.SecurityContext = message.GetSecurityContext() is { } reference ? new ResourceReference(reference) : null;
206211
result.Meta ??= new();

src/Hl7.Fhir.Support.Poco.Tests/Rest/ResponseMessageTests.cs

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ public async Task SetAndExtractRelevantHeaders()
101101
response.Headers.Location = new Uri("http://nu.nl");
102102
response.Headers.TryAddWithoutValidation("Test-key", "Test-value");
103103

104-
var extracted = await response.ExtractResponseData(engine, expectBinaryProtocol: false);
104+
var extracted = await response.ExtractResponseData(engine, expectBinaryProtocol: false, TESTINSPECTOR.FhirRelease);
105105

106106
extracted.BodyText.Should().Be(DEFAULT_XML);
107107
engine.SerializeToXml(extracted.BodyResource!).Should().Be(DEFAULT_XML);
@@ -117,7 +117,7 @@ public async Task SetAndExtractRelevantHeaders()
117117
public async Task GetEmptyResponse()
118118
{
119119
var response = new HttpResponseMessage(HttpStatusCode.Conflict);
120-
var components = await response.ExtractResponseData(POCOENGINE, expectBinaryProtocol: false);
120+
var components = await response.ExtractResponseData(POCOENGINE, expectBinaryProtocol: false, fhirRelease: Specification.FhirRelease.R4);
121121

122122
components.Response.Status.Should().Be("409");
123123
components.BodyData.Should().BeNull();
@@ -128,7 +128,7 @@ public async Task GetEmptyResponse()
128128
private static async Task check(HttpResponseMessage response, IFhirSerializationEngine engine,
129129
bool hasResource = false, Type? expectedIssue = null, string? messagePattern = null, string? notMessagePattern = null)
130130
{
131-
var components = await response.ExtractResponseData(engine, expectBinaryProtocol: false).ConfigureAwait(false);
131+
var components = await response.ExtractResponseData(engine, expectBinaryProtocol: false, fhirRelease: Specification.FhirRelease.R4).ConfigureAwait(false);
132132
await checkResult(response, components, engine, hasResource, expectedIssue, messagePattern, notMessagePattern);
133133
}
134134

@@ -299,7 +299,7 @@ await assertIssue<FhirOperationException>(response, "Operation was unsuccessful,
299299
public async Task TurnsNewParsingFailureIntoDFE()
300300
{
301301
var response = makeXmlMessage(xml: """<Unknown><active value="true" /></Unknown>""");
302-
await assertIssue<DeserializationFailedException>(response, "*Unknown type 'Unknown' found in root property*", engine: POCOENGINE, version: "1.0.0");
302+
await assertIssue<DeserializationFailedException>(response, "*Unknown type 'Unknown' found in root property*", engine: POCOENGINE, suggestVersionOnParsingError: true, version: "1.0.0");
303303
}
304304

305305
[TestMethod]
@@ -308,17 +308,17 @@ public async Task TurnsLegacyParsingFailureIntoFE()
308308
var response = makeXmlMessage(xml: """<Unknown><active value="true" /></Unknown>""");
309309
await assertIssue<FormatException>(response, "*Cannot locate type information for type 'Unknown'*", engine: ELEMENTENGINE, notmatch: "*with FHIR version 1.0.0*");
310310
await assertIssue<StructuralTypeException>(response, "*Cannot locate type information for type 'Unknown'*" +
311-
"Are you connected to a FHIR server with FHIR version 1.0.0*", version: "1.0.0", engine: ELEMENTENGINE);
311+
"Are you connected to a FHIR server with FHIR version 1.0.0*", true, version: "1.0.0", engine: ELEMENTENGINE);
312312

313313
response = makeJsonMessage(json: """{ "resourceType": "Patient", "activex": 4 }""");
314314
await assertIssue<StructuralTypeException>(response, "*Encountered unknown element 'activex' at location*", engine: ELEMENTENGINE);
315315
}
316316

317-
private static async Task assertIssue<T>(HttpResponseMessage response, string match, string? version = null,
317+
private static async Task assertIssue<T>(HttpResponseMessage response, string match, bool suggestVersionOnParsingError = false, string? version = null,
318318
IFhirSerializationEngine? engine = null, string? notmatch = null)
319319
where T : Exception
320320
{
321-
var result = await BaseFhirClient.ValidateResponse(response, new[] { HttpStatusCode.OK }, engine ?? POCOENGINE, version, useBinaryProtocol: false);
321+
var result = await BaseFhirClient.ValidateResponse(response, new[] { HttpStatusCode.OK }, engine ?? POCOENGINE, suggestVersionOnParsingError, (suggestVersionOnParsingError) ? version! : "1.0.0", useBinaryProtocol: false);
322322
await checkResult(response, result, engine ?? POCOENGINE, hasResource: false, expectedIssue: typeof(T), messagePattern: match, notMessagePattern: notmatch);
323323
}
324324

@@ -336,15 +336,20 @@ public async Task GetBinaryBody()
336336
msg.SetSecurityContext("http://nu.nl");
337337
msg.SetVersionFromETag("123");
338338

339-
var b = await msg.ReadBinaryDataFromMessage();
339+
var b = await msg.ReadBinaryDataFromMessage(Specification.FhirRelease.R4);
340340

341-
b.Content.Should().BeEquivalentTo(data);
341+
b.Content.Should().BeNull();
342342
b.Data.Should().BeEquivalentTo(data);
343343
b.SecurityContext.Reference.Should().Be("http://nu.nl");
344344
b.ContentType.Should().Be("application/crap");
345345
b.Meta.VersionId.Should().Be("123");
346346
b.Meta.LastUpdated.Should().Be(when);
347347
b.Id.Should().Be("4");
348+
349+
b = await msg.ReadBinaryDataFromMessage(Specification.FhirRelease.STU3);
350+
b.Content.Should().BeEquivalentTo(data);
351+
b.Data.Should().BeNull();
352+
348353
}
349354
}
350355
}

0 commit comments

Comments
 (0)