diff --git a/src/CommunicationControl/DevOpsProject.CommunicationControl.Logic/Services/CommunicationControlService.cs b/src/CommunicationControl/DevOpsProject.CommunicationControl.Logic/Services/CommunicationControlService.cs index 2e1a753..5e0b449 100644 --- a/src/CommunicationControl/DevOpsProject.CommunicationControl.Logic/Services/CommunicationControlService.cs +++ b/src/CommunicationControl/DevOpsProject.CommunicationControl.Logic/Services/CommunicationControlService.cs @@ -37,7 +37,7 @@ namespace DevOpsProject.CommunicationControl.Logic.Services bool isSuccessfullyDisconnected = false; try { - var result = await _redisService.DeleteAsync(hiveId); + var result = await _redisService.DeleteAsync(GetHiveKey(hiveId)); isSuccessfullyDisconnected = result; return result; } @@ -65,9 +65,11 @@ namespace DevOpsProject.CommunicationControl.Logic.Services public async Task ConnectHive(HiveModel model) { + _logger.LogInformation("Trying to connect Hive: {@model}", model); bool result = await _redisService.SetAsync(GetHiveKey(model.HiveID), model); if (result) { + _logger.LogInformation("Successfully connected Hive: {@model}", model); var operationalArea = _spatialService.GetHiveOperationalArea(model); await _messageBus.Publish(new HiveConnectedMessage { @@ -80,46 +82,41 @@ namespace DevOpsProject.CommunicationControl.Logic.Services } else { - await _messageBus.Publish(new HiveConnectedMessage - { - HiveID = model.HiveID, - Hive = model, - IsSuccessfullyConnected = result - }); + _logger.LogError("Failed to connect Hive: {@model}", model); throw new HiveConnectionException($"Failed to connect hive for HiveId: {model.HiveID}"); } } + public async Task IsHiveConnected(string hiveId) + { + string hiveKey = GetHiveKey(hiveId); + return await _redisService.CheckIfKeyExists(hiveKey); + } + public async Task AddTelemetry(HiveTelemetryModel model) { string hiveKey = GetHiveKey(model.HiveID); - bool hiveExists = await _redisService.CheckIfKeyExists(hiveKey); - if (hiveExists) + bool result = await _redisService.UpdateAsync(hiveKey, (HiveModel hive) => { - bool result = await _redisService.UpdateAsync(hiveKey, (HiveModel hive) => - { - hive.Telemetry = model; - }); + hive.Telemetry = model; + }); - await _messageBus.Publish(new TelemetrySentMessage - { - HiveID = model.HiveID, - Telemetry = model, - IsSuccessfullySent = result - }); - return model.Timestamp; + if (result) + { + _logger.LogInformation("Telemetry updated for HiveID: {hiveId}. Updated telemetry timestamp: {timestamp}", model.HiveID, model.Timestamp); } else { - await _messageBus.Publish(new TelemetrySentMessage - { - HiveID = model.HiveID, - Telemetry = model, - IsSuccessfullySent = false - }); - throw new HiveNotFoundException($"Hive not found for id: {model.HiveID}"); + _logger.LogError("Failed to update Telemetry - Redis update issue. HiveID: {hiveId}, Telemetry model: {@telemetry}", model.HiveID, model); } + await _messageBus.Publish(new TelemetrySentMessage + { + HiveID = model.HiveID, + Telemetry = model, + IsSuccessfullySent = result + }); + return model.Timestamp; } public async Task SendHiveControlSignal(string hiveId, Location destination) @@ -127,33 +124,40 @@ namespace DevOpsProject.CommunicationControl.Logic.Services var hive = await GetHiveModel(hiveId); if (hive == null) { - throw new Exception($"Hive control signal error: cannot find hive with id: {hiveId}"); + _logger.LogError("Sending Hive Control signal: Hive not found for HiveID: {hiveId}", hiveId); + return null; } bool isSuccessfullySent = false; - + string hiveMindPath = _communicationControlConfiguration.CurrentValue.HiveMindPath; + var command = new MoveHiveMindCommand + { + CommandType = State.Move, + Location = destination, + Timestamp = DateTime.Now + }; try { - var command = new MoveHiveMindCommand - { - CommandType = State.Move, - Location = destination, - Timestamp = DateTime.Now - }; - - var result = await _hiveHttpClient.SendHiveControlCommandAsync(_communicationControlConfiguration.CurrentValue.RequestScheme, - hive.HiveIP, hive.HivePort, _communicationControlConfiguration.CurrentValue.HiveMindPath, command); + var result = await _hiveHttpClient.SendHiveControlCommandAsync(hive.HiveSchema, hive.HiveIP, hive.HivePort, hiveMindPath, command); isSuccessfullySent = true; return result; } finally { - await _messageBus.Publish(new MoveHiveMessage + if (isSuccessfullySent) { - IsSuccessfullySent = isSuccessfullySent, - Destination = destination, - HiveID = hiveId - }); + await _messageBus.Publish(new MoveHiveMessage + { + IsSuccessfullySent = isSuccessfullySent, + Destination = destination, + HiveID = hiveId + }); + } + else + { + _logger.LogError("Failed to send control command for Hive: {@hive}, path: {path}, \n Command: {@command}", hive, hiveMindPath, command); + } + } } diff --git a/src/CommunicationControl/DevOpsProject.CommunicationControl.Logic/Services/Interfaces/ICommunicationControlService.cs b/src/CommunicationControl/DevOpsProject.CommunicationControl.Logic/Services/Interfaces/ICommunicationControlService.cs index 86445cf..6569990 100644 --- a/src/CommunicationControl/DevOpsProject.CommunicationControl.Logic/Services/Interfaces/ICommunicationControlService.cs +++ b/src/CommunicationControl/DevOpsProject.CommunicationControl.Logic/Services/Interfaces/ICommunicationControlService.cs @@ -8,6 +8,7 @@ namespace DevOpsProject.CommunicationControl.Logic.Services.Interfaces Task GetHiveModel(string hiveId); Task> GetAllHives(); Task ConnectHive(HiveModel model); + Task IsHiveConnected(string hiveId); Task AddTelemetry(HiveTelemetryModel model); Task SendHiveControlSignal(string hiveId, Location destination); } diff --git a/src/CommunicationControl/DevOpsProject.HiveMind.API/DI/ApiVersioningConfiguration.cs b/src/CommunicationControl/DevOpsProject.HiveMind.API/DI/ApiVersioningConfiguration.cs new file mode 100644 index 0000000..8ad0c38 --- /dev/null +++ b/src/CommunicationControl/DevOpsProject.HiveMind.API/DI/ApiVersioningConfiguration.cs @@ -0,0 +1,29 @@ +using DevOpsProject.HiveMind.Logic.Services.Interfaces; +using DevOpsProject.HiveMind.Logic.Services; +using Asp.Versioning; + +namespace DevOpsProject.HiveMind.API.DI +{ + public static class ApiVersioningConfiguration + { + public static IServiceCollection AddApiVersioningConfiguration(this IServiceCollection serviceCollection) + { + serviceCollection.AddApiVersioning(options => + { + options.DefaultApiVersion = new ApiVersion(1, 0); + options.AssumeDefaultVersionWhenUnspecified = true; + options.ReportApiVersions = true; + options.ApiVersionReader = ApiVersionReader.Combine( + new UrlSegmentApiVersionReader(), + new HeaderApiVersionReader("X-Api-Version") + ); + }).AddApiExplorer(options => + { + options.GroupNameFormat = "'v'VVV"; + options.SubstituteApiVersionInUrl = true; + }); + + return serviceCollection; + } + } +} diff --git a/src/CommunicationControl/DevOpsProject.HiveMind.API/DI/CorsConfiguration.cs b/src/CommunicationControl/DevOpsProject.HiveMind.API/DI/CorsConfiguration.cs new file mode 100644 index 0000000..6544b85 --- /dev/null +++ b/src/CommunicationControl/DevOpsProject.HiveMind.API/DI/CorsConfiguration.cs @@ -0,0 +1,21 @@ +namespace DevOpsProject.HiveMind.API.DI +{ + public static class CorsConfiguration + { + public static IServiceCollection AddCorsConfiguration(this IServiceCollection serviceCollection, string corsPolicyName) + { + serviceCollection.AddCors(options => + { + options.AddPolicy(name: corsPolicyName, + policy => + { + policy.AllowAnyOrigin() //SECURITY WARNING ! Never allow all origins + .AllowAnyMethod() + .AllowAnyHeader(); + }); + }); + + return serviceCollection; + } + } +} diff --git a/src/CommunicationControl/DevOpsProject.HiveMind.API/DI/HttpClientsConfiguration.cs b/src/CommunicationControl/DevOpsProject.HiveMind.API/DI/HttpClientsConfiguration.cs new file mode 100644 index 0000000..c1ffa3e --- /dev/null +++ b/src/CommunicationControl/DevOpsProject.HiveMind.API/DI/HttpClientsConfiguration.cs @@ -0,0 +1,21 @@ +using DevOpsProject.Shared.Clients; +using Polly.Extensions.Http; +using Polly; + +namespace DevOpsProject.HiveMind.API.DI +{ + public static class HttpClientsConfiguration + { + public static IServiceCollection AddHttpClientsConfiguration(this IServiceCollection serviceCollection) + { + var communicationControlTelemetryPolicy = HttpPolicyExtensions + .HandleTransientHttpError() + .WaitAndRetryAsync(3, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt))); + serviceCollection.AddHttpClient() + .AddPolicyHandler(communicationControlTelemetryPolicy); + serviceCollection.AddHttpClient("HiveConnectClient"); + + return serviceCollection; + } + } +} diff --git a/src/CommunicationControl/DevOpsProject.HiveMind.API/DI/OptionsConfiguration.cs b/src/CommunicationControl/DevOpsProject.HiveMind.API/DI/OptionsConfiguration.cs new file mode 100644 index 0000000..701b349 --- /dev/null +++ b/src/CommunicationControl/DevOpsProject.HiveMind.API/DI/OptionsConfiguration.cs @@ -0,0 +1,14 @@ +using DevOpsProject.Shared.Configuration; + +namespace DevOpsProject.HiveMind.API.DI +{ + public static class OptionsConfiguration + { + public static IServiceCollection AddOptionsConfiguration(this IServiceCollection serviceCollection, IConfiguration configuration) + { + serviceCollection.Configure(configuration.GetSection("CommunicationConfiguration")); + + return serviceCollection; + } + } +} diff --git a/src/CommunicationControl/DevOpsProject.HiveMind.API/Program.cs b/src/CommunicationControl/DevOpsProject.HiveMind.API/Program.cs index 65003c5..013786e 100644 --- a/src/CommunicationControl/DevOpsProject.HiveMind.API/Program.cs +++ b/src/CommunicationControl/DevOpsProject.HiveMind.API/Program.cs @@ -3,13 +3,10 @@ using Asp.Versioning.Builder; using DevOpsProject.HiveMind.API.DI; using DevOpsProject.HiveMind.API.Middleware; using DevOpsProject.HiveMind.Logic.Services.Interfaces; -using DevOpsProject.Shared.Clients; using DevOpsProject.Shared.Configuration; using DevOpsProject.Shared.Models; using Microsoft.Extensions.Options; using Microsoft.OpenApi.Models; -using Polly; -using Polly.Extensions.Http; using Serilog; var builder = WebApplication.CreateBuilder(args); @@ -19,52 +16,23 @@ builder.Host.UseSerilog((context, services, loggerConfig) => .ReadFrom.Services(services) .Enrich.FromLogContext()); -builder.Services.AddApiVersioning(options => -{ - options.DefaultApiVersion = new ApiVersion(1, 0); - options.AssumeDefaultVersionWhenUnspecified = true; - options.ReportApiVersions = true; - options.ApiVersionReader = ApiVersionReader.Combine( - new UrlSegmentApiVersionReader(), - new HeaderApiVersionReader("X-Api-Version") - ); -}).AddApiExplorer(options => -{ - options.GroupNameFormat = "'v'VVV"; - options.SubstituteApiVersionInUrl = true; -}); +builder.Services.AddApiVersioningConfiguration(); -// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle builder.Services.AddEndpointsApiExplorer(); builder.Services.AddAuthorization(); builder.Services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new OpenApiInfo { Title = "HiveMind - V1", Version = "v1.0" }); }); + +builder.Services.AddOptionsConfiguration(builder.Configuration); + builder.Services.AddHiveMindLogic(); -builder.Services.Configure(builder.Configuration.GetSection("CommunicationConfiguration")); - -var communicationControlTelemetryPolicy = HttpPolicyExtensions - .HandleTransientHttpError() - .WaitAndRetryAsync(3, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt))); -builder.Services.AddHttpClient() - .AddPolicyHandler(communicationControlTelemetryPolicy); - -// register NAMED client for connect request -builder.Services.AddHttpClient("HiveConnectClient"); +builder.Services.AddHttpClientsConfiguration(); string corsPolicyName = "HiveMindCorsPolicy"; -builder.Services.AddCors(options => -{ - options.AddPolicy(name: corsPolicyName, - policy => - { - policy.AllowAnyOrigin() //SECURITY WARNING ! Never allow all origins - .AllowAnyMethod() - .AllowAnyHeader(); - }); -}); +builder.Services.AddCorsConfiguration(corsPolicyName); builder.Services.AddExceptionHandler(); builder.Services.AddProblemDetails(); @@ -82,7 +50,7 @@ using (var scope = app.Services.CreateScope()) catch (Exception ex) { logger.LogError($"Error occured while connecting Hive to Communication Control. \nException text: {ex.Message}"); - System.Diagnostics.Process.GetCurrentProcess().Kill(); + Environment.Exit(1); } } @@ -105,7 +73,6 @@ ApiVersionSet apiVersionSet = app.NewApiVersionSet() .HasApiVersion(new ApiVersion(1)) .ReportApiVersions() .Build(); - RouteGroupBuilder groupBuilder = app.MapGroup("api/v{apiVersion:apiVersion}").WithApiVersionSet(apiVersionSet); groupBuilder.MapGet("ping", (IOptionsSnapshot config) => diff --git a/src/CommunicationControl/DevOpsProject.HiveMind.API/appsettings.json b/src/CommunicationControl/DevOpsProject.HiveMind.API/appsettings.json index f12b9a4..376b264 100644 --- a/src/CommunicationControl/DevOpsProject.HiveMind.API/appsettings.json +++ b/src/CommunicationControl/DevOpsProject.HiveMind.API/appsettings.json @@ -38,7 +38,7 @@ "rollingInterval": "Day", "rollOnFileSizeLimit": true, "formatter": "Serilog.Formatting.Compact.CompactJsonFormatter, Serilog.Formatting.Compact", - "restrictedToMinimumLevel": "Warning" + "restrictedToMinimumLevel": "Information" } } ], diff --git a/src/CommunicationControl/DevOpsProject.HiveMind.Logic/Services/HiveMindMovingService.cs b/src/CommunicationControl/DevOpsProject.HiveMind.Logic/Services/HiveMindMovingService.cs index 5633e8a..553e89b 100644 --- a/src/CommunicationControl/DevOpsProject.HiveMind.Logic/Services/HiveMindMovingService.cs +++ b/src/CommunicationControl/DevOpsProject.HiveMind.Logic/Services/HiveMindMovingService.cs @@ -27,6 +27,7 @@ namespace DevOpsProject.HiveMind.Logic.Services // If already moving - stop movement if (HiveInMemoryState.IsMoving) { + _logger.LogWarning("Previous movement command terminated. Previous destination: {@destination}, Current Location: {@current}, new destination: {@destination}", HiveInMemoryState.Destination, HiveInMemoryState.CurrentLocation, destination); StopMovement(); } @@ -39,8 +40,9 @@ namespace DevOpsProject.HiveMind.Logic.Services if (_movementTimer == null) { // TODO: Recalculating position each N seconds - _movementTimer = new Timer(UpdateMovement, null, TimeSpan.Zero, TimeSpan.FromSeconds(3)); - _logger.LogInformation("Movement timer started."); + int intervalFromSeconds = 3; + _movementTimer = new Timer(UpdateMovement, null, TimeSpan.Zero, TimeSpan.FromSeconds(intervalFromSeconds)); + _logger.LogInformation("Movement timer started. Destination: {@destination}, recalculation interval: {interval}", destination, intervalFromSeconds); } } } @@ -60,14 +62,13 @@ namespace DevOpsProject.HiveMind.Logic.Services if (AreLocationsEqual(currentLocation.Value, destination.Value)) { + _logger.LogInformation("Reached destination. Current location: {@currentLocation}, Destination: {@destination}", currentLocation, destination); StopMovement(); return; } Location newLocation = CalculateNextPosition(currentLocation.Value, destination.Value, 0.1f); HiveInMemoryState.CurrentLocation = newLocation; - - _logger.LogInformation($"Moved closer: {newLocation}"); } } @@ -77,7 +78,6 @@ namespace DevOpsProject.HiveMind.Logic.Services _movementTimer = null; HiveInMemoryState.IsMoving = false; HiveInMemoryState.Destination = null; - _logger.LogInformation("Movement stopped: Reached destination."); } private static bool AreLocationsEqual(Location loc1, Location loc2) diff --git a/src/CommunicationControl/DevOpsProject.HiveMind.Logic/Services/HiveMindService.cs b/src/CommunicationControl/DevOpsProject.HiveMind.Logic/Services/HiveMindService.cs index 4d3204f..df1c01d 100644 --- a/src/CommunicationControl/DevOpsProject.HiveMind.Logic/Services/HiveMindService.cs +++ b/src/CommunicationControl/DevOpsProject.HiveMind.Logic/Services/HiveMindService.cs @@ -31,6 +31,7 @@ namespace DevOpsProject.HiveMind.Logic.Services { var request = new HiveConnectRequest { + HiveSchema = _communicationConfigurationOptions.RequestSchema, HiveIP = _communicationConfigurationOptions.HiveIP, HivePort = _communicationConfigurationOptions.HivePort, HiveID = _communicationConfigurationOptions.HiveID @@ -47,6 +48,8 @@ namespace DevOpsProject.HiveMind.Logic.Services }; var jsonContent = new StringContent(JsonSerializer.Serialize(request), Encoding.UTF8, "application/json"); + _logger.LogInformation("Attempting to connect Hive. Request: {@request}, URI: {uri}", request, uriBuilder.Uri); + var retryPolicy = Policy.HandleResult(r => !r.IsSuccessStatusCode) .WaitAndRetryAsync( 10, @@ -68,7 +71,6 @@ namespace DevOpsProject.HiveMind.Logic.Services HiveInMemoryState.OperationalArea = hiveConnectResponse.OperationalArea; HiveInMemoryState.CurrentLocation = _communicationConfigurationOptions.InitialLocation; - // HERE - we are starting to send telemetry StartTelemetry(); } else @@ -80,7 +82,7 @@ namespace DevOpsProject.HiveMind.Logic.Services else { _logger.LogError($"Failed to connect hive, terminating process"); - System.Diagnostics.Process.GetCurrentProcess().Kill(); + Environment.Exit(1); } } diff --git a/src/CommunicationControl/DevOpsProject.Shared/Configuration/ComControlCommunicationConfiguration.cs b/src/CommunicationControl/DevOpsProject.Shared/Configuration/ComControlCommunicationConfiguration.cs index 731d58d..e70b84b 100644 --- a/src/CommunicationControl/DevOpsProject.Shared/Configuration/ComControlCommunicationConfiguration.cs +++ b/src/CommunicationControl/DevOpsProject.Shared/Configuration/ComControlCommunicationConfiguration.cs @@ -2,7 +2,6 @@ { public class ComControlCommunicationConfiguration { - public string RequestScheme { get; set; } public string HiveMindPath { get; set; } } } diff --git a/src/CommunicationControl/DevOpsProject.Shared/Exceptions/HiveNotFoundException.cs b/src/CommunicationControl/DevOpsProject.Shared/Exceptions/HiveNotFoundException.cs deleted file mode 100644 index ada24fa..0000000 --- a/src/CommunicationControl/DevOpsProject.Shared/Exceptions/HiveNotFoundException.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace DevOpsProject.Shared.Exceptions -{ - public class HiveNotFoundException : Exception - { - public HiveNotFoundException() - { - } - - public HiveNotFoundException(string message) - : base(message) - { - } - - public HiveNotFoundException(string message, Exception inner) - : base(message, inner) - { - } - } -} diff --git a/src/CommunicationControl/DevOpsProject.Shared/Models/HiveConnectRequest.cs b/src/CommunicationControl/DevOpsProject.Shared/Models/HiveConnectRequest.cs index 86a2570..4776bc7 100644 --- a/src/CommunicationControl/DevOpsProject.Shared/Models/HiveConnectRequest.cs +++ b/src/CommunicationControl/DevOpsProject.Shared/Models/HiveConnectRequest.cs @@ -1,13 +1,8 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace DevOpsProject.Shared.Models +namespace DevOpsProject.Shared.Models { public class HiveConnectRequest { + public string HiveSchema { get; set; } public string HiveIP { get; set; } public int HivePort { get; set; } public string HiveID { get; set; } diff --git a/src/CommunicationControl/DevOpsProject.Shared/Models/HiveModel.cs b/src/CommunicationControl/DevOpsProject.Shared/Models/HiveModel.cs index 9a77869..174dca2 100644 --- a/src/CommunicationControl/DevOpsProject.Shared/Models/HiveModel.cs +++ b/src/CommunicationControl/DevOpsProject.Shared/Models/HiveModel.cs @@ -5,6 +5,7 @@ public string HiveID { get; set; } public string HiveIP { get; set; } public int HivePort { get; set; } + public string HiveSchema { get; set; } public HiveTelemetryModel Telemetry { get; set; } } } diff --git a/src/CommunicationControl/DevOpsProject/Controllers/ClientController.cs b/src/CommunicationControl/DevOpsProject/Controllers/ClientController.cs index 0798b0d..1a4200f 100644 --- a/src/CommunicationControl/DevOpsProject/Controllers/ClientController.cs +++ b/src/CommunicationControl/DevOpsProject/Controllers/ClientController.cs @@ -32,17 +32,21 @@ namespace DevOpsProject.CommunicationControl.API.Controllers [HttpGet("hive/{hiveId}")] public async Task GetHive(string hiveId) { - var hiveModel = await _communicationControlService.GetHiveModel(hiveId); + var hiveExists = await _communicationControlService.IsHiveConnected(hiveId); + if (!hiveExists) + { + _logger.LogWarning("Failed to get Hive for HiveID: {hiveId}", hiveId); + return NotFound($"Hive with HiveID: {hiveId} is not found"); + } + var hiveModel = await _communicationControlService.GetHiveModel(hiveId); return Ok(hiveModel); } [HttpGet("hive")] public async Task GetHives() { - var hives = await _communicationControlService.GetAllHives(); - return Ok(hives); } @@ -59,6 +63,7 @@ namespace DevOpsProject.CommunicationControl.API.Controllers if (request?.Hives == null || !request.Hives.Any()) return BadRequest("No hive IDs provided."); + _logger.LogInformation("Hive moving request accepted by enpdoint. Request: {@request}", request); foreach (var id in request.Hives) { _ = Task.Run(async () => @@ -69,7 +74,7 @@ namespace DevOpsProject.CommunicationControl.API.Controllers } catch (Exception ex) { - _logger.LogError(ex, $"Failed to send control signal for HiveID: {id}"); + _logger.LogError(ex, "Failed to send control signal for HiveID: {id} \n Request: {@request}", id, request); } }); } diff --git a/src/CommunicationControl/DevOpsProject/Controllers/HiveController.cs b/src/CommunicationControl/DevOpsProject/Controllers/HiveController.cs index 4e4cd00..8c3bf9c 100644 --- a/src/CommunicationControl/DevOpsProject/Controllers/HiveController.cs +++ b/src/CommunicationControl/DevOpsProject/Controllers/HiveController.cs @@ -11,10 +11,12 @@ namespace DevOpsProject.CommunicationControl.API.Controllers public class HiveController : Controller { private readonly ICommunicationControlService _communicationControlService; + private readonly ILogger _logger; - public HiveController(ICommunicationControlService communicationControlService) + public HiveController(ICommunicationControlService communicationControlService, ILogger logger) { _communicationControlService = communicationControlService; + _logger = logger; } [HttpPost("connect")] @@ -25,8 +27,16 @@ namespace DevOpsProject.CommunicationControl.API.Controllers HiveID = request.HiveID, HiveIP = request.HiveIP, HivePort = request.HivePort, + HiveSchema = request.HiveSchema }; + bool isConnected = await _communicationControlService.IsHiveConnected(request.HiveID); + if (isConnected) + { + _logger.LogError("Hive with HiveID: {hiveId} already connected. Request: {@request}", request.HiveID, request); + return BadRequest($"Hive with HiveID: {request.HiveID} already connected"); + } + var hiveOperationalArea = await _communicationControlService.ConnectHive(hiveModel); var connectResponse = new HiveConnectResponse { @@ -35,6 +45,7 @@ namespace DevOpsProject.CommunicationControl.API.Controllers }; return Ok(connectResponse); + } [HttpPost("telemetry")] @@ -50,13 +61,22 @@ namespace DevOpsProject.CommunicationControl.API.Controllers Timestamp = DateTime.Now }; - var telemetryUpdateTimestamp = await _communicationControlService.AddTelemetry(hiveTelemetryModel); - var telemetryResponse = new HiveTelemetryResponse + bool isHiveConnected = await _communicationControlService.IsHiveConnected(request.HiveID); + if (isHiveConnected) { - Timestamp = telemetryUpdateTimestamp - }; + var telemetryUpdateTimestamp = await _communicationControlService.AddTelemetry(hiveTelemetryModel); + var telemetryResponse = new HiveTelemetryResponse + { + Timestamp = telemetryUpdateTimestamp + }; - return Ok(telemetryResponse); + return Ok(telemetryResponse); + } + else + { + _logger.LogWarning("Failed to write telemetry. Hive with HiveID: {hiveId} is not connected. Request: {@request}", request.HiveID, request); + return NotFound($"Failed to write telemetry. Hive with HiveID: {request.HiveID} is not connected"); + } } } diff --git a/src/CommunicationControl/DevOpsProject/DI/ApiVersioningConfiguration.cs b/src/CommunicationControl/DevOpsProject/DI/ApiVersioningConfiguration.cs new file mode 100644 index 0000000..8886291 --- /dev/null +++ b/src/CommunicationControl/DevOpsProject/DI/ApiVersioningConfiguration.cs @@ -0,0 +1,27 @@ +using Asp.Versioning; + +namespace DevOpsProject.CommunicationControl.API.DI +{ + public static class ApiVersioningConfiguration + { + public static IServiceCollection AddApiVersioningConfiguration(this IServiceCollection serviceCollection) + { + serviceCollection.AddApiVersioning(options => + { + options.DefaultApiVersion = new ApiVersion(1, 0); + options.AssumeDefaultVersionWhenUnspecified = true; + options.ReportApiVersions = true; + options.ApiVersionReader = ApiVersionReader.Combine( + new UrlSegmentApiVersionReader(), + new HeaderApiVersionReader("X-Api-Version") + ); + }).AddApiExplorer(options => + { + options.GroupNameFormat = "'v'VVV"; + options.SubstituteApiVersionInUrl = true; + }); + + return serviceCollection; + } + } +} diff --git a/src/CommunicationControl/DevOpsProject/DI/CorsConfiguration.cs b/src/CommunicationControl/DevOpsProject/DI/CorsConfiguration.cs new file mode 100644 index 0000000..efb3832 --- /dev/null +++ b/src/CommunicationControl/DevOpsProject/DI/CorsConfiguration.cs @@ -0,0 +1,21 @@ +namespace DevOpsProject.CommunicationControl.API.DI +{ + public static class CorsConfiguration + { + public static IServiceCollection AddCorsConfiguration(this IServiceCollection serviceCollection, string corsPolicyName) + { + serviceCollection.AddCors(options => + { + options.AddPolicy(name: corsPolicyName, + policy => + { + policy.AllowAnyOrigin() //SECURITY WARNING ! Never allow all origins + .AllowAnyMethod() + .AllowAnyHeader(); + }); + }); + + return serviceCollection; + } + } +} diff --git a/src/CommunicationControl/DevOpsProject/DI/HttpClientsConfiguration.cs b/src/CommunicationControl/DevOpsProject/DI/HttpClientsConfiguration.cs new file mode 100644 index 0000000..ddced4d --- /dev/null +++ b/src/CommunicationControl/DevOpsProject/DI/HttpClientsConfiguration.cs @@ -0,0 +1,20 @@ +using DevOpsProject.Shared.Clients; +using Polly; +using Polly.Extensions.Http; + +namespace DevOpsProject.CommunicationControl.API.DI +{ + public static class HttpClientsConfiguration + { + public static IServiceCollection AddHttpClientsConfiguration(this IServiceCollection serviceCollection) + { + var hiveRetryPolicy = HttpPolicyExtensions + .HandleTransientHttpError() + .WaitAndRetryAsync(3, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt))); + serviceCollection.AddHttpClient() + .AddPolicyHandler(hiveRetryPolicy); + + return serviceCollection; + } + } +} diff --git a/src/CommunicationControl/DevOpsProject/DI/JsonControllerOptionsConfiguration.cs b/src/CommunicationControl/DevOpsProject/DI/JsonControllerOptionsConfiguration.cs new file mode 100644 index 0000000..c797ad6 --- /dev/null +++ b/src/CommunicationControl/DevOpsProject/DI/JsonControllerOptionsConfiguration.cs @@ -0,0 +1,15 @@ +namespace DevOpsProject.CommunicationControl.API.DI +{ + public static class JsonControllerOptionsConfiguration + { + public static IServiceCollection AddJsonControllerOptionsConfiguration(this IServiceCollection serviceCollection) + { + serviceCollection.AddControllers().AddJsonOptions(options => + { + options.JsonSerializerOptions.PropertyNamingPolicy = null; + }); + + return serviceCollection; + } + } +} diff --git a/src/CommunicationControl/DevOpsProject/DI/LogicConfiguration.cs b/src/CommunicationControl/DevOpsProject/DI/LogicConfiguration.cs index e5bfa63..e55cf46 100644 --- a/src/CommunicationControl/DevOpsProject/DI/LogicConfiguration.cs +++ b/src/CommunicationControl/DevOpsProject/DI/LogicConfiguration.cs @@ -12,7 +12,5 @@ namespace DevOpsProject.CommunicationControl.API.DI return serviceCollection; } - - } } diff --git a/src/CommunicationControl/DevOpsProject/DI/OptionsConfiguration.cs b/src/CommunicationControl/DevOpsProject/DI/OptionsConfiguration.cs new file mode 100644 index 0000000..dcf856b --- /dev/null +++ b/src/CommunicationControl/DevOpsProject/DI/OptionsConfiguration.cs @@ -0,0 +1,16 @@ +using DevOpsProject.Shared.Configuration; +using DevOpsProject.Shared.Models; + +namespace DevOpsProject.CommunicationControl.API.DI +{ + public static class OptionsConfiguration + { + public static IServiceCollection AddOptionsConfiguration(this IServiceCollection serviceCollection, IConfiguration configuration) + { + serviceCollection.Configure(configuration.GetSection("OperationalArea")); + serviceCollection.Configure(configuration.GetSection("CommunicationConfiguration")); + + return serviceCollection; + } + } +} diff --git a/src/CommunicationControl/DevOpsProject/Program.cs b/src/CommunicationControl/DevOpsProject/Program.cs index 3b276c9..46db467 100644 --- a/src/CommunicationControl/DevOpsProject/Program.cs +++ b/src/CommunicationControl/DevOpsProject/Program.cs @@ -1,13 +1,6 @@ -using Asp.Versioning; using DevOpsProject.CommunicationControl.API.DI; using DevOpsProject.CommunicationControl.API.Middleware; -using DevOpsProject.Shared.Clients; -using DevOpsProject.Shared.Configuration; -using DevOpsProject.Shared.Models; -using Microsoft.Extensions.Options; using Microsoft.OpenApi.Models; -using Polly; -using Polly.Extensions.Http; using Serilog; internal class Program @@ -21,26 +14,11 @@ internal class Program .ReadFrom.Services(services) .Enrich.FromLogContext()); - builder.Services.AddApiVersioning(options => - { - options.DefaultApiVersion = new ApiVersion(1, 0); - options.AssumeDefaultVersionWhenUnspecified = true; - options.ReportApiVersions = true; - options.ApiVersionReader = ApiVersionReader.Combine( - new UrlSegmentApiVersionReader(), - new HeaderApiVersionReader("X-Api-Version") - ); - }).AddApiExplorer(options => - { - options.GroupNameFormat = "'v'VVV"; - options.SubstituteApiVersionInUrl = true; - }); + builder.Services.AddApiVersioningConfiguration(); // TODO: consider this approach - builder.Services.AddControllers().AddJsonOptions(options => - { - options.JsonSerializerOptions.PropertyNamingPolicy = null; - }); // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle + builder.Services.AddJsonControllerOptionsConfiguration(); + builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(c => { @@ -52,36 +30,12 @@ internal class Program builder.Services.AddRedis(builder.Configuration); builder.Services.AddCommunicationControlLogic(); - builder.Services.Configure(builder.Configuration.GetSection("OperationalArea")); - builder.Services.Configure(builder.Configuration.GetSection("CommunicationConfiguration")); - - var hiveRetryPolicy = HttpPolicyExtensions - .HandleTransientHttpError() - .WaitAndRetryAsync(3, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt))); - builder.Services.AddHttpClient() - .AddPolicyHandler(hiveRetryPolicy); + builder.Services.AddOptionsConfiguration(builder.Configuration); + builder.Services.AddHttpClientsConfiguration(); var corsPolicyName = "AllowReactApp"; - var localCorsPolicyName = "AllowLocalHtml"; - builder.Services.AddCors(options => - { - options.AddPolicy(name: corsPolicyName, - policy => - { - policy.AllowAnyOrigin() //SECURITY WARNING ! Never allow all origins - .AllowAnyMethod() - .AllowAnyHeader(); - }); - - options.AddPolicy(name: localCorsPolicyName, - policy => - { - policy.AllowAnyOrigin() //SECURITY WARNING ! Never allow all origins - .AllowAnyMethod() - .AllowAnyHeader(); - }); - }); + builder.Services.AddCorsConfiguration(corsPolicyName); builder.Services.AddExceptionHandler(); builder.Services.AddProblemDetails(); @@ -97,7 +51,6 @@ internal class Program } app.UseCors(corsPolicyName); - //app.UseCors(localCorsPolicyName); app.UseAuthorization(); diff --git a/src/CommunicationControl/DevOpsProject/appsettings.json b/src/CommunicationControl/DevOpsProject/appsettings.json index 16d1344..882ad53 100644 --- a/src/CommunicationControl/DevOpsProject/appsettings.json +++ b/src/CommunicationControl/DevOpsProject/appsettings.json @@ -14,10 +14,8 @@ "InitialSpeed_KM": 5, "TelemetryInterval_MS": 30000, "PingInterval_MS": 15000 - }, "CommunicationConfiguration": { - "RequestScheme": "http", "HiveMindPath": "api/v1" }, "AllowedHosts": "*", @@ -45,7 +43,7 @@ "rollingInterval": "Day", "rollOnFileSizeLimit": true, "formatter": "Serilog.Formatting.Compact.CompactJsonFormatter, Serilog.Formatting.Compact", - "restrictedToMinimumLevel": "Warning" + "restrictedToMinimumLevel": "Information" } } ],