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

Virtual AE & Dynamic DICOMWeb STOW-RS Endpoints #448

Merged
merged 31 commits into from
Aug 16, 2023
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
1e46d60
Update dependencies
mocsharp Aug 10, 2023
a6b4a9f
Fix typos
mocsharp Aug 11, 2023
2a94fb4
Change return type of IInputDataPluginEngine to Tuple
mocsharp Aug 11, 2023
1f480be
Add VirtualApplicationEntity to support dynamic DICOMWeb STOW-RS endp…
mocsharp Aug 11, 2023
679ae31
New APIs for Virtual AE and support dynamic endpoints for STOW-RS
mocsharp Aug 11, 2023
834751f
Update DICOMWeb STOW-RS integration test feature with VAE support
mocsharp Aug 11, 2023
fdc766a
Update changelog
mocsharp Aug 11, 2023
d9531b9
Update user guide and comments
mocsharp Aug 11, 2023
bb4f8b5
Escape user inputs
mocsharp Aug 11, 2023
adc2072
Fix unit test
mocsharp Aug 11, 2023
2bc5acd
Update dependencies
mocsharp Aug 10, 2023
ffaaeac
Fix typos
mocsharp Aug 11, 2023
52a3106
Change return type of IInputDataPluginEngine to Tuple
mocsharp Aug 11, 2023
1ae6807
Add VirtualApplicationEntity to support dynamic DICOMWeb STOW-RS endp…
mocsharp Aug 11, 2023
3a0dc94
New APIs for Virtual AE and support dynamic endpoints for STOW-RS
mocsharp Aug 11, 2023
7b8c944
Update DICOMWeb STOW-RS integration test feature with VAE support
mocsharp Aug 11, 2023
ee72c65
Update changelog
mocsharp Aug 11, 2023
edb1163
Update user guide and comments
mocsharp Aug 11, 2023
edf8c10
Escape user inputs
mocsharp Aug 11, 2023
b4c87a3
Fix unit test
mocsharp Aug 11, 2023
248d49b
Update dependency decisions
mocsharp Aug 11, 2023
e843ad8
Merge branch 'vchang/gh-425' of https://github.com/Project-MONAI/mona…
mocsharp Aug 11, 2023
6096d1d
Remove reference to Microsoft.AspNet.WebApi.Client
mocsharp Aug 11, 2023
016fc0c
Remove unused references
mocsharp Aug 11, 2023
3f7da5b
Fix warnings and update dependency decisions
mocsharp Aug 12, 2023
5defa03
Change endpoint prefix from u/ to vae/
mocsharp Aug 12, 2023
e339241
Use HttpUtility.HtmlEncode
mocsharp Aug 12, 2023
ee133ea
Merge branch 'vchang/gh-425' of https://github.com/Project-MONAI/mona…
mocsharp Aug 12, 2023
f6a5732
Include grype.yaml for ignore vulnerabilities
mocsharp Aug 16, 2023
1147904
Update dependencies
mocsharp Aug 16, 2023
61df4c8
Add .trivyignore
mocsharp Aug 16, 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
80 changes: 73 additions & 7 deletions docs/api/rest/dicomweb-stow.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,19 @@ interface for triggering new workflows.

The *STOW-RS* service provides the following two endpoints.

## POST /dicomweb/studies/[{study-instance-uid}]
## POST /dicomweb/studies/[{study-instance-uid}/]

Triggers a new workflow request with the uploaded DICOM dataset.

> [!IMPORTANT]
> Each HTTP POST request triggers a new workflow request; the service *does not* support waiting
for additional instances like the DIMSE service.

### Example Endpoints

- POST /dicomweb/studies/
- POST /dicomweb/studies/123.001.123.1.4.976.20160825112022727.3/

### Parameters

#### Query Parameters:
Expand All @@ -52,30 +57,91 @@ Response Content Type: `JSON`

| Code | Data Type | Description |
| ---- | --------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ |
| 200 | [DicomDataset](https://github.com/fo-dicom/fo-dicom/blob/development/FO-DICOM.Core/DicomDataset.cs) | All instances are received and stored succesfully. |
| 200 | [DicomDataset](https://github.com/fo-dicom/fo-dicom/blob/development/FO-DICOM.Core/DicomDataset.cs) | All instances are received and stored successfully. |
| 202 | [DicomDataset](https://github.com/fo-dicom/fo-dicom/blob/development/FO-DICOM.Core/DicomDataset.cs) | All instances are received and stored with warnings (e.g. for a mismatched StudyInstanceUID. |
| 204 | `none` | No data is provided. |
| 400 | [Problem details](https://datatracker.ietf.org/doc/html/rfc7807) | Request contains invalid values. |
| 415 | `none` | Unsupported media typ. |
| 415 | `none` | Unsupported media type. |
| 500 | [Problem details](https://datatracker.ietf.org/doc/html/rfc7807) | Server error. |
| 507 | [Problem details](https://datatracker.ietf.org/doc/html/rfc7807) | Insufficient storage. |

---


## POST /dicomweb/{workflow-id}/studies/[{study-instance-uid}]
## POST /dicomweb/{workflow-id}/studies/[{study-instance-uid}/]

Triggers the specified workflow with the uploaded DICOM dataset.

> [!IMPORTANT]
> Each HTTP POST request triggers a new workflow request; the service *does not* support waiting for additional instances like the DIMSE service.


### Example Endpoints

- POST /dicomweb/liver-segmentation/studies/
- POST /dicomweb/my-awesome-workflow/studies/123.001.123.1.4.976.20160825112022727.3/

### Parameters

#### Query Parameters:

| Name | Type | Description |
| ------------------ | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| workflow-id | string | The unique identifier of the workflow registered with the Workflow Manager. |
| study-instance-uid | string | (Optional) Associate the DICOM dataset with a StudyInstanceUID. Note that the service records any mismatch between the StudyInstanceUID header and the provided value in the response as `Warning Reason (0008,1196)` = `B007`. |

#### Request Body:

Supported Content-Types:

- `application/dicom`
- `multipart/related`

### Responses

Response Content Type: `JSON`

| Code | Data Type | Description |
| ---- | --------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- |
| 200 | [DicomDataset](https://github.com/fo-dicom/fo-dicom/blob/development/FO-DICOM.Core/DicomDataset.cs) | All instances are received and stored successfully. |
| 202 | [DicomDataset](https://github.com/fo-dicom/fo-dicom/blob/development/FO-DICOM.Core/DicomDataset.cs) | All instances are received and stored with warnings (e.g. for a mismatched StudyInstanceUID. |
| 204 | `none` | No data is provided. |
| 400 | [Problem details](https://datatracker.ietf.org/doc/html/rfc7807) | Request contains invalid values. |
| 415 | `none` | Unsupported media type. |
| 500 | [Problem details](https://datatracker.ietf.org/doc/html/rfc7807) | Server error. |
| 507 | [Problem details](https://datatracker.ietf.org/doc/html/rfc7807) | Insufficient storage. |

---


## POST /dicomweb/u/{aet}/[{workflow-id}/]studies/[{study-instance-uid}/]

A DICOMWeb STOW-RS endpoint associated with the specified *Virtual Application Entity*.

This endpoint can either trigger workflows defined in a *Virtual Application Entity* or trigger the workflow specified in the URL segment where the latter
takes precedence when specified.


> [!IMPORTANT]
> Each HTTP POST request triggers one or more workflow requests depending on number of workflows defined in the *Virtual Application Entity*;
the service *does not* support waiting for additional instances like the DIMSE service.


### Example Endpoints

- POST /dicomweb/u/my-aet/studies/
- POST /dicomweb/u/my-aet/studies/123.001.123.1.4.976.20160825112022727.3/
- POST /dicomweb/u/my-aet/my-awesome-workflow/studies/
- POST /dicomweb/u/my-aet/my-awesome-workflow/studies/123.001.123.1.4.976.20160825112022727.3/


### Parameters

#### Query Parameters:

| Name | Type | Description |
| ------------------ | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| aet | string | A registered Virtual Application Entity. |
| workflow-id | string | The unique identifier of the workflow registered with the Workflow Manager. |
| study-instance-uid | string | (Optional) Associate the DICOM dataset with a StudyInstanceUID. Note that the service records any mismatch between the StudyInstanceUID header and the provided value in the response as `Warning Reason (0008,1196)` = `B007`. |

Expand All @@ -92,10 +158,10 @@ Response Content Type: `JSON`

| Code | Data Type | Description |
| ---- | --------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- |
| 200 | [DicomDataset](https://github.com/fo-dicom/fo-dicom/blob/development/FO-DICOM.Core/DicomDataset.cs) | All instances are received and stored succesfully. |
| 200 | [DicomDataset](https://github.com/fo-dicom/fo-dicom/blob/development/FO-DICOM.Core/DicomDataset.cs) | All instances are received and stored successfully. |
| 202 | [DicomDataset](https://github.com/fo-dicom/fo-dicom/blob/development/FO-DICOM.Core/DicomDataset.cs) | All instances are received and stored with warnings (e.g. for a mismatched StudyInstanceUID. |
| 204 | `none` | No data is provided. |
| 400 | [Problem details](https://datatracker.ietf.org/doc/html/rfc7807) | Request contains invalid values. |
| 415 | `none` | Unsupported media type |
| 500 | [Problem details](https://datatracker.ietf.org/doc/html/rfc7807) | Server error |
| 415 | `none` | Unsupported media type. |
| 500 | [Problem details](https://datatracker.ietf.org/doc/html/rfc7807) | Server error. |
| 507 | [Problem details](https://datatracker.ietf.org/doc/html/rfc7807) | Insufficient storage. |
1 change: 1 addition & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
[GitHub Milestone 0.4.0](https://github.com/Project-MONAI/monai-deploy-informatics-gateway/milestone/5)

- gh-435 Fix CLI to read log dir path from NLog config file.
- New Virtual Application Entity support for DICOMWeb STOW-RS APIs to enable dynamic endpoints.


## 0.3.21
Expand Down
2 changes: 2 additions & 0 deletions src/Api/IInputDataPlugin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ namespace Monai.Deploy.InformaticsGateway.Api
/// </summary>
public interface IInputDataPlugin
{
string Name { get; }

Task<(DicomFile dicomFile, FileStorageMetadata fileMetadata)> Execute(DicomFile dicomFile, FileStorageMetadata fileMetadata);
}
}
7 changes: 4 additions & 3 deletions src/Api/IInputDataPluginEngine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
* limitations under the License.
*/

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using FellowOakDicom;
Expand All @@ -28,14 +29,14 @@ namespace Monai.Deploy.InformaticsGateway.Api
/// <list type="bullet">
/// <item>SCP: A list of plug-ins can be configured with each AET, and each plug-in is executed in the order stored, enabling piping of the incoming data before each file is uploaded to the storage service.</item>
/// <item>Incoming data is processed one file at a time and SHALL not wait for the entire study to arrive.</item>
/// <item>Plugins MUST be lightweight and not hinder the upload process.</item>
/// <item>Plugins SHALL not accumulate files in memory or storage for bulk processing.</item>
/// <item>Plug-ins MUST be lightweight and not hinder the upload process.</item>
/// <item>Plug-ins SHALL not accumulate files in memory or storage for bulk processing.</item>
/// </list>
/// </summary>
public interface IInputDataPluginEngine
{
void Configure(IReadOnlyList<string> pluginAssemblies);

Task<(DicomFile dicomFile, FileStorageMetadata fileMetadata)> ExecutePlugins(DicomFile dicomFile, FileStorageMetadata fileMetadata);
Task<Tuple<DicomFile, FileStorageMetadata>> ExecutePlugins(DicomFile dicomFile, FileStorageMetadata fileMetadata);
}
}
2 changes: 2 additions & 0 deletions src/Api/IOutputDataPlugin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ namespace Monai.Deploy.InformaticsGateway.Api
/// </summary>
public interface IOutputDataPlugin
{
string Name { get; }

Task<(DicomFile dicomFile, ExportRequestDataMessage exportRequestDataMessage)> Execute(DicomFile dicomFile, ExportRequestDataMessage exportRequestDataMessage);
}
}
4 changes: 2 additions & 2 deletions src/Api/Monai.Deploy.InformaticsGateway.Api.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@

<ItemGroup>
<PackageReference Include="Macross.Json.Extensions" Version="3.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Abstractions" Version="6.0.20" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Abstractions" Version="6.0.21" />
<PackageReference Include="Monai.Deploy.Messaging" Version="0.1.24" />
<PackageReference Include="Monai.Deploy.Storage" Version="0.2.16" />
<PackageReference Include="Monai.Deploy.Storage" Version="0.2.17" />
</ItemGroup>

<ItemGroup>
Expand Down
4 changes: 2 additions & 2 deletions src/Api/Storage/FhirFileStorageMetadata.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public sealed record FhirFileStorageMetadata : FileStorageMetadata
{
public const string FhirSubDirectoryName = "ehr";
public const string JsonFilExtension = ".json";
public const string XmlFilExtension = ".xml";
public const string XmlFileExtension = ".xml";

/// <summary>
/// The transaction ID of the original ACR request.
Expand Down Expand Up @@ -77,7 +77,7 @@ public FhirFileStorageMetadata(string transactionId, string resourceType, string
ResourceType = resourceType;
ResourceId = resourceId;

var fileExtension = fhirFileFormat == FhirStorageFormat.Json ? JsonFilExtension : XmlFilExtension;
var fileExtension = fhirFileFormat == FhirStorageFormat.Json ? JsonFilExtension : XmlFileExtension;
File = new StorageObjectMetadata(fileExtension)
{
TemporaryPath = string.Join(PathSeparator, transactionId, DataTypeDirectoryName, $"{Guid.NewGuid()}{fileExtension}"),
Expand Down
4 changes: 2 additions & 2 deletions src/Api/Test/Storage/FhirFileStorageMetadataTest.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2021-2022 MONAI Consortium
* Copyright 2021-2023 MONAI Consortium
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -24,7 +24,7 @@ namespace Monai.Deploy.InformaticsGateway.Api.Test
public class FhirFileStorageMetadataTest
{
[Theory(DisplayName = "Shall return FHIR resource upload path")]
[InlineData(FhirStorageFormat.Xml, FhirFileStorageMetadata.XmlFilExtension)]
[InlineData(FhirStorageFormat.Xml, FhirFileStorageMetadata.XmlFileExtension)]
[InlineData(FhirStorageFormat.Json, FhirFileStorageMetadata.JsonFilExtension)]
public void GivenFhirFileStorageMetadataWithSpecifiedFormat_ExpectToHaveCorrectFileExtension(FhirStorageFormat fileFormat, string fileExtension)
{
Expand Down
Loading
Loading