Skip to content

New APIs to get a list of input/output data plug-ins #437

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

Merged
merged 9 commits into from
Aug 10, 2023
Merged
Show file tree
Hide file tree
Changes from 6 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
70 changes: 70 additions & 0 deletions docs/api/rest/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,41 @@ curl --location --request DELETE 'http://localhost:5000/config/ae/breast-tumor'

---

## GET /config/ae/plug-ins

Returns a list of data input plug-ins that can be used with SCP Application Entity.

### Parameters

N/A

### Responses

Response Content Type: JSON - An object containing zero or more key-value pairs where the key is the name of the plug-in and the value is the fully qualified assembly type name of the plug-in.

| Code | Description |
| ---- | --------------------------------------------------------------------------------------------------------------------------------------- |
| 200 | Plug-ins retrieved successfully. |
| 500 | Server error. The response will be a [Problem details](https://datatracker.ietf.org/doc/html/rfc7807) object with server error details. |

### Example Request

```bash
curl --location --request GET 'http://localhost:5000/config/ae/plug-ins'
```

### Example Response

```json
{
"testInputDataPluginAddWorkflow": "Monai.Deploy.InformaticsGateway.Test.Plugins.TestInputDataPluginAddWorkflow, Monai.Deploy.InformaticsGateway.Test.Plugins, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
"testInputDataPluginResumeWorkflow": "Monai.Deploy.InformaticsGateway.Test.Plugins.TestInputDataPluginResumeWorkflow, Monai.Deploy.InformaticsGateway.Test.Plugins, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
"testInputDataPluginModifyDicomFile": "Monai.Deploy.InformaticsGateway.Test.Plugins.TestInputDataPluginModifyDicomFile, Monai.Deploy.InformaticsGateway.Test.Plugins, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
}
```

---

## GET /config/source

Returns a list of calling (source) AE Titles configured on the Informatics Gateway.
Expand Down Expand Up @@ -768,3 +803,38 @@ curl --location --request DELETE 'http://localhost:5000/config/destination/USEAS
"hostIp": "10.20.3.4"
}
```
---

## GET /config/destination/plug-ins

Returns a list of data output plug-ins that can be used with SCP Application Entity.

### Parameters

N/A

### Responses

Response Content Type: JSON - An object containing zero or more key-value pairs where the key is the name of the plug-in and the value is the fully qualified assembly type name of the plug-in.

| Code | Description |
| ---- | --------------------------------------------------------------------------------------------------------------------------------------- |
| 200 | Plug-ins retrieved successfully. |
| 500 | Server error. The response will be a [Problem details](https://datatracker.ietf.org/doc/html/rfc7807) object with server error details. |

### Example Request

```bash
curl --location --request GET 'http://localhost:5000/config/destination/plug-ins'
```

### Example Response

```json
{
"testOutputDataPluginAddMessage": "Monai.Deploy.InformaticsGateway.Test.Plugins.TestOutputDataPluginAddMessage, Monai.Deploy.InformaticsGateway.Test.Plugins, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
"testOutputDataPluginModifyDicomFile": "Monai.Deploy.InformaticsGateway.Test.Plugins.TestOutputDataPluginModifyDicomFile, Monai.Deploy.InformaticsGateway.Test.Plugins, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
}
```

---
18 changes: 9 additions & 9 deletions docs/docfx.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@
"src": [
{
"files": [
"Api/bin/Release/net6.0/Monai.Deploy.InformaticsGateway.Api.dll",
"Client/bin/Release/net6.0/Monai.Deploy.InformaticsGateway.Client.dll",
"Client.Common/bin/Release/net6.0/Monai.Deploy.InformaticsGateway.Client.Common.dll",
"Common/bin/Release/net6.0/Monai.Deploy.InformaticsGateway.Common.dll",
"Configuration/bin/Release/net6.0/Monai.Deploy.InformaticsGateway.Configuration.dll",
"Database/bin/Release/net6.0/Monai.Deploy.InformaticsGateway.Database.dll",
"Database/Api/bin/Release/net6.0/Monai.Deploy.InformaticsGateway.Database.Api.dll",
"DicomWebClient/bin/Release/net6.0/Monai.Deploy.InformaticsGateway.DicomWeb.Client.dll"
"Api/Monai.Deploy.InformaticsGateway.Api.csproj",
"Client/Monai.Deploy.InformaticsGateway.Client.csproj",
"Client.Common/Monai.Deploy.InformaticsGateway.Client.Common.csproj",
"Common/Monai.Deploy.InformaticsGateway.Common.csproj",
"Configuration/Monai.Deploy.InformaticsGateway.Configuration.csproj",
"Database/Monai.Deploy.InformaticsGateway.Database.csproj",
"Database/Api/Monai.Deploy.InformaticsGateway.Database.Api.csproj",
"DicomWebClient/Monai.Deploy.InformaticsGateway.DicomWeb.Client.csproj"
],
"exclude": [
"**/obj/**",
Expand Down Expand Up @@ -61,7 +61,7 @@
"_enableSearch": true,
"_appFaviconPath": "images/favicon.ico",
"_appLogoPath": "images/MONAI-logo-color.svg",
"_appFooter": "Copyright © 2022 <a href=\"https://monai.io/\">Project MONAI</a><br>Generated by <strong>DocFX</strong>",
"_appFooter": "Copyright © 2022-2023 <a href=\"https://monai.io/\">Project MONAI</a><br>Generated by <strong>DocFX</strong>",
"_gitContribute": {
"repo": "https://github.com/Project-MONAI/monai-deploy-informatics-gateway.git",
"branch": "main",
Expand Down
4 changes: 2 additions & 2 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ MIG contains the following standard protocols for communicating with medical dev
* **DICOMweb client**: QIDO-RS, WADO-RS, STOW-RS
* **FHIR client**: GET

[!Note]
The ACR DSI API uses the DICOMweb client and FHIR client.
> [!Note]
> The ACR DSI API uses the DICOMweb client and FHIR client.

### DICOM SCP

Expand Down
47 changes: 47 additions & 0 deletions docs/setup/setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,30 @@ This section outlines the steps to download and install the Informatics Gateway
## Runtime Requirements

* Docker 20.10.12 or higher
* [Database service](#database-configuration)
* [Message Broker service](#message-broker)
* [Storage service](#storage-service)

For development requirements, refer to the [Informatics Gateway README.md](https://github.com/Project-MONAI/monai-deploy-informatics-gateway).

> [!Note]
> Use [MONAI Deploy Express](https://github.com/Project-MONAI/monai-deploy/tree/main/deploy/monai-deploy-express) to quickly
> bring up all required services, including the Informatics Gateway.
>
> Skip to [Configure Informatics Gateway](#configure-informatics-gateway) if you are using MONAI Deploy Express.



## Installation

### Informatics Gateway CLI

Download and install the Informatics Gateway CLI from the [Releases](https://github.com/Project-MONAI/monai-deploy-informatics-gateway/releases) section of
the repository and install it.

> [!Note]
> We use `v0.2.0` release as an example here, always download the latest from the [Releases](https://github.com/Project-MONAI/monai-deploy-informatics-gateway/releases) section.

#### On Linux

```bash
Expand Down Expand Up @@ -80,6 +94,9 @@ mig-cli.exe config init
mig-cli.exe config endpoint http://localhost:5000 #skip if running locally
```

> [!Note]
> For [MONAI Deploy Express](https://github.com/Project-MONAI/monai-deploy/tree/main/deploy/monai-deploy-express), use `http://localhost:5003`.

The first command extracts the default `appsettings.json` file into the home directory:

* Linux: `~/.mig/appsettings.json`
Expand Down Expand Up @@ -144,6 +161,27 @@ If the database system is supported by [Microsoft Entity Framework](https://lear
For other database systems that are not listed in the link above, simply implement the [Repository APIs](xref:Monai.Deploy.InformaticsGateway.Database.Api.Repositories), update the [Database Manager](xref:Monai.Deploy.InformaticsGateway.Database.DatabaseManager) to support the new database type and optionally, implement the [IDabaseMigrationManager](xref:Monai.Deploy.InformaticsGateway.Database.Api.IDatabaseMigrationManager).


## Authentication

Authentication is disabled by default. To enable authentication using OpenID, edit the `appsettings.json` file and set `bypassAuthentication` to `true`:

```json
{
"MonaiDeployAuthentication": {
"bypassAuthentication": true,
"openId": {
"realm": "{realm}",
"realmKey": "{realm-secret-key}",
"clientId": "{client-id}",
"audiences": [ "{audiences}" ],
"roleClaimType": "{roles}",
...
}
```

Refer to [Authentication Setup Using Keycloak](https://github.com/Project-MONAI/monai-deploy-workflow-manager/blob/develop/guidelines/mwm-auth.md) for additional details.


## Storage Consideration & Configuration

The Informatics Gateway operates on two storage locations. In the first location, the incoming data for data grouping is temporarily stored. In the second location, the Informatics Gateway uploads grouped datasets to final storage shared by other MONAI Deploy sub-systems.
Expand Down Expand Up @@ -358,3 +396,12 @@ The command adds a DICOM export destination with AE Title `WORKSTATION1` at IP `
## Logging

See [schema](./schema.md#logging) page for additional information on logging.

## Data Plug-ins

You may write your own data plug-ins to manipulate incoming data before they are saved to the storage service or outgoing data right before they are exported.

To write an input data plug-in, implement the [IInputDataPlugin](xref:Monai.Deploy.InformaticsGateway.Api.IInputDataPlugin) interface and put the assmblye dll in the
plug-ins directories. Similarly for output data plug-ins, implement the [IOutputDataPlugin](xref:Monai.Deploy.InformaticsGateway.Api.IOutputDataPlugin) interface.

Refer to [Configuration API](../api/rest/config.md) page to retrieve available [input](../api/rest/config.md#get-configaeplug-ins) and [output](../api/rest/config.md#get-configdestinationplug-ins) data plug-ins.
34 changes: 34 additions & 0 deletions src/Api/PluginNameAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

using System;
using Ardalis.GuardClauses;

namespace Monai.Deploy.InformaticsGateway.Api
{
[AttributeUsage(AttributeTargets.Class)]
public class PluginNameAttribute : Attribute
{
public string Name { get; set; }

public PluginNameAttribute(string name)
{
Guard.Against.NullOrWhiteSpace(name, nameof(name));

Name = name;
}
}
}
4 changes: 2 additions & 2 deletions src/Api/Storage/DicomFileStorageMetadata.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 Down Expand Up @@ -34,7 +34,7 @@ public sealed record DicomFileStorageMetadata : FileStorageMetadata
/// <summary>
/// The calling AE title of the DICOM instance.
/// For ACR, this is the Transaction ID of the original request.
/// Note: this value is same as <seealso cref="Source"></c>
/// Note: this value is same as <see cref="FileStorageMetadata.Source"/>
/// </summary>
[JsonIgnore]
public string CallingAeTitle { get => Source; }
Expand Down
2 changes: 1 addition & 1 deletion src/Api/Storage/FhirFileStorageMetadata.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public sealed record FhirFileStorageMetadata : FileStorageMetadata

/// <summary>
/// The transaction ID of the original ACR request.
/// Note: this value is same as <seealso cref="Source"></c>
/// Note: this value is same as <see cref="FileStorageMetadata.Source"/>
/// </summary>
[JsonIgnore]
public string TransactionId { get => Source; }
Expand Down
72 changes: 72 additions & 0 deletions src/CLI/Commands/AetCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ public AetCommand() : base("aet", "Configure SCP Application Entities")
SetupEditAetCommand();
SetupRemoveAetCommand();
SetupListAetCommand();
SetupPluginsCommand();
}

private void SetupListAetCommand()
Expand Down Expand Up @@ -146,6 +147,14 @@ private void SetupEditAetCommand()
addCommand.Handler = CommandHandler.Create<MonaiApplicationEntity, IHost, bool, CancellationToken>(EditAeTitleHandlerAsync);
}

private void SetupPluginsCommand()
{
var pluginsCommand = new Command("plugins", "List all available plug-ins for SCP Application Entities");
AddCommand(pluginsCommand);

pluginsCommand.Handler = CommandHandler.Create<IHost, bool, CancellationToken>(ListPluginsHandlerAsync);
}

private async Task<int> ListAeTitlehandlerAsync(IHost host, bool verbose, CancellationToken cancellationToken)
{
Guard.Against.Null(host, nameof(host));
Expand Down Expand Up @@ -358,5 +367,68 @@ private async Task<int> EditAeTitleHandlerAsync(MonaiApplicationEntity entity, I
}
return ExitCodes.Success;
}

private async Task<int> ListPluginsHandlerAsync(IHost host, bool verbose, CancellationToken cancellationToken)
{
Guard.Against.Null(host, nameof(host));

LogVerbose(verbose, host, "Configuring services...");

var console = host.Services.GetRequiredService<IConsole>();
var configService = host.Services.GetRequiredService<IConfigurationService>();
var client = host.Services.GetRequiredService<IInformaticsGatewayClient>();
var consoleRegion = host.Services.GetRequiredService<IConsoleRegion>();
var logger = CreateLogger<AetCommand>(host);

Guard.Against.Null(logger, nameof(logger), "Logger is unavailable.");
Guard.Against.Null(console, nameof(console), "Console service is unavailable.");
Guard.Against.Null(configService, nameof(configService), "Configuration service is unavailable.");
Guard.Against.Null(client, nameof(client), $"{Strings.ApplicationName} client is unavailable.");
Guard.Against.Null(consoleRegion, nameof(consoleRegion), "Console region is unavailable.");

IDictionary<string, string> items = null;
try
{
CheckConfiguration(configService);
client.ConfigureServiceUris(configService.Configurations.InformaticsGatewayServerUri);
LogVerbose(verbose, host, $"Connecting to {Strings.ApplicationName} at {configService.Configurations.InformaticsGatewayServerEndpoint}...");
LogVerbose(verbose, host, $"Retrieving MONAI SCP AE Titles...");
items = await client.MonaiScpAeTitle.Plugins(cancellationToken).ConfigureAwait(false);
}
catch (ConfigurationException ex)
{
logger.ConfigurationException(ex.Message);
return ExitCodes.Config_NotConfigured;
}
catch (Exception ex)
{
logger.ErrorListingDataInputPlugins(ex.Message);
return ExitCodes.MonaiScp_ErrorPlugins;
}

if (items.IsNullOrEmpty())
{
logger.NoAeTitlesFound();
}
else
{
if (console is ITerminal terminal)
{
terminal.Clear();
}
var consoleRenderer = new ConsoleRenderer(console);

var table = new TableView<KeyValuePair<string, string>>
{
Items = items.Select(x => new KeyValuePair<string, string>(x.Key, x.Value)).ToList()
};
table.AddColumn(p => p.Key, new ContentView("Name".Underline()));
table.AddColumn(p => p.Value, new ContentView("Assembly Name".Underline()));
table.Render(consoleRenderer, consoleRegion.GetDefaultConsoleRegion());

logger.ListedNItems(items.Count);
}
return ExitCodes.Success;
}
}
}
Loading