diff --git a/src/.sonarlint/project-monai_monai-deploy-workflow-managercsharp.ruleset b/src/.sonarlint/project-monai_monai-deploy-workflow-managercsharp.ruleset index 8c6d762ed..af7ee0d2e 100644 --- a/src/.sonarlint/project-monai_monai-deploy-workflow-managercsharp.ruleset +++ b/src/.sonarlint/project-monai_monai-deploy-workflow-managercsharp.ruleset @@ -1,5 +1,9 @@ - - + + + + + + @@ -367,4 +371,7 @@ + + + \ No newline at end of file diff --git a/src/Common/Extensions/StorageListExtensions.cs b/src/Common/Extensions/StorageListExtensions.cs new file mode 100644 index 000000000..442cad9b5 --- /dev/null +++ b/src/Common/Extensions/StorageListExtensions.cs @@ -0,0 +1,29 @@ +// SPDX-FileCopyrightText: © 2021-2022 MONAI Consortium +// SPDX-License-Identifier: Apache License 2.0 + +using Ardalis.GuardClauses; + +namespace Monai.Deploy.WorkflowManager.Common.Extensions +{ + public static class StorageListExtensions + { + public static Dictionary ToArtifactDictionary(this List storageList) + { + Guard.Against.Null(storageList, nameof(storageList)); + + var artifactDict = new Dictionary(); + + foreach (var storage in storageList) + { + if (string.IsNullOrWhiteSpace(storage.Name) || string.IsNullOrWhiteSpace(storage.RelativeRootPath)) + { + continue; + } + + artifactDict.Add(storage.Name, storage.RelativeRootPath); + } + + return artifactDict; + } + } +} diff --git a/src/Configuration/Monai.Deploy.WorkflowManager.Configuration.csproj b/src/Configuration/Monai.Deploy.WorkflowManager.Configuration.csproj index 54050a155..4eee66ab0 100644 --- a/src/Configuration/Monai.Deploy.WorkflowManager.Configuration.csproj +++ b/src/Configuration/Monai.Deploy.WorkflowManager.Configuration.csproj @@ -15,8 +15,8 @@ SPDX-License-Identifier: Apache License 2.0 - - + + @@ -29,10 +29,9 @@ SPDX-License-Identifier: Apache License 2.0 - + true - true true diff --git a/src/Configuration/packages.lock.json b/src/Configuration/packages.lock.json index 70ab503a1..0c8199f4c 100644 --- a/src/Configuration/packages.lock.json +++ b/src/Configuration/packages.lock.json @@ -24,9 +24,9 @@ }, "Monai.Deploy.Messaging": { "type": "Direct", - "requested": "[0.1.0-rc0036, )", - "resolved": "0.1.0-rc0036", - "contentHash": "ONfPbkd0bsJGJHOPVn7U5z0FDNirYuvMswLauN8rxHNSat0GbhFDnObrY8iIuCFY02zfPfpoL9mywkksVwsUAA==", + "requested": "[0.1.0-rc0040, )", + "resolved": "0.1.0-rc0040", + "contentHash": "q5t6h24M+22JQqeBTD9wOOYYAvb1Vk29nkadcOJ05wT569jnF+MGmEherDn1moX45Ehj4TsCvQl3TD4HJlC1mA==", "dependencies": { "Ardalis.GuardClauses": "4.0.1", "Microsoft.Extensions.Configuration": "6.0.1", @@ -38,9 +38,9 @@ }, "Monai.Deploy.Storage": { "type": "Direct", - "requested": "[0.1.0-rc0019, )", - "resolved": "0.1.0-rc0019", - "contentHash": "YAXgTE7vTqeMc+ZhprUg/muQ+JDVMaix1Ha1vLJTSSjfZWBOnxyK5SugS50uNlvByJcqHEXh+ygd9219wK1S+Q==", + "requested": "[0.1.0-rc0021, )", + "resolved": "0.1.0-rc0021", + "contentHash": "jealmI38hO1f8iTGt9ACYUx5sIyUnrf5tMVA7DNekzJ9mufhTMpNgukCqmkvYpXeljaPBp6yWn7NzfjiS1fnhw==", "dependencies": { "AWSSDK.SecurityToken": "3.7.1.141", "Ardalis.GuardClauses": "4.0.0", diff --git a/src/Contracts/Models/Artifact.cs b/src/Contracts/Models/Artifact.cs index 309038262..d3e6067dc 100644 --- a/src/Contracts/Models/Artifact.cs +++ b/src/Contracts/Models/Artifact.cs @@ -12,5 +12,8 @@ public class Artifact [JsonProperty(PropertyName = "value")] public string Value { get; set; } + + [JsonProperty(PropertyName = "mandatory")] + public bool Mandatory { get; set; } } } diff --git a/src/Contracts/Models/TaskExecution.cs b/src/Contracts/Models/TaskExecution.cs index 6d6085a5f..7c59232ea 100644 --- a/src/Contracts/Models/TaskExecution.cs +++ b/src/Contracts/Models/TaskExecution.cs @@ -27,6 +27,9 @@ public class TaskExecution [JsonProperty(PropertyName = "input_artifacts")] public Dictionary InputArtifacts { get; set; } + [JsonProperty(PropertyName = "output_artifacts")] + public Dictionary OutputArtifacts { get; set; } + [JsonProperty(PropertyName = "output_directory")] public string OutputDirectory { get; set; } diff --git a/src/Contracts/Monai.Deploy.WorkflowManager.Contracts.csproj b/src/Contracts/Monai.Deploy.WorkflowManager.Contracts.csproj index 8e6e4f7d8..61fb91d44 100644 --- a/src/Contracts/Monai.Deploy.WorkflowManager.Contracts.csproj +++ b/src/Contracts/Monai.Deploy.WorkflowManager.Contracts.csproj @@ -18,7 +18,7 @@ SPDX-License-Identifier: Apache License 2.0 - + diff --git a/src/Database/Interfaces/IWorkflowInstanceRepository.cs b/src/Database/Interfaces/IWorkflowInstanceRepository.cs index 6425a6d13..4a4ea4ac8 100644 --- a/src/Database/Interfaces/IWorkflowInstanceRepository.cs +++ b/src/Database/Interfaces/IWorkflowInstanceRepository.cs @@ -37,6 +37,21 @@ public interface IWorkflowInstanceRepository /// Status to set. Task UpdateTaskStatusAsync(string workflowInstanceId, string taskId, TaskExecutionStatus status); + /// + /// Updates the Task output artifacts for a given task within a workflow instance. + /// + /// Workflow Instance to update. + /// TaskId to update. + /// Output artifacts to set. + Task UpdateTaskOutputArtifactsAsync(string workflowInstanceId, string taskId, Dictionary outputArtifacts); + + /// + /// Gets a task execution for a given workflow instance id and task id. + /// + /// A Workflow Instance Id to retrieve a task from. + /// A Task Id to retrieve. + Task GetTaskByIdAsync(string workflowInstanceId, string taskId); + /// /// Updates the Task list for a given workflow instance. /// diff --git a/src/Database/Monai.Deploy.WorkflowManager.Database.csproj b/src/Database/Monai.Deploy.WorkflowManager.Database.csproj index c95ddae26..2ff560d94 100644 --- a/src/Database/Monai.Deploy.WorkflowManager.Database.csproj +++ b/src/Database/Monai.Deploy.WorkflowManager.Database.csproj @@ -20,7 +20,7 @@ - + diff --git a/src/Database/WorkflowInstanceRepository.cs b/src/Database/WorkflowInstanceRepository.cs index b05226973..0e11ce1c7 100644 --- a/src/Database/WorkflowInstanceRepository.cs +++ b/src/Database/WorkflowInstanceRepository.cs @@ -55,6 +55,7 @@ public async Task> GetByWorkflowsIdsAsync(List w catch (Exception e) { _logger.DbCallFailed(nameof(GetByWorkflowsIdsAsync), e); + return new List(); } } @@ -66,11 +67,13 @@ public async Task CreateAsync(IList workflowInstances) try { await _workflowInstanceCollection.InsertManyAsync(workflowInstances); + return true; } catch (Exception e) { _logger.DbCallFailed(nameof(CreateAsync), e); + return false; } } @@ -83,14 +86,38 @@ public async Task UpdateTaskStatusAsync(string workflowInstanceId, string try { - var result = await _workflowInstanceCollection.FindOneAndUpdateAsync( + await _workflowInstanceCollection.FindOneAndUpdateAsync( i => i.Id == workflowInstanceId && i.Tasks.Any(t => t.TaskId == taskId), Builders.Update.Set(w => w.Tasks[-1].Status, status)); + return true; } catch (Exception e) { _logger.DbCallFailed(nameof(UpdateTaskStatusAsync), e); + + return false; + } + } + + public async Task UpdateTaskOutputArtifactsAsync(string workflowInstanceId, string taskId, Dictionary outputArtifacts) + { + Guard.Against.NullOrWhiteSpace(workflowInstanceId, nameof(workflowInstanceId)); + Guard.Against.NullOrWhiteSpace(taskId, nameof(taskId)); + Guard.Against.Null(outputArtifacts, nameof(outputArtifacts)); + + try + { + await _workflowInstanceCollection.FindOneAndUpdateAsync( + i => i.Id == workflowInstanceId && i.Tasks.Any(t => t.TaskId == taskId), + Builders.Update.Set(w => w.Tasks[-1].OutputArtifacts, outputArtifacts)); + + return true; + } + catch (Exception e) + { + _logger.DbCallFailed(nameof(UpdateTaskOutputArtifactsAsync), e); + return false; } } @@ -114,6 +141,27 @@ public async Task UpdateWorkflowInstanceStatusAsync(string workflowInstanc } } + public async Task GetTaskByIdAsync(string workflowInstanceId, string taskId) + { + Guard.Against.NullOrWhiteSpace(workflowInstanceId, nameof(workflowInstanceId)); + Guard.Against.NullOrWhiteSpace(taskId, nameof(taskId)); + + try + { + var workflowInstance = await _workflowInstanceCollection + .Find(x => x.Id == workflowInstanceId) + .FirstOrDefaultAsync(); + + return workflowInstance?.Tasks?.FirstOrDefault(t => t.TaskId == taskId); + } + catch (Exception e) + { + _logger.DbCallFailed(nameof(GetTaskByIdAsync), e); + + return null; + } + } + public async Task UpdateTasksAsync(string workflowInstanceId, List tasks) { Guard.Against.NullOrWhiteSpace(workflowInstanceId, nameof(workflowInstanceId)); diff --git a/src/Database/packages.lock.json b/src/Database/packages.lock.json index e216297bd..c05ec08f2 100644 --- a/src/Database/packages.lock.json +++ b/src/Database/packages.lock.json @@ -29,9 +29,9 @@ }, "Monai.Deploy.Messaging": { "type": "Direct", - "requested": "[0.1.0-rc0036, )", - "resolved": "0.1.0-rc0036", - "contentHash": "ONfPbkd0bsJGJHOPVn7U5z0FDNirYuvMswLauN8rxHNSat0GbhFDnObrY8iIuCFY02zfPfpoL9mywkksVwsUAA==", + "requested": "[0.1.0-rc0040, )", + "resolved": "0.1.0-rc0040", + "contentHash": "q5t6h24M+22JQqeBTD9wOOYYAvb1Vk29nkadcOJ05wT569jnF+MGmEherDn1moX45Ehj4TsCvQl3TD4HJlC1mA==", "dependencies": { "Ardalis.GuardClauses": "4.0.1", "Microsoft.Extensions.Configuration": "6.0.1", @@ -240,7 +240,7 @@ "dependencies": { "AWSSDK.SecurityToken": "3.7.1.141", "Microsoft.Extensions.Configuration": "6.0.1", - "Monai.Deploy.Messaging": "0.1.0-rc0036", + "Monai.Deploy.Messaging": "0.1.0-rc0040", "MongoDB.Bson": "2.15.0", "Newtonsoft.Json": "13.0.1" } diff --git a/src/Monai.Deploy.WorkflowManager.sln b/src/Monai.Deploy.WorkflowManager.sln index 765bc1cd5..fecf4a8f8 100644 --- a/src/Monai.Deploy.WorkflowManager.sln +++ b/src/Monai.Deploy.WorkflowManager.sln @@ -55,6 +55,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Monai.Deploy.WorkflowManage EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Monai.Deploy.WorkflowManager.ConditionsResolver.Tests", "..\tests\UnitTests\ConditionsResolver.Tests\Monai.Deploy.WorkflowManager.ConditionsResolver.Tests.csproj", "{918E4DE3-A7BF-4B7F-9B5A-5C36FEFA3C30}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Monai.Deploy.WorkflowManager.Common.Tests", "..\tests\UnitTests\WorkflowManager.Common.Tests\Monai.Deploy.WorkflowManager.Common.Tests.csproj", "{A44F975E-70CA-49D6-8513-78F2D5210EAF}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -157,6 +159,10 @@ Global {918E4DE3-A7BF-4B7F-9B5A-5C36FEFA3C30}.Debug|Any CPU.Build.0 = Debug|Any CPU {918E4DE3-A7BF-4B7F-9B5A-5C36FEFA3C30}.Release|Any CPU.ActiveCfg = Release|Any CPU {918E4DE3-A7BF-4B7F-9B5A-5C36FEFA3C30}.Release|Any CPU.Build.0 = Release|Any CPU + {A44F975E-70CA-49D6-8513-78F2D5210EAF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A44F975E-70CA-49D6-8513-78F2D5210EAF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A44F975E-70CA-49D6-8513-78F2D5210EAF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A44F975E-70CA-49D6-8513-78F2D5210EAF}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -176,6 +182,7 @@ Global {2DA40575-4748-4198-BE57-F4AF070DE8E3} = {71DDEE7B-E213-4E39-A7F4-4646783A27F7} {89D3D817-CCFE-4933-9089-D1283F2EA1B5} = {71DDEE7B-E213-4E39-A7F4-4646783A27F7} {918E4DE3-A7BF-4B7F-9B5A-5C36FEFA3C30} = {71DDEE7B-E213-4E39-A7F4-4646783A27F7} + {A44F975E-70CA-49D6-8513-78F2D5210EAF} = {71DDEE7B-E213-4E39-A7F4-4646783A27F7} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {DC0D56C8-D8CB-45CE-B528-F3DCF86D63ED} diff --git a/src/PayloadListener/Monai.Deploy.WorkflowManager.PayloadListener.csproj b/src/PayloadListener/Monai.Deploy.WorkflowManager.PayloadListener.csproj index 0d60d833e..c4b810337 100644 --- a/src/PayloadListener/Monai.Deploy.WorkflowManager.PayloadListener.csproj +++ b/src/PayloadListener/Monai.Deploy.WorkflowManager.PayloadListener.csproj @@ -11,11 +11,15 @@ SPDX-License-Identifier: Apache License 2.0 enable + + + + - - + + @@ -26,4 +30,9 @@ SPDX-License-Identifier: Apache License 2.0 + + true + true + + diff --git a/src/PayloadListener/packages.lock.json b/src/PayloadListener/packages.lock.json new file mode 100644 index 000000000..d3221817e --- /dev/null +++ b/src/PayloadListener/packages.lock.json @@ -0,0 +1,413 @@ +{ + "version": 1, + "dependencies": { + "net6.0": { + "Ardalis.GuardClauses": { + "type": "Direct", + "requested": "[4.0.1, )", + "resolved": "4.0.1", + "contentHash": "RemnImQf/BWR8oYqFpdw+hn+b4Q1w+pGujkRiSfjQhMPuiERwGn4UMmQv+6UDE4qbPlnIN+e3e40JkvBhzgfzg==", + "dependencies": { + "JetBrains.Annotations": "2021.3.0" + } + }, + "Microsoft.Extensions.Hosting.Abstractions": { + "type": "Direct", + "requested": "[6.0.0, )", + "resolved": "6.0.0", + "contentHash": "GcT5l2CYXL6Sa27KCSh0TixsRfADUgth+ojQSD5EkzisZxmGFh7CwzkcYuGwvmXLjr27uWRNrJ2vuuEjMhU05Q==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.FileProviders.Abstractions": "6.0.0" + } + }, + "Monai.Deploy.Messaging": { + "type": "Direct", + "requested": "[0.1.0-rc0040, )", + "resolved": "0.1.0-rc0040", + "contentHash": "q5t6h24M+22JQqeBTD9wOOYYAvb1Vk29nkadcOJ05wT569jnF+MGmEherDn1moX45Ehj4TsCvQl3TD4HJlC1mA==", + "dependencies": { + "Ardalis.GuardClauses": "4.0.1", + "Microsoft.Extensions.Configuration": "6.0.1", + "Microsoft.Extensions.Logging": "6.0.0", + "Newtonsoft.Json": "13.0.1", + "RabbitMQ.Client": "6.2.4", + "System.ComponentModel.Annotations": "5.0.0" + } + }, + "Monai.Deploy.Storage": { + "type": "Direct", + "requested": "[0.1.0-rc0021, )", + "resolved": "0.1.0-rc0021", + "contentHash": "jealmI38hO1f8iTGt9ACYUx5sIyUnrf5tMVA7DNekzJ9mufhTMpNgukCqmkvYpXeljaPBp6yWn7NzfjiS1fnhw==", + "dependencies": { + "AWSSDK.SecurityToken": "3.7.1.141", + "Ardalis.GuardClauses": "4.0.0", + "Microsoft.Extensions.Configuration": "6.0.1", + "Microsoft.Extensions.Logging": "6.0.0", + "Minio": "3.1.13", + "Newtonsoft.Json": "13.0.1" + } + }, + "AWSSDK.Core": { + "type": "Transitive", + "resolved": "3.7.10.10", + "contentHash": "eieuNNym2E63hAnUwZiEQtNCNT5knh2PTqcFVuFQsZpYCw22cGXLq9miC8TDENLFRHh2I1mkTBRiC9E8UlKQhw==" + }, + "AWSSDK.SecurityToken": { + "type": "Transitive", + "resolved": "3.7.1.141", + "contentHash": "paspGCeiN5Qjl6vJanqP1Ll8l3t9hqUmVWmeHMELXyvUgozvVzaSh+PTQ/gj6gmFQ64Us7aeCFSQlfBPZag+vQ==", + "dependencies": { + "AWSSDK.Core": "[3.7.10.10, 4.0.0)" + } + }, + "Crc32.NET": { + "type": "Transitive", + "resolved": "1.2.0", + "contentHash": "wNW/huzolu8MNKUnwCVKxjfAlCFpeI8AZVfF46iAWJ1+P6bTU1AZct7VAkDDEjgeeTJCVTkGZaD6jSd/fOiUkA==", + "dependencies": { + "NETStandard.Library": "2.0.0" + } + }, + "DnsClient": { + "type": "Transitive", + "resolved": "1.6.0", + "contentHash": "kVd3IxAs5EQZSKTQhMUaJmuOQ1n2VWKExatdqNszsxxKzTB+Toaxx3M1DsN3yKfHloMXPmt6fgfcOqRwKheoUQ==", + "dependencies": { + "Microsoft.Win32.Registry": "5.0.0" + } + }, + "JetBrains.Annotations": { + "type": "Transitive", + "resolved": "2021.3.0", + "contentHash": "Ddxjs5RRjf+c8m9m++WvhW1lz1bqNhsTjWvCLbQN9bvKbkJeR9MhtfNwKgBRRdG2yLHcXFr5Lf7fsvvkiPaDRg==" + }, + "Microsoft.CSharp": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "kaj6Wb4qoMuH3HySFJhxwQfe8R/sJsNJnANrvv8WdFPMoNbKY5htfNscv+LHCu5ipz+49m2e+WQXpLXr9XYemQ==" + }, + "Microsoft.Extensions.Configuration": { + "type": "Transitive", + "resolved": "6.0.1", + "contentHash": "BUyFU9t+HzlSE7ri4B+AQN2BgTgHv/uM82s5ZkgU1BApyzWzIl48nDsG5wR1t0pniNuuyTBzG3qCW8152/NtSw==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", + "Microsoft.Extensions.Primitives": "6.0.0" + } + }, + "Microsoft.Extensions.Configuration.Abstractions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "qWzV9o+ZRWq+pGm+1dF+R7qTgTYoXvbyowRoBxQJGfqTpqDun2eteerjRQhq5PQ/14S+lqto3Ft4gYaRyl4rdQ==", + "dependencies": { + "Microsoft.Extensions.Primitives": "6.0.0" + } + }, + "Microsoft.Extensions.DependencyInjection": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "k6PWQMuoBDGGHOQTtyois2u4AwyVcIwL2LaSLlTZQm2CYcJ1pxbt6jfAnpWmzENA/wfrYRI/X9DTLoUkE4AsLw==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "xlzi2IYREJH3/m6+lUrQlujzX8wDitm4QGnUu6kUXTQAWPuZY8i+ticFJbzfqaetLA6KR/rO6Ew/HuYD+bxifg==" + }, + "Microsoft.Extensions.FileProviders.Abstractions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "0pd4/fho0gC12rQswaGQxbU34jOS1TPS8lZPpkFCH68ppQjHNHYle9iRuHeev1LhrJ94YPvzcRd8UmIuFk23Qw==", + "dependencies": { + "Microsoft.Extensions.Primitives": "6.0.0" + } + }, + "Microsoft.Extensions.Logging": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "eIbyj40QDg1NDz0HBW0S5f3wrLVnKWnDJ/JtZ+yJDFnDj90VoPuoPmFkeaXrtu+0cKm5GRAwoDf+dBWXK0TUdg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection": "6.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.Logging.Abstractions": "6.0.0", + "Microsoft.Extensions.Options": "6.0.0", + "System.Diagnostics.DiagnosticSource": "6.0.0" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "Transitive", + "resolved": "6.0.1", + "contentHash": "dzB2Cgg+JmrouhjkcQGzSFjjvpwlq353i8oBQO2GWNjCXSzhbtBRUf28HSauWe7eib3wYOdb3tItdjRwAdwCSg==" + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "dzXN0+V1AyjOe2xcJ86Qbo233KHuLEY0njf/P2Kw8SfJU+d45HNS2ctJdnEnrWbM9Ye2eFgaC5Mj9otRMU6IsQ==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.Primitives": "6.0.0" + } + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "9+PnzmQFfEFNR9J2aDTfJGGupShHjOuGw4VUv+JB044biSHrnmCIMD+mJHmb2H7YryrfBEXDurxQ47gJZdCKNQ==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "Microsoft.NETCore.Platforms": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "VyPlqzH2wavqquTcYpkIIAQ6WdenuKoFN0BdYBbCWsclXacSOHNQn66Gt4z5NBqEYW0FAPm5rlvki9ZiCij5xQ==" + }, + "Microsoft.NETCore.Targets": { + "type": "Transitive", + "resolved": "1.1.0", + "contentHash": "aOZA3BWfz9RXjpzt0sRJJMjAscAUm3Hoa4UWAfceV9UTYxgwZ1lZt5nO2myFf+/jetYQo4uTP7zS8sJY67BBxg==" + }, + "Microsoft.Win32.Registry": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "dependencies": { + "System.Security.AccessControl": "5.0.0", + "System.Security.Principal.Windows": "5.0.0" + } + }, + "Minio": { + "type": "Transitive", + "resolved": "3.1.13", + "contentHash": "i4h+gXs7kFHn1QUP0ZcuZz4Xa/PgNzZo+QRmi0WbFG5TYec7ozhrV+/cq7zfJbLOgcB89XwieASLLiduGNuwdA==", + "dependencies": { + "Crc32.NET": "1.2.0", + "Microsoft.CSharp": "4.5.0", + "RestSharp": "106.10.1", + "System.Reactive.Linq": "4.0.0" + } + }, + "MongoDB.Bson": { + "type": "Transitive", + "resolved": "2.15.0", + "contentHash": "5uPSADVLydGJ4CbmjrsQn7v3NugSdx6htJ4cMNbo8youlfBa5v6YbEqztbipnl+dT1L/YBkJKZ1DDp6LWl0Hqw==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "5.0.0" + } + }, + "MongoDB.Driver": { + "type": "Transitive", + "resolved": "2.15.0", + "contentHash": "MaDExORt2JLBHFlG3dJZIdQlZsfK8YjfEbXJECqAU/Ez8BwbJwjHS0AjOBQZPj3U4UDU5pnGYiQn4P0dM9DnMw==", + "dependencies": { + "MongoDB.Bson": "2.15.0", + "MongoDB.Driver.Core": "2.15.0", + "MongoDB.Libmongocrypt": "1.3.0" + } + }, + "MongoDB.Driver.Core": { + "type": "Transitive", + "resolved": "2.15.0", + "contentHash": "oEfGhyxHDmH3KhRHPZqVEYSfLqFwOLrY5zMszbELKNxrgkJj82MNJ8AzGVOsayaxtrQqqcAGfyQK8Ecc1yoc0Q==", + "dependencies": { + "DnsClient": "1.6.0", + "MongoDB.Bson": "2.15.0", + "MongoDB.Libmongocrypt": "1.3.0", + "SharpCompress": "0.30.1", + "System.Buffers": "4.5.1" + } + }, + "MongoDB.Libmongocrypt": { + "type": "Transitive", + "resolved": "1.3.0", + "contentHash": "BAvqY/otOsmAGTsmzIYXRFysvpu8X7rYkDzEBX4iBlrF57SB+fD5px9sCPR6sETgfZ6k98qUNS6go5wtY9WOBA==" + }, + "NETStandard.Library": { + "type": "Transitive", + "resolved": "2.0.0", + "contentHash": "7jnbRU+L08FXKMxqUflxEXtVymWvNOrS8yHgu9s6EM8Anr6T/wIX4nZ08j/u3Asz+tCufp3YVwFSEvFTPYmBPA==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0" + } + }, + "Newtonsoft.Json": { + "type": "Transitive", + "resolved": "13.0.1", + "contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==" + }, + "RabbitMQ.Client": { + "type": "Transitive", + "resolved": "6.2.4", + "contentHash": "ttM7F+Ymb00EyQ25UCC44djr5GN/+cZNey2B3xD6JdJQQx7UcCtHdKBCE09zcmWuB+Afp07tFzetE14l/U8xQw==", + "dependencies": { + "System.Memory": "4.5.4", + "System.Threading.Channels": "4.7.1" + } + }, + "RestSharp": { + "type": "Transitive", + "resolved": "106.10.1", + "contentHash": "MhR4w8LbArVaRA5T/oAR3ZnF852QXtDXRBO84GsvyYzsm7FsjXHJ+JaM8x/nR/B/OCB1WbolGH7hqMT1GZpNJw==" + }, + "SharpCompress": { + "type": "Transitive", + "resolved": "0.30.1", + "contentHash": "XqD4TpfyYGa7QTPzaGlMVbcecKnXy4YmYLDWrU+JIj7IuRNl7DH2END+Ll7ekWIY8o3dAMWLFDE1xdhfIWD1nw==" + }, + "System.Buffers": { + "type": "Transitive", + "resolved": "4.5.1", + "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" + }, + "System.ComponentModel.Annotations": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "dMkqfy2el8A8/I76n2Hi1oBFEbG1SfxD2l5nhwXV3XjlnOmwxJlQbYpJH4W51odnU9sARCSAgv7S3CyAFMkpYg==" + }, + "System.Diagnostics.DiagnosticSource": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "frQDfv0rl209cKm1lnwTgFPzNigy2EKk1BS3uAvHvlBVKe5cymGyHO+Sj+NLv5VF/AhHsqPIUUwya5oV4CHMUw==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "System.IO.Abstractions": { + "type": "Transitive", + "resolved": "16.1.25", + "contentHash": "PZ3y8SgASqtIlJyhss47FGLwBMXMWvxL9MKk9dEqIZT/vrfQr5yhKwdJ/vNm2H8FqsWJoxu0P12nYGXFj0irkA==" + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.4", + "contentHash": "1MbJTHS1lZ4bS4FmsJjnuGJOu88ZzTT2rLvrhW7Ygic+pC0NWA+3hgAen0HRdsocuQXCkUTdFn9yHJJhsijDXw==" + }, + "System.Reactive": { + "type": "Transitive", + "resolved": "4.0.0", + "contentHash": "yaVAxT2Yl28p9+DupGwj2oPbIOYZUQimOQ+n7N7vML2oofIRj9BmcfD7DHc6FLrNah65EigOgG6U9VnwWEyz5g==", + "dependencies": { + "System.Runtime.InteropServices.WindowsRuntime": "4.3.0" + } + }, + "System.Reactive.Linq": { + "type": "Transitive", + "resolved": "4.0.0", + "contentHash": "LaYTtUoOtQJ7SBb1/tfM/kcIHy0dsA7E+31/739HyTyGg9uSGzP/fV4VFI3/Atg7MQiks+JbEAwcoTDJY0pJoA==", + "dependencies": { + "System.Reactive": "4.0.0" + } + }, + "System.Runtime": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "JufQi0vPQ0xGnAczR13AUFglDyVYt4Kqnz1AZaiKZ5+GICq0/1MH/mO/eAJHt/mHW1zjKBJd7kV26SrxddAhiw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0" + } + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" + }, + "System.Runtime.InteropServices.WindowsRuntime": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "J4GUi3xZQLUBasNwZnjrffN8i5wpHrBtZoLG+OhRyGo/+YunMRWWtwoMDlUAIdmX0uRfpHIBDSV6zyr3yf00TA==", + "dependencies": { + "System.Runtime": "4.3.0" + } + }, + "System.Security.AccessControl": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "5.0.0", + "System.Security.Principal.Windows": "5.0.0" + } + }, + "System.Security.Principal.Windows": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA==" + }, + "System.Threading.Channels": { + "type": "Transitive", + "resolved": "4.7.1", + "contentHash": "6akRtHK/wab3246t4p5v3HQrtQk8LboOt5T4dtpNgsp3zvDeM4/Gx8V12t0h+c/W9/enUrilk8n6EQqdQorZAA==" + }, + "monai.deploy.workflowmanager.common": { + "type": "Project", + "dependencies": { + "Ardalis.GuardClauses": "4.0.1", + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Monai.Deploy.WorkflowManager.Contracts": "1.0.0", + "Monai.Deploy.WorkflowManager.Database": "1.0.0" + } + }, + "monai.deploy.workflowmanager.configuration": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.Configuration": "6.0.1", + "Microsoft.Extensions.Options": "6.0.0", + "Monai.Deploy.Messaging": "0.1.0-rc0040", + "Monai.Deploy.Storage": "0.1.0-rc0021", + "Newtonsoft.Json": "13.0.1", + "System.IO.Abstractions": "16.1.25" + } + }, + "monai.deploy.workflowmanager.contracts": { + "type": "Project", + "dependencies": { + "AWSSDK.SecurityToken": "3.7.1.141", + "Microsoft.Extensions.Configuration": "6.0.1", + "Monai.Deploy.Messaging": "0.1.0-rc0040", + "MongoDB.Bson": "2.15.0", + "Newtonsoft.Json": "13.0.1" + } + }, + "monai.deploy.workflowmanager.database": { + "type": "Project", + "dependencies": { + "Ardalis.GuardClauses": "4.0.1", + "Microsoft.Extensions.Logging.Abstractions": "6.0.1", + "Microsoft.Extensions.Options": "6.0.0", + "Monai.Deploy.Messaging": "0.1.0-rc0040", + "Monai.Deploy.WorkflowManager.Contracts": "1.0.0", + "Monai.Deploy.WorkflowManager.Logging": "1.0.0", + "MongoDB.Bson": "2.15.0", + "MongoDB.Driver": "2.15.0" + } + }, + "monai.deploy.workflowmanager.logging": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "6.0.1" + } + }, + "monai.deploy.workloadmanager.workfowexecuter": { + "type": "Project", + "dependencies": { + "AWSSDK.SecurityToken": "3.7.1.141", + "Ardalis.GuardClauses": "4.0.1", + "Monai.Deploy.Messaging": "0.1.0-rc0040", + "Monai.Deploy.Storage": "0.1.0-rc0021", + "Monai.Deploy.WorkflowManager.Common": "1.0.0", + "Monai.Deploy.WorkflowManager.Configuration": "1.0.0", + "Monai.Deploy.WorkflowManager.Contracts": "1.0.0", + "Monai.Deploy.WorkflowManager.Database": "1.0.0", + "Monai.Deploy.WorkflowManager.Logging": "1.0.0", + "Newtonsoft.Json": "13.0.1" + } + } + } + } +} \ No newline at end of file diff --git a/src/TaskManager/API/Monai.Deploy.WorkflowManager.TaskManager.API.csproj b/src/TaskManager/API/Monai.Deploy.WorkflowManager.TaskManager.API.csproj index cd5dce2ab..04e907ed7 100644 --- a/src/TaskManager/API/Monai.Deploy.WorkflowManager.TaskManager.API.csproj +++ b/src/TaskManager/API/Monai.Deploy.WorkflowManager.TaskManager.API.csproj @@ -13,7 +13,6 @@ SPDX-License-Identifier: Apache License 2.0 true - true true ..\..\.sonarlint\project-monai_monai-deploy-workflow-managercsharp.ruleset @@ -23,8 +22,8 @@ SPDX-License-Identifier: Apache License 2.0 - - + + diff --git a/src/TaskManager/API/packages.lock.json b/src/TaskManager/API/packages.lock.json index c14d3c86d..3286f66c9 100644 --- a/src/TaskManager/API/packages.lock.json +++ b/src/TaskManager/API/packages.lock.json @@ -4,9 +4,9 @@ "net6.0": { "Monai.Deploy.Messaging": { "type": "Direct", - "requested": "[0.1.0-rc0036, )", - "resolved": "0.1.0-rc0036", - "contentHash": "ONfPbkd0bsJGJHOPVn7U5z0FDNirYuvMswLauN8rxHNSat0GbhFDnObrY8iIuCFY02zfPfpoL9mywkksVwsUAA==", + "requested": "[0.1.0-rc0040, )", + "resolved": "0.1.0-rc0040", + "contentHash": "q5t6h24M+22JQqeBTD9wOOYYAvb1Vk29nkadcOJ05wT569jnF+MGmEherDn1moX45Ehj4TsCvQl3TD4HJlC1mA==", "dependencies": { "Ardalis.GuardClauses": "4.0.1", "Microsoft.Extensions.Configuration": "6.0.1", @@ -18,9 +18,9 @@ }, "Monai.Deploy.Storage": { "type": "Direct", - "requested": "[0.1.0-rc0019, )", - "resolved": "0.1.0-rc0019", - "contentHash": "YAXgTE7vTqeMc+ZhprUg/muQ+JDVMaix1Ha1vLJTSSjfZWBOnxyK5SugS50uNlvByJcqHEXh+ygd9219wK1S+Q==", + "requested": "[0.1.0-rc0021, )", + "resolved": "0.1.0-rc0021", + "contentHash": "jealmI38hO1f8iTGt9ACYUx5sIyUnrf5tMVA7DNekzJ9mufhTMpNgukCqmkvYpXeljaPBp6yWn7NzfjiS1fnhw==", "dependencies": { "AWSSDK.SecurityToken": "3.7.1.141", "Ardalis.GuardClauses": "4.0.0", @@ -245,4 +245,4 @@ } } } -} +} \ No newline at end of file diff --git a/src/TaskManager/Monai.Deploy.WorkflowManager.TaskManager.csproj b/src/TaskManager/Monai.Deploy.WorkflowManager.TaskManager.csproj index 4748f0f08..368a7daaf 100644 --- a/src/TaskManager/Monai.Deploy.WorkflowManager.TaskManager.csproj +++ b/src/TaskManager/Monai.Deploy.WorkflowManager.TaskManager.csproj @@ -38,8 +38,8 @@ SPDX-License-Identifier: Apache License 2.0 - - + + @@ -50,7 +50,6 @@ SPDX-License-Identifier: Apache License 2.0 true - true true ..\.sonarlint\project-monai_monai-deploy-workflow-managercsharp.ruleset diff --git a/src/TaskManager/Plug-ins/Argo/Monai.Deploy.WorkflowManager.TaskManager.Argo.csproj b/src/TaskManager/Plug-ins/Argo/Monai.Deploy.WorkflowManager.TaskManager.Argo.csproj index 308b47a3e..5f8c60fd0 100644 --- a/src/TaskManager/Plug-ins/Argo/Monai.Deploy.WorkflowManager.TaskManager.Argo.csproj +++ b/src/TaskManager/Plug-ins/Argo/Monai.Deploy.WorkflowManager.TaskManager.Argo.csproj @@ -22,8 +22,8 @@ SPDX-License-Identifier: Apache License 2.0 all runtime; build; native; contentfiles; analyzers; buildtransitive - - + + @@ -34,7 +34,6 @@ SPDX-License-Identifier: Apache License 2.0 true - true true ..\..\..\.sonarlint\project-monai_monai-deploy-workflow-managercsharp.ruleset diff --git a/src/TaskManager/Plug-ins/Argo/packages.lock.json b/src/TaskManager/Plug-ins/Argo/packages.lock.json index 39fef6fb9..348b62522 100644 --- a/src/TaskManager/Plug-ins/Argo/packages.lock.json +++ b/src/TaskManager/Plug-ins/Argo/packages.lock.json @@ -34,9 +34,9 @@ }, "Monai.Deploy.Messaging": { "type": "Direct", - "requested": "[0.1.0-rc0036, )", - "resolved": "0.1.0-rc0036", - "contentHash": "ONfPbkd0bsJGJHOPVn7U5z0FDNirYuvMswLauN8rxHNSat0GbhFDnObrY8iIuCFY02zfPfpoL9mywkksVwsUAA==", + "requested": "[0.1.0-rc0040, )", + "resolved": "0.1.0-rc0040", + "contentHash": "q5t6h24M+22JQqeBTD9wOOYYAvb1Vk29nkadcOJ05wT569jnF+MGmEherDn1moX45Ehj4TsCvQl3TD4HJlC1mA==", "dependencies": { "Ardalis.GuardClauses": "4.0.1", "Microsoft.Extensions.Configuration": "6.0.1", @@ -48,9 +48,9 @@ }, "Monai.Deploy.Storage": { "type": "Direct", - "requested": "[0.1.0-rc0019, )", - "resolved": "0.1.0-rc0019", - "contentHash": "YAXgTE7vTqeMc+ZhprUg/muQ+JDVMaix1Ha1vLJTSSjfZWBOnxyK5SugS50uNlvByJcqHEXh+ygd9219wK1S+Q==", + "requested": "[0.1.0-rc0021, )", + "resolved": "0.1.0-rc0021", + "contentHash": "jealmI38hO1f8iTGt9ACYUx5sIyUnrf5tMVA7DNekzJ9mufhTMpNgukCqmkvYpXeljaPBp6yWn7NzfjiS1fnhw==", "dependencies": { "AWSSDK.SecurityToken": "3.7.1.141", "Ardalis.GuardClauses": "4.0.0", @@ -601,7 +601,7 @@ "dependencies": { "AWSSDK.SecurityToken": "3.7.1.141", "Microsoft.Extensions.Configuration": "6.0.1", - "Monai.Deploy.Messaging": "0.1.0-rc0036", + "Monai.Deploy.Messaging": "0.1.0-rc0040", "MongoDB.Bson": "2.15.0", "Newtonsoft.Json": "13.0.1" } @@ -612,7 +612,7 @@ "Ardalis.GuardClauses": "4.0.1", "Microsoft.Extensions.Logging.Abstractions": "6.0.1", "Microsoft.Extensions.Options": "6.0.0", - "Monai.Deploy.Messaging": "0.1.0-rc0036", + "Monai.Deploy.Messaging": "0.1.0-rc0040", "Monai.Deploy.WorkflowManager.Contracts": "1.0.0", "Monai.Deploy.WorkflowManager.Logging": "1.0.0", "MongoDB.Bson": "2.15.0", @@ -628,10 +628,10 @@ "monai.deploy.workflowmanager.taskmanager.api": { "type": "Project", "dependencies": { - "Monai.Deploy.Messaging": "0.1.0-rc0036", - "Monai.Deploy.Storage": "0.1.0-rc0019" + "Monai.Deploy.Messaging": "0.1.0-rc0040", + "Monai.Deploy.Storage": "0.1.0-rc0021" } } } } -} +} \ No newline at end of file diff --git a/src/TaskManager/packages.lock.json b/src/TaskManager/packages.lock.json index 17077ed77..675bf516c 100644 --- a/src/TaskManager/packages.lock.json +++ b/src/TaskManager/packages.lock.json @@ -60,9 +60,9 @@ }, "Monai.Deploy.Messaging": { "type": "Direct", - "requested": "[0.1.0-rc0036, )", - "resolved": "0.1.0-rc0036", - "contentHash": "ONfPbkd0bsJGJHOPVn7U5z0FDNirYuvMswLauN8rxHNSat0GbhFDnObrY8iIuCFY02zfPfpoL9mywkksVwsUAA==", + "requested": "[0.1.0-rc0040, )", + "resolved": "0.1.0-rc0040", + "contentHash": "q5t6h24M+22JQqeBTD9wOOYYAvb1Vk29nkadcOJ05wT569jnF+MGmEherDn1moX45Ehj4TsCvQl3TD4HJlC1mA==", "dependencies": { "Ardalis.GuardClauses": "4.0.1", "Microsoft.Extensions.Configuration": "6.0.1", @@ -74,9 +74,9 @@ }, "Monai.Deploy.Storage": { "type": "Direct", - "requested": "[0.1.0-rc0019, )", - "resolved": "0.1.0-rc0019", - "contentHash": "YAXgTE7vTqeMc+ZhprUg/muQ+JDVMaix1Ha1vLJTSSjfZWBOnxyK5SugS50uNlvByJcqHEXh+ygd9219wK1S+Q==", + "requested": "[0.1.0-rc0021, )", + "resolved": "0.1.0-rc0021", + "contentHash": "jealmI38hO1f8iTGt9ACYUx5sIyUnrf5tMVA7DNekzJ9mufhTMpNgukCqmkvYpXeljaPBp6yWn7NzfjiS1fnhw==", "dependencies": { "AWSSDK.SecurityToken": "3.7.1.141", "Ardalis.GuardClauses": "4.0.0", @@ -562,8 +562,8 @@ "dependencies": { "Microsoft.Extensions.Configuration": "6.0.1", "Microsoft.Extensions.Options": "6.0.0", - "Monai.Deploy.Messaging": "0.1.0-rc0036", - "Monai.Deploy.Storage": "0.1.0-rc0019", + "Monai.Deploy.Messaging": "0.1.0-rc0040", + "Monai.Deploy.Storage": "0.1.0-rc0021", "Newtonsoft.Json": "13.0.1", "System.IO.Abstractions": "16.1.25" } @@ -573,7 +573,7 @@ "dependencies": { "AWSSDK.SecurityToken": "3.7.1.141", "Microsoft.Extensions.Configuration": "6.0.1", - "Monai.Deploy.Messaging": "0.1.0-rc0036", + "Monai.Deploy.Messaging": "0.1.0-rc0040", "MongoDB.Bson": "2.15.0", "Newtonsoft.Json": "13.0.1" } @@ -584,7 +584,7 @@ "Ardalis.GuardClauses": "4.0.1", "Microsoft.Extensions.Logging.Abstractions": "6.0.1", "Microsoft.Extensions.Options": "6.0.0", - "Monai.Deploy.Messaging": "0.1.0-rc0036", + "Monai.Deploy.Messaging": "0.1.0-rc0040", "Monai.Deploy.WorkflowManager.Contracts": "1.0.0", "Monai.Deploy.WorkflowManager.Logging": "1.0.0", "MongoDB.Bson": "2.15.0", @@ -600,10 +600,10 @@ "monai.deploy.workflowmanager.taskmanager.api": { "type": "Project", "dependencies": { - "Monai.Deploy.Messaging": "0.1.0-rc0036", - "Monai.Deploy.Storage": "0.1.0-rc0019" + "Monai.Deploy.Messaging": "0.1.0-rc0040", + "Monai.Deploy.Storage": "0.1.0-rc0021" } } } } -} +} \ No newline at end of file diff --git a/src/WorkflowExecuter/Common/ArtifactExtensions.cs b/src/WorkflowExecuter/Common/ArtifactExtensions.cs deleted file mode 100644 index 174daec2c..000000000 --- a/src/WorkflowExecuter/Common/ArtifactExtensions.cs +++ /dev/null @@ -1,18 +0,0 @@ -// SPDX-FileCopyrightText: © 2021-2022 MONAI Consortium -// SPDX-License-Identifier: Apache License 2.0 - -using Ardalis.GuardClauses; -using Monai.Deploy.WorkflowManager.Contracts.Models; - -namespace Monai.Deploy.WorkloadManager.WorkfowExecuter.Common -{ - public static class ArtifactExtensions - { - public static Dictionary ToDictionary(this Artifact[] artifacts) - { - Guard.Against.NullOrEmpty(artifacts, nameof(artifacts)); - - return artifacts.ToDictionary(a => a.Name, a => a.Value); - } - } -} diff --git a/src/WorkflowExecuter/Common/ArtifactMapper.cs b/src/WorkflowExecuter/Common/ArtifactMapper.cs new file mode 100644 index 000000000..eb3db4db2 --- /dev/null +++ b/src/WorkflowExecuter/Common/ArtifactMapper.cs @@ -0,0 +1,111 @@ +// SPDX-FileCopyrightText: © 2021-2022 MONAI Consortium +// SPDX-License-Identifier: Apache License 2.0 + +using Ardalis.GuardClauses; +using Monai.Deploy.Storage; +using Monai.Deploy.WorkflowManager.Contracts.Models; +using Monai.Deploy.WorkflowManager.Database.Interfaces; + +namespace Monai.Deploy.WorkloadManager.WorkfowExecuter.Common +{ + public class ArtifactMapper : IArtifactMapper + { + private readonly IWorkflowInstanceRepository _workflowInstanceRepository; + private readonly IStorageService _storageService; + + public ArtifactMapper( + IWorkflowInstanceRepository workflowInstanceRepository, + IStorageService storageService + ) + { + _workflowInstanceRepository = workflowInstanceRepository ?? throw new ArgumentNullException(nameof(workflowInstanceRepository)); + _storageService = storageService ?? throw new ArgumentNullException(nameof(storageService)); + } + + public async Task> ConvertArtifactVariablesToPath(Artifact[] artifacts, string payloadId, string workflowInstanceId, string bucketId) + { + Guard.Against.Null(artifacts); + Guard.Against.NullOrWhiteSpace(payloadId); + Guard.Against.NullOrWhiteSpace(workflowInstanceId); + + var artifactPathDictionary = new Dictionary(); + + foreach (var artifact in artifacts) + { + Guard.Against.NullOrWhiteSpace(artifact.Value); + Guard.Against.NullOrWhiteSpace(artifact.Name); + + if (!TrimArtifactVariable(artifact.Value, out var variableString)) + { + continue; + } + + var mappedArtifact = await ConvertVariableStringToPath(artifact, variableString, workflowInstanceId, payloadId, bucketId); + + if (!mappedArtifact.Equals(default(KeyValuePair))) + { + artifactPathDictionary.Add(mappedArtifact.Key, mappedArtifact.Value); + } + + if (artifact.Mandatory) + { + throw new FileNotFoundException($"Mandatory artifact was not found: {artifact.Name}, {artifact.Value}"); + } + } + + return artifactPathDictionary; + } + + private static bool TrimArtifactVariable(string valueString, out string variableString) + { + var variableStrings = valueString.Split(" "); + + if (variableStrings.Length < 2) + { + variableString = null; + + return false; + } + + variableString = variableStrings[1]; + + return true; + } + + private async Task> ConvertVariableStringToPath(Artifact artifact, string variableString, string workflowInstanceId, string payloadId, string bucketId) + { + if (variableString.StartsWith("context.input")) + { + return _storageService.VerifyObjectExists(bucketId, new KeyValuePair(artifact.Name, $"{payloadId}/dcm/")); + } + + if (variableString.StartsWith("context.executions")) + { + var variableWords = variableString.Split("."); + + var variableTaskId = variableWords[2]; + var variableLocation = variableWords[3]; + + var task = await _workflowInstanceRepository.GetTaskByIdAsync(workflowInstanceId, variableTaskId); + + if (variableLocation == "output_dir") + { + return _storageService.VerifyObjectExists(bucketId, new KeyValuePair(artifact.Name, task.OutputDirectory)); + } + + if (variableLocation == "artifacts") + { + var artifactName = variableWords[4]; + var outputArtifact = task.OutputArtifacts.FirstOrDefault(a => a.Key == artifactName); + + if (!outputArtifact.Equals(default(KeyValuePair))) + { + return _storageService.VerifyObjectExists(bucketId, new KeyValuePair(outputArtifact.Key, outputArtifact.Value)); + } + } + } + + return default; + } + } +} diff --git a/src/WorkflowExecuter/Common/IArtifactMapper.cs b/src/WorkflowExecuter/Common/IArtifactMapper.cs new file mode 100644 index 000000000..a957f36c3 --- /dev/null +++ b/src/WorkflowExecuter/Common/IArtifactMapper.cs @@ -0,0 +1,19 @@ +// SPDX-FileCopyrightText: © 2021-2022 MONAI Consortium +// SPDX-License-Identifier: Apache License 2.0 + +using Monai.Deploy.WorkflowManager.Contracts.Models; + +namespace Monai.Deploy.WorkloadManager.WorkfowExecuter.Common +{ + public interface IArtifactMapper + { + /// + /// Converts an array of artifacts to a dictionary of artifact path variables. + /// + /// Array of artifacts to convert. + /// Payload id to check against. + /// Workflow instance id to check against. + /// Bucket id used to verify. + Task> ConvertArtifactVariablesToPath(Artifact[] artifacts, string payloadId, string workflowInstanceId, string bucketId); + } +} diff --git a/src/WorkflowExecuter/Monai.Deploy.WorkloadManager.WorkfowExecuter.csproj b/src/WorkflowExecuter/Monai.Deploy.WorkloadManager.WorkfowExecuter.csproj index 769d367fa..365a3c77d 100644 --- a/src/WorkflowExecuter/Monai.Deploy.WorkloadManager.WorkfowExecuter.csproj +++ b/src/WorkflowExecuter/Monai.Deploy.WorkloadManager.WorkfowExecuter.csproj @@ -2,23 +2,34 @@ net6.0 + ..\.sonarlint\project-monai_monai-deploy-workflow-managercsharp.ruleset enable enable + + + + - - + + + + + true + true + + diff --git a/src/WorkflowExecuter/Services/WorkflowExecuterService.cs b/src/WorkflowExecuter/Services/WorkflowExecuterService.cs index a2619bd7b..6b4a6b628 100644 --- a/src/WorkflowExecuter/Services/WorkflowExecuterService.cs +++ b/src/WorkflowExecuter/Services/WorkflowExecuterService.cs @@ -7,7 +7,9 @@ using Monai.Deploy.Messaging; using Monai.Deploy.Messaging.Events; using Monai.Deploy.Messaging.Messages; +using Monai.Deploy.Storage; using Monai.Deploy.Storage.Configuration; +using Monai.Deploy.WorkflowManager.Common.Extensions; using Monai.Deploy.WorkflowManager.Configuration; using Monai.Deploy.WorkflowManager.Contracts.Models; using Monai.Deploy.WorkflowManager.Database.Interfaces; @@ -23,6 +25,8 @@ public class WorkflowExecuterService : IWorkflowExecuterService private readonly IWorkflowRepository _workflowRepository; private readonly IWorkflowInstanceRepository _workflowInstanceRepository; private readonly IMessageBrokerPublisherService _messageBrokerPublisherService; + private readonly IArtifactMapper _artifactMapper; + private readonly IStorageService _storageService; private readonly StorageServiceConfiguration _storageConfiguration; private string TaskDispatchRoutingKey { get; } @@ -33,7 +37,9 @@ public WorkflowExecuterService( IOptions storageConfiguration, IWorkflowRepository workflowRepository, IWorkflowInstanceRepository workflowInstanceRepository, - IMessageBrokerPublisherService messageBrokerPublisherService) + IMessageBrokerPublisherService messageBrokerPublisherService, + IArtifactMapper artifactMapper, + IStorageService storageService) { if (configuration is null) { @@ -53,6 +59,8 @@ public WorkflowExecuterService( _workflowRepository = workflowRepository ?? throw new ArgumentNullException(nameof(workflowRepository)); _workflowInstanceRepository = workflowInstanceRepository ?? throw new ArgumentNullException(nameof(workflowInstanceRepository)); _messageBrokerPublisherService = messageBrokerPublisherService ?? throw new ArgumentNullException(nameof(messageBrokerPublisherService)); + _artifactMapper = artifactMapper ?? throw new ArgumentNullException(nameof(artifactMapper)); + _storageService = storageService ?? throw new ArgumentNullException(nameof(storageService)); } public async Task ProcessPayload(WorkflowRequestEvent message) @@ -73,7 +81,7 @@ await _workflowRepository.GetWorkflowsByAeTitleAsync(message.CalledAeTitle) as L var workflowInstances = new List(); - workflows.ForEach((workflow) => workflowInstances.Add(CreateWorkFlowIntsance(message, workflow))); + workflows.ForEach(async (workflow) => workflowInstances.Add(await CreateWorkflowInstanceAsync(message, workflow))); var existingInstances = await _workflowInstanceRepository.GetByWorkflowsIdsAsync(workflowInstances.Select(w => w.WorkflowId).ToList()); workflowInstances.RemoveAll(i => existingInstances.ToList().Exists(e => e.WorkflowId == i.WorkflowId && e.PayloadId == i.PayloadId)); @@ -92,8 +100,18 @@ await _workflowRepository.GetWorkflowsByAeTitleAsync(message.CalledAeTitle) as L foreach (var workflowInstance in workflowInstances) { + if (workflowInstance.Status == Status.Failed) + { + continue; + } + var task = workflowInstance.Tasks.FirstOrDefault(); + if (task is null) + { + continue; + } + if (task.Status != TaskExecutionStatus.Created) { _logger.TaskPreviouslyDispatched(workflowInstance.PayloadId, task.TaskId); @@ -137,7 +155,9 @@ public async Task ProcessTaskUpdate(TaskUpdateEvent message) return false; } - if (message.Status != TaskExecutionStatus.Succeeded) + var previouslyFailed = workflowInstance.Tasks.Any(t => t.Status == TaskExecutionStatus.Failed) || workflowInstance.Status == Status.Failed; + + if (message.Status != TaskExecutionStatus.Succeeded || previouslyFailed) { await UpdateWorkflowInstanceStatus(workflowInstance, message.TaskId, message.Status); @@ -152,18 +172,31 @@ public async Task ProcessTaskUpdate(TaskUpdateEvent message) return false; } + var artifactDict = message.Outputs.ToArtifactDictionary(); + + var validOutputArtifacts = _storageService.VerifyObjectsExist(workflowInstance.BucketId, artifactDict); + + workflowInstance.Tasks?.ForEach(t => + { + if (t.TaskId == message.TaskId) + { + t.OutputArtifacts = validOutputArtifacts; + } + }); var currentTaskDestinations = workflow.Workflow?.Tasks?.SingleOrDefault(t => t.Id == message.TaskId)?.TaskDestinations; - var newTaskExecutions = HandleTaskDestinations(workflowInstance, workflow, currentTaskDestinations); + var newTaskExecutions = await HandleTaskDestinations(workflowInstance, workflow, currentTaskDestinations); if (!newTaskExecutions.Any()) { await UpdateWorkflowInstanceStatus(workflowInstance, message.TaskId, message.Status); + await _workflowInstanceRepository.UpdateTaskOutputArtifactsAsync(workflowInstance.Id, message.TaskId, validOutputArtifacts); + return await _workflowInstanceRepository.UpdateTaskStatusAsync(message.WorkflowInstanceId, message.TaskId, message.Status); } - workflowInstance.Tasks.AddRange(newTaskExecutions); + workflowInstance.Tasks?.AddRange(newTaskExecutions); if (!await _workflowInstanceRepository.UpdateTasksAsync(message.WorkflowInstanceId, workflowInstance.Tasks)) { @@ -196,24 +229,28 @@ private async Task UpdateWorkflowInstanceStatus(WorkflowInstance workflowI return false; } + if (workflowInstance.Status == Status.Failed) + { + return true; + } + var previousTasks = workflowInstance.Tasks.Where(t => t.TaskId != taskId); - if (!previousTasks.Any(t => t.Status != TaskExecutionStatus.Succeeded && t.Status != TaskExecutionStatus.Canceled) - && (currentTaskStatus == TaskExecutionStatus.Succeeded || currentTaskStatus == TaskExecutionStatus.Canceled)) + if (previousTasks.Any(t => t.Status == TaskExecutionStatus.Failed) || currentTaskStatus == TaskExecutionStatus.Failed) { - return await _workflowInstanceRepository.UpdateWorkflowInstanceStatusAsync(workflowInstance.Id, Status.Succeeded); + return await _workflowInstanceRepository.UpdateWorkflowInstanceStatusAsync(workflowInstance.Id, Status.Failed); } - if (!previousTasks.Any(t => t.Status != TaskExecutionStatus.Succeeded && t.Status != TaskExecutionStatus.Failed && t.Status != TaskExecutionStatus.Canceled) - && (currentTaskStatus == TaskExecutionStatus.Succeeded || currentTaskStatus == TaskExecutionStatus.Canceled || currentTaskStatus == TaskExecutionStatus.Failed)) + if (!previousTasks.Any(t => t.Status != TaskExecutionStatus.Succeeded && t.Status != TaskExecutionStatus.Canceled) + && (currentTaskStatus == TaskExecutionStatus.Succeeded || currentTaskStatus == TaskExecutionStatus.Canceled)) { - return await _workflowInstanceRepository.UpdateWorkflowInstanceStatusAsync(workflowInstance.Id, Status.Failed); + return await _workflowInstanceRepository.UpdateWorkflowInstanceStatusAsync(workflowInstance.Id, Status.Succeeded); } return true; } - private List HandleTaskDestinations(WorkflowInstance workflowInstance, WorkflowRevision workflow, TaskDestination[]? currentTaskDestinations) + private async Task> HandleTaskDestinations(WorkflowInstance workflowInstance, WorkflowRevision workflow, TaskDestination[]? currentTaskDestinations) { var newTaskExecutions = new List(); @@ -243,7 +280,7 @@ private List HandleTaskDestinations(WorkflowInstance workflowInst continue; } - newTaskExecutions.Add(CreateTaskExecution(newTask, workflowInstance.Id, workflowInstance.BucketId)); + newTaskExecutions.Add(await CreateTaskExecutionAsync(newTask, workflowInstance.Id, workflowInstance.BucketId, workflowInstance.PayloadId)); } return newTaskExecutions; @@ -259,7 +296,7 @@ private async Task DispatchTask(WorkflowInstance workflowInstance, TaskExe return await _workflowInstanceRepository.UpdateTaskStatusAsync(workflowInstance.Id, taskExec.TaskId, TaskExecutionStatus.Dispatched); } - private WorkflowInstance CreateWorkFlowIntsance(WorkflowRequestEvent message, WorkflowRevision workflow) + private async Task CreateWorkflowInstanceAsync(WorkflowRequestEvent message, WorkflowRevision workflow) { Guard.Against.Null(message, nameof(message)); Guard.Against.Null(workflow, nameof(workflow)); @@ -293,7 +330,14 @@ private WorkflowInstance CreateWorkFlowIntsance(WorkflowRequestEvent message, Wo // firstTask = template ?? firstTask; //} - tasks.Add(CreateTaskExecution(firstTask, workflowInstance.Id, message.Bucket)); + try + { + tasks.Add(await CreateTaskExecutionAsync(firstTask, workflowInstance.Id, message.Bucket, message.PayloadId.ToString())); + } + catch (FileNotFoundException) + { + workflowInstance.Status = Status.Failed; + } } workflowInstance.Tasks = tasks; @@ -301,13 +345,14 @@ private WorkflowInstance CreateWorkFlowIntsance(WorkflowRequestEvent message, Wo return workflowInstance; } - private TaskExecution CreateTaskExecution(TaskObject task, string workflowInstanceId, string bucketName) + private async Task CreateTaskExecutionAsync(TaskObject task, string workflowInstanceId, string bucketName, string payloadId) { Guard.Against.Null(task, nameof(task)); Guard.Against.NullOrWhiteSpace(task.Type, nameof(task.Type)); Guard.Against.NullOrWhiteSpace(task.Id, nameof(task.Id)); Guard.Against.NullOrWhiteSpace(workflowInstanceId, nameof(workflowInstanceId)); Guard.Against.NullOrWhiteSpace(bucketName, nameof(bucketName)); + Guard.Against.NullOrWhiteSpace(payloadId, nameof(payloadId)); var executionId = Guid.NewGuid().ToString(); @@ -318,8 +363,8 @@ private TaskExecution CreateTaskExecution(TaskObject task, string workflowInstan TaskPluginArguments = task.Args ?? new Dictionary { }, TaskId = task.Id, Status = TaskExecutionStatus.Created, - InputArtifacts = task.Artifacts?.Input?.ToDictionary() ?? new Dictionary { }, - OutputDirectory = $"{bucketName}/{workflowInstanceId}/{executionId}", + InputArtifacts = await _artifactMapper.ConvertArtifactVariablesToPath(task?.Artifacts?.Input ?? new Artifact[] { }, payloadId, workflowInstanceId, bucketName), + OutputDirectory = $"{payloadId}/workflows/{workflowInstanceId}/{executionId}/", Metadata = { } }; } diff --git a/src/WorkflowExecuter/packages.lock.json b/src/WorkflowExecuter/packages.lock.json new file mode 100644 index 000000000..f9aad89f4 --- /dev/null +++ b/src/WorkflowExecuter/packages.lock.json @@ -0,0 +1,381 @@ +{ + "version": 1, + "dependencies": { + "net6.0": { + "Ardalis.GuardClauses": { + "type": "Direct", + "requested": "[4.0.1, )", + "resolved": "4.0.1", + "contentHash": "RemnImQf/BWR8oYqFpdw+hn+b4Q1w+pGujkRiSfjQhMPuiERwGn4UMmQv+6UDE4qbPlnIN+e3e40JkvBhzgfzg==", + "dependencies": { + "JetBrains.Annotations": "2021.3.0" + } + }, + "AWSSDK.SecurityToken": { + "type": "Direct", + "requested": "[3.7.1.141, )", + "resolved": "3.7.1.141", + "contentHash": "paspGCeiN5Qjl6vJanqP1Ll8l3t9hqUmVWmeHMELXyvUgozvVzaSh+PTQ/gj6gmFQ64Us7aeCFSQlfBPZag+vQ==", + "dependencies": { + "AWSSDK.Core": "[3.7.10.10, 4.0.0)" + } + }, + "Monai.Deploy.Messaging": { + "type": "Direct", + "requested": "[0.1.0-rc0040, )", + "resolved": "0.1.0-rc0040", + "contentHash": "q5t6h24M+22JQqeBTD9wOOYYAvb1Vk29nkadcOJ05wT569jnF+MGmEherDn1moX45Ehj4TsCvQl3TD4HJlC1mA==", + "dependencies": { + "Ardalis.GuardClauses": "4.0.1", + "Microsoft.Extensions.Configuration": "6.0.1", + "Microsoft.Extensions.Logging": "6.0.0", + "Newtonsoft.Json": "13.0.1", + "RabbitMQ.Client": "6.2.4", + "System.ComponentModel.Annotations": "5.0.0" + } + }, + "Monai.Deploy.Storage": { + "type": "Direct", + "requested": "[0.1.0-rc0021, )", + "resolved": "0.1.0-rc0021", + "contentHash": "jealmI38hO1f8iTGt9ACYUx5sIyUnrf5tMVA7DNekzJ9mufhTMpNgukCqmkvYpXeljaPBp6yWn7NzfjiS1fnhw==", + "dependencies": { + "AWSSDK.SecurityToken": "3.7.1.141", + "Ardalis.GuardClauses": "4.0.0", + "Microsoft.Extensions.Configuration": "6.0.1", + "Microsoft.Extensions.Logging": "6.0.0", + "Minio": "3.1.13", + "Newtonsoft.Json": "13.0.1" + } + }, + "Newtonsoft.Json": { + "type": "Direct", + "requested": "[13.0.1, )", + "resolved": "13.0.1", + "contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==" + }, + "AWSSDK.Core": { + "type": "Transitive", + "resolved": "3.7.10.10", + "contentHash": "eieuNNym2E63hAnUwZiEQtNCNT5knh2PTqcFVuFQsZpYCw22cGXLq9miC8TDENLFRHh2I1mkTBRiC9E8UlKQhw==" + }, + "Crc32.NET": { + "type": "Transitive", + "resolved": "1.2.0", + "contentHash": "wNW/huzolu8MNKUnwCVKxjfAlCFpeI8AZVfF46iAWJ1+P6bTU1AZct7VAkDDEjgeeTJCVTkGZaD6jSd/fOiUkA==", + "dependencies": { + "NETStandard.Library": "2.0.0" + } + }, + "DnsClient": { + "type": "Transitive", + "resolved": "1.6.0", + "contentHash": "kVd3IxAs5EQZSKTQhMUaJmuOQ1n2VWKExatdqNszsxxKzTB+Toaxx3M1DsN3yKfHloMXPmt6fgfcOqRwKheoUQ==", + "dependencies": { + "Microsoft.Win32.Registry": "5.0.0" + } + }, + "JetBrains.Annotations": { + "type": "Transitive", + "resolved": "2021.3.0", + "contentHash": "Ddxjs5RRjf+c8m9m++WvhW1lz1bqNhsTjWvCLbQN9bvKbkJeR9MhtfNwKgBRRdG2yLHcXFr5Lf7fsvvkiPaDRg==" + }, + "Microsoft.CSharp": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "kaj6Wb4qoMuH3HySFJhxwQfe8R/sJsNJnANrvv8WdFPMoNbKY5htfNscv+LHCu5ipz+49m2e+WQXpLXr9XYemQ==" + }, + "Microsoft.Extensions.Configuration": { + "type": "Transitive", + "resolved": "6.0.1", + "contentHash": "BUyFU9t+HzlSE7ri4B+AQN2BgTgHv/uM82s5ZkgU1BApyzWzIl48nDsG5wR1t0pniNuuyTBzG3qCW8152/NtSw==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", + "Microsoft.Extensions.Primitives": "6.0.0" + } + }, + "Microsoft.Extensions.Configuration.Abstractions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "qWzV9o+ZRWq+pGm+1dF+R7qTgTYoXvbyowRoBxQJGfqTpqDun2eteerjRQhq5PQ/14S+lqto3Ft4gYaRyl4rdQ==", + "dependencies": { + "Microsoft.Extensions.Primitives": "6.0.0" + } + }, + "Microsoft.Extensions.DependencyInjection": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "k6PWQMuoBDGGHOQTtyois2u4AwyVcIwL2LaSLlTZQm2CYcJ1pxbt6jfAnpWmzENA/wfrYRI/X9DTLoUkE4AsLw==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "xlzi2IYREJH3/m6+lUrQlujzX8wDitm4QGnUu6kUXTQAWPuZY8i+ticFJbzfqaetLA6KR/rO6Ew/HuYD+bxifg==" + }, + "Microsoft.Extensions.Logging": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "eIbyj40QDg1NDz0HBW0S5f3wrLVnKWnDJ/JtZ+yJDFnDj90VoPuoPmFkeaXrtu+0cKm5GRAwoDf+dBWXK0TUdg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection": "6.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.Logging.Abstractions": "6.0.0", + "Microsoft.Extensions.Options": "6.0.0", + "System.Diagnostics.DiagnosticSource": "6.0.0" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "Transitive", + "resolved": "6.0.1", + "contentHash": "dzB2Cgg+JmrouhjkcQGzSFjjvpwlq353i8oBQO2GWNjCXSzhbtBRUf28HSauWe7eib3wYOdb3tItdjRwAdwCSg==" + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "dzXN0+V1AyjOe2xcJ86Qbo233KHuLEY0njf/P2Kw8SfJU+d45HNS2ctJdnEnrWbM9Ye2eFgaC5Mj9otRMU6IsQ==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.Primitives": "6.0.0" + } + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "9+PnzmQFfEFNR9J2aDTfJGGupShHjOuGw4VUv+JB044biSHrnmCIMD+mJHmb2H7YryrfBEXDurxQ47gJZdCKNQ==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "Microsoft.NETCore.Platforms": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "VyPlqzH2wavqquTcYpkIIAQ6WdenuKoFN0BdYBbCWsclXacSOHNQn66Gt4z5NBqEYW0FAPm5rlvki9ZiCij5xQ==" + }, + "Microsoft.NETCore.Targets": { + "type": "Transitive", + "resolved": "1.1.0", + "contentHash": "aOZA3BWfz9RXjpzt0sRJJMjAscAUm3Hoa4UWAfceV9UTYxgwZ1lZt5nO2myFf+/jetYQo4uTP7zS8sJY67BBxg==" + }, + "Microsoft.Win32.Registry": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "dependencies": { + "System.Security.AccessControl": "5.0.0", + "System.Security.Principal.Windows": "5.0.0" + } + }, + "Minio": { + "type": "Transitive", + "resolved": "3.1.13", + "contentHash": "i4h+gXs7kFHn1QUP0ZcuZz4Xa/PgNzZo+QRmi0WbFG5TYec7ozhrV+/cq7zfJbLOgcB89XwieASLLiduGNuwdA==", + "dependencies": { + "Crc32.NET": "1.2.0", + "Microsoft.CSharp": "4.5.0", + "RestSharp": "106.10.1", + "System.Reactive.Linq": "4.0.0" + } + }, + "MongoDB.Bson": { + "type": "Transitive", + "resolved": "2.15.0", + "contentHash": "5uPSADVLydGJ4CbmjrsQn7v3NugSdx6htJ4cMNbo8youlfBa5v6YbEqztbipnl+dT1L/YBkJKZ1DDp6LWl0Hqw==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "5.0.0" + } + }, + "MongoDB.Driver": { + "type": "Transitive", + "resolved": "2.15.0", + "contentHash": "MaDExORt2JLBHFlG3dJZIdQlZsfK8YjfEbXJECqAU/Ez8BwbJwjHS0AjOBQZPj3U4UDU5pnGYiQn4P0dM9DnMw==", + "dependencies": { + "MongoDB.Bson": "2.15.0", + "MongoDB.Driver.Core": "2.15.0", + "MongoDB.Libmongocrypt": "1.3.0" + } + }, + "MongoDB.Driver.Core": { + "type": "Transitive", + "resolved": "2.15.0", + "contentHash": "oEfGhyxHDmH3KhRHPZqVEYSfLqFwOLrY5zMszbELKNxrgkJj82MNJ8AzGVOsayaxtrQqqcAGfyQK8Ecc1yoc0Q==", + "dependencies": { + "DnsClient": "1.6.0", + "MongoDB.Bson": "2.15.0", + "MongoDB.Libmongocrypt": "1.3.0", + "SharpCompress": "0.30.1", + "System.Buffers": "4.5.1" + } + }, + "MongoDB.Libmongocrypt": { + "type": "Transitive", + "resolved": "1.3.0", + "contentHash": "BAvqY/otOsmAGTsmzIYXRFysvpu8X7rYkDzEBX4iBlrF57SB+fD5px9sCPR6sETgfZ6k98qUNS6go5wtY9WOBA==" + }, + "NETStandard.Library": { + "type": "Transitive", + "resolved": "2.0.0", + "contentHash": "7jnbRU+L08FXKMxqUflxEXtVymWvNOrS8yHgu9s6EM8Anr6T/wIX4nZ08j/u3Asz+tCufp3YVwFSEvFTPYmBPA==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0" + } + }, + "RabbitMQ.Client": { + "type": "Transitive", + "resolved": "6.2.4", + "contentHash": "ttM7F+Ymb00EyQ25UCC44djr5GN/+cZNey2B3xD6JdJQQx7UcCtHdKBCE09zcmWuB+Afp07tFzetE14l/U8xQw==", + "dependencies": { + "System.Memory": "4.5.4", + "System.Threading.Channels": "4.7.1" + } + }, + "RestSharp": { + "type": "Transitive", + "resolved": "106.10.1", + "contentHash": "MhR4w8LbArVaRA5T/oAR3ZnF852QXtDXRBO84GsvyYzsm7FsjXHJ+JaM8x/nR/B/OCB1WbolGH7hqMT1GZpNJw==" + }, + "SharpCompress": { + "type": "Transitive", + "resolved": "0.30.1", + "contentHash": "XqD4TpfyYGa7QTPzaGlMVbcecKnXy4YmYLDWrU+JIj7IuRNl7DH2END+Ll7ekWIY8o3dAMWLFDE1xdhfIWD1nw==" + }, + "System.Buffers": { + "type": "Transitive", + "resolved": "4.5.1", + "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" + }, + "System.ComponentModel.Annotations": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "dMkqfy2el8A8/I76n2Hi1oBFEbG1SfxD2l5nhwXV3XjlnOmwxJlQbYpJH4W51odnU9sARCSAgv7S3CyAFMkpYg==" + }, + "System.Diagnostics.DiagnosticSource": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "frQDfv0rl209cKm1lnwTgFPzNigy2EKk1BS3uAvHvlBVKe5cymGyHO+Sj+NLv5VF/AhHsqPIUUwya5oV4CHMUw==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "System.IO.Abstractions": { + "type": "Transitive", + "resolved": "16.1.25", + "contentHash": "PZ3y8SgASqtIlJyhss47FGLwBMXMWvxL9MKk9dEqIZT/vrfQr5yhKwdJ/vNm2H8FqsWJoxu0P12nYGXFj0irkA==" + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.4", + "contentHash": "1MbJTHS1lZ4bS4FmsJjnuGJOu88ZzTT2rLvrhW7Ygic+pC0NWA+3hgAen0HRdsocuQXCkUTdFn9yHJJhsijDXw==" + }, + "System.Reactive": { + "type": "Transitive", + "resolved": "4.0.0", + "contentHash": "yaVAxT2Yl28p9+DupGwj2oPbIOYZUQimOQ+n7N7vML2oofIRj9BmcfD7DHc6FLrNah65EigOgG6U9VnwWEyz5g==", + "dependencies": { + "System.Runtime.InteropServices.WindowsRuntime": "4.3.0" + } + }, + "System.Reactive.Linq": { + "type": "Transitive", + "resolved": "4.0.0", + "contentHash": "LaYTtUoOtQJ7SBb1/tfM/kcIHy0dsA7E+31/739HyTyGg9uSGzP/fV4VFI3/Atg7MQiks+JbEAwcoTDJY0pJoA==", + "dependencies": { + "System.Reactive": "4.0.0" + } + }, + "System.Runtime": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "JufQi0vPQ0xGnAczR13AUFglDyVYt4Kqnz1AZaiKZ5+GICq0/1MH/mO/eAJHt/mHW1zjKBJd7kV26SrxddAhiw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0" + } + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" + }, + "System.Runtime.InteropServices.WindowsRuntime": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "J4GUi3xZQLUBasNwZnjrffN8i5wpHrBtZoLG+OhRyGo/+YunMRWWtwoMDlUAIdmX0uRfpHIBDSV6zyr3yf00TA==", + "dependencies": { + "System.Runtime": "4.3.0" + } + }, + "System.Security.AccessControl": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "5.0.0", + "System.Security.Principal.Windows": "5.0.0" + } + }, + "System.Security.Principal.Windows": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA==" + }, + "System.Threading.Channels": { + "type": "Transitive", + "resolved": "4.7.1", + "contentHash": "6akRtHK/wab3246t4p5v3HQrtQk8LboOt5T4dtpNgsp3zvDeM4/Gx8V12t0h+c/W9/enUrilk8n6EQqdQorZAA==" + }, + "monai.deploy.workflowmanager.common": { + "type": "Project", + "dependencies": { + "Ardalis.GuardClauses": "4.0.1", + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Monai.Deploy.WorkflowManager.Contracts": "1.0.0", + "Monai.Deploy.WorkflowManager.Database": "1.0.0" + } + }, + "monai.deploy.workflowmanager.configuration": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.Configuration": "6.0.1", + "Microsoft.Extensions.Options": "6.0.0", + "Monai.Deploy.Messaging": "0.1.0-rc0040", + "Monai.Deploy.Storage": "0.1.0-rc0021", + "Newtonsoft.Json": "13.0.1", + "System.IO.Abstractions": "16.1.25" + } + }, + "monai.deploy.workflowmanager.contracts": { + "type": "Project", + "dependencies": { + "AWSSDK.SecurityToken": "3.7.1.141", + "Microsoft.Extensions.Configuration": "6.0.1", + "Monai.Deploy.Messaging": "0.1.0-rc0040", + "MongoDB.Bson": "2.15.0", + "Newtonsoft.Json": "13.0.1" + } + }, + "monai.deploy.workflowmanager.database": { + "type": "Project", + "dependencies": { + "Ardalis.GuardClauses": "4.0.1", + "Microsoft.Extensions.Logging.Abstractions": "6.0.1", + "Microsoft.Extensions.Options": "6.0.0", + "Monai.Deploy.Messaging": "0.1.0-rc0040", + "Monai.Deploy.WorkflowManager.Contracts": "1.0.0", + "Monai.Deploy.WorkflowManager.Logging": "1.0.0", + "MongoDB.Bson": "2.15.0", + "MongoDB.Driver": "2.15.0" + } + }, + "monai.deploy.workflowmanager.logging": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "6.0.1" + } + } + } + } +} \ No newline at end of file diff --git a/src/WorkflowManager/Monai.Deploy.WorkflowManager.csproj b/src/WorkflowManager/Monai.Deploy.WorkflowManager.csproj index 883e9f057..c95162514 100644 --- a/src/WorkflowManager/Monai.Deploy.WorkflowManager.csproj +++ b/src/WorkflowManager/Monai.Deploy.WorkflowManager.csproj @@ -8,7 +8,7 @@ SPDX-License-Identifier: Apache License 2.0 Exe net6.0 Monai.Deploy.WorkflowManager - ..\..\StyleCop.Analyzers.ruleset + ..\.sonarlint\project-monai_monai-deploy-workflow-managercsharp.ruleset false false @@ -34,7 +34,7 @@ SPDX-License-Identifier: Apache License 2.0 - + all diff --git a/src/WorkflowManager/Program.cs b/src/WorkflowManager/Program.cs index 9ff59e6a5..4988dfee7 100644 --- a/src/WorkflowManager/Program.cs +++ b/src/WorkflowManager/Program.cs @@ -27,6 +27,7 @@ using Monai.Deploy.WorkflowManager.PayloadListener.Validators; using Monai.Deploy.WorkflowManager.Services.DataRetentionService; using Monai.Deploy.WorkflowManager.Services.Http; +using Monai.Deploy.WorkloadManager.WorkfowExecuter.Common; using Monai.Deploy.WorkloadManager.WorkfowExecuter.Services; using MongoDB.Driver; @@ -126,6 +127,7 @@ internal static IHostBuilder CreateHostBuilder(string[] args) => services.AddSingleton(); services.AddTransient(); services.AddSingleton(); + services.AddSingleton(); services.AddSingleton(); diff --git a/src/WorkflowManager/packages.lock.json b/src/WorkflowManager/packages.lock.json index d9c2716f8..8add31a5a 100644 --- a/src/WorkflowManager/packages.lock.json +++ b/src/WorkflowManager/packages.lock.json @@ -138,9 +138,9 @@ }, "Monai.Deploy.Messaging": { "type": "Direct", - "requested": "[0.1.0-rc0036, )", - "resolved": "0.1.0-rc0036", - "contentHash": "ONfPbkd0bsJGJHOPVn7U5z0FDNirYuvMswLauN8rxHNSat0GbhFDnObrY8iIuCFY02zfPfpoL9mywkksVwsUAA==", + "requested": "[0.1.0-rc0040, )", + "resolved": "0.1.0-rc0040", + "contentHash": "q5t6h24M+22JQqeBTD9wOOYYAvb1Vk29nkadcOJ05wT569jnF+MGmEherDn1moX45Ehj4TsCvQl3TD4HJlC1mA==", "dependencies": { "Ardalis.GuardClauses": "4.0.1", "Microsoft.Extensions.Configuration": "6.0.1", @@ -490,8 +490,8 @@ }, "Monai.Deploy.Storage": { "type": "Transitive", - "resolved": "0.1.0-rc0019", - "contentHash": "YAXgTE7vTqeMc+ZhprUg/muQ+JDVMaix1Ha1vLJTSSjfZWBOnxyK5SugS50uNlvByJcqHEXh+ygd9219wK1S+Q==", + "resolved": "0.1.0-rc0021", + "contentHash": "jealmI38hO1f8iTGt9ACYUx5sIyUnrf5tMVA7DNekzJ9mufhTMpNgukCqmkvYpXeljaPBp6yWn7NzfjiS1fnhw==", "dependencies": { "AWSSDK.SecurityToken": "3.7.1.141", "Ardalis.GuardClauses": "4.0.0", @@ -721,8 +721,8 @@ "dependencies": { "Microsoft.Extensions.Configuration": "6.0.1", "Microsoft.Extensions.Options": "6.0.0", - "Monai.Deploy.Messaging": "0.1.0-rc0036", - "Monai.Deploy.Storage": "0.1.0-rc0019", + "Monai.Deploy.Messaging": "0.1.0-rc0040", + "Monai.Deploy.Storage": "0.1.0-rc0021", "Newtonsoft.Json": "13.0.1", "System.IO.Abstractions": "16.1.25" } @@ -732,7 +732,7 @@ "dependencies": { "AWSSDK.SecurityToken": "3.7.1.141", "Microsoft.Extensions.Configuration": "6.0.1", - "Monai.Deploy.Messaging": "0.1.0-rc0036", + "Monai.Deploy.Messaging": "0.1.0-rc0040", "MongoDB.Bson": "2.15.0", "Newtonsoft.Json": "13.0.1" } @@ -743,7 +743,7 @@ "Ardalis.GuardClauses": "4.0.1", "Microsoft.Extensions.Logging.Abstractions": "6.0.1", "Microsoft.Extensions.Options": "6.0.0", - "Monai.Deploy.Messaging": "0.1.0-rc0036", + "Monai.Deploy.Messaging": "0.1.0-rc0040", "Monai.Deploy.WorkflowManager.Contracts": "1.0.0", "Monai.Deploy.WorkflowManager.Logging": "1.0.0", "MongoDB.Bson": "2.15.0", @@ -761,8 +761,8 @@ "dependencies": { "Ardalis.GuardClauses": "4.0.1", "Microsoft.Extensions.Hosting.Abstractions": "6.0.0", - "Monai.Deploy.Messaging": "0.1.0-rc0036", - "Monai.Deploy.Storage": "0.1.0-rc0019", + "Monai.Deploy.Messaging": "0.1.0-rc0040", + "Monai.Deploy.Storage": "0.1.0-rc0021", "Monai.Deploy.WorkflowManager.Common": "1.0.0", "Monai.Deploy.WorkflowManager.Configuration": "1.0.0", "Monai.Deploy.WorkflowManager.Contracts": "1.0.0", @@ -775,8 +775,9 @@ "dependencies": { "AWSSDK.SecurityToken": "3.7.1.141", "Ardalis.GuardClauses": "4.0.1", - "Monai.Deploy.Messaging": "0.1.0-rc0036", - "Monai.Deploy.Storage": "0.1.0-rc0019", + "Monai.Deploy.Messaging": "0.1.0-rc0040", + "Monai.Deploy.Storage": "0.1.0-rc0021", + "Monai.Deploy.WorkflowManager.Common": "1.0.0", "Monai.Deploy.WorkflowManager.Configuration": "1.0.0", "Monai.Deploy.WorkflowManager.Contracts": "1.0.0", "Monai.Deploy.WorkflowManager.Database": "1.0.0", diff --git a/tests/IntegrationTests/WorkflowManager.IntegrationTests/TestData/WorkflowInstanceTestData.cs b/tests/IntegrationTests/WorkflowManager.IntegrationTests/TestData/WorkflowInstanceTestData.cs index bca83ed33..a087cc4ab 100644 --- a/tests/IntegrationTests/WorkflowManager.IntegrationTests/TestData/WorkflowInstanceTestData.cs +++ b/tests/IntegrationTests/WorkflowManager.IntegrationTests/TestData/WorkflowInstanceTestData.cs @@ -82,6 +82,7 @@ public static class WorkflowInstancesTestData WorkflowId = Helper.GetWorkflowByName("Task_Status_Update_Workflow").WorkflowRevision.WorkflowId, PayloadId = Guid.NewGuid().ToString(), StartTime = DateTime.Now, + BucketId = "bucket1", Status = Status.Created, InputMetaData = new Dictionary() { diff --git a/tests/IntegrationTests/WorkflowManager.IntegrationTests/docker-compose.yml b/tests/IntegrationTests/WorkflowManager.IntegrationTests/docker-compose.yml index 34cbe3a5d..8e6b4b43b 100644 --- a/tests/IntegrationTests/WorkflowManager.IntegrationTests/docker-compose.yml +++ b/tests/IntegrationTests/WorkflowManager.IntegrationTests/docker-compose.yml @@ -1,4 +1,21 @@ version: '3.9' + +# Settings and configurations that are common for all containers +x-minio-common: &minio-common + image: quay.io/minio/minio:RELEASE.2022-04-12T06-55-35Z + command: server --console-address ":9001" http://minio{1...4}/data{1...2} + expose: + - "9000" + - "9001" + environment: + MINIO_ROOT_USER: minioadmin + MINIO_ROOT_PASSWORD: minioadmin + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"] + interval: 30s + timeout: 20s + retries: 3 + services: rabbit1: image: "rabbitmq:3.8.18-management" @@ -23,6 +40,56 @@ services: - 27017:27017 volumes: - mongodb_data_container:/data/db + + minio1: + <<: *minio-common + hostname: minio1 + volumes: + - data1-1:/data1 + - data1-2:/data2 + + minio2: + <<: *minio-common + hostname: minio2 + volumes: + - data2-1:/data1 + - data2-2:/data2 + + minio3: + <<: *minio-common + hostname: minio3 + volumes: + - data3-1:/data1 + - data3-2:/data2 + + minio4: + <<: *minio-common + hostname: minio4 + volumes: + - data4-1:/data1 + - data4-2:/data2 + + nginx: + image: nginx:1.19.2-alpine + hostname: nginx + volumes: + - ./nginx.conf:/etc/nginx/nginx.conf:ro + ports: + - "9000:9000" + - "9001:9001" + depends_on: + - minio1 + - minio2 + - minio3 + - minio4 volumes: mongodb_data_container: + data1-1: + data1-2: + data2-1: + data2-2: + data3-1: + data3-2: + data4-1: + data4-2: diff --git a/tests/IntegrationTests/WorkflowManager.IntegrationTests/nginx.conf b/tests/IntegrationTests/WorkflowManager.IntegrationTests/nginx.conf new file mode 100644 index 000000000..cca82f6fe --- /dev/null +++ b/tests/IntegrationTests/WorkflowManager.IntegrationTests/nginx.conf @@ -0,0 +1,106 @@ +user nginx; +worker_processes auto; + +error_log /var/log/nginx/error.log warn; +pid /var/run/nginx.pid; + +events { + worker_connections 4096; +} + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + access_log /var/log/nginx/access.log main; + sendfile on; + keepalive_timeout 65; + + # include /etc/nginx/conf.d/*.conf; + + upstream minio { + server minio1:9000; + server minio2:9000; + server minio3:9000; + server minio4:9000; + } + + upstream console { + ip_hash; + server minio1:9001; + server minio2:9001; + server minio3:9001; + server minio4:9001; + } + + server { + listen 9000; + listen [::]:9000; + server_name localhost; + + # To allow special characters in headers + ignore_invalid_headers off; + # Allow any size file to be uploaded. + # Set to a value such as 1000m; to restrict file size to a specific value + client_max_body_size 0; + # To disable buffering + proxy_buffering off; + proxy_request_buffering off; + + location / { + proxy_set_header Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + + proxy_connect_timeout 300; + # Default is HTTP/1, keepalive is only enabled in HTTP/1.1 + proxy_http_version 1.1; + proxy_set_header Connection ""; + chunked_transfer_encoding off; + + proxy_pass http://minio; + } + } + + server { + listen 9001; + listen [::]:9001; + server_name localhost; + + # To allow special characters in headers + ignore_invalid_headers off; + # Allow any size file to be uploaded. + # Set to a value such as 1000m; to restrict file size to a specific value + client_max_body_size 0; + # To disable buffering + proxy_buffering off; + proxy_request_buffering off; + + location / { + proxy_set_header Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-NginX-Proxy true; + + # This is necessary to pass the correct IP to be hashed + real_ip_header X-Real-IP; + + proxy_connect_timeout 300; + + # To support websocket + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + + chunked_transfer_encoding off; + + proxy_pass http://console; + } + } +} diff --git a/tests/UnitTests/Configuration.Tests/Monai.Deploy.WorkflowManager.Configuration.Tests.csproj b/tests/UnitTests/Configuration.Tests/Monai.Deploy.WorkflowManager.Configuration.Tests.csproj index 5a4e186d1..eed9bac31 100644 --- a/tests/UnitTests/Configuration.Tests/Monai.Deploy.WorkflowManager.Configuration.Tests.csproj +++ b/tests/UnitTests/Configuration.Tests/Monai.Deploy.WorkflowManager.Configuration.Tests.csproj @@ -18,7 +18,7 @@ SPDX-License-Identifier: Apache License 2.0 runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/tests/UnitTests/PayloadListener.Tests/Monai.Deploy.WorkflowManager.PayloadListener.Tests.csproj b/tests/UnitTests/PayloadListener.Tests/Monai.Deploy.WorkflowManager.PayloadListener.Tests.csproj index fb692bef8..4aedd4ba3 100644 --- a/tests/UnitTests/PayloadListener.Tests/Monai.Deploy.WorkflowManager.PayloadListener.Tests.csproj +++ b/tests/UnitTests/PayloadListener.Tests/Monai.Deploy.WorkflowManager.PayloadListener.Tests.csproj @@ -18,7 +18,7 @@ SPDX-License-Identifier: Apache License 2.0 - + diff --git a/tests/UnitTests/WorkflowExecuter.Tests/Common/ArtifactMapperTests.cs b/tests/UnitTests/WorkflowExecuter.Tests/Common/ArtifactMapperTests.cs new file mode 100644 index 000000000..1cd643f7c --- /dev/null +++ b/tests/UnitTests/WorkflowExecuter.Tests/Common/ArtifactMapperTests.cs @@ -0,0 +1,264 @@ +// SPDX-FileCopyrightText: © 2021-2022 MONAI Consortium +// SPDX-License-Identifier: Apache License 2.0 + +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; +using FluentAssertions; +using Monai.Deploy.Messaging.Events; +using Monai.Deploy.Storage; +using Monai.Deploy.WorkflowManager.Contracts.Models; +using Monai.Deploy.WorkflowManager.Database.Interfaces; +using Monai.Deploy.WorkloadManager.Contracts.Models; +using Monai.Deploy.WorkloadManager.WorkfowExecuter.Common; +using Moq; +using Xunit; + +namespace Monai.Deploy.WorkflowManager.WorkflowExecuter.Tests.Common +{ + public class ArtifactMapperTests + { + private IArtifactMapper ArtifactMapper { get; set; } + + private readonly Mock _workflowInstanceRepository; + private readonly Mock _storageService; + + public ArtifactMapperTests() + { + _workflowInstanceRepository = new Mock(); + _storageService = new Mock(); + + ArtifactMapper = new ArtifactMapper(_workflowInstanceRepository.Object, _storageService.Object); + } + + [Fact] + public async Task ConvertArtifactVariablesToPath_MultipleArtifacts_ReturnsMappedPaths() + { + var artifacts = new Artifact[] + { + new Artifact + { + Name = "taskartifact", + Value = "{{ context.executions.image_type_detector.artifacts.dicom }}" + }, + new Artifact + { + Name = "dicomimage", + Value = "{{ context.input }}" + }, + new Artifact + { + Name = "outputtaskdir", + Value = "{{ context.executions.coffee.output_dir }}" + } + }; + + var payloadId = Guid.NewGuid().ToString(); + var workflowInstanceId = Guid.NewGuid().ToString(); + var executionId = Guid.NewGuid().ToString(); + + var expected = new Dictionary + { + { "dicom", $"{payloadId}/workflows/{workflowInstanceId}/{executionId}/" }, + { "dicomimage", $"{payloadId}/dcm/" }, + { "outputtaskdir", $"{payloadId}/workflows/{workflowInstanceId}/{executionId}/" } + }; + + var workflowInstance = new WorkflowInstance + { + Id = workflowInstanceId, + WorkflowId = Guid.NewGuid().ToString(), + PayloadId = payloadId, + Status = Status.Created, + BucketId = "bucket", + Tasks = new List + { + new TaskExecution + { + TaskId = "image_type_detector", + ExecutionId = executionId, + Status = TaskExecutionStatus.Dispatched, + OutputArtifacts = new Dictionary + { + { "dicom", $"{payloadId}/workflows/{workflowInstanceId}/{executionId}/" } + } + }, + new TaskExecution + { + TaskId = "coffee", + Status = TaskExecutionStatus.Created, + OutputDirectory = $"{payloadId}/workflows/{workflowInstanceId}/{executionId}/" + } + } + }; + + _workflowInstanceRepository.Setup(w => w.GetTaskByIdAsync(workflowInstance.Id, "image_type_detector")).ReturnsAsync(workflowInstance.Tasks[0]); + _workflowInstanceRepository.Setup(w => w.GetTaskByIdAsync(workflowInstance.Id, "coffee")).ReturnsAsync(workflowInstance.Tasks[1]); + + var value1 = new KeyValuePair("dicom", $"{payloadId}/workflows/{workflowInstanceId}/{executionId}/"); + var value2 = new KeyValuePair("dicomimage", $"{payloadId}/dcm/"); + var value3 = new KeyValuePair("outputtaskdir", $"{payloadId}/workflows/{workflowInstanceId}/{executionId}/"); + + _storageService.Setup(w => w.VerifyObjectExists(workflowInstance.BucketId, value1)).Returns(value1); + _storageService.Setup(w => w.VerifyObjectExists(workflowInstance.BucketId, value2)).Returns(value2); + _storageService.Setup(w => w.VerifyObjectExists(workflowInstance.BucketId, value3)).Returns(value3); + + var response = await ArtifactMapper.ConvertArtifactVariablesToPath(artifacts, workflowInstance.PayloadId, workflowInstance.Id, workflowInstance.BucketId); + + response.Should().BeEquivalentTo(expected); + } + + [Fact] + public async Task ConvertArtifactVariablesToPath_RequiredArtifact_ThrowsException() + { + var artifacts = new Artifact[] + { + new Artifact + { + Name = "taskartifact", + Value = "{{ context.executions.image_type_detector.artifacts.dicom }}", + Mandatory = true + }, + new Artifact + { + Name = "dicomimage", + Value = "{{ context.input }}", + Mandatory = true + }, + new Artifact + { + Name = "outputtaskdir", + Value = "{{ context.executions.coffee.output_dir }}", + Mandatory = true + } + }; + + var payloadId = Guid.NewGuid().ToString(); + var workflowInstanceId = Guid.NewGuid().ToString(); + var executionId = Guid.NewGuid().ToString(); + + var expected = new Dictionary + { + { "dicom", $"{payloadId}/workflows/{workflowInstanceId}/{executionId}/" }, + { "dicomimage", $"{payloadId}/dcm/" }, + { "outputtaskdir", $"{payloadId}/workflows/{workflowInstanceId}/{executionId}/" } + }; + + var workflowInstance = new WorkflowInstance + { + Id = workflowInstanceId, + WorkflowId = Guid.NewGuid().ToString(), + PayloadId = payloadId, + Status = Status.Created, + BucketId = "bucket", + Tasks = new List + { + new TaskExecution + { + TaskId = "image_type_detector", + ExecutionId = executionId, + Status = TaskExecutionStatus.Dispatched, + OutputArtifacts = new Dictionary + { + { "dicom", $"{payloadId}/workflows/{workflowInstanceId}/{executionId}/" } + } + }, + new TaskExecution + { + TaskId = "coffee", + Status = TaskExecutionStatus.Created, + OutputDirectory = $"{payloadId}/workflows/{workflowInstanceId}/{executionId}/" + } + } + }; + + _workflowInstanceRepository.Setup(w => w.GetTaskByIdAsync(workflowInstance.Id, "image_type_detector")).ReturnsAsync(workflowInstance.Tasks[0]); + _workflowInstanceRepository.Setup(w => w.GetTaskByIdAsync(workflowInstance.Id, "coffee")).ReturnsAsync(workflowInstance.Tasks[1]); + + var value1 = new KeyValuePair("dicom", $"{payloadId}/workflows/{workflowInstanceId}/{executionId}/"); + var value2 = new KeyValuePair("dicomimage", $"{payloadId}/dcm/"); + var value3 = new KeyValuePair("outputtaskdir", $"{payloadId}/workflows/{workflowInstanceId}/{executionId}/"); + + _storageService.Setup(w => w.VerifyObjectExists(workflowInstance.BucketId, value1)).Returns(new KeyValuePair()); + _storageService.Setup(w => w.VerifyObjectExists(workflowInstance.BucketId, value2)).Returns(value2); + _storageService.Setup(w => w.VerifyObjectExists(workflowInstance.BucketId, value3)).Returns(value3); + + await Assert.ThrowsAsync(() => ArtifactMapper.ConvertArtifactVariablesToPath(artifacts, workflowInstance.PayloadId, workflowInstance.Id, workflowInstance.BucketId)); + } + + [Fact] + public async Task ConvertArtifactVariablesToPath_MultipleInvalid_ReturnsEmptyDict() + { + var artifacts = new Artifact[] + { + new Artifact + { + Name = "taskartifact", + Value = "{{ test.not.an.artifact }}" + }, + new Artifact + { + Name = "dicomimage", + Value = "{{ context }}" + }, + new Artifact + { + Name = "outputtaskdir", + Value = "{{ sandwich.executions.coffee.output_dir }}" + }, + new Artifact + { + Name = "outputtaskdir4", + Value = "{{ sandwich.executions.coffee.artifacts.test }}" + }, + new Artifact + { + Name = "outputtaskdir2", + Value = "{{sandwich.executions.coffee.output_dir" + } + }; + + var payloadId = Guid.NewGuid().ToString(); + var workflowInstanceId = Guid.NewGuid().ToString(); + var executionId = Guid.NewGuid().ToString(); + + var expected = new Dictionary(); + + var workflowInstance = new WorkflowInstance + { + Id = workflowInstanceId, + WorkflowId = Guid.NewGuid().ToString(), + PayloadId = payloadId, + Status = Status.Created, + BucketId = "bucket", + Tasks = new List + { + new TaskExecution + { + TaskId = "image_type_detector", + ExecutionId = executionId, + Status = TaskExecutionStatus.Dispatched, + OutputArtifacts = new Dictionary + { + { "dicom", $"{payloadId}/workflows/{workflowInstanceId}/{executionId}" } + } + }, + new TaskExecution + { + TaskId = "coffee", + Status = TaskExecutionStatus.Created, + OutputDirectory = $"{payloadId}/workflows/{workflowInstanceId}/{executionId}" + } + } + }; + + _workflowInstanceRepository.Setup(w => w.GetTaskByIdAsync(workflowInstance.Id, "image_type_detector")).ReturnsAsync(workflowInstance.Tasks[0]); + _workflowInstanceRepository.Setup(w => w.GetTaskByIdAsync(workflowInstance.Id, "coffee")).ReturnsAsync(workflowInstance.Tasks[1]); + + var response = await ArtifactMapper.ConvertArtifactVariablesToPath(artifacts, workflowInstance.PayloadId, workflowInstance.Id, workflowInstance.BucketId); + + response.Should().BeEquivalentTo(expected); + } + } +} diff --git a/tests/UnitTests/WorkflowExecuter.Tests/Services/WorkflowExecuterServiceTests.cs b/tests/UnitTests/WorkflowExecuter.Tests/Services/WorkflowExecuterServiceTests.cs index d30cec919..b70a86abd 100644 --- a/tests/UnitTests/WorkflowExecuter.Tests/Services/WorkflowExecuterServiceTests.cs +++ b/tests/UnitTests/WorkflowExecuter.Tests/Services/WorkflowExecuterServiceTests.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.IO; using System.Threading.Tasks; using FluentAssertions; using Microsoft.Extensions.Logging; @@ -10,11 +11,14 @@ using Monai.Deploy.Messaging; using Monai.Deploy.Messaging.Events; using Monai.Deploy.Messaging.Messages; +using Monai.Deploy.Storage; using Monai.Deploy.Storage.Configuration; +using Monai.Deploy.WorkflowManager.Common.Extensions; using Monai.Deploy.WorkflowManager.Configuration; using Monai.Deploy.WorkflowManager.Contracts.Models; using Monai.Deploy.WorkflowManager.Database.Interfaces; using Monai.Deploy.WorkloadManager.Contracts.Models; +using Monai.Deploy.WorkloadManager.WorkfowExecuter.Common; using Monai.Deploy.WorkloadManager.WorkfowExecuter.Services; using Moq; using Xunit; @@ -26,22 +30,26 @@ public class WorkflowExecuterServiceTests private IWorkflowExecuterService WorkflowExecuterService { get; set; } private readonly Mock _workflowRepository; + private readonly Mock _artifactMapper; private readonly Mock> _logger; private readonly Mock _workflowInstanceRepository; private readonly Mock _messageBrokerPublisherService; + private readonly Mock _storageService; private readonly IOptions _configuration; private readonly IOptions _storageConfiguration; public WorkflowExecuterServiceTests() { _workflowRepository = new Mock(); + _artifactMapper = new Mock(); _logger = new Mock>(); _workflowInstanceRepository = new Mock(); _messageBrokerPublisherService = new Mock(); + _storageService = new Mock(); _configuration = Options.Create(new WorkflowManagerOptions() { Messaging = new MessageBrokerConfiguration { Topics = new MessageBrokerConfigurationKeys { TaskDispatchRequest = "md.task.dispatch" } } }); _storageConfiguration = Options.Create(new StorageServiceConfiguration()); - WorkflowExecuterService = new WorkflowExecuterService(_logger.Object, _configuration, _storageConfiguration, _workflowRepository.Object, _workflowInstanceRepository.Object, _messageBrokerPublisherService.Object); + WorkflowExecuterService = new WorkflowExecuterService(_logger.Object, _configuration, _storageConfiguration, _workflowRepository.Object, _workflowInstanceRepository.Object, _messageBrokerPublisherService.Object, _artifactMapper.Object, _storageService.Object); } [Fact] @@ -173,6 +181,7 @@ public async Task ProcessPayload_ValidWorkflowIdRequest_ReturnesTrue() _workflowInstanceRepository.Setup(w => w.CreateAsync(It.IsAny>())).ReturnsAsync(true); _workflowInstanceRepository.Setup(w => w.GetByWorkflowsIdsAsync(It.IsAny>())).ReturnsAsync(new List()); _workflowInstanceRepository.Setup(w => w.UpdateTaskStatusAsync(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(true); + _artifactMapper.Setup(a => a.ConvertArtifactVariablesToPath(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(new Dictionary()); var result = await WorkflowExecuterService.ProcessPayload(workflowRequest); @@ -181,6 +190,91 @@ public async Task ProcessPayload_ValidWorkflowIdRequest_ReturnesTrue() Assert.True(result); } + [Fact] + public async Task ProcessPayload_FileNotFound_FailsWorkflow() + { + var workflowId1 = Guid.NewGuid().ToString(); + var workflowId2 = Guid.NewGuid().ToString(); + var workflowRequest = new WorkflowRequestEvent + { + Bucket = "testbucket", + CalledAeTitle = "aetitle", + CallingAeTitle = "aetitle", + CorrelationId = Guid.NewGuid().ToString(), + Timestamp = DateTime.UtcNow, + Workflows = new List + { + workflowId1.ToString(), + workflowId2.ToString() + } + }; + + var workflows = new List + { + new WorkflowRevision + { + Id = Guid.NewGuid().ToString(), + WorkflowId = workflowId1, + Revision = 1, + Workflow = new Workflow + { + Name = "Workflowname1", + Description = "Workflowdesc1", + Version = "1", + InformaticsGateway = new InformaticsGateway + { + AeTitle = "aetitle" + }, + Tasks = new TaskObject[] + { + new TaskObject { + Id = Guid.NewGuid().ToString(), + Type = "type", + Description = "taskdesc" + } + } + } + }, + new WorkflowRevision + { + Id = Guid.NewGuid().ToString(), + WorkflowId = workflowId2, + Revision = 1, + Workflow = new Workflow + { + Name = "Workflowname2", + Description = "Workflowdesc2", + Version = "1", + InformaticsGateway = new InformaticsGateway + { + AeTitle = "aetitle" + }, + Tasks = new TaskObject[] + { + new TaskObject { + Id = Guid.NewGuid().ToString(), + Type = "type", + Description = "taskdesc" + } + } + } + } + }; + + _workflowRepository.Setup(w => w.GetByWorkflowsIdsAsync(new List { workflowId1.ToString(), workflowId2.ToString() })).ReturnsAsync(workflows); + + _workflowInstanceRepository.Setup(w => w.CreateAsync(It.IsAny>())).ReturnsAsync(true); + _workflowInstanceRepository.Setup(w => w.GetByWorkflowsIdsAsync(It.IsAny>())).ReturnsAsync(new List()); + _workflowInstanceRepository.Setup(w => w.UpdateTaskStatusAsync(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(true); + _artifactMapper.Setup(a => a.ConvertArtifactVariablesToPath(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())).ThrowsAsync(new FileNotFoundException()); + + var result = await WorkflowExecuterService.ProcessPayload(workflowRequest); + + _messageBrokerPublisherService.Verify(w => w.Publish(_configuration.Value.Messaging.Topics.TaskDispatchRequest, It.IsAny()), Times.Exactly(0)); + + Assert.True(result); + } + [Fact] public async Task ProcessPayload_WorkflowAlreadyStarted_TaskNotDispatched() { @@ -279,6 +373,7 @@ public async Task ProcessPayload_WorkflowAlreadyStarted_TaskNotDispatched() _workflowInstanceRepository.Setup(w => w.CreateAsync(It.IsAny>())).ReturnsAsync(true); _workflowInstanceRepository.Setup(w => w.GetByWorkflowsIdsAsync(It.IsAny>())).ReturnsAsync(workflowsInstance); _workflowInstanceRepository.Setup(w => w.UpdateTaskStatusAsync(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(true); + _artifactMapper.Setup(a => a.ConvertArtifactVariablesToPath(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(new Dictionary()); var result = await WorkflowExecuterService.ProcessPayload(workflowRequest); @@ -351,6 +446,7 @@ public async Task ProcessTaskUpdate_ValidTaskUpdateEvent_ReturnsTrue() _workflowInstanceRepository.Setup(w => w.UpdateTaskStatusAsync(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(true); _workflowInstanceRepository.Setup(w => w.GetByWorkflowInstanceIdAsync(workflowInstance.Id)).ReturnsAsync(workflowInstance); _workflowRepository.Setup(w => w.GetByWorkflowIdAsync(workflowInstance.WorkflowId)).ReturnsAsync(workflow); + _artifactMapper.Setup(a => a.ConvertArtifactVariablesToPath(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(new Dictionary()); var response = await WorkflowExecuterService.ProcessTaskUpdate(updateEvent); @@ -446,6 +542,7 @@ public async Task ProcessTaskUpdate_ValidTaskUpdateEventWithTaskDestinations_Ret _workflowInstanceRepository.Setup(w => w.GetByWorkflowInstanceIdAsync(workflowInstance.Id)).ReturnsAsync(workflowInstance); _workflowInstanceRepository.Setup(w => w.UpdateTasksAsync(workflowInstance.Id, It.IsAny>())).ReturnsAsync(true); _workflowRepository.Setup(w => w.GetByWorkflowIdAsync(workflowInstance.WorkflowId)).ReturnsAsync(workflow); + _artifactMapper.Setup(a => a.ConvertArtifactVariablesToPath(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(new Dictionary()); var response = await WorkflowExecuterService.ProcessTaskUpdate(updateEvent); @@ -645,6 +742,116 @@ public async Task ProcessTaskUpdate_ValidTaskUpdateEventOneTaskDestinationDispat response.Should().BeTrue(); } + [Fact] + public async Task ProcessTaskUpdate_ValidTaskUpdateEventWithOutputArtifacts_ReturnsTrue() + { + var workflowInstanceId = Guid.NewGuid().ToString(); + + var updateEvent = new TaskUpdateEvent + { + WorkflowInstanceId = workflowInstanceId, + TaskId = "pizza", + ExecutionId = Guid.NewGuid().ToString(), + Status = TaskExecutionStatus.Succeeded, + Reason = FailureReason.None, + Message = "This is a message", + Metadata = new Dictionary(), + CorrelationId = Guid.NewGuid().ToString(), + Outputs = new List + { + new Messaging.Common.Storage + { + Name = "artifactname", + RelativeRootPath = "path/to/artifact" + } + } + }; + + var workflowId = Guid.NewGuid().ToString(); + + var workflow = new WorkflowRevision + { + Id = Guid.NewGuid().ToString(), + WorkflowId = workflowId, + Revision = 1, + Workflow = new Workflow + { + Name = "Workflowname2", + Description = "Workflowdesc2", + Version = "1", + InformaticsGateway = new InformaticsGateway + { + AeTitle = "aetitle" + }, + Tasks = new TaskObject[] + { + new TaskObject { + Id = "pizza", + Type = "type", + Description = "taskdesc", + TaskDestinations = new TaskDestination[] + { + new TaskDestination + { + Name = "coffee" + }, + new TaskDestination + { + Name = "doughnuts" + } + } + }, + new TaskObject { + Id = "coffee", + Type = "type", + Description = "taskdesc" + }, + new TaskObject { + Id = "doughnuts", + Type = "type", + Description = "taskdesc" + } + } + } + }; + + var workflowInstance = new WorkflowInstance + { + Id = workflowInstanceId, + WorkflowId = workflowId, + PayloadId = Guid.NewGuid().ToString(), + Status = Status.Created, + BucketId = "bucket", + Tasks = new List + { + new TaskExecution + { + TaskId = "pizza", + Status = TaskExecutionStatus.Dispatched + }, + new TaskExecution + { + TaskId = "coffee", + Status = TaskExecutionStatus.Dispatched + } + } + }; + + var artifactDict = updateEvent.Outputs.ToArtifactDictionary(); + + _workflowInstanceRepository.Setup(w => w.UpdateTaskStatusAsync(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(true); + _workflowInstanceRepository.Setup(w => w.GetByWorkflowInstanceIdAsync(workflowInstance.Id)).ReturnsAsync(workflowInstance); + _workflowInstanceRepository.Setup(w => w.UpdateTasksAsync(workflowInstance.Id, It.IsAny>())).ReturnsAsync(true); + _workflowRepository.Setup(w => w.GetByWorkflowIdAsync(workflowInstance.WorkflowId)).ReturnsAsync(workflow); + _storageService.Setup(w => w.VerifyObjectsExist(workflowInstance.BucketId, artifactDict)).Returns(artifactDict); + + var response = await WorkflowExecuterService.ProcessTaskUpdate(updateEvent); + + _messageBrokerPublisherService.Verify(w => w.Publish(_configuration.Value.Messaging.Topics.TaskDispatchRequest, It.IsAny()), Times.Exactly(1)); + + response.Should().BeTrue(); + } + [Fact] public async Task ProcessTaskUpdate_ValidTaskUpdateEventWorkflowDoesNotExist_ReturnsTrue() { diff --git a/tests/UnitTests/WorkflowManager.Common.Tests/Monai.Deploy.WorkflowManager.Common.Tests.csproj b/tests/UnitTests/WorkflowManager.Common.Tests/Monai.Deploy.WorkflowManager.Common.Tests.csproj new file mode 100644 index 000000000..dd325396f --- /dev/null +++ b/tests/UnitTests/WorkflowManager.Common.Tests/Monai.Deploy.WorkflowManager.Common.Tests.csproj @@ -0,0 +1,28 @@ + + + + net6.0 + enable + enable + + false + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + diff --git a/tests/UnitTests/WorkflowManager.Common.Tests/StorageListExtensionsTests.cs b/tests/UnitTests/WorkflowManager.Common.Tests/StorageListExtensionsTests.cs new file mode 100644 index 000000000..f174a5781 --- /dev/null +++ b/tests/UnitTests/WorkflowManager.Common.Tests/StorageListExtensionsTests.cs @@ -0,0 +1,66 @@ +using Monai.Deploy.WorkflowManager.Common.Extensions; +using Xunit; + +namespace Monai.Deploy.WorkflowManager.Common.Tests +{ + public class StorageListExtensionsTests + { + [Fact] + public void ToArtifactDictionary_ValidStorageList_GeneratesDictionary() + { + var storageList = new List + { + new Messaging.Common.Storage + { + Name = "test", + RelativeRootPath = "payloadid/dcm" + }, + new Messaging.Common.Storage + { + Name = "test2", + RelativeRootPath = "payloadid/dcm" + } + }; + + var expected = new Dictionary + { + { "test", "payloadid/dcm" }, + { "test2", "payloadid/dcm" } + }; + + var artifactDict = storageList.ToArtifactDictionary(); + + Assert.Equal(expected, artifactDict); + } + + [Fact] + public void ToArtifactDictionary_MissingFields_ReturnsEmpty() + { + var storageList = new List + { + new Messaging.Common.Storage + { + Endpoint = "test" + } + }; + + var expected = new Dictionary(); + + var artifactDict = storageList.ToArtifactDictionary(); + + Assert.Equal(expected, artifactDict); + } + + [Fact] + public void ToArtifactDictionary_EmptyList_ReturnsEmpty() + { + var storageList = new List(); + + var expected = new Dictionary(); + + var artifactDict = storageList.ToArtifactDictionary(); + + Assert.Equal(expected, artifactDict); + } + } +} diff --git a/tests/UnitTests/WorkflowManager.Tests/packages.lock.json b/tests/UnitTests/WorkflowManager.Tests/packages.lock.json index a52d2781b..8c7ccb8c9 100644 --- a/tests/UnitTests/WorkflowManager.Tests/packages.lock.json +++ b/tests/UnitTests/WorkflowManager.Tests/packages.lock.json @@ -592,8 +592,8 @@ }, "Monai.Deploy.Messaging": { "type": "Transitive", - "resolved": "0.1.0-rc0036", - "contentHash": "ONfPbkd0bsJGJHOPVn7U5z0FDNirYuvMswLauN8rxHNSat0GbhFDnObrY8iIuCFY02zfPfpoL9mywkksVwsUAA==", + "resolved": "0.1.0-rc0040", + "contentHash": "q5t6h24M+22JQqeBTD9wOOYYAvb1Vk29nkadcOJ05wT569jnF+MGmEherDn1moX45Ehj4TsCvQl3TD4HJlC1mA==", "dependencies": { "Ardalis.GuardClauses": "4.0.1", "Microsoft.Extensions.Configuration": "6.0.1", @@ -605,8 +605,8 @@ }, "Monai.Deploy.Storage": { "type": "Transitive", - "resolved": "0.1.0-rc0019", - "contentHash": "YAXgTE7vTqeMc+ZhprUg/muQ+JDVMaix1Ha1vLJTSSjfZWBOnxyK5SugS50uNlvByJcqHEXh+ygd9219wK1S+Q==", + "resolved": "0.1.0-rc0021", + "contentHash": "jealmI38hO1f8iTGt9ACYUx5sIyUnrf5tMVA7DNekzJ9mufhTMpNgukCqmkvYpXeljaPBp6yWn7NzfjiS1fnhw==", "dependencies": { "AWSSDK.SecurityToken": "3.7.1.141", "Ardalis.GuardClauses": "4.0.0", @@ -1376,7 +1376,7 @@ "Microsoft.Extensions.Logging": "6.0.0", "Microsoft.Extensions.Logging.Console": "6.0.0", "Microsoft.Extensions.Options": "6.0.0", - "Monai.Deploy.Messaging": "0.1.0-rc0036", + "Monai.Deploy.Messaging": "0.1.0-rc0040", "Monai.Deploy.WorkflowManager.Common": "1.0.0", "Monai.Deploy.WorkflowManager.Configuration": "1.0.0", "Monai.Deploy.WorkflowManager.Contracts": "1.0.0", @@ -1401,8 +1401,8 @@ "dependencies": { "Microsoft.Extensions.Configuration": "6.0.1", "Microsoft.Extensions.Options": "6.0.0", - "Monai.Deploy.Messaging": "0.1.0-rc0036", - "Monai.Deploy.Storage": "0.1.0-rc0019", + "Monai.Deploy.Messaging": "0.1.0-rc0040", + "Monai.Deploy.Storage": "0.1.0-rc0021", "Newtonsoft.Json": "13.0.1", "System.IO.Abstractions": "16.1.25" } @@ -1412,7 +1412,7 @@ "dependencies": { "AWSSDK.SecurityToken": "3.7.1.141", "Microsoft.Extensions.Configuration": "6.0.1", - "Monai.Deploy.Messaging": "0.1.0-rc0036", + "Monai.Deploy.Messaging": "0.1.0-rc0040", "MongoDB.Bson": "2.15.0", "Newtonsoft.Json": "13.0.1" } @@ -1423,7 +1423,7 @@ "Ardalis.GuardClauses": "4.0.1", "Microsoft.Extensions.Logging.Abstractions": "6.0.1", "Microsoft.Extensions.Options": "6.0.0", - "Monai.Deploy.Messaging": "0.1.0-rc0036", + "Monai.Deploy.Messaging": "0.1.0-rc0040", "Monai.Deploy.WorkflowManager.Contracts": "1.0.0", "Monai.Deploy.WorkflowManager.Logging": "1.0.0", "MongoDB.Bson": "2.15.0", @@ -1441,8 +1441,8 @@ "dependencies": { "Ardalis.GuardClauses": "4.0.1", "Microsoft.Extensions.Hosting.Abstractions": "6.0.0", - "Monai.Deploy.Messaging": "0.1.0-rc0036", - "Monai.Deploy.Storage": "0.1.0-rc0019", + "Monai.Deploy.Messaging": "0.1.0-rc0040", + "Monai.Deploy.Storage": "0.1.0-rc0021", "Monai.Deploy.WorkflowManager.Common": "1.0.0", "Monai.Deploy.WorkflowManager.Configuration": "1.0.0", "Monai.Deploy.WorkflowManager.Contracts": "1.0.0", @@ -1455,8 +1455,9 @@ "dependencies": { "AWSSDK.SecurityToken": "3.7.1.141", "Ardalis.GuardClauses": "4.0.1", - "Monai.Deploy.Messaging": "0.1.0-rc0036", - "Monai.Deploy.Storage": "0.1.0-rc0019", + "Monai.Deploy.Messaging": "0.1.0-rc0040", + "Monai.Deploy.Storage": "0.1.0-rc0021", + "Monai.Deploy.WorkflowManager.Common": "1.0.0", "Monai.Deploy.WorkflowManager.Configuration": "1.0.0", "Monai.Deploy.WorkflowManager.Contracts": "1.0.0", "Monai.Deploy.WorkflowManager.Database": "1.0.0",