Compare commits
27 Commits
2d8d5442d1
...
6044d0bcb6
| Author | SHA1 | Date | |
|---|---|---|---|
| 6044d0bcb6 | |||
| aaaaf283ee | |||
| 2877d62f95 | |||
| 3ca148f341 | |||
| ad9f7ef7e4 | |||
| 4bb6a6cf18 | |||
| e85a4986e6 | |||
| 1ed1937c40 | |||
| 3082c0b77c | |||
| 6836b422a4 | |||
| 6b2c897e5b | |||
| db3137ef9d | |||
| 02b4aa342a | |||
| 57b273cde4 | |||
| aa192626c2 | |||
| 9142b9c49a | |||
| f994781713 | |||
| 65d59c6c67 | |||
| 32b631a6c2 | |||
| 3eacbd89f7 | |||
| 3af571ea37 | |||
| c21e4a93ef | |||
| dd60555ed3 | |||
| a7cbced3e6 | |||
| 786086a260 | |||
| 087df71b7b | |||
| a7a16ab281 |
@@ -11,7 +11,7 @@ public static class GetExtensions
|
|||||||
/// <param name="role">Optional role of the logged-in user.</param>
|
/// <param name="role">Optional role of the logged-in user.</param>
|
||||||
/// <param name="apiVersion">Optional API version.</param>
|
/// <param name="apiVersion">Optional API version.</param>
|
||||||
/// <returns>A <see cref="Task{Boolean}"/> representing whether the application is alive.</returns>
|
/// <returns>A <see cref="Task{Boolean}"/> representing whether the application is alive.</returns>
|
||||||
public static Task<bool> IsAliveAsync(this IMediator mediator, Role? role = null, int? apiVersion = null) =>
|
public static Task<bool> IsAliveAsync(this IMediator mediator, Role? role = null, int apiVersion = 1) =>
|
||||||
mediator.Send(new GetRequest(role)
|
mediator.Send(new GetRequest(role)
|
||||||
{
|
{
|
||||||
ApiVersion = apiVersion
|
ApiVersion = apiVersion
|
||||||
@@ -24,7 +24,7 @@ public static class GetExtensions
|
|||||||
/// <param name="role">Optional role of the logged-in user.</param>
|
/// <param name="role">Optional role of the logged-in user.</param>
|
||||||
/// <param name="apiVersion">Optional API version.</param>
|
/// <param name="apiVersion">Optional API version.</param>
|
||||||
/// <returns>True if the application is alive; otherwise, false.</returns>
|
/// <returns>True if the application is alive; otherwise, false.</returns>
|
||||||
public static bool IsAlive(this IMediator mediator, Role? role = null, int? apiVersion = null) =>
|
public static bool IsAlive(this IMediator mediator, Role? role = null, int apiVersion = 1) =>
|
||||||
mediator.Send(new GetRequest(role)
|
mediator.Send(new GetRequest(role)
|
||||||
{
|
{
|
||||||
ApiVersion = apiVersion
|
ApiVersion = apiVersion
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ public static class GetExtensions
|
|||||||
/// <returns>
|
/// <returns>
|
||||||
/// A task that represents the asynchronous operation. The task result contains the matching <see cref="Article"/>, or <c>null</c> if not found.
|
/// A task that represents the asynchronous operation. The task result contains the matching <see cref="Article"/>, or <c>null</c> if not found.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
public static async Task<Article?> GetArticleByIdAsync(this IMediator mediator, int id, int? apiVersion = null)
|
public static async Task<Article?> GetArticleByIdAsync(this IMediator mediator, int id, int apiVersion = 1)
|
||||||
{
|
{
|
||||||
var articles = await mediator.Send(new GetRequest(id) { ApiVersion = apiVersion });
|
var articles = await mediator.Send(new GetRequest(id) { ApiVersion = apiVersion });
|
||||||
return articles.FirstOrDefault();
|
return articles.FirstOrDefault();
|
||||||
@@ -34,7 +34,7 @@ public static class GetExtensions
|
|||||||
/// <returns>
|
/// <returns>
|
||||||
/// The matching <see cref="Article"/>, or <c>null</c> if not found.
|
/// The matching <see cref="Article"/>, or <c>null</c> if not found.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
public static Article? GetArticleById(this IMediator mediator, int id, int? apiVersion = null)
|
public static Article? GetArticleById(this IMediator mediator, int id, int apiVersion = 1)
|
||||||
{
|
{
|
||||||
var articles = mediator.Send(new GetRequest(id) { ApiVersion = apiVersion }).GetAwaiter().GetResult();
|
var articles = mediator.Send(new GetRequest(id) { ApiVersion = apiVersion }).GetAwaiter().GetResult();
|
||||||
return articles.FirstOrDefault();
|
return articles.FirstOrDefault();
|
||||||
@@ -52,7 +52,7 @@ public static class GetExtensions
|
|||||||
/// <returns>
|
/// <returns>
|
||||||
/// A task that represents the asynchronous operation. The task result contains a collection of all available <see cref="Article"/> entities.
|
/// A task that represents the asynchronous operation. The task result contains a collection of all available <see cref="Article"/> entities.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
public static Task<IEnumerable<Article>> GetAllArticlesAsync(this IMediator mediator, int? apiVersion = null) => mediator.Send(new GetRequest()
|
public static Task<IEnumerable<Article>> GetAllArticlesAsync(this IMediator mediator, int apiVersion = 1) => mediator.Send(new GetRequest()
|
||||||
{
|
{
|
||||||
ApiVersion = apiVersion
|
ApiVersion = apiVersion
|
||||||
});
|
});
|
||||||
@@ -65,7 +65,7 @@ public static class GetExtensions
|
|||||||
/// <returns>
|
/// <returns>
|
||||||
/// A collection of all available <see cref="Article"/> entities.
|
/// A collection of all available <see cref="Article"/> entities.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
public static IEnumerable<Article> GetAllArticles(this IMediator mediator, int? apiVersion = null) => mediator.Send(new GetRequest()
|
public static IEnumerable<Article> GetAllArticles(this IMediator mediator, int apiVersion = 1) => mediator.Send(new GetRequest()
|
||||||
{
|
{
|
||||||
ApiVersion = apiVersion
|
ApiVersion = apiVersion
|
||||||
}).GetAwaiter().GetResult();
|
}).GetAwaiter().GetResult();
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ public static class PostExtensions
|
|||||||
/// <param name="article">The <see cref="Article"/> entity to be created.</param>
|
/// <param name="article">The <see cref="Article"/> entity to be created.</param>
|
||||||
/// <param name="apiVersion">Optional API version to include in the request.</param>
|
/// <param name="apiVersion">Optional API version to include in the request.</param>
|
||||||
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
|
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
|
||||||
public static Task CreateArticleAsync(this IMediator mediator, Article article, int? apiVersion = null) => mediator.Send(new PostRequest(article)
|
public static Task CreateArticleAsync(this IMediator mediator, Article article, int apiVersion = 1) => mediator.Send(new PostRequest(article)
|
||||||
{
|
{
|
||||||
ApiVersion = apiVersion
|
ApiVersion = apiVersion
|
||||||
});
|
});
|
||||||
@@ -26,7 +26,7 @@ public static class PostExtensions
|
|||||||
/// <param name="mediator">The <see cref="IMediator"/> instance used to send the request.</param>
|
/// <param name="mediator">The <see cref="IMediator"/> instance used to send the request.</param>
|
||||||
/// <param name="article">The <see cref="Article"/> entity to be created.</param>
|
/// <param name="article">The <see cref="Article"/> entity to be created.</param>
|
||||||
/// <param name="apiVersion">Optional API version to include in the request.</param>
|
/// <param name="apiVersion">Optional API version to include in the request.</param>
|
||||||
public static void CreateArticle(this IMediator mediator, Article article, int? apiVersion = null) => mediator.Send(new PostRequest(article)
|
public static void CreateArticle(this IMediator mediator, Article article, int apiVersion = 1) => mediator.Send(new PostRequest(article)
|
||||||
{
|
{
|
||||||
ApiVersion = apiVersion
|
ApiVersion = apiVersion
|
||||||
}).GetAwaiter().GetResult();
|
}).GetAwaiter().GetResult();
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ public static class PutExtensions
|
|||||||
/// <param name="article">The <see cref="Article"/> entity to be updated.</param>
|
/// <param name="article">The <see cref="Article"/> entity to be updated.</param>
|
||||||
/// <param name="apiVersion">Optional API version to include in the request.</param>
|
/// <param name="apiVersion">Optional API version to include in the request.</param>
|
||||||
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
|
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
|
||||||
public static Task UpdateArticleAsync(this IMediator mediator, Article article, int? apiVersion = null) => mediator.Send(new PutRequest(article)
|
public static Task UpdateArticleAsync(this IMediator mediator, Article article, int apiVersion = 1) => mediator.Send(new PutRequest(article)
|
||||||
{
|
{
|
||||||
ApiVersion = apiVersion
|
ApiVersion = apiVersion
|
||||||
});
|
});
|
||||||
@@ -26,7 +26,7 @@ public static class PutExtensions
|
|||||||
/// <param name="mediator">The <see cref="IMediator"/> instance used to send the request.</param>
|
/// <param name="mediator">The <see cref="IMediator"/> instance used to send the request.</param>
|
||||||
/// <param name="article">The <see cref="Article"/> entity to be updated.</param>
|
/// <param name="article">The <see cref="Article"/> entity to be updated.</param>
|
||||||
/// <param name="apiVersion">Optional API version to include in the request.</param>
|
/// <param name="apiVersion">Optional API version to include in the request.</param>
|
||||||
public static void UpdateArticle(this IMediator mediator, Article article, int? apiVersion = null) => mediator.Send(new PutRequest(article)
|
public static void UpdateArticle(this IMediator mediator, Article article, int apiVersion = 1) => mediator.Send(new PutRequest(article)
|
||||||
{
|
{
|
||||||
ApiVersion = apiVersion
|
ApiVersion = apiVersion
|
||||||
}).GetAwaiter().GetResult();
|
}).GetAwaiter().GetResult();
|
||||||
|
|||||||
@@ -9,31 +9,13 @@ namespace Leanetec.EConnect.Client;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static class Client
|
public static class Client
|
||||||
{
|
{
|
||||||
private static Action<ClientOptions>? _options = null;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the configuration options for the EConnect client.
|
|
||||||
/// Can only be set once; subsequent attempts to set will result in an exception.
|
|
||||||
/// </summary>
|
|
||||||
public static Action<ClientOptions> Options
|
|
||||||
{
|
|
||||||
get => _options ?? throw new InvalidOperationException("EConnect Client options have not been configured. Please set the 'Client.Options' property before accessing client services.");
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (_options is null)
|
|
||||||
_options = value;
|
|
||||||
else
|
|
||||||
throw new InvalidOperationException("EConnect Client options have already been configured. Reassignment is not allowed.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Lazily initializes the <see cref="IServiceProvider"/> that registers and builds the EConnect client services.
|
/// Lazily initializes the <see cref="IServiceProvider"/> that registers and builds the EConnect client services.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static readonly Lazy<IServiceProvider> _serviceProvider = new(() =>
|
private static readonly Lazy<IServiceProvider> _serviceProvider = new(() =>
|
||||||
{
|
{
|
||||||
var services = new ServiceCollection();
|
var services = new ServiceCollection();
|
||||||
services.AddEConnectClient(Options);
|
services.AddClientServices();
|
||||||
return services.BuildServiceProvider();
|
return services.BuildServiceProvider();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
namespace Leanetec.EConnect.Client;
|
|
||||||
|
|
||||||
public class ClientOptions
|
|
||||||
{
|
|
||||||
public string? BaseAddress { get; set; }
|
|
||||||
|
|
||||||
public string? ApiKey { get; set; }
|
|
||||||
|
|
||||||
public TimeSpan? Timeout { get; set; }
|
|
||||||
}
|
|
||||||
@@ -1,36 +1,12 @@
|
|||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
|
|
||||||
namespace Leanetec.EConnect.Client;
|
namespace Leanetec.EConnect.Client;
|
||||||
|
|
||||||
public static class DependencyInjection
|
public static class DependencyInjection
|
||||||
{
|
{
|
||||||
public static IServiceCollection AddEConnectClient(this IServiceCollection services, Action<Config>? options = null)
|
public static IServiceCollection AddClientServices(this IServiceCollection services)
|
||||||
{
|
{
|
||||||
var config = new Config(services);
|
|
||||||
options?.Invoke(config);
|
|
||||||
|
|
||||||
services.AddMediatR(cfg => cfg.RegisterServicesFromAssembly(typeof(DependencyInjection).Assembly));
|
services.AddMediatR(cfg => cfg.RegisterServicesFromAssembly(typeof(DependencyInjection).Assembly));
|
||||||
return services;
|
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -4,7 +4,7 @@ namespace Leanetec.EConnect.Client.Dto;
|
|||||||
|
|
||||||
public record HttpRequestBase
|
public record HttpRequestBase
|
||||||
{
|
{
|
||||||
public int? ApiVersion { get; set; }
|
public int ApiVersion { get; set; } = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public record HttpRequest : HttpRequestBase, IRequest
|
public record HttpRequest : HttpRequestBase, IRequest
|
||||||
|
|||||||
@@ -4,8 +4,12 @@ namespace Leanetec.EConnect.Client.Interface;
|
|||||||
|
|
||||||
public interface IEConnectClient<TError> where TError : class
|
public interface IEConnectClient<TError> where TError : class
|
||||||
{
|
{
|
||||||
public Task<Response<TData, TError>> GetAsync<TData>(string? route = null, CancellationToken cancel = default) where TData : class;
|
public Task<Response<TData, TError>> GetAsync<TData>(string? route = null, object? queryParams = null, CancellationToken cancel = default) where TData : class;
|
||||||
|
|
||||||
public Task<Response<IAsyncEnumerable<TData?>, TError>> GetListAsAsyncEnumerable<TData>(string? route = null, CancellationToken cancel = default)
|
public Task<Response<IAsyncEnumerable<TData?>, TError>> GetListAsAsyncEnumerable<TData>(string? route = null, object? queryParams = null, CancellationToken cancel = default)
|
||||||
where TData : class;
|
where TData : class;
|
||||||
|
|
||||||
|
public Task<Response<TError>> PostAsync(string? route = null, object? queryParams = null, CancellationToken cancel = default);
|
||||||
|
|
||||||
|
public Task<Response<TError>> PostAsync(StreamContent content, string? route = null, object? queryParams = null, CancellationToken cancel = default);
|
||||||
}
|
}
|
||||||
29
src/Leanetec.EConnect.Client/Order/GetDocumentRequest.cs
Normal file
29
src/Leanetec.EConnect.Client/Order/GetDocumentRequest.cs
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
using Leanetec.EConnect.Client.Dto;
|
||||||
|
using Leanetec.EConnect.Client.Interface;
|
||||||
|
using Leanetec.EConnect.Domain.Entities;
|
||||||
|
using MediatR;
|
||||||
|
|
||||||
|
namespace Leanetec.EConnect.Client.Order;
|
||||||
|
|
||||||
|
public record GetDocumentRequest(string TenantId, int OrderId) : HttpRequest<Response<IEnumerable<OrderDocument>, ProblemDetail>>
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public class GetDocumentRequestHandler : IRequestHandler<GetDocumentRequest, Response<IEnumerable<OrderDocument>, ProblemDetail>>
|
||||||
|
{
|
||||||
|
private readonly IEConnectClient<ProblemDetail> _client;
|
||||||
|
|
||||||
|
public GetDocumentRequestHandler(IEConnectClient<ProblemDetail> client)
|
||||||
|
{
|
||||||
|
_client = client;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Response<IEnumerable<OrderDocument>, ProblemDetail>> Handle(GetDocumentRequest request, CancellationToken cancel)
|
||||||
|
{
|
||||||
|
return await _client.GetAsync<IEnumerable<OrderDocument>>(
|
||||||
|
$"api/public/v{request.ApiVersion}/econnect/order/document/list",
|
||||||
|
new { request.TenantId, request.OrderId },
|
||||||
|
cancel
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
30
src/Leanetec.EConnect.Client/Order/PostDocumentRequest.cs
Normal file
30
src/Leanetec.EConnect.Client/Order/PostDocumentRequest.cs
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
using Leanetec.EConnect.Client.Dto;
|
||||||
|
using Leanetec.EConnect.Client.Interface;
|
||||||
|
using Leanetec.EConnect.Domain.Entities;
|
||||||
|
using MediatR;
|
||||||
|
|
||||||
|
namespace Leanetec.EConnect.Client.Order;
|
||||||
|
|
||||||
|
public record PostDocumentRequest(string TenantId, int OrderId, StreamContent Content) : HttpRequest<Response<ProblemDetail>>
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PostDocumentRequestHandler : IRequestHandler<PostDocumentRequest, Response<ProblemDetail>>
|
||||||
|
{
|
||||||
|
private readonly IEConnectClient<ProblemDetail> _client;
|
||||||
|
|
||||||
|
public PostDocumentRequestHandler(IEConnectClient<ProblemDetail> client)
|
||||||
|
{
|
||||||
|
_client = client;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Response<ProblemDetail>> Handle(PostDocumentRequest request, CancellationToken cancel)
|
||||||
|
{
|
||||||
|
return await _client.PostAsync(
|
||||||
|
request.Content,
|
||||||
|
$"api/public/v{request.ApiVersion}/econnect/order/document/list",
|
||||||
|
new { request.TenantId, request.OrderId },
|
||||||
|
cancel
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,17 @@
|
|||||||
namespace Leanetec.EConnect.Domain.Entities;
|
using System.Net;
|
||||||
|
|
||||||
public record Response<TData, TError>(bool Ok, TData? Data = null, TError? Error = null)
|
namespace Leanetec.EConnect.Domain.Entities;
|
||||||
where TData : class
|
|
||||||
where TError : class;
|
public record Response<TError>() where TError : class
|
||||||
|
{
|
||||||
|
public bool Ok { get; init; }
|
||||||
|
|
||||||
|
public HttpStatusCode? StatusCode { get; init; }
|
||||||
|
|
||||||
|
public TError? Error { get; init; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public record Response<TData, TError> : Response<TError> where TData : class where TError : class
|
||||||
|
{
|
||||||
|
public TData? Data { get; init; }
|
||||||
|
}
|
||||||
34
src/Leanetec.EConnect.Infrastructure/ClientOptions.cs
Normal file
34
src/Leanetec.EConnect.Infrastructure/ClientOptions.cs
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
namespace Leanetec.EConnect.Infrastructure;
|
||||||
|
|
||||||
|
public class ClientOptions
|
||||||
|
{
|
||||||
|
public string? BaseAddress { get; set; }
|
||||||
|
|
||||||
|
public TimeSpan? Timeout { get; set; }
|
||||||
|
|
||||||
|
public Dictionary<string, string?>? DefaultQueryStrings { get; set; }
|
||||||
|
|
||||||
|
public Action<HttpClient>? AfterHttpInit { get; set; }
|
||||||
|
|
||||||
|
private string? _jsonSerializerDateFormat = null;
|
||||||
|
|
||||||
|
public string? JsonSerializerDateFormat
|
||||||
|
{
|
||||||
|
get => _jsonSerializerDateFormat;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_jsonSerializerDateFormat = value;
|
||||||
|
if (value is not null)
|
||||||
|
{
|
||||||
|
JsonSerializerOptions.Converters.Add(new DateTimeConverter(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public JsonSerializerOptions JsonSerializerOptions { get; set; } = new()
|
||||||
|
{
|
||||||
|
PropertyNameCaseInsensitive = true
|
||||||
|
};
|
||||||
|
}
|
||||||
26
src/Leanetec.EConnect.Infrastructure/DateTimeConverter.cs
Normal file
26
src/Leanetec.EConnect.Infrastructure/DateTimeConverter.cs
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
using System.Globalization;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Leanetec.EConnect.Infrastructure;
|
||||||
|
|
||||||
|
public class DateTimeConverter : JsonConverter<DateTime>
|
||||||
|
{
|
||||||
|
private readonly string _format;
|
||||||
|
|
||||||
|
public DateTimeConverter(string format)
|
||||||
|
{
|
||||||
|
_format = format;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
var value = reader.GetString();
|
||||||
|
return DateTime.ParseExact(value!, _format, CultureInfo.InvariantCulture);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
writer.WriteStringValue(value.ToString(_format));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
using Leanetec.EConnect.Client;
|
using Leanetec.EConnect.Client.Interface;
|
||||||
using Leanetec.EConnect.Client.Interface;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
|
|
||||||
@@ -9,7 +9,7 @@ public static class DependencyInjection
|
|||||||
{
|
{
|
||||||
internal static readonly string EConnectClientName = Guid.NewGuid().ToString();
|
internal static readonly string EConnectClientName = Guid.NewGuid().ToString();
|
||||||
|
|
||||||
internal static IServiceCollection ConfigureEConnectClient(this IServiceCollection services, Action<HttpClient> configureClient)
|
internal static IServiceCollection AddEConnectHttpClient(this IServiceCollection services)
|
||||||
{
|
{
|
||||||
services.AddHttpClient(EConnectClientName, (provider, client) => {
|
services.AddHttpClient(EConnectClientName, (provider, client) => {
|
||||||
var opt = provider.GetRequiredService<IOptions<ClientOptions>>().Value;
|
var opt = provider.GetRequiredService<IOptions<ClientOptions>>().Value;
|
||||||
@@ -21,9 +21,9 @@ public static class DependencyInjection
|
|||||||
if (opt.Timeout is TimeSpan timeout)
|
if (opt.Timeout is TimeSpan timeout)
|
||||||
client.Timeout = timeout;
|
client.Timeout = timeout;
|
||||||
|
|
||||||
// add spesific (library based) parameters
|
opt.AfterHttpInit?.Invoke(client);
|
||||||
configureClient(client);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -31,7 +31,7 @@ public static class DependencyInjection
|
|||||||
{
|
{
|
||||||
Config config = new(services);
|
Config config = new(services);
|
||||||
options?.Invoke(config);
|
options?.Invoke(config);
|
||||||
services.ConfigureEConnectClient(config.EConnectClient);
|
services.AddEConnectHttpClient();
|
||||||
services.AddScoped(typeof(IEConnectClient<>), typeof(EConnectClient<>));
|
services.AddScoped(typeof(IEConnectClient<>), typeof(EConnectClient<>));
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
@@ -45,6 +45,14 @@ public static class DependencyInjection
|
|||||||
_services = services;
|
_services = services;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Action<HttpClient> EConnectClient { get; set; } = _ => { };
|
public void ConfigureClient(IConfiguration config)
|
||||||
|
{
|
||||||
|
_services.Configure<ClientOptions>(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ConfigureClient(Action<ClientOptions> options)
|
||||||
|
{
|
||||||
|
_services.Configure(options);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using Leanetec.EConnect.Client;
|
using Leanetec.EConnect.Client.Interface;
|
||||||
using Leanetec.EConnect.Client.Interface;
|
|
||||||
using Leanetec.EConnect.Domain.Entities;
|
using Leanetec.EConnect.Domain.Entities;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using System.Net.Http.Json;
|
using System.Net.Http.Json;
|
||||||
@@ -20,37 +19,128 @@ public class EConnectClient<TError> : IEConnectClient<TError> where TError : cl
|
|||||||
LazyHttp = new Lazy<HttpClient>(httpFactory.CreateEConnectClient);
|
LazyHttp = new Lazy<HttpClient>(httpFactory.CreateEConnectClient);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Response<TData, TError>> GetAsync<TData>(string? route = null, CancellationToken cancel = default)
|
private string? AddQueryString(string? route = null, object? queryParams = null)
|
||||||
|
{
|
||||||
|
// add global query strings
|
||||||
|
if (_options.DefaultQueryStrings is not null)
|
||||||
|
route = route.AddQueryString(_options.DefaultQueryStrings);
|
||||||
|
|
||||||
|
// add query strings
|
||||||
|
if (queryParams is not null)
|
||||||
|
route = route.AddQueryString(queryParams.ToPropertyDictionary());
|
||||||
|
|
||||||
|
return route;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Response<TData, TError>> GetAsync<TData>(string? route = null, object? queryParams = null, CancellationToken cancel = default)
|
||||||
where TData : class
|
where TData : class
|
||||||
{
|
{
|
||||||
|
route = AddQueryString(route, queryParams);
|
||||||
|
|
||||||
var res = await Http.GetAsync(route, cancel);
|
var res = await Http.GetAsync(route, cancel);
|
||||||
|
|
||||||
if (res.IsSuccessStatusCode)
|
if (res.IsSuccessStatusCode)
|
||||||
{
|
{
|
||||||
var data = await res.Content.ReadFromJsonAsync<TData>(cancel);
|
var data = await res.Content.ReadFromJsonAsync<TData>(_options.JsonSerializerOptions, cancel);
|
||||||
return new Response<TData, TError>(true, Data: data);
|
return new ()
|
||||||
|
{
|
||||||
|
Ok = true,
|
||||||
|
StatusCode = res.StatusCode,
|
||||||
|
Data = data
|
||||||
|
};
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var error = await res.Content.ReadFromJsonAsync<TError>(cancel);
|
var error = await res.Content.ReadFromJsonAsync<TError>(_options.JsonSerializerOptions, cancel);
|
||||||
return new Response<TData, TError>(false, Error: error);
|
return new ()
|
||||||
|
{
|
||||||
|
Ok = false,
|
||||||
|
StatusCode = res.StatusCode,
|
||||||
|
Error = error
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Response<IAsyncEnumerable<TData?>, TError>> GetListAsAsyncEnumerable<TData>(string? route = null, CancellationToken cancel = default)
|
public async Task<Response<IAsyncEnumerable<TData?>, TError>> GetListAsAsyncEnumerable<TData>(string? route = null, object? queryParams = null, CancellationToken cancel = default)
|
||||||
where TData : class
|
where TData : class
|
||||||
{
|
{
|
||||||
|
route = AddQueryString(route, queryParams);
|
||||||
|
|
||||||
var res = await Http.GetAsync(route, cancel);
|
var res = await Http.GetAsync(route, cancel);
|
||||||
|
|
||||||
if (res.IsSuccessStatusCode)
|
if (res.IsSuccessStatusCode)
|
||||||
{
|
{
|
||||||
var data = res.Content.ReadFromJsonAsAsyncEnumerable<TData>(cancel);
|
var data = res.Content.ReadFromJsonAsAsyncEnumerable<TData>(_options.JsonSerializerOptions, cancel);
|
||||||
return new Response<IAsyncEnumerable<TData?>, TError>(true, Data: data);
|
return new ()
|
||||||
|
{
|
||||||
|
Ok = true,
|
||||||
|
StatusCode = res.StatusCode,
|
||||||
|
Data = data
|
||||||
|
};
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var error = await res.Content.ReadFromJsonAsync<TError>(cancellationToken: cancel);
|
var error = await res.Content.ReadFromJsonAsync<TError>(_options.JsonSerializerOptions, cancel);
|
||||||
return new Response<IAsyncEnumerable<TData?>, TError>(false, Error: error);
|
return new()
|
||||||
|
{
|
||||||
|
Ok = false,
|
||||||
|
StatusCode = res.StatusCode,
|
||||||
|
Error = error
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Response<TError>> PostAsync(string? route = null, object? queryParams = null, CancellationToken cancel = default)
|
||||||
|
where TData : class
|
||||||
|
{
|
||||||
|
route = AddQueryString(route, queryParams);
|
||||||
|
|
||||||
|
var res = await Http.PostAsync(route, null, cancel);
|
||||||
|
|
||||||
|
if (res.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
return new()
|
||||||
|
{
|
||||||
|
Ok = true,
|
||||||
|
StatusCode = res.StatusCode
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var error = await res.Content.ReadFromJsonAsync<TError>(_options.JsonSerializerOptions, cancel);
|
||||||
|
return new()
|
||||||
|
{
|
||||||
|
Ok = false,
|
||||||
|
StatusCode = res.StatusCode,
|
||||||
|
Error = error
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Response<TError>> PostAsync(StreamContent content, string? route = null, object? queryParams = null, CancellationToken cancel = default)
|
||||||
|
where TData : class
|
||||||
|
{
|
||||||
|
route = AddQueryString(route, queryParams);
|
||||||
|
|
||||||
|
var res = await Http.PostAsync(route, content, cancel);
|
||||||
|
|
||||||
|
if (res.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
return new()
|
||||||
|
{
|
||||||
|
Ok = true,
|
||||||
|
StatusCode = res.StatusCode
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var error = await res.Content.ReadFromJsonAsync<TError>(_options.JsonSerializerOptions, cancel);
|
||||||
|
return new()
|
||||||
|
{
|
||||||
|
Ok = false,
|
||||||
|
StatusCode = res.StatusCode,
|
||||||
|
Error = error
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -11,15 +11,16 @@ public static class ObjectExtensions
|
|||||||
.GetType()
|
.GetType()
|
||||||
.GetProperties(BindingFlags.Instance | BindingFlags.Public)
|
.GetProperties(BindingFlags.Instance | BindingFlags.Public)
|
||||||
.ToDictionary(
|
.ToDictionary(
|
||||||
prop => prop.Name,
|
prop => prop.Name.ToCamelCase(),
|
||||||
prop => prop.GetValue(obj).ToSafeString()
|
prop => prop.GetValue(obj).ToSafeString()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Dictionary<string, string?> ToPropertyDictionary(this Dictionary<string, object?> obj)
|
public static Dictionary<string, string?> ToPropertyDictionary(this Dictionary<string, object?> obj)
|
||||||
{
|
{
|
||||||
return obj
|
return obj
|
||||||
.ToDictionary(
|
.ToDictionary(
|
||||||
prop => prop.Key,
|
prop => prop.Key.ToCamelCase(),
|
||||||
prop => prop.Value?.ToString()
|
prop => prop.Value?.ToString()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -29,12 +30,23 @@ public static class ObjectExtensions
|
|||||||
? (b ? "true" : "false")
|
? (b ? "true" : "false")
|
||||||
: obj?.ToString();
|
: obj?.ToString();
|
||||||
|
|
||||||
public static string? AddQueryString(this string? route, Dictionary<string, string?> queryPrams)
|
public static string? AddQueryString(this string? route, Dictionary<string, string?> queryParams)
|
||||||
{
|
{
|
||||||
if (queryPrams.Count > 0)
|
if (queryParams.Count > 0)
|
||||||
route = QueryHelpers.AddQueryString(route ?? string.Empty, queryPrams);
|
route = QueryHelpers.AddQueryString(route ?? string.Empty, queryParams);
|
||||||
return route;
|
return route;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string? AddQueryString(this string? route, object queryPrams) => route.AddQueryString(queryPrams.ToPropertyDictionary());
|
public static string? AddQueryString(this string? route, object queryParams) => route.AddQueryString(queryParams.ToPropertyDictionary());
|
||||||
|
|
||||||
|
public static string ToCamelCase(this string input)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(input))
|
||||||
|
return input;
|
||||||
|
|
||||||
|
if (char.IsLower(input[0]))
|
||||||
|
return input;
|
||||||
|
|
||||||
|
return char.ToLowerInvariant(input[0]) + input[1..];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
31
src/Leanetec.EConnect.Proxy/Controllers/OrderController.cs
Normal file
31
src/Leanetec.EConnect.Proxy/Controllers/OrderController.cs
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
using Leanetec.EConnect.Client.Order;
|
||||||
|
using MediatR;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
namespace Leanetec.EConnect.Proxy.Controllers;
|
||||||
|
|
||||||
|
[Route("api/[controller]")]
|
||||||
|
[ApiController]
|
||||||
|
public class OrderController : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly IMediator _mediator;
|
||||||
|
|
||||||
|
public OrderController(IMediator mediator)
|
||||||
|
{
|
||||||
|
_mediator = mediator;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("document")]
|
||||||
|
public async Task<IActionResult> GetDocument([FromQuery] GetDocumentRequest request, CancellationToken cancel)
|
||||||
|
{
|
||||||
|
var res = await _mediator.Send(request, cancel);
|
||||||
|
if(res.Ok)
|
||||||
|
{
|
||||||
|
return res.Data is null || !res.Data.Any() ? NotFound() : Ok(res.Data);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return StatusCode(res?.Error?.Status ?? 500, res?.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,10 +10,6 @@
|
|||||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.6.2" />
|
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.6.2" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Folder Include="Controllers\" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Leanetec.EConnect.Client\Leanetec.EConnect.Client.csproj" />
|
<ProjectReference Include="..\Leanetec.EConnect.Client\Leanetec.EConnect.Client.csproj" />
|
||||||
<ProjectReference Include="..\Leanetec.EConnect.Infrastructure\Leanetec.EConnect.Infrastructure.csproj" />
|
<ProjectReference Include="..\Leanetec.EConnect.Infrastructure\Leanetec.EConnect.Infrastructure.csproj" />
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ var config = builder.Configuration;
|
|||||||
|
|
||||||
// Add services to the container.
|
// Add services to the container.
|
||||||
var clientOpt = config.GetSection("EConnect");
|
var clientOpt = config.GetSection("EConnect");
|
||||||
builder.Services.AddEConnectClient(opt => opt.ConfigureClient(clientOpt)).AddInfrastructureServices();
|
builder.Services.AddClientServices().AddInfrastructureServices(opt => opt.ConfigureClient(clientOpt));
|
||||||
|
|
||||||
builder.Services.AddControllers();
|
builder.Services.AddControllers();
|
||||||
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
|
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
|
||||||
|
|||||||
@@ -8,6 +8,9 @@
|
|||||||
"AllowedHosts": "*",
|
"AllowedHosts": "*",
|
||||||
"EConnect": {
|
"EConnect": {
|
||||||
"BaseAddress": "https://portal.demoportal01.leanetec.com",
|
"BaseAddress": "https://portal.demoportal01.leanetec.com",
|
||||||
"apiKey": "HGpGp3vk3MsKgSe0tKVZ1ZRCuq6bFoJ1"
|
"DefaultQueryStrings": {
|
||||||
|
"apiKey": "HGpGp3vk3MsKgSe0tKVZ1ZRCuq6bFoJ1"
|
||||||
|
},
|
||||||
|
"JsonSerializerDateFormat": "yyyy-MM-dd HH:mm"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user