Compare commits

...

12 Commits

Author SHA1 Message Date
Developer 02
2d8d5442d1 bind appsettings 2025-08-14 18:57:27 +02:00
Developer 02
fe198615fc Verbinde HttpClient mit ClientOptions 2025-08-14 18:46:04 +02:00
Developer 02
8c6719f516 feat(ClientOptions): add BaseAddress and Timeout 2025-08-14 18:39:53 +02:00
Developer 02
8db4b6e15a add dependencies and configure AddEConnectClient 2025-08-14 18:33:21 +02:00
Developer 02
92910ecb19 add proxy server 2025-08-14 18:27:01 +02:00
Developer 02
ce33b50953 merge options 2025-08-14 18:20:08 +02:00
Developer 02
2e59c090a8 feat(EConnectClientOptions): created and inject 2025-08-14 17:49:04 +02:00
Developer 02
c6ec3ca054 Refactor: Verwenden Sie Lazy<HttpClient> in EConnectClient, um die Erstellung zu verzögern. 2025-08-14 17:41:39 +02:00
Developer 02
9117a23be3 refactor(econnect-client): Gespeicherten HttpClient entfernen und pro Anfrage erstellen
- Feld „_http“ durch „_httpFactory“ ersetzt, um die Erstellung von HttpClient zu verzögern
- Eigenschaft „Http“ hinzugefügt, um einen neuen Client aus der Factory abzurufen
- Alle Verwendungen aktualisiert, um die Eigenschaft „Http“ anstelle der gespeicherten Instanz zu verwenden
2025-08-14 17:27:28 +02:00
Developer 02
4b8217bb80 add ToPropertyDictionary to convert Dictionary<string, object?> to Dictionary<string, string?> 2025-08-14 17:20:59 +02:00
Developer 02
6cf47dc617 feat: AddQueryString-Erweiterungsmethoden für String-Routen hinzufügen 2025-08-14 17:05:53 +02:00
Developer 02
94c6813306 feat: Hinzufügen von ObjectExtensions mit Eigenschaft-zu-Wörterbuch und sicherer Zeichenfolgenkonvertierung 2025-08-14 16:34:56 +02:00
13 changed files with 227 additions and 14 deletions

View File

@ -17,6 +17,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docs", "docs", "{AC628874-E
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Leanetec.EConnect.Infrastructure", "src\Leanetec.EConnect.Infrastructure\Leanetec.EConnect.Infrastructure.csproj", "{88A1AA25-45F3-4082-8B5A-F95FC8A21057}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Leanetec.EConnect.Proxy", "src\Leanetec.EConnect.Proxy\Leanetec.EConnect.Proxy.csproj", "{79D9868E-6A67-45C1-BA3E-1C2E822ECC58}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -35,6 +37,10 @@ Global
{88A1AA25-45F3-4082-8B5A-F95FC8A21057}.Debug|Any CPU.Build.0 = Debug|Any CPU
{88A1AA25-45F3-4082-8B5A-F95FC8A21057}.Release|Any CPU.ActiveCfg = Release|Any CPU
{88A1AA25-45F3-4082-8B5A-F95FC8A21057}.Release|Any CPU.Build.0 = Release|Any CPU
{79D9868E-6A67-45C1-BA3E-1C2E822ECC58}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{79D9868E-6A67-45C1-BA3E-1C2E822ECC58}.Debug|Any CPU.Build.0 = Debug|Any CPU
{79D9868E-6A67-45C1-BA3E-1C2E822ECC58}.Release|Any CPU.ActiveCfg = Release|Any CPU
{79D9868E-6A67-45C1-BA3E-1C2E822ECC58}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -43,6 +49,7 @@ Global
{34DC964A-1905-7DFC-0125-D551D3EEFCDD} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
{9242EEA9-447B-44A6-A66D-D671DD16D0BB} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
{88A1AA25-45F3-4082-8B5A-F95FC8A21057} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
{79D9868E-6A67-45C1-BA3E-1C2E822ECC58} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {8C81AD6F-B903-4C78-873C-38EE216EFAD5}

View File

@ -2,5 +2,9 @@
public class ClientOptions
{
public int ApiVersion { get; set; } = 1;
public string? BaseAddress { get; set; }
public string? ApiKey { get; set; }
public TimeSpan? Timeout { get; set; }
}

View File

@ -1,13 +1,36 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
namespace Leanetec.EConnect.Client;
public static class DependencyInjection
{
public static IServiceCollection AddEConnectClient(this IServiceCollection services, Action<ClientOptions> clientOptions)
public static IServiceCollection AddEConnectClient(this IServiceCollection services, Action<Config>? options = null)
{
services.Configure(clientOptions);
var config = new Config(services);
options?.Invoke(config);
services.AddMediatR(cfg => cfg.RegisterServicesFromAssembly(typeof(DependencyInjection).Assembly));
return services;
}
public sealed class Config
{
private readonly IServiceCollection _services;
internal Config(IServiceCollection services)
{
_services = services;
}
public void ConfigureClient(IConfiguration config)
{
_services.Configure<ClientOptions>(config);
}
public void ConfigureClient(Action<ClientOptions> options)
{
_services.Configure(options);
}
}
}

View File

@ -8,6 +8,8 @@
<ItemGroup>
<PackageReference Include="MediatR" Version="13.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="8.0.0" />
</ItemGroup>
<ItemGroup>

View File

@ -1,5 +1,7 @@
using Leanetec.EConnect.Client.Interface;
using Leanetec.EConnect.Client;
using Leanetec.EConnect.Client.Interface;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
namespace Leanetec.EConnect.Infrastructure;
@ -9,14 +11,26 @@ public static class DependencyInjection
internal static IServiceCollection ConfigureEConnectClient(this IServiceCollection services, Action<HttpClient> configureClient)
{
services.AddHttpClient(EConnectClientName, configureClient);
services.AddHttpClient(EConnectClientName, (provider, client) => {
var opt = provider.GetRequiredService<IOptions<ClientOptions>>().Value;
// add common parameters
if (opt.BaseAddress is string baseAddress)
client.BaseAddress = new Uri(baseAddress);
if (opt.Timeout is TimeSpan timeout)
client.Timeout = timeout;
// add spesific (library based) parameters
configureClient(client);
});
return services;
}
public static IServiceCollection AddInfrastructureServices(this IServiceCollection services, Action<Config> options)
public static IServiceCollection AddInfrastructureServices(this IServiceCollection services, Action<Config>? options = null)
{
Config config = new(services);
options.Invoke(config);
options?.Invoke(config);
services.ConfigureEConnectClient(config.EConnectClient);
services.AddScoped(typeof(IEConnectClient<>), typeof(EConnectClient<>));
return services;

View File

@ -1,22 +1,29 @@
using Leanetec.EConnect.Client.Interface;
using Leanetec.EConnect.Client;
using Leanetec.EConnect.Client.Interface;
using Leanetec.EConnect.Domain.Entities;
using Microsoft.Extensions.Options;
using System.Net.Http.Json;
namespace Leanetec.EConnect.Infrastructure;
public class EConnectClient<TError> : IEConnectClient<TError> where TError : class
{
private readonly HttpClient _http;
private readonly ClientOptions _options;
public EConnectClient(IHttpClientFactory factory)
private readonly Lazy<HttpClient> LazyHttp;
private HttpClient Http => LazyHttp.Value;
public EConnectClient(IOptions<ClientOptions> options, IHttpClientFactory httpFactory)
{
_http = factory.CreateEConnectClient();
_options = options.Value;
LazyHttp = new Lazy<HttpClient>(httpFactory.CreateEConnectClient);
}
public async Task<Response<TData, TError>> GetAsync<TData>(string? route = null, CancellationToken cancel = default)
where TData : class
{
var res = await _http.GetAsync(route, cancel);
var res = await Http.GetAsync(route, cancel);
if (res.IsSuccessStatusCode)
{
@ -33,7 +40,7 @@ public class EConnectClient<TError> : IEConnectClient<TError> where TError : cl
public async Task<Response<IAsyncEnumerable<TData?>, TError>> GetListAsAsyncEnumerable<TData>(string? route = null, CancellationToken cancel = default)
where TData : class
{
var res = await _http.GetAsync(route, cancel);
var res = await Http.GetAsync(route, cancel);
if (res.IsSuccessStatusCode)
{

View File

@ -7,6 +7,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.WebUtilities" Version="8.0.19" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.2" />
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.1" />
</ItemGroup>

View File

@ -0,0 +1,40 @@
using Microsoft.AspNetCore.WebUtilities;
using System.Reflection;
namespace Leanetec.EConnect.Infrastructure;
public static class ObjectExtensions
{
public static Dictionary<string, string?> ToPropertyDictionary(this object obj)
{
return obj
.GetType()
.GetProperties(BindingFlags.Instance | BindingFlags.Public)
.ToDictionary(
prop => prop.Name,
prop => prop.GetValue(obj).ToSafeString()
);
}
public static Dictionary<string, string?> ToPropertyDictionary(this Dictionary<string, object?> obj)
{
return obj
.ToDictionary(
prop => prop.Key,
prop => prop.Value?.ToString()
);
}
public static string? ToSafeString(this object? obj)
=> obj is bool b
? (b ? "true" : "false")
: obj?.ToString();
public static string? AddQueryString(this string? route, Dictionary<string, string?> queryPrams)
{
if (queryPrams.Count > 0)
route = QueryHelpers.AddQueryString(route ?? string.Empty, queryPrams);
return route;
}
public static string? AddQueryString(this string? route, object queryPrams) => route.AddQueryString(queryPrams.ToPropertyDictionary());
}

View File

@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.6.2" />
</ItemGroup>
<ItemGroup>
<Folder Include="Controllers\" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Leanetec.EConnect.Client\Leanetec.EConnect.Client.csproj" />
<ProjectReference Include="..\Leanetec.EConnect.Infrastructure\Leanetec.EConnect.Infrastructure.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,31 @@
using Leanetec.EConnect.Client;
using Leanetec.EConnect.Infrastructure;
var builder = WebApplication.CreateBuilder(args);
var config = builder.Configuration;
// Add services to the container.
var clientOpt = config.GetSection("EConnect");
builder.Services.AddEConnectClient(opt => opt.ConfigureClient(clientOpt)).AddInfrastructureServices();
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();

View File

@ -0,0 +1,41 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:25272",
"sslPort": 44340
}
},
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"launchUrl": "swagger",
"applicationUrl": "http://localhost:5254",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"launchUrl": "swagger",
"applicationUrl": "https://localhost:7067;http://localhost:5254",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "swagger",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

View File

@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}

View File

@ -0,0 +1,13 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"EConnect": {
"BaseAddress": "https://portal.demoportal01.leanetec.com",
"apiKey": "HGpGp3vk3MsKgSe0tKVZ1ZRCuq6bFoJ1"
}
}