From 064de558636e9e25371cc49184177e70b05c2a6e Mon Sep 17 00:00:00 2001
From: pdimitratos
Date: Tue, 1 May 2018 16:26:18 -0700
Subject: [PATCH] Refactor/simplify gateway controllers (#58)
Restructuring to reduce responsibility of Controllers by delegating to more specific classes
---
domain | 2 +-
.../Controllers/EngagementsController.cs | 12 +-
.../Controllers/EventsController.cs | 116 +++++-------------
.../Controllers/IncidentsController.cs | 91 +++++---------
.../Playbook/EventTypesController.cs | 4 +-
.../Playbook/GlobalActionsController.cs | 2 +-
.../Controllers/TicketsController.cs | 2 +-
.../Initialization/Startup/ServicesStartup.cs | 9 +-
src/Sia.Gateway/Links/EventLinks.cs | 83 +++++++++++++
src/Sia.Gateway/Links/IncidentLinks.cs | 67 ++++++++++
src/Sia.Gateway/Links/LinksProvider.cs | 19 +++
src/Sia.Gateway/Requests/Events/GetEvents.cs | 14 ++-
src/Sia.Gateway/Requests/Events/PostEvent.cs | 58 ++++++++-
.../Filters/EventFiltersTests.cs | 2 +
test/Sia.Gateway.Tests/GlobalSuppressions.cs | 2 +-
.../Engagements/GetEngagementTests.cs | 4 +-
...orksCorrectly_WhenCalledByEventsMethods.cs | 56 ++-------
.../GenerateLinksHeaderTestsForIncidents.cs | 56 ++-------
18 files changed, 331 insertions(+), 268 deletions(-)
create mode 100644 src/Sia.Gateway/Links/EventLinks.cs
create mode 100644 src/Sia.Gateway/Links/IncidentLinks.cs
create mode 100644 src/Sia.Gateway/Links/LinksProvider.cs
diff --git a/domain b/domain
index 1af7202..e45d439 160000
--- a/domain
+++ b/domain
@@ -1 +1 @@
-Subproject commit 1af7202242b329206cc58d2534687b12e7a94809
+Subproject commit e45d43973339e0208c429d9f40f676db656f514f
diff --git a/src/Sia.Gateway/Controllers/EngagementsController.cs b/src/Sia.Gateway/Controllers/EngagementsController.cs
index 74d9f5a..64a22f3 100644
--- a/src/Sia.Gateway/Controllers/EngagementsController.cs
+++ b/src/Sia.Gateway/Controllers/EngagementsController.cs
@@ -23,20 +23,16 @@ namespace Sia.Gateway.Controllers
public async Task Get([FromRoute]long incidentId, [FromRoute]long id)
{
var result = await _mediator
- .Send(new GetEngagementRequest(incidentId, id, authContext))
+ .Send(new GetEngagementRequest(incidentId, id, AuthContext))
.ConfigureAwait(continueOnCapturedContext: false);
- if (result == null)
- {
- return NotFound(notFoundMessage);
- }
- return Ok(result);
+ return OkIfFound(result);
}
[HttpPost()]
public async Task Post([FromRoute]long incidentId, [FromBody]NewEngagement newEngagement)
{
var result = await _mediator
- .Send(new PostEngagementRequest(incidentId, newEngagement, authContext))
+ .Send(new PostEngagementRequest(incidentId, newEngagement, AuthContext))
.ConfigureAwait(continueOnCapturedContext: false);
if (result == null)
{
@@ -49,7 +45,7 @@ namespace Sia.Gateway.Controllers
public async Task Put([FromRoute]long incidentId, [FromRoute]long engagementId, [FromBody]UpdateEngagement updatedEngagement)
{
await _mediator
- .Send(new PutEngagementRequest(incidentId, engagementId, updatedEngagement, authContext))
+ .Send(new PutEngagementRequest(incidentId, engagementId, updatedEngagement, AuthContext))
.ConfigureAwait(continueOnCapturedContext: false);
return Ok();
}
diff --git a/src/Sia.Gateway/Controllers/EventsController.cs b/src/Sia.Gateway/Controllers/EventsController.cs
index b05fafb..70ee64a 100644
--- a/src/Sia.Gateway/Controllers/EventsController.cs
+++ b/src/Sia.Gateway/Controllers/EventsController.cs
@@ -13,134 +13,80 @@ using Sia.Core.Protocol;
using System;
using System.Net.Http;
using System.Threading.Tasks;
+using Sia.Gateway.Links;
+using Sia.Core.Validation;
+using Sia.Domain;
namespace Sia.Gateway.Controllers
{
public class EventsController : BaseController
{
- private const string notFoundMessage = "Incident or event not found";
- private readonly HubConnectionBuilder _hubConnectionBuilder;
- private readonly ILogger _logger;
+ public EventLinksProvider Links { get; }
public EventsController(IMediator mediator,
AzureActiveDirectoryAuthenticationInfo authConfig,
- HubConnectionBuilder hubConnectionBuilder,
IUrlHelper urlHelper,
- ILoggerFactory loggerFactory)
+ EventLinksProvider links)
: base(mediator, authConfig, urlHelper)
{
- _hubConnectionBuilder = hubConnectionBuilder;
- _logger = loggerFactory.CreateLogger();
+ Links = links;
}
-
- public LinksHeader CreateLinks(string id, string incidentId, EventFilters filter, PaginationMetadata pagination, string routeName)
- {
- var _operationLinks = new OperationLinks()
- {
- Single = new SingleOperationLinks()
- {
- Get = _urlHelper.Link(GetSingleRouteName, new { id }),
- Post = _urlHelper.Link(PostSingleRouteName, new { })
-
- },
- Multiple = new MultipleOperationLinks()
- {
- Get = _urlHelper.Link(GetMultipleRouteName, new { })
- }
- };
- var _relationLinks = new RelationLinks()
- {
- Parent = new RelatedParentLinks()
- {
- Incident = _urlHelper.Link(IncidentsController.GetSingleRouteName, new { id = incidentId })
- }
- };
- return new LinksHeader(filter, pagination, _urlHelper, routeName, _operationLinks, _relationLinks);
- }
-
- public const string GetMultipleRouteName = "GetEvents";
- [HttpGet("incidents/{incidentId}/events", Name = GetMultipleRouteName)]
+
+ [HttpGet("incidents/{incidentId}/events", Name = EventRoutesByIncident.GetMultiple)]
public async Task GetEvents([FromRoute]long incidentId,
[FromQuery]PaginationMetadata pagination,
[FromQuery]EventFilters filter)
{
var result = await _mediator
- .Send(new GetEventsRequest(incidentId, pagination, filter, authContext))
+ .Send(new GetEventsRequest(incidentId, pagination, filter, AuthContext))
.ConfigureAwait(continueOnCapturedContext: false);
- Response.Headers.AddLinksHeader(CreateLinks(null, incidentId.ToPathTokenString(), filter, pagination, GetMultipleRouteName));
+ var links = Links.CreatePaginatedLinks(EventRoutesByIncident.GetMultiple, pagination, filter, incidentId);
- return Ok(result);
+ return OkIfFound(result, links);
}
- public const string GetSingleRouteName = "GetEvent";
- [HttpGet("incidents/{incidentId}/events/{id}", Name = GetSingleRouteName)]
- public async Task Get([FromRoute]long incidentId, [FromRoute]long id)
+ [HttpGet("incidents/{incidentId}/events/{eventId}", Name = EventRoutesByIncident.GetSingle)]
+ public async Task Get([FromRoute]long incidentId, [FromRoute]long eventId)
{
var result = await _mediator
- .Send(new GetEventRequest(incidentId, id, authContext))
+ .Send(new GetEventRequest(incidentId, eventId, AuthContext))
.ConfigureAwait(continueOnCapturedContext: false);
- Response.Headers.AddLinksHeader(CreateLinks(id.ToPathTokenString(), incidentId.ToPathTokenString(), null, null, GetSingleRouteName));
- if (result == null)
- {
- return NotFound(notFoundMessage);
- }
+ var links = Links.CreateLinks(incidentId, eventId);
- return Ok(result);
+ return OkIfFound(result, links);
}
- public const string PostSingleRouteName = "PostEvent";
- [HttpPost("incidents/{incidentId}/events", Name = PostSingleRouteName)]
+ [HttpPost("incidents/{incidentId}/events", Name = EventRoutesByIncident.PostSingle)]
public async Task Post([FromRoute]long incidentId, [FromBody]NewEvent newEvent)
{
var result = await _mediator
- .Send(new PostEventRequest(incidentId, newEvent, authContext))
+ .Send(new PostEventRequest(incidentId, newEvent, AuthContext, new Lazy(() => GetTokenFromHeaders()), Request.Host.Port))
.ConfigureAwait(continueOnCapturedContext: false);
- if (result == null)
- {
- return NotFound(notFoundMessage);
- }
+ ILinksHeader getLinks(Event res) =>
+ Links.CreateLinks(incidentId, res.Id);
- await SendEventToSubscribers(result).ConfigureAwait(continueOnCapturedContext: false);
+ Uri getRetrievalRoute(Event res) =>
+ new Uri(_urlHelper.Link(EventRoutesByIncident.GetSingle, new { incidentId, eventId = res.Id }));
- var newUrl = new Uri(_urlHelper.Link(GetSingleRouteName, new { id = result.Id }));
-
- Response.Headers.AddLinksHeader(CreateLinks(result.Id.ToPathTokenString(), incidentId.ToPathTokenString(), null, null, PostSingleRouteName));
- return Created(newUrl, result);
+ return CreatedIfExists(result, getRetrievalRoute, getLinks);
}
- public const string GetMultipleUncorrelatedRouteName = "GetUncorrelatedEvent";
- [HttpGet("events", Name = GetMultipleUncorrelatedRouteName)]
- public async Task GetUncorrelatedEvents([FromQuery]PaginationMetadata pagination,
- [FromQuery]EventFilters filter)
+ [HttpGet("events", Name = EventRoutes.GetMultiple)]
+ public async Task GetUncorrelatedEvents(
+ [FromQuery]PaginationMetadata pagination,
+ [FromQuery]EventFilters filter
+ )
{
var result = await _mediator
- .Send(new GetUncorrelatedEventsRequest(pagination, filter, authContext))
+ .Send(new GetUncorrelatedEventsRequest(pagination, filter, AuthContext))
.ConfigureAwait(continueOnCapturedContext: false);
- return Ok(result);
- }
+ var links = Links.CreatePaginatedLinks(EventRoutes.GetMultiple, pagination, filter);
- private async Task SendEventToSubscribers(Domain.Event result)
- {
- var url = $"http://localhost:{Request.Host.Port}{EventsHub.HubPath}";
- try
- {
- string token = GetTokenFromHeaders();
- var eventHubConnection = _hubConnectionBuilder
- .WithAccessToken(() => token)
- .WithUrl(url)
- .Build();
- await eventHubConnection.StartAsync().ConfigureAwait(continueOnCapturedContext: false);
- await eventHubConnection.SendAsync("Send", result).ConfigureAwait(continueOnCapturedContext: false);
- await eventHubConnection.DisposeAsync().ConfigureAwait(continueOnCapturedContext: false);
- }
- catch (Exception ex)
- {
- _logger.LogError(ex, $"Encountered exception when attempting to send posted event to SignalR subscribers, url: {url}");
- }
+ return OkIfFound(result, links);
}
private string GetTokenFromHeaders()
diff --git a/src/Sia.Gateway/Controllers/IncidentsController.cs b/src/Sia.Gateway/Controllers/IncidentsController.cs
index 5d87561..ae89569 100644
--- a/src/Sia.Gateway/Controllers/IncidentsController.cs
+++ b/src/Sia.Gateway/Controllers/IncidentsController.cs
@@ -8,97 +8,64 @@ using Sia.Core.Controllers;
using Sia.Core.Protocol;
using System.Threading.Tasks;
using System;
+using Sia.Core.Validation;
+using Sia.Gateway.Links;
namespace Sia.Gateway.Controllers
{
[Route("[controller]")]
public class IncidentsController : BaseController
{
- public IncidentsController(IMediator mediator, AzureActiveDirectoryAuthenticationInfo authConfig, IUrlHelper urlHelper)
+ public IncidentsController(
+ IMediator mediator,
+ AzureActiveDirectoryAuthenticationInfo authConfig,
+ IUrlHelper urlHelper,
+ IncidentLinksProvider links)
: base(mediator, authConfig, urlHelper)
{
+ Links = links;
}
- public LinksHeader CreateLinks(string id, PaginationMetadata pagination, string routeName)
- {
- var _operationLinks = new OperationLinks()
- {
- Single = new SingleOperationLinks()
- {
- Get = _urlHelper.Link(GetSingleRouteName, new { id }),
- Post = _urlHelper.Link(PostSingleRouteName, new { })
- },
- Multiple = new MultipleOperationLinks()
- {
- Get = _urlHelper.Link(GetMultipleRouteName, new { })
- }
+ protected IncidentLinksProvider Links { get; }
- };
- RelationLinks _relationLinks = null;
- if (id != null)
- {
- _relationLinks = new RelationLinks()
- {
- Children = new RelatedChildLinks()
- {
- Events = _urlHelper.Link(EventsController.GetMultipleRouteName, new { incidentId = id })
- }
- };
- }
-
- return new LinksHeader(null, pagination, _urlHelper, routeName, _operationLinks, _relationLinks);
- }
-
- public const string GetSingleRouteName = "GetIncident";
- [HttpGet("{id}", Name = GetSingleRouteName)]
- public async Task Get(long id)
+ [HttpGet("{incidentId}", Name = IncidentRoutes.GetSingle)]
+ public async Task Get(long incidentId)
{
var result = await _mediator
- .Send(new GetIncidentRequest(id, authContext))
+ .Send(new GetIncidentRequest(incidentId, AuthContext))
.ConfigureAwait(continueOnCapturedContext: false);
- if (result == null)
- {
- return NotFound($"{nameof(Incident)} not found");
- }
- Response.Headers.AddLinksHeader(CreateLinks(id.ToPathTokenString(), null, GetSingleRouteName));
-
- return Ok(result);
+ var links = Links.CreateLinks(incidentId);
+
+ return OkIfFound(result, links);
}
- public const string GetMultipleRouteName = "GetIncidents";
- [HttpGet(Name = GetMultipleRouteName)]
+ [HttpGet(Name = IncidentRoutes.GetMultiple)]
public async Task Get([FromQuery] PaginationMetadata pagination)
{
var result = await _mediator
- .Send(new GetIncidentsRequest(pagination, authContext))
+ .Send(new GetIncidentsRequest(pagination, AuthContext))
.ConfigureAwait(continueOnCapturedContext: false);
- if (result == null)
- {
- return NotFound($"{nameof(Incident)}s not found");
- }
- Response.Headers.AddLinksHeader(CreateLinks(null, pagination, GetSingleRouteName));
- return Ok(result);
+ var links = Links.CreatePaginatedLinks(IncidentRoutes.GetSingle, pagination);
+
+ return OkIfFound(result, links);
}
- public const string PostSingleRouteName = "PostIncident";
- [HttpPost(Name = PostSingleRouteName)]
+ [HttpPost(Name = IncidentRoutes.PostSingle)]
public async Task Post([FromBody]NewIncident incident)
{
var result = await _mediator
- .Send(new PostIncidentRequest(incident, authContext))
+ .Send(new PostIncidentRequest(incident, AuthContext))
.ConfigureAwait(continueOnCapturedContext: false);
- if (result == null)
- {
- return NotFound($"{nameof(Incident)} not found");
- }
- var newUrl = new Uri(_urlHelper.Link(EventsController.GetMultipleRouteName, new { incidentId = result.Id }));
- Response.Headers.AddLinksHeader(CreateLinks(result.Id.ToPathTokenString(), null, PostSingleRouteName));
- return Created(newUrl, result);
+ ILinksHeader getLinks(Incident res) =>
+ Links.CreateLinks(res.Id);
+
+ Uri getRetrievalRoute(Incident res) =>
+ new Uri(_urlHelper.Link(IncidentRoutes.GetSingle, new { incidentId = res.Id }));
+
+ return CreatedIfExists(result, getRetrievalRoute, getLinks);
}
-
-
}
}
diff --git a/src/Sia.Gateway/Controllers/Playbook/EventTypesController.cs b/src/Sia.Gateway/Controllers/Playbook/EventTypesController.cs
index 896736f..8d5d5e2 100644
--- a/src/Sia.Gateway/Controllers/Playbook/EventTypesController.cs
+++ b/src/Sia.Gateway/Controllers/Playbook/EventTypesController.cs
@@ -23,13 +23,13 @@ namespace Sia.Gateway.Controllers
[HttpGet(Name = nameof(GetAll) + nameof(EventType))]
public async Task GetAll()
=> OkIfFound(await _mediator
- .Send(new GetEventTypesRequest(authContext))
+ .Send(new GetEventTypesRequest(AuthContext))
.ConfigureAwait(continueOnCapturedContext: false));
[HttpGet("{id}", Name = nameof(Get) + nameof(EventType))]
public async Task Get(long id)
=> OkIfFound(await _mediator
- .Send(new GetEventTypeRequest(id, authContext))
+ .Send(new GetEventTypeRequest(id, AuthContext))
.ConfigureAwait(continueOnCapturedContext: false));
}
}
diff --git a/src/Sia.Gateway/Controllers/Playbook/GlobalActionsController.cs b/src/Sia.Gateway/Controllers/Playbook/GlobalActionsController.cs
index 95d6ee4..0c1f23c 100644
--- a/src/Sia.Gateway/Controllers/Playbook/GlobalActionsController.cs
+++ b/src/Sia.Gateway/Controllers/Playbook/GlobalActionsController.cs
@@ -23,7 +23,7 @@ namespace Sia.Gateway.Controllers
[HttpGet(Name = nameof(GetAll) + "Global" + nameof(Domain.Playbook.Action))]
public async Task GetAll()
=> OkIfFound(await _mediator
- .Send(new GetGlobalActionsRequest(authContext))
+ .Send(new GetGlobalActionsRequest(AuthContext))
.ConfigureAwait(continueOnCapturedContext: false));
}
}
diff --git a/src/Sia.Gateway/Controllers/TicketsController.cs b/src/Sia.Gateway/Controllers/TicketsController.cs
index 8d20474..a633d5e 100644
--- a/src/Sia.Gateway/Controllers/TicketsController.cs
+++ b/src/Sia.Gateway/Controllers/TicketsController.cs
@@ -22,7 +22,7 @@ namespace Sia.Gateway.Controllers
public async Task Get(string id)
{
var result = await _mediator
- .Send(new GetIncidentsByTicketCreateIfNeededRequest(id, authContext))
+ .Send(new GetIncidentsByTicketCreateIfNeededRequest(id, AuthContext))
.ConfigureAwait(continueOnCapturedContext: false);
return Ok(result);
}
diff --git a/src/Sia.Gateway/Initialization/Startup/ServicesStartup.cs b/src/Sia.Gateway/Initialization/Startup/ServicesStartup.cs
index 569d8df..04cc86c 100644
--- a/src/Sia.Gateway/Initialization/Startup/ServicesStartup.cs
+++ b/src/Sia.Gateway/Initialization/Startup/ServicesStartup.cs
@@ -30,6 +30,7 @@ using System.Reflection;
using System.Runtime.Loader;
using System.Threading.Tasks;
using System.Globalization;
+using Sia.Gateway.Links;
namespace Sia.Gateway.Initialization
{
@@ -46,7 +47,8 @@ namespace Sia.Gateway.Initialization
.AddAuth(config)
.AddDatabase(env, config)
.AddTicketingConnector(env, rawConfig, config?.Connector?.Ticket)
- .AddMicroserviceProxies(config);
+ .AddMicroserviceProxies(config)
+ .AddRouteHelpers();
public static IServiceCollection AddAuth(this IServiceCollection services, GatewayConfiguration config)
{
@@ -160,8 +162,11 @@ namespace Sia.Gateway.Initialization
GetGlobalActionsShortCircuit.RegisterMe(services)));
return services;
}
-
+ public static IServiceCollection AddRouteHelpers(this IServiceCollection services)
+ => services
+ .AddScoped()
+ .AddScoped();
}
}
\ No newline at end of file
diff --git a/src/Sia.Gateway/Links/EventLinks.cs b/src/Sia.Gateway/Links/EventLinks.cs
new file mode 100644
index 0000000..4c14b1c
--- /dev/null
+++ b/src/Sia.Gateway/Links/EventLinks.cs
@@ -0,0 +1,83 @@
+using Microsoft.AspNetCore.Mvc;
+using Sia.Core.Protocol;
+using Sia.Core.Validation;
+using Sia.Gateway.Filters;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace Sia.Gateway.Links
+{
+ public static class EventRoutes
+ {
+ public const string GetMultiple = "GetUncorrelatedEvent";
+ }
+
+ public static class EventRoutesByIncident
+ {
+ public const string GetMultiple = "GetEvents";
+ public const string GetSingle = "GetEvent";
+ public const string PostSingle = "PostEvent";
+ }
+
+ public class EventLinksProvider : LinksProvider
+ {
+ public EventLinksProvider(IUrlHelper urlHelper)
+ : base(urlHelper) { }
+
+ protected override OperationLinks GetOperationLinks(object routeValues)
+ => new OperationLinks()
+ {
+ Single = new SingleOperationLinks()
+ {
+ Get = UrlHelper.Link(EventRoutesByIncident.GetSingle, routeValues),
+ Post = UrlHelper.Link(EventRoutesByIncident.PostSingle, routeValues)
+
+ },
+ Multiple = new MultipleOperationLinks()
+ {
+ Get = UrlHelper.Link(EventRoutesByIncident.GetMultiple, routeValues)
+ }
+ };
+
+ public ILinksHeader CreatePaginatedLinks(string routeName, PaginationMetadata pagination, EventFilters filter, long incidentId = default(long))
+ {
+ ThrowIf.NullOrWhiteSpace(routeName, nameof(routeName));
+ var routeValues = (incidentId == default(long))
+ ? (object)new { }
+ : new { incidentId = incidentId.ToPathTokenString() };
+ var route = UrlHelper.Link(routeName, routeValues);
+
+ var operationLinks = GetOperationLinks(routeValues);
+ RelationLinks relationLinks = null;
+
+ return new PaginatedLinksHeader(route, operationLinks, relationLinks, pagination, filter);
+ }
+
+ public ILinksHeader CreateLinks(long incidentId, long eventId)
+ {
+ var routeValues = new
+ {
+ incidentId = incidentId.ToPathTokenString(),
+ eventId = eventId.ToPathTokenString()
+ };
+
+ var operationLinks = GetOperationLinks(routeValues);
+ var relationLinks = new RelationLinks()
+ {
+ Parent = new EventParentLinks()
+ {
+ Incident = UrlHelper.Link(IncidentRoutes.GetSingle, new { incidentId })
+ }
+ };
+
+ return new CrudLinksHeader(operationLinks, relationLinks);
+ }
+ }
+
+ public class EventParentLinks
+ {
+ public string Incident { get; set; }
+ }
+}
diff --git a/src/Sia.Gateway/Links/IncidentLinks.cs b/src/Sia.Gateway/Links/IncidentLinks.cs
new file mode 100644
index 0000000..e8e809d
--- /dev/null
+++ b/src/Sia.Gateway/Links/IncidentLinks.cs
@@ -0,0 +1,67 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.Routing;
+using Sia.Core.Protocol;
+using Sia.Core.Validation;
+
+namespace Sia.Gateway.Links
+{
+ public static class IncidentRoutes
+ {
+ public const string GetSingle = "GetIncident";
+ public const string GetMultiple = "GetIncidents";
+ public const string PostSingle = "PostIncident";
+ }
+ public class IncidentLinksProvider : LinksProvider
+ {
+ public IncidentLinksProvider(IUrlHelper urlHelper)
+ : base(urlHelper) { }
+
+ protected override OperationLinks GetOperationLinks(object routeValues)
+ => new OperationLinks()
+ {
+ Single = new SingleOperationLinks()
+ {
+ Get = UrlHelper.Link(IncidentRoutes.GetSingle, routeValues),
+ Post = UrlHelper.Link(IncidentRoutes.PostSingle, routeValues)
+ },
+ Multiple = new MultipleOperationLinks()
+ {
+ Get = UrlHelper.Link(IncidentRoutes.GetMultiple, routeValues)
+ }
+ };
+ public ILinksHeader CreatePaginatedLinks(string routeName, PaginationMetadata pagination)
+ {
+ ThrowIf.NullOrWhiteSpace(routeName, nameof(routeName));
+ var routeValues = new { };
+ var route = UrlHelper.Link(routeName, routeValues);
+
+ var operationLinks = GetOperationLinks(routeValues);
+ RelationLinks relationLinks = null;
+
+ return new PaginatedLinksHeader(route, operationLinks, relationLinks, pagination);
+ }
+
+ public ILinksHeader CreateLinks(long incidentId)
+ {
+ var routeValues = new { incidentId = incidentId.ToPathTokenString() };
+ var operationLinks = GetOperationLinks(routeValues);
+ var relationLinks = new RelationLinks()
+ {
+ Children = new IncidentChildLinks()
+ {
+ Events = UrlHelper.Link(EventRoutesByIncident.GetMultiple, routeValues)
+ }
+ };
+
+ return new CrudLinksHeader(operationLinks, relationLinks);
+ }
+ }
+ public class IncidentChildLinks
+ {
+ public string Events { get; set; }
+ }
+}
diff --git a/src/Sia.Gateway/Links/LinksProvider.cs b/src/Sia.Gateway/Links/LinksProvider.cs
new file mode 100644
index 0000000..c97aed5
--- /dev/null
+++ b/src/Sia.Gateway/Links/LinksProvider.cs
@@ -0,0 +1,19 @@
+using Microsoft.AspNetCore.Mvc;
+using Sia.Core.Protocol;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace Sia.Gateway.Links
+{
+ public abstract class LinksProvider
+ {
+ protected IUrlHelper UrlHelper { get; }
+ protected LinksProvider(IUrlHelper urlHelper)
+ {
+ UrlHelper = urlHelper;
+ }
+ protected abstract OperationLinks GetOperationLinks(object routeValues);
+ }
+}
diff --git a/src/Sia.Gateway/Requests/Events/GetEvents.cs b/src/Sia.Gateway/Requests/Events/GetEvents.cs
index cdee0e4..5ed548a 100644
--- a/src/Sia.Gateway/Requests/Events/GetEvents.cs
+++ b/src/Sia.Gateway/Requests/Events/GetEvents.cs
@@ -43,11 +43,13 @@ namespace Sia.Gateway.Requests.Events
}
public override async Task> Handle(GetEventsRequest request, CancellationToken cancellationToken)
=> await _context.Events
- .Where(ev => ev.IncidentId == request.IncidentId)
- .WithFilter(request.Filter)
- .WithPagination(request.Pagination)
- .ProjectTo()
- .ToListAsync(cancellationToken)
- .ConfigureAwait(continueOnCapturedContext: false);
+ .Include(ev => ev.Incident)
+ .ThenInclude(inc => inc.Tickets)
+ .Where(ev => ev.IncidentId == request.IncidentId)
+ .WithFilter(request.Filter)
+ .WithPagination(request.Pagination)
+ .ProjectTo()
+ .ToListAsync(cancellationToken)
+ .ConfigureAwait(continueOnCapturedContext: false);
}
}
diff --git a/src/Sia.Gateway/Requests/Events/PostEvent.cs b/src/Sia.Gateway/Requests/Events/PostEvent.cs
index ea6b4f1..f7d1b93 100644
--- a/src/Sia.Gateway/Requests/Events/PostEvent.cs
+++ b/src/Sia.Gateway/Requests/Events/PostEvent.cs
@@ -12,27 +12,50 @@ using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Sia.Core.Validation;
+using Sia.Gateway.Hubs;
+using Microsoft.Extensions.Logging;
+using Microsoft.AspNetCore.SignalR.Client;
namespace Sia.Gateway.Requests
{
public class PostEventRequest : AuthenticatedRequest
{
- public PostEventRequest(long incidentId, NewEvent newEvent, AuthenticatedUserContext userContext)
- :base(userContext)
+ public PostEventRequest(
+ long incidentId,
+ NewEvent newEvent,
+ AuthenticatedUserContext userContext,
+ Lazy lazyToken,
+ int? port
+ ) : base(userContext)
{
IncidentId = incidentId;
NewEvent = ThrowIf.Null(newEvent, nameof(newEvent));
+ LazyToken = ThrowIf.Null(lazyToken, nameof(lazyToken));
+ Port = port;
}
public NewEvent NewEvent { get; }
public long IncidentId { get; }
+ public Lazy LazyToken { get; }
+ public int? Port { get; }
+ }
+
+ public class HubConnectionInfo
+ {
+ public int Port { get; set; }
}
public class PostEventHandler : IncidentContextHandler
{
- public PostEventHandler(IncidentContext context)
- : base(context)
+ private ILogger Logger { get; }
+ private HubConnectionBuilder EventHubConnectionBuilder { get; }
+ public PostEventHandler(
+ IncidentContext context,
+ HubConnectionBuilder hubConnectionBuilder,
+ ILoggerFactory loggerFactory
+ ) : base(context)
{
-
+ EventHubConnectionBuilder = hubConnectionBuilder;
+ Logger = loggerFactory.CreateLogger>();
}
public override async Task Handle(PostEventRequest request, CancellationToken cancellationToken)
{
@@ -48,7 +71,30 @@ namespace Sia.Gateway.Requests
dataIncident.Events.Add(dataEvent);
await _context.SaveChangesAsync(cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
- return Mapper.Map(dataEvent);
+ var result = Mapper.Map(dataEvent);
+
+ await SendEventToSubscribers(request, result, cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
+
+ return result;
+ }
+
+ private async Task SendEventToSubscribers(PostEventRequest request, Event result, CancellationToken cancellationToken)
+ {
+ var url = $"http://localhost:{request.Port}{EventsHub.HubPath}";
+ try
+ {
+ var eventHubConnection = EventHubConnectionBuilder
+ .WithAccessToken(() => request.LazyToken.Value)
+ .WithUrl(url)
+ .Build();
+ await eventHubConnection.StartAsync().ConfigureAwait(continueOnCapturedContext: false);
+ await eventHubConnection.SendAsync("Send", result, cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
+ await eventHubConnection.DisposeAsync().ConfigureAwait(continueOnCapturedContext: false);
+ }
+ catch (Exception ex)
+ {
+ Logger.LogError(ex, $"Encountered exception when attempting to send posted event to SignalR subscribers, url: {url}");
+ }
}
}
}
diff --git a/test/Sia.Gateway.Tests/Filters/EventFiltersTests.cs b/test/Sia.Gateway.Tests/Filters/EventFiltersTests.cs
index 29c9baa..1e3e808 100644
--- a/test/Sia.Gateway.Tests/Filters/EventFiltersTests.cs
+++ b/test/Sia.Gateway.Tests/Filters/EventFiltersTests.cs
@@ -3,6 +3,8 @@ using Newtonsoft.Json;
using Sia.Data.Incidents.Models;
using Sia.Gateway.Filters;
using System;
+using System.Collections.Generic;
+
namespace Sia.Gateway.Tests.Filters
{
diff --git a/test/Sia.Gateway.Tests/GlobalSuppressions.cs b/test/Sia.Gateway.Tests/GlobalSuppressions.cs
index 5497235..ec903f4 100644
--- a/test/Sia.Gateway.Tests/GlobalSuppressions.cs
+++ b/test/Sia.Gateway.Tests/GlobalSuppressions.cs
@@ -15,4 +15,4 @@
"CA1707:Identifiers should not contain underscores",
Justification = "Test methods are more readable with underscores",
Scope = "module"
-)]
\ No newline at end of file
+)]
diff --git a/test/Sia.Gateway.Tests/Requests/Engagements/GetEngagementTests.cs b/test/Sia.Gateway.Tests/Requests/Engagements/GetEngagementTests.cs
index be78893..2f657dc 100644
--- a/test/Sia.Gateway.Tests/Requests/Engagements/GetEngagementTests.cs
+++ b/test/Sia.Gateway.Tests/Requests/Engagements/GetEngagementTests.cs
@@ -17,7 +17,7 @@ namespace Sia.Gateway.Tests.Requests
=> AutoMapperStartup.InitializeAutomapper();
[TestMethod]
- public async Task HandleWhenEFReturnsSuccessfulReturnCorrectEngagement()
+ public async Task Handle_WhenEFReturnsSuccessful_ReturnCorrectEngagement()
{
var expectedEngagement = new Engagement
{
@@ -35,7 +35,7 @@ namespace Sia.Gateway.Tests.Requests
var serviceUnderTest = new GetEngagementHandler(
await MockFactory
- .IncidentContext(nameof(HandleWhenEFReturnsSuccessfulReturnCorrectEngagement))
+ .IncidentContext(nameof(Handle_WhenEFReturnsSuccessful_ReturnCorrectEngagement))
.ConfigureAwait(continueOnCapturedContext: false)
);
var request = new GetEngagementRequest(1, 1, new DummyAuthenticatedUserContext());
diff --git a/test/Sia.Gateway.Tests/Requests/Events/CreatLinksWorksCorrectly_WhenCalledByEventsMethods.cs b/test/Sia.Gateway.Tests/Requests/Events/CreatLinksWorksCorrectly_WhenCalledByEventsMethods.cs
index d8b6d18..1b47e6c 100644
--- a/test/Sia.Gateway.Tests/Requests/Events/CreatLinksWorksCorrectly_WhenCalledByEventsMethods.cs
+++ b/test/Sia.Gateway.Tests/Requests/Events/CreatLinksWorksCorrectly_WhenCalledByEventsMethods.cs
@@ -5,6 +5,7 @@ using Sia.Gateway.Controllers;
using Sia.Gateway.Tests.TestDoubles;
using Sia.Core.Protocol;
using System.Collections.Generic;
+using Sia.Gateway.Links;
namespace Sia.Gateway.Tests.Requests.Events
{
@@ -39,55 +40,20 @@ namespace Sia.Gateway.Tests.Requests.Events
//Arrange
methods.Clear();
ids.Clear();
- var eventsController = new EventsController(null, null, null, urlHelperMock.Object, new StubLoggerFactory());
+ var eventLinksProvider = new EventLinksProvider(urlHelperMock.Object);
// Act
- eventsController.CreateLinks("1", "2", null,null,"");
+ eventLinksProvider.CreateLinks(2, 1);
// Assert
- urlHelperMock.Verify(foo => foo.Link(EventsController.GetSingleRouteName, It.IsAny