From a94096da429f36d2f1c09db44bf25800be87cbd3 Mon Sep 17 00:00:00 2001 From: Jarl Gullberg Date: Wed, 18 Jan 2023 19:28:05 +0100 Subject: [PATCH] Implement new error code and more error fields. See discord/discord-api-docs#5574. --- .../API/Objects/Errors/IRestError.cs | 15 ++++++++++++++- .../Results/DiscordError.cs | 5 +++++ .../API/Objects/Errors/RestError.cs | 6 ++++-- .../Extensions/ServiceCollectionExtensions.cs | 3 ++- .../Polly/DiscordRateLimitPolicy.cs | 4 +++- .../Samples/Objects/REST_ERROR/REST_ERROR.json | 4 +++- .../Objects/REST_ERROR/REST_ERROR.optionals.json | 3 +++ 7 files changed, 34 insertions(+), 6 deletions(-) create mode 100644 Tests/Remora.Discord.Tests/Samples/Objects/REST_ERROR/REST_ERROR.optionals.json diff --git a/Backend/Remora.Discord.API.Abstractions/API/Objects/Errors/IRestError.cs b/Backend/Remora.Discord.API.Abstractions/API/Objects/Errors/IRestError.cs index b4e989271f..1ed319a42b 100644 --- a/Backend/Remora.Discord.API.Abstractions/API/Objects/Errors/IRestError.cs +++ b/Backend/Remora.Discord.API.Abstractions/API/Objects/Errors/IRestError.cs @@ -31,13 +31,16 @@ namespace Remora.Discord.API.Abstractions.Objects; /// /// Represents an error reported by the REST API. /// +/// +/// This is a combination of the "normal" REST error and the rate limiting error. +/// [PublicAPI] public interface IRestError { /// /// Gets the error code. /// - DiscordError Code { get; } + Optional Code { get; } /// /// Gets the per-property error details. @@ -48,4 +51,14 @@ public interface IRestError /// Gets a descriptive error message. /// string Message { get; } + + /// + /// Gets a value indicating whether you are globally rate limited. + /// + Optional IsGlobal { get; } + + /// + /// Gets the time (in seconds) after which the request should be retried. + /// + Optional RetryAfter { get; } } diff --git a/Backend/Remora.Discord.API.Abstractions/Results/DiscordError.cs b/Backend/Remora.Discord.API.Abstractions/Results/DiscordError.cs index 035fca8fe8..554e44a98b 100644 --- a/Backend/Remora.Discord.API.Abstractions/Results/DiscordError.cs +++ b/Backend/Remora.Discord.API.Abstractions/Results/DiscordError.cs @@ -531,6 +531,11 @@ public enum DiscordError /// TagNamesMustBeUnique = 40061, + /// + /// A service resource is being rate limited. + /// + ServiceResourceIsBeingRateLimited = 40062, + /// /// There are no tags available that can be set by non-moderators. /// diff --git a/Backend/Remora.Discord.API/API/Objects/Errors/RestError.cs b/Backend/Remora.Discord.API/API/Objects/Errors/RestError.cs index ea8c70c92e..b6ecb7e849 100644 --- a/Backend/Remora.Discord.API/API/Objects/Errors/RestError.cs +++ b/Backend/Remora.Discord.API/API/Objects/Errors/RestError.cs @@ -35,7 +35,9 @@ namespace Remora.Discord.API.Objects; [PublicAPI] public record RestError ( - DiscordError Code, + Optional Code, Optional>>> Errors, - string Message + string Message, + Optional IsGlobal, + Optional RetryAfter ) : IRestError; diff --git a/Backend/Remora.Discord.API/Extensions/ServiceCollectionExtensions.cs b/Backend/Remora.Discord.API/Extensions/ServiceCollectionExtensions.cs index f798d19ab0..d795d6eaa6 100644 --- a/Backend/Remora.Discord.API/Extensions/ServiceCollectionExtensions.cs +++ b/Backend/Remora.Discord.API/Extensions/ServiceCollectionExtensions.cs @@ -939,7 +939,8 @@ private static JsonSerializerOptions AddWebhookObjectConverters(this JsonSeriali /// The options, with the converters added. private static JsonSerializerOptions AddErrorObjectConverters(this JsonSerializerOptions options) { - options.AddDataObjectConverter(); + options.AddDataObjectConverter() + .WithPropertyName(e => e.IsGlobal, "global"); options.AddDataObjectConverter(); return options; diff --git a/Backend/Remora.Discord.Rest/Polly/DiscordRateLimitPolicy.cs b/Backend/Remora.Discord.Rest/Polly/DiscordRateLimitPolicy.cs index 3f29658e18..1c22123e0a 100644 --- a/Backend/Remora.Discord.Rest/Polly/DiscordRateLimitPolicy.cs +++ b/Backend/Remora.Discord.Rest/Polly/DiscordRateLimitPolicy.cs @@ -138,10 +138,12 @@ bool continueOnCapturedContext bucketIdentifier = endpoint; } - var getValue = await cache.RetrieveAsync( + var getValue = await cache.RetrieveAsync + ( CacheKey.LocalizedStringKey(nameof(Polly), bucketIdentifier), cancellationToken ); + if (getValue.IsDefined(out var rateLimitBucket)) { // We don't reset route-specific rate limits ourselves; that's the responsibility of the returned headers diff --git a/Tests/Remora.Discord.Tests/Samples/Objects/REST_ERROR/REST_ERROR.json b/Tests/Remora.Discord.Tests/Samples/Objects/REST_ERROR/REST_ERROR.json index 1d70a63c41..23407dc4aa 100644 --- a/Tests/Remora.Discord.Tests/Samples/Objects/REST_ERROR/REST_ERROR.json +++ b/Tests/Remora.Discord.Tests/Samples/Objects/REST_ERROR/REST_ERROR.json @@ -30,5 +30,7 @@ ] } }, - "message": "Invalid Form Body" + "message": "Invalid Form Body", + "global": true, + "retry_after": 1 } diff --git a/Tests/Remora.Discord.Tests/Samples/Objects/REST_ERROR/REST_ERROR.optionals.json b/Tests/Remora.Discord.Tests/Samples/Objects/REST_ERROR/REST_ERROR.optionals.json new file mode 100644 index 0000000000..aa635d1706 --- /dev/null +++ b/Tests/Remora.Discord.Tests/Samples/Objects/REST_ERROR/REST_ERROR.optionals.json @@ -0,0 +1,3 @@ +{ + "message": "Invalid Form Body" +}