From 18fbfb755fa29ffd769b41fc43c15d4b8ba0a0f2 Mon Sep 17 00:00:00 2001 From: Neil South Date: Mon, 15 Jan 2024 10:06:26 +0000 Subject: [PATCH] adding HL7 message plugin support Signed-off-by: Neil South --- .github/workflows/ci.yml | 4 +- src/Api/Hl7ApplicationConfigEntity.cs | 8 +- .../HealthLevel7 => Api/Mllp}/IMllpClient.cs | 6 +- .../HealthLevel7 => Api/Mllp}/IMllpExtract.cs | 8 +- .../HealthLevel7 => Api/Mllp}/IMllpService.cs | 2 +- .../Mllp}/MllpClientResult.cs | 4 +- ...Monai.Deploy.InformaticsGateway.Api.csproj | 1 + src/Api/PlugIns/IInputHL7DataPlugIn.cs | 35 ++ src/Api/PlugIns/IInputHL7DataPlugInEngine.cs | 42 ++ src/Api/Storage/Hl7FileStorageMetadata.cs | 2 +- src/Api/Test/packages.lock.json | 6 + src/Api/packages.lock.json | 6 + src/CLI/Test/packages.lock.json | 6 + src/CLI/packages.lock.json | 6 + src/Client/Test/packages.lock.json | 1 + src/Client/packages.lock.json | 6 + src/Configuration/Test/packages.lock.json | 6 + src/Configuration/packages.lock.json | 6 + src/Database/Api/Test/packages.lock.json | 6 + src/Database/Api/packages.lock.json | 6 + src/Database/DatabaseMigrationManager.cs | 2 +- .../20231204113501_Hl7DEstinationAndConfig.cs | 2 +- .../20231207154732_Hl7Plugins.Designer.cs | 508 ++++++++++++++++++ .../Migrations/20231207154732_Hl7Plugins.cs | 27 + .../InformaticsGatewayContextModelSnapshot.cs | 3 + .../Hl7ApplicationConfigRepository.cs | 6 +- .../EntityFramework/Test/packages.lock.json | 6 + .../EntityFramework/packages.lock.json | 6 + .../Integration.Test/packages.lock.json | 6 + .../HL7DestinationEntityRepository.cs | 5 +- .../Hl7ApplicationConfigRepository.cs | 5 +- src/Database/MongoDB/packages.lock.json | 6 + src/Database/packages.lock.json | 6 + .../Logging/Log.800.Hl7Service.cs | 21 +- src/InformaticsGateway/Program.cs | 7 +- .../Common/IInputDataPluginEngineFactory.cs | 7 + .../Services/Common/InputDataPluginEngine.cs | 11 +- .../Common/InputHL7DataPlugInEngine.cs | 94 ++++ .../Services/Common/OutputDataPluginEngine.cs | 2 +- .../Services/Connectors/PayloadAssembler.cs | 22 + .../PayloadNotificationActionHandler.cs | 3 + .../Services/Export/ExportServiceBase.cs | 3 +- .../Services/Export/Hl7ExportService.cs | 6 +- .../HealthLevel7/IMllpClientFactory.cs | 2 +- .../Services/HealthLevel7/MllpClient.cs | 19 +- .../Services/HealthLevel7/MllpExtract.cs | 62 ++- .../Services/HealthLevel7/MllpService.cs | 51 +- .../Services/Http/Startup.cs | 2 - .../Services/Scp/ApplicationEntityHandler.cs | 2 +- .../Test/Plug-ins/TestInputHL7DataPlugs.cs | 39 ++ .../InputHL7DataPlugInEngineFactoryTest.cs | 69 +++ .../Common/InputHL7DataPlugInEngineTest.cs | 164 ++++++ .../Services/Export/ExportHl7ServiceTests.cs | 1 + .../Services/HealthLevel7/MllPExtractTests.cs | 17 +- .../Services/HealthLevel7/MllpClientTest.cs | 3 +- .../Services/HealthLevel7/MllpServiceTest.cs | 23 +- .../Test/packages.lock.json | 1 + src/InformaticsGateway/nlog.config | 1 + src/InformaticsGateway/packages.lock.json | 1 + .../Test/packages.lock.json | 6 + .../RemoteAppExecution/packages.lock.json | 6 + tests/Integration.Test/Common/DataProvider.cs | 3 +- .../StepDefinitions/Hl7StepDefinitions.cs | 2 +- ...emoteAppExecutionPlugInsStepDefinitions.cs | 1 - tests/Integration.Test/appsettings.json | 19 + tests/Integration.Test/packages.lock.json | 127 +++++ 66 files changed, 1455 insertions(+), 97 deletions(-) mode change 100644 => 100755 .github/workflows/ci.yml rename src/{InformaticsGateway/Services/HealthLevel7 => Api/Mllp}/IMllpClient.cs (88%) mode change 100644 => 100755 rename src/{InformaticsGateway/Services/HealthLevel7 => Api/Mllp}/IMllpExtract.cs (78%) rename src/{InformaticsGateway/Services/HealthLevel7 => Api/Mllp}/IMllpService.cs (92%) rename src/{InformaticsGateway/Services/HealthLevel7 => Api/Mllp}/MllpClientResult.cs (91%) create mode 100755 src/Api/PlugIns/IInputHL7DataPlugIn.cs create mode 100755 src/Api/PlugIns/IInputHL7DataPlugInEngine.cs mode change 100644 => 100755 src/Database/DatabaseMigrationManager.cs create mode 100755 src/Database/EntityFramework/Migrations/20231207154732_Hl7Plugins.Designer.cs create mode 100755 src/Database/EntityFramework/Migrations/20231207154732_Hl7Plugins.cs mode change 100644 => 100755 src/Database/EntityFramework/Repositories/Hl7ApplicationConfigRepository.cs mode change 100644 => 100755 src/Database/MongoDB/Repositories/HL7DestinationEntityRepository.cs mode change 100644 => 100755 src/InformaticsGateway/Services/Common/IInputDataPluginEngineFactory.cs mode change 100644 => 100755 src/InformaticsGateway/Services/Common/InputDataPluginEngine.cs create mode 100755 src/InformaticsGateway/Services/Common/InputHL7DataPlugInEngine.cs create mode 100755 src/InformaticsGateway/Test/Plug-ins/TestInputHL7DataPlugs.cs create mode 100755 src/InformaticsGateway/Test/Services/Common/InputHL7DataPlugInEngineFactoryTest.cs create mode 100755 src/InformaticsGateway/Test/Services/Common/InputHL7DataPlugInEngineTest.cs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml old mode 100644 new mode 100755 index ca6a5527c..51a009a7b --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -183,11 +183,11 @@ jobs: ports: - 27017:27017 steps: - - name: Set up JDK 11 + - name: Set up JDK 17 uses: actions/setup-java@v3 with: distribution: zulu - java-version: '11' + java-version: '17' - uses: actions/setup-dotnet@v3 with: diff --git a/src/Api/Hl7ApplicationConfigEntity.cs b/src/Api/Hl7ApplicationConfigEntity.cs index aefa605cf..3cc3160d9 100755 --- a/src/Api/Hl7ApplicationConfigEntity.cs +++ b/src/Api/Hl7ApplicationConfigEntity.cs @@ -26,7 +26,7 @@ namespace Monai.Deploy.InformaticsGateway.Api { - public class Hl7ApplicationConfigEntity : MongoDBEntityBase + public sealed class Hl7ApplicationConfigEntity : MongoDBEntityBase { /// /// Gets or sets the name of a Hl7 application entity. @@ -60,6 +60,8 @@ public class Hl7ApplicationConfigEntity : MongoDBEntityBase /// public List PlugInAssemblies { get; set; } = default!; + public DateTime LastModified { get; set; } = DateTime.UtcNow; + public IEnumerable Validate() { var errors = new List(); @@ -114,7 +116,7 @@ public override string ToString() } //string key, string value - public class StringKeyValuePair : IKeyValuePair + public sealed class StringKeyValuePair : IKeyValuePair, IEquatable { [Key] public string Key { get; set; } = string.Empty; @@ -136,7 +138,7 @@ public static List FromDictionary(Dictionary } - public class DataKeyValuePair : IKeyValuePair + public sealed class DataKeyValuePair : IKeyValuePair, IEquatable { [Key] public string Key { get; set; } = string.Empty; diff --git a/src/InformaticsGateway/Services/HealthLevel7/IMllpClient.cs b/src/Api/Mllp/IMllpClient.cs old mode 100644 new mode 100755 similarity index 88% rename from src/InformaticsGateway/Services/HealthLevel7/IMllpClient.cs rename to src/Api/Mllp/IMllpClient.cs index 8e38ac846..e8c631c32 --- a/src/InformaticsGateway/Services/HealthLevel7/IMllpClient.cs +++ b/src/Api/Mllp/IMllpClient.cs @@ -18,9 +18,9 @@ using System.Threading; using System.Threading.Tasks; -namespace Monai.Deploy.InformaticsGateway.Services.HealthLevel7 +namespace Monai.Deploy.InformaticsGateway.Api.Mllp { - internal interface IMllpClient : IDisposable + public interface IMllpClient : IDisposable { Guid ClientId { get; } @@ -28,4 +28,4 @@ internal interface IMllpClient : IDisposable Task Start(Func onDisconnect, CancellationToken cancellationToken); } -} \ No newline at end of file +} diff --git a/src/InformaticsGateway/Services/HealthLevel7/IMllpExtract.cs b/src/Api/Mllp/IMllpExtract.cs similarity index 78% rename from src/InformaticsGateway/Services/HealthLevel7/IMllpExtract.cs rename to src/Api/Mllp/IMllpExtract.cs index 8fd8bbf37..2c82fee46 100755 --- a/src/InformaticsGateway/Services/HealthLevel7/IMllpExtract.cs +++ b/src/Api/Mllp/IMllpExtract.cs @@ -19,10 +19,12 @@ using HL7.Dotnetcore; using Monai.Deploy.InformaticsGateway.Api.Storage; -namespace Monai.Deploy.InformaticsGateway.Services.HealthLevel7 +namespace Monai.Deploy.InformaticsGateway.Api.Mllp { - internal interface IMllpExtract + public interface IMllpExtract { - Task ExtractInfo(Hl7FileStorageMetadata meta, Message message); + Task ExtractInfo(Hl7FileStorageMetadata meta, Message message, Hl7ApplicationConfigEntity configItem); + + Task GetConfigItem(Message message); } } diff --git a/src/InformaticsGateway/Services/HealthLevel7/IMllpService.cs b/src/Api/Mllp/IMllpService.cs similarity index 92% rename from src/InformaticsGateway/Services/HealthLevel7/IMllpService.cs rename to src/Api/Mllp/IMllpService.cs index 6272464b2..d12e1fd28 100755 --- a/src/InformaticsGateway/Services/HealthLevel7/IMllpService.cs +++ b/src/Api/Mllp/IMllpService.cs @@ -18,7 +18,7 @@ using System.Threading; using System.Threading.Tasks; -namespace Monai.Deploy.InformaticsGateway.Services.HealthLevel7 +namespace Monai.Deploy.InformaticsGateway.Api.Mllp { public interface IMllpService { diff --git a/src/InformaticsGateway/Services/HealthLevel7/MllpClientResult.cs b/src/Api/Mllp/MllpClientResult.cs similarity index 91% rename from src/InformaticsGateway/Services/HealthLevel7/MllpClientResult.cs rename to src/Api/Mllp/MllpClientResult.cs index 401875804..36db3b65f 100755 --- a/src/InformaticsGateway/Services/HealthLevel7/MllpClientResult.cs +++ b/src/Api/Mllp/MllpClientResult.cs @@ -18,9 +18,9 @@ using System.Collections.Generic; using HL7.Dotnetcore; -namespace Monai.Deploy.InformaticsGateway.Services.HealthLevel7 +namespace Monai.Deploy.InformaticsGateway.Api.Mllp { - internal class MllpClientResult + public class MllpClientResult { public IList Messages { get; } public AggregateException? AggregateException { get; } diff --git a/src/Api/Monai.Deploy.InformaticsGateway.Api.csproj b/src/Api/Monai.Deploy.InformaticsGateway.Api.csproj index f4b8450b0..181306f97 100755 --- a/src/Api/Monai.Deploy.InformaticsGateway.Api.csproj +++ b/src/Api/Monai.Deploy.InformaticsGateway.Api.csproj @@ -53,6 +53,7 @@ + diff --git a/src/Api/PlugIns/IInputHL7DataPlugIn.cs b/src/Api/PlugIns/IInputHL7DataPlugIn.cs new file mode 100755 index 000000000..b44d0f736 --- /dev/null +++ b/src/Api/PlugIns/IInputHL7DataPlugIn.cs @@ -0,0 +1,35 @@ +/* + * 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.Threading.Tasks; +using HL7.Dotnetcore; +using Monai.Deploy.InformaticsGateway.Api.Storage; + +namespace Monai.Deploy.InformaticsGateway.Api.PlugIns +{ + /// + /// IInputDataPlugIn enables lightweight data processing over incoming data received from supported data ingestion + /// services. + /// Refer to for additional details. + /// + public interface IInputHL7DataPlugIn + { + string Name { get; } + + Task<(Message hl7Message, FileStorageMetadata fileMetadata)> ExecuteAsync(Message hl7File, FileStorageMetadata fileMetadata); + } + +} diff --git a/src/Api/PlugIns/IInputHL7DataPlugInEngine.cs b/src/Api/PlugIns/IInputHL7DataPlugInEngine.cs new file mode 100755 index 000000000..dc34b976d --- /dev/null +++ b/src/Api/PlugIns/IInputHL7DataPlugInEngine.cs @@ -0,0 +1,42 @@ +/* + * 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 System.Collections.Generic; +using System.Threading.Tasks; +using HL7.Dotnetcore; +using Monai.Deploy.InformaticsGateway.Api.Storage; + +namespace Monai.Deploy.InformaticsGateway.Api.PlugIns +{ + /// + /// IInputDataPlugInEngine processes incoming data receivied from various supported services through + /// a list of plug-ins based on . + /// Rules: + /// + /// 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. + /// Incoming data is processed one file at a time and SHALL not wait for the entire study to arrive. + /// Plug-ins MUST be lightweight and not hinder the upload process. + /// Plug-ins SHALL not accumulate files in memory or storage for bulk processing. + /// + /// + public interface IInputHL7DataPlugInEngine + { + void Configure(IReadOnlyList pluginAssemblies); + + Task> ExecutePlugInsAsync(Message hl7File, FileStorageMetadata fileMetadata, Hl7ApplicationConfigEntity configItem); + } +} diff --git a/src/Api/Storage/Hl7FileStorageMetadata.cs b/src/Api/Storage/Hl7FileStorageMetadata.cs index 576f88ae3..b4e33aed3 100755 --- a/src/Api/Storage/Hl7FileStorageMetadata.cs +++ b/src/Api/Storage/Hl7FileStorageMetadata.cs @@ -27,7 +27,7 @@ namespace Monai.Deploy.InformaticsGateway.Api.Storage public sealed record Hl7FileStorageMetadata : FileStorageMetadata { public const string Hl7SubDirectoryName = "ehr"; - public const string FileExtension = ".txt"; + public const string FileExtension = ".hl7"; /// [JsonIgnore] diff --git a/src/Api/Test/packages.lock.json b/src/Api/Test/packages.lock.json index c3b2fbbdd..903cc6b49 100755 --- a/src/Api/Test/packages.lock.json +++ b/src/Api/Test/packages.lock.json @@ -94,6 +94,11 @@ "System.Threading.Channels": "6.0.0" } }, + "HL7-dotnetcore": { + "type": "Transitive", + "resolved": "2.36.0", + "contentHash": "N1HLMeIqYuY+4O69ItgZJoDBnnpNkK5N2pClceTJ2nFJxsP48iCsA4iz3tm43Yszi4r/vaThoc3UoLBfGP3vKw==" + }, "Macross.Json.Extensions": { "type": "Transitive", "resolved": "3.0.0", @@ -1277,6 +1282,7 @@ "monai.deploy.informaticsgateway.api": { "type": "Project", "dependencies": { + "HL7-dotnetcore": "[2.36.0, )", "Macross.Json.Extensions": "[3.0.0, )", "Microsoft.EntityFrameworkCore.Abstractions": "[6.0.25, )", "Monai.Deploy.InformaticsGateway.Common": "[1.0.0, )", diff --git a/src/Api/packages.lock.json b/src/Api/packages.lock.json index 2f0038d42..1a4adf62a 100755 --- a/src/Api/packages.lock.json +++ b/src/Api/packages.lock.json @@ -21,6 +21,12 @@ "System.Threading.Channels": "6.0.0" } }, + "HL7-dotnetcore": { + "type": "Direct", + "requested": "[2.36.0, )", + "resolved": "2.36.0", + "contentHash": "N1HLMeIqYuY+4O69ItgZJoDBnnpNkK5N2pClceTJ2nFJxsP48iCsA4iz3tm43Yszi4r/vaThoc3UoLBfGP3vKw==" + }, "Macross.Json.Extensions": { "type": "Direct", "requested": "[3.0.0, )", diff --git a/src/CLI/Test/packages.lock.json b/src/CLI/Test/packages.lock.json index 7ae5d5d97..dbdd6f20e 100755 --- a/src/CLI/Test/packages.lock.json +++ b/src/CLI/Test/packages.lock.json @@ -137,6 +137,11 @@ "System.Threading.Channels": "6.0.0" } }, + "HL7-dotnetcore": { + "type": "Transitive", + "resolved": "2.36.0", + "contentHash": "N1HLMeIqYuY+4O69ItgZJoDBnnpNkK5N2pClceTJ2nFJxsP48iCsA4iz3tm43Yszi4r/vaThoc3UoLBfGP3vKw==" + }, "Macross.Json.Extensions": { "type": "Transitive", "resolved": "3.0.0", @@ -1560,6 +1565,7 @@ "monai.deploy.informaticsgateway.api": { "type": "Project", "dependencies": { + "HL7-dotnetcore": "[2.36.0, )", "Macross.Json.Extensions": "[3.0.0, )", "Microsoft.EntityFrameworkCore.Abstractions": "[6.0.25, )", "Monai.Deploy.InformaticsGateway.Common": "[1.0.0, )", diff --git a/src/CLI/packages.lock.json b/src/CLI/packages.lock.json index 75418ec51..22c1f3e80 100755 --- a/src/CLI/packages.lock.json +++ b/src/CLI/packages.lock.json @@ -93,6 +93,11 @@ "System.Threading.Channels": "6.0.0" } }, + "HL7-dotnetcore": { + "type": "Transitive", + "resolved": "2.36.0", + "contentHash": "N1HLMeIqYuY+4O69ItgZJoDBnnpNkK5N2pClceTJ2nFJxsP48iCsA4iz3tm43Yszi4r/vaThoc3UoLBfGP3vKw==" + }, "Macross.Json.Extensions": { "type": "Transitive", "resolved": "3.0.0", @@ -541,6 +546,7 @@ "monai.deploy.informaticsgateway.api": { "type": "Project", "dependencies": { + "HL7-dotnetcore": "[2.36.0, )", "Macross.Json.Extensions": "[3.0.0, )", "Microsoft.EntityFrameworkCore.Abstractions": "[6.0.25, )", "Monai.Deploy.InformaticsGateway.Common": "[1.0.0, )", diff --git a/src/Client/Test/packages.lock.json b/src/Client/Test/packages.lock.json index 49b143571..2bbd6ee7a 100755 --- a/src/Client/Test/packages.lock.json +++ b/src/Client/Test/packages.lock.json @@ -1826,6 +1826,7 @@ "monai.deploy.informaticsgateway.api": { "type": "Project", "dependencies": { + "HL7-dotnetcore": "[2.36.0, )", "Macross.Json.Extensions": "[3.0.0, )", "Microsoft.EntityFrameworkCore.Abstractions": "[6.0.25, )", "Monai.Deploy.InformaticsGateway.Common": "[1.0.0, )", diff --git a/src/Client/packages.lock.json b/src/Client/packages.lock.json index d3be0afb3..2a3e704e0 100755 --- a/src/Client/packages.lock.json +++ b/src/Client/packages.lock.json @@ -43,6 +43,11 @@ "System.Threading.Channels": "6.0.0" } }, + "HL7-dotnetcore": { + "type": "Transitive", + "resolved": "2.36.0", + "contentHash": "N1HLMeIqYuY+4O69ItgZJoDBnnpNkK5N2pClceTJ2nFJxsP48iCsA4iz3tm43Yszi4r/vaThoc3UoLBfGP3vKw==" + }, "Macross.Json.Extensions": { "type": "Transitive", "resolved": "3.0.0", @@ -274,6 +279,7 @@ "monai.deploy.informaticsgateway.api": { "type": "Project", "dependencies": { + "HL7-dotnetcore": "[2.36.0, )", "Macross.Json.Extensions": "[3.0.0, )", "Microsoft.EntityFrameworkCore.Abstractions": "[6.0.25, )", "Monai.Deploy.InformaticsGateway.Common": "[1.0.0, )", diff --git a/src/Configuration/Test/packages.lock.json b/src/Configuration/Test/packages.lock.json index 9265712dc..02e67b1eb 100755 --- a/src/Configuration/Test/packages.lock.json +++ b/src/Configuration/Test/packages.lock.json @@ -102,6 +102,11 @@ "System.Threading.Channels": "6.0.0" } }, + "HL7-dotnetcore": { + "type": "Transitive", + "resolved": "2.36.0", + "contentHash": "N1HLMeIqYuY+4O69ItgZJoDBnnpNkK5N2pClceTJ2nFJxsP48iCsA4iz3tm43Yszi4r/vaThoc3UoLBfGP3vKw==" + }, "Macross.Json.Extensions": { "type": "Transitive", "resolved": "3.0.0", @@ -1290,6 +1295,7 @@ "monai.deploy.informaticsgateway.api": { "type": "Project", "dependencies": { + "HL7-dotnetcore": "[2.36.0, )", "Macross.Json.Extensions": "[3.0.0, )", "Microsoft.EntityFrameworkCore.Abstractions": "[6.0.25, )", "Monai.Deploy.InformaticsGateway.Common": "[1.0.0, )", diff --git a/src/Configuration/packages.lock.json b/src/Configuration/packages.lock.json index 5af67fda9..2b5691080 100755 --- a/src/Configuration/packages.lock.json +++ b/src/Configuration/packages.lock.json @@ -43,6 +43,11 @@ "System.Threading.Channels": "6.0.0" } }, + "HL7-dotnetcore": { + "type": "Transitive", + "resolved": "2.36.0", + "contentHash": "N1HLMeIqYuY+4O69ItgZJoDBnnpNkK5N2pClceTJ2nFJxsP48iCsA4iz3tm43Yszi4r/vaThoc3UoLBfGP3vKw==" + }, "Macross.Json.Extensions": { "type": "Transitive", "resolved": "3.0.0", @@ -274,6 +279,7 @@ "monai.deploy.informaticsgateway.api": { "type": "Project", "dependencies": { + "HL7-dotnetcore": "[2.36.0, )", "Macross.Json.Extensions": "[3.0.0, )", "Microsoft.EntityFrameworkCore.Abstractions": "[6.0.25, )", "Monai.Deploy.InformaticsGateway.Common": "[1.0.0, )", diff --git a/src/Database/Api/Test/packages.lock.json b/src/Database/Api/Test/packages.lock.json index 93a1cc3a7..65aff269f 100755 --- a/src/Database/Api/Test/packages.lock.json +++ b/src/Database/Api/Test/packages.lock.json @@ -76,6 +76,11 @@ "System.Threading.Channels": "6.0.0" } }, + "HL7-dotnetcore": { + "type": "Transitive", + "resolved": "2.36.0", + "contentHash": "N1HLMeIqYuY+4O69ItgZJoDBnnpNkK5N2pClceTJ2nFJxsP48iCsA4iz3tm43Yszi4r/vaThoc3UoLBfGP3vKw==" + }, "Macross.Json.Extensions": { "type": "Transitive", "resolved": "3.0.0", @@ -1264,6 +1269,7 @@ "monai.deploy.informaticsgateway.api": { "type": "Project", "dependencies": { + "HL7-dotnetcore": "[2.36.0, )", "Macross.Json.Extensions": "[3.0.0, )", "Microsoft.EntityFrameworkCore.Abstractions": "[6.0.25, )", "Monai.Deploy.InformaticsGateway.Common": "[1.0.0, )", diff --git a/src/Database/Api/packages.lock.json b/src/Database/Api/packages.lock.json index f86d76521..53e07aa40 100755 --- a/src/Database/Api/packages.lock.json +++ b/src/Database/Api/packages.lock.json @@ -49,6 +49,11 @@ "System.Threading.Channels": "6.0.0" } }, + "HL7-dotnetcore": { + "type": "Transitive", + "resolved": "2.36.0", + "contentHash": "N1HLMeIqYuY+4O69ItgZJoDBnnpNkK5N2pClceTJ2nFJxsP48iCsA4iz3tm43Yszi4r/vaThoc3UoLBfGP3vKw==" + }, "Macross.Json.Extensions": { "type": "Transitive", "resolved": "3.0.0", @@ -280,6 +285,7 @@ "monai.deploy.informaticsgateway.api": { "type": "Project", "dependencies": { + "HL7-dotnetcore": "[2.36.0, )", "Macross.Json.Extensions": "[3.0.0, )", "Microsoft.EntityFrameworkCore.Abstractions": "[6.0.25, )", "Monai.Deploy.InformaticsGateway.Common": "[1.0.0, )", diff --git a/src/Database/DatabaseMigrationManager.cs b/src/Database/DatabaseMigrationManager.cs old mode 100644 new mode 100755 index da35c17a4..72acb2cd8 --- a/src/Database/DatabaseMigrationManager.cs +++ b/src/Database/DatabaseMigrationManager.cs @@ -61,7 +61,7 @@ private static Type[] FindMatchingTypesFromAssemblies(Assembly[] assemblies) var matchingTypes = new List(); foreach (var assembly in assemblies) { - var types = assembly.ExportedTypes.Where(p => p.IsAssignableFrom(typeof(IDatabaseMigrationManager))); + var types = assembly.ExportedTypes.Where(p => p.IsAssignableFrom(typeof(IDatabaseMigrationManager)) && p.Name != nameof(IDatabaseMigrationManager)); if (types.Any()) { matchingTypes.AddRange(types); diff --git a/src/Database/EntityFramework/Migrations/20231204113501_Hl7DEstinationAndConfig.cs b/src/Database/EntityFramework/Migrations/20231204113501_Hl7DEstinationAndConfig.cs index 7dbf72215..d23d89c28 100755 --- a/src/Database/EntityFramework/Migrations/20231204113501_Hl7DEstinationAndConfig.cs +++ b/src/Database/EntityFramework/Migrations/20231204113501_Hl7DEstinationAndConfig.cs @@ -1,4 +1,4 @@ -using System; + using Microsoft.EntityFrameworkCore.Migrations; #nullable disable diff --git a/src/Database/EntityFramework/Migrations/20231207154732_Hl7Plugins.Designer.cs b/src/Database/EntityFramework/Migrations/20231207154732_Hl7Plugins.Designer.cs new file mode 100755 index 000000000..256eb5527 --- /dev/null +++ b/src/Database/EntityFramework/Migrations/20231207154732_Hl7Plugins.Designer.cs @@ -0,0 +1,508 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Monai.Deploy.InformaticsGateway.Database.EntityFramework; + +#nullable disable + +namespace Monai.Deploy.InformaticsGateway.Database.Migrations +{ + [DbContext(typeof(InformaticsGatewayContext))] + [Migration("20231207154732_Hl7Plugins")] + partial class Hl7Plugins + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "6.0.25"); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.Hl7ApplicationConfigEntity", b => + { + b.Property("Name") + .HasColumnType("TEXT") + .HasColumnOrder(0); + + b.Property("DataLink") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("DataMapping") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("LastModified") + .HasColumnType("TEXT"); + + b.Property("PlugInAssemblies") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("SendingId") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Name"); + + b.HasIndex(new[] { "Name" }, "idx_hl7_name") + .IsUnique(); + + b.ToTable("Hl7ApplicationConfig"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.Models.DestinationApplicationEntity", b => + { + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("AeTitle") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("DateTimeUpdated") + .HasColumnType("TEXT"); + + b.Property("HostIp") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Port") + .HasColumnType("INTEGER"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.HasKey("Name"); + + b.HasIndex(new[] { "Name" }, "idx_destination_name") + .IsUnique(); + + b.HasIndex(new[] { "Name", "AeTitle", "HostIp", "Port" }, "idx_source_all") + .IsUnique(); + + b.ToTable("DestinationApplicationEntities"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.Models.DicomAssociationInfo", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CalledAeTitle") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CallingAeTitle") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CorrelationId") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("DateTimeDisconnected") + .HasColumnType("TEXT"); + + b.Property("Duration") + .HasColumnType("TEXT"); + + b.Property("Errors") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("FileCount") + .HasColumnType("INTEGER"); + + b.Property("PayloadIds") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("RemoteHost") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("RemotePort") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("DicomAssociationHistories"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.Models.ExternalAppDetails", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CorrelationId") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("DestinationFolder") + .HasColumnType("TEXT"); + + b.Property("ExportTaskID") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("PatientId") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("PatientIdOutBound") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("StudyInstanceUid") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("StudyInstanceUidOutBound") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("WorkflowInstanceId") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("ExternalAppDetails"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.Models.HL7DestinationEntity", b => + { + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("AeTitle") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("DateTimeUpdated") + .HasColumnType("TEXT"); + + b.Property("HostIp") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Port") + .HasColumnType("INTEGER"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.HasKey("Name"); + + b.HasIndex(new[] { "Name" }, "idx_destination_name") + .IsUnique() + .HasDatabaseName("idx_destination_name1"); + + b.HasIndex(new[] { "Name", "AeTitle", "HostIp", "Port" }, "idx_source_all") + .IsUnique() + .HasDatabaseName("idx_source_all1"); + + b.ToTable("HL7DestinationEntities"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.Models.MonaiApplicationEntity", b => + { + b.Property("Name") + .HasColumnType("TEXT") + .HasColumnOrder(0); + + b.Property("AeTitle") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("AllowedSopClasses") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("DateTimeUpdated") + .HasColumnType("TEXT"); + + b.Property("Grouping") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("IgnoredSopClasses") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("PlugInAssemblies") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Timeout") + .HasColumnType("INTEGER"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.Property("Workflows") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Name"); + + b.HasIndex(new[] { "Name" }, "idx_monaiae_name") + .IsUnique(); + + b.ToTable("MonaiApplicationEntities"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.Rest.InferenceRequest", b => + { + b.Property("InferenceRequestId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("InputMetadata") + .HasColumnType("TEXT"); + + b.Property("InputResources") + .HasColumnType("TEXT"); + + b.Property("OutputResources") + .HasColumnType("TEXT"); + + b.Property("Priority") + .HasColumnType("INTEGER"); + + b.Property("State") + .HasColumnType("INTEGER"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("TransactionId") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("TryCount") + .HasColumnType("INTEGER"); + + b.HasKey("InferenceRequestId"); + + b.HasIndex(new[] { "InferenceRequestId" }, "idx_inferencerequest_inferencerequestid") + .IsUnique(); + + b.HasIndex(new[] { "State" }, "idx_inferencerequest_state"); + + b.HasIndex(new[] { "TransactionId" }, "idx_inferencerequest_transactionid") + .IsUnique(); + + b.ToTable("InferenceRequests"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.SourceApplicationEntity", b => + { + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("AeTitle") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("DateTimeUpdated") + .HasColumnType("TEXT"); + + b.Property("HostIp") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.HasKey("Name"); + + b.HasIndex(new[] { "Name", "AeTitle", "HostIp" }, "idx_source_all") + .IsUnique() + .HasDatabaseName("idx_source_all2"); + + b.HasIndex(new[] { "Name" }, "idx_source_name") + .IsUnique(); + + b.ToTable("SourceApplicationEntities"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.Storage.Payload", b => + { + b.Property("PayloadId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CorrelationId") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("DataOrigins") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("DataTrigger") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("DestinationFolder") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Files") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Key") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("MachineName") + .HasColumnType("TEXT"); + + b.Property("RetryCount") + .HasColumnType("INTEGER"); + + b.Property("State") + .HasColumnType("INTEGER"); + + b.Property("TaskId") + .HasColumnType("TEXT"); + + b.Property("Timeout") + .HasColumnType("INTEGER"); + + b.Property("WorkflowInstanceId") + .HasColumnType("TEXT"); + + b.HasKey("PayloadId"); + + b.HasIndex(new[] { "CorrelationId", "PayloadId" }, "idx_payload_ids") + .IsUnique(); + + b.HasIndex(new[] { "State" }, "idx_payload_state"); + + b.ToTable("Payloads"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.VirtualApplicationEntity", b => + { + b.Property("Name") + .HasColumnType("TEXT") + .HasColumnOrder(0); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("DateTimeUpdated") + .HasColumnType("TEXT"); + + b.Property("PlugInAssemblies") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.Property("VirtualAeTitle") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Workflows") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Name"); + + b.HasIndex(new[] { "Name" }, "idx_virtualae_name") + .IsUnique(); + + b.ToTable("VirtualApplicationEntities"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Database.Api.StorageMetadataWrapper", b => + { + b.Property("CorrelationId") + .HasColumnType("TEXT"); + + b.Property("Identity") + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("IsUploaded") + .HasColumnType("INTEGER"); + + b.Property("TypeName") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Value") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("CorrelationId", "Identity"); + + b.HasIndex(new[] { "CorrelationId" }, "idx_storagemetadata_correlation"); + + b.HasIndex(new[] { "CorrelationId", "Identity" }, "idx_storagemetadata_ids"); + + b.HasIndex(new[] { "IsUploaded" }, "idx_storagemetadata_uploaded"); + + b.ToTable("StorageMetadataWrapperEntities"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Database/EntityFramework/Migrations/20231207154732_Hl7Plugins.cs b/src/Database/EntityFramework/Migrations/20231207154732_Hl7Plugins.cs new file mode 100755 index 000000000..c86631267 --- /dev/null +++ b/src/Database/EntityFramework/Migrations/20231207154732_Hl7Plugins.cs @@ -0,0 +1,27 @@ + +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Monai.Deploy.InformaticsGateway.Database.Migrations +{ + public partial class Hl7Plugins : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "LastModified", + table: "Hl7ApplicationConfig", + type: "TEXT", + nullable: false, + defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "LastModified", + table: "Hl7ApplicationConfig"); + } + } +} diff --git a/src/Database/EntityFramework/Migrations/InformaticsGatewayContextModelSnapshot.cs b/src/Database/EntityFramework/Migrations/InformaticsGatewayContextModelSnapshot.cs index cec0093f1..8eb10b0df 100755 --- a/src/Database/EntityFramework/Migrations/InformaticsGatewayContextModelSnapshot.cs +++ b/src/Database/EntityFramework/Migrations/InformaticsGatewayContextModelSnapshot.cs @@ -34,6 +34,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("DateTimeCreated") .HasColumnType("TEXT"); + b.Property("LastModified") + .HasColumnType("TEXT"); + b.Property("PlugInAssemblies") .IsRequired() .HasColumnType("TEXT"); diff --git a/src/Database/EntityFramework/Repositories/Hl7ApplicationConfigRepository.cs b/src/Database/EntityFramework/Repositories/Hl7ApplicationConfigRepository.cs old mode 100644 new mode 100755 index 7f6711ab4..898181503 --- a/src/Database/EntityFramework/Repositories/Hl7ApplicationConfigRepository.cs +++ b/src/Database/EntityFramework/Repositories/Hl7ApplicationConfigRepository.cs @@ -63,11 +63,7 @@ public Task DeleteAsync(string id, CancellationToken { return _retryPolicy.ExecuteAsync(async () => { - var entity = await GetByIdAsync(id).ConfigureAwait(false); - if (entity is null) - { - throw new DatabaseException("Failed to delete entity."); - } + var entity = await GetByIdAsync(id).ConfigureAwait(false) ?? throw new DatabaseException("Failed to delete entity."); var result = _dataset.Remove(entity); await _informaticsGatewayContext.SaveChangesAsync(cancellationToken).ConfigureAwait(false); return result.Entity; diff --git a/src/Database/EntityFramework/Test/packages.lock.json b/src/Database/EntityFramework/Test/packages.lock.json index 40ce6d70b..95ac88e42 100755 --- a/src/Database/EntityFramework/Test/packages.lock.json +++ b/src/Database/EntityFramework/Test/packages.lock.json @@ -102,6 +102,11 @@ "System.Threading.Channels": "6.0.0" } }, + "HL7-dotnetcore": { + "type": "Transitive", + "resolved": "2.36.0", + "contentHash": "N1HLMeIqYuY+4O69ItgZJoDBnnpNkK5N2pClceTJ2nFJxsP48iCsA4iz3tm43Yszi4r/vaThoc3UoLBfGP3vKw==" + }, "Macross.Json.Extensions": { "type": "Transitive", "resolved": "3.0.0", @@ -1468,6 +1473,7 @@ "monai.deploy.informaticsgateway.api": { "type": "Project", "dependencies": { + "HL7-dotnetcore": "[2.36.0, )", "Macross.Json.Extensions": "[3.0.0, )", "Microsoft.EntityFrameworkCore.Abstractions": "[6.0.25, )", "Monai.Deploy.InformaticsGateway.Common": "[1.0.0, )", diff --git a/src/Database/EntityFramework/packages.lock.json b/src/Database/EntityFramework/packages.lock.json index c80430fab..91f7b6eac 100755 --- a/src/Database/EntityFramework/packages.lock.json +++ b/src/Database/EntityFramework/packages.lock.json @@ -110,6 +110,11 @@ "System.Threading.Channels": "6.0.0" } }, + "HL7-dotnetcore": { + "type": "Transitive", + "resolved": "2.36.0", + "contentHash": "N1HLMeIqYuY+4O69ItgZJoDBnnpNkK5N2pClceTJ2nFJxsP48iCsA4iz3tm43Yszi4r/vaThoc3UoLBfGP3vKw==" + }, "Humanizer.Core": { "type": "Transitive", "resolved": "2.8.26", @@ -472,6 +477,7 @@ "monai.deploy.informaticsgateway.api": { "type": "Project", "dependencies": { + "HL7-dotnetcore": "[2.36.0, )", "Macross.Json.Extensions": "[3.0.0, )", "Microsoft.EntityFrameworkCore.Abstractions": "[6.0.25, )", "Monai.Deploy.InformaticsGateway.Common": "[1.0.0, )", diff --git a/src/Database/MongoDB/Integration.Test/packages.lock.json b/src/Database/MongoDB/Integration.Test/packages.lock.json index 8ad2d5e4f..c0cbcf5cf 100755 --- a/src/Database/MongoDB/Integration.Test/packages.lock.json +++ b/src/Database/MongoDB/Integration.Test/packages.lock.json @@ -110,6 +110,11 @@ "System.Threading.Channels": "6.0.0" } }, + "HL7-dotnetcore": { + "type": "Transitive", + "resolved": "2.36.0", + "contentHash": "N1HLMeIqYuY+4O69ItgZJoDBnnpNkK5N2pClceTJ2nFJxsP48iCsA4iz3tm43Yszi4r/vaThoc3UoLBfGP3vKw==" + }, "Macross.Json.Extensions": { "type": "Transitive", "resolved": "3.0.0", @@ -1395,6 +1400,7 @@ "monai.deploy.informaticsgateway.api": { "type": "Project", "dependencies": { + "HL7-dotnetcore": "[2.36.0, )", "Macross.Json.Extensions": "[3.0.0, )", "Microsoft.EntityFrameworkCore.Abstractions": "[6.0.25, )", "Monai.Deploy.InformaticsGateway.Common": "[1.0.0, )", diff --git a/src/Database/MongoDB/Repositories/HL7DestinationEntityRepository.cs b/src/Database/MongoDB/Repositories/HL7DestinationEntityRepository.cs old mode 100644 new mode 100755 index 37676d848..feb428f09 --- a/src/Database/MongoDB/Repositories/HL7DestinationEntityRepository.cs +++ b/src/Database/MongoDB/Repositories/HL7DestinationEntityRepository.cs @@ -53,7 +53,10 @@ public HL7DestinationEntityRepository( _scope = serviceScopeFactory.CreateScope(); _retryPolicy = Policy.Handle().WaitAndRetryAsync( options.Value.Retries.RetryDelays, - (exception, timespan, count, context) => _logger.DatabaseErrorRetry(timespan, count, exception)); + (exception, timespan, count, context) => + { + _logger.DatabaseErrorRetry(timespan, count, exception); + }); var mongoDbClient = _scope.ServiceProvider.GetRequiredService(); var mongoDatabase = mongoDbClient.GetDatabase(mongoDbOptions.Value.DatabaseName); diff --git a/src/Database/MongoDB/Repositories/Hl7ApplicationConfigRepository.cs b/src/Database/MongoDB/Repositories/Hl7ApplicationConfigRepository.cs index b7a35ce5e..8b23cf257 100755 --- a/src/Database/MongoDB/Repositories/Hl7ApplicationConfigRepository.cs +++ b/src/Database/MongoDB/Repositories/Hl7ApplicationConfigRepository.cs @@ -49,7 +49,10 @@ public Hl7ApplicationConfigRepository(IServiceScopeFactory serviceScopeFactory, _scope = serviceScopeFactory.CreateScope(); _retryPolicy = Policy.Handle().WaitAndRetryAsync( options.Value.Retries.RetryDelays, - (exception, timespan, count, context) => _logger.DatabaseErrorRetry(timespan, count, exception)); + (exception, timespan, count, context) => + { + _logger.DatabaseErrorRetry(timespan, count, exception); + }); var mongoDbClient = _scope.ServiceProvider.GetRequiredService(); var mongoDatabase = mongoDbClient.GetDatabase(options.Value.DatabaseName); diff --git a/src/Database/MongoDB/packages.lock.json b/src/Database/MongoDB/packages.lock.json index b248f4c03..cd453a7c0 100755 --- a/src/Database/MongoDB/packages.lock.json +++ b/src/Database/MongoDB/packages.lock.json @@ -69,6 +69,11 @@ "System.Threading.Channels": "6.0.0" } }, + "HL7-dotnetcore": { + "type": "Transitive", + "resolved": "2.36.0", + "contentHash": "N1HLMeIqYuY+4O69ItgZJoDBnnpNkK5N2pClceTJ2nFJxsP48iCsA4iz3tm43Yszi4r/vaThoc3UoLBfGP3vKw==" + }, "Macross.Json.Extensions": { "type": "Transitive", "resolved": "3.0.0", @@ -373,6 +378,7 @@ "monai.deploy.informaticsgateway.api": { "type": "Project", "dependencies": { + "HL7-dotnetcore": "[2.36.0, )", "Macross.Json.Extensions": "[3.0.0, )", "Microsoft.EntityFrameworkCore.Abstractions": "[6.0.25, )", "Monai.Deploy.InformaticsGateway.Common": "[1.0.0, )", diff --git a/src/Database/packages.lock.json b/src/Database/packages.lock.json index 9fa7405f1..af4bbeb2a 100755 --- a/src/Database/packages.lock.json +++ b/src/Database/packages.lock.json @@ -94,6 +94,11 @@ "System.Threading.Channels": "6.0.0" } }, + "HL7-dotnetcore": { + "type": "Transitive", + "resolved": "2.36.0", + "contentHash": "N1HLMeIqYuY+4O69ItgZJoDBnnpNkK5N2pClceTJ2nFJxsP48iCsA4iz3tm43Yszi4r/vaThoc3UoLBfGP3vKw==" + }, "Humanizer.Core": { "type": "Transitive", "resolved": "2.8.26", @@ -609,6 +614,7 @@ "monai.deploy.informaticsgateway.api": { "type": "Project", "dependencies": { + "HL7-dotnetcore": "[2.36.0, )", "Macross.Json.Extensions": "[3.0.0, )", "Microsoft.EntityFrameworkCore.Abstractions": "[6.0.25, )", "Monai.Deploy.InformaticsGateway.Common": "[1.0.0, )", diff --git a/src/InformaticsGateway/Logging/Log.800.Hl7Service.cs b/src/InformaticsGateway/Logging/Log.800.Hl7Service.cs index a3235b8a0..b207d5f87 100755 --- a/src/InformaticsGateway/Logging/Log.800.Hl7Service.cs +++ b/src/InformaticsGateway/Logging/Log.800.Hl7Service.cs @@ -51,14 +51,14 @@ public static partial class Log [LoggerMessage(EventId = 809, Level = LogLevel.Debug, Message = "Acknowledgment type={value}.")] public static partial void AcknowledgmentType(this ILogger logger, string value); - [LoggerMessage(EventId = 810, Level = LogLevel.Information, Message = "Acknowledgment sent: length={length}.")] - public static partial void AcknowledgmentSent(this ILogger logger, int length); + [LoggerMessage(EventId = 810, Level = LogLevel.Information, Message = "Acknowledgment sent message:{message} length:{length}.")] + public static partial void AcknowledgmentSent(this ILogger logger, string message, int length); [LoggerMessage(EventId = 811, Level = LogLevel.Debug, Message = "HL7 bytes received: {length}.")] public static partial void Hl7MessageBytesRead(this ILogger logger, int length); - [LoggerMessage(EventId = 812, Level = LogLevel.Debug, Message = "Parsing message with {length} bytes.")] - public static partial void Hl7GenerateMessage(this ILogger logger, int length); + [LoggerMessage(EventId = 812, Level = LogLevel.Debug, Message = "Parsing message with {length} bytes. {message}")] + public static partial void Hl7GenerateMessage(this ILogger logger, int length, string message); [LoggerMessage(EventId = 813, Level = LogLevel.Debug, Message = "Waiting for HL7 message.")] public static partial void HL7ReadingMessage(this ILogger logger); @@ -101,5 +101,18 @@ public static partial class Log [LoggerMessage(EventId = 826, Level = LogLevel.Debug, Message = "HL7 meassage sent received {ack}")] public static partial void Hl7MessageSent(this ILogger logger, string ack); + + [LoggerMessage(EventId = 827, Level = LogLevel.Warning, Message = "HL7 plugin loading exceptions")] + public static partial void HL7PluginLoadingExceptions(this ILogger logger, Exception ex); + + [LoggerMessage(EventId = 828, Level = LogLevel.Information, Message = "HL7 message recieved. {message}")] + public static partial void Hl7MessageReceieved(this ILogger logger, string message); + + [LoggerMessage(EventId = 829, Level = LogLevel.Trace, Message = "HL7 config Not matching message Id {senderId} configId {configID}")] + public static partial void Hl7NotMatchingConfig(this ILogger logger, string senderId, string configID); + + [LoggerMessage(EventId = 830, Level = LogLevel.Error, Message = "Error generating HL7 acknowledgment. for message {message}")] + public static partial void ErrorGeneratingHl7Acknowledgment(this ILogger logger, Exception ex, string message); + } } diff --git a/src/InformaticsGateway/Program.cs b/src/InformaticsGateway/Program.cs index 9586ee88c..c860f83aa 100755 --- a/src/InformaticsGateway/Program.cs +++ b/src/InformaticsGateway/Program.cs @@ -25,6 +25,7 @@ using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; +using Monai.Deploy.InformaticsGateway.Api.Mllp; using Monai.Deploy.InformaticsGateway.Api.PlugIns; using Monai.Deploy.InformaticsGateway.Common; using Monai.Deploy.InformaticsGateway.Configuration; @@ -35,7 +36,6 @@ using Monai.Deploy.InformaticsGateway.Services.DicomWeb; using Monai.Deploy.InformaticsGateway.Services.Export; using Monai.Deploy.InformaticsGateway.Services.Fhir; -using Monai.Deploy.InformaticsGateway.Services.HealthLevel7; using Monai.Deploy.InformaticsGateway.Services.Http; using Monai.Deploy.InformaticsGateway.Services.Scp; using Monai.Deploy.InformaticsGateway.Services.Scu; @@ -116,8 +116,10 @@ internal static IHostBuilder CreateHostBuilder(string[] args) => services.AddScoped(); services.AddScoped(); services.AddScoped(); + services.AddScoped(); services.AddScoped, InputDataPlugInEngineFactory>(); services.AddScoped, OutputDataPlugInEngineFactory>(); + services.AddScoped, InputHL7DataPlugInEngineFactory>(); services.AddMonaiDeployStorageService(hostContext.Configuration!.GetSection("InformaticsGateway:storage:serviceAssemblyName").Value, Monai.Deploy.Storage.HealthCheckOptions.ServiceHealthCheck); @@ -152,7 +154,6 @@ internal static IHostBuilder CreateHostBuilder(string[] args) => .AddHttpClient("fhir", configure => configure.Timeout = timeout) .SetHandlerLifetime(timeout); -#pragma warning disable CS8603 // Possible null reference return. services.AddHostedService(); services.AddHostedService(); services.AddHostedService(); @@ -164,7 +165,6 @@ internal static IHostBuilder CreateHostBuilder(string[] args) => services.AddHostedService(); services.AddHostedService(); services.AddHostedService(); -#pragma warning restore CS8603 // Possible null reference return. }) .ConfigureWebHostDefaults(webBuilder => @@ -182,6 +182,7 @@ private static NLog.Logger ConfigureNLog(string assemblyVersionNumber) ext.RegisterLayoutRenderer("servicename", logEvent => typeof(Program).Namespace); ext.RegisterLayoutRenderer("serviceversion", logEvent => assemblyVersionNumber); ext.RegisterLayoutRenderer("machinename", logEvent => Environment.MachineName); + ext.RegisterLayoutRenderer("appname", logEvent => "MIG"); }) .LoadConfigurationFromAppSettings() .GetCurrentClassLogger(); diff --git a/src/InformaticsGateway/Services/Common/IInputDataPluginEngineFactory.cs b/src/InformaticsGateway/Services/Common/IInputDataPluginEngineFactory.cs old mode 100644 new mode 100755 index 4f122da6f..880eaef95 --- a/src/InformaticsGateway/Services/Common/IInputDataPluginEngineFactory.cs +++ b/src/InformaticsGateway/Services/Common/IInputDataPluginEngineFactory.cs @@ -125,4 +125,11 @@ public OutputDataPlugInEngineFactory(IFileSystem fileSystem, ILogger + { + public InputHL7DataPlugInEngineFactory(IFileSystem fileSystem, ILogger> logger) : base(fileSystem, logger) + { + } + } } diff --git a/src/InformaticsGateway/Services/Common/InputDataPluginEngine.cs b/src/InformaticsGateway/Services/Common/InputDataPluginEngine.cs old mode 100644 new mode 100755 index 3b0d177d2..51d7b18b3 --- a/src/InformaticsGateway/Services/Common/InputDataPluginEngine.cs +++ b/src/InformaticsGateway/Services/Common/InputDataPluginEngine.cs @@ -17,6 +17,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Text.Json; using System.Threading.Tasks; using FellowOakDicom; using Microsoft.Extensions.Logging; @@ -31,7 +32,7 @@ internal class InputDataPlugInEngine : IInputDataPlugInEngine { private readonly IServiceProvider _serviceProvider; private readonly ILogger _logger; - private IReadOnlyList? _plugsins; + private IReadOnlyList? _plugins; public InputDataPlugInEngine(IServiceProvider serviceProvider, ILogger logger) { @@ -41,20 +42,22 @@ public InputDataPlugInEngine(IServiceProvider serviceProvider, ILogger pluginAssemblies) { - _plugsins = LoadPlugIns(_serviceProvider, pluginAssemblies); + _plugins = LoadPlugIns(_serviceProvider, pluginAssemblies); } public async Task> ExecutePlugInsAsync(DicomFile dicomFile, FileStorageMetadata fileMetadata) { - if (_plugsins == null) + if (_plugins == null) { throw new PlugInInitializationException("InputDataPlugInEngine not configured, please call Configure() first."); } - foreach (var plugin in _plugsins) + foreach (var plugin in _plugins) { _logger.ExecutingInputDataPlugIn(plugin.Name); (dicomFile, fileMetadata) = await plugin.ExecuteAsync(dicomFile, fileMetadata).ConfigureAwait(false); + + _logger.InputDataPlugInEngineexecuted(plugin.Name, JsonSerializer.Serialize(fileMetadata)); } return new Tuple(dicomFile, fileMetadata); diff --git a/src/InformaticsGateway/Services/Common/InputHL7DataPlugInEngine.cs b/src/InformaticsGateway/Services/Common/InputHL7DataPlugInEngine.cs new file mode 100755 index 000000000..6f7f1d7bf --- /dev/null +++ b/src/InformaticsGateway/Services/Common/InputHL7DataPlugInEngine.cs @@ -0,0 +1,94 @@ +/* + * 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 System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using HL7.Dotnetcore; +using Microsoft.Extensions.Logging; +using Monai.Deploy.InformaticsGateway.Api; +using Monai.Deploy.InformaticsGateway.Api.PlugIns; +using Monai.Deploy.InformaticsGateway.Api.Storage; +using Monai.Deploy.InformaticsGateway.Common; +using Monai.Deploy.InformaticsGateway.Logging; + +namespace Monai.Deploy.InformaticsGateway.Services.Common +{ + public class InputHL7DataPlugInEngine : IInputHL7DataPlugInEngine + { + private readonly IServiceProvider _serviceProvider; + private readonly ILogger _logger; + private IReadOnlyList? _plugsins; + + public InputHL7DataPlugInEngine(IServiceProvider serviceProvider, ILogger logger) + { + _serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + } + + public void Configure(IReadOnlyList pluginAssemblies) + { + _plugsins = LoadPlugIns(_serviceProvider, pluginAssemblies); + } + + public async Task> ExecutePlugInsAsync(Message hl7File, FileStorageMetadata fileMetadata, Hl7ApplicationConfigEntity? configItem) + { + if (configItem?.PlugInAssemblies is not null && configItem.PlugInAssemblies.Any()) + { + if (_plugsins == null) + { + throw new PlugInInitializationException("InputHL7DataPlugInEngine not configured, please call Configure() first."); + } + + foreach (var plugin in _plugsins) + { + if (configItem is not null && configItem.PlugInAssemblies.Exists(a => a.StartsWith(plugin.ToString()!))) + { + _logger.ExecutingInputDataPlugIn(plugin.Name); + (hl7File, fileMetadata) = await plugin.ExecuteAsync(hl7File, fileMetadata).ConfigureAwait(false); + } + } + } + return new Tuple(hl7File, fileMetadata); + } + + private IReadOnlyList LoadPlugIns(IServiceProvider serviceProvider, IReadOnlyList pluginAssemblies) + { + var exceptions = new List(); + var list = new List(); + foreach (var plugin in pluginAssemblies) + { + try + { + _logger.AddingInputDataPlugIn(plugin); + list.Add(typeof(IInputHL7DataPlugIn).CreateInstance(serviceProvider, typeString: plugin)); + } + catch (Exception ex) + { + exceptions.Add(new PlugInLoadingException($"Error loading plug-in '{plugin}'.", ex)); + } + } + + if (exceptions.Any()) + { + throw new AggregateException("Error loading plug-in(s).", exceptions); + } + + return list; + } + } +} diff --git a/src/InformaticsGateway/Services/Common/OutputDataPluginEngine.cs b/src/InformaticsGateway/Services/Common/OutputDataPluginEngine.cs index ecbf77a93..a497862dc 100755 --- a/src/InformaticsGateway/Services/Common/OutputDataPluginEngine.cs +++ b/src/InformaticsGateway/Services/Common/OutputDataPluginEngine.cs @@ -61,7 +61,7 @@ public async Task ExecutePlugInsAsync(ExportRequestDat (dicomFile, exportRequestDataMessage) = await plugin.ExecuteAsync(dicomFile, exportRequestDataMessage).ConfigureAwait(false); } using var ms = new MemoryStream(); - await dicomFile.SaveAsync(ms); + await dicomFile.SaveAsync(ms).ConfigureAwait(false); exportRequestDataMessage.SetData(ms.ToArray()); return exportRequestDataMessage; diff --git a/src/InformaticsGateway/Services/Connectors/PayloadAssembler.cs b/src/InformaticsGateway/Services/Connectors/PayloadAssembler.cs index 1222add24..9485f7fc0 100755 --- a/src/InformaticsGateway/Services/Connectors/PayloadAssembler.cs +++ b/src/InformaticsGateway/Services/Connectors/PayloadAssembler.cs @@ -18,6 +18,7 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; +using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Ardalis.GuardClauses; @@ -134,6 +135,21 @@ private async void OnTimedEvent(Object? source, System.Timers.ElapsedEventArgs e foreach (var key in _payloads.Keys) { var payload = await _payloads[key].Task.ConfigureAwait(false); + + + + + + + _logger.LogTrace($"Payload = correlationId {payload.CorrelationId} obj: {JsonSerializer.Serialize(payload)}"); + + + + + + + + using var loggerScope = _logger.BeginScope(new LoggingDataDictionary { { "CorrelationId", payload.CorrelationId } }); _logger.BucketElapsedTime(key, payload.Timeout, payload.ElapsedTime().TotalSeconds, payload.Files.Count, payload.FilesUploaded, payload.FilesFailedToUpload); @@ -204,10 +220,16 @@ private async Task CreateOrGetPayload(string key, string correlationId, { return await _payloads.GetOrAdd(key, x => new AsyncLazy(async () => { + + var scope = _serviceScopeFactory.CreateScope(); var repository = scope.ServiceProvider.GetRequiredService(); var newPayload = new Payload(key, correlationId, workflowInstanceId, taskId, dataOrigin, timeout, null, destinationFolder); await repository.AddAsync(newPayload).ConfigureAwait(false); + + _logger.LogTrace($"CreateOrGetPayload = correlationId {correlationId} obj: {JsonSerializer.Serialize(newPayload)}"); + + _logger.BucketCreated(key, timeout); return newPayload; })); diff --git a/src/InformaticsGateway/Services/Connectors/PayloadNotificationActionHandler.cs b/src/InformaticsGateway/Services/Connectors/PayloadNotificationActionHandler.cs index 1aa2a848a..cae010ecf 100755 --- a/src/InformaticsGateway/Services/Connectors/PayloadNotificationActionHandler.cs +++ b/src/InformaticsGateway/Services/Connectors/PayloadNotificationActionHandler.cs @@ -16,6 +16,7 @@ using System; using System.Linq; +using System.Text.Json; using System.Threading; using System.Threading.Tasks; using System.Threading.Tasks.Dataflow; @@ -169,6 +170,8 @@ private async Task SendArtifactRecievedEvent(Payload payload) }; artifiactRecievedEvent.DataOrigins.AddRange(payload.DataOrigins); + _logger.LogTrace($"Adding files to ArtifactsReceivedEvent files {JsonSerializer.Serialize(payload)}"); + artifiactRecievedEvent.Artifacts = payload.Files.Select(f => new Artifact { Type = f.DataOrigin.ArtifactType, Path = f.File.UploadPath }).ToList(); var message = new JsonMessage( diff --git a/src/InformaticsGateway/Services/Export/ExportServiceBase.cs b/src/InformaticsGateway/Services/Export/ExportServiceBase.cs index 935f87817..b6f73b1c6 100755 --- a/src/InformaticsGateway/Services/Export/ExportServiceBase.cs +++ b/src/InformaticsGateway/Services/Export/ExportServiceBase.cs @@ -138,7 +138,6 @@ public async Task StopAsync(CancellationToken cancellationToken) await Task.Delay(250).ConfigureAwait(false); #pragma warning restore CA2016 // Forward the 'CancellationToken' parameter to methods _cancellationTokenSource.Dispose(); - return; } private void SetupPolling() @@ -147,7 +146,7 @@ private void SetupPolling() _logger.ExportEventSubscription(ServiceName, RoutingKey); } - protected async Task OnMessageReceivedCallback(MessageReceivedEventArgs eventArgs) + protected virtual async Task OnMessageReceivedCallback(MessageReceivedEventArgs eventArgs) { using var loggerScope = _logger.BeginScope(new Messaging.Common.LoggingDataDictionary { { "ThreadId", Environment.CurrentManagedThreadId }, diff --git a/src/InformaticsGateway/Services/Export/Hl7ExportService.cs b/src/InformaticsGateway/Services/Export/Hl7ExportService.cs index 16ed9a345..1acf2f17e 100755 --- a/src/InformaticsGateway/Services/Export/Hl7ExportService.cs +++ b/src/InformaticsGateway/Services/Export/Hl7ExportService.cs @@ -28,9 +28,10 @@ using Monai.Deploy.InformaticsGateway.Configuration; using Monai.Deploy.InformaticsGateway.Database.Api.Repositories; using Monai.Deploy.InformaticsGateway.Logging; -using Monai.Deploy.InformaticsGateway.Services.HealthLevel7; +using Monai.Deploy.InformaticsGateway.Api.Mllp; using Monai.Deploy.Messaging.Common; using Polly; +using System.Linq; namespace Monai.Deploy.InformaticsGateway.Services.Export { @@ -119,7 +120,7 @@ private async Task ExecuteHl7Export( .ExecuteAsync(async () => { await _mllpService.SendMllp( - IPAddress.Parse(destination.HostIp), + Dns.GetHostAddresses(destination.HostIp).First(), destination.Port, Encoding.UTF8.GetString(exportRequestData.FileContent), cancellationToken ).ConfigureAwait(false); @@ -159,5 +160,6 @@ protected override Task ExecuteOutputDataEngineCallbac { return Task.FromResult(exportDataRequest); } + } } diff --git a/src/InformaticsGateway/Services/HealthLevel7/IMllpClientFactory.cs b/src/InformaticsGateway/Services/HealthLevel7/IMllpClientFactory.cs index fe2c1b8a5..3640227a2 100755 --- a/src/InformaticsGateway/Services/HealthLevel7/IMllpClientFactory.cs +++ b/src/InformaticsGateway/Services/HealthLevel7/IMllpClientFactory.cs @@ -18,7 +18,7 @@ using Monai.Deploy.InformaticsGateway.Configuration; using Monai.Deploy.InformaticsGateway.Services.Common; -namespace Monai.Deploy.InformaticsGateway.Services.HealthLevel7 +namespace Monai.Deploy.InformaticsGateway.Api.Mllp { internal interface IMllpClientFactory { diff --git a/src/InformaticsGateway/Services/HealthLevel7/MllpClient.cs b/src/InformaticsGateway/Services/HealthLevel7/MllpClient.cs index b3c87372b..be1cf2c02 100755 --- a/src/InformaticsGateway/Services/HealthLevel7/MllpClient.cs +++ b/src/InformaticsGateway/Services/HealthLevel7/MllpClient.cs @@ -23,12 +23,12 @@ using Ardalis.GuardClauses; using HL7.Dotnetcore; using Microsoft.Extensions.Logging; -using Monai.Deploy.InformaticsGateway.Api; using Monai.Deploy.InformaticsGateway.Configuration; using Monai.Deploy.InformaticsGateway.Logging; using Monai.Deploy.InformaticsGateway.Services.Common; +using Monai.Deploy.InformaticsGateway.Services.HealthLevel7; -namespace Monai.Deploy.InformaticsGateway.Services.HealthLevel7 +namespace Monai.Deploy.InformaticsGateway.Api.Mllp { internal sealed class MllpClient : IMllpClient { @@ -159,13 +159,18 @@ private async Task SendAcknowledgment(INetworkStream clientStream, Message messa if (ShouldSendAcknowledgment(message)) { - var ackMessage = message.GetACK(); + var ackMessage = message.GetACK(true); + if (ackMessage is null) + { + _logger.ErrorGeneratingHl7Acknowledgment(new Exception(), message.HL7Message); + return; + } var ackData = new ReadOnlyMemory(ackMessage.GetMLLP()); try { await clientStream.WriteAsync(ackData, cancellationToken).ConfigureAwait(false); await clientStream.FlushAsync(cancellationToken).ConfigureAwait(false); - _logger.AcknowledgmentSent(ackData.Length); + _logger.AcknowledgmentSent(ackMessage.HL7Message, ackData.Length); } catch (Exception ex) { @@ -181,7 +186,7 @@ private bool ShouldSendAcknowledgment(Message message) try { var value = message.DefaultSegment(Resources.MessageHeaderSegment).Fields(Resources.AcceptAcknowledgementType); - if (value is null) + if (value is null || string.IsNullOrWhiteSpace(value.Value)) { return true; } @@ -211,9 +216,9 @@ private bool CreateMessage(int startIndex, int endIndex, ref string data, out Me try { var text = data.Substring(messageStartIndex, endIndex - messageStartIndex); - _logger.Hl7GenerateMessage(text.Length); + _logger.Hl7GenerateMessage(text.Length, text); message = new Message(text); - message.ParseMessage(); + message.ParseMessage(true); data = data.Length > endIndex ? data.Substring(messageEndIndex) : string.Empty; return true; } diff --git a/src/InformaticsGateway/Services/HealthLevel7/MllpExtract.cs b/src/InformaticsGateway/Services/HealthLevel7/MllpExtract.cs index d27e7a71e..ccc571859 100755 --- a/src/InformaticsGateway/Services/HealthLevel7/MllpExtract.cs +++ b/src/InformaticsGateway/Services/HealthLevel7/MllpExtract.cs @@ -17,20 +17,21 @@ using System; using System.Collections.Generic; +using System.Text.Json; +using System.Text.Json.Nodes; using System.Threading; using System.Threading.Tasks; using FellowOakDicom; using HL7.Dotnetcore; using Microsoft.Extensions.Logging; -using Monai.Deploy.InformaticsGateway.Api; using Monai.Deploy.InformaticsGateway.Api.Models; using Monai.Deploy.InformaticsGateway.Api.Storage; using Monai.Deploy.InformaticsGateway.Database.Api.Repositories; using Monai.Deploy.InformaticsGateway.Logging; -namespace Monai.Deploy.InformaticsGateway.Services.HealthLevel7 +namespace Monai.Deploy.InformaticsGateway.Api.Mllp { - internal sealed class MllpExtract : IMllpExtract + public sealed class MllpExtract : IMllpExtract { private readonly ILogger _logger; private readonly IHl7ApplicationConfigRepository _hl7ApplicationConfigRepository; @@ -44,25 +45,10 @@ public MllpExtract(IHl7ApplicationConfigRepository hl7ApplicationConfigRepositor } - public async Task ExtractInfo(Hl7FileStorageMetadata meta, Message message) + public async Task ExtractInfo(Hl7FileStorageMetadata meta, Message message, Hl7ApplicationConfigEntity configItem) { try { - // load the config - var config = await _hl7ApplicationConfigRepository.GetAllAsync().ConfigureAwait(false); - if (config == null) - { - _logger.Hl7NoConfig(); - return message; - } - _logger.Hl7ConfigLoaded($"Config: {config}"); - // get config for vendorId - var configItem = GetConfig(config, message); - if (configItem == null) - { - _logger.Hl7NoMatchingConfig(message.HL7Message); - return message; - } // extract data for the given fields // Use Id to get record from Db var details = await GetExtAppDetails(configItem, message).ConfigureAwait(false); @@ -94,6 +80,26 @@ public async Task ExtractInfo(Hl7FileStorageMetadata meta, Message mess return message; } + public async Task GetConfigItem(Message message) + { + // load the config + var config = await _hl7ApplicationConfigRepository.GetAllAsync().ConfigureAwait(false); + if (config == null) + { + _logger.Hl7NoConfig(); + return null; + } + _logger.Hl7ConfigLoaded($"Config: {JsonSerializer.Serialize(config)}"); + // get config for vendorId + var configItem = GetConfig(config, message); + if (configItem == null) + { + _logger.Hl7NoMatchingConfig(message.HL7Message); + return null; + } + return configItem; + } + private async Task GetExtAppDetails(Hl7ApplicationConfigEntity hl7ApplicationConfigEntity, Message message) { var tagId = message.GetValue(hl7ApplicationConfigEntity.DataLink.Key); @@ -101,9 +107,9 @@ public async Task ExtractInfo(Hl7FileStorageMetadata meta, Message mess switch (type) { case DataLinkType.PatientId: - return await _externalAppDetailsRepository.GetByPatientIdOutboundAsync(tagId, new CancellationToken()).ConfigureAwait(false); ; + return await _externalAppDetailsRepository.GetByPatientIdOutboundAsync(tagId, new CancellationToken()).ConfigureAwait(false); case DataLinkType.StudyInstanceUid: - return await _externalAppDetailsRepository.GetByStudyIdOutboundAsync(tagId, new CancellationToken()).ConfigureAwait(false); ; + return await _externalAppDetailsRepository.GetByStudyIdOutboundAsync(tagId, new CancellationToken()).ConfigureAwait(false); default: break; } @@ -111,14 +117,20 @@ public async Task ExtractInfo(Hl7FileStorageMetadata meta, Message mess throw new Exception($"Invalid DataLinkType: {type}"); } - internal static Hl7ApplicationConfigEntity? GetConfig(List config, Message message) + internal Hl7ApplicationConfigEntity? GetConfig(List config, Message message) { foreach (var item in config) { - if (item.SendingId.Value == message.GetValue(item.SendingId.Key)) + var sendingId = message.GetValue(item.SendingId.Key); + if (item.SendingId.Value == sendingId) { + _logger.Hl7FoundMatchingConfig(sendingId, JsonSerializer.Serialize(item)); return item; } + else + { + _logger.Hl7NotMatchingConfig(sendingId, item.SendingId.Value); + } } return null; } @@ -138,7 +150,7 @@ private Message RepopulateMessage(Hl7ApplicationConfigEntity config, ExternalApp { var newMess = message.HL7Message.Replace(oldvalue, details.PatientId); message = new Message(newMess); - message.ParseMessage(); + message.ParseMessage(true); } } else if (tag == DicomTag.StudyInstanceUID) @@ -150,7 +162,7 @@ private Message RepopulateMessage(Hl7ApplicationConfigEntity config, ExternalApp { var newMess = message.HL7Message.Replace(oldvalue, details.StudyInstanceUid); message = new Message(newMess); - message.ParseMessage(); + message.ParseMessage(true); } } } diff --git a/src/InformaticsGateway/Services/HealthLevel7/MllpService.cs b/src/InformaticsGateway/Services/HealthLevel7/MllpService.cs index 8a4a99cd4..0004d0636 100755 --- a/src/InformaticsGateway/Services/HealthLevel7/MllpService.cs +++ b/src/InformaticsGateway/Services/HealthLevel7/MllpService.cs @@ -18,6 +18,7 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.IO.Abstractions; +using System.Linq; using System.Net; using System.Net.Sockets; using System.Text; @@ -29,17 +30,20 @@ using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; +using Monai.Deploy.InformaticsGateway.Api.PlugIns; using Monai.Deploy.InformaticsGateway.Api.Rest; using Monai.Deploy.InformaticsGateway.Api.Storage; using Monai.Deploy.InformaticsGateway.Common; using Monai.Deploy.InformaticsGateway.Configuration; +using Monai.Deploy.InformaticsGateway.Database.Api.Repositories; using Monai.Deploy.InformaticsGateway.Logging; using Monai.Deploy.InformaticsGateway.Services.Common; using Monai.Deploy.InformaticsGateway.Services.Connectors; +using Monai.Deploy.InformaticsGateway.Services.HealthLevel7; using Monai.Deploy.InformaticsGateway.Services.Storage; using Monai.Deploy.Messaging.Events; -namespace Monai.Deploy.InformaticsGateway.Services.HealthLevel7 +namespace Monai.Deploy.InformaticsGateway.Api.Mllp { internal sealed class MllpService : IMllpService, IHostedService, IDisposable, IMonaiService { @@ -56,6 +60,9 @@ internal sealed class MllpService : IMllpService, IHostedService, IDisposable, I private readonly IStorageInfoProvider _storageInfoProvider; private readonly ConcurrentDictionary _activeTasks; private readonly IMllpExtract _mIIpExtract; + private readonly IInputHL7DataPlugInEngine _inputHL7DataPlugInEngine; + private readonly IHl7ApplicationConfigRepository _hl7ApplicationConfigRepository; + private DateTime _lastConfigRead = new(2000, 1, 1, 0, 0, 0, DateTimeKind.Utc); public int ActiveConnections { @@ -91,6 +98,8 @@ public MllpService(IServiceScopeFactory serviceScopeFactory, _storageInfoProvider = serviceScope.ServiceProvider.GetService() ?? throw new ServiceNotFoundException(nameof(IStorageInfoProvider)); _mIIpExtract = serviceScope.ServiceProvider.GetService() ?? throw new ServiceNotFoundException(nameof(IMllpExtract)); _activeTasks = new ConcurrentDictionary(); + _inputHL7DataPlugInEngine = serviceScope.ServiceProvider.GetService() ?? throw new ServiceNotFoundException(nameof(IInputHL7DataPlugInEngine)); + _hl7ApplicationConfigRepository = serviceScope.ServiceProvider.GetService() ?? throw new ServiceNotFoundException(nameof(IHl7ApplicationConfigRepository)); } public Task StartAsync(CancellationToken cancellationToken) @@ -170,12 +179,23 @@ private async Task OnDisconnect(IMllpClient client, MllpClientResult result) Guard.Against.Null(client, nameof(client)); Guard.Against.Null(result, nameof(result)); + await ConfigurePlugInEngine().ConfigureAwait(false); + try { foreach (var message in result.Messages) { + var newMessage = message; var hl7Filemetadata = new Hl7FileStorageMetadata(client.ClientId.ToString(), DataService.HL7, client.ClientIp); - var newMessage = await _mIIpExtract.ExtractInfo(hl7Filemetadata, message).ConfigureAwait(false); + var configItem = await _mIIpExtract.GetConfigItem(message).ConfigureAwait(false); + if (configItem is not null) + { + await _inputHL7DataPlugInEngine.ExecutePlugInsAsync(message, hl7Filemetadata, configItem).ConfigureAwait(false); + newMessage = await _mIIpExtract.ExtractInfo(hl7Filemetadata, message, configItem).ConfigureAwait(false); + + _logger.LogTrace($"HL7 message after plug-in processing: {newMessage.HL7Message} correlationId: {hl7Filemetadata.CorrelationId}"); + } + _logger.Hl7MessageReceieved(newMessage.HL7Message); await hl7Filemetadata.SetDataStream(newMessage.HL7Message, _configuration.Value.Storage.TemporaryDataStorage, _fileSystem, _configuration.Value.Storage.LocalTemporaryStoragePath).ConfigureAwait(false); var payloadId = await _payloadAssembler.Queue(client.ClientId.ToString(), hl7Filemetadata, new DataOrigin { DataService = DataService.HL7, Source = client.ClientIp, Destination = FileStorageMetadata.IpAddress() }).ConfigureAwait(false); hl7Filemetadata.PayloadId ??= payloadId.ToString(); @@ -194,6 +214,31 @@ private async Task OnDisconnect(IMllpClient client, MllpClientResult result) } } + private async Task ConfigurePlugInEngine() + { + var configs = await _hl7ApplicationConfigRepository.GetAllAsync().ConfigureAwait(false); + if (configs is not null && configs.Any() && configs.Max(c => c.LastModified) > _lastConfigRead) + { + var pluginAssemblies = new List(); + foreach (var config in configs.Where(p => p.PlugInAssemblies?.Count > 0)) + { + try + { + pluginAssemblies.AddRange(config.PlugInAssemblies.Where(p => pluginAssemblies.Contains(p) is false)); + } + catch (Exception ex) + { + _logger.HL7PluginLoadingExceptions(ex); + } + } + if (pluginAssemblies.Any()) + { + _inputHL7DataPlugInEngine.Configure(pluginAssemblies); + } + } + _lastConfigRead = DateTime.UtcNow; + } + private void WaitUntilAvailable(int maximumNumberOfConnections) { var count = 0; @@ -295,7 +340,7 @@ private async Task EnsureAck(NetworkStream networkStream) foreach (var message in _rawHl7Messages) { var hl7Message = new Message(message); - hl7Message.ParseMessage(); + hl7Message.ParseMessage(true); if (hl7Message.MessageStructure == "ACK") { return; diff --git a/src/InformaticsGateway/Services/Http/Startup.cs b/src/InformaticsGateway/Services/Http/Startup.cs index dfc6dc5e1..6f0779ebd 100755 --- a/src/InformaticsGateway/Services/Http/Startup.cs +++ b/src/InformaticsGateway/Services/Http/Startup.cs @@ -44,10 +44,8 @@ public Startup(IConfiguration configuration) public IConfiguration Configuration { get; } -#pragma warning disable CA1822 // Mark members as static public void ConfigureServices(IServiceCollection services) -#pragma warning restore CA1822 // Mark members as static { services.AddHttpContextAccessor(); services.AddControllers(opts => diff --git a/src/InformaticsGateway/Services/Scp/ApplicationEntityHandler.cs b/src/InformaticsGateway/Services/Scp/ApplicationEntityHandler.cs index 411657c86..4946bc6ac 100755 --- a/src/InformaticsGateway/Services/Scp/ApplicationEntityHandler.cs +++ b/src/InformaticsGateway/Services/Scp/ApplicationEntityHandler.cs @@ -112,7 +112,7 @@ public async Task HandleInstanceAsync(DicomCStoreRequest request, string _logger.InstanceIgnoredWIthMatchingSopClassUid(request.SOPClassUID.UID); return string.Empty; } - ExternalAppDetails? storedDetails = null; + ExternalAppDetails? storedDetails; diff --git a/src/InformaticsGateway/Test/Plug-ins/TestInputHL7DataPlugs.cs b/src/InformaticsGateway/Test/Plug-ins/TestInputHL7DataPlugs.cs new file mode 100755 index 000000000..c3b45c8f4 --- /dev/null +++ b/src/InformaticsGateway/Test/Plug-ins/TestInputHL7DataPlugs.cs @@ -0,0 +1,39 @@ +/* + * 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.Reflection; +using HL7.Dotnetcore; +using Monai.Deploy.InformaticsGateway.Api.PlugIns; +using Monai.Deploy.InformaticsGateway.Api.Storage; + +namespace Monai.Deploy.InformaticsGateway.Test.PlugIns +{ + [PlugInName("TestInputHL7DataPlugInAddWorkflow")] + public class TestInputHL7DataPlugInAddWorkflow : IInputHL7DataPlugIn + { + public static readonly string TestString = "HOSPITAL changed!"; + + public string Name => GetType().GetCustomAttribute()?.Name ?? GetType().Name; + + public Task<(Message hl7Message, FileStorageMetadata fileMetadata)> ExecuteAsync(Message hl7File, FileStorageMetadata fileMetadata) + { + hl7File.SetValue("MSH.3", TestString); + hl7File = new Message(hl7File.SerializeMessage(false)); + hl7File.ParseMessage(); + fileMetadata.Workflows.Add(TestString); + return Task.FromResult((hl7File, fileMetadata)); + } + } +} diff --git a/src/InformaticsGateway/Test/Services/Common/InputHL7DataPlugInEngineFactoryTest.cs b/src/InformaticsGateway/Test/Services/Common/InputHL7DataPlugInEngineFactoryTest.cs new file mode 100755 index 000000000..47a56a735 --- /dev/null +++ b/src/InformaticsGateway/Test/Services/Common/InputHL7DataPlugInEngineFactoryTest.cs @@ -0,0 +1,69 @@ +/* + * 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 System.Collections.Generic; +using System.IO.Abstractions; +using System.Linq; +using System.Reflection; +using System.Text.Json; +using Microsoft.Extensions.Logging; +using Monai.Deploy.InformaticsGateway.Api.PlugIns; +using Monai.Deploy.InformaticsGateway.Common; +using Monai.Deploy.InformaticsGateway.Services.Common; +using Monai.Deploy.InformaticsGateway.SharedTest; +using Monai.Deploy.InformaticsGateway.Test.PlugIns; +using Moq; +using Xunit; +using Xunit.Abstractions; +namespace Monai.Deploy.InformaticsGateway.Test.Services.Common +{ + public class InputHL7DataPlugInEngineFactoryTest + { + private readonly Mock> _logger; + private readonly FileSystem _fileSystem; + private readonly ITestOutputHelper _output; + + public InputHL7DataPlugInEngineFactoryTest(ITestOutputHelper output) + { + _logger = new Mock>(); + _fileSystem = new FileSystem(); + _output = output; + + _logger.Setup(p => p.IsEnabled(It.IsAny())).Returns(true); + } + + [Fact] + public void RegisteredPlugIns_WhenCalled_ReturnsListOfPlugIns() + { + var factory = new InputHL7DataPlugInEngineFactory(_fileSystem, _logger.Object); + var result = factory.RegisteredPlugIns().OrderBy(p => p.Value).ToArray(); + + _output.WriteLine($"result now = {JsonSerializer.Serialize(result)}"); + + Assert.Collection(result, + p => VerifyPlugIn(p, typeof(TestInputHL7DataPlugInAddWorkflow))); + + _logger.VerifyLogging($"{typeof(IInputHL7DataPlugIn).Name} data plug-in found {typeof(TestInputHL7DataPlugInAddWorkflow).GetCustomAttribute()?.Name}: {typeof(TestInputHL7DataPlugInAddWorkflow).GetShortTypeAssemblyName()}.", LogLevel.Information, Times.Once()); + } + + private void VerifyPlugIn(KeyValuePair values, Type type) + { + Assert.Equal(values.Key, type.GetCustomAttribute()?.Name); + Assert.Equal(values.Value, type.GetShortTypeAssemblyName()); + } + } +} diff --git a/src/InformaticsGateway/Test/Services/Common/InputHL7DataPlugInEngineTest.cs b/src/InformaticsGateway/Test/Services/Common/InputHL7DataPlugInEngineTest.cs new file mode 100755 index 000000000..917cf5cf0 --- /dev/null +++ b/src/InformaticsGateway/Test/Services/Common/InputHL7DataPlugInEngineTest.cs @@ -0,0 +1,164 @@ +/* + * 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 System.Collections.Generic; +using System.Threading.Tasks; +using HL7.Dotnetcore; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Monai.Deploy.InformaticsGateway.Api; +using Monai.Deploy.InformaticsGateway.Api.Storage; +using Monai.Deploy.InformaticsGateway.Common; +using Monai.Deploy.InformaticsGateway.Services.Common; +using Monai.Deploy.InformaticsGateway.Test.PlugIns; +using Monai.Deploy.Messaging.Events; +using Moq; +using Xunit; + +namespace Monai.Deploy.InformaticsGateway.Test.Services.Common +{ + public class InputHL7DataPlugInEngineTest + { + private readonly Mock> _logger; + private readonly Mock _serviceScopeFactory; + private readonly Mock _serviceScope; + private readonly ServiceProvider _serviceProvider; + + private const string SampleMessage = "MSH|^~\\&|MD|MD HOSPITAL|MD Test|MONAI Deploy|202207130000|SECURITY|MD^A01^ADT_A01|MSG00001|P|2.8||||\r\n"; + + public InputHL7DataPlugInEngineTest() + { + _logger = new Mock>(); + _serviceScopeFactory = new Mock(); + _serviceScope = new Mock(); + + var services = new ServiceCollection(); + services.AddScoped(p => _logger.Object); + + _serviceProvider = services.BuildServiceProvider(); + _serviceScopeFactory.Setup(p => p.CreateScope()).Returns(_serviceScope.Object); + _serviceScope.Setup(p => p.ServiceProvider).Returns(_serviceProvider); + + _logger.Setup(p => p.IsEnabled(It.IsAny())).Returns(true); + } + + [Fact] + public void GivenAnInputHL7DataPlugInEngine_WhenInitialized_ExpectParametersToBeValidated() + { + Assert.Throws(() => new InputHL7DataPlugInEngine(null, null)); + Assert.Throws(() => new InputHL7DataPlugInEngine(_serviceProvider, null)); + + _ = new InputHL7DataPlugInEngine(_serviceProvider, _logger.Object); + } + + + [Fact] + public void GivenAnInputHL7DataPlugInEngine_WhenConfigureIsCalledWithBogusAssemblies_ThrowsException() + { + var pluginEngine = new InputHL7DataPlugInEngine(_serviceProvider, _logger.Object); + var assemblies = new List() { "SomeBogusAssemblye" }; + + var exceptions = Assert.Throws(() => pluginEngine.Configure(assemblies)); + + Assert.Single(exceptions.InnerExceptions); + Assert.True(exceptions.InnerException is PlugInLoadingException); + Assert.Contains("Error loading plug-in 'SomeBogusAssemblye'", exceptions.InnerException.Message); + } + + [Fact] + public void GivenAnInputHL7DataPlugInEngine_WhenConfigureIsCalledWithAValidAssembly_ExpectNoExceptions() + { + var pluginEngine = new InputHL7DataPlugInEngine(_serviceProvider, _logger.Object); + var assemblies = new List() { typeof(TestInputHL7DataPlugInAddWorkflow).AssemblyQualifiedName }; + + pluginEngine.Configure(assemblies); + Assert.NotNull(pluginEngine); + } + + [Fact] + public async Task GivenAnInputHL7DataPlugInEngine_WhenExecutePlugInsIsCalledWithoutConfigure_ThrowsException() + { + var pluginEngine = new InputHL7DataPlugInEngine(_serviceProvider, _logger.Object); + var assemblies = new List() { typeof(TestInputHL7DataPlugInAddWorkflow).AssemblyQualifiedName }; + + var config = new Hl7ApplicationConfigEntity() + { + PlugInAssemblies = assemblies, + DataMapping = new List() + { + new StringKeyValuePair() { Key = "MSH.3", Value = "MSH.3" }, + }, + }; + + var dicomInfo = new DicomFileStorageMetadata( + Guid.NewGuid().ToString(), + Guid.NewGuid().ToString(), + "StudyInstanceUID", + "SeriesInstanceUID", + "SOPInstanceUID", + DataService.DicomWeb, + "calling", + "called"); + + var message = new HL7.Dotnetcore.Message(SampleMessage); + message.ParseMessage(true); + + await Assert.ThrowsAsync(async () => await pluginEngine.ExecutePlugInsAsync(message, dicomInfo, config)); + } + + [Fact] + public async Task GivenAnInputHL7DataPlugInEngine_WhenExecutePlugInsIsCalled_ExpectDataIsProcessedByPlugInAsync() + { + + var pluginEngine = new InputHL7DataPlugInEngine(_serviceProvider, _logger.Object); + var assemblies = new List() + { + typeof(TestInputHL7DataPlugInAddWorkflow).AssemblyQualifiedName, + }; + + var config = new Hl7ApplicationConfigEntity() + { + PlugInAssemblies = assemblies, + DataMapping = new List() + { + new StringKeyValuePair() { Key = "MSH.3", Value = "MSH.3" }, + }, + }; + + pluginEngine.Configure(assemblies); + + var dicomInfo = new DicomFileStorageMetadata( + Guid.NewGuid().ToString(), + Guid.NewGuid().ToString(), + "StudyInstanceUID", + "SeriesInstanceUID", + "SOPInstanceUID", + DataService.DicomWeb, + "calling", + "called"); + + var message = new HL7.Dotnetcore.Message(SampleMessage); + message.ParseMessage(true); + + var (Hl7Message, resultDicomInfo) = await pluginEngine.ExecutePlugInsAsync(message, dicomInfo, config); + + Assert.NotEqual(Hl7Message, message); + Assert.Equal(resultDicomInfo, dicomInfo); + Assert.Equal(Hl7Message.GetValue("MSH.3"), TestInputHL7DataPlugInAddWorkflow.TestString); + } + } +} diff --git a/src/InformaticsGateway/Test/Services/Export/ExportHl7ServiceTests.cs b/src/InformaticsGateway/Test/Services/Export/ExportHl7ServiceTests.cs index 216709cbf..0897a68a2 100755 --- a/src/InformaticsGateway/Test/Services/Export/ExportHl7ServiceTests.cs +++ b/src/InformaticsGateway/Test/Services/Export/ExportHl7ServiceTests.cs @@ -27,6 +27,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; +using Monai.Deploy.InformaticsGateway.Api.Mllp; using Monai.Deploy.InformaticsGateway.Api.Models; using Monai.Deploy.InformaticsGateway.Api.PlugIns; using Monai.Deploy.InformaticsGateway.Common; diff --git a/src/InformaticsGateway/Test/Services/HealthLevel7/MllPExtractTests.cs b/src/InformaticsGateway/Test/Services/HealthLevel7/MllPExtractTests.cs index d8809e333..c9fba7e67 100755 --- a/src/InformaticsGateway/Test/Services/HealthLevel7/MllPExtractTests.cs +++ b/src/InformaticsGateway/Test/Services/HealthLevel7/MllPExtractTests.cs @@ -16,7 +16,6 @@ */ -using Monai.Deploy.InformaticsGateway.Services.HealthLevel7; using System.Threading; using Moq; using Xunit; @@ -26,6 +25,7 @@ using System.Collections.Generic; using HL7.Dotnetcore; using Monai.Deploy.InformaticsGateway.Database.Api.Repositories; +using Monai.Deploy.InformaticsGateway.Api.Mllp; using Monai.Deploy.InformaticsGateway.Api.Storage; using System.Threading.Tasks; using Monai.Deploy.InformaticsGateway.Api.Models; @@ -75,13 +75,13 @@ public void ParseConfig_Should_Return_Correct_Item() var message = new Message(SampleMessage); var isParsed = message.ParseMessage(); - var config = MllpExtract.GetConfig(configs, message); + var config = _sut.GetConfig(configs, message); Assert.Equal(correctid, config?.Id); message = new Message(ABCDEMessage); isParsed = message.ParseMessage(); - config = MllpExtract.GetConfig(configs, message); + config = _sut.GetConfig(configs, message); Assert.Equal(azCorrectid, config?.Id); } @@ -117,7 +117,8 @@ public async Task Should_Set_MetaData_On_Hl7FileStorageMetadata_Object() var meatData = new Hl7FileStorageMetadata { Id = "metaId", File = new StorageObjectMetadata("txt") }; - await _sut.ExtractInfo(meatData, message); + var configItem = await _sut.GetConfigItem(message); + await _sut.ExtractInfo(meatData, message, configItem); Assert.Equal("WorkflowInstanceId2", meatData.WorkflowInstanceId); Assert.Equal("ExportTaskID2", meatData.TaskId); @@ -166,8 +167,8 @@ public async Task Should_Set_Original_Patient_And_Study_Uid() var meatData = new Hl7FileStorageMetadata { Id = "metaId", File = new StorageObjectMetadata("txt") }; - - message = await _sut.ExtractInfo(meatData, message); + var configItem = await _sut.GetConfigItem(message); + message = await _sut.ExtractInfo(meatData, message, configItem); Assert.Equal("PatentID", message.GetValue("OBR.3")); Assert.Equal("PatentID", message.GetValue("PID.2")); @@ -190,13 +191,13 @@ public void ParseConfig_Should_Return_Correct_Item_For_Vendor() var message = new Message(VendorMessage); var isParsed = message.ParseMessage(); - var config = MllpExtract.GetConfig(configs, message); + var config = _sut.GetConfig(configs, message); Assert.Equal(correctid, config?.Id); message = new Message(ABCDEMessage); isParsed = message.ParseMessage(); - config = MllpExtract.GetConfig(configs, message); + config = _sut.GetConfig(configs, message); Assert.Equal(azCorrectid, config?.Id); } } diff --git a/src/InformaticsGateway/Test/Services/HealthLevel7/MllpClientTest.cs b/src/InformaticsGateway/Test/Services/HealthLevel7/MllpClientTest.cs index 033efea81..6eb272064 100755 --- a/src/InformaticsGateway/Test/Services/HealthLevel7/MllpClientTest.cs +++ b/src/InformaticsGateway/Test/Services/HealthLevel7/MllpClientTest.cs @@ -22,6 +22,7 @@ using System.Threading.Tasks; using HL7.Dotnetcore; using Microsoft.Extensions.Logging; +using Monai.Deploy.InformaticsGateway.Api.Mllp; using Monai.Deploy.InformaticsGateway.Configuration; using Monai.Deploy.InformaticsGateway.Services.Common; using Monai.Deploy.InformaticsGateway.Services.HealthLevel7; @@ -32,7 +33,7 @@ namespace Monai.Deploy.InformaticsGateway.Test.Services.HealthLevel7 { public class MllpClientTest { - private const string SampleMessage = "MSH|^~\\&|MD|MD HOSPITAL|MD Test|MONAI Deploy|202207130000|SECURITY|MD^A01^ADT_A01|MSG00001|P|2.8||||\r\n"; + private const string SampleMessage = "MSH|^~\\&|MD|MD HOSPITAL|MD Test|MONAI Deploy|202207130000|SECURITY|MD^A01^ADT_A01|MSG00001|P|2.8||||\r"; private readonly Mock _tcpClient; private readonly Hl7Configuration _config; diff --git a/src/InformaticsGateway/Test/Services/HealthLevel7/MllpServiceTest.cs b/src/InformaticsGateway/Test/Services/HealthLevel7/MllpServiceTest.cs index 52757d6d0..1f2119bc6 100755 --- a/src/InformaticsGateway/Test/Services/HealthLevel7/MllpServiceTest.cs +++ b/src/InformaticsGateway/Test/Services/HealthLevel7/MllpServiceTest.cs @@ -29,13 +29,16 @@ using Monai.Deploy.InformaticsGateway.Configuration; using Monai.Deploy.InformaticsGateway.Services.Common; using Monai.Deploy.InformaticsGateway.Services.Connectors; -using Monai.Deploy.InformaticsGateway.Services.HealthLevel7; +using Monai.Deploy.InformaticsGateway.Api.Mllp; using Monai.Deploy.InformaticsGateway.Services.Storage; using Monai.Deploy.InformaticsGateway.SharedTest; using Monai.Deploy.Messaging.Events; using Moq; using xRetry; using Xunit; +using Monai.Deploy.InformaticsGateway.Api; +using Monai.Deploy.InformaticsGateway.Api.PlugIns; +using Monai.Deploy.InformaticsGateway.Database.Api.Repositories; namespace Monai.Deploy.InformaticsGateway.Test.Services.HealthLevel7 { @@ -57,6 +60,8 @@ public class MllpServiceTest private readonly IServiceProvider _serviceProvider; private readonly Mock _storageInfoProvider; private readonly Mock _mIIpExtract = new Mock(); + private readonly Mock _hl7DataPlugInEngine = new Mock(); + private readonly Mock _hl7ApplicationConfigRepository = new Mock(); public MllpServiceTest() { @@ -87,6 +92,8 @@ public MllpServiceTest() services.AddScoped(p => _fileSystem.Object); services.AddScoped(p => _storageInfoProvider.Object); services.AddScoped(p => _mIIpExtract.Object); + services.AddScoped(p => _hl7DataPlugInEngine.Object); + services.AddScoped(p => _hl7ApplicationConfigRepository.Object); _serviceProvider = services.BuildServiceProvider(); _serviceScopeFactory.Setup(p => p.CreateScope()).Returns(_serviceScope.Object); @@ -276,8 +283,9 @@ public async Task GivenATcpClientWithHl7Messages_WhenDisconnected_ExpectMessageT { var checkEvent = new ManualResetEventSlim(); var client = new Mock(); - _mIIpExtract.Setup(e => e.ExtractInfo(It.IsAny(), It.IsAny())) - .ReturnsAsync((Hl7FileStorageMetadata meta, Message Msg) => Msg); + _mIIpExtract.Setup(e => e.ExtractInfo(It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync((Hl7FileStorageMetadata meta, Message Msg, Hl7ApplicationConfigEntity configItem) => Msg); + _mllpClientFactory.Setup(p => p.CreateClient(It.IsAny(), It.IsAny(), It.IsAny>())) .Returns(() => { @@ -319,8 +327,11 @@ public async Task GivenATcpClientWithHl7Messages_WhenDisconnected_ExpectMessageT var checkEvent = new ManualResetEventSlim(); var client = new Mock(); - _mIIpExtract.Setup(e => e.ExtractInfo(It.IsAny(), It.IsAny())) - .ReturnsAsync((Hl7FileStorageMetadata meta, Message Msg) => Msg); + _mIIpExtract.Setup(e => e.ExtractInfo(It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync((Hl7FileStorageMetadata meta, Message Msg, Hl7ApplicationConfigEntity configItem) => Msg); + + _mIIpExtract.Setup(e => e.GetConfigItem(It.IsAny())) + .ReturnsAsync((Message Msg) => new Hl7ApplicationConfigEntity()); _mllpClientFactory.Setup(p => p.CreateClient(It.IsAny(), It.IsAny(), It.IsAny>())) .Returns(() => @@ -353,7 +364,7 @@ public async Task GivenATcpClientWithHl7Messages_WhenDisconnected_ExpectMessageT Assert.True(checkEvent.Wait(3000)); await Task.Delay(500).ConfigureAwait(false); - _mIIpExtract.Verify(p => p.ExtractInfo(It.IsAny(), It.IsAny()), Times.Exactly(3)); + _mIIpExtract.Verify(p => p.ExtractInfo(It.IsAny(), It.IsAny(), It.IsAny()), Times.Exactly(3)); } } } diff --git a/src/InformaticsGateway/Test/packages.lock.json b/src/InformaticsGateway/Test/packages.lock.json index 5820049ac..0cbb69366 100755 --- a/src/InformaticsGateway/Test/packages.lock.json +++ b/src/InformaticsGateway/Test/packages.lock.json @@ -2085,6 +2085,7 @@ "monai.deploy.informaticsgateway.api": { "type": "Project", "dependencies": { + "HL7-dotnetcore": "[2.36.0, )", "Macross.Json.Extensions": "[3.0.0, )", "Microsoft.EntityFrameworkCore.Abstractions": "[6.0.25, )", "Monai.Deploy.InformaticsGateway.Common": "[1.0.0, )", diff --git a/src/InformaticsGateway/nlog.config b/src/InformaticsGateway/nlog.config index 4b106748b..8798fce06 100755 --- a/src/InformaticsGateway/nlog.config +++ b/src/InformaticsGateway/nlog.config @@ -51,6 +51,7 @@ limitations under the License. + diff --git a/src/InformaticsGateway/packages.lock.json b/src/InformaticsGateway/packages.lock.json index 630572169..1778d145c 100755 --- a/src/InformaticsGateway/packages.lock.json +++ b/src/InformaticsGateway/packages.lock.json @@ -1695,6 +1695,7 @@ "monai.deploy.informaticsgateway.api": { "type": "Project", "dependencies": { + "HL7-dotnetcore": "[2.36.0, )", "Macross.Json.Extensions": "[3.0.0, )", "Microsoft.EntityFrameworkCore.Abstractions": "[6.0.25, )", "Monai.Deploy.InformaticsGateway.Common": "[1.0.0, )", diff --git a/src/Plug-ins/RemoteAppExecution/Test/packages.lock.json b/src/Plug-ins/RemoteAppExecution/Test/packages.lock.json index 4d70f5c72..ac62dee73 100755 --- a/src/Plug-ins/RemoteAppExecution/Test/packages.lock.json +++ b/src/Plug-ins/RemoteAppExecution/Test/packages.lock.json @@ -149,6 +149,11 @@ "System.Threading.Channels": "6.0.0" } }, + "HL7-dotnetcore": { + "type": "Transitive", + "resolved": "2.36.0", + "contentHash": "N1HLMeIqYuY+4O69ItgZJoDBnnpNkK5N2pClceTJ2nFJxsP48iCsA4iz3tm43Yszi4r/vaThoc3UoLBfGP3vKw==" + }, "Macross.Json.Extensions": { "type": "Transitive", "resolved": "3.0.0", @@ -1595,6 +1600,7 @@ "monai.deploy.informaticsgateway.api": { "type": "Project", "dependencies": { + "HL7-dotnetcore": "[2.36.0, )", "Macross.Json.Extensions": "[3.0.0, )", "Microsoft.EntityFrameworkCore.Abstractions": "[6.0.25, )", "Monai.Deploy.InformaticsGateway.Common": "[1.0.0, )", diff --git a/src/Plug-ins/RemoteAppExecution/packages.lock.json b/src/Plug-ins/RemoteAppExecution/packages.lock.json index 5c4215fea..a171e527a 100755 --- a/src/Plug-ins/RemoteAppExecution/packages.lock.json +++ b/src/Plug-ins/RemoteAppExecution/packages.lock.json @@ -169,6 +169,11 @@ "System.Threading.Channels": "6.0.0" } }, + "HL7-dotnetcore": { + "type": "Transitive", + "resolved": "2.36.0", + "contentHash": "N1HLMeIqYuY+4O69ItgZJoDBnnpNkK5N2pClceTJ2nFJxsP48iCsA4iz3tm43Yszi4r/vaThoc3UoLBfGP3vKw==" + }, "Humanizer.Core": { "type": "Transitive", "resolved": "2.8.26", @@ -589,6 +594,7 @@ "monai.deploy.informaticsgateway.api": { "type": "Project", "dependencies": { + "HL7-dotnetcore": "[2.36.0, )", "Macross.Json.Extensions": "[3.0.0, )", "Microsoft.EntityFrameworkCore.Abstractions": "[6.0.25, )", "Monai.Deploy.InformaticsGateway.Common": "[1.0.0, )", diff --git a/tests/Integration.Test/Common/DataProvider.cs b/tests/Integration.Test/Common/DataProvider.cs index cb609530b..2748a16a2 100755 --- a/tests/Integration.Test/Common/DataProvider.cs +++ b/tests/Integration.Test/Common/DataProvider.cs @@ -311,7 +311,8 @@ internal async Task GenerateHl7Messages(string version) var message = new HL7.Dotnetcore.Message(text); message.ParseMessage(); message.SetValue("MSH.10", file); - HL7Specs.Files[file] = message; + HL7Specs.Files[file] = new HL7.Dotnetcore.Message(message.SerializeMessage(false)); + HL7Specs.Files[file].ParseMessage(); } } } diff --git a/tests/Integration.Test/StepDefinitions/Hl7StepDefinitions.cs b/tests/Integration.Test/StepDefinitions/Hl7StepDefinitions.cs index be93a8bce..69887eff2 100755 --- a/tests/Integration.Test/StepDefinitions/Hl7StepDefinitions.cs +++ b/tests/Integration.Test/StepDefinitions/Hl7StepDefinitions.cs @@ -219,7 +219,7 @@ public async Task ThenEnsureThatExportcompleteMessagesAreSentWithSuscess(string private async Task SendAcknowledgment(NetworkStream networkStream, HL7.Dotnetcore.Message message, CancellationToken cancellationToken) { if (message == null) { return; } - var ackMessage = message.GetACK(); + var ackMessage = message.GetACK(true); var ackData = new ReadOnlyMemory(ackMessage.GetMLLP()); { try diff --git a/tests/Integration.Test/StepDefinitions/RemoteAppExecutionPlugInsStepDefinitions.cs b/tests/Integration.Test/StepDefinitions/RemoteAppExecutionPlugInsStepDefinitions.cs index 2e126327b..55e7137cf 100755 --- a/tests/Integration.Test/StepDefinitions/RemoteAppExecutionPlugInsStepDefinitions.cs +++ b/tests/Integration.Test/StepDefinitions/RemoteAppExecutionPlugInsStepDefinitions.cs @@ -25,7 +25,6 @@ using Monai.Deploy.InformaticsGateway.Configuration; using Monai.Deploy.InformaticsGateway.Integration.Test.Common; using Monai.Deploy.InformaticsGateway.Integration.Test.Drivers; -//using Monai.Deploy.InformaticsGateway.PlugIns.Pseudonymisation; using Monai.Deploy.InformaticsGateway.PlugIns.RemoteAppExecution; using Monai.Deploy.Messaging.Events; using Monai.Deploy.Messaging.Messages; diff --git a/tests/Integration.Test/appsettings.json b/tests/Integration.Test/appsettings.json index e16ccb4b4..8e3d304b6 100755 --- a/tests/Integration.Test/appsettings.json +++ b/tests/Integration.Test/appsettings.json @@ -5,6 +5,25 @@ "plugins": { "remoteApp": { "ReplaceTags": "AccessionNumber, StudyDescription, SeriesDescription, PatientAddress, PatientAge, PatientName" + }, + "Pseudonymise": { + "ConnectionString": "mongodb://root:rootpassword@localhost:27017", + "DatabaseName": "InformaticsGateway", + "EncriptionClientTimeoutSeconds": "900", + "ExpiresAfterDays": "1", + "ExternalAppTaskInboundKeepTags": "0020 000E, 0020 0010, 0020 00013, 0008 0018, 0020 000E", + "ImportantTags": "0010 0020, 0008 0018, 0008 0016, 0020 000D, 0020 000E, 0020 0010,0008 1155, 0008 0014, 0008 0050, 0008 0080, 0008 0081, 0008 0090, 0008 0092, 0008 0094, 0008 1010, 0008 1030, 0008 103E, 0008 1040, 0008 1048,0008 1050, 0008 1060, 0008 1070, 0008 1080, 0008 2111, 0010 0010, 0010 0030, 0010 0030, 0010 0032, 0010 0040, 0010 1000, 0010 1001, 0010 1001, 0010 1010, 0010 1020, 0010 1030, 0010 1090, 0010 2160, 0010 2180, 0010 21B0, 0010 4000, 0018 1000, 0018 1030, 0020 0052, 0020 0200, 0020 4000, 0040 0275, 0040 A124, 0040 A730, 0088 0140, 3006 0024, 3006 00C2", + "SecurityProfile": "\t\t\t\t0010,0020;K;;;;;;;;;;\r\n\t\t\t\t0008,0018;C;;;;;;;;;;\r\n\t\t\t\t0008,0016;K;;;;;;;;;;\r\n\t\t\t\t0020,000D;K;;;;;;;;;;\r\n\t\t\t\t0020,000E;C;;;;;;;;;;\r\n\t\t\t\t0020,0010;C;;;;;;;;;;\r\n\t\t\t\t0008,1155;C;;;;;;;;;;\r\n\t\t\t\t0008,0014;X;;;;;;;;;;\r\n\t\t\t\t0008,0050;K;;;;;;;;;;\r\n\t\t\t\t0008,0080;X;;;;;;;;;;\r\n\t\t\t\t0008,0081;X;;;;;;;;;;\r\n\t\t\t\t0008,0090;X;;;;;;;;;;\r\n\t\t\t\t0008,0092;X;;;;;;;;;;\r\n\t\t\t\t0008,0094;X;;;;;;;;;;\r\n\t\t\t\t0008,1010;X;;;;;;;;;;\r\n\t\t\t\t0008,1030;X;;;;;;;;;;\r\n\t\t\t\t0008,103E;X;;;;;;;;;;\r\n\t\t\t\t0008,1040;X;;;;;;;;;;\r\n\t\t\t\t0008,1048;X;;;;;;;;;;\r\n\t\t\t\t0008,1050;X;;;;;;;;;;\r\n\t\t\t\t0008,1060;X;;;;;;;;;;\r\n\t\t\t\t0008,1070;X;;;;;;;;;;\r\n\t\t\t\t0008,1080;X;;;;;;;;;;\r\n\t\t\t\t0008,2111;X;;;;;;;;;;\r\n\t\t\t\t0010,0010;X;;;;;;;;;;\r\n\t\t\t\t0010,0030;X;;;;;;;;;;\r\n\t\t\t\t0010,0030;X;;;;;;;;;;\r\n\t\t\t\t0010,0032;X;;;;;;;;;;\r\n\t\t\t\t0010,0040;X;;;;;;;;;;\r\n\t\t\t\t0010,1000;X;;;;;;;;;;\r\n\t\t\t\t0010,1001;X;;;;;;;;;;\r\n\t\t\t\t0010,1001;X;;;;;;;;;;\r\n\t\t\t\t0010,1010;X;;;;;;;;;;\r\n\t\t\t\t0010,1020;X;;;;;;;;;;\r\n\t\t\t\t0010,1030;X;;;;;;;;;;\r\n\t\t\t\t0010,1090;X;;;;;;;;;;\r\n\t\t\t\t0010,2160;X;;;;;;;;;;\r\n\t\t\t\t0010,2180;X;;;;;;;;;;\r\n\t\t\t\t0010,21B0;X;;;;;;;;;;\r\n\t\t\t\t0010,4000;X;;;;;;;;;;\r\n\t\t\t\t\r\n\t\t\t\t0018,1000;X;;;;;;;;;;\r\n\t\t\t\t0018,1030;X;;;;;;;;;;\r\n\t\t\t\t0020,0052;X;;;;;;;;;;\r\n\t\t\t\t0020,0200;X;;;;;;;;;;\r\n\t\t\t\t0020,4000;X;;;;;;;;;;\r\n\t\t\t\t0040,0275;X;;;;;;;;;;\r\n\t\t\t\t0040,A124;X;;;;;;;;;;\r\n\t\t\t\t0040,A730;X;;;;;;;;;;\r\n\t\t\t\t0088,0140;X;;;;;;;;;;\r\n\t\t\t\t3006,0024;X;;;;;;;;;;\r\n\t\t\t\t3006,00C2;X;;;;;;;;;;\r\n\t\t\t\t", + "KMS": { + "KeyVaultNamespace": "InformaticsGateway.KeyVault", + "AWS": { + "accessKeyId": "", + "arnKey": "", + "roleArnToAssume": "", + "region": "eu-west-2", + "secretAccessKey": "" + } + } } }, "ConnectionStrings": { diff --git a/tests/Integration.Test/packages.lock.json b/tests/Integration.Test/packages.lock.json index c3b9e5483..9a208b6ce 100755 --- a/tests/Integration.Test/packages.lock.json +++ b/tests/Integration.Test/packages.lock.json @@ -226,6 +226,16 @@ "resolved": "2.5.0", "contentHash": "+Gp9vuC2431yPyKB15YrOTxCuEAErBQUTIs6CquumX1F073UaPHGW0VE/XVJLMh9W4sXdz3TBkcHdFWZrRn2Hw==" }, + "AnswerDicomTools": { + "type": "Transitive", + "resolved": "0.1.1-rc0089", + "contentHash": "DgDBjo708kHmr0lUdUrYaRLjmaICrJMBh6w/Vd0E/r2SJ0DDiQuxMABJzIwXwrVFSzrrwpqPqWBQMTCY++9uPQ==", + "dependencies": { + "Microsoft.Extensions.Configuration": "6.0.1", + "MongoDB.Driver": "2.21.0", + "fo-dicom": "5.1.1" + } + }, "Ardalis.GuardClauses": { "type": "Transitive", "resolved": "4.1.1", @@ -413,6 +423,15 @@ "Microsoft.Extensions.Primitives": "6.0.0" } }, + "Microsoft.Extensions.Configuration.CommandLine": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "3nL1qCkZ1Oxx14ZTzgo4MmlO7tso7F+TtMZAY2jUAtTLyAcDp+EDjk3RqafoKiNaePyPvvlleEcBxh3b2Hzl1g==", + "dependencies": { + "Microsoft.Extensions.Configuration": "6.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "6.0.0" + } + }, "Microsoft.Extensions.Configuration.FileExtensions": { "type": "Transitive", "resolved": "6.0.0", @@ -425,6 +444,17 @@ "Microsoft.Extensions.Primitives": "6.0.0" } }, + "Microsoft.Extensions.Configuration.UserSecrets": { + "type": "Transitive", + "resolved": "6.0.1", + "contentHash": "Fy8yr4V6obi7ZxvKYI1i85jqtwMq8tqyxQVZpRSkgeA8enqy/KvBIMdcuNdznlxQMZa72mvbHqb7vbg4Pyx95w==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", + "Microsoft.Extensions.Configuration.Json": "6.0.0", + "Microsoft.Extensions.FileProviders.Abstractions": "6.0.0", + "Microsoft.Extensions.FileProviders.Physical": "6.0.0" + } + }, "Microsoft.Extensions.DependencyInjection": { "type": "Transitive", "resolved": "6.0.1", @@ -500,6 +530,34 @@ "resolved": "6.0.0", "contentHash": "ip8jnL1aPiaPeKINCqaTEbvBFDmVx9dXQEBZ2HOBRXPD1eabGNqP/bKlsIcp7U2lGxiXd5xIhoFcmY8nM4Hdiw==" }, + "Microsoft.Extensions.Hosting": { + "type": "Transitive", + "resolved": "6.0.1", + "contentHash": "hbmizc9KPWOacLU8Z8YMaBG6KWdZFppczYV/KwnPGU/8xebWxQxdDeJmLOgg968prb7g2oQgnp6JVLX6lgby8g==", + "dependencies": { + "Microsoft.Extensions.Configuration": "6.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", + "Microsoft.Extensions.Configuration.Binder": "6.0.0", + "Microsoft.Extensions.Configuration.CommandLine": "6.0.0", + "Microsoft.Extensions.Configuration.EnvironmentVariables": "6.0.1", + "Microsoft.Extensions.Configuration.FileExtensions": "6.0.0", + "Microsoft.Extensions.Configuration.Json": "6.0.0", + "Microsoft.Extensions.Configuration.UserSecrets": "6.0.1", + "Microsoft.Extensions.DependencyInjection": "6.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.FileProviders.Abstractions": "6.0.0", + "Microsoft.Extensions.FileProviders.Physical": "6.0.0", + "Microsoft.Extensions.Hosting.Abstractions": "6.0.0", + "Microsoft.Extensions.Logging": "6.0.0", + "Microsoft.Extensions.Logging.Abstractions": "6.0.0", + "Microsoft.Extensions.Logging.Configuration": "6.0.0", + "Microsoft.Extensions.Logging.Console": "6.0.0", + "Microsoft.Extensions.Logging.Debug": "6.0.0", + "Microsoft.Extensions.Logging.EventLog": "6.0.0", + "Microsoft.Extensions.Logging.EventSource": "6.0.0", + "Microsoft.Extensions.Options": "6.0.0" + } + }, "Microsoft.Extensions.Hosting.Abstractions": { "type": "Transitive", "resolved": "6.0.0", @@ -542,6 +600,55 @@ "Microsoft.Extensions.Options.ConfigurationExtensions": "6.0.0" } }, + "Microsoft.Extensions.Logging.Console": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "gsqKzOEdsvq28QiXFxagmn1oRB9GeI5GgYCkoybZtQA0IUb7QPwf1WmN3AwJeNIsadTvIFQCiVK0OVIgKfOBGg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.Logging": "6.0.0", + "Microsoft.Extensions.Logging.Abstractions": "6.0.0", + "Microsoft.Extensions.Logging.Configuration": "6.0.0", + "Microsoft.Extensions.Options": "6.0.0", + "System.Text.Json": "6.0.0" + } + }, + "Microsoft.Extensions.Logging.Debug": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "M9g/JixseSZATJE9tcMn9uzoD4+DbSglivFqVx8YkRJ7VVPmnvCEbOZ0AAaxsL1EKyI4cz07DXOOJExxNsUOHw==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.Logging": "6.0.0", + "Microsoft.Extensions.Logging.Abstractions": "6.0.0" + } + }, + "Microsoft.Extensions.Logging.EventLog": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "rlo0RxlMd0WtLG3CHI0qOTp6fFn7MvQjlrCjucA31RqmiMFCZkF8CHNbe8O7tbBIyyoLGWB1he9CbaA5iyHthg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.Logging": "6.0.0", + "Microsoft.Extensions.Logging.Abstractions": "6.0.0", + "Microsoft.Extensions.Options": "6.0.0", + "System.Diagnostics.EventLog": "6.0.0" + } + }, + "Microsoft.Extensions.Logging.EventSource": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "BeDyyqt7nkm/nr+Gdk+L8n1tUT/u33VkbXAOesgYSNsxDM9hJ1NOBGoZfj9rCbeD2+9myElI6JOVVFmnzgeWQA==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.Logging": "6.0.0", + "Microsoft.Extensions.Logging.Abstractions": "6.0.0", + "Microsoft.Extensions.Options": "6.0.0", + "Microsoft.Extensions.Primitives": "6.0.0", + "System.Runtime.CompilerServices.Unsafe": "6.0.0", + "System.Text.Json": "6.0.0" + } + }, "Microsoft.Extensions.Options": { "type": "Transitive", "resolved": "6.0.0", @@ -1941,6 +2048,25 @@ "resolved": "0.6.2", "contentHash": "jPao/LdUNLUz8rn3H1D8W7wQbZsRZM0iayvWI4xGejJg3XJHT56gcmYdgmCGPdJF1UEBqUjucCRrFB+4HbJsbw==" }, + "monai-deploy-informatics-gateway-pseudonymisation": { + "type": "Project", + "dependencies": { + "AnswerDicomTools": "[0.1.1-rc0089, )", + "Ardalis.GuardClauses": "[4.1.1, )", + "HL7-dotnetcore": "[2.36.0, )", + "Microsoft.EntityFrameworkCore": "[6.0.25, )", + "Microsoft.EntityFrameworkCore.Relational": "[6.0.25, )", + "Microsoft.EntityFrameworkCore.Sqlite": "[6.0.25, )", + "Microsoft.Extensions.Configuration": "[6.0.1, )", + "Microsoft.Extensions.Configuration.FileExtensions": "[6.0.0, )", + "Microsoft.Extensions.Configuration.Json": "[6.0.0, )", + "Microsoft.Extensions.Hosting": "[6.0.1, )", + "MongoDB.Driver": "[2.21.0, )", + "NLog": "[5.2.4, )", + "Polly": "[7.2.4, )", + "fo-dicom": "[5.1.1, )" + } + }, "monai.deploy.informaticsgateway": { "type": "Project", "dependencies": { @@ -1964,6 +2090,7 @@ "monai.deploy.informaticsgateway.api": { "type": "Project", "dependencies": { + "HL7-dotnetcore": "[2.36.0, )", "Macross.Json.Extensions": "[3.0.0, )", "Microsoft.EntityFrameworkCore.Abstractions": "[6.0.25, )", "Monai.Deploy.InformaticsGateway.Common": "[1.0.0, )",