From ec482b9233a39e53cb19f6af4c4ef9506cfc3047 Mon Sep 17 00:00:00 2001 From: Laci0503 Date: Mon, 22 Jul 2024 21:44:25 +0200 Subject: [PATCH] RestClient tests with mocking --- Discord.API.Tests/Discord.API.Tests.csproj | 12 +- Discord.API.Tests/Rest/RestClientTests.cs | 51 + Discord.API/DiscordClient.cs | 5 +- .../HttpClientWrapper/HttpClientWrapper.cs | 45 - .../HttpClientWrapper/IHttpClientWrapper.cs | 1327 ----------------- Discord.API/Rest/RestClient.cs | 21 +- Discord.API/Rest/RestError.cs | 2 +- 7 files changed, 75 insertions(+), 1388 deletions(-) create mode 100644 Discord.API.Tests/Rest/RestClientTests.cs delete mode 100644 Discord.API/Rest/HttpClientWrapper/HttpClientWrapper.cs delete mode 100644 Discord.API/Rest/HttpClientWrapper/IHttpClientWrapper.cs diff --git a/Discord.API.Tests/Discord.API.Tests.csproj b/Discord.API.Tests/Discord.API.Tests.csproj index 8ccb65d..34131f0 100644 --- a/Discord.API.Tests/Discord.API.Tests.csproj +++ b/Discord.API.Tests/Discord.API.Tests.csproj @@ -14,14 +14,16 @@ - - - - + + + + + + - + diff --git a/Discord.API.Tests/Rest/RestClientTests.cs b/Discord.API.Tests/Rest/RestClientTests.cs new file mode 100644 index 0000000..022480b --- /dev/null +++ b/Discord.API.Tests/Rest/RestClientTests.cs @@ -0,0 +1,51 @@ +using System.Net; +using Discord.API.Rest; +using RichardSzalay.MockHttp; + +namespace Discord.API.Tests; + +public class RestClientTests{ + + [Fact] + public async void GetGatewayTest(){ + // Arrange + var mock = new MockHttpMessageHandler(); + mock.Expect(HttpMethod.Get, "https://localhost/gateway").Respond(HttpStatusCode.OK,"application/json", """ + {"url":"wss://test.gateway.api/"} + """); + + // Act + var rest_client = new RestClient("https://localhost/", "api_key", mock); + var res = await rest_client.GetGateway(); + + // Assert + Assert.IsType>(res); + Assert.True(res.Success); + Assert.Equal("wss://test.gateway.api/", res.Value.Url); + mock.VerifyNoOutstandingExpectation(); + } + + [Fact] + public async void GetGatewayErrorTest(){ + // Arrange + var mock = new MockHttpMessageHandler(); + mock.Expect(HttpMethod.Get, "https://localhost/gateway").Respond(HttpStatusCode.TooManyRequests,"application/json", """ + { + "message": "You are being rate limited.", + "retry_after": 64.57, + "global": false + } + """); + + // Act + var rest_client = new RestClient("https://localhost/", "api_key", mock); + var res = await rest_client.GetGateway(); + + // Assert + Assert.IsType>(res); + Assert.False(res.Success); + Assert.Equal("You are being rate limited.", res.Error.Message); + mock.VerifyNoOutstandingExpectation(); + } + +} \ No newline at end of file diff --git a/Discord.API/DiscordClient.cs b/Discord.API/DiscordClient.cs index bcf618a..6370038 100644 --- a/Discord.API/DiscordClient.cs +++ b/Discord.API/DiscordClient.cs @@ -1,5 +1,4 @@ using Discord.API.Rest; -using Discord.API.Rest.HttpClientWrapper; using Serilog; namespace Discord.API; @@ -20,10 +19,10 @@ public class DiscordClient Task.Run(ConnectGateway); } - internal DiscordClient(string api_key, TimeProvider time_provider, IHttpClientWrapper http_client){ + internal DiscordClient(string api_key, TimeProvider time_provider, HttpMessageHandler msg_handler){ this.TimeProvider=time_provider; this.ApiKey=api_key; - Rest = new RestClient(ApiKey, http_client); + Rest = new RestClient(ApiBaseUrl, ApiKey, msg_handler); Task.Run(ConnectGateway); } diff --git a/Discord.API/Rest/HttpClientWrapper/HttpClientWrapper.cs b/Discord.API/Rest/HttpClientWrapper/HttpClientWrapper.cs deleted file mode 100644 index 4ffd038..0000000 --- a/Discord.API/Rest/HttpClientWrapper/HttpClientWrapper.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using System.Net.Http.Headers; -using System.Runtime.Versioning; - -namespace Discord.API.Rest.HttpClientWrapper; - -public class HttpClientWrapper : HttpClient, IHttpClientWrapper{ - // - // Summary: - // Initializes a new instance of the System.Net.Http.HttpClient class using a System.Net.Http.HttpClientHandler - // that is disposed when this instance is disposed. - public HttpClientWrapper() : base() { } - // - // Summary: - // Initializes a new instance of the System.Net.Http.HttpClient class with the specified - // handler. The handler is disposed when this instance is disposed. - // - // Parameters: - // handler: - // The HTTP handler stack to use for sending requests. - // - // Exceptions: - // T:System.ArgumentNullException: - // The handler is null. - public HttpClientWrapper(HttpMessageHandler handler) : base(handler) { } - // - // Summary: - // Initializes a new instance of the System.Net.Http.HttpClient class with the provided - // handler, and specifies whether that handler should be disposed when this instance - // is disposed. - // - // Parameters: - // handler: - // The System.Net.Http.HttpMessageHandler responsible for processing the HTTP response - // messages. - // - // disposeHandler: - // true if the inner handler should be disposed of by HttpClient.Dispose; false - // if you intend to reuse the inner handler. - // - // Exceptions: - // T:System.ArgumentNullException: - // The handler is null. - public HttpClientWrapper(HttpMessageHandler handler, bool disposeHandler) : base(handler, disposeHandler) { } -} \ No newline at end of file diff --git a/Discord.API/Rest/HttpClientWrapper/IHttpClientWrapper.cs b/Discord.API/Rest/HttpClientWrapper/IHttpClientWrapper.cs deleted file mode 100644 index ecbd228..0000000 --- a/Discord.API/Rest/HttpClientWrapper/IHttpClientWrapper.cs +++ /dev/null @@ -1,1327 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using System.Net.Http.Headers; -using System.Runtime.Versioning; - -namespace Discord.API.Rest.HttpClientWrapper; - -/// -/// Interface wrapper around the HttpClient to use for tests -/// -/// remarks Copied over from decompiled HttpClient code. -public interface IHttpClientWrapper{ - public HttpVersionPolicy DefaultVersionPolicy { get; set; } - // - // Summary: - // Gets or sets the default HTTP version used on subsequent requests made by this - // System.Net.Http.HttpClient instance. - // - // Returns: - // The default version to use for any requests made with this System.Net.Http.HttpClient - // instance. - // - // Exceptions: - // T:System.ArgumentNullException: - // In a set operation, DefaultRequestVersion is null. - // - // T:System.InvalidOperationException: - // The System.Net.Http.HttpClient instance has already started one or more requests. - // - // - // T:System.ObjectDisposedException: - // The System.Net.Http.HttpClient instance has already been disposed. - public Version DefaultRequestVersion { get; set; } - // - // Summary: - // Gets the headers which should be sent with each request. - // - // Returns: - // The headers which should be sent with each request. - public HttpRequestHeaders DefaultRequestHeaders { get; } - // - // Summary: - // Gets or sets the base address of Uniform Resource Identifier (URI) of the Internet - // resource used when sending requests. - // - // Returns: - // The base address of Uniform Resource Identifier (URI) of the Internet resource - // used when sending requests. - public Uri? BaseAddress { get; set; } - // - // Summary: - // Gets or sets the maximum number of bytes to buffer when reading the response - // content. - // - // Returns: - // The maximum number of bytes to buffer when reading the response content. The - // default value for this property is 2 gigabytes. - // - // Exceptions: - // T:System.ArgumentOutOfRangeException: - // The size specified is less than or equal to zero. - // - // T:System.InvalidOperationException: - // An operation has already been started on the current instance. - // - // T:System.ObjectDisposedException: - // The current instance has been disposed. - public long MaxResponseContentBufferSize { get; set; } - // - // Summary: - // Gets or sets the timespan to wait before the request times out. - // - // Returns: - // The timespan to wait before the request times out. - // - // Exceptions: - // T:System.ArgumentOutOfRangeException: - // The timeout specified is less than or equal to zero and is not System.Threading.Timeout.InfiniteTimeSpan. - // - // - // T:System.InvalidOperationException: - // An operation has already been started on the current instance. - // - // T:System.ObjectDisposedException: - // The current instance has been disposed. - public TimeSpan Timeout { get; set; } - - // - // Summary: - // Cancel all pending requests on this instance. - public void CancelPendingRequests(); - // - // Summary: - // Send a DELETE request to the specified Uri as an asynchronous operation. - // - // Parameters: - // requestUri: - // The Uri the request is sent to. - // - // Returns: - // The task object representing the asynchronous operation. - // - // Exceptions: - // T:System.InvalidOperationException: - // The request message was already sent by the System.Net.Http.HttpClient instance. - // -or- The requestUri is not an absolute URI. -or- System.Net.Http.HttpClient.BaseAddress - // is not set. - // - // T:System.Net.Http.HttpRequestException: - // The request failed due to an underlying issue such as network connectivity, DNS - // failure, server certificate validation or timeout. - // - // T:System.Threading.Tasks.TaskCanceledException: - // .NET Core and .NET 5 and later only: The request failed due to timeout. - public Task DeleteAsync(Uri? requestUri); - // - // Summary: - // Send a DELETE request to the specified Uri with a cancellation token as an asynchronous - // operation. - // - // Parameters: - // requestUri: - // The Uri the request is sent to. - // - // cancellationToken: - // A cancellation token that can be used by other objects or threads to receive - // notice of cancellation. - // - // Returns: - // The task object representing the asynchronous operation. - // - // Exceptions: - // T:System.InvalidOperationException: - // The request message was already sent by the System.Net.Http.HttpClient instance. - // -or- The requestUri is not an absolute URI. -or- System.Net.Http.HttpClient.BaseAddress - // is not set. - // - // T:System.Net.Http.HttpRequestException: - // The request failed due to an underlying issue such as network connectivity, DNS - // failure, server certificate validation or timeout. - // - // T:System.Threading.Tasks.TaskCanceledException: - // .NET Core and .NET 5 and later only: The request failed due to timeout. - public Task DeleteAsync([StringSyntax("Uri")] string? requestUri, CancellationToken cancellationToken); - // - // Summary: - // Send a DELETE request to the specified Uri as an asynchronous operation. - // - // Parameters: - // requestUri: - // The Uri the request is sent to. - // - // Returns: - // The task object representing the asynchronous operation. - // - // Exceptions: - // T:System.InvalidOperationException: - // The request message was already sent by the System.Net.Http.HttpClient instance. - // -or- The requestUri is not an absolute URI. -or- System.Net.Http.HttpClient.BaseAddress - // is not set. - // - // T:System.Net.Http.HttpRequestException: - // The request failed due to an underlying issue such as network connectivity, DNS - // failure, server certificate validation or timeout. - // - // T:System.Threading.Tasks.TaskCanceledException: - // .NET Core and .NET 5 and later only: The request failed due to timeout. - public Task DeleteAsync([StringSyntax("Uri")] string? requestUri); - // - // Summary: - // Send a DELETE request to the specified Uri with a cancellation token as an asynchronous - // operation. - // - // Parameters: - // requestUri: - // The Uri the request is sent to. - // - // cancellationToken: - // A cancellation token that can be used by other objects or threads to receive - // notice of cancellation. - // - // Returns: - // The task object representing the asynchronous operation. - // - // Exceptions: - // T:System.InvalidOperationException: - // The request message was already sent by the System.Net.Http.HttpClient instance. - // -or- The requestUri is not an absolute URI. -or- System.Net.Http.HttpClient.BaseAddress - // is not set. - // - // T:System.Net.Http.HttpRequestException: - // The request failed due to an underlying issue such as network connectivity, DNS - // failure, server certificate validation or timeout. - // - // T:System.Threading.Tasks.TaskCanceledException: - // .NET Core and .NET 5 and later only: The request failed due to timeout. - public Task DeleteAsync(Uri? requestUri, CancellationToken cancellationToken); - // - // Summary: - // Send a GET request to the specified Uri with an HTTP completion option as an - // asynchronous operation. - // - // Parameters: - // requestUri: - // The Uri the request is sent to. - // - // completionOption: - // An HTTP completion option value that indicates when the operation should be considered - // completed. - // - // Returns: - // The task object representing the asynchronous operation. - // - // Exceptions: - // T:System.InvalidOperationException: - // The requestUri must be an absolute URI or System.Net.Http.HttpClient.BaseAddress - // must be set. - // - // T:System.Net.Http.HttpRequestException: - // The request failed due to an underlying issue such as network connectivity, DNS - // failure, server certificate validation or timeout. - // - // T:System.Threading.Tasks.TaskCanceledException: - // .NET Core and .NET 5 and later only: The request failed due to timeout. - // - // T:System.UriFormatException: - // The provided request URI is not valid relative or absolute URI. - public Task GetAsync([StringSyntax("Uri")] string? requestUri, HttpCompletionOption completionOption); - // - // Summary: - // Send a GET request to the specified Uri as an asynchronous operation. - // - // Parameters: - // requestUri: - // The Uri the request is sent to. - // - // Returns: - // The task object representing the asynchronous operation. - // - // Exceptions: - // T:System.InvalidOperationException: - // The requestUri must be an absolute URI or System.Net.Http.HttpClient.BaseAddress - // must be set. - // - // T:System.Net.Http.HttpRequestException: - // The request failed due to an underlying issue such as network connectivity, DNS - // failure, server certificate validation or timeout. - // - // T:System.Threading.Tasks.TaskCanceledException: - // .NET Core and .NET 5 and later only: The request failed due to timeout. - // - // T:System.UriFormatException: - // The provided request URI is not valid relative or absolute URI. - public Task GetAsync([StringSyntax("Uri")] string? requestUri); - // - // Summary: - // Send a GET request to the specified Uri with an HTTP completion option and a - // cancellation token as an asynchronous operation. - // - // Parameters: - // requestUri: - // The Uri the request is sent to. - // - // completionOption: - // An HTTP completion option value that indicates when the operation should be considered - // completed. - // - // cancellationToken: - // A cancellation token that can be used by other objects or threads to receive - // notice of cancellation. - // - // Returns: - // The task object representing the asynchronous operation. - // - // Exceptions: - // T:System.InvalidOperationException: - // The requestUri must be an absolute URI or System.Net.Http.HttpClient.BaseAddress - // must be set. - // - // T:System.Net.Http.HttpRequestException: - // The request failed due to an underlying issue such as network connectivity, DNS - // failure, server certificate validation or timeout. - // - // T:System.Threading.Tasks.TaskCanceledException: - // .NET Core and .NET 5 and later only: The request failed due to timeout. - public Task GetAsync(Uri? requestUri, HttpCompletionOption completionOption, CancellationToken cancellationToken); - // - // Summary: - // Send a GET request to the specified Uri with an HTTP completion option as an - // asynchronous operation. - // - // Parameters: - // requestUri: - // The Uri the request is sent to. - // - // completionOption: - // An HTTP completion option value that indicates when the operation should be considered - // completed. - // - // Returns: - // The task object representing the asynchronous operation. - // - // Exceptions: - // T:System.InvalidOperationException: - // The requestUri must be an absolute URI or System.Net.Http.HttpClient.BaseAddress - // must be set. - // - // T:System.Net.Http.HttpRequestException: - // The request failed due to an underlying issue such as network connectivity, DNS - // failure, server certificate validation or timeout. - // - // T:System.Threading.Tasks.TaskCanceledException: - // .NET Core and .NET 5 and later only: The request failed due to timeout. - public Task GetAsync(Uri? requestUri, HttpCompletionOption completionOption); - // - // Summary: - // Send a GET request to the specified Uri as an asynchronous operation. - // - // Parameters: - // requestUri: - // The Uri the request is sent to. - // - // Returns: - // The task object representing the asynchronous operation. - // - // Exceptions: - // T:System.InvalidOperationException: - // The requestUri must be an absolute URI or System.Net.Http.HttpClient.BaseAddress - // must be set. - // - // T:System.Net.Http.HttpRequestException: - // The request failed due to an underlying issue such as network connectivity, DNS - // failure, server certificate validation or timeout. - // - // T:System.Threading.Tasks.TaskCanceledException: - // .NET Core and .NET 5 and later only: The request failed due to timeout. - public Task GetAsync(Uri? requestUri); - // - // Summary: - // Send a GET request to the specified Uri with a cancellation token as an asynchronous - // operation. - // - // Parameters: - // requestUri: - // The Uri the request is sent to. - // - // cancellationToken: - // A cancellation token that can be used by other objects or threads to receive - // notice of cancellation. - // - // Returns: - // The task object representing the asynchronous operation. - // - // Exceptions: - // T:System.InvalidOperationException: - // The requestUri must be an absolute URI or System.Net.Http.HttpClient.BaseAddress - // must be set. - // - // T:System.Net.Http.HttpRequestException: - // The request failed due to an underlying issue such as network connectivity, DNS - // failure, server certificate validation or timeout. - // - // T:System.Threading.Tasks.TaskCanceledException: - // .NET Core and .NET 5 and later only: The request failed due to timeout. - // - // T:System.UriFormatException: - // The provided request URI is not valid relative or absolute URI. - public Task GetAsync([StringSyntax("Uri")] string? requestUri, CancellationToken cancellationToken); - // - // Summary: - // Send a GET request to the specified Uri with an HTTP completion option and a - // cancellation token as an asynchronous operation. - // - // Parameters: - // requestUri: - // The Uri the request is sent to. - // - // completionOption: - // An HTTP completion option value that indicates when the operation should be considered - // completed. - // - // cancellationToken: - // A cancellation token that can be used by other objects or threads to receive - // notice of cancellation. - // - // Returns: - // The task object representing the asynchronous operation. - // - // Exceptions: - // T:System.InvalidOperationException: - // The requestUri must be an absolute URI or System.Net.Http.HttpClient.BaseAddress - // must be set. - // - // T:System.Net.Http.HttpRequestException: - // The request failed due to an underlying issue such as network connectivity, DNS - // failure, server certificate validation or timeout. - // - // T:System.Threading.Tasks.TaskCanceledException: - // .NET Core and .NET 5 and later only: The request failed due to timeout. - // - // T:System.UriFormatException: - // The provided request URI is not valid relative or absolute URI. - public Task GetAsync([StringSyntax("Uri")] string? requestUri, HttpCompletionOption completionOption, CancellationToken cancellationToken); - // - // Summary: - // Send a GET request to the specified Uri with a cancellation token as an asynchronous - // operation. - // - // Parameters: - // requestUri: - // The Uri the request is sent to. - // - // cancellationToken: - // A cancellation token that can be used by other objects or threads to receive - // notice of cancellation. - // - // Returns: - // The task object representing the asynchronous operation. - // - // Exceptions: - // T:System.InvalidOperationException: - // The requestUri must be an absolute URI or System.Net.Http.HttpClient.BaseAddress - // must be set. - // - // T:System.Net.Http.HttpRequestException: - // The request failed due to an underlying issue such as network connectivity, DNS - // failure, server certificate validation or timeout. - // - // T:System.Threading.Tasks.TaskCanceledException: - // .NET Core and .NET 5 and later only: The request failed due to timeout. - public Task GetAsync(Uri? requestUri, CancellationToken cancellationToken); - // - // Summary: - // Sends a GET request to the specified Uri and return the response body as a byte - // array in an asynchronous operation. - // - // Parameters: - // requestUri: - // The Uri the request is sent to. - // - // Returns: - // The task object representing the asynchronous operation. - // - // Exceptions: - // T:System.InvalidOperationException: - // The requestUri must be an absolute URI or System.Net.Http.HttpClient.BaseAddress - // must be set. - // - // T:System.Net.Http.HttpRequestException: - // The request failed due to an underlying issue such as network connectivity, DNS - // failure, server certificate validation or timeout. - // - // T:System.Threading.Tasks.TaskCanceledException: - // .NET Core and .NET 5 and later only: The request failed due to timeout. - // - // T:System.UriFormatException: - // The provided request URI is not valid relative or absolute URI. - public Task GetByteArrayAsync([StringSyntax("Uri")] string? requestUri); - // - // Summary: - // Sends a GET request to the specified Uri and return the response body as a byte - // array in an asynchronous operation. - // - // Parameters: - // requestUri: - // The Uri the request is sent to. - // - // cancellationToken: - // The cancellation token to cancel the operation. - // - // Returns: - // The task object representing the asynchronous operation. - // - // Exceptions: - // T:System.Net.Http.HttpRequestException: - // The request failed due to an underlying issue such as network connectivity, DNS - // failure, server certificate validation (or timeout for .NET Framework only). - // - // - // T:System.Threading.Tasks.TaskCanceledException: - // .NET Core and .NET 5 and later only: The request failed due to timeout. - // - // T:System.UriFormatException: - // The provided request URI is not valid relative or absolute URI. - public Task GetByteArrayAsync([StringSyntax("Uri")] string? requestUri, CancellationToken cancellationToken); - // - // Summary: - // Send a GET request to the specified Uri and return the response body as a byte - // array in an asynchronous operation. - // - // Parameters: - // requestUri: - // The Uri the request is sent to. - // - // Returns: - // The task object representing the asynchronous operation. - // - // Exceptions: - // T:System.InvalidOperationException: - // The requestUri must be an absolute URI or System.Net.Http.HttpClient.BaseAddress - // must be set. - // - // T:System.Net.Http.HttpRequestException: - // The request failed due to an underlying issue such as network connectivity, DNS - // failure, server certificate validation (or timeout for .NET Framework only). - // - // - // T:System.Threading.Tasks.TaskCanceledException: - // .NET Core and .NET 5 and later only: The request failed due to timeout. - public Task GetByteArrayAsync(Uri? requestUri); - // - // Summary: - // Send a GET request to the specified Uri and return the response body as a byte - // array in an asynchronous operation. - // - // Parameters: - // requestUri: - // The Uri the request is sent to. - // - // cancellationToken: - // The cancellation token to cancel the operation. - // - // Returns: - // The task object representing the asynchronous operation. - // - // Exceptions: - // T:System.Net.Http.HttpRequestException: - // The request failed due to an underlying issue such as network connectivity, DNS - // failure, server certificate validation (or timeout for .NET Framework only). - // - // - // T:System.Threading.Tasks.TaskCanceledException: - // .NET Core and .NET 5 and later only: The request failed due to timeout. - public Task GetByteArrayAsync(Uri? requestUri, CancellationToken cancellationToken); - // - // Summary: - // Send a GET request to the specified Uri and return the response body as a stream - // in an asynchronous operation. - // - // Parameters: - // requestUri: - // The Uri the request is sent to. - // - // Returns: - // The task object representing the asynchronous operation. - // - // Exceptions: - // T:System.InvalidOperationException: - // The requestUri must be an absolute URI or System.Net.Http.HttpClient.BaseAddress - // must be set. - // - // T:System.Net.Http.HttpRequestException: - // The request failed due to an underlying issue such as network connectivity, DNS - // failure, server certificate validation (or timeout for .NET Framework only). - // - // - // T:System.Threading.Tasks.TaskCanceledException: - // .NET Core and .NET 5 and later only: The request failed due to timeout. - // - // T:System.UriFormatException: - // The provided request URI is not valid relative or absolute URI. - public Task GetStreamAsync([StringSyntax("Uri")] string? requestUri); - // - // Summary: - // Send a GET request to the specified Uri and return the response body as a stream - // in an asynchronous operation. - // - // Parameters: - // requestUri: - // The Uri the request is sent to. - // - // cancellationToken: - // The cancellation token to cancel the operation. - // - // Returns: - // The task object representing the asynchronous operation. - public Task GetStreamAsync([StringSyntax("Uri")] string? requestUri, CancellationToken cancellationToken); - // - // Summary: - // Send a GET request to the specified Uri and return the response body as a stream - // in an asynchronous operation. - // - // Parameters: - // requestUri: - // The Uri the request is sent to. - // - // Returns: - // The task object representing the asynchronous operation. - // - // Exceptions: - // T:System.InvalidOperationException: - // The requestUri must be an absolute URI or System.Net.Http.HttpClient.BaseAddress - // must be set. - // - // T:System.Net.Http.HttpRequestException: - // The request failed due to an underlying issue such as network connectivity, DNS - // failure, server certificate validation (or timeout for .NET Framework only). - // - // - // T:System.Threading.Tasks.TaskCanceledException: - // .NET Core and .NET 5 and later only: The request failed due to timeout. - public Task GetStreamAsync(Uri? requestUri); - // - // Summary: - // Send a GET request to the specified Uri and return the response body as a stream - // in an asynchronous operation. - // - // Parameters: - // requestUri: - // The Uri the request is sent to. - // - // cancellationToken: - // The cancellation token to cancel the operation. - // - // Returns: - // The task object representing the asynchronous operation. - // - // Exceptions: - // T:System.ArgumentNullException: - // The requestUri is null. - // - // T:System.Net.Http.HttpRequestException: - // The request failed due to an underlying issue such as network connectivity, DNS - // failure, server certificate validation (or timeout for .NET Framework only). - // - // - // T:System.Threading.Tasks.TaskCanceledException: - // .NET Core and .NET 5 and later only: The request failed due to timeout. - public Task GetStreamAsync(Uri? requestUri, CancellationToken cancellationToken); - // - // Summary: - // Send a GET request to the specified Uri and return the response body as a string - // in an asynchronous operation. - // - // Parameters: - // requestUri: - // The Uri the request is sent to. - // - // Returns: - // The task object representing the asynchronous operation. - // - // Exceptions: - // T:System.InvalidOperationException: - // The requestUri must be an absolute URI or System.Net.Http.HttpClient.BaseAddress - // must be set. - // - // T:System.Net.Http.HttpRequestException: - // The request failed due to an underlying issue such as network connectivity, DNS - // failure, server certificate validation (or timeout for .NET Framework only). - // - // - // T:System.Threading.Tasks.TaskCanceledException: - // .NET Core and .NET 5 and later only: The request failed due to timeout. - public Task GetStringAsync(Uri? requestUri); - // - // Summary: - // Send a GET request to the specified Uri and return the response body as a string - // in an asynchronous operation. - // - // Parameters: - // requestUri: - // The Uri the request is sent to. - // - // Returns: - // The task object representing the asynchronous operation. - // - // Exceptions: - // T:System.InvalidOperationException: - // The requestUri must be an absolute URI or System.Net.Http.HttpClient.BaseAddress - // must be set. - // - // T:System.Net.Http.HttpRequestException: - // The request failed due to an underlying issue such as network connectivity, DNS - // failure, server certificate validation (or timeout for .NET Framework only). - // - // - // T:System.Threading.Tasks.TaskCanceledException: - // .NET Core and .NET 5 and later only: The request failed due to timeout. - // - // T:System.UriFormatException: - // The provided request URI is not valid relative or absolute URI. - public Task GetStringAsync([StringSyntax("Uri")] string? requestUri); - // - // Summary: - // Send a GET request to the specified Uri and return the response body as a string - // in an asynchronous operation. - // - // Parameters: - // requestUri: - // The Uri the request is sent to. - // - // cancellationToken: - // The cancellation token to cancel the operation. - // - // Returns: - // The task object representing the asynchronous operation. - // - // Exceptions: - // T:System.ArgumentNullException: - // The requestUri is null. - // - // T:System.Net.Http.HttpRequestException: - // The request failed due to an underlying issue such as network connectivity, DNS - // failure, server certificate validation (or timeout for .NET Framework only). - // - // - // T:System.Threading.Tasks.TaskCanceledException: - // .NET Core and .NET 5 and later only: The request failed due to timeout. - // - // T:System.UriFormatException: - // The provided request URI is not valid relative or absolute URI. - public Task GetStringAsync([StringSyntax("Uri")] string? requestUri, CancellationToken cancellationToken); - // - // Summary: - // Send a GET request to the specified Uri and return the response body as a string - // in an asynchronous operation. - // - // Parameters: - // requestUri: - // The Uri the request is sent to. - // - // cancellationToken: - // The cancellation token to cancel the operation. - // - // Returns: - // The task object representing the asynchronous operation. - // - // Exceptions: - // T:System.ArgumentNullException: - // The requestUri is null. - // - // T:System.Net.Http.HttpRequestException: - // The request failed due to an underlying issue such as network connectivity, DNS - // failure, server certificate validation (or timeout for .NET Framework only). - // - // - // T:System.Threading.Tasks.TaskCanceledException: - // .NET Core and .NET 5 and later only: The request failed due to timeout. - public Task GetStringAsync(Uri? requestUri, CancellationToken cancellationToken); - // - // Summary: - // Sends a PATCH request with a cancellation token to a Uri represented as a string - // as an asynchronous operation. - // - // Parameters: - // requestUri: - // The Uri the request is sent to. - // - // content: - // The HTTP request content sent to the server. - // - // cancellationToken: - // A cancellation token that can be used by other objects or threads to receive - // notice of cancellation. - // - // Returns: - // The task object representing the asynchronous operation. - // - // Exceptions: - // T:System.UriFormatException: - // The provided request URI is not valid relative or absolute URI. - public Task PatchAsync([StringSyntax("Uri")] string? requestUri, HttpContent? content, CancellationToken cancellationToken); - // - // Summary: - // Sends a PATCH request to a Uri designated as a string as an asynchronous operation. - // - // - // Parameters: - // requestUri: - // The Uri the request is sent to. - // - // content: - // The HTTP request content sent to the server. - // - // Returns: - // The task object representing the asynchronous operation. - // - // Exceptions: - // T:System.UriFormatException: - // The provided request URI is not valid relative or absolute URI. - public Task PatchAsync([StringSyntax("Uri")] string? requestUri, HttpContent? content); - // - // Summary: - // Sends a PATCH request as an asynchronous operation. - // - // Parameters: - // requestUri: - // The Uri the request is sent to. - // - // content: - // The HTTP request content sent to the server. - // - // Returns: - // The task object representing the asynchronous operation. - public Task PatchAsync(Uri? requestUri, HttpContent? content); - // - // Summary: - // Sends a PATCH request with a cancellation token as an asynchronous operation. - // - // - // Parameters: - // requestUri: - // The Uri the request is sent to. - // - // content: - // The HTTP request content sent to the server. - // - // cancellationToken: - // A cancellation token that can be used by other objects or threads to receive - // notice of cancellation. - // - // Returns: - // The task object representing the asynchronous operation. - public Task PatchAsync(Uri? requestUri, HttpContent? content, CancellationToken cancellationToken); - // - // Summary: - // Send a POST request to the specified Uri as an asynchronous operation. - // - // Parameters: - // requestUri: - // The Uri the request is sent to. - // - // content: - // The HTTP request content sent to the server. - // - // Returns: - // The task object representing the asynchronous operation. - // - // Exceptions: - // T:System.InvalidOperationException: - // The requestUri must be an absolute URI or System.Net.Http.HttpClient.BaseAddress - // must be set. - // - // T:System.Net.Http.HttpRequestException: - // The request failed due to an underlying issue such as network connectivity, DNS - // failure, server certificate validation or timeout. - // - // T:System.Threading.Tasks.TaskCanceledException: - // .NET Core and .NET 5 and later only: The request failed due to timeout. - // - // T:System.UriFormatException: - // The provided request URI is not valid relative or absolute URI. - public Task PostAsync([StringSyntax("Uri")] string? requestUri, HttpContent? content); - // - // Summary: - // Send a POST request with a cancellation token as an asynchronous operation. - // - // Parameters: - // requestUri: - // The Uri the request is sent to. - // - // content: - // The HTTP request content sent to the server. - // - // cancellationToken: - // A cancellation token that can be used by other objects or threads to receive - // notice of cancellation. - // - // Returns: - // The task object representing the asynchronous operation. - // - // Exceptions: - // T:System.InvalidOperationException: - // The requestUri must be an absolute URI or System.Net.Http.HttpClient.BaseAddress - // must be set. - // - // T:System.Net.Http.HttpRequestException: - // The request failed due to an underlying issue such as network connectivity, DNS - // failure, server certificate validation or timeout. - // - // T:System.Threading.Tasks.TaskCanceledException: - // .NET Core and .NET 5 and later only: The request failed due to timeout. - // - // T:System.UriFormatException: - // The provided request URI is not valid relative or absolute URI. - public Task PostAsync([StringSyntax("Uri")] string? requestUri, HttpContent? content, CancellationToken cancellationToken); - // - // Summary: - // Send a POST request to the specified Uri as an asynchronous operation. - // - // Parameters: - // requestUri: - // The Uri the request is sent to. - // - // content: - // The HTTP request content sent to the server. - // - // Returns: - // The task object representing the asynchronous operation. - // - // Exceptions: - // T:System.InvalidOperationException: - // The requestUri must be an absolute URI or System.Net.Http.HttpClient.BaseAddress - // must be set. - // - // T:System.Net.Http.HttpRequestException: - // The request failed due to an underlying issue such as network connectivity, DNS - // failure, server certificate validation or timeout. - // - // T:System.Threading.Tasks.TaskCanceledException: - // .NET Core and .NET 5 and later only: The request failed due to timeout. - public Task PostAsync(Uri? requestUri, HttpContent? content); - // - // Summary: - // Send a POST request with a cancellation token as an asynchronous operation. - // - // Parameters: - // requestUri: - // The Uri the request is sent to. - // - // content: - // The HTTP request content sent to the server. - // - // cancellationToken: - // A cancellation token that can be used by other objects or threads to receive - // notice of cancellation. - // - // Returns: - // The task object representing the asynchronous operation. - // - // Exceptions: - // T:System.InvalidOperationException: - // The requestUri must be an absolute URI or System.Net.Http.HttpClient.BaseAddress - // must be set. - // - // T:System.Net.Http.HttpRequestException: - // The request failed due to an underlying issue such as network connectivity, DNS - // failure, server certificate validation or timeout. - // - // T:System.Threading.Tasks.TaskCanceledException: - // .NET Core and .NET 5 and later only: The request failed due to timeout. - public Task PostAsync(Uri? requestUri, HttpContent? content, CancellationToken cancellationToken); - // - // Summary: - // Send a PUT request to the specified Uri as an asynchronous operation. - // - // Parameters: - // requestUri: - // The Uri the request is sent to. - // - // content: - // The HTTP request content sent to the server. - // - // Returns: - // The task object representing the asynchronous operation. - // - // Exceptions: - // T:System.InvalidOperationException: - // The requestUri must be an absolute URI or System.Net.Http.HttpClient.BaseAddress - // must be set. - // - // T:System.Net.Http.HttpRequestException: - // The request failed due to an underlying issue such as network connectivity, DNS - // failure, server certificate validation or timeout. - // - // T:System.Threading.Tasks.TaskCanceledException: - // .NET Core and .NET 5 and later only: The request failed due to timeout. - public Task PutAsync(Uri? requestUri, HttpContent? content); - // - // Summary: - // Send a PUT request with a cancellation token as an asynchronous operation. - // - // Parameters: - // requestUri: - // The Uri the request is sent to. - // - // content: - // The HTTP request content sent to the server. - // - // cancellationToken: - // A cancellation token that can be used by other objects or threads to receive - // notice of cancellation. - // - // Returns: - // The task object representing the asynchronous operation. - // - // Exceptions: - // T:System.InvalidOperationException: - // The requestUri must be an absolute URI or System.Net.Http.HttpClient.BaseAddress - // must be set. - // - // T:System.Net.Http.HttpRequestException: - // The request failed due to an underlying issue such as network connectivity, DNS - // failure, server certificate validation or timeout. - // - // T:System.Threading.Tasks.TaskCanceledException: - // .NET Core and .NET 5 and later only: The request failed due to timeout. - public Task PutAsync(Uri? requestUri, HttpContent? content, CancellationToken cancellationToken); - // - // Summary: - // Send a PUT request to the specified Uri as an asynchronous operation. - // - // Parameters: - // requestUri: - // The Uri the request is sent to. - // - // content: - // The HTTP request content sent to the server. - // - // Returns: - // The task object representing the asynchronous operation. - // - // Exceptions: - // T:System.InvalidOperationException: - // The requestUri must be an absolute URI or System.Net.Http.HttpClient.BaseAddress - // must be set. - // - // T:System.Net.Http.HttpRequestException: - // The request failed due to an underlying issue such as network connectivity, DNS - // failure, server certificate validation or timeout. - // - // T:System.Threading.Tasks.TaskCanceledException: - // .NET Core and .NET 5 and later only: The request failed due to timeout. - // - // T:System.UriFormatException: - // The provided request URI is not valid relative or absolute URI. - public Task PutAsync([StringSyntax("Uri")] string? requestUri, HttpContent? content); - // - // Summary: - // Send a PUT request with a cancellation token as an asynchronous operation. - // - // Parameters: - // requestUri: - // The Uri the request is sent to. - // - // content: - // The HTTP request content sent to the server. - // - // cancellationToken: - // A cancellation token that can be used by other objects or threads to receive - // notice of cancellation. - // - // Returns: - // The task object representing the asynchronous operation. - // - // Exceptions: - // T:System.InvalidOperationException: - // The requestUri must be an absolute URI or System.Net.Http.HttpClient.BaseAddress - // must be set. - // - // T:System.Net.Http.HttpRequestException: - // The request failed due to an underlying issue such as network connectivity, DNS - // failure, server certificate validation or timeout. - // - // T:System.Threading.Tasks.TaskCanceledException: - // .NET Core and .NET 5 and later only: The request failed due to timeout. - // - // T:System.UriFormatException: - // The provided request URI is not valid relative or absolute URI. - public Task PutAsync([StringSyntax("Uri")] string? requestUri, HttpContent? content, CancellationToken cancellationToken); - // - // Summary: - // Sends an HTTP request with the specified request, completion option and cancellation - // token. - // - // Parameters: - // request: - // The HTTP request message to send. - // - // completionOption: - // One of the enumeration values that specifies when the operation should complete - // (as soon as a response is available or after reading the response content). - // - // cancellationToken: - // The token to cancel the operation. - // - // Returns: - // The HTTP response message. - // - // Exceptions: - // T:System.ArgumentNullException: - // The request is null. - // - // T:System.NotSupportedException: - // The HTTP version is 2.0 or higher or the version policy is set to System.Net.Http.HttpVersionPolicy.RequestVersionOrHigher. - // -or- The custom class derived from System.Net.Http.HttpContent does not override - // the System.Net.Http.HttpContent.SerializeToStream(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken) - // method. -or- The custom System.Net.Http.HttpMessageHandler does not override - // the System.Net.Http.HttpMessageHandler.Send(System.Net.Http.HttpRequestMessage,System.Threading.CancellationToken) - // method. - // - // T:System.InvalidOperationException: - // The request message was already sent by the System.Net.Http.HttpClient instance. - // - // - // T:System.Net.Http.HttpRequestException: - // The request failed due to an underlying issue such as network connectivity, DNS - // failure, or server certificate validation. - // - // T:System.Threading.Tasks.TaskCanceledException: - // The request was canceled. -or- If the System.Threading.Tasks.TaskCanceledException - // exception nests the System.TimeoutException: The request failed due to timeout. - [UnsupportedOSPlatform("browser")] - public HttpResponseMessage Send(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationToken cancellationToken); - // - // Summary: - // Sends an HTTP request with the specified request and cancellation token. - // - // Parameters: - // request: - // The HTTP request message to send. - // - // cancellationToken: - // The token to cancel the operation. - // - // Returns: - // The HTTP response message. - // - // Exceptions: - // T:System.ArgumentNullException: - // The request is null. - // - // T:System.NotSupportedException: - // The HTTP version is 2.0 or higher or the version policy is set to System.Net.Http.HttpVersionPolicy.RequestVersionOrHigher. - // -or- The custom class derived from System.Net.Http.HttpContent does not override - // the System.Net.Http.HttpContent.SerializeToStream(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken) - // method. -or- The custom System.Net.Http.HttpMessageHandler does not override - // the System.Net.Http.HttpMessageHandler.Send(System.Net.Http.HttpRequestMessage,System.Threading.CancellationToken) - // method. - // - // T:System.InvalidOperationException: - // The request message was already sent by the System.Net.Http.HttpClient instance. - // - // - // T:System.Net.Http.HttpRequestException: - // The request failed due to an underlying issue such as network connectivity, DNS - // failure, or server certificate validation. - // - // T:System.Threading.Tasks.TaskCanceledException: - // The request was canceled. -or- If the System.Threading.Tasks.TaskCanceledException - // exception nests the System.TimeoutException: The request failed due to timeout. - [UnsupportedOSPlatform("browser")] - public HttpResponseMessage Send(HttpRequestMessage request, CancellationToken cancellationToken); - // - // Summary: - // Sends an HTTP request. - // - // Parameters: - // request: - // The HTTP request message to send. - // - // completionOption: - // One of the enumeration values that specifies when the operation should complete - // (as soon as a response is available or after reading the response content). - // - // Returns: - // The HTTP response message. - // - // Exceptions: - // T:System.ArgumentNullException: - // The request is null. - // - // T:System.NotSupportedException: - // The HTTP version is 2.0 or higher or the version policy is set to System.Net.Http.HttpVersionPolicy.RequestVersionOrHigher. - // -or- The custom class derived from System.Net.Http.HttpContent does not override - // the System.Net.Http.HttpContent.SerializeToStream(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken) - // method. -or- The custom System.Net.Http.HttpMessageHandler does not override - // the System.Net.Http.HttpMessageHandler.Send(System.Net.Http.HttpRequestMessage,System.Threading.CancellationToken) - // method. - // - // T:System.InvalidOperationException: - // The request message was already sent by the System.Net.Http.HttpClient instance. - // - // - // T:System.Net.Http.HttpRequestException: - // The request failed due to an underlying issue such as network connectivity, DNS - // failure, or server certificate validation. - // - // T:System.Threading.Tasks.TaskCanceledException: - // If the System.Threading.Tasks.TaskCanceledException exception nests the System.TimeoutException: - // The request failed due to timeout. - [UnsupportedOSPlatform("browser")] - public HttpResponseMessage Send(HttpRequestMessage request, HttpCompletionOption completionOption); - // - // Summary: - // Sends an HTTP request with the specified request. - // - // Parameters: - // request: - // The HTTP request message to send. - // - // Returns: - // An HTTP response message. - // - // Exceptions: - // T:System.ArgumentNullException: - // The request is null. - // - // T:System.NotSupportedException: - // The HTTP version is 2.0 or higher or the version policy is set to System.Net.Http.HttpVersionPolicy.RequestVersionOrHigher. - // -or- The custom class derived from System.Net.Http.HttpContent does not override - // the System.Net.Http.HttpContent.SerializeToStream(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken) - // method. -or- The custom System.Net.Http.HttpMessageHandler does not override - // the System.Net.Http.HttpMessageHandler.Send(System.Net.Http.HttpRequestMessage,System.Threading.CancellationToken) - // method. - // - // T:System.InvalidOperationException: - // The request message was already sent by the System.Net.Http.HttpClient instance. - // - // - // T:System.Net.Http.HttpRequestException: - // The request failed due to an underlying issue such as network connectivity, DNS - // failure, or server certificate validation. - // - // T:System.Threading.Tasks.TaskCanceledException: - // If the System.Threading.Tasks.TaskCanceledException exception nests the System.TimeoutException: - // The request failed due to timeout. - [UnsupportedOSPlatform("browser")] - public HttpResponseMessage Send(HttpRequestMessage request); - // - // Summary: - // Send an HTTP request as an asynchronous operation. - // - // Parameters: - // request: - // The HTTP request message to send. - // - // cancellationToken: - // The cancellation token to cancel operation. - // - // Returns: - // The task object representing the asynchronous operation. - // - // Exceptions: - // T:System.ArgumentNullException: - // The request is null. - // - // T:System.InvalidOperationException: - // The request message was already sent by the System.Net.Http.HttpClient instance. - // - // - // T:System.Net.Http.HttpRequestException: - // The request failed due to an underlying issue such as network connectivity, DNS - // failure, server certificate validation or timeout. - // - // T:System.Threading.Tasks.TaskCanceledException: - // .NET Core and .NET 5 and later only: The request failed due to timeout. - public Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken); - // - // Summary: - // Send an HTTP request as an asynchronous operation. - // - // Parameters: - // request: - // The HTTP request message to send. - // - // Returns: - // The task object representing the asynchronous operation. - // - // Exceptions: - // T:System.ArgumentNullException: - // The request is null. - // - // T:System.InvalidOperationException: - // The request message was already sent by the System.Net.Http.HttpClient instance. - // - // - // T:System.Net.Http.HttpRequestException: - // The request failed due to an underlying issue such as network connectivity, DNS - // failure, server certificate validation or timeout. - // - // T:System.Threading.Tasks.TaskCanceledException: - // .NET Core and .NET 5 and later only: The request failed due to timeout. - public Task SendAsync(HttpRequestMessage request); - // - // Summary: - // Send an HTTP request as an asynchronous operation. - // - // Parameters: - // request: - // The HTTP request message to send. - // - // completionOption: - // When the operation should complete (as soon as a response is available or after - // reading the whole response content). - // - // Returns: - // The task object representing the asynchronous operation. - // - // Exceptions: - // T:System.ArgumentNullException: - // The request is null. - // - // T:System.InvalidOperationException: - // The request message was already sent by the System.Net.Http.HttpClient instance. - // - // - // T:System.Net.Http.HttpRequestException: - // The request failed due to an underlying issue such as network connectivity, DNS - // failure, server certificate validation or timeout. - // - // T:System.Threading.Tasks.TaskCanceledException: - // .NET Core and .NET 5 and later only: The request failed due to timeout. - public Task SendAsync(HttpRequestMessage request, HttpCompletionOption completionOption); - // - // Summary: - // Send an HTTP request as an asynchronous operation. - // - // Parameters: - // request: - // The HTTP request message to send. - // - // completionOption: - // When the operation should complete (as soon as a response is available or after - // reading the whole response content). - // - // cancellationToken: - // The cancellation token to cancel operation. - // - // Returns: - // The task object representing the asynchronous operation. - // - // Exceptions: - // T:System.ArgumentNullException: - // The request is null. - // - // T:System.InvalidOperationException: - // The request message was already sent by the System.Net.Http.HttpClient instance. - // - // - // T:System.Net.Http.HttpRequestException: - // The request failed due to an underlying issue such as network connectivity, DNS - // failure, server certificate validation or timeout. - // - // T:System.Threading.Tasks.TaskCanceledException: - // .NET Core and .NET 5 and later only: The request failed due to timeout. - public Task SendAsync(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationToken cancellationToken); -} \ No newline at end of file diff --git a/Discord.API/Rest/RestClient.cs b/Discord.API/Rest/RestClient.cs index 3f045a3..534c592 100644 --- a/Discord.API/Rest/RestClient.cs +++ b/Discord.API/Rest/RestClient.cs @@ -1,20 +1,27 @@ using System.Text.Json; -using Discord.API.Rest.HttpClientWrapper; using Serilog; namespace Discord.API.Rest; internal class RestClient{ - private IHttpClientWrapper httpClient; + private HttpClient httpClient; public RestClient(string base_url, string api_key) - : this(api_key, new HttpClientWrapper.HttpClientWrapper() {BaseAddress = new Uri(base_url)}) - { } + { + Log.Debug("REST: Creating new rest client. Base url: {base_url}", base_url); + httpClient = new HttpClient(){ + BaseAddress=new Uri(base_url) + }; + httpClient.DefaultRequestHeaders.Add("Authorization", $"Bot {api_key}"); + httpClient.DefaultRequestHeaders.Add("User-Agent", $"DiscordBot ({StaticProperties.LibraryWebsite}, {StaticProperties.LibraryVersion})"); + } - internal RestClient(string api_key, IHttpClientWrapper http_client){ - Log.Debug("REST: Creating new rest client. Base url: {base_url}", http_client.BaseAddress?.AbsoluteUri); - httpClient = http_client; + internal RestClient(string base_url, string api_key, HttpMessageHandler msg_handler){ + Log.Debug("REST: Creating new rest client. Base url: {base_url}", base_url); + httpClient = new HttpClient(msg_handler){ + BaseAddress=new Uri(base_url) + }; httpClient.DefaultRequestHeaders.Add("Authorization", $"Bot {api_key}"); httpClient.DefaultRequestHeaders.Add("User-Agent", $"DiscordBot ({StaticProperties.LibraryWebsite}, {StaticProperties.LibraryVersion})"); } diff --git a/Discord.API/Rest/RestError.cs b/Discord.API/Rest/RestError.cs index 383c956..bbf54dd 100644 --- a/Discord.API/Rest/RestError.cs +++ b/Discord.API/Rest/RestError.cs @@ -2,6 +2,6 @@ public readonly struct RestError { - public required int Code {get; init;} + public int? Code {get; init;} public required string Message {get; init;} }