Skip to content

Commit c2d6ccb

Browse files
authored
Merge pull request #752 from Project-MONAI/AC-1979
AC-1979 added endpoint for get by AE Title
2 parents 0afb899 + 37401a7 commit c2d6ccb

File tree

9 files changed

+250
-2
lines changed

9 files changed

+250
-2
lines changed

src/WorkflowManager/Common/Interfaces/IWorkflowService.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,5 +50,21 @@ public interface IWorkflowService : IPaginatedApi<WorkflowRevision>
5050
/// </summary>
5151
/// <param name="workflow">Workflow to delete.</param>
5252
Task<DateTime> DeleteWorkflowAsync(WorkflowRevision workflow);
53+
54+
/// <summary>
55+
/// get all workflows with AeTitle
56+
/// </summary>
57+
/// <param name="aeTitle">the title to get</param>
58+
/// <param name="skip">skip x num of records</param>
59+
/// <param name="limit">limit to x number</param>
60+
/// <returns></returns>
61+
Task<IEnumerable<WorkflowRevision>> GetByAeTitleAsync(string aeTitle, int? skip = null, int? limit = null);
62+
63+
/// <summary>
64+
/// returns the number of workflows with this aetitle
65+
/// </summary>
66+
/// <param name="aeTitle">the title to count</param>
67+
/// <returns></returns>
68+
Task<long> GetCountByAeTitleAsync(string aeTitle);
5369
}
5470
}

src/WorkflowManager/Common/Services/WorkflowService.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,5 +88,13 @@ public Task<DateTime> DeleteWorkflowAsync(WorkflowRevision workflow)
8888

8989
public async Task<IList<WorkflowRevision>> GetAllAsync(int? skip = null, int? limit = null)
9090
=> await _workflowRepository.GetAllAsync(skip, limit);
91+
92+
93+
public async Task<IEnumerable<WorkflowRevision>> GetByAeTitleAsync(string aeTitle, int? skip = null, int? limit = null)
94+
=> await _workflowRepository.GetAllByAeTitleAsync(aeTitle, skip, limit);
95+
96+
public async Task<long> GetCountByAeTitleAsync(string aeTitle)
97+
=> await _workflowRepository.GetCountByAeTitleAsync(aeTitle);
98+
9199
}
92100
}

src/WorkflowManager/Database/Interfaces/IWorkflowRepository.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,15 @@ public interface IWorkflowRepository
5252
/// <param name="aeTitle">An aeTitle to retrieve.</param>
5353
Task<WorkflowRevision> GetByAeTitleAsync(string aeTitle);
5454

55+
Task<IEnumerable<WorkflowRevision>> GetAllByAeTitleAsync(string aeTitle, int? skip, int? limit);
56+
57+
/// <summary>
58+
/// Retrieves a count of workflows based on an aeTitle.
59+
/// </summary>
60+
/// <param name="aeTitle"></param>
61+
/// <returns></returns>
62+
Task<long> GetCountByAeTitleAsync(string aeTitle);
63+
5564
/// <summary>
5665
/// Retrieves a list of workflows based on an aeTitle.
5766
/// </summary>

src/WorkflowManager/Database/Repositories/WorkflowRepository.cs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,33 @@ public WorkflowRepository(
4343

4444
var mongoDatabase = client.GetDatabase(databaseSettings.Value.DatabaseName);
4545
_workflowCollection = mongoDatabase.GetCollection<WorkflowRevision>("Workflows");
46+
EnsureIndex().GetAwaiter().GetResult();
47+
}
48+
49+
private async Task EnsureIndex()
50+
{
51+
Guard.Against.Null(_workflowCollection, "WorkflowCollection");
52+
53+
var asyncCursor = (await _workflowCollection.Indexes.ListAsync());
54+
var bsonDocuments = (await asyncCursor.ToListAsync());
55+
var indexes = bsonDocuments.Select(_ => _.GetElement("name").Value.ToString()).ToList();
56+
57+
// If index not present create it else skip.
58+
if (!indexes.Any(i => i is not null && i.Equals("AeTitleIndex")))
59+
{
60+
// Create Index here
61+
62+
var options = new CreateIndexOptions()
63+
{
64+
Name = "AeTitleIndex"
65+
};
66+
var model = new CreateIndexModel<WorkflowRevision>(
67+
Builders<WorkflowRevision>.IndexKeys.Ascending(s => s.Workflow.InformaticsGateway.AeTitle),
68+
options
69+
);
70+
71+
await _workflowCollection.Indexes.CreateOneAsync(model);
72+
}
4673
}
4774

4875
public List<WorkflowRevision> GetWorkflowsList()
@@ -123,6 +150,29 @@ public async Task<WorkflowRevision> GetByAeTitleAsync(string aeTitle)
123150
return workflow;
124151
}
125152

153+
public async Task<IEnumerable<WorkflowRevision>> GetAllByAeTitleAsync(string aeTitle, int? skip, int? limit)
154+
{
155+
Guard.Against.NullOrWhiteSpace(aeTitle, nameof(aeTitle));
156+
#pragma warning disable CS8602 // Dereference of a possibly null reference.
157+
var workflows = await _workflowCollection
158+
.Find(x => x.Workflow.InformaticsGateway.AeTitle == aeTitle && x.Deleted == null)
159+
.Skip(skip)
160+
.Limit(limit)
161+
.ToListAsync();
162+
163+
return workflows
164+
.GroupBy(w => w.WorkflowId)
165+
.Select(g => g.First())
166+
.ToList(); ;
167+
}
168+
169+
public async Task<long> GetCountByAeTitleAsync(string aeTitle)
170+
{
171+
Guard.Against.NullOrWhiteSpace(aeTitle, nameof(aeTitle));
172+
return await _workflowCollection
173+
.CountDocumentsAsync(x => x.Workflow.InformaticsGateway.AeTitle == aeTitle && x.Deleted == null);
174+
}
175+
126176
public async Task<IList<WorkflowRevision>> GetWorkflowsByAeTitleAsync(List<string> aeTitles)
127177
{
128178
Guard.Against.NullOrEmpty(aeTitles, nameof(aeTitles));

src/WorkflowManager/Logging/Log.100000.Http.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,5 +61,8 @@ public static partial class Log
6161

6262
[LoggerMessage(EventId = 100013, Level = LogLevel.Information, Message = "BYpass authentication.")]
6363
public static partial void BypassAuthentication(this ILogger logger);
64+
65+
[LoggerMessage(EventId = 100014, Level = LogLevel.Error, Message = "Unexpected error occurred in get /workflows/aetitle API.")]
66+
public static partial void WorkflowGetAeTitleAsyncError(this ILogger logger, Exception ex);
6467
}
6568
}

src/WorkflowManager/WorkflowManager/Controllers/WorkflowsController.cs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,5 +295,52 @@ public async Task<IActionResult> DeleteAsync([FromRoute] string id)
295295
InternalServerError);
296296
}
297297
}
298+
299+
/// <summary>
300+
///
301+
/// </summary>
302+
/// <param name="title"></param>
303+
/// <param name="filter"></param>
304+
/// <returns>A <see cref="Task{TResult}"/> representing the result of the asynchronous operation.</returns>
305+
[HttpGet("aetitle/{title}")]
306+
[ProducesResponseType(typeof(WorkflowRevision), StatusCodes.Status200OK)]
307+
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)]
308+
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)]
309+
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status500InternalServerError)]
310+
public async Task<IActionResult> GetByAeTitle([FromRoute] string title, [FromQuery] PaginationFilter filter)
311+
{
312+
if (string.IsNullOrWhiteSpace(title))
313+
{
314+
_logger.LogDebug($"{nameof(GetByAeTitle)} - Failed to validate {nameof(title)}");
315+
316+
return Problem($"Failed to validate {nameof(title)}, not a valid AE title", $"/workflows/aetitle/{title}", BadRequest);
317+
}
318+
319+
try
320+
{
321+
var route = Request?.Path.Value ?? string.Empty;
322+
var pageSize = filter.PageSize ?? _options.Value.EndpointSettings.DefaultPageSize;
323+
var validFilter = new PaginationFilter(filter.PageNumber, pageSize, _options.Value.EndpointSettings.MaxPageSize);
324+
325+
var pagedData = await _workflowService.GetByAeTitleAsync(
326+
title,
327+
(validFilter.PageNumber - 1) * validFilter.PageSize,
328+
validFilter.PageSize);
329+
330+
var dataTotal = await _workflowService.GetCountByAeTitleAsync(title);
331+
var pagedReponse = CreatePagedReponse(pagedData.ToList(), validFilter, dataTotal, _uriService, route);
332+
333+
return Ok(pagedReponse);
334+
}
335+
catch (Exception e)
336+
{
337+
_logger.WorkflowGetAeTitleAsyncError(e);
338+
339+
return Problem(
340+
$"Unexpected error occurred: {e.Message}",
341+
$"/workflows/aetitle",
342+
InternalServerError);
343+
}
344+
}
298345
}
299346
}

tests/UnitTests/Common.Tests/Services/WorkflowServiceTests.cs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,24 @@ public async Task WorkflowService_NoExistingWorkflow_ReturnsNull()
5353
Assert.Null(result);
5454
}
5555

56+
[Fact]
57+
public async Task WorkflowService_GetAsync_With_Empty()
58+
{
59+
await Assert.ThrowsAsync<ArgumentException>(() => WorkflowService.GetAsync(string.Empty));
60+
}
61+
62+
[Fact]
63+
public async Task WorkflowService_GetByNameAsync_With_Empty()
64+
{
65+
await Assert.ThrowsAsync<ArgumentException>(() => WorkflowService.GetByNameAsync(string.Empty));
66+
}
67+
68+
[Fact]
69+
public async Task WorkflowService_CreateAsync_With_Empty()
70+
{
71+
await Assert.ThrowsAsync<ArgumentNullException>(() => WorkflowService.CreateAsync(null));
72+
}
73+
5674
[Fact]
5775
public async Task WorkflowService_WorkflowExists_ReturnsWorkflowId()
5876
{
@@ -88,5 +106,39 @@ public async Task WorkflowService_WorkflowExists_ReturnsWorkflowId()
88106

89107
Assert.Equal(workflowRevision.WorkflowId, result);
90108
}
109+
110+
[Fact]
111+
public async Task WorkflowService_DeleteWorkflow_With_Empty()
112+
{
113+
await Assert.ThrowsAsync<ArgumentNullException>(() => WorkflowService.DeleteWorkflowAsync(null));
114+
}
115+
116+
[Fact]
117+
public async Task WorkflowService_DeleteWorkflow_Calls_SoftDelete()
118+
{
119+
var result = await WorkflowService.DeleteWorkflowAsync(new WorkflowRevision());
120+
_workflowRepository.Verify(r => r.SoftDeleteWorkflow(It.IsAny<WorkflowRevision>()), Times.Once());
121+
}
122+
123+
[Fact]
124+
public async Task WorkflowService_Count_Calls_Count()
125+
{
126+
var result = await WorkflowService.CountAsync();
127+
_workflowRepository.Verify(r => r.CountAsync(), Times.Once());
128+
}
129+
130+
[Fact]
131+
public async Task WorkflowService_GetCountByAeTitleAsync_Calls_Count()
132+
{
133+
var result = await WorkflowService.GetCountByAeTitleAsync("string");
134+
_workflowRepository.Verify(r => r.GetCountByAeTitleAsync(It.IsAny<string>()), Times.Once());
135+
}
136+
137+
[Fact]
138+
public async Task WorkflowService_GetAllAsync_Calls_GetAllAsync()
139+
{
140+
var result = await WorkflowService.GetAllAsync(1, 2);
141+
_workflowRepository.Verify(r => r.GetAllAsync(It.IsAny<int>(), It.IsAny<int>()), Times.Once());
142+
}
91143
}
92144
}

tests/UnitTests/TaskManager.Tests/TaskExecutionStatsRepositoryTests.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,6 @@ public async Task TaskExecutionStatsRepository_Insert_Should_Check_For_Null_Even
114114
await Assert.ThrowsAsync<ArgumentNullException>(() => service.CreateAsync(default));
115115
}
116116

117-
118117
[Fact]
119118
public async Task TaskExecutionStatsRepository_update_Should_Check_For_Null_Event()
120119
{

tests/UnitTests/WorkflowManager.Tests/Controllers/WorkflowsControllerTests.cs

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
using Moq;
3333
using Xunit;
3434
using Monai.Deploy.WorkflowManager.Shared.Filter;
35+
using Monai.Deploy.WorkflowManager.Common.Services;
3536

3637
namespace Monai.Deploy.WorkflowManager.Test.Controllers
3738
{
@@ -48,7 +49,7 @@ public class WorkflowsControllerTests
4849

4950
public WorkflowsControllerTests()
5051
{
51-
_options = Options.Create(new WorkflowManagerOptions());
52+
_options = Options.Create(new WorkflowManagerOptions { EndpointSettings = new EndpointSettings { MaxPageSize = 99 } });
5253
_workflowService = new Mock<IWorkflowService>();
5354

5455
_logger = new Mock<ILogger<WorkflowsController>>();
@@ -883,5 +884,68 @@ public async Task DeleteAsync_WorkflowsGivenInvalidId_ShouldBadRequest()
883884
const string expectedInstance = "/workflows";
884885
Assert.StartsWith(expectedInstance, ((ProblemDetails)objectResult.Value).Instance);
885886
}
887+
888+
[Fact]
889+
public async Task GetByAeTitle_WorkflowsGivenEmptyTitle_ShouldBadRequest()
890+
{
891+
892+
var result = await WorkflowsController.GetByAeTitle(string.Empty, null);
893+
894+
var objectResult = Assert.IsType<ObjectResult>(result);
895+
Assert.Equal("Failed to validate title, not a valid AE title", result.As<ObjectResult>().Value.As<ProblemDetails>().Detail);
896+
897+
Assert.Equal(400, objectResult.StatusCode);
898+
899+
const string expectedInstance = "/workflows";
900+
Assert.StartsWith(expectedInstance, ((ProblemDetails)objectResult.Value).Instance);
901+
}
902+
903+
[Fact]
904+
public async Task GetByAeTitle_ShouldCall_GetByAeTitleAsync()
905+
{
906+
907+
var result = await WorkflowsController.GetByAeTitle("test", new PaginationFilter());
908+
var objectResult = Assert.IsType<OkObjectResult>(result);
909+
910+
_workflowService.Verify(x => x.GetByAeTitleAsync("test", It.IsAny<int>(), It.IsAny<int>()), Times.Once);
911+
912+
Assert.Equal(200, objectResult.StatusCode);
913+
}
914+
915+
[Fact]
916+
public async Task GetByAeTitle_ShouldCall_GetByAeTitleAsync_With_Skip()
917+
{
918+
919+
var result = await WorkflowsController.GetByAeTitle("test", new PaginationFilter { PageSize = 2, PageNumber = 2 });
920+
var objectResult = Assert.IsType<OkObjectResult>(result);
921+
922+
_workflowService.Verify(x => x.GetByAeTitleAsync("test", 2, It.IsAny<int>()), Times.Once);
923+
924+
Assert.Equal(200, objectResult.StatusCode);
925+
}
926+
927+
[Fact]
928+
public async Task GetByAeTitle_ShouldCall_GetByAeTitleAsync_With_Limit()
929+
{
930+
931+
var result = await WorkflowsController.GetByAeTitle("test", new PaginationFilter { PageNumber = 2, PageSize = 45 });
932+
var objectResult = Assert.IsType<OkObjectResult>(result);
933+
934+
_workflowService.Verify(x => x.GetByAeTitleAsync("test", It.IsAny<int>(), 45), Times.Once);
935+
936+
Assert.Equal(200, objectResult.StatusCode);
937+
}
938+
939+
[Fact]
940+
public async Task GetByAeTitle_ShouldCall_GetCountByAeTitleAsync()
941+
{
942+
943+
var result = await WorkflowsController.GetByAeTitle("test", new PaginationFilter());
944+
var objectResult = Assert.IsType<OkObjectResult>(result);
945+
946+
_workflowService.Verify(x => x.GetCountByAeTitleAsync("test"), Times.Once);
947+
948+
Assert.Equal(200, objectResult.StatusCode);
949+
}
886950
}
887951
}

0 commit comments

Comments
 (0)