Skip to content

Commit 13a6e83

Browse files
authored
Merge pull request #391 from Project-MONAI/patch/0.3.18
Patch/0.3.18
2 parents 7b451ed + 6edaa54 commit 13a6e83

File tree

13 files changed

+164
-14
lines changed

13 files changed

+164
-14
lines changed

.github/.gitversion.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,4 @@ branches:
2929
ignore:
3030
sha: []
3131
merge-message-formats: {}
32-
next-version: 0.3.16
32+
next-version: 0.3.17

docs/api/rest/config.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,45 @@ curl --location --request GET 'http://localhost:5000/config/source/USEAST'
345345

346346
---
347347

348+
## GET /config/source/aetitle/{aeTitle}
349+
350+
Returns configurations for the specified calling (source) AET.
351+
352+
### Parameters
353+
354+
| Name | Type | Description |
355+
| ---- | ------ | ------------------------------------------ |
356+
| name | string | The aeTitle of an AE Title to be retrieved. |
357+
358+
### Responses
359+
360+
Response Content Type: JSON - [SourceApplicationEntity[]](xref:Monai.Deploy.InformaticsGateway.Api.SourceApplicationEntity).
361+
362+
| Code | Description |
363+
| ---- | --------------------------------------------------------------------------------------------------------------------------------------- |
364+
| 200 | AE Titles retrieved successfully. |
365+
| 404 | Named source not found. |
366+
| 500 | Server error. The response will be a [Problem details](https://datatracker.ietf.org/doc/html/rfc7807) object with server error details. |
367+
368+
### Example Request
369+
370+
```bash
371+
curl --location --request GET 'http://localhost:5000/config/source/aetitle/USEAST'
372+
```
373+
374+
### Example Response
375+
376+
```json
377+
[{
378+
"name": "USEAST",
379+
"aeTitle": "PACSUSEAST",
380+
"hostIp": "10.20.3.4"
381+
},
382+
{...}]
383+
```
384+
385+
---
386+
348387
## POST /config/source
349388

350389
Adds a new calling (source) AE Title to the Informatics Gateway to allow DICOM instances from the specified IP address and AE Title.

docs/index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
~ limitations under the License.
1515
-->
1616

17-
# MONAI Deploy Informatics Gateway <small>v0.0.0</small>
17+
# MONAI Deploy Informatics Gateway
1818

1919
![NVIDIA](./images/MONAI-logo_color.svg)
2020

src/Common/packages.lock.json

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,6 @@
3434
"resolved": "17.2.3",
3535
"contentHash": "VcozGeE4SxIo0cnXrDHhbrh/Gb8KQnZ3BvMelvh+iw0PrIKtuuA46U2Xm4e4pgnaWFgT4RdZfTpWl/WPRdw0WQ=="
3636
},
37-
"System.IO.Abstractions": {
38-
"type": "Direct",
39-
"requested": "[17.2.3, )",
40-
"resolved": "17.2.3",
41-
"contentHash": "VcozGeE4SxIo0cnXrDHhbrh/Gb8KQnZ3BvMelvh+iw0PrIKtuuA46U2Xm4e4pgnaWFgT4RdZfTpWl/WPRdw0WQ=="
42-
},
4337
"System.Threading.Tasks.Dataflow": {
4438
"type": "Direct",
4539
"requested": "[6.0.0, )",

src/Database/Api/Repositories/ISourceApplicationEntityRepository.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ public interface ISourceApplicationEntityRepository
2525

2626
Task<SourceApplicationEntity?> FindByNameAsync(string name, CancellationToken cancellationToken = default);
2727

28+
Task<SourceApplicationEntity[]?> FindByAETAsync(string name, CancellationToken cancellationToken = default);
29+
2830
Task<SourceApplicationEntity> AddAsync(SourceApplicationEntity item, CancellationToken cancellationToken = default);
2931

3032
Task<SourceApplicationEntity> UpdateAsync(SourceApplicationEntity entity, CancellationToken cancellationToken = default);

src/Database/EntityFramework/Repositories/SourceApplicationEntityRepository.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,16 @@ public async Task<bool> ContainsAsync(Expression<Func<SourceApplicationEntity, b
8787
}).ConfigureAwait(false);
8888
}
8989

90+
public async Task<SourceApplicationEntity[]?> FindByAETAsync(string aeTitle, CancellationToken cancellationToken = default)
91+
{
92+
Guard.Against.NullOrWhiteSpace(aeTitle);
93+
94+
return await _retryPolicy.ExecuteAsync(async () =>
95+
{
96+
return await _dataset.Where(p => p.AeTitle.Equals(aeTitle)).ToArrayAsync(cancellationToken).ConfigureAwait(false);
97+
}).ConfigureAwait(false);
98+
}
99+
90100
public async Task<SourceApplicationEntity> RemoveAsync(SourceApplicationEntity entity, CancellationToken cancellationToken = default)
91101
{
92102
Guard.Against.Null(entity);

src/Database/EntityFramework/Test/SourceApplicationEntityRepositoryTest.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,20 @@ public async Task GivenAAETitleName_WhenFindByNameAsyncIsCalled_ExpectItToReturn
108108
Assert.Null(actual);
109109
}
110110

111+
[Fact]
112+
public async Task GivenAAETitleName_WhenFindByAETAsyncIsCalled_ExpectItToReturnMatchingEntity()
113+
{
114+
var store = new SourceApplicationEntityRepository(_serviceScopeFactory.Object, _logger.Object, _options);
115+
116+
var actual = await store.FindByAETAsync("AET1").ConfigureAwait(false);
117+
Assert.NotNull(actual);
118+
Assert.Equal("AET1", actual.FirstOrDefault()!.AeTitle);
119+
Assert.Equal("AET1", actual.FirstOrDefault()!.Name);
120+
121+
actual = await store.FindByAETAsync("AET6").ConfigureAwait(false);
122+
Assert.Empty(actual);
123+
}
124+
111125
[Fact]
112126
public async Task GivenASourceApplicationEntity_WhenRemoveIsCalled_ExpectItToDeleted()
113127
{

src/Database/MongoDB/Integration.Test/SourceApplicationEntityRepositoryTest.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,20 @@ public async Task GivenAAETitleName_WhenFindByNameAsyncIsCalled_ExpectItToReturn
113113
Assert.Null(actual);
114114
}
115115

116+
[Fact]
117+
public async Task GivenAETitle_WhenFindByAETitleAsyncIsCalled_ExpectItToReturnMatchingEntity()
118+
{
119+
var store = new SourceApplicationEntityRepository(_serviceScopeFactory.Object, _logger.Object, _options, _databaseFixture.Options);
120+
121+
var actual = await store.FindByAETAsync("AET1").ConfigureAwait(false);
122+
Assert.NotNull(actual);
123+
Assert.Equal("AET1", actual.FirstOrDefault()!.AeTitle);
124+
Assert.Equal("AET1", actual.FirstOrDefault()!.Name);
125+
126+
actual = await store.FindByAETAsync("AET6").ConfigureAwait(false);
127+
Assert.Empty(actual);
128+
}
129+
116130
[Fact]
117131
public async Task GivenASourceApplicationEntity_WhenRemoveIsCalled_ExpectItToDeleted()
118132
{
@@ -158,5 +172,7 @@ public async Task GivenASourceApplicationEntity_WhenUpdatedIsCalled_ExpectItToSa
158172
Assert.NotNull(dbResult);
159173
Assert.Equal(expected.AeTitle, dbResult!.AeTitle);
160174
}
175+
176+
161177
}
162178
}

src/Database/MongoDB/Repositories/SourceApplicationEntityRepository.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,16 @@ public async Task<List<SourceApplicationEntity>> ToListAsync(CancellationToken c
9797
}).ConfigureAwait(false);
9898
}
9999

100+
public async Task<SourceApplicationEntity[]?> FindByAETAsync(string aeTitle, CancellationToken cancellationToken = default)
101+
{
102+
return await _retryPolicy.ExecuteAsync(async () =>
103+
{
104+
return (await _collection
105+
.Find(x => x.AeTitle == aeTitle)
106+
.ToListAsync(cancellationToken).ConfigureAwait(false)).ToArray();
107+
}).ConfigureAwait(false);
108+
}
109+
100110
public async Task<SourceApplicationEntity> AddAsync(SourceApplicationEntity item, CancellationToken cancellationToken = default)
101111
{
102112
Guard.Against.Null(item);

src/DicomWebClient/packages.lock.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1248,8 +1248,8 @@
12481248
"monai.deploy.informaticsgateway.client.common": {
12491249
"type": "Project",
12501250
"dependencies": {
1251-
"Ardalis.GuardClauses": "4.0.1",
1252-
"System.Text.Json": "6.0.7"
1251+
"Ardalis.GuardClauses": "[4.0.1, )",
1252+
"System.Text.Json": "[6.0.7, )"
12531253
}
12541254
}
12551255
}

src/InformaticsGateway/Services/Http/SourceAeTitleController.cs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,32 @@ public async Task<ActionResult<SourceApplicationEntity>> GetAeTitle(string name)
8888
}
8989
}
9090

91+
[HttpGet("/aetitle/{aeTitle}")]
92+
[Produces("application/json")]
93+
[ProducesResponseType(StatusCodes.Status200OK)]
94+
[ProducesResponseType(StatusCodes.Status404NotFound)]
95+
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
96+
[ActionName(nameof(GetAeTitleByAET))]
97+
public async Task<ActionResult<SourceApplicationEntity>> GetAeTitleByAET(string aeTitle)
98+
{
99+
try
100+
{
101+
var sourceApplicationEntity = await _repository.FindByAETAsync(aeTitle, HttpContext.RequestAborted).ConfigureAwait(false);
102+
103+
if (sourceApplicationEntity is null)
104+
{
105+
return NotFound();
106+
}
107+
108+
return Ok(sourceApplicationEntity);
109+
}
110+
catch (Exception ex)
111+
{
112+
_logger.ErrorListingSourceApplicationEntities(ex);
113+
return Problem(title: "Error querying DICOM sources.", statusCode: (int)System.Net.HttpStatusCode.InternalServerError, detail: ex.Message);
114+
}
115+
}
116+
91117
[HttpPost]
92118
[Consumes(MediaTypeNames.Application.Json)]
93119
[ProducesResponseType(StatusCodes.Status201Created)]

src/InformaticsGateway/Test/Services/Http/SourceAeTitleControllerTest.cs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,27 @@ public async Task GetAeTitle_ReturnsAMatch()
145145
_repository.Verify(p => p.FindByNameAsync(value, It.IsAny<CancellationToken>()), Times.Once());
146146
}
147147

148+
[RetryFact(5, 250, DisplayName = "GetAeTitle - Shall return matching object")]
149+
public async Task GetAeTitle_ViaAETitleReturnsAMatch()
150+
{
151+
var value = "AET";
152+
_repository.Setup(p => p.FindByAETAsync(It.IsAny<string>(), It.IsAny<CancellationToken>())).Returns(
153+
Task.FromResult(
154+
new SourceApplicationEntity[]{
155+
new SourceApplicationEntity
156+
{
157+
AeTitle = value,
158+
HostIp = "host",
159+
Name = $"{value}name",
160+
}}));
161+
162+
var result = await _controller.GetAeTitleByAET(value);
163+
var okObjectResult = result.Result as OkObjectResult;
164+
var response = okObjectResult.Value as SourceApplicationEntity[];
165+
Assert.Equal(value, response.FirstOrDefault().AeTitle);
166+
_repository.Verify(p => p.FindByAETAsync(value, It.IsAny<CancellationToken>()), Times.Once());
167+
}
168+
148169
[RetryFact(5, 250, DisplayName = "GetAeTitle - Shall return 404 if not found")]
149170
public async Task GetAeTitle_Returns404IfNotFound()
150171
{
@@ -175,6 +196,24 @@ public async Task GetAeTitle_ShallReturnProblemOnFailure()
175196
_repository.Verify(p => p.FindByNameAsync(value, It.IsAny<CancellationToken>()), Times.Once());
176197
}
177198

199+
[RetryFact(5, 250, DisplayName = "GetAeTitle from AETitle - Shall return problem on failure")]
200+
public async Task GetAeTitleViaAETitle_ShallReturnProblemOnFailure()
201+
{
202+
var value = "AET";
203+
_repository.Setup(p => p.FindByAETAsync(It.IsAny<string>(), It.IsAny<CancellationToken>())).Throws(new Exception("error"));
204+
205+
var result = await _controller.GetAeTitleByAET(value);
206+
207+
var objectResult = result.Result as ObjectResult;
208+
Assert.NotNull(objectResult);
209+
var problem = objectResult.Value as ProblemDetails;
210+
Assert.NotNull(problem);
211+
Assert.Equal("Error querying DICOM sources.", problem.Title);
212+
Assert.Equal("error", problem.Detail);
213+
Assert.Equal((int)HttpStatusCode.InternalServerError, problem.Status);
214+
_repository.Verify(p => p.FindByAETAsync(value, It.IsAny<CancellationToken>()), Times.Once());
215+
}
216+
178217
#endregion GetAeTitle
179218

180219
#region Create

src/InformaticsGateway/appsettings.Development.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,15 @@
1717
"messaging": {
1818
"publisherSettings": {
1919
"endpoint": "localhost",
20-
"username": "rabbitmq",
21-
"password": "rabbitmq",
20+
"username": "admin",
21+
"password": "admin",
2222
"virtualHost": "monaideploy",
2323
"exchange": "monaideploy"
2424
},
2525
"subscriberSettings": {
2626
"endpoint": "localhost",
27-
"username": "rabbitmq",
28-
"password": "rabbitmq",
27+
"username": "admin",
28+
"password": "admin",
2929
"virtualHost": "monaideploy",
3030
"exchange": "monaideploy",
3131
"exportRequestQueue": "export_tasks"

0 commit comments

Comments
 (0)