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

Allow multipart/related media type for retrieved instance transaction #735

Merged
merged 5 commits into from
Apr 23, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 4 additions & 0 deletions docs/resources/conformance-statement.md
Original file line number Diff line number Diff line change
Expand Up @@ -181,9 +181,13 @@ The following `Accept` header(s) are supported for retrieving instances within a
The following `Accept` header(s) are supported for retrieving a specific instance:

- `application/dicom; transfer-syntax=*`
- `multipart/related; type="application/dicom"; transfer-syntax=*`
- `application/dicom;` (when transfer-syntax is not specified, 1.2.840.10008.1.2.1 is used as default)
- `multipart/related; type="application/dicom"` (when transfer-syntax is not specified, 1.2.840.10008.1.2.1 is used as default)
- `application/dicom; transfer-syntax=1.2.840.10008.1.2.1`
- `multipart/related; type="application/dicom"; transfer-syntax=1.2.840.10008.1.2.1`
- `application/dicom; transfer-syntax=1.2.840.10008.1.2.4.90`
- `multipart/related; type="application/dicom"; transfer-syntax=1.2.840.10008.1.2.4.90`

### Retrieve Frames

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
using Microsoft.Health.Dicom.Core.Features.Retrieve;
using Microsoft.Health.Dicom.Core.Messages;
using Microsoft.Health.Dicom.Core.Messages.Retrieve;
using Microsoft.Health.Dicom.Core.Web;
using Microsoft.Health.Dicom.Tests.Common;
using Microsoft.IO;
using NSubstitute;
Expand Down Expand Up @@ -187,8 +188,10 @@ await Assert.ThrowsAsync<InstanceNotFoundException>(() => _retrieveResourceServi
DefaultCancellationToken));
}

[Fact]
public async Task GivenStoredInstances_WhenRetrieveRequestForInstance_ThenInstanceIsRetrievedSuccessfully()
[Theory]
[InlineData(PayloadTypes.SinglePart)]
[InlineData(PayloadTypes.MultipartRelated)]
public async Task GivenStoredInstances_WhenRetrieveRequestForInstance_ThenInstanceIsRetrievedSuccessfully(PayloadTypes payloadTypes)
{
// Add multiple instances to validate that we return the requested instance and ignore the other(s).
List<VersionedInstanceIdentifier> versionedInstanceIdentifiers = SetupInstanceIdentifiersList(ResourceType.Instance);
Expand All @@ -202,12 +205,15 @@ public async Task GivenStoredInstances_WhenRetrieveRequestForInstance_ThenInstan
_studyInstanceUid,
_firstSeriesInstanceUid,
_sopInstanceUid,
new[] { AcceptHeaderHelpers.CreateAcceptHeaderForGetInstance() }),
new[] { AcceptHeaderHelpers.CreateAcceptHeaderForGetInstance(payloadTypes: payloadTypes) }),
DefaultCancellationToken);

// Validate response status code and ensure response stream has expected file - it should be equivalent to what the store was set up to return.
ValidateResponseStreams(new List<DicomFile>() { streamAndStoredFile.Key }, response.ResponseStreams);

// Validate content type
Assert.Equal(KnownContentTypes.ApplicationDicom, response.ContentType);

// Dispose created streams.
streamAndStoredFile.Value.Dispose();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public class RetrieveTransferSyntaxHandler : IRetrieveTransferSyntaxHandler
{
{ ResourceType.Study, DescriptorsForGetNonFrameResource(PayloadTypes.MultipartRelated) },
{ ResourceType.Series, DescriptorsForGetNonFrameResource(PayloadTypes.MultipartRelated) },
{ ResourceType.Instance, DescriptorsForGetNonFrameResource(PayloadTypes.SinglePart) },
{ ResourceType.Instance, DescriptorsForGetNonFrameResource(PayloadTypes.SinglePartOrMultipartRelated) },
{ ResourceType.Frames, DescriptorsForGetFrame() },
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,11 @@ public static AcceptHeader CreateAcceptHeaderForGetSeries(string transferSyntax
quality: quality);
}

public static AcceptHeader CreateAcceptHeaderForGetInstance(string transferSyntax = "*", string mediaType = KnownContentTypes.ApplicationDicom, double? quality = null)
public static AcceptHeader CreateAcceptHeaderForGetInstance(string transferSyntax = "*", string mediaType = KnownContentTypes.ApplicationDicom, double? quality = null, PayloadTypes payloadTypes = PayloadTypes.SinglePart)
{
return CreateAcceptHeader(
transferSyntax: transferSyntax,
payloadType: PayloadTypes.SinglePart,
payloadType: payloadTypes,
mediaType: mediaType,
quality: quality);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public partial class RetrieveTransactionResourceTests
[InlineData(FromJPEG2000LosslessToExplicitVRLittleEndianTestFolder, null)]
[InlineData(FromJPEG2000LosslessToExplicitVRLittleEndianTestFolder, "1.2.840.10008.1.2.1")]
[InlineData(FromExplicitVRLittleEndianToJPEG2000LosslessTestFolder, "1.2.840.10008.1.2.4.90")]
public async Task GivenSupportedAcceptHeaders_WhenRetrieveInstance_ThenServerShouldReturnExpectedContent(string testDataFolder, string transferSyntax)
public async Task GivenSinglePartAcceptHeader_WhenRetrieveInstance_ThenServerShouldReturnExpectedContent(string testDataFolder, string transferSyntax)
{
TranscoderTestData transcoderTestData = TranscoderTestDataHelper.GetTestData(testDataFolder);
DicomFile inputDicomFile = DicomFile.Open(transcoderTestData.InputDicomFile);
Expand All @@ -53,7 +53,26 @@ public async Task GivenSupportedAcceptHeaders_WhenRetrieveInstance_ThenServerSho
}

[Theory]
[InlineData(false, DicomWebConstants.ApplicationDicomMediaType, DicomWebConstants.OriginalDicomTransferSyntax)] // use multipe part instead of single part
[InlineData(RequestOriginalContentTestFolder, "*")]
[InlineData(FromJPEG2000LosslessToExplicitVRLittleEndianTestFolder, null)]
[InlineData(FromJPEG2000LosslessToExplicitVRLittleEndianTestFolder, "1.2.840.10008.1.2.1")]
public async Task GivenMultipartAcceptHeader_WhenRetrieveInstance_ThenServerShouldReturnExpectedContent(string testDataFolder, string transferSyntax)
{
TranscoderTestData transcoderTestData = TranscoderTestDataHelper.GetTestData(testDataFolder);
DicomFile inputDicomFile = DicomFile.Open(transcoderTestData.InputDicomFile);
var instanceId = RandomizeInstanceIdentifier(inputDicomFile.Dataset);

await InternalStoreAsync(new[] { inputDicomFile });

var requestUri = new Uri(string.Format(DicomWebConstants.BaseInstanceUriFormat, instanceId.StudyInstanceUid, instanceId.SeriesInstanceUid, instanceId.SopInstanceUid), UriKind.Relative);

using HttpRequestMessage request = new HttpRequestMessageBuilder().Build(requestUri, singlePart: false, DicomWebConstants.ApplicationDicomMediaType, transferSyntax);
using HttpResponseMessage response = await _client.HttpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);

Assert.Equal(HttpStatusCode.OK, response.StatusCode);
}

[Theory]
[InlineData(true, DicomWebConstants.ApplicationOctetStreamMediaType, DicomWebConstants.OriginalDicomTransferSyntax)] // unsupported media type image/png
[InlineData(true, DicomWebConstants.ApplicationDicomMediaType, "1.2.840.10008.1.2.4.100")] // unsupported transfer syntax MPEG2
public async Task GivenUnsupportedAcceptHeaders_WhenRetrieveInstance_ThenServerShouldReturnNotAcceptable(bool singlePart, string mediaType, string transferSyntax)
Expand Down