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: route53 custom error unmarshalling #961

Merged
merged 25 commits into from
Jul 7, 2023

Conversation

0marperez
Copy link
Contributor

Issue #

Github issue #242

Description of changes

Implements route 53 custom error unmarshalling for InvaildChangeBatchResponse. Tests included.

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 customization Service Customization label Jun 23, 2023
@0marperez 0marperez requested a review from a team as a code owner June 23, 2023 19:09
@0marperez 0marperez changed the title feat: route53 custom error unmarshalling fix: route53 custom error unmarshalling Jun 23, 2023
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 pretty good. A few minor points left...fix and ship!

return ErrorDetails(details.code, details.message, details.requestId)
}

internal object InvalidChangeBatchDeserializer {
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: Couldn't this and basically everything else in this file be private (except for parseCustomXmlErrorResponse)?

Comment on lines 87 to 108
// XML Error response parser from RestXMLErrorDeserializer
internal interface RestXmlErrorDetails {
val requestId: String?
val code: String?
val message: String?
}

// Models "ErrorResponse" type in https://awslabs.github.io/smithy/1.0/spec/aws/aws-restxml-protocol.html#operation-error-serialization
internal data class XmlErrorResponse(
val error: XmlError?,
override val requestId: String? = error?.requestId,
) : RestXmlErrorDetails {
override val code: String? = error?.code
override val message: String? = error?.message
}

// Models "Error" type in https://awslabs.github.io/smithy/1.0/spec/aws/aws-restxml-protocol.html#operation-error-serialization
internal data class XmlError(
override val requestId: String?,
override val code: String?,
override val message: String?,
) : RestXmlErrorDetails
Copy link
Contributor

Choose a reason for hiding this comment

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

Question: Do we need these classes? Couldn't the deserializers return ErrorDetails? directly?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

One of the deserializers can, the other can't. I can have the one that can return ErrorDetails? do that but I would have to leave the other data class in there for the one that can't.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Doing that resulted in some weird errors with other parts of the code, I think I'll just leave it as is

Comment on lines 21 to 22
val lines = originalValue?.split("\n")
writer.write("#L\naws.sdk.kotlin.services.route53.internal.parseCustomXmlErrorResponse(payload) ?: #L", lines?.get(0) ?: "", lines?.get(1) ?: "")
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: Solely referring to the original value with this lines list is pretty inscrutable. I suggest giving each line a clearer name so that it's easier to understand what's going on here. We can also clean up the null checking by just requiring a non-null value up front:

requireNotNull(originalValue) { "Unexpectedly empty original value" }
val (nullCheckLine, parseLine) = originalValue.split("\n", limit = 2)
writer.write("#L", nullCheckLine)
writer.write("aws.sdk.kotlin.services.route53.internal.parseCustomXmlErrorResponse(payload) ?: #L", parseLine)

Copy link
Member

@lauzadis lauzadis left a comment

Choose a reason for hiding this comment

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

Great! No comments outside of what Ian already had.

@0marperez 0marperez requested a review from aajtodd June 30, 2023 17:43
}

// XML Error response parser from RestXMLErrorDeserializer
private interface RestXmlErrorDetails {
Copy link
Collaborator

Choose a reason for hiding this comment

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

question: what purpose does this serve?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It was there from the copy and pasting I did and was necessary for that code to work. I modified things and it's no longer there anymore

}
}

private object InvalidChangeBatchMessageDeserializer {
Copy link
Collaborator

Choose a reason for hiding this comment

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

question: This is a nested type right? Can we not just re-use the generated deserializer for this then?

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 modified things and it's not exactly the same so no

Copy link
Collaborator

Choose a reason for hiding this comment

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

Why is the generated deserializer not usable as is?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Because of this line I believe. It should be Messages instead of Error. But I could be wrong

Copy link
Collaborator

Choose a reason for hiding this comment

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

I see now it's not a nested type it's just a list of string. Ok I have further questions now, new comments incoming.

loop@ while (true) {
when (findNextFieldIndex()) {
MESSAGE_DESCRIPTOR.index ->
if (messages == null) messages = deserializeString() else messages += "\n" + deserializeString()
Copy link
Collaborator

Choose a reason for hiding this comment

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

correctness: This is modeled as a list field why are we joining it to a string with \n?

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 see how it looks like the Messages field here because of the variable but it's Message which is a String. I am assuming a response that looks like this here:

<?xml version="1.0" encoding="UTF-8"?>
<InvalidChangeBatch xmlns="https://route53.amazonaws.com/doc/2013-04-01/">
  <Messages>
    <Message>Tried to create resource record set duplicate.example.com. type A, but it already exists</Message>
    <Message>Tried to create resource record set duplicate.example.com. type B, but it already exists</Message>
  </Messages>
  <RequestId>b25f48e8-84fd-11e6-80d9-574e0c4664cb</RequestId>
</InvalidChangeBatch>

Is this even right/possible?

private fun buildInvalidChangeBatchException(messages: String?): InvalidChangeBatch {
messages ?: throw DeserializationException("Missing message in InvalidChangeBatch XML response")
val builder = InvalidChangeBatch.Builder()
builder.message = messages
Copy link
Collaborator

Choose a reason for hiding this comment

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

correctness: Why are we setting message to the result of parsing the list field? Also what if it populates both Message and Messages?

e.g.

<?xml version="1.0" encoding="UTF-8"?>
<InvalidChangeBatch xmlns="https://route53.amazonaws.com/doc/2013-04-01/">
  <Messages>
    <Message>Message from list</Message>
  </Messages>
  <Message>Some other message</Message>
  <RequestId>b25f48e8-84fd-11e6-80d9-574e0c4664cb</RequestId>
</InvalidChangeBatch>

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Why are we setting message to the result of parsing the list field? :
I'm setting the Message value here which is a String not a String list. That variable name is pretty confusing and makes it seem like the Messages field. I'll change it

What if it populates both Message and Messages? :
So I don't populate Messages right now I only extract the Message. I also asked Monday if I had to worry about seeing a Message not inside a Messages tag and got told that it shouldn't happen. So that's something we have to look out for as well?

@sonarcloud
Copy link

sonarcloud bot commented Jul 7, 2023

SonarCloud Quality Gate failed.    Quality Gate failed

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

No Coverage information No Coverage information
17.5% 17.5% Duplication

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

@@ -108,6 +111,7 @@ abstract class AwsHttpBindingProtocolGenerator : HttpBindingProtocolGenerator()
writer.write("val payload = response.body.#T()", RuntimeTypes.Http.readAll)
.write("val wrappedResponse = response.#T(payload)", RuntimeTypes.AwsProtocolCore.withPayload)
.write("")
.declareSection(ProtocolErrorDeserialization) { }
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit: you don't need the empty { } it's already the default

@0marperez 0marperez merged commit 74fb92d into main Jul 7, 2023
9 of 10 checks passed
@0marperez 0marperez deleted the route53-custom-error-unmarshalling branch July 7, 2023 15:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
customization Service Customization
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants