Browse Source

Use the common services in the Ordering.SignalRHub project

davidfowl/common-services
David Fowler 1 year ago
committed by Reuben Bond
parent
commit
9743c83221
7 changed files with 88 additions and 242 deletions
  1. +42
    -0
      src/Services/Ordering/Ordering.SignalrHub/CustomExtensionMethods.cs
  2. +1
    -14
      src/Services/Ordering/Ordering.SignalrHub/GlobalUsings.cs
  3. +0
    -1
      src/Services/Ordering/Ordering.SignalrHub/NotificationHub.cs
  4. +1
    -0
      src/Services/Ordering/Ordering.SignalrHub/Ordering.SignalrHub.csproj
  5. +25
    -205
      src/Services/Ordering/Ordering.SignalrHub/Program.cs
  6. +2
    -17
      src/Services/Ordering/Ordering.SignalrHub/Properties/launchSettings.json
  7. +17
    -5
      src/Services/Ordering/Ordering.SignalrHub/appsettings.json

+ 42
- 0
src/Services/Ordering/Ordering.SignalrHub/CustomExtensionMethods.cs View File

@ -0,0 +1,42 @@
using Microsoft.AspNetCore.Http;
public static class CustomExtensionMethods
{
public static IServiceCollection AddSignalR(this IServiceCollection services, IConfiguration configuration)
{
if (configuration.GetConnectionString("redis") is string redisConnection)
{
// Add a redis health check
services.AddSignalR().AddStackExchangeRedis(redisConnection);
}
else
{
services.AddSignalR();
}
// Configure hub auth (grab the token from the query string)
return services.Configure<JwtBearerOptions>(JwtBearerDefaults.AuthenticationScheme, options =>
{
options.Events = new JwtBearerEvents
{
OnMessageReceived = context =>
{
var accessToken = context.Request.Query["access_token"];
var endpoint = context.HttpContext.GetEndpoint();
// Make sure this is a Hub endpoint.
if (endpoint?.Metadata.GetMetadata<HubMetadata>() is null)
{
return Task.CompletedTask;
}
context.Token = accessToken;
return Task.CompletedTask;
}
};
});
}
}

+ 1
- 14
src/Services/Ordering/Ordering.SignalrHub/GlobalUsings.cs View File

@ -1,29 +1,16 @@
global using HealthChecks.UI.Client;
global using Microsoft.AspNetCore.Authentication.JwtBearer;
global using Microsoft.AspNetCore.Authentication.JwtBearer;
global using Microsoft.AspNetCore.Authorization;
global using Microsoft.AspNetCore.Builder;
global using Microsoft.AspNetCore.Diagnostics.HealthChecks;
global using Microsoft.AspNetCore.Hosting;
global using Microsoft.AspNetCore.SignalR;
global using Microsoft.AspNetCore;
global using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
global using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
global using Microsoft.eShopOnContainers.BuildingBlocks.EventBus;
global using Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ;
global using Microsoft.eShopOnContainers.BuildingBlocks.EventBusServiceBus;
global using Microsoft.eShopOnContainers.Services.Ordering.SignalrHub.IntegrationEvents.Events;
global using Microsoft.eShopOnContainers.Services.Ordering.SignalrHub.IntegrationEvents;
global using Microsoft.Extensions.Configuration;
global using Microsoft.Extensions.DependencyInjection;
global using Microsoft.Extensions.Diagnostics.HealthChecks;
global using Microsoft.Extensions.Logging;
global using Microsoft.eShopOnContainers.Services.Ordering.SignalrHub.IntegrationEvents.EventHandling;
global using Microsoft.eShopOnContainers.Services.Ordering.SignalrHub;
global using RabbitMQ.Client;
global using System.Collections.Generic;
global using System.IdentityModel.Tokens.Jwt;
global using System.IO;
global using System.Reflection;
global using System.Threading.Tasks;
global using System;
global using Microsoft.Extensions.Hosting;

+ 0
- 1
src/Services/Ordering/Ordering.SignalrHub/NotificationHub.cs View File

@ -3,7 +3,6 @@
[Authorize]
public class NotificationsHub : Hub
{
public override async Task OnConnectedAsync()
{
await Groups.AddToGroupAsync(Context.ConnectionId, Context.User.Identity.Name);


+ 1
- 0
src/Services/Ordering/Ordering.SignalrHub/Ordering.SignalrHub.csproj View File

@ -28,6 +28,7 @@
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBusRabbitMQ\EventBusRabbitMQ.csproj" />
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBusServiceBus\EventBusServiceBus.csproj" />
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBus\EventBus.csproj" />
<ProjectReference Include="..\..\Services.Common\Services.Common.csproj" />
</ItemGroup>
</Project>

+ 25
- 205
src/Services/Ordering/Ordering.SignalrHub/Program.cs View File

@ -1,82 +1,21 @@
var builder = WebApplication.CreateBuilder(new WebApplicationOptions
{
Args = args,
ApplicationName = typeof(Program).Assembly.FullName,
ContentRootPath = Directory.GetCurrentDirectory()
});
builder.Configuration.SetBasePath(Directory.GetCurrentDirectory());
builder.Configuration.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
builder.Configuration.AddEnvironmentVariables();
builder.WebHost.CaptureStartupErrors(false);
builder.Services
.AddCustomHealthCheck(builder.Configuration)
.AddCors(options =>
{
options.AddPolicy("CorsPolicy",
builder => builder
.AllowAnyMethod()
.AllowAnyHeader()
.SetIsOriginAllowed((host) => true)
.AllowCredentials());
});
if (builder.Configuration.GetValue<string>("IsClusterEnv") == bool.TrueString)
{
builder.Services
.AddSignalR()
.AddStackExchangeRedis(builder.Configuration["SignalrStoreConnectionString"]);
}
else
{
builder.Services.AddSignalR();
}
using Services.Common;
if (builder.Configuration.GetValue<bool>("AzureServiceBusEnabled"))
{
builder.Services.AddSingleton<IServiceBusPersisterConnection>(sp =>
{
var serviceBusConnectionString = builder.Configuration["EventBusConnection"];
var builder = WebApplication.CreateBuilder(args);
var subscriptionClientName = builder.Configuration["SubscriptionClientName"];
builder.AddServiceDefaults();
return new DefaultServiceBusPersisterConnection(serviceBusConnectionString);
});
}
else
builder.Services.AddCors(options =>
{
builder.Services.AddSingleton<IRabbitMQPersistentConnection>(sp =>
{
var logger = sp.GetRequiredService<ILogger<DefaultRabbitMQPersistentConnection>>();
var factory = new ConnectionFactory()
{
HostName = builder.Configuration["EventBusConnection"],
DispatchConsumersAsync = true
};
if (!string.IsNullOrEmpty(builder.Configuration["EventBusUserName"]))
{
factory.UserName = builder.Configuration["EventBusUserName"];
}
if (!string.IsNullOrEmpty(builder.Configuration["EventBusPassword"]))
{
factory.Password = builder.Configuration["EventBusPassword"];
}
var retryCount = 5;
if (!string.IsNullOrEmpty(builder.Configuration["EventBusRetryCount"]))
{
retryCount = int.Parse(builder.Configuration["EventBusRetryCount"]);
}
options.AddPolicy("CorsPolicy",
builder => builder
.AllowAnyMethod()
.AllowAnyHeader()
.SetIsOriginAllowed((host) => true)
.AllowCredentials());
});
return new DefaultRabbitMQPersistentConnection(factory, logger, retryCount);
});
}
builder.Services.AddSignalR(builder.Configuration);
ConfigureAuthService(builder.Services, builder.Configuration);
RegisterEventBus(builder.Services, builder.Configuration);
builder.Services.AddSingleton<IIntegrationEventHandler<OrderStatusChangedToAwaitingValidationIntegrationEvent>, OrderStatusChangedToAwaitingValidationIntegrationEventHandler>();
builder.Services.AddSingleton<IIntegrationEventHandler<OrderStatusChangedToCancelledIntegrationEvent>, OrderStatusChangedToCancelledIntegrationEventHandler>();
builder.Services.AddSingleton<IIntegrationEventHandler<OrderStatusChangedToPaidIntegrationEvent>, OrderStatusChangedToPaidIntegrationEventHandler>();
@ -85,146 +24,27 @@ builder.Services.AddSingleton<IIntegrationEventHandler<OrderStatusChangedToStock
builder.Services.AddSingleton<IIntegrationEventHandler<OrderStatusChangedToSubmittedIntegrationEvent>, OrderStatusChangedToSubmittedIntegrationEventHandler>();
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Home/Error");
}
var pathBase = builder.Configuration["PATH_BASE"];
if (!string.IsNullOrEmpty(pathBase))
if (!await app.CheckHealthAsync())
{
app.UsePathBase(pathBase);
return;
}
app.UseRouting();
app.UseServiceDefaults();
app.UseCors("CorsPolicy");
app.UseAuthentication();
app.UseAuthorization();
app.MapHealthChecks("/hc", new HealthCheckOptions()
{
Predicate = _ => true,
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
});
app.MapHealthChecks("/liveness", new HealthCheckOptions
{
Predicate = r => r.Name.Contains("self")
});
app.MapHub<NotificationsHub>("/hub/notificationhub");
ConfigureEventBus(app);
await app.RunAsync();
void ConfigureEventBus(IApplicationBuilder app)
{
var eventBus = app.ApplicationServices.GetRequiredService<IEventBus>();
eventBus.Subscribe<OrderStatusChangedToAwaitingValidationIntegrationEvent, OrderStatusChangedToAwaitingValidationIntegrationEventHandler>();
eventBus.Subscribe<OrderStatusChangedToPaidIntegrationEvent, OrderStatusChangedToPaidIntegrationEventHandler>();
eventBus.Subscribe<OrderStatusChangedToStockConfirmedIntegrationEvent, OrderStatusChangedToStockConfirmedIntegrationEventHandler>();
eventBus.Subscribe<OrderStatusChangedToShippedIntegrationEvent, OrderStatusChangedToShippedIntegrationEventHandler>();
eventBus.Subscribe<OrderStatusChangedToCancelledIntegrationEvent, OrderStatusChangedToCancelledIntegrationEventHandler>();
eventBus.Subscribe<OrderStatusChangedToSubmittedIntegrationEvent, OrderStatusChangedToSubmittedIntegrationEventHandler>();
}
void ConfigureAuthService(IServiceCollection services, IConfiguration configuration)
{
// prevent from mapping "sub" claim to nameidentifier.
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("sub");
var identityUrl = configuration.GetValue<string>("IdentityUrl");
services.AddAuthentication("Bearer").AddJwtBearer(options =>
{
options.Authority = identityUrl;
options.RequireHttpsMetadata = false;
options.Audience = "orders.signalrhub";
options.TokenValidationParameters.ValidateAudience = false;
options.Events = new JwtBearerEvents
{
OnMessageReceived = context =>
{
var accessToken = context.Request.Query["access_token"];
var path = context.HttpContext.Request.Path;
if (!string.IsNullOrEmpty(accessToken) && (path.StartsWithSegments("/hub/notificationhub")))
{
context.Token = accessToken;
}
return Task.CompletedTask;
}
};
});
services.AddAuthorization(options =>
{
options.AddPolicy("ApiScope", policy =>
{
policy.RequireAuthenticatedUser();
policy.RequireClaim("scope", "orders.signalrhub");
});
});
}
void RegisterEventBus(IServiceCollection services, IConfiguration configuration)
{
if (configuration.GetValue<bool>("AzureServiceBusEnabled"))
{
services.AddSingleton<IEventBus, EventBusServiceBus>(sp =>
{
var serviceBusPersisterConnection = sp.GetRequiredService<IServiceBusPersisterConnection>();
var logger = sp.GetRequiredService<ILogger<EventBusServiceBus>>();
var eventBusSubcriptionsManager = sp.GetRequiredService<IEventBusSubscriptionsManager>();
string subscriptionName = configuration["SubscriptionClientName"];
return new EventBusServiceBus(serviceBusPersisterConnection, logger,
eventBusSubcriptionsManager, sp, subscriptionName);
});
}
else
{
services.AddSingleton<IEventBus, EventBusRabbitMQ>(sp =>
{
var subscriptionClientName = configuration["SubscriptionClientName"];
var rabbitMQPersistentConnection = sp.GetRequiredService<IRabbitMQPersistentConnection>();
var logger = sp.GetRequiredService<ILogger<EventBusRabbitMQ>>();
var eventBusSubcriptionsManager = sp.GetRequiredService<IEventBusSubscriptionsManager>();
var retryCount = 5;
if (!string.IsNullOrEmpty(configuration["EventBusRetryCount"]))
{
retryCount = int.Parse(configuration["EventBusRetryCount"]);
}
return new EventBusRabbitMQ(rabbitMQPersistentConnection, logger, sp, eventBusSubcriptionsManager, subscriptionClientName, retryCount);
});
}
services.AddSingleton<IEventBusSubscriptionsManager, InMemoryEventBusSubscriptionsManager>();
}
public static class CustomExtensionMethods
{
public static IServiceCollection AddCustomHealthCheck(this IServiceCollection services, IConfiguration configuration)
{
var hcBuilder = services.AddHealthChecks();
app.MapHub<NotificationsHub>("/hub/notificationhub");
hcBuilder.AddCheck("self", () => HealthCheckResult.Healthy());
var eventBus = app.Services.GetRequiredService<IEventBus>();
if (configuration.GetValue<bool>("AzureServiceBusEnabled"))
{
hcBuilder
.AddAzureServiceBusTopic(
configuration["EventBusConnection"],
topicName: "eshop_event_bus",
name: "signalr-servicebus-check",
tags: new string[] { "servicebus" });
}
else
{
hcBuilder
.AddRabbitMQ(
$"amqp://{configuration["EventBusConnection"]}",
name: "signalr-rabbitmqbus-check",
tags: new string[] { "rabbitmqbus" });
}
eventBus.Subscribe<OrderStatusChangedToAwaitingValidationIntegrationEvent, OrderStatusChangedToAwaitingValidationIntegrationEventHandler>();
eventBus.Subscribe<OrderStatusChangedToPaidIntegrationEvent, OrderStatusChangedToPaidIntegrationEventHandler>();
eventBus.Subscribe<OrderStatusChangedToStockConfirmedIntegrationEvent, OrderStatusChangedToStockConfirmedIntegrationEventHandler>();
eventBus.Subscribe<OrderStatusChangedToShippedIntegrationEvent, OrderStatusChangedToShippedIntegrationEventHandler>();
eventBus.Subscribe<OrderStatusChangedToCancelledIntegrationEvent, OrderStatusChangedToCancelledIntegrationEventHandler>();
eventBus.Subscribe<OrderStatusChangedToSubmittedIntegrationEvent, OrderStatusChangedToSubmittedIntegrationEventHandler>();
return services;
}
}
await app.RunAsync();

+ 2
- 17
src/Services/Ordering/Ordering.SignalrHub/Properties/launchSettings.json View File

@ -1,27 +1,12 @@
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:51311/",
"sslPort": 0
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"Ordering.SignalrHub": {
"commandName": "Project",
"launchBrowser": true,
"applicationUrl": "http://localhost:5223/",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "http://localhost:51312/"
}
}
}
}

+ 17
- 5
src/Services/Ordering/Ordering.SignalrHub/appsettings.json View File

@ -1,7 +1,19 @@
{
"IdentityUrl": "http://localhost:5105",
"AzureServiceBusEnabled": false,
"SubscriptionClientName": "Ordering.signalrhub",
"EventBusRetryCount": 5,
"EventBusConnection": "localhost"
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"Identity": {
"Audience": "orders.signalrhub",
"Url": "http://localhost:5105"
},
"EventBus": {
"SubscriptionClientName": "Ordering.signalrhub",
"RetryCount": 5
},
"ConnectionStrings": {
"EventBus": "localhost"
}
}

Loading…
Cancel
Save