some more work on api
This commit is contained in:
10
Pilz.Extensions/Collections/IEnumerableExtensions.cs
Normal file
10
Pilz.Extensions/Collections/IEnumerableExtensions.cs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
namespace Pilz.Extensions.Collections;
|
||||||
|
|
||||||
|
public static class IEnumerableExtensions
|
||||||
|
{
|
||||||
|
public static void ForEach<T>(this IEnumerable<T> @this, Action<T> action)
|
||||||
|
{
|
||||||
|
foreach (var t in @this)
|
||||||
|
action(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,9 +1,11 @@
|
|||||||
namespace Pilz.Net.Api;
|
namespace Pilz.Net.Api;
|
||||||
|
|
||||||
public record class ApiAuthCheckEventArgs(string AuthKey)
|
public class ApiAuthCheckEventArgs(string authKey) : EventArgs
|
||||||
{
|
{
|
||||||
private bool hasDenyed;
|
private bool hasDenyed;
|
||||||
|
|
||||||
|
public string AuthKey { get; } = authKey;
|
||||||
|
|
||||||
public bool Valid { get; set; }
|
public bool Valid { get; set; }
|
||||||
|
|
||||||
public void Deny()
|
public void Deny()
|
||||||
|
|||||||
@@ -14,24 +14,24 @@ public class ApiClient(string apiUrl) : IApiClient
|
|||||||
|
|
||||||
public ILogger Log { get; set; } = NullLogger.Instance;
|
public ILogger Log { get; set; } = NullLogger.Instance;
|
||||||
|
|
||||||
public virtual async Task<ApiResponse> SendMessage<TResponse>(string url, ApiMessage message, IMessageSerializer? serializer)
|
public virtual async Task<ApiResponse> SendMessage<TResponse>(string route, ApiMessage message, IMessageSerializer? serializer = null)
|
||||||
{
|
{
|
||||||
serializer ??= Serializer;
|
serializer ??= Serializer;
|
||||||
|
|
||||||
Log.InfoFormat("Send message to {0}", url);
|
Log.InfoFormat("Send message to {0}", route);
|
||||||
|
|
||||||
var res = await Send(url, message, serializer);
|
var res = await Send(route, message, serializer);
|
||||||
|
|
||||||
return new(res.StatusCode);
|
return new(res.StatusCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual async Task<ApiResponse<TResponse>> SendRequest<TResponse>(string url, ApiMessage message, IMessageSerializer? serializer) where TResponse : ApiMessage
|
public virtual async Task<ApiResponse<TResponse>> SendRequest<TResponse>(string route, ApiMessage message, IMessageSerializer? serializer = null) where TResponse : ApiMessage
|
||||||
{
|
{
|
||||||
serializer ??= Serializer;
|
serializer ??= Serializer;
|
||||||
|
|
||||||
Log.InfoFormat("Send request to {0}", url);
|
Log.InfoFormat("Send request to {0}", route);
|
||||||
|
|
||||||
var res = await Send(url, message, serializer);
|
var res = await Send(route, message, serializer);
|
||||||
TResponse? result = null;
|
TResponse? result = null;
|
||||||
|
|
||||||
if (res.IsSuccessStatusCode)
|
if (res.IsSuccessStatusCode)
|
||||||
@@ -40,11 +40,16 @@ public class ApiClient(string apiUrl) : IApiClient
|
|||||||
return new(res.StatusCode, result);
|
return new(res.StatusCode, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual async Task<HttpResponseMessage> Send(string url, ApiMessage message, IMessageSerializer serializer)
|
protected virtual async Task<HttpResponseMessage> Send(string route, ApiMessage message, IMessageSerializer serializer)
|
||||||
{
|
{
|
||||||
var fullRequestUrl = ApiUrl + url;
|
var url = ApiUrl + route;
|
||||||
var content = new StringContent(serializer.Serialize(message)!, null, "application/json");
|
var content = new StringContent(serializer.Serialize(message)!, null, "application/json");
|
||||||
content.Headers.Add("API-AUTH-KEY", AuthKey);
|
content.Headers.Add("API-AUTH-KEY", EncodeAuthKey());
|
||||||
return await httpClient.PostAsync(fullRequestUrl, content);
|
return await httpClient.PostAsync(url, content);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual string? EncodeAuthKey()
|
||||||
|
{
|
||||||
|
return AuthKey;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
9
Pilz.Net/Api/ApiRequestInfo.cs
Normal file
9
Pilz.Net/Api/ApiRequestInfo.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
|
||||||
|
namespace Pilz.Net.Api;
|
||||||
|
|
||||||
|
public record class ApiRequestInfo(
|
||||||
|
ApiMessage Message,
|
||||||
|
[property: MemberNotNullWhen(true, "AuthKey")]
|
||||||
|
bool IsAuthenticated,
|
||||||
|
string? AuthKey);
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using System.Net;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.Net;
|
||||||
|
|
||||||
namespace Pilz.Net.Api;
|
namespace Pilz.Net.Api;
|
||||||
|
|
||||||
@@ -7,9 +8,10 @@ public record class ApiResponse<T>(
|
|||||||
T? Message)
|
T? Message)
|
||||||
where T : ApiMessage
|
where T : ApiMessage
|
||||||
{
|
{
|
||||||
|
[MemberNotNull(nameof(Message))]
|
||||||
public void EnsureOk()
|
public void EnsureOk()
|
||||||
{
|
{
|
||||||
if (StatusCode != HttpStatusCode.OK)
|
if (StatusCode != HttpStatusCode.OK || Message is null)
|
||||||
throw new Exception("Api return is not ok!");
|
throw new Exception("Api return is not ok!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -6,13 +6,21 @@ public record class ApiResult(
|
|||||||
HttpStatusCode StatusCode,
|
HttpStatusCode StatusCode,
|
||||||
ApiMessage? Message = null)
|
ApiMessage? Message = null)
|
||||||
{
|
{
|
||||||
public static ApiResult Ok()
|
public static ApiResult Ok() => new(HttpStatusCode.OK);
|
||||||
{
|
|
||||||
return new(HttpStatusCode.OK);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ApiResult Ok(ApiMessage message)
|
public static ApiResult Ok(ApiMessage message) => new(HttpStatusCode.OK, message);
|
||||||
{
|
|
||||||
return new(HttpStatusCode.OK, message);
|
public static ApiResult Unauthorized() => new(HttpStatusCode.Unauthorized);
|
||||||
}
|
|
||||||
|
public static ApiResult NotFound() => new(HttpStatusCode.NotFound);
|
||||||
|
|
||||||
|
public static ApiResult Forbidden() => new(HttpStatusCode.Forbidden);
|
||||||
|
|
||||||
|
public static ApiResult Locked() => new(HttpStatusCode.Locked);
|
||||||
|
|
||||||
|
public static ApiResult TooManyRequests() => new(HttpStatusCode.TooManyRequests);
|
||||||
|
|
||||||
|
public static ApiResult ServiceUnavailable() => new(HttpStatusCode.ServiceUnavailable);
|
||||||
|
|
||||||
|
public static ApiResult UnavailableForLegalReasons() => new(HttpStatusCode.UnavailableForLegalReasons);
|
||||||
}
|
}
|
||||||
@@ -54,7 +54,9 @@ public class ApiServer(string apiUrl) : IApiServer
|
|||||||
// Sanity checks
|
// Sanity checks
|
||||||
if (method.GetCustomAttribute<MessageHandlerAttribute>() is not MessageHandlerAttribute attribute
|
if (method.GetCustomAttribute<MessageHandlerAttribute>() is not MessageHandlerAttribute attribute
|
||||||
|| method.GetParameters().FirstOrDefault() is not ParameterInfo parameter
|
|| method.GetParameters().FirstOrDefault() is not ParameterInfo parameter
|
||||||
|| !parameter.ParameterType.IsAssignableTo(typeof(ApiMessage))
|
|| !parameter.ParameterType.IsGenericType
|
||||||
|
|| parameter.ParameterType.GenericTypeArguments.Length != 1
|
||||||
|
|| !parameter.ParameterType.GenericTypeArguments[0].IsAssignableTo(typeof(ApiMessage))
|
||||||
|| !method.ReturnType.IsAssignableTo(typeof(ApiResult)))
|
|| !method.ReturnType.IsAssignableTo(typeof(ApiResult)))
|
||||||
throw new NotSupportedException("The first parameter needs to be of type ApiMessage and must return an ApiResult object and the method must have the MessageHandlerAttribute.");
|
throw new NotSupportedException("The first parameter needs to be of type ApiMessage and must return an ApiResult object and the method must have the MessageHandlerAttribute.");
|
||||||
|
|
||||||
@@ -146,8 +148,11 @@ public class ApiServer(string apiUrl) : IApiServer
|
|||||||
|
|
||||||
// Check authentication
|
// Check authentication
|
||||||
Log.Debug("Check authentication");
|
Log.Debug("Check authentication");
|
||||||
if (attribute.RequiesAuth && (string.IsNullOrWhiteSpace(authKey) || !CheckAuthentication(authKey)))
|
var isAuthenticated = false;
|
||||||
return null;
|
if (!string.IsNullOrWhiteSpace(authKey) && DecodeAuthKey(authKey) is string authKeyDecoded)
|
||||||
|
isAuthenticated = CheckAuthentication(authKeyDecoded);
|
||||||
|
else
|
||||||
|
authKeyDecoded = null!;
|
||||||
|
|
||||||
// Get required infos
|
// Get required infos
|
||||||
Log.Debug("Find other infos");
|
Log.Debug("Find other infos");
|
||||||
@@ -161,7 +166,8 @@ public class ApiServer(string apiUrl) : IApiServer
|
|||||||
|
|
||||||
// Invoke handler
|
// Invoke handler
|
||||||
Log.Debug("Invoke handler");
|
Log.Debug("Invoke handler");
|
||||||
if (handler.DynamicInvoke(message) is not ApiResult result)
|
var parameters = BuildParameters(handler, () => message, () => new(message, isAuthenticated, authKeyDecoded));
|
||||||
|
if (handler.DynamicInvoke(parameters) is not ApiResult result)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
// Return result without message
|
// Return result without message
|
||||||
@@ -179,6 +185,22 @@ public class ApiServer(string apiUrl) : IApiServer
|
|||||||
return new(result, resultStr);
|
return new(result, resultStr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected virtual object?[]? BuildParameters(Delegate handler, Func<ApiMessage> getMessage, Func<ApiRequestInfo> getRequestInfo)
|
||||||
|
{
|
||||||
|
var infos = handler.Method.GetParameters();
|
||||||
|
var objs = new List<object?>();
|
||||||
|
|
||||||
|
foreach (var info in infos)
|
||||||
|
{
|
||||||
|
if (info.ParameterType.IsAssignableTo(typeof(ApiMessage)))
|
||||||
|
objs.Add(getMessage());
|
||||||
|
else if (info.ParameterType.IsAssignableTo(typeof(ApiRequestInfo)))
|
||||||
|
objs.Add(getRequestInfo());
|
||||||
|
}
|
||||||
|
|
||||||
|
return [.. objs];
|
||||||
|
}
|
||||||
|
|
||||||
protected virtual IMessageSerializer GetSerializer(Type? t)
|
protected virtual IMessageSerializer GetSerializer(Type? t)
|
||||||
{
|
{
|
||||||
if (t is not null)
|
if (t is not null)
|
||||||
@@ -204,4 +226,9 @@ public class ApiServer(string apiUrl) : IApiServer
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected virtual string? DecodeAuthKey(string authKey)
|
||||||
|
{
|
||||||
|
return authKey;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ public interface IApiClient
|
|||||||
|
|
||||||
ILogger Log { get; set; }
|
ILogger Log { get; set; }
|
||||||
|
|
||||||
Task<ApiResponse> SendMessage<TResponse>(string url, ApiMessage message, IMessageSerializer? serializer = null);
|
Task<ApiResponse> SendMessage<TResponse>(string route, ApiMessage message, IMessageSerializer? serializer = null);
|
||||||
|
|
||||||
Task<ApiResponse<TResponse>> SendRequest<TResponse>(string url, ApiMessage message, IMessageSerializer? serializer = null) where TResponse : ApiMessage;
|
Task<ApiResponse<TResponse>> SendRequest<TResponse>(string route, ApiMessage message, IMessageSerializer? serializer = null) where TResponse : ApiMessage;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user