Skip to content
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

fix: ignore __type when deserializing union for AWS Json protocols #964

Merged
merged 6 commits into from
Sep 28, 2023

Conversation

0marperez
Copy link
Contributor

Issue #

closes aws-sdk-kotlin#1044

Description of changes

-Added IgnoreKey trait and adapted JsonDeserializer to it

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

@0marperez 0marperez added the no-changelog Indicates that a changelog entry isn't required for a pull request. Use sparingly. label Sep 27, 2023
@0marperez 0marperez marked this pull request as ready for review September 27, 2023 14:11
@0marperez 0marperez requested a review from a team as a code owner September 27, 2023 14:11
* in a tag called "boo", pass an instance of XmlList to the FieldDescriptor of `XmlList(elementName="boo")`.
*/
@InternalApi
public interface FieldTrait
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are these moved?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the file was named SdkFieldDescriptor I thought it would make more sense to have field traits in a different place, I can change it to how it was though

*
*/
@InternalApi
public data class IgnoreKey(public val key: String, public val regardlessOfInModel: Boolean = true) : FieldTrait
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be in serde-json as it only applies there right? (see JsonFieldTraits.kt)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: Why do we need regardlessOfInModel, I would assume if we set IgnoreKey as a trait on the object descriptor it's going to be ignored regardless of whether it's modeled. This seems unnecessary to me but I'd like to understand what the motivation was.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking of a case where we have a union with a member called __type. If that happens we would not deserialize that member, because we're ignoring that key regardless of what the model says. The default is true anyway so it behaves as expected unless set explicitly to false.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't put it in a JSON specific file because I think this might be useful in the future for other deserializers like the XML one. I don't know if we have a use case already but that was my reasoning behind it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think? Should regardlessOfInModel stay? Is this something that could be useful elsewhere?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking of a case where we have a union with a member called __type. If that happens we would not deserialize that member, because we're ignoring that key regardless of what the model says

Shouldn't this just be a choice by codegen though? If we want it deserialized because it's in the model don't add the IgnoreKey trait.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't put it in a JSON specific file because I think this might be useful in the future for other deserializers like the XML one. I don't know if we have a use case already but that was my reasoning behind it.

YAGNI probably. Also IgnoreKey was a suggestion for the name based on JSON objects are "keyed". The same isn't really true of XML.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense but the issue this is trying to solve: smithy-lang/smithy#1945
is that sometimes a unnecessary __type is serialized. What you're suggesting is to always ignore __type. But there could be a cases where __type could be in the model and we would be ignoring it because sometimes there's an extra one there unnecessarily

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What you're suggesting is to always ignore __type. But there could be a cases where __type could be in the model and we would be ignoring it because sometimes there's an extra one there unnecessarily

I don't follow, if __type is in the model and we want to deserialize it then just don't emit the IgnoreKey trait during codegen. What am I missing here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess that logic could live in codegen, now it's in the deserializer

Comment on lines 15 to 16
class JsonDeserializerIgnoresKeysTest {
class IgnoresKeysTest {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this double-nested class seems unnecessary, I think you can just declare these at the top-level without using a companion object for simplicity

Comment on lines 125 to 127
val X_DESCRIPTOR = SdkFieldDescriptor(SerialKind.Integer, JsonSerialName("x"))
val Y_DESCRIPTOR = SdkFieldDescriptor(SerialKind.Integer, JsonSerialName("y"))
val Z_DESCRIPTOR = SdkFieldDescriptor(SerialKind.Integer, JsonSerialName("z"))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

duplicated, can re-use the others descriptors above

Copy link
Contributor

@ianbotsf ianbotsf left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good overall!

@@ -198,7 +198,13 @@ private class JsonFieldIterator(
val token = reader.nextTokenOf<JsonToken.Name>()
val propertyName = token.value
val field = descriptor.fields.find { it.serialName == propertyName }
field?.index ?: Deserializer.FieldIterator.UNKNOWN_FIELD

if (descriptor.traits.contains(IgnoreKey(propertyName))) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Style: Consider if (IgnoreKey(propertyName) in descriptor.traits)

private val Y_DESCRIPTOR = SdkFieldDescriptor(SerialKind.Integer, JsonSerialName("y"))
private val Z_DESCRIPTOR = SdkFieldDescriptor(SerialKind.Integer, JsonSerialName("z"))
private val OBJ_DESCRIPTOR = SdkObjectDescriptor.build {
trait(IgnoreKey("z")) // <----
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: What does the comment // <---- mean? Same question on L120-121.

shape: Shape,
members: List<MemberShape>,
writer: KotlinWriter,
): JsonSerdeDescriptorGenerator = JsonSerdeDescriptorGenerator(ctx.toRenderingContext(protocolGenerator, shape, writer), members, supportsJsonNameTrait)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Style: Line length > 120 chars

Copy link
Contributor

@aajtodd aajtodd left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, one suggestion but otherwise approved.

import kotlin.test.Test
import kotlin.test.assertEquals

class JsonDeserializerIgnoresKeysTest {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Every test has explicitly modeled field descriptors. You're missing coverage for unmodeled fields that we want to skip rather than enumerate as unknown.

@sonarqubecloud
Copy link

SonarCloud Quality Gate failed.    Quality Gate failed

Bug A 0 Bugs
Vulnerability A 0 Vulnerabilities
Security Hotspot A 0 Security Hotspots
Code Smell A 0 Code Smells

No Coverage information No Coverage information
39.9% 39.9% Duplication

idea Catch issues before they fail your Quality Gate with our IDE extension sonarlint SonarLint

@0marperez 0marperez merged commit a9aa47c into main Sep 28, 2023
8 of 9 checks passed
@0marperez 0marperez deleted the ignore-type-unions-aws-json-protocols branch September 28, 2023 15:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
no-changelog Indicates that a changelog entry isn't required for a pull request. Use sparingly.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Ignore __type field when deserializing unions in JSON protocols
4 participants