Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Alpha 0.3.6 - Command Serializer for ICommands to be instantiated from ServiceBusMessages #5

Merged
merged 7 commits into from
Jun 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions SengokuProvider.API/Controllers/CoreController.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using Microsoft.AspNetCore.Mvc;
using SengokuProvider.Library.Models.Common;
using SengokuProvider.Library.Services.Common;
using SengokuProvider.Library.Services.Common.Interfaces;

namespace SengokuProvider.API.Controllers
{
Expand Down
7 changes: 3 additions & 4 deletions SengokuProvider.API/Controllers/PlayerController.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
using SengokuProvider.Library.Models.Events;
using SengokuProvider.Library.Models.Players;
using SengokuProvider.Library.Services.Common;
using SengokuProvider.Library.Services.Players;
Expand All @@ -27,16 +26,16 @@
[HttpPost("IntakePlayersByTournament")]
public async Task<IActionResult> IntakePlayersByTournament([FromBody] IntakePlayersByTournamentCommand command)
{
if(command == null)
if (command == null)
{
_log.LogError("Command cannot be empty or null");
return new BadRequestObjectResult("Command cannot be null") { StatusCode = StatusCodes.Status400BadRequest };
}

var parsedRequest = await _commandProcessor.ParseRequest(command);
if(!string.IsNullOrEmpty(parsedRequest.Response) && parsedRequest.Response.Equals("BadRequest"))
if (!string.IsNullOrEmpty(parsedRequest.Response) && parsedRequest.Response.Equals("BadRequest"))
{
_log.LogError($"REquest parsing failed: {parsedRequest.Response}");
_log.LogError($"Request parsing failed: {parsedRequest.Response}");
Dismissed Show dismissed Hide dismissed
return new BadRequestObjectResult(parsedRequest.Response);
}

Expand Down
20 changes: 17 additions & 3 deletions SengokuProvider.API/Program.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using Azure.Messaging.ServiceBus;
using GraphQL.Client.Http;
using GraphQL.Client.Serializer.Newtonsoft;
using SengokuProvider.Library.Services.Common;
using SengokuProvider.Library.Services.Common.Interfaces;
using SengokuProvider.Library.Services.Events;
using SengokuProvider.Library.Services.Legends;
using SengokuProvider.Library.Services.Players;
Expand All @@ -13,11 +15,20 @@
var connectionString = builder.Configuration["ConnectionStrings:AlexandriaConnectionString"];
var graphQLUrl = builder.Configuration["GraphQLSettings:Endpoint"];
var bearerToken = builder.Configuration["GraphQLSettings:Bearer"];
var serviceBusConnection = builder.Configuration["ServiceBusSettings:AzureWebJobsServiceBus"];

// Add services to the container.
//Singletons
builder.Services.AddTransient<CommandProcessor>();
builder.Services.AddSingleton<IntakeValidator>();
builder.Services.AddSingleton<RequestThrottler>();
builder.Services.AddSingleton(provider => { return new ServiceBusClient(serviceBusConnection); });

//Scopes
builder.Services.AddScoped<IAzureBusApiService, AzureBusApiService>(provider =>
{
var client = provider.GetService<ServiceBusClient>();
return new AzureBusApiService(client);
});
builder.Services.AddScoped(provider => new GraphQLHttpClient(graphQLUrl, new NewtonsoftJsonSerializer())
{
HttpClient = { DefaultRequestHeaders = { Authorization = new AuthenticationHeaderValue("Bearer", bearerToken) } }
Expand Down Expand Up @@ -53,15 +64,18 @@
});
builder.Services.AddScoped<IPlayerIntakeService, PlayerIntakeService>(provider =>
{
var configuration = provider.GetService<IConfiguration>();
var playerQueryService = provider.GetService<IPlayerQueryService>();
var legendQueryService = provider.GetService<ILegendQueryService>();
return new PlayerIntakeService(connectionString, playerQueryService, legendQueryService);
var serviceBus = provider.GetService<IAzureBusApiService>();
return new PlayerIntakeService(connectionString, configuration, playerQueryService, legendQueryService, serviceBus);
});
builder.Services.AddScoped<IEventQueryService, EventQueryService>(provider =>
{
var intakeValidator = provider.GetService<IntakeValidator>();
var graphQlClient = provider.GetService<GraphQLHttpClient>();
return new EventQueryService(connectionString, graphQlClient, intakeValidator);
var throttler = provider.GetService<RequestThrottler>();
return new EventQueryService(connectionString, graphQlClient, intakeValidator, throttler);
});

builder.Services.AddControllers();
Expand Down
2 changes: 1 addition & 1 deletion SengokuProvider.API/SengokuProvider.API.sln
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SengokuProvider.Worker", ".
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "API", "API", "{BD491C8C-EC45-45C2-861A-537CD6B6DAF0}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Worker", "Worker", "{9B18D3C2-D611-485D-8FA2-036AC6FE0CFC}"
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Workers", "Workers", "{9B18D3C2-D611-485D-8FA2-036AC6FE0CFC}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Libraries", "Libraries", "{FACDBAB8-DDD1-4C7C-B0EA-40EE5FC647D0}"
EndProject
Expand Down
4 changes: 2 additions & 2 deletions SengokuProvider.Library/Models/Common/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ public struct QueryConstants
public const string DatePriority = @"
SELECT
a.address, a.latitude, a.longitude,
e.event_name, e.event_description, e.region, e.start_time, e.end_time, e.link_id, e.closing_registration_date, e.registration_open,
e.event_name, e.event_description, e.region, e.start_time, e.end_time, e.link_id, e.closing_registration_date, e.registration_open, e.url_slug, e.online_tournament,
SQRT(
POW(a.longitude - @ReferenceLongitude, 2) + POW(a.latitude - @ReferenceLatitude, 2)
) AS distance
Expand All @@ -27,7 +27,7 @@ distance ASC
public const string DistancePriority = @"
SELECT
a.address, a.latitude, a.longitude,
e.event_name, e.event_description, e.region, e.start_time, e.end_time, e.link_id, e.closing_registration_date, e.registration_open,
e.event_name, e.event_description, e.region, e.start_time, e.end_time, e.link_id, e.closing_registration_date, e.registration_open, e.url_slug, e.online_tournament,
SQRT(
POW(a.longitude - @ReferenceLongitude, 2) + POW(a.latitude - @ReferenceLatitude, 2)
) AS distance
Expand Down
3 changes: 2 additions & 1 deletion SengokuProvider.Library/Models/Events/EventData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ public class EventData
public DateTime? EndTime { get; set; }
public DateTime? ClosingRegistration { get; set; }
public bool? IsRegistrationOpen { get; set; }
public bool IsOnline { get; set; }
public bool? IsOnline { get; set; }
public string? UrlSlug { get; set; }
public required DateTime LastUpdate { get; set; }
}
}
2 changes: 2 additions & 0 deletions SengokuProvider.Library/Models/Events/EventQueryResult.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,7 @@ public class AddressEventResult
public DateTime EndTime { get; set; }
public required int LinkId { get; set; }
public required DateTime ClosingRegistration { get; set; }
public required string UrlSlug { get; set; }
public required bool IsOnline { get; set; }
}
}
23 changes: 18 additions & 5 deletions SengokuProvider.Library/Models/Legends/LegendCommands.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
using SengokuProvider.Library.Models.Common;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SengokuProvider.Library.Models.Legends
{
Expand All @@ -18,4 +13,22 @@ public bool Validate()
return false;
}
}
public class OnboardLegendsByPlayerCommand : ICommand
{
public required int PlayerId { get; set; }
public required string GamerTag { get; set; }
public string? Response { get; set; }
public required LegendCommandRegistry Topic { get; set; }

public bool Validate()
{
throw new NotImplementedException();
}
}
public enum LegendCommandRegistry
{
UpdateLegend,
OnboardLegendsByPlayerData,
IntakeLegendsByTournament,
}
}
11 changes: 11 additions & 0 deletions SengokuProvider.Library/Models/Legends/OnboardReceivedData.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using SengokuProvider.Library.Models.Common;

namespace SengokuProvider.Library.Models.Legends
{
public class OnboardReceivedData : IServiceBusCommand
{
public required LegendCommandRegistry Topic { get; set; }
public required ICommand Command { get; set; }
public required MessagePriority MessagePriority { get; set; }
}
}
2 changes: 2 additions & 0 deletions SengokuProvider.Library/SengokuProvider.Library.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Azure.Messaging.ServiceBus" Version="7.17.5" />
<PackageReference Include="Dapper" Version="2.1.35" />
<PackageReference Include="GeoAPI" Version="1.7.5" />
<PackageReference Include="GraphQL.Client" Version="6.0.5" />
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="8.0.0" />
<PackageReference Include="NetTopologySuite" Version="2.5.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Npgsql" Version="8.0.3" />
Expand Down
34 changes: 34 additions & 0 deletions SengokuProvider.Library/Services/Common/AzureBusApiService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using Azure.Messaging.ServiceBus;
using SengokuProvider.Library.Services.Common.Interfaces;

namespace SengokuProvider.Library.Services.Common
{
public class AzureBusApiService : IAzureBusApiService
{
private readonly ServiceBusClient _client;
public AzureBusApiService(ServiceBusClient client)
{
_client = client;
}
public async Task<bool> SendBatchAsync(string? queueName, string messages)
{
if (string.IsNullOrEmpty(queueName) || string.IsNullOrEmpty(messages)) return false;

ServiceBusSender sender = _client.CreateSender(queueName);
ServiceBusMessageBatch batch = await sender.CreateMessageBatchAsync();

ServiceBusMessage busMessage = new ServiceBusMessage(messages)
{
ContentType = "application/json",
};
if (!batch.TryAddMessage(busMessage))
{
throw new InvalidOperationException("Message too large for batch.");
}
await sender.SendMessagesAsync(batch);
await sender.DisposeAsync();

return true;
}
}
}
31 changes: 31 additions & 0 deletions SengokuProvider.Library/Services/Common/CommandSerializer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using SengokuProvider.Library.Models.Common;
using SengokuProvider.Library.Models.Events;
using SengokuProvider.Library.Models.Legends;

namespace SengokuProvider.Library.Services.Common
{
public class CommandSerializer : JsonConverter<ICommand>
{
public override ICommand? ReadJson(JsonReader reader, Type objectType, ICommand? existingValue, bool hasExistingValue, JsonSerializer serializer)
{
var jsonObject = JObject.Load(reader);
var topic = jsonObject["Topic"]?.ToObject<string>();

ICommand? command = topic switch
{
nameof(LegendCommandRegistry.OnboardLegendsByPlayerData) => jsonObject.ToObject<OnboardLegendsByPlayerCommand>(),
nameof(EventCommandRegistry.IntakeEventsByTournament) => jsonObject.ToObject<IntakeEventsByTournamentIdCommand>(),
_ => throw new NotSupportedException($"Command topic '{topic}' is not supported")
};

return command;
}

public override void WriteJson(JsonWriter writer, ICommand? value, JsonSerializer serializer)
{
serializer.Serialize(writer, value);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Dapper;
using Npgsql;
using SengokuProvider.Library.Services.Common.Interfaces;

namespace SengokuProvider.Library.Services.Common
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace SengokuProvider.Library.Services.Common.Interfaces
{
public interface IAzureBusApiService
{
public Task<bool> SendBatchAsync(string? queueName, string messages);
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace SengokuProvider.Library.Services.Common
namespace SengokuProvider.Library.Services.Common.Interfaces
{
public interface ICommonDatabaseService
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ public class RequestThrottler
{
private bool _isPaused = false;
private readonly SemaphoreSlim _pauseSemaphore = new SemaphoreSlim(1, 1);
private readonly TimeSpan _pauseDuration = TimeSpan.FromSeconds(3);
private readonly TimeSpan _pauseDuration = TimeSpan.FromSeconds(5);

public async Task WaitIfPaused()
{
Expand Down
17 changes: 10 additions & 7 deletions SengokuProvider.Library/Services/Events/EventIntakeService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ INSERT INTO addresses (address, latitude, longitude)
var result = await command.ExecuteScalarAsync();
if (result != null && int.TryParse(result.ToString(), out int addressId) && addressId > 0)
{
_addressCache.TryAdd(newAddress.Address, addressId);
_addressCache.TryAdd(newAddress.Address ?? string.Empty, addressId);
newAddress.Id = addressId;
totalSuccess++;
}
Expand Down Expand Up @@ -263,8 +263,8 @@ ON CONFLICT (link_id) DO UPDATE SET
online_tournament = EXCLUDED.online_tournament;";
using (var command = new NpgsqlCommand(createNewInsertCommand, conn))
{
command.Parameters.AddWithValue("@Event_Name", newEvent.EventName);
command.Parameters.AddWithValue(@"Event_Description", newEvent.EventDescription);
command.Parameters.AddWithValue("@Event_Name", newEvent.EventName ?? string.Empty);
command.Parameters.AddWithValue(@"Event_Description", newEvent.EventDescription ?? string.Empty);
command.Parameters.AddWithValue(@"Region", newEvent.Region);
command.Parameters.AddWithValue("@Address_Id", newEvent.AddressID);
command.Parameters.AddWithValue(@"Start_Time", newEvent.StartTime);
Expand Down Expand Up @@ -330,8 +330,8 @@ private async Task<Tuple<int, int>> ProcessEventData(EventGraphQLResult queryDat
// Update cache with new IDs
foreach (var address in addresses)
{
_addressCache[address.Address] = address.Id;
addressMap[address.Address] = address.Id;
_addressCache[address.Address ?? string.Empty] = address.Id;
addressMap[address.Address ?? string.Empty] = address.Id;
}

// Process events after all addresses are handled
Expand Down Expand Up @@ -365,7 +365,7 @@ private async Task<Tuple<int, int>> ProcessEventData(EventGraphQLResult queryDat
}
private async Task<int> GetRegionId(string? city)
{
var queryResult = await _queryService.QueryRegion(new GetRegionCommand { QueryParameter = new Tuple<string, string>("name", city) });
var queryResult = await _queryService.QueryRegion(new GetRegionCommand { QueryParameter = new Tuple<string, string>("name", city ?? string.Empty) });
return queryResult?.Id ?? 1;
}
public async Task<int> IntakeNewRegion(AddressData addressData)
Expand All @@ -377,7 +377,10 @@ public async Task<int> IntakeNewRegion(AddressData addressData)

var tempCity = addressSplit[1].Trim();
int tempZipCode = 0;
if (addressSplit.Length > 2) { tempZipCode = int.Parse(addressSplit[2].Trim().Split(" ")[1]); }
if (addressSplit.Length > 2)
{
if (!int.TryParse(addressSplit[2].Trim().Split(" ")[1], out tempZipCode)) return 0;
}

var cityQuery = new GetRegionCommand { QueryParameter = new Tuple<string, string>("name", tempCity) };
var result = await VerifyRegion(cityQuery);
Expand Down
Loading