From 519cfa9a4dd03b54d9ebd8e8b0338fb958b8745c Mon Sep 17 00:00:00 2001 From: arenekosreal <17194552+arenekosreal@users.noreply.github.com> Date: Fri, 22 Nov 2024 19:45:40 +0800 Subject: [PATCH] Use source generation on json api serialization/deserialization. --- .../Controllers/JsonAPIV1ControllerTests.cs | 12 ++--- .../SimpleDummyResultGeneratorTests.cs | 4 +- .../Controllers/UnspecifiedControllerTests.cs | 4 +- .../Controllers/IDummyResultGenerator.cs | 4 +- .../Controllers/IQueryCollectionExtends.cs | 31 +++++++++++++ E5Renewer/Controllers/InvokeResult.cs | 28 ------------ E5Renewer/Controllers/JsonAPIV1Controller.cs | 37 ++++++++------- E5Renewer/Controllers/JsonAPIV1Request.cs | 36 +++++++++++++++ E5Renewer/Controllers/JsonAPIV1Response.cs | 45 +++++++++++++++++++ .../Controllers/SimpleDummyResultGenerator.cs | 43 ++++++------------ .../Controllers/UnspecifiedController.cs | 2 +- E5Renewer/IServiceCollectionExtends.cs | 1 + E5Renewer/Program.cs | 7 ++- E5Renewer/StreamExtends.cs | 14 ++++++ E5Renewer/WebApplicationExtends.cs | 44 +++++++++--------- README.md | 4 +- 16 files changed, 198 insertions(+), 118 deletions(-) create mode 100644 E5Renewer/Controllers/IQueryCollectionExtends.cs delete mode 100644 E5Renewer/Controllers/InvokeResult.cs create mode 100644 E5Renewer/Controllers/JsonAPIV1Request.cs create mode 100644 E5Renewer/Controllers/JsonAPIV1Response.cs create mode 100644 E5Renewer/StreamExtends.cs diff --git a/E5Renewer.Tests/Controllers/JsonAPIV1ControllerTests.cs b/E5Renewer.Tests/Controllers/JsonAPIV1ControllerTests.cs index d5a131f..e50e67b 100644 --- a/E5Renewer.Tests/Controllers/JsonAPIV1ControllerTests.cs +++ b/E5Renewer.Tests/Controllers/JsonAPIV1ControllerTests.cs @@ -37,7 +37,7 @@ public JsonAPIV1ControllerTests() generator.GetUnixTimestamp().Returns((long)42); IDummyResultGenerator dummyResultGenerator = Substitute.For(); - InvokeResult dummyResult = new(); + JsonAPIV1Response dummyResult = new(); HttpContext context = new DefaultHttpContext(); dummyResultGenerator.GenerateDummyResultAsync(context).ReturnsForAnyArgs(dummyResult); dummyResultGenerator.GenerateDummyResult(context).ReturnsForAnyArgs(dummyResult); @@ -50,7 +50,7 @@ public JsonAPIV1ControllerTests() [TestMethod] public async Task TestGetListApis() { - InvokeResult result = await this.controller.GetListApis(); + JsonAPIV1Response result = await this.controller.GetListApis(); Assert.AreEqual(0, result.args.Count); Assert.AreEqual("list_apis", result.method); string? id = ((IEnumerable?)result.result)?.First(); @@ -62,7 +62,7 @@ public async Task TestGetListApis() [TestMethod] public async Task TestGetRunningUsers() { - InvokeResult result = await this.controller.GetRunningUsers(); + JsonAPIV1Response result = await this.controller.GetRunningUsers(); Assert.AreEqual(0, result.args.Count); Assert.AreEqual("running_users", result.method); string? user = ((IEnumerable?)result.result)?.First(); @@ -74,7 +74,7 @@ public async Task TestGetRunningUsers() [TestMethod] public async Task TestGetWaitingUsers() { - InvokeResult result = await this.controller.GetWaitingUsers(); + JsonAPIV1Response result = await this.controller.GetWaitingUsers(); Assert.AreEqual(0, result.args.Count); Assert.AreEqual("waiting_users", result.method); string? user = ((IEnumerable?)result.result)?.First(); @@ -86,7 +86,7 @@ public async Task TestGetWaitingUsers() [TestMethod] public async Task TestGetUserResults() { - InvokeResult result = await this.controller.GetUserResults("testName", "testId"); + JsonAPIV1Response result = await this.controller.GetUserResults("testName", "testId"); Assert.AreEqual(2, result.args.Count); Assert.AreEqual("testName", result.args["user"]); Assert.AreEqual("testId", result.args["api_name"]); @@ -99,7 +99,7 @@ public async Task TestGetUserResults() [TestMethod] public async Task TestHandle() { - InvokeResult result = await this.controller.Handle(); + JsonAPIV1Response result = await this.controller.Handle(); Assert.AreEqual(new(), result); } } diff --git a/E5Renewer.Tests/Controllers/SimpleDummyResultGeneratorTests.cs b/E5Renewer.Tests/Controllers/SimpleDummyResultGeneratorTests.cs index 8fd56a6..c67c0f2 100644 --- a/E5Renewer.Tests/Controllers/SimpleDummyResultGeneratorTests.cs +++ b/E5Renewer.Tests/Controllers/SimpleDummyResultGeneratorTests.cs @@ -32,7 +32,7 @@ public SimpleDummyResultGeneratorTests() public async Task TestGenerateDummyResultAsync() { HttpContext context = new DefaultHttpContext(); - InvokeResult result = await this.dummyResultGenerator.GenerateDummyResultAsync(context); + JsonAPIV1Response result = await this.dummyResultGenerator.GenerateDummyResultAsync(context); Assert.AreEqual((long)42, result.timestamp); } /// Test @@ -42,7 +42,7 @@ public async Task TestGenerateDummyResultAsync() public void TestGenerateDummyResult() { HttpContext context = new DefaultHttpContext(); - InvokeResult result = this.dummyResultGenerator.GenerateDummyResult(context); + JsonAPIV1Response result = this.dummyResultGenerator.GenerateDummyResult(context); Assert.AreEqual((long)42, result.timestamp); } } diff --git a/E5Renewer.Tests/Controllers/UnspecifiedControllerTests.cs b/E5Renewer.Tests/Controllers/UnspecifiedControllerTests.cs index b5eadc2..869e151 100644 --- a/E5Renewer.Tests/Controllers/UnspecifiedControllerTests.cs +++ b/E5Renewer.Tests/Controllers/UnspecifiedControllerTests.cs @@ -20,7 +20,7 @@ public UnspecifiedControllerTests() { ILogger logger = Substitute.For>(); IDummyResultGenerator dummyResultGenerator = Substitute.For(); - InvokeResult result = new(); + JsonAPIV1Response result = new(); HttpContext context = new DefaultHttpContext(); dummyResultGenerator.GenerateDummyResultAsync(context).Returns(Task.FromResult(result)); dummyResultGenerator.GenerateDummyResult(context).Returns(result); @@ -34,7 +34,7 @@ public UnspecifiedControllerTests() [TestMethod] public async Task TestHandle() { - InvokeResult result = await this.controller.Handle(); + JsonAPIV1Response result = await this.controller.Handle(); Assert.AreEqual(new(), result); } } diff --git a/E5Renewer/Controllers/IDummyResultGenerator.cs b/E5Renewer/Controllers/IDummyResultGenerator.cs index f800e05..6a52767 100644 --- a/E5Renewer/Controllers/IDummyResultGenerator.cs +++ b/E5Renewer/Controllers/IDummyResultGenerator.cs @@ -4,9 +4,9 @@ namespace E5Renewer.Controllers public interface IDummyResultGenerator { /// Generate a dummy result when something not right. - public Task GenerateDummyResultAsync(HttpContext httpContext); + public Task GenerateDummyResultAsync(HttpContext httpContext); /// Generate a dummy result when something not right. - public InvokeResult GenerateDummyResult(HttpContext httpContext); + public JsonAPIV1Response GenerateDummyResult(HttpContext httpContext); } } diff --git a/E5Renewer/Controllers/IQueryCollectionExtends.cs b/E5Renewer/Controllers/IQueryCollectionExtends.cs new file mode 100644 index 0000000..7a69401 --- /dev/null +++ b/E5Renewer/Controllers/IQueryCollectionExtends.cs @@ -0,0 +1,31 @@ +using Microsoft.Extensions.Primitives; + +namespace E5Renewer.Controllers +{ + internal static class IQueryCollectionExtends + { + public static JsonAPIV1Request ToJsonAPIV1Request(this IQueryCollection queryCollection) + { + StringValues value; + long timestamp = queryCollection.TryGetValue(nameof(timestamp), out value) && + long.TryParse(value, out long parsedValue) + ? parsedValue : default; + string method; + if (queryCollection.TryGetValue(nameof(method), out value)) + { + method = value.FirstOrDefault() ?? string.Empty; + } + else + { + method = string.Empty; + } + + Dictionary args = + queryCollection.TakeWhile((kv) => kv.Key != nameof(timestamp) && kv.Key != nameof(method)) + .Select((kv) => new KeyValuePair(kv.Key, kv.Value.FirstOrDefault())) + .ToDictionary(); + + return new(timestamp, method, new(args)); + } + } +} diff --git a/E5Renewer/Controllers/InvokeResult.cs b/E5Renewer/Controllers/InvokeResult.cs deleted file mode 100644 index 0c77982..0000000 --- a/E5Renewer/Controllers/InvokeResult.cs +++ /dev/null @@ -1,28 +0,0 @@ -namespace E5Renewer.Controllers -{ - /// The struct which stores query result and as a response. - public readonly struct InvokeResult - { - /// Request method. - /// Is **NOT** HttpContext.Request.Method - public readonly string method { get; } - /// The params to be passed to request method. - public readonly Dictionary args { get; } - /// The request result. - public readonly object? result { get; } - /// The timestamp. - public readonly long timestamp { get; } - /// Initialize an InvokeResult from method, params, result and timestamp. - /// The request method. - /// The request params. - /// The request result. - /// The request timestamp. - public InvokeResult(string method, Dictionary args, object? result, long timestamp) - { - this.method = method; - this.args = args; - this.result = result; - this.timestamp = timestamp; - } - } -} diff --git a/E5Renewer/Controllers/JsonAPIV1Controller.cs b/E5Renewer/Controllers/JsonAPIV1Controller.cs index 872492b..a8cdc23 100644 --- a/E5Renewer/Controllers/JsonAPIV1Controller.cs +++ b/E5Renewer/Controllers/JsonAPIV1Controller.cs @@ -1,3 +1,5 @@ +using System.Collections.ObjectModel; + using E5Renewer.Models.GraphAPIs; using E5Renewer.Models.Statistics; @@ -35,50 +37,50 @@ IDummyResultGenerator dummyResponseGenerator /// Handler for /v1/list_apis. [HttpGet("list_apis")] - public ValueTask GetListApis() + public ValueTask GetListApis() { IEnumerable result = this.apiFunctions. Select((c) => c.id); logger.LogDebug("Getting result [{0}]", string.Join(", ", result)); - return ValueTask.FromResult(new InvokeResult( + return ValueTask.FromResult(new JsonAPIV1Response( + this.unixTimestampGenerator.GetUnixTimestamp(), "list_apis", - new(), result, - this.unixTimestampGenerator.GetUnixTimestamp() + ReadOnlyDictionary.Empty )); } /// Handler for /v1/running_users. [HttpGet("running_users")] - public async ValueTask GetRunningUsers() + public async ValueTask GetRunningUsers() { IEnumerable result = await this.statusManager.GetRunningUsersAsync(); logger.LogDebug("Getting result [{0}]", string.Join(", ", result)); return new( + this.unixTimestampGenerator.GetUnixTimestamp(), "running_users", - new(), result, - this.unixTimestampGenerator.GetUnixTimestamp() + ReadOnlyDictionary.Empty ); } /// Handler for /v1/waiting_users. [HttpGet("waiting_users")] - public async ValueTask GetWaitingUsers() + public async ValueTask GetWaitingUsers() { IEnumerable result = await this.statusManager.GetWaitingUsersAsync(); logger.LogDebug("Getting result [{0}]", string.Join(", ", result)); return new( + this.unixTimestampGenerator.GetUnixTimestamp(), "waiting_users", - new(), result, - this.unixTimestampGenerator.GetUnixTimestamp() + ReadOnlyDictionary.Empty ); } /// Handler for /v1/user_results. [HttpGet("user_results")] - public async ValueTask GetUserResults( + public async ValueTask GetUserResults( [FromQuery(Name = "user")] string userName, @@ -88,20 +90,21 @@ string apiName { IEnumerable result = await this.statusManager.GetResultsAsync(userName, apiName); logger.LogDebug("Getting result [{0}]", string.Join(", ", result)); + Dictionary queries = new() + { + {"user", userName},{"api_name", apiName} + }; return new( + this.unixTimestampGenerator.GetUnixTimestamp(), "user_results", - new Dictionary() - { - { "user", userName }, { "api_name", apiName } - }, result, - this.unixTimestampGenerator.GetUnixTimestamp() + new(queries) ); } /// Handler for /v1/*. [Route("{*method}")] - public async ValueTask Handle() => + public async ValueTask Handle() => await this.dummyResponseGenerator.GenerateDummyResultAsync(this.HttpContext); } } diff --git a/E5Renewer/Controllers/JsonAPIV1Request.cs b/E5Renewer/Controllers/JsonAPIV1Request.cs new file mode 100644 index 0000000..4c9f668 --- /dev/null +++ b/E5Renewer/Controllers/JsonAPIV1Request.cs @@ -0,0 +1,36 @@ +using System.Collections.ObjectModel; +using System.Text.Json.Serialization; + +namespace E5Renewer.Controllers +{ + /// Readonly struct to store json api request. + public readonly struct JsonAPIV1Request + { + /// If this protocol is valid. + [JsonIgnore] + public bool valid { get => timestamp > 0 && !string.IsNullOrEmpty(method) && string.IsNullOrWhiteSpace(method); } + + /// The timestamp. + public long timestamp { get; } + + /// Request method. + /// Is **NOT** HttpContext.Request.Method + public string? method { get; } + + /// The params to be passed to request method. + public ReadOnlyDictionary args { get; } + + /// Initialize with arguments given. + [JsonConstructor] + public JsonAPIV1Request(long timestamp, string? method, ReadOnlyDictionary args) => + (this.timestamp, this.method, this.args) = (timestamp, method, args); + } + + [JsonSourceGenerationOptions( + WriteIndented = true, + PropertyNamingPolicy = JsonKnownNamingPolicy.SnakeCaseLower, + IncludeFields = true + )] + [JsonSerializable(typeof(JsonAPIV1Request))] + internal partial class JsonAPIV1RequestJsonSerializerContext : JsonSerializerContext { } +} diff --git a/E5Renewer/Controllers/JsonAPIV1Response.cs b/E5Renewer/Controllers/JsonAPIV1Response.cs new file mode 100644 index 0000000..e6a3db8 --- /dev/null +++ b/E5Renewer/Controllers/JsonAPIV1Response.cs @@ -0,0 +1,45 @@ +using System.Collections.ObjectModel; +using System.Text.Json.Serialization; + +namespace E5Renewer.Controllers +{ + /// Readonly struct to store json api response. + public readonly struct JsonAPIV1Response + { + /// If this protocol is valid. + [JsonIgnore] + public bool valid { get => timestamp > 0 && !string.IsNullOrEmpty(method) && string.IsNullOrWhiteSpace(method); } + + /// The timestamp. + public long timestamp { get; } + + /// Request method. + /// Is **NOT** HttpContext.Request.Method + public string method { get; } + + /// The request result. + public object? result { get; } + + /// The params to be passed to request method. + public ReadOnlyDictionary args { get; } + + /// Initialize with arguments given. + [JsonConstructor] + public JsonAPIV1Response(long timestamp, string method, object? result, ReadOnlyDictionary args) => + (this.timestamp, this.method, this.result, this.args) = (timestamp, method, result, args); + } + + [JsonSourceGenerationOptions( + WriteIndented = true, + PropertyNamingPolicy = JsonKnownNamingPolicy.SnakeCaseLower, + IncludeFields = true + )] + [JsonSerializable(typeof(JsonAPIV1Response))] + [JsonSerializable(typeof(IEnumerable))] + [JsonSerializable(typeof(IEnumerable))] + [JsonSerializable(typeof(bool))] + [JsonSerializable(typeof(int))] + [JsonSerializable(typeof(string))] + [JsonSerializable(typeof(object))] + internal partial class JsonAPIV1ResponseJsonSerializerContext : JsonSerializerContext { } +} diff --git a/E5Renewer/Controllers/SimpleDummyResultGenerator.cs b/E5Renewer/Controllers/SimpleDummyResultGenerator.cs index 8c1a47f..ad58936 100644 --- a/E5Renewer/Controllers/SimpleDummyResultGenerator.cs +++ b/E5Renewer/Controllers/SimpleDummyResultGenerator.cs @@ -1,5 +1,4 @@ -using System.Diagnostics.CodeAnalysis; -using System.Text.Json; +using System.Collections.ObjectModel; using E5Renewer.Models.Statistics; @@ -19,31 +18,14 @@ public SimpleDummyResultGenerator(ILogger logger, IU (this.logger, this.unixTimestampGenerator) = (logger, unixTimestampGenerator); /// - [RequiresUnreferencedCode("Calls JsonSerializer.Deserialize(ReadOnlySpan)")] - public async Task GenerateDummyResultAsync(HttpContext httpContext) + public async Task GenerateDummyResultAsync(HttpContext httpContext) { - Dictionary queries; - switch (httpContext.Request.Method) + JsonAPIV1Request input = httpContext.Request.Method switch { - case "GET": - queries = httpContext.Request.Query.Select( - (kv) => new KeyValuePair(kv.Key, kv.Value.FirstOrDefault() as object) - ).ToDictionary(); - break; - case "POST": - byte[] buffer = new byte[httpContext.Request.ContentLength ?? httpContext.Request.Body.Length]; - int length = await httpContext.Request.Body.ReadAsync(buffer); - byte[] contents = buffer.Take(length).ToArray(); - queries = JsonSerializer.Deserialize>(contents) ?? new(); - break; - default: - queries = new(); - break; - } - if (queries.ContainsKey("timestamp")) - { - queries.Remove("timestamp"); - } + "GET" => httpContext.Request.Query.ToJsonAPIV1Request(), + "POST" => await httpContext.Request.Body.ToJsonAPIV1RequestAsync(), + _ => new() + }; string fullPath = httpContext.Request.PathBase + httpContext.Request.Path; int lastOfSlash = fullPath.LastIndexOf("/"); int firstOfQuote = fullPath.IndexOf("?"); @@ -51,14 +33,15 @@ public async Task GenerateDummyResultAsync(HttpContext httpContext firstOfQuote > lastOfSlash ? fullPath.Substring(lastOfSlash + 1, firstOfQuote - lastOfSlash) : fullPath.Substring(lastOfSlash + 1); - return new(methodName, - queries, - null, - this.unixTimestampGenerator.GetUnixTimestamp() + return new( + this.unixTimestampGenerator.GetUnixTimestamp(), + methodName, + null, + input.args ?? ReadOnlyDictionary.Empty ); } /// - public InvokeResult GenerateDummyResult(HttpContext httpContext) => this.GenerateDummyResultAsync(httpContext).Result; + public JsonAPIV1Response GenerateDummyResult(HttpContext httpContext) => this.GenerateDummyResultAsync(httpContext).Result; } } diff --git a/E5Renewer/Controllers/UnspecifiedController.cs b/E5Renewer/Controllers/UnspecifiedController.cs index 12777db..fa61471 100644 --- a/E5Renewer/Controllers/UnspecifiedController.cs +++ b/E5Renewer/Controllers/UnspecifiedController.cs @@ -20,7 +20,7 @@ public UnspecifiedController(ILogger logger, IDummyResult /// Handle all unspecified requests. [Route("{*method}")] - public async ValueTask Handle() + public async ValueTask Handle() { this.logger.LogWarning("Someone called a unspecified path {0}.", this.HttpContext.Request.Path); this.HttpContext.Response.StatusCode = (int)HttpStatusCode.BadRequest; diff --git a/E5Renewer/IServiceCollectionExtends.cs b/E5Renewer/IServiceCollectionExtends.cs index 6dd6739..370bbf9 100644 --- a/E5Renewer/IServiceCollectionExtends.cs +++ b/E5Renewer/IServiceCollectionExtends.cs @@ -48,6 +48,7 @@ public static IServiceCollection AddTokenOverride(this IServiceCollection servic public static IServiceCollection AddUserSecretFile(this IServiceCollection services, FileInfo userSecret) => services.AddKeyedSingleton(nameof(userSecret), userSecret); + [RequiresUnreferencedCode("Calls E5Renewer.AssemblyExtends.IterE5RenewerModules()")] public static IServiceCollection AddModules(this IServiceCollection services, params Assembly[] assemblies) { foreach (Assembly assembly in assemblies) diff --git a/E5Renewer/Program.cs b/E5Renewer/Program.cs index 063ce85..1da2c51 100644 --- a/E5Renewer/Program.cs +++ b/E5Renewer/Program.cs @@ -1,7 +1,6 @@ using System.Net; using System.Net.Sockets; using System.Reflection; -using System.Text.Json; using E5Renewer; using E5Renewer.Controllers; @@ -108,14 +107,14 @@ .AddControllers() .AddJsonOptions( (options) => - options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower + options.JsonSerializerOptions.TypeInfoResolverChain.Add(JsonAPIV1ResponseJsonSerializerContext.Default) ) .ConfigureApiBehaviorOptions( (options) => options.InvalidModelStateResponseFactory = (actionContext) => { - InvokeResult result = actionContext.HttpContext.RequestServices.GetRequiredService() + JsonAPIV1Response result = actionContext.HttpContext.RequestServices.GetRequiredService() .GenerateDummyResult(actionContext.HttpContext); return new JsonResult(result); } @@ -131,7 +130,7 @@ exceptionHandlerApp.Run( async (context) => { - InvokeResult result = await context.RequestServices.GetRequiredService() + JsonAPIV1Response result = await context.RequestServices.GetRequiredService() .GenerateDummyResultAsync(context); await context.Response.WriteAsJsonAsync(result); } diff --git a/E5Renewer/StreamExtends.cs b/E5Renewer/StreamExtends.cs new file mode 100644 index 0000000..acded96 --- /dev/null +++ b/E5Renewer/StreamExtends.cs @@ -0,0 +1,14 @@ +using System.Text.Json; + +using E5Renewer.Controllers; + +namespace E5Renewer +{ + internal static class StreamExtends + { + public static async Task ToJsonAPIV1RequestAsync(this Stream stream) => + await JsonSerializer.DeserializeAsync( + stream, JsonAPIV1RequestJsonSerializerContext.Default.JsonAPIV1Request + ); + } +} diff --git a/E5Renewer/WebApplicationExtends.cs b/E5Renewer/WebApplicationExtends.cs index 22dc6f7..af3c295 100644 --- a/E5Renewer/WebApplicationExtends.cs +++ b/E5Renewer/WebApplicationExtends.cs @@ -1,5 +1,6 @@ -using System.Diagnostics.CodeAnalysis; -using System.Text.Json; +using System.Text.Json.Serialization.Metadata; + +using CaseConverter; using E5Renewer.Controllers; using E5Renewer.Models.GraphAPIs; @@ -13,6 +14,9 @@ namespace E5Renewer { internal static class WebApplicationExtends { + private static readonly JsonTypeInfo responseJsonTypeInfo = + JsonAPIV1ResponseJsonSerializerContext.Default.JsonAPIV1Response; + public static IApplicationBuilder UseModulesCheckers(this WebApplication app) { IEnumerable modulesCheckers = app.Services.GetServices(); @@ -51,16 +55,16 @@ public static IApplicationBuilder UseAuthTokenAuthentication(this WebApplication { ISecretProvider secretProvider = app.Services.GetRequiredService(); IDummyResultGenerator dummyResultGenerator = app.Services.GetRequiredService(); - const string authenticationHeaderName = "Authentication"; - string? authentication = context.Request.Headers[authenticationHeaderName].FirstOrDefault(); - if (authentication == await secretProvider.GetRuntimeTokenAsync()) + const string authorizationScheme = "Bearer"; + string authorization = context.Request.Headers[nameof(authorization).ToTitleCase()].FirstOrDefault() ?? string.Empty; + if (authorization == $"{authorizationScheme.ToTitleCase()} {await secretProvider.GetRuntimeTokenAsync()}") { await next(); return; } context.Response.StatusCode = StatusCodes.Status403Forbidden; - InvokeResult result = await dummyResultGenerator.GenerateDummyResultAsync(context); - await context.Response.WriteAsJsonAsync(result); + JsonAPIV1Response result = await dummyResultGenerator.GenerateDummyResultAsync(context); + await context.Response.WriteAsJsonAsync(result, WebApplicationExtends.responseJsonTypeInfo); } ); } @@ -86,17 +90,15 @@ public static IApplicationBuilder UseUnixTimestampChecker(this WebApplication ap return app.Use( async (context, next) => { - const string timestampKey = "timestamp"; - string timestampValue = context.Request.Method switch + long timestamp = context.Request.Method switch { - "GET" => context.Request.Query.TryGetValue(timestampKey, out StringValues value) ? value.FirstOrDefault() ?? "" : "", - "POST" => (await context.Request.Body.ToDictionary()).TryGetValue(timestampKey, out string? value) ? value ?? "" : "", - _ => "" + "GET" => context.Request.Query.TryGetValue(nameof(timestamp), out StringValues value) && + long.TryParse(value, out long parsedValue) + ? parsedValue : -1, + "POST" => (await context.Request.Body.ToJsonAPIV1RequestAsync()).timestamp, + _ => -1 }; - if (!long.TryParse(timestampValue, out long timestamp)) - { - timestamp = -1; - } + long timestampNow = app.Services.GetRequiredService().GetUnixTimestamp(); if ((timestamp > 0) && (timestampNow > timestamp) && (timestampNow - timestamp <= allowedMaxSeconds * 1000)) { @@ -104,17 +106,11 @@ public static IApplicationBuilder UseUnixTimestampChecker(this WebApplication ap return; } context.Response.StatusCode = StatusCodes.Status403Forbidden; - InvokeResult result = await app.Services.GetRequiredService().GenerateDummyResultAsync(context); - await context.Response.WriteAsJsonAsync(result); + JsonAPIV1Response result = await app.Services.GetRequiredService().GenerateDummyResultAsync(context); + await context.Response.WriteAsJsonAsync(result, WebApplicationExtends.responseJsonTypeInfo); } ); } - [RequiresUnreferencedCode("Calls System.Text.Json.JsonSerializer.DeserializeAsync(Stream, JsonSerializerOptions, CancellationToken)")] - private static async Task> ToDictionary(this Stream stream) - where K : notnull - { - return await JsonSerializer.DeserializeAsync>(stream) ?? new(); - } } } diff --git a/README.md b/README.md index 807cc03..734ee30 100644 --- a/README.md +++ b/README.md @@ -79,7 +79,7 @@ A tool to renew e5 subscription by calling msgraph APIs ## Get running statistics Using `curl` or any tool which can send http request, send request to `http://:` or unix socket ``, -each request should be sent with header `Authentication: `. +each request should be sent with header `Authorization: Bearer `. You will get json response if everything is fine. If it is a GET request, send milisecond timestamp in query param `timestamp`, If it is a POST request, send milisecond timestamp in post json with key `timestamp` and convert it to string. Most of the time, we will return json instead plain text, but you need to check response code to see if request is success. @@ -91,7 +91,7 @@ For example: HTTP API for /v1/list_apis ``` -curl -H 'Authentication: ' -H 'Accept: application/json' \ +curl -H 'Authorization: Bearer ' -H 'Accept: application/json' \ 'http://:/v1/list_apis?timestamp=' | jq '.' { "method": "list_apis",