Commit 633f0131 authored by Markus Esmann's avatar Markus Esmann
Browse files

added message handler

parent 1af52b9b
......@@ -11,6 +11,7 @@ using Botolution.ApiMsgLibrary.ApiObjects;
using MatchManager.IntegrationTests.Fixtures;
using Microsoft.Extensions.DependencyInjection;
using MatchManager.IntegrationTests.Authentication;
using Botolution.ApiMsgLibrary.ApiMessages.GetMatch;
using Botolution.ApiMsgLibrary.ApiMessages.GetMatches;
using Microsoft.AspNetCore.Authorization.Policy;
......@@ -89,11 +90,11 @@ namespace MatchManager.IntegrationTests
var contentString = await response.Content.ReadAsStringAsync();
var matchDetails = JsonConvert.DeserializeObject<MatchStorage>(contentString);
Assert.Equal(match.MatchId, matchDetails?.MatchId);
Assert.NotNull(matchDetails?.Parameters);
var matchDetails = JsonConvert.DeserializeObject<GetMatchResponse>(contentString);
Assert.Equal(match.MatchId, matchDetails?.Match.MatchId);
Assert.NotNull(matchDetails?.Match.Parameters);
var turnDetails = matchDetails?.Turns[0].ToObject<TurnParameter>();
var turnDetails = matchDetails?.Match.Turns[0].ToObject<TurnParameter>();
Assert.Equal("winner", turnDetails?.Winner);
}
}
......
......@@ -2,6 +2,7 @@ using MatchManager.Service;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization;
using Botolution.ApiMsgLibrary.ApiObjects;
using Botolution.ApiMsgLibrary.ApiMessages.GetMatch;
using Botolution.ApiMsgLibrary.ApiMessages.GetMatches;
namespace MatchManager.Controllers
......@@ -34,7 +35,7 @@ namespace MatchManager.Controllers
{
var matchOption = await _matchManagerService.GetMatch(matchId);
return matchOption.Match<IActionResult>(Ok, NotFound);
return matchOption.Match<IActionResult>((match) => Ok(new GetMatchResponse() { Match = match }), NotFound);
}
}
}
\ No newline at end of file
......@@ -13,6 +13,6 @@ namespace MatchManager.Database.Repository
Task<bool> ExistsAsync(Guid matchId);
void DeleteAsync(Guid matchId);
Task DeleteAsync(Guid matchId);
}
}
......@@ -8,6 +8,6 @@ namespace MatchManager.Database.Repository
Task<bool> InsertAsync(Turn turn);
void DeleteAllAsync(Guid matchId);
Task DeleteAllAsync(Guid matchId);
}
}
......@@ -90,7 +90,7 @@ namespace MatchManager.Database.Repository
}
}
public async void DeleteAsync(Guid matchId)
public async Task DeleteAsync(Guid matchId)
{
try
{
......
......@@ -52,7 +52,7 @@ namespace MatchManager.Database.Repository
}
}
public async void DeleteAllAsync(Guid matchId)
public async Task DeleteAllAsync(Guid matchId)
{
try
{
......
using MatchManager.Service;
using RabbitMQ.Client.Events;
using RabbitMQ.Client.Core.DependencyInjection.MessageHandlers;
namespace MatchManager.MessageQueue.MessageHandler
{
public abstract class AbstractMessageHandler : IMessageHandler
{
protected readonly ILogger Logger;
protected readonly IMatchManagerService MatchManagerService;
protected AbstractMessageHandler(ILogger<AbstractMessageHandler> logger, IMatchManagerService matchManagerService)
{
Logger = logger;
MatchManagerService = matchManagerService;
}
public abstract void Handle(BasicDeliverEventArgs eventArgs, string matchingRoute);
}
}
using Newtonsoft.Json;
using MatchManager.Service;
using RabbitMQ.Client.Events;
using RabbitMQ.Client.Core.DependencyInjection;
using Botolution.ApiMsgLibrary.ApiMessages.DeleteMatch;
using RabbitMQ.Client.Core.DependencyInjection.MessageHandlers;
namespace MatchManager.MessageQueue.MessageHandler
{
public class DeleteMatchMessageHandler : IMessageHandler
{
private readonly ILogger<PostMatchMessageHandler> _logger;
private readonly IMatchManagerService _matchManagerService;
public DeleteMatchMessageHandler(ILogger<PostMatchMessageHandler> logger, IMatchManagerService matchManagerService)
{
_logger = logger;
_matchManagerService = matchManagerService;
}
public void Handle(BasicDeliverEventArgs eventArgs, string matchingRoute)
{
var message = eventArgs.GetMessage();
var messageObject = JsonConvert.DeserializeObject<DeleteMatchRequest>(message);
_logger.LogInformation($"received delete match request: {message}");
_matchManagerService.DeleteMatch(messageObject?.MatchId ?? default);
}
}
}
using RabbitMQ.Client.Events;
using Newtonsoft.Json;
using MatchManager.Service;
using RabbitMQ.Client.Events;
using RabbitMQ.Client.Core.DependencyInjection;
using Botolution.ApiMsgLibrary.ApiMessages.PostMatch;
using RabbitMQ.Client.Core.DependencyInjection.MessageHandlers;
namespace MatchManager.MessageQueue.MessageHandler
{
public class PostMatchMessageHandler : IMessageHandler
{
private readonly ILogger<PostMatchMessageHandler> _logger;
private readonly IMatchManagerService _matchManagerService;
public PostMatchMessageHandler(ILogger<PostMatchMessageHandler> logger, IMatchManagerService matchManagerService)
{
_logger = logger;
_matchManagerService = matchManagerService;
}
public void Handle(BasicDeliverEventArgs eventArgs, string matchingRoute)
{
return;
var message = eventArgs.GetMessage();
var messageObject = JsonConvert.DeserializeObject<PostMatchRequest>(message);
_logger.LogInformation($"received post match request: {message}");
_matchManagerService.InsertMatch(messageObject?.Match);
}
}
}
using Newtonsoft.Json;
using MatchManager.Service;
using RabbitMQ.Client.Events;
using RabbitMQ.Client.Core.DependencyInjection;
using Botolution.ApiMsgLibrary.ApiMessages.PostTurn;
using RabbitMQ.Client.Core.DependencyInjection.MessageHandlers;
namespace MatchManager.MessageQueue.MessageHandler
{
public class PostTurnMessageHandler : IMessageHandler
{
private readonly ILogger<PostMatchMessageHandler> _logger;
private readonly IMatchManagerService _matchManagerService;
public PostTurnMessageHandler(ILogger<PostMatchMessageHandler> logger, IMatchManagerService matchManagerService)
{
_logger = logger;
_matchManagerService = matchManagerService;
}
public void Handle(BasicDeliverEventArgs eventArgs, string matchingRoute)
{
var message = eventArgs.GetMessage();
var messageObject = JsonConvert.DeserializeObject<PostTurnRequest>(message);
_logger.LogInformation($"received post turn request: {message}");
_matchManagerService.InsertTurn(messageObject?.Turn);
}
}
}
......@@ -7,11 +7,15 @@ namespace MatchManager.MessageQueue.Producing
public class AbstractProducingService
{
protected readonly ILogger Logger;
private readonly IQueueService _queueService;
private IQueueService? _queueService;
protected AbstractProducingService(IQueueService queueService, ILogger<ProducingService> logger)
protected AbstractProducingService(ILogger<ProducingService> logger)
{
Logger = logger;
}
public void SetQueueService(IQueueService queueService)
{
_queueService = queueService;
}
......
namespace MatchManager.MessageQueue.Producing
using Botolution.ApiMsgLibrary.ApiObjects;
namespace MatchManager.MessageQueue.Producing
{
public interface IProducingService
{
void PostMatchToFrontend(bool insertResult);
void SendPostMatchResponse(MatchDefinition? match, bool result);
void SendPostTurnResponse(TurnObject? turn, bool result);
}
}
using RabbitMQ.Client.Core.DependencyInjection.Services;
using Newtonsoft.Json;
using Botolution.ApiMsgLibrary.ApiObjects;
using Botolution.ApiMsgLibrary.ApiMessages.PostTurn;
using Botolution.ApiMsgLibrary.ApiMessages.PostMatch;
namespace MatchManager.MessageQueue.Producing
{
public class ProducingService : AbstractProducingService, IProducingService
{
public ProducingService(IQueueService queueService, ILogger<ProducingService> logger) : base(queueService, logger)
public ProducingService(ILogger<ProducingService> logger) : base(logger)
{ }
public void PostMatchToFrontend(bool insertResult)
public void SendPostMatchResponse(MatchDefinition? match, bool result)
{
Send("", "amq.topic", "MatchManagerQueue.MatchUpdate");
var sendMsg = new PostMatchResponse()
{
Match = match,
Result = result
};
Logger.LogInformation($"send post match response: {JsonConvert.SerializeObject(sendMsg)}");
Send(sendMsg, "amq.topic", $"MatchMakerQueue.PostMatchResponse");
if (result)
{
MatchListUpdate();
return;
}
ErrorUpdate("match insert failed");
}
public void SendPostTurnResponse(TurnObject? turn, bool result)
{
var sendMsg = new PostTurnResponse()
{
Turn = turn,
Result = result
};
Logger.LogInformation($"send post turn response: {JsonConvert.SerializeObject(sendMsg)}");
Send(sendMsg, "amq.topic", $"MatchMakerQueue.PostTurnResponse");
if (result)
{
TurnUpdate(turn!);
return;
}
ErrorUpdate($"turn insert of match {turn!.MatchId} failed");
}
private void MatchListUpdate()
{
Logger.LogInformation("match list update");
Send("", "amq.topic", "GuiQueue.MatchUpdate");
}
private void TurnUpdate(TurnObject turnObject)
{
Logger.LogInformation($"turn update: {JsonConvert.SerializeObject(turnObject)}");
Send(turnObject, "amq.topic", "GuiQueue.TurnUpdate");
}
private void ErrorUpdate(string message)
{
Logger.LogInformation($"error update: {message}");
Send(message, "amq.topic", "GuiQueue.ErrorUpdate");
}
}
}
using Serilog;
using ServiceHelper;
using IdentityModel;
using MatchManager.Service;
using MatchManager.Database.Context;
using MatchManager.Database.Settings;
using Microsoft.IdentityModel.Tokens;
using MatchManager.Database.Repository;
using MatchManager.MessageQueue.Consuming;
using MatchManager.MessageQueue.Producing;
using MatchManager.MessageQueue.MessageHandler;
using RabbitMQ.Client.Core.DependencyInjection;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Steeltoe.Extensions.Configuration.ConfigServer;
using IProducingService = MatchManager.MessageQueue.Producing.IProducingService;
// Setup web builder
var builder = WebApplication.CreateBuilder(args);
// Setup Logging
builder.Host.UseSerilog(SerilogElasticSinkLogger.Configure);
// Setup config service and configuration
builder.Host.AddConfigServer();
var configuration = builder.Configuration;
// Setup Authentication and Cors
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(options =>
{
options.Authority = "http://keycloak:8080/auth/realms/Botolution";
options.RequireHttpsMetadata = false;
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateAudience = false,
ValidateIssuer = false,
NameClaimType = JwtClaimTypes.Name,
};
});
builder.Services.AddCors(options =>
namespace MatchManager
{
options.AddDefaultPolicy(builder =>
public class Program
{
builder.AllowAnyOrigin();
builder.AllowAnyHeader();
builder.AllowAnyMethod();
});
});
// Setup RabbitMQ
var rabbitMqSection = configuration.GetSection("RabbitMq");
var exchangeSection = configuration.GetSection("RabbitMqExchange");
builder.Services.AddRabbitMqClient(rabbitMqSection)
.AddExchange("amq.topic", true, exchangeSection)
.AddMessageHandlerSingleton<PostMatchMessageHandler>("*.PostMatchRequest");
builder.Services.AddSingleton<IHostedService, ConsumingService>();
builder.Services.AddSingleton<IProducingService, ProducingService>();
// Add services to DI-Container
builder.Services.Configure<MatchDbSettings>(configuration.GetSection("MatchDatabase"));
builder.Services.AddSingleton<MatchDbContext>();
builder.Services.AddSingleton<ITurnRepository, TurnRepository>();
builder.Services.AddSingleton<IMatchRepository, MatchRepository>();
builder.Services.AddSingleton<IMatchManagerService, MatchManagerService>();
builder.Services.AddControllers();
builder.Services.AddAutoMapper(typeof(Program));
builder.Services.AddMvc().AddNewtonsoftJson();
builder.Services.AddEndpointsApiExplorer();
// Build app
var app = builder.Build();
app.UseHttpsRedirection();
app.UseAuthorization();
app.UseAuthentication();
app.MapControllers();
app.Run();
public partial class Program { }
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.AddConfigServer();
webBuilder.UseStartup<Startup>();
})
.UseSerilog(SerilogElasticSinkLogger.Configure);
}
}
......@@ -5,8 +5,14 @@ namespace MatchManager.Service
{
public interface IMatchManagerService
{
Task<IEnumerable<MatchDefinition>> GetMatchDefinitions();
Task InsertMatch(MatchStorage? match);
Task InsertTurn(TurnObject? turn);
Task<Option<MatchStorage>> GetMatch(Guid matchId);
Task<IEnumerable<MatchDefinition>> GetMatchDefinitions();
Task DeleteMatch(Guid matchId);
}
}
using AutoMapper;
using LanguageExt;
using Newtonsoft.Json;
using MatchManager.Database.Model;
using MatchManager.Database.Repository;
using Botolution.ApiMsgLibrary.ApiObjects;
using MatchManager.MessageQueue.Producing;
......@@ -51,5 +53,47 @@ namespace MatchManager.Service
return match;
}
public async Task InsertMatch(MatchStorage? match)
{
if (match != default && !await _matchRepository.ExistsAsync(match.MatchId))
{
var dbMatch = _mapper.Map<Match>(match);
bool insertResult = await _matchRepository.InsertAsync(dbMatch);
_producingService.SendPostMatchResponse(match, insertResult);
return;
}
_logger.LogError($"invalid match: {JsonConvert.SerializeObject(match)}");
_producingService.SendPostMatchResponse(match, false);
}
public async Task InsertTurn(TurnObject? turn)
{
if (turn != default && await _matchRepository.ExistsAsync(turn.MatchId))
{
var dbTurn = _mapper.Map<Turn>(turn);
bool insertResult = await _turnRepository.InsertAsync(dbTurn);
_producingService.SendPostTurnResponse(turn, insertResult);
return;
}
_logger.LogError($"invalid turn: {JsonConvert.SerializeObject(turn)}");
_producingService.SendPostTurnResponse(turn, false);
}
public async Task DeleteMatch(Guid matchId)
{
var deleteMatchTAsk = _matchRepository.DeleteAsync(matchId);
var deleteTurnTask = _turnRepository.DeleteAllAsync(matchId);
await Task.WhenAll(deleteMatchTAsk, deleteTurnTask);
}
}
}
using IdentityModel;
using MatchManager.Service;
using Microsoft.IdentityModel.Tokens;
using MatchManager.Database.Context;
using MatchManager.Database.Settings;
using MatchManager.Database.Repository;
using MatchManager.MessageQueue.Producing;
using MatchManager.MessageQueue.Consuming;
using MatchManager.MessageQueue.MessageHandler;
using RabbitMQ.Client.Core.DependencyInjection;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using RabbitMQ.Client.Core.DependencyInjection.Services;
using IProducingService = MatchManager.MessageQueue.Producing.IProducingService;
namespace MatchManager
{
public class Startup
{
private readonly IConfiguration _configuration;
public Startup(IConfiguration configuration)
{
_configuration = configuration;
}
public void ConfigureServices(IServiceCollection services)
{
// Setup Authentication and Cors
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(options =>
{
options.Authority = "http://keycloak:8080/auth/realms/Botolution";
options.RequireHttpsMetadata = false;
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateAudience = false,
ValidateIssuer = false,
NameClaimType = JwtClaimTypes.Name,
};
});
services.AddCors(options =>
{
options.AddDefaultPolicy(builder =>
{
builder.AllowAnyOrigin();
builder.AllowAnyHeader();
builder.AllowAnyMethod();
});
});
// Setup RabbitMQ
var rabbitMqSection = _configuration.GetSection("RabbitMq");
var exchangeSection = _configuration.GetSection("RabbitMqExchange");
services.AddRabbitMqClient(rabbitMqSection)
.AddExchange("amq.topic", true, exchangeSection)
.AddMessageHandlerSingleton<PostMatchMessageHandler>("*.PostMatchRequest")
.AddMessageHandlerSingleton<DeleteMatchMessageHandler>("*.DeleteMatchRequest")
.AddMessageHandlerSingleton<PostTurnMessageHandler>("*.PostTurnRequest");
services.AddSingleton<IHostedService, ConsumingService>();
services.AddSingleton<IProducingService, ProducingService>();
// Add services to DI-Container
services.Configure<MatchDbSettings>(_configuration.GetSection("MatchDatabase"));
services.AddSingleton<MatchDbContext>();
services.AddSingleton<ITurnRepository, TurnRepository>();
services.AddSingleton<IMatchRepository, MatchRepository>();
services.AddSingleton<IMatchManagerService, MatchManagerService>();
services.AddControllers();
services.AddAutoMapper(typeof(Startup));
services.AddMvc().AddNewtonsoftJson();
services.AddEndpointsApiExplorer();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IQueueService queueService, IProducingService producingService)
{
(producingService as AbstractProducingService)?.SetQueueService(queueService);
app.UseRouting();
app.UseCors();
app.UseAuthorization();
app.UseAuthentication();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment