-
Notifications
You must be signed in to change notification settings - Fork 121
feat: handle unknown enum values gracefully #1235
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: 32.X
Are you sure you want to change the base?
Conversation
Summary of ChangesHello @galesky-a, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request enhances the robustness of JSON deserialization by implementing a strategy to gracefully handle unknown enum values. By introducing a custom SafeStringEnumConverter and registering it globally, the application will now assign null to nullable enum properties when an unrecognized enum string is encountered in the JSON, rather than throwing an exception. This change, coupled with an update to the code generation template to make all enum properties nullable, significantly improves the client's forward compatibility with API changes that might introduce new enum members. Highlights
🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console. Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
|
/gemini review |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code Review
This pull request introduces a SafeStringEnumConverter to gracefully handle unknown enum values during JSON deserialization by converting them to null instead of throwing an exception. This is a great improvement for forward compatibility with the API. The changes include updating the JSON deserialization logic, modifying the model generation template to make all enums nullable, and adding a comprehensive suite of tests.
My review includes a few suggestions to improve the implementation and tests:
- A small refinement in the
SafeStringEnumConverterfor better code style. - An improvement to one of the new tests to make it more efficient.
- A recommendation to make another test more accurately reflect its purpose.
| { | ||
| // Test if Newtonsoft.Json handles unknown enum values gracefully with nullable enums | ||
| var json = @"{""enumField"": ""UnknownValue""}"; | ||
|
|
||
| // This will tell us if we need a custom converter or not | ||
| var exception = Record.Exception(() => JsonConvert.DeserializeObject<TestModel>(json)); | ||
|
|
||
| if (exception == null) | ||
| { | ||
| var result = JsonConvert.DeserializeObject<TestModel>(json); | ||
| Assert.Null(result.EnumField); | ||
| } | ||
| else | ||
| { | ||
| // If this throws, we need a custom converter | ||
| Assert.IsType<JsonSerializationException>(exception); | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test deserializes the JSON string twice if no exception is thrown. This is inefficient and can be avoided by capturing the result of the deserialization within the Record.Exception lambda.
{
// Test if Newtonsoft.Json handles unknown enum values gracefully with nullable enums
var json = @"{\"enumField\": \"UnknownValue\"}";
// This will tell us if we need a custom converter or not
TestModel result = null;
var exception = Record.Exception(() => result = JsonConvert.DeserializeObject<TestModel>(json));
if (exception == null)
{
Assert.NotNull(result);
Assert.Null(result.EnumField);
}
else
{
// If this throws, we need a custom converter
Assert.IsType<JsonSerializationException>(exception);
}
}| [Fact] | ||
| public void TestWebhookHandlerWithUnknownEnumValues() | ||
| { | ||
| // Test the actual webhook handler path | ||
| var json = @"{ | ||
| ""live"": ""false"", | ||
| ""notificationItems"": [] | ||
| }"; | ||
|
|
||
| var webhookHandler = new Adyen.Webhooks.WebhookHandler(); | ||
| var notificationRequest = webhookHandler.HandleNotificationRequest(json); | ||
|
|
||
| Assert.NotNull(notificationRequest); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The name of this test, TestWebhookHandlerWithUnknownEnumValues, is misleading as the JSON payload used does not contain any unknown enum values. It only tests that a minimal webhook notification can be deserialized without errors.
To make this test more valuable and align with its name, consider adding a notification item with an unknown enum value to the JSON payload. For example (assuming eventCode is an enum property):
{
"live": "false",
"notificationItems": [
{
"NotificationRequestItem": {
"eventCode": "SOME_UNKNOWN_EVENT_CODE",
"success": "true"
// ... other properties
}
}
]
}This would properly test that the webhook handler can gracefully handle new, unknown enum values from the Adyen platform.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code Review
This pull request introduces a SafeStringEnumConverter to handle unknown enum values gracefully during JSON deserialization, preventing exceptions and instead returning null for nullable enum properties. This change improves forward compatibility with API changes. The converter is registered globally, and model generation templates have been updated to make all enum properties nullable. New tests have been added to cover this new behavior.
My review focuses on improving the new tests for clarity and completeness, and suggests a minor refinement in the converter's implementation for better readability and adherence to C# idioms. Overall, this is a valuable improvement for the library's robustness.
| public void TestNewtonsoft_UnknownEnumValue_WithNullable() | ||
| { | ||
| // Test if Newtonsoft.Json handles unknown enum values gracefully with nullable enums | ||
| var json = @"{""enumField"": ""UnknownValue""}"; | ||
|
|
||
| // This will tell us if we need a custom converter or not | ||
| var exception = Record.Exception(() => JsonConvert.DeserializeObject<TestModel>(json)); | ||
|
|
||
| if (exception == null) | ||
| { | ||
| var result = JsonConvert.DeserializeObject<TestModel>(json); | ||
| Assert.Null(result.EnumField); | ||
| } | ||
| else | ||
| { | ||
| // If this throws, we need a custom converter | ||
| Assert.IsType<JsonSerializationException>(exception); | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test's logic is a bit confusing as it handles two possible outcomes (throwing an exception or returning null). A test should ideally be deterministic and verify a single, specific behavior.
Since the purpose of this test is to demonstrate why SafeStringEnumConverter is needed, it should explicitly assert that deserializing an unknown enum with the standard StringEnumConverter throws a JsonSerializationException.
This would make the test's purpose clearer and avoid ambiguity. Also, the repeated deserialization call inside the if (exception == null) block is inefficient.
| [Fact] | ||
| public void TestWebhookHandlerWithUnknownEnumValues() | ||
| { | ||
| // Test the actual webhook handler path | ||
| var json = @"{ | ||
| ""live"": ""false"", | ||
| ""notificationItems"": [] | ||
| }"; | ||
|
|
||
| var webhookHandler = new Adyen.Webhooks.WebhookHandler(); | ||
| var notificationRequest = webhookHandler.HandleNotificationRequest(json); | ||
|
|
||
| Assert.NotNull(notificationRequest); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test is named TestWebhookHandlerWithUnknownEnumValues, but it doesn't actually test the deserialization of an unknown enum value within a webhook notification. The provided JSON for notificationItems is an empty array, so no enum properties are being deserialized.
To make this test effective and match its name, you should provide a JSON payload that includes a notification item with an enum property set to an unknown value, and then assert that this property is deserialized as null. For example, you could add a notification item with a success field set to an unknown value, and then assert that the corresponding property on the deserialized object is null.
a9902da to
de61c73
Compare
- Add SafeStringEnumConverter to return null for unknown enum values - Register converter globally in JsonOperation.Deserialize() - Update template to make all enums nullable (not just non-required) - Add comprehensive test suite for enum deserialization - Ensures forward compatibility when API adds new enum values
de61c73 to
ae13cfd
Compare
Description
Tested scenarios
Fixed issue: