diff --git a/hiPower.Manager.sln b/hiPower.Manager.sln index 118500c..0c6347b 100644 --- a/hiPower.Manager.sln +++ b/hiPower.Manager.sln @@ -33,7 +33,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "hiPower.Test.Integration", EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "hiPower.Repository", "src\hiPower.Repository\hiPower.Repository.csproj", "{90CDBFED-0814-4F69-9039-CC087D0B3C55}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "hiPower.Repository.Azure", "src\hiPower.Repository.Azure\hiPower.Repository.Azure.csproj", "{0C64BF69-BBE7-4AAA-A9C0-4A48CF4521BC}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "hiPower.Repository.Azure", "src\hiPower.Repository.Azure\hiPower.Repository.Azure.csproj", "{0C64BF69-BBE7-4AAA-A9C0-4A48CF4521BC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "hiPower.Server.Communication", "src\hiPower.Server.Communication\hiPower.Server.Communication.csproj", "{09F62807-E6CE-4C3B-904C-7CE16852D579}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -89,6 +91,10 @@ Global {0C64BF69-BBE7-4AAA-A9C0-4A48CF4521BC}.Debug|Any CPU.Build.0 = Debug|Any CPU {0C64BF69-BBE7-4AAA-A9C0-4A48CF4521BC}.Release|Any CPU.ActiveCfg = Release|Any CPU {0C64BF69-BBE7-4AAA-A9C0-4A48CF4521BC}.Release|Any CPU.Build.0 = Release|Any CPU + {09F62807-E6CE-4C3B-904C-7CE16852D579}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {09F62807-E6CE-4C3B-904C-7CE16852D579}.Debug|Any CPU.Build.0 = Debug|Any CPU + {09F62807-E6CE-4C3B-904C-7CE16852D579}.Release|Any CPU.ActiveCfg = Release|Any CPU + {09F62807-E6CE-4C3B-904C-7CE16852D579}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -106,6 +112,7 @@ Global {3C659985-42FD-4891-A56B-6D3D36BE939F} = {9AAC9818-174C-4CA7-A6A7-3A3CEE2A195C} {90CDBFED-0814-4F69-9039-CC087D0B3C55} = {82B938FA-29E8-41B4-B4FA-AF77C343884E} {0C64BF69-BBE7-4AAA-A9C0-4A48CF4521BC} = {82B938FA-29E8-41B4-B4FA-AF77C343884E} + {09F62807-E6CE-4C3B-904C-7CE16852D579} = {82B938FA-29E8-41B4-B4FA-AF77C343884E} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {F5E8CDB0-BE42-425F-B06D-EEF8F312C879} diff --git a/src/hiPower.Abstracts/IRemoteService.cs b/src/hiPower.Abstracts/IRemoteService.cs new file mode 100644 index 0000000..bc2f4ef --- /dev/null +++ b/src/hiPower.Abstracts/IRemoteService.cs @@ -0,0 +1,8 @@ +namespace hiPower.Abstracts; + +public interface IRemoteService +{ + Task>> GetConfigurationAsync (RemoteServiceOptions options); + Task>> GetStatisticsAsync (RemoteServiceOptions options); + Task> GetInfoAsync (RemoteServiceOptions options); +} diff --git a/src/hiPower.Abstracts/IServerService.cs b/src/hiPower.Abstracts/IServerService.cs index db064a3..2450952 100644 --- a/src/hiPower.Abstracts/IServerService.cs +++ b/src/hiPower.Abstracts/IServerService.cs @@ -3,14 +3,14 @@ public interface IServerService { Task>> GetAllAsync (); - Task>> GetAllAsync (string dataCenterId); + Task>> GetAllAsync (string dataCenterId); Task> GetAsync (string id); - Task> CreateAsync(Server server); - Task> UpdateAsync(string id, Server server); - Task> DeleteAsync (string id); - Task>> GetAvailableDataCentersAsync (); + + Task>> GetRemoteConfigurationAsync(string id); + Task>> GetRemoteStatisticsAsync (string id); + Task> GetRemoteServerInfoAsync(string id); } diff --git a/src/hiPower.Abstracts/IUnitOfWork.cs b/src/hiPower.Abstracts/IUnitOfWork.cs index a2fda65..38d0aad 100644 --- a/src/hiPower.Abstracts/IUnitOfWork.cs +++ b/src/hiPower.Abstracts/IUnitOfWork.cs @@ -5,6 +5,6 @@ namespace hiPower.Abstracts; public interface IUnitOfWork : IDisposable { IGenericRepository DataCenterRepository { get; } - IGenericRepository ServerRepository { get; } + IGenericRepository ServerRepository { get; } Task SaveAsync (); } diff --git a/src/hiPower.Abstracts/hiPower.Abstracts.csproj b/src/hiPower.Abstracts/hiPower.Abstracts.csproj index c8a3be5..65b405c 100644 --- a/src/hiPower.Abstracts/hiPower.Abstracts.csproj +++ b/src/hiPower.Abstracts/hiPower.Abstracts.csproj @@ -7,9 +7,10 @@ - - - + + + + @@ -18,6 +19,7 @@ + diff --git a/src/hiPower.Common.Type/Extensions/ProtocolEnumExtension.cs b/src/hiPower.Common.Type/Extensions/ProtocolEnumExtension.cs new file mode 100644 index 0000000..4cb25d1 --- /dev/null +++ b/src/hiPower.Common.Type/Extensions/ProtocolEnumExtension.cs @@ -0,0 +1,14 @@ +namespace hiPower.Common.Type.Extensions; + +public static class ProtocolEnumExtension +{ + public static Protocol ToProtocol(this string value) + { + var isDone = Enum.TryParse(value, out var protocol); + if (isDone) + { + return protocol; + } + return Protocol.HTTP; + } +} diff --git a/src/hiPower.Common.Type/Options/RemoteServiceOptions.cs b/src/hiPower.Common.Type/Options/RemoteServiceOptions.cs new file mode 100644 index 0000000..09e2ca7 --- /dev/null +++ b/src/hiPower.Common.Type/Options/RemoteServiceOptions.cs @@ -0,0 +1,4 @@ +namespace hiPower.Common.Type.Options; + +public record struct RemoteServiceOptions(Protocol Proto, string HostAddress, uint Port, string LocalId, string ApiKey, string AuthToken); + diff --git a/src/hiPower.Common.Type/Protocol.cs b/src/hiPower.Common.Type/Protocol.cs new file mode 100644 index 0000000..e104952 --- /dev/null +++ b/src/hiPower.Common.Type/Protocol.cs @@ -0,0 +1,7 @@ +namespace hiPower.Common.Type; + +public enum Protocol +{ + HTTP, + HTTPS +} diff --git a/src/hiPower.Core/Mappings/ServerMappring.cs b/src/hiPower.Core/Mappings/ServerMappring.cs index ae7a56a..074f067 100644 --- a/src/hiPower.Core/Mappings/ServerMappring.cs +++ b/src/hiPower.Core/Mappings/ServerMappring.cs @@ -1,10 +1,13 @@ -namespace hiPower.Core.Mappings; +using hiPower.Common.Type.Extensions; +using hiPower.Common.Type.Options; + +namespace hiPower.Core.Mappings; internal class ServerMappring : IRegister { public void Register (TypeAdapterConfig config) { - config.NewConfig () + config.NewConfig () .Map (dest => dest.Id, src => src.Id) .Map (dest => dest.LocalId, src => src.LocalId) .Map (dest => dest.LocationId, src => src.LocationId) @@ -17,11 +20,9 @@ public void Register (TypeAdapterConfig config) .Map (dest => dest.Auth, src => src.Auth) .Map (dest => dest.Version, src => src.Version) .Map (dest => dest.OS, src => src.OS) - .Map (dest => dest.Configuration, src => src.Configuration) - .Map (dest => dest.Timeout, src => src.Timeout) - .Map (dest => dest.Retries, src => src.Retries); + .Map (dest => dest.Configuration, src => src.Configuration); - config.NewConfig () + config.NewConfig () .Map (dest => dest.Id, src => src.Id) .Map (dest => dest.LocalId, src => src.LocalId) .Map (dest => dest.LocationId, src => src.LocationId) @@ -34,8 +35,9 @@ public void Register (TypeAdapterConfig config) .Map (dest => dest.Auth, src => src.Auth) .Map (dest => dest.Version, src => src.Version) .Map (dest => dest.OS, src => src.OS) - .Map (dest => dest.Configuration, src => src.Configuration) - .Map (dest => dest.Timeout, src => src.Timeout) - .Map (dest => dest.Retries, src => src.Retries); + .Map (dest => dest.Configuration, src => src.Configuration); + + config.NewConfig () + .MapWith (src => new RemoteServiceOptions(src.Proto.ToProtocol(), src.HostAddress, Convert.ToUInt16(src.Port), src.LocalId, src.ApiKey, src.Auth)); } } diff --git a/src/hiPower.Core/ServerService.cs b/src/hiPower.Core/ServerService.cs index d160d3e..145cc78 100644 --- a/src/hiPower.Core/ServerService.cs +++ b/src/hiPower.Core/ServerService.cs @@ -1,17 +1,19 @@ using hiPower.Abstracts; using hiPower.Common.Type.Errors; +using hiPower.Common.Type.Options; using MapsterMapper; namespace hiPower.Core; public class ServerService(IUnitOfWork unit, - IMapper mapper) : IServerService + IMapper mapper, + IRemoteService remoteService) : IServerService { - public async Task> CreateAsync (Dto.Manager.Server server) + public async Task> CreateAsync (Server server) { - var result = await unit.ServerRepository.CreateAsync(server.Adapt()); + var result = await unit.ServerRepository.CreateAsync(server.Adapt()); await unit.SaveAsync (); - return mapper.Map (result); + return mapper.Map (result); } public async Task> DeleteAsync (string id) @@ -20,31 +22,31 @@ public async Task> DeleteAsync (string id) return true; } - public async Task>> GetAllAsync (string dataCenterId) + public async Task>> GetAllAsync (string dataCenterId) { var result = await unit.ServerRepository.GetAll(x => x.LocationId.Equals(dataCenterId.ToUpper()), null, null); if (result.Any ()) { - return result.Adapt> () + return result.Adapt> () .ToErrorOr (); } return Error.NotFound(); } - public async Task>> GetAllAsync () + public async Task>> GetAllAsync () { var result = await unit.ServerRepository.GetAll(null, null, null); - return result.Adapt>() + return result.Adapt>() .ToErrorOr(); } - public async Task> GetAsync (string id) + public async Task> GetAsync (string id) { var result = await unit.ServerRepository .GetAsync(x => x.Id.Equals(id.ToUpper()), null); if (result is not null) { - return mapper.Map (result); + return mapper.Map (result); } return Error.NotFound(); } @@ -60,15 +62,75 @@ public async Task>> GetAvailableDataCentersAsync ( return Error.NotFound (); } - public async Task> UpdateAsync (string id, Dto.Manager.Server server) + public async Task> UpdateAsync (string id, Server server) { if (id.ToUpper ().Equals (server.Id.ToUpper ())) { - var updateServer = server.Adapt (); + var updateServer = server.Adapt (); var result = unit.ServerRepository.Update(updateServer); await unit.SaveAsync (); - return await Task.FromResult(result.Adapt()); + return await Task.FromResult(result.Adapt()); } return ServerServiceErrors.UpdateError; } + + public async Task>> GetRemoteConfigurationAsync(string id) + { + var service = await GetAsync(id); + + if (service.IsError && service.FirstError.Type == ErrorType.NotFound) + { + return Error.NotFound(); + } + + var options = service.Value.Adapt (); + var response = await remoteService.GetConfigurationAsync (options); + + if (response.IsError) + { + return response.FirstError; + } + + return response; + } + + public async Task>> GetRemoteStatisticsAsync (string id) + { + var service = await GetAsync(id); + + if (service.IsError && service.FirstError.Type == ErrorType.NotFound) + { + return Error.NotFound (); + } + + var options = service.Value.Adapt (); + var response = await remoteService.GetStatisticsAsync (options); + + if (response.IsError) + { + return response.FirstError; + } + + return response; + } + + public async Task> GetRemoteServerInfoAsync (string id) + { + var service = await GetAsync(id); + + if (service.IsError && service.FirstError.Type == ErrorType.NotFound) + { + return Error.NotFound (); + } + + var options = service.Value.Adapt (); + var response = await remoteService.GetInfoAsync (options); + + if (response.IsError) + { + return response.FirstError; + } + + return response; + } } diff --git a/src/hiPower.Database/Configuration/ServerEntityConfiguration.cs b/src/hiPower.Database/Configuration/ServerEntityConfiguration.cs index c32b9e2..7d87d8b 100644 --- a/src/hiPower.Database/Configuration/ServerEntityConfiguration.cs +++ b/src/hiPower.Database/Configuration/ServerEntityConfiguration.cs @@ -4,11 +4,11 @@ namespace hiPower.Database.Configuration; -internal class ServerEntityConfiguration : IEntityTypeConfiguration +internal class ServerEntityConfiguration : IEntityTypeConfiguration { - public void Configure (EntityTypeBuilder builder) + public void Configure (EntityTypeBuilder builder) { - builder.ToTable ($"{Prefix.Table}{nameof(Server)}"); + builder.ToTable ($"{Prefix.Table}Server"); builder.HasKey ( t => t.Id ); diff --git a/src/hiPower.Database/ManagerDbContext.DbSet.cs b/src/hiPower.Database/ManagerDbContext.DbSet.cs index 9e74a82..64b0e36 100644 --- a/src/hiPower.Database/ManagerDbContext.DbSet.cs +++ b/src/hiPower.Database/ManagerDbContext.DbSet.cs @@ -5,6 +5,6 @@ namespace hiPower.Database; public partial class ManagerDbContext { - public DbSet Servers { get; set; } + public DbSet Servers { get; set; } public DbSet Locations { get; set; } } diff --git a/src/hiPower.Dto/Manager/SettingsItem.cs b/src/hiPower.Dto/Manager/SettingsItem.cs new file mode 100644 index 0000000..37bf2e5 --- /dev/null +++ b/src/hiPower.Dto/Manager/SettingsItem.cs @@ -0,0 +1,8 @@ +namespace hiPower.Dto.Manager; + +public class SettingsItem +{ + public string Name { get; set; } + public string Type { get; set; } + public string Value { get; set; } +} diff --git a/src/hiPower.Dto/Remote/ConfigSetting.cs b/src/hiPower.Dto/Remote/ConfigSetting.cs new file mode 100644 index 0000000..3675c06 --- /dev/null +++ b/src/hiPower.Dto/Remote/ConfigSetting.cs @@ -0,0 +1,4 @@ +namespace hiPower.Dto.Remote; + +public record class ConfigSetting(string Name, string Type, string Value); + diff --git a/src/hiPower.Dto/Remote/MasterZone.cs b/src/hiPower.Dto/Remote/MasterZone.cs new file mode 100644 index 0000000..4802ba7 --- /dev/null +++ b/src/hiPower.Dto/Remote/MasterZone.cs @@ -0,0 +1,5 @@ +namespace hiPower.Dto.Remote; + +public class MasterZone +{ +} diff --git a/src/hiPower.Dto/Remote/ServerItem.cs b/src/hiPower.Dto/Remote/ServerInfo.cs similarity index 95% rename from src/hiPower.Dto/Remote/ServerItem.cs rename to src/hiPower.Dto/Remote/ServerInfo.cs index ac6963b..7d65b27 100644 --- a/src/hiPower.Dto/Remote/ServerItem.cs +++ b/src/hiPower.Dto/Remote/ServerInfo.cs @@ -4,7 +4,7 @@ namespace hiPower.Dto.Remote; -public sealed class ServerItem +public sealed class ServerInfo { [JsonPropertyName ("id")] diff --git a/src/hiPower.Dto/Remote/SlaveZone.cs b/src/hiPower.Dto/Remote/SlaveZone.cs new file mode 100644 index 0000000..6aaf971 --- /dev/null +++ b/src/hiPower.Dto/Remote/SlaveZone.cs @@ -0,0 +1,10 @@ +using hiPower.Common.Type; + +namespace hiPower.Dto.Remote; + +public class SlaveZone +{ + public string Name { get; set; } + public ZoneKind Kind { get; set; } + public IEnumerable Nameservers { get; set; } +} diff --git a/src/hiPower.Dto/Remote/StatisticsItemResult.cs b/src/hiPower.Dto/Remote/StatisticsItem.cs similarity index 81% rename from src/hiPower.Dto/Remote/StatisticsItemResult.cs rename to src/hiPower.Dto/Remote/StatisticsItem.cs index 39066b4..9924b55 100644 --- a/src/hiPower.Dto/Remote/StatisticsItemResult.cs +++ b/src/hiPower.Dto/Remote/StatisticsItem.cs @@ -1,6 +1,6 @@ namespace hiPower.Dto.Remote; -public class StatisticsItemResult +public class StatisticsItem { public string Name { get; set; } public string Type { get; set; } diff --git a/src/hiPower.Entity/ApiAddressConfiguration.cs b/src/hiPower.Entity/ApiAddressConfiguration.cs new file mode 100644 index 0000000..46f13da --- /dev/null +++ b/src/hiPower.Entity/ApiAddressConfiguration.cs @@ -0,0 +1,56 @@ +#nullable disable + +namespace hiPower.Common.Type; + +public class ApiAddressConfiguration +{ + private const uint IANAMinTcpPortRange = 1024; + private const uint IANAMaxTcpPortRange = 49151; + private const uint DefaultWebTcpPort = 80; + + public string Proto{ get; private set; } + + private uint port = DefaultWebTcpPort; + public uint Port { get => port; private set { port = value; } } + public string HostAddress { get; private set; } + + public ApiAddressConfiguration SetProtocol (Protocol protocol) + { + Proto = protocol.ToString(); + return this; + } + + public ApiAddressConfiguration SetPort (uint port) + { + bool isPortInRange = port >= IANAMinTcpPortRange && port <= IANAMaxTcpPortRange; + + if (!isPortInRange) + { + throw new ArgumentOutOfRangeException (nameof(port)); + } + + Port = port; + return this; + } + + public ApiAddressConfiguration SetHost (string host) + { + HostAddress = host; + return this; + } + + public string Build () + { + ArgumentNullException.ThrowIfNullOrWhiteSpace (Proto); + ArgumentNullException.ThrowIfNullOrWhiteSpace(HostAddress); + + string result = $"{Proto}://{HostAddress}"; + + if(Port != DefaultWebTcpPort) + { + result += $":{Port}"; + } + + return result; + } +} diff --git a/src/hiPower.Entity/Server.cs b/src/hiPower.Entity/ServerDetails.cs similarity index 91% rename from src/hiPower.Entity/Server.cs rename to src/hiPower.Entity/ServerDetails.cs index 2dc42f0..adfe2cd 100644 --- a/src/hiPower.Entity/Server.cs +++ b/src/hiPower.Entity/ServerDetails.cs @@ -2,7 +2,7 @@ namespace hiPower.Entity; -public class Server: EntityBase +public class ServerDetails: EntityBase { public string Name { get; set; } @@ -28,8 +28,10 @@ public class Server: EntityBase public ServerState State { get; set; } + [Obsolete] public int Timeout { get; set; } + [Obsolete] public int Retries { get; set; } public string LocationId { get; set; } diff --git a/src/hiPower.Entity/ServerLocation.cs b/src/hiPower.Entity/ServerLocation.cs index ff77966..fe73ea5 100644 --- a/src/hiPower.Entity/ServerLocation.cs +++ b/src/hiPower.Entity/ServerLocation.cs @@ -10,5 +10,5 @@ public class ServerLocation : EntityBase public string PostalCode { get; set; } public string Country { get; set; } - public ICollection Servers { get; set; } + public ICollection Servers { get; set; } } diff --git a/src/hiPower.Infrastructure/Extensions/DependencyInjection/InfrastructureServicesExtensions.cs b/src/hiPower.Infrastructure/Extensions/DependencyInjection/InfrastructureServicesExtensions.cs index ef5bc02..1ea3df9 100644 --- a/src/hiPower.Infrastructure/Extensions/DependencyInjection/InfrastructureServicesExtensions.cs +++ b/src/hiPower.Infrastructure/Extensions/DependencyInjection/InfrastructureServicesExtensions.cs @@ -2,6 +2,7 @@ using hiPower.Repository; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using hiPower.Server.Communication.Extensions.DependencyInjection; namespace hiPower.Infrastructure.Extensions.DependencyInjection; @@ -9,6 +10,7 @@ public static class InfrastructureServicesExtensions { public static IServiceCollection ConfigureInfrastructureServices(this IServiceCollection services, IConfiguration configuration) { + services.ConfigureDistributedCommunication (configuration); services.AddScoped(typeof(IGenericRepository<>), typeof (GenericRepository<>)); services.AddScoped(); diff --git a/src/hiPower.Infrastructure/hiPower.Infrastructure.csproj b/src/hiPower.Infrastructure/hiPower.Infrastructure.csproj index 98065f8..0b79612 100644 --- a/src/hiPower.Infrastructure/hiPower.Infrastructure.csproj +++ b/src/hiPower.Infrastructure/hiPower.Infrastructure.csproj @@ -6,14 +6,10 @@ enable - - - - - + diff --git a/src/hiPower.Repository/UnitOfWork.cs b/src/hiPower.Repository/UnitOfWork.cs index 80fcb63..eb8b934 100644 --- a/src/hiPower.Repository/UnitOfWork.cs +++ b/src/hiPower.Repository/UnitOfWork.cs @@ -11,11 +11,11 @@ public class UnitOfWork(DbContextOptions options) : IUnitOfWor private readonly ManagerDbContext dbContext = new(options); private IGenericRepository? dataCenterRepository; - private IGenericRepository? serverRepository; + private IGenericRepository? serverRepository; public IGenericRepository DataCenterRepository => dataCenterRepository ??= new GenericRepository (dbContext); - public IGenericRepository ServerRepository => serverRepository ??= new GenericRepository (dbContext); + public IGenericRepository ServerRepository => serverRepository ??= new GenericRepository (dbContext); public async Task SaveAsync () { @@ -42,6 +42,5 @@ protected virtual void Dispose(bool disposing) } #endregion IDisposable - } } diff --git a/src/hiPower.Server.Communication/ApiAddressConfiguration.cs b/src/hiPower.Server.Communication/ApiAddressConfiguration.cs new file mode 100644 index 0000000..86c6085 --- /dev/null +++ b/src/hiPower.Server.Communication/ApiAddressConfiguration.cs @@ -0,0 +1,58 @@ +#nullable disable + +using hiPower.Common.Type; + +namespace hiPower.Server.Communication; + +internal class ApiAddressConfiguration +{ + private const uint IANAMinTcpPortRange = 1024; + private const uint IANAMaxTcpPortRange = 49151; + private const uint DefaultWebTcpPort = 80; + + public string Proto{ get; private set; } + + private uint port = DefaultWebTcpPort; + public uint Port { get => port; private set { port = value; } } + public string HostAddress { get; private set; } + + public ApiAddressConfiguration SetProtocol (Protocol protocol) + { + Proto = protocol.ToString(); + return this; + } + + public ApiAddressConfiguration SetPort (uint port) + { + bool isPortInRange = port >= IANAMinTcpPortRange && port <= IANAMaxTcpPortRange; + + if (!isPortInRange) + { + throw new ArgumentOutOfRangeException (nameof(port)); + } + + Port = port; + return this; + } + + public ApiAddressConfiguration SetHost (string host) + { + HostAddress = host; + return this; + } + + public string Build () + { + ArgumentNullException.ThrowIfNullOrWhiteSpace (Proto); + ArgumentNullException.ThrowIfNullOrWhiteSpace (HostAddress); + + string result = $"{Proto}://{HostAddress}"; + + if(Port != DefaultWebTcpPort) + { + result += $":{Port}"; + } + + return result; + } +} diff --git a/src/hiPower.Server.Communication/Consts.cs b/src/hiPower.Server.Communication/Consts.cs new file mode 100644 index 0000000..efc816d --- /dev/null +++ b/src/hiPower.Server.Communication/Consts.cs @@ -0,0 +1,10 @@ +namespace hiPower.Server.Communication; + +internal static class Consts +{ + internal const string ResilientPipelineName = "Custom"; + internal const string HttpClientName = "DistributedCommunication"; + + internal const string ApiKeyHeaderName = "X-API-Key"; + internal const string EnpointApiPrefix = "api/v1"; +} diff --git a/src/hiPower.Server.Communication/Extensions/DependencyInjection/ConfigureCommunicationExtension.cs b/src/hiPower.Server.Communication/Extensions/DependencyInjection/ConfigureCommunicationExtension.cs new file mode 100644 index 0000000..081ef89 --- /dev/null +++ b/src/hiPower.Server.Communication/Extensions/DependencyInjection/ConfigureCommunicationExtension.cs @@ -0,0 +1,35 @@ +using System.Net.Mime; +using hiPower.Abstracts; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Http.Resilience; +using Polly; + +namespace hiPower.Server.Communication.Extensions.DependencyInjection; + +public static class ConfigureCommunicationExtension +{ + public static IServiceCollection ConfigureDistributedCommunication(this IServiceCollection services, IConfiguration configuration) + { + services.AddHttpClient (Consts.HttpClientName, client => + { + client.DefaultRequestHeaders.Accept.ParseAdd (MediaTypeNames.Application.Json); + }) + .AddResilienceHandler (Consts.ResilientPipelineName, pipeline => { + pipeline.AddTimeout (TimeSpan.FromSeconds (4)); + pipeline.AddRetry (new HttpRetryStrategyOptions () + { + MaxRetryAttempts = 3, + BackoffType = DelayBackoffType.Exponential, + UseJitter = true, + MaxDelay = TimeSpan.FromSeconds (500), + Delay = TimeSpan.FromSeconds (500), + ShouldRetryAfterHeader = true + }); + }); + + services.AddScoped (); + + return services; + } +} diff --git a/src/hiPower.Server.Communication/RemoteService.cs b/src/hiPower.Server.Communication/RemoteService.cs new file mode 100644 index 0000000..0138dff --- /dev/null +++ b/src/hiPower.Server.Communication/RemoteService.cs @@ -0,0 +1,76 @@ +#nullable disable + +using System.Text.Json; +using ErrorOr; +using hiPower.Abstracts; +using hiPower.Common.Type.Options; +using hiPower.Dto.Remote; + +namespace hiPower.Server.Communication; + +public class RemoteService (IHttpClientFactory clientFactory) : IRemoteService +{ + private readonly HttpClient httpClient = clientFactory.CreateClient(Consts.HttpClientName); + private readonly ApiAddressConfiguration addressBuilder = new(); + private readonly JsonSerializerOptions jsonSerializerOptions = new () + { + PropertyNameCaseInsensitive = true, + }; + + public async Task>> GetConfigurationAsync (RemoteServiceOptions options) + { + ConfigureRequest (options); + var response = await httpClient.GetAsync ($"{Consts.EnpointApiPrefix}/servers/{options.LocalId}/config"); + + if (!response.IsSuccessStatusCode) + { + return Error.Failure (); + } + + var content = await response.Content.ReadAsStringAsync (); + + return JsonSerializer.Deserialize> (content, jsonSerializerOptions) + .ToErrorOr(); + } + + public async Task> GetInfoAsync (RemoteServiceOptions options) + { + ConfigureRequest (options); + var response = await httpClient.GetAsync ($"{Consts.EnpointApiPrefix}/servers/{options.LocalId}"); + + if (!response.IsSuccessStatusCode) + { + return Error.Failure (); + } + + var content = await response.Content.ReadAsStringAsync (); + + return JsonSerializer.Deserialize (content, jsonSerializerOptions) + .ToErrorOr (); + } + + public async Task>> GetStatisticsAsync (RemoteServiceOptions options) + { + ConfigureRequest (options); + var response = await httpClient.GetAsync($"{Consts.EnpointApiPrefix}/servers/{options.LocalId}/statistics"); + if (!response.IsSuccessStatusCode) + { + return Error.Failure (); + } + + var content = await response.Content.ReadAsStringAsync (); + + return JsonSerializer.Deserialize> (content, jsonSerializerOptions) + .ToErrorOr (); + } + + private void ConfigureRequest(RemoteServiceOptions options) + { + var baseAddress = addressBuilder.SetProtocol(options.Proto) + .SetHost(options.HostAddress) + .SetPort(options.Port) + .Build(); + httpClient.BaseAddress = new Uri(baseAddress); + httpClient.DefaultRequestHeaders.Add(Consts.ApiKeyHeaderName, options.ApiKey); + } +} diff --git a/src/hiPower.Server.Communication/hiPower.Server.Communication.csproj b/src/hiPower.Server.Communication/hiPower.Server.Communication.csproj new file mode 100644 index 0000000..2e8940f --- /dev/null +++ b/src/hiPower.Server.Communication/hiPower.Server.Communication.csproj @@ -0,0 +1,18 @@ + + + + net8.0 + enable + enable + + + + + + + + + + + + diff --git a/src/hiPower.WebApi/Controllers/DataCentersController.cs b/src/hiPower.WebApi/Controllers/DataCentersController.cs index e97e3d7..cb3e607 100644 --- a/src/hiPower.WebApi/Controllers/DataCentersController.cs +++ b/src/hiPower.WebApi/Controllers/DataCentersController.cs @@ -49,7 +49,7 @@ public async Task Get ([FromRoute] string id) [HttpGet ("{id}/servers")] [ValidateIdFilter] - [ProducesResponseType (StatusCodes.Status200OK, Type = typeof (ApiResult>))] + [ProducesResponseType (StatusCodes.Status200OK, Type = typeof (ApiResult>))] [ProducesResponseType (StatusCodes.Status400BadRequest, Type = typeof (ProblemDetails))] [ProducesResponseType (StatusCodes.Status404NotFound, Type = typeof (ProblemDetails))] public async Task GetServers ([FromRoute] string id) @@ -61,7 +61,7 @@ public async Task GetServers ([FromRoute] string id) return BadRequest (); } - var response = new ApiResult>(true, result.Value); + var response = new ApiResult>(true, result.Value); return Ok (response); } diff --git a/src/hiPower.WebApi/Controllers/ServersController.cs b/src/hiPower.WebApi/Controllers/ServersController.cs index 6648e47..265f1ec 100644 --- a/src/hiPower.WebApi/Controllers/ServersController.cs +++ b/src/hiPower.WebApi/Controllers/ServersController.cs @@ -1,5 +1,4 @@ -using ErrorOr; -using hiPower.Abstracts; +using hiPower.Abstracts; using Microsoft.AspNetCore.Mvc; namespace hiPower.WebApi.Controllers @@ -11,7 +10,7 @@ public class ServersController(IServerService serverService) : ControllerBase { [HttpGet("{dataCenterId}")] [ValidateIdFilter] - [ProducesResponseType (StatusCodes.Status200OK, Type = typeof (ApiResult>))] + [ProducesResponseType (StatusCodes.Status200OK, Type = typeof (ApiResult>))] public async Task GetAll ([FromRoute] string dataCenterId) { var result = await serverService.GetAllAsync (dataCenterId); @@ -22,13 +21,13 @@ public async Task GetAll ([FromRoute] string dataCenterId) return NotFound (); } - return Ok (new ApiResult>(!result.IsError, result.Value)); + return Ok (new ApiResult>(!result.IsError, result.Value)); } [HttpGet ("{id}/server")] [ValidateIdFilter] - [ProducesResponseType (StatusCodes.Status200OK, Type = typeof (ApiResult))] + [ProducesResponseType (StatusCodes.Status200OK, Type = typeof (ApiResult))] [ProducesResponseType (StatusCodes.Status400BadRequest, Type = typeof (ProblemDetails))] [ProducesResponseType (StatusCodes.Status404NotFound, Type = typeof (ProblemDetails))] public async Task Get ([FromRoute] string id) @@ -42,7 +41,28 @@ public async Task Get ([FromRoute] string id) return NotFound (); } - return Ok (new ApiResult (!result.IsError, result.Value)); + return Ok (new ApiResult (!result.IsError, result.Value)); + } + + [HttpGet("{id}/config")] + public async Task GetConfiguration ([FromRoute] string id) + { + var result = await serverService.GetRemoteConfigurationAsync (id); + return Ok (result.Value); + } + + [HttpGet ("{id}/statistics")] + public async Task GetStatistics ([FromRoute] string id) + { + var result = await serverService.GetRemoteStatisticsAsync (id); + return Ok (result.Value); + } + + [HttpGet ("{id}/info")] + public async Task GetInfo ([FromRoute] string id) + { + var result = await serverService.GetRemoteServerInfoAsync (id); + return Ok (result.Value); } [HttpGet("datacenters")] @@ -60,21 +80,21 @@ public async Task HintDataCenters() [HttpPost] [VallidatModel] - [ProducesResponseType(StatusCodes.Status201Created, Type = typeof(ApiResult))] + [ProducesResponseType(StatusCodes.Status201Created, Type = typeof(ApiResult))] [ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(ProblemDetails))] [ProducesResponseType(StatusCodes.Status500InternalServerError, Type = typeof(ProblemDetails))] - public async Task Create ([FromBody] Server server) + public async Task Create ([FromBody] Dto.Manager.Server server) { var result = await serverService.CreateAsync(server); - return Created (string.Empty, new ApiResult (!result.IsError, result.Value)); + return Created (string.Empty, new ApiResult (!result.IsError, result.Value)); } [HttpPut ("{id}")] [ValidateIdFilter] [VallidatModel] - [ProducesResponseType (StatusCodes.Status200OK, Type = typeof (ApiResult))] + [ProducesResponseType (StatusCodes.Status200OK, Type = typeof (ApiResult))] [ProducesResponseType (StatusCodes.Status400BadRequest, Type = typeof (ProblemDetails))] - public async Task Update ([FromRoute] string id, [FromBody] Server server) + public async Task Update ([FromRoute] string id, [FromBody] Dto.Manager.Server server) { var result = await serverService.UpdateAsync(id, server); bool notFound = result.IsError && result.FirstError.Type == ErrorType.NotFound; @@ -82,14 +102,14 @@ public async Task Update ([FromRoute] string id, [FromBody] Serve { return NotFound (); } - return Ok (new ApiResult (!result.IsError, result.Value)); + return Ok (new ApiResult (!result.IsError, result.Value)); } [HttpDelete("{id}")] [ValidateIdFilter] - [ProducesResponseType (StatusCodes.Status200OK, Type = typeof (ApiResult))] + [ProducesResponseType (StatusCodes.Status200OK, Type = typeof (ApiResult))] [ProducesResponseType (StatusCodes.Status400BadRequest, Type = typeof (ProblemDetails))] - public async Task Deleted ([FromQuery] string id) + public async Task Deleted ([FromRoute] string id) { var result = await serverService.DeleteAsync(id); bool notFound = result.IsError && result.FirstError.Type == ErrorType.NotFound; diff --git a/src/hiPower.WebApi/Docs/power-dns-webapi.http b/src/hiPower.WebApi/Docs/power-dns-webapi.http index 888a6d3..dd75d75 100644 --- a/src/hiPower.WebApi/Docs/power-dns-webapi.http +++ b/src/hiPower.WebApi/Docs/power-dns-webapi.http @@ -1,4 +1,3 @@ -# For more info on HTTP files go to https://aka.ms/vs/httpfile # The variables that start with a capital letter are from the environment file. @apiUrl={{Proto}}://{{HostAddress}}:{{Port}}/api/v1 @metricsUrl={{Proto}}://{{HostAddress}}:{{Port}} @@ -20,6 +19,11 @@ X-API-Key: {{ApiKey}} Accept: {{accept}} ### +# Configuration +GET {{apiUrl}}/servers/{{serverId}}/config +X-API-Key: {{ApiKey}} +### + # Statistics GET {{apiUrl}}/servers/{{serverId}}/statistics X-API-Key: {{ApiKey}} diff --git a/src/hiPower.WebApi/Program.cs b/src/hiPower.WebApi/Program.cs index 20a9272..846fe90 100644 --- a/src/hiPower.WebApi/Program.cs +++ b/src/hiPower.WebApi/Program.cs @@ -2,6 +2,7 @@ using hiPower.Database.Extensions.DependencyInjection; using hiPower.Infrastructure.Extensions.DependencyInjection; using hiPower.WebApi.Extensions.DependencyInjection; +using Microsoft.AspNetCore.Mvc.ModelBinding; var builder = WebApplication.CreateBuilder(args); @@ -13,6 +14,7 @@ .ConfigureCoreServices () .ConfigureInfrastructureServices(builder.Configuration); + var app = builder.Build(); await app.Services.UseManagerSeederAsync (); diff --git a/src/hiPower.WebApi/hiPower.WebApi.csproj b/src/hiPower.WebApi/hiPower.WebApi.csproj index e083e43..9dffb6d 100644 --- a/src/hiPower.WebApi/hiPower.WebApi.csproj +++ b/src/hiPower.WebApi/hiPower.WebApi.csproj @@ -12,12 +12,12 @@ - + - + @@ -25,8 +25,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - - + diff --git a/test/hiPower.Test.Integration/hiPower.Test.Integration.csproj b/test/hiPower.Test.Integration/hiPower.Test.Integration.csproj index b9f3d8d..137c0c2 100644 --- a/test/hiPower.Test.Integration/hiPower.Test.Integration.csproj +++ b/test/hiPower.Test.Integration/hiPower.Test.Integration.csproj @@ -16,7 +16,6 @@ -