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()), Times.Exactly(1)); - urlHelperMock.Verify(foo => foo.Link(EventsController.PostSingleRouteName, It.IsAny()), Times.Exactly(1)); - urlHelperMock.Verify(foo => foo.Link(EventsController.GetMultipleRouteName, It.IsAny()), Times.Exactly(1)); - urlHelperMock.Verify(foo => foo.Link(IncidentsController.GetSingleRouteName, It.IsAny()), Times.Exactly(1)); + urlHelperMock.Verify(foo => foo.Link(EventRoutesByIncident.GetSingle, It.IsAny()), Times.Exactly(1)); + urlHelperMock.Verify(foo => foo.Link(EventRoutesByIncident.PostSingle, It.IsAny()), Times.Exactly(1)); + urlHelperMock.Verify(foo => foo.Link(EventRoutesByIncident.GetMultiple, It.IsAny()), Times.Exactly(1)); + urlHelperMock.Verify(foo => foo.Link(IncidentRoutes.GetSingle, It.IsAny()), Times.Exactly(1)); - Assert.AreEqual(GetProperty(ids[0], "id"), "1"); - Assert.AreEqual(GetProperty(ids[1], "id"), ""); - Assert.AreEqual(GetProperty(ids[2], "id"), ""); - Assert.AreEqual(GetProperty(ids[3], "id"), "2"); - } - - [TestMethod] - public void GetHeaderValues_AssignsMetadataAndPaginationAsNull_WhenNoMetaDataPassedIn() - { - //Arrange - methods.Clear(); - ids.Clear(); - var pagination = new PaginationMetadata() - { - PageNumber = 2, - PageSize = 2, - TotalRecords = 10 - - }; - - var linksHeaderWithoutMetadata = new LinksHeader(null, null, urlHelperMock.Object, "EventsController", null, - null); - var linksHeaderWithMetadata = new LinksHeader(null, pagination, urlHelperMock.Object, "EventsController", null, - null); - - //Act - var linksWithoutMetadata = linksHeaderWithoutMetadata.GetHeaderValues(); - var linksWithMetadata = linksHeaderWithMetadata.GetHeaderValues(); - - //Assert - Assert.IsNull(linksWithoutMetadata.Metadata); - Assert.IsNull(linksWithoutMetadata.Links.Pagination); - - Assert.IsNotNull(linksWithMetadata.Metadata); - Assert.IsNotNull(linksWithMetadata.Links.Pagination); - urlHelperMock.Verify(foo => foo.Link("EventsController", It.IsAny()), Times.Exactly(2)); - - Assert.AreEqual(GetProperty(ids[0], "id"), ""); - Assert.AreEqual(GetProperty(ids[1], "id"), ""); + Assert.AreEqual(GetProperty(ids[0], "eventId"), "1"); + Assert.AreEqual(GetProperty(ids[1], "eventId"), "1"); + Assert.AreEqual(GetProperty(ids[2], "eventId"), "1"); + Assert.AreEqual(GetProperty(ids[3], "incidentId"), "2"); } } } diff --git a/test/Sia.Gateway.Tests/Requests/Incidents/GenerateLinksHeaderTestsForIncidents.cs b/test/Sia.Gateway.Tests/Requests/Incidents/GenerateLinksHeaderTestsForIncidents.cs index d6bfde0..f804a45 100644 --- a/test/Sia.Gateway.Tests/Requests/Incidents/GenerateLinksHeaderTestsForIncidents.cs +++ b/test/Sia.Gateway.Tests/Requests/Incidents/GenerateLinksHeaderTestsForIncidents.cs @@ -5,7 +5,7 @@ using Moq; using Sia.Gateway.Controllers; using Sia.Core.Protocol; using System.Collections.Generic; - +using Sia.Gateway.Links; namespace Sia.Gateway.Tests.Requests.Incidents { @@ -49,57 +49,21 @@ namespace Sia.Gateway.Tests.Requests.Incidents ids.Add(o); } ); - var incidentsController = new IncidentsController(null, null, urlHelperMock.Object); + var incidentLinksProvider = new IncidentLinksProvider(urlHelperMock.Object); // Act - incidentsController.CreateLinks("1", null, ""); + incidentLinksProvider.CreateLinks(1); // Assert - urlHelperMock.Verify(foo => foo.Link(IncidentsController.GetSingleRouteName, It.IsAny()), Times.Exactly(1)); - urlHelperMock.Verify(foo => foo.Link(IncidentsController.PostSingleRouteName, It.IsAny()), Times.Exactly(1)); - urlHelperMock.Verify(foo => foo.Link(IncidentsController.GetMultipleRouteName, It.IsAny()), Times.Exactly(1)); - urlHelperMock.Verify(foo => foo.Link(EventsController.GetMultipleRouteName, It.IsAny()), Times.Exactly(1)); + urlHelperMock.Verify(foo => foo.Link(IncidentRoutes.GetSingle, It.IsAny()), Times.Exactly(1)); + urlHelperMock.Verify(foo => foo.Link(IncidentRoutes.PostSingle, It.IsAny()), Times.Exactly(1)); + urlHelperMock.Verify(foo => foo.Link(IncidentRoutes.GetMultiple, It.IsAny()), Times.Exactly(1)); + urlHelperMock.Verify(foo => foo.Link(EventRoutesByIncident.GetMultiple, It.IsAny()), Times.Exactly(1)); - Assert.AreEqual(GetProperty(ids[0], "id"), "1"); - Assert.AreEqual(GetProperty(ids[1], "id"), ""); - Assert.AreEqual(GetProperty(ids[2], "id"), ""); + Assert.AreEqual(GetProperty(ids[0], "incidentId"), "1"); + Assert.AreEqual(GetProperty(ids[1], "incidentId"), "1"); + Assert.AreEqual(GetProperty(ids[2], "incidentId"), "1"); Assert.AreEqual(GetProperty(ids[3], "incidentId"), "1"); } - - [TestMethod] - public void GetHeaderValues_AssignsMetadataAndPaginationAsNull_WhenNoMetaDataPassedIn() - { - //Arrange - methods.Clear(); - ids.Clear(); - - var pagination = new PaginationMetadata() - { - PageNumber = 2, - PageSize = 2, - TotalRecords = 10 - - }; - - var linksHeaderWithoutMetadata = new LinksHeader(null, null, urlHelperMock.Object, "IncidentsController", null, - null); - var linksHeaderWithMetadata = new LinksHeader(null, pagination, urlHelperMock.Object, "IncidentsController", null, - null); - - //Act - var linksWithoutMetadata = linksHeaderWithoutMetadata.GetHeaderValues(); - var linksWithMetadata = linksHeaderWithMetadata.GetHeaderValues(); - - //Assert - Assert.IsNull(linksWithoutMetadata.Metadata); - Assert.IsNull(linksWithoutMetadata.Links.Pagination); - - Assert.IsNotNull(linksWithMetadata.Metadata); - Assert.IsNotNull(linksWithMetadata.Links.Pagination); - urlHelperMock.Verify(foo => foo.Link("IncidentsController", It.IsAny()), Times.Exactly(2)); - - Assert.AreEqual(GetProperty(ids[0], "id"), ""); - Assert.AreEqual(GetProperty(ids[1], "id"), ""); - } } }