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

feat: Add WaiterTypedError type, conform operation errors to it #491

Merged
merged 8 commits into from
Dec 8, 2022

Conversation

jbelkins
Copy link
Contributor

@jbelkins jbelkins commented Dec 8, 2022

Description of changes

This PR merges to the waiters feature branch.

Adds a WaiterTypedError Swift protocol so that an error can provide its error type. This will be used in the implementation of the errorType acceptor for Smithy waiters, which will match the error based on an expected error type string that will match the errorCode property on a WaiterTypedError.

  • Conforms SdkError to WaiterTypedError
  • Conforms operation errors to WaiterTypedError
  • Conforms unknown HTTP errors to WaiterTypedError
  • Adds a test for correct codegen of WaiterTypedError compliance of an OperationError

As written, this PR generates WaiterTypedError compliance for every operation error, not just the ones with errorType waiters. When this work is integrated with Smithy acceptor work, WaiterTypedError compliance will be generated only for operations that have a waiter with an WaiterTypedError acceptor.

Scope

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)

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

@@ -36,6 +36,7 @@ class HttpResponseGenerator(
httpOperations.forEach {
httpResponseBindingErrorGenerator.render(ctx, it)
HttpResponseBindingErrorNarrowGenerator(ctx, it, unknownServiceErrorSymbol).render()
CodedErrorGenerator(ctx, it, unknownServiceErrorSymbol).render()
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Rendering coded error compliance here does it for every operation. When this work is integrated with Smithy acceptors, coded error compliance will only be generated for operations that have a waiter with an errorType acceptor.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

(Note that this branch merges to feat/waiters, not main)

Copy link
Contributor

Choose a reason for hiding this comment

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

Does that mean that the implementation of CodedErrorGenerator here will be removed? Assuming it will be implemented somewhere else where it is restricted to only operations that have a waiter with an errorType acceptor?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, I'll remove it here, and I will conditionally perform this render when a waiter requires it.

I placed this render here so that I could render coded errors into the SDK for testing.

@jbelkins jbelkins marked this pull request as ready for review December 8, 2022 01:31
@jbelkins jbelkins requested a review from epau December 8, 2022 01:31

import Foundation

public protocol CodedError: Error {
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we add a doc comment for this type?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added

Comment on lines 10 to 14
/// The `String` error code that identifies the general type of this HTTP service error, or `nil` if the
/// error has no known code.
///
/// The source of this code is the HTTP response to an API call, but what exact header, body field, etc.
/// it comes from is specific to the response encoding used.
Copy link
Contributor

Choose a reason for hiding this comment

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

We should refrain from documenting protocol methods with details from specific conforming types.

Suggested change
/// The `String` error code that identifies the general type of this HTTP service error, or `nil` if the
/// error has no known code.
///
/// The source of this code is the HTTP response to an API call, but what exact header, body field, etc.
/// it comes from is specific to the response encoding used.
/// The error code.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Changed

@@ -5,6 +5,8 @@

/// General Service Error structure used when exact error could not be deduced from the `HttpResponse`
public struct UnknownHttpServiceError: HttpServiceError, Swift.Equatable {
public var _errorCode: String?
Copy link
Contributor

Choose a reason for hiding this comment

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

Unrelated to this PR:
I wonder why we prefix all the properties here with an underscore? 🤔
Especially since there properties are public

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 don't know the purpose either; in the past I have used underscore on private properties to help keep them separate from a related public property.

When we overhaul our error system...


extension UnknownHttpServiceError: CodedError {

public var errorCode: String? { _errorCode }
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we add docs as to what an error code is (or should be) for an UnknownHttpServiceError?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added

@@ -19,9 +21,15 @@ public struct UnknownHttpServiceError: HttpServiceError, Swift.Equatable {
}

extension UnknownHttpServiceError {
public init(httpResponse: HttpResponse, message: String? = nil) {
public init(httpResponse: HttpResponse, message: String? = nil, errorCode: String? = nil) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Mind adding docs for this initializer while you're 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.

Added


extension SdkError: CodedError {

public var errorCode: String? {
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we add docs as to what an error code is (or should be) for a SdkError?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added

writer.declareSection(CodedErrorGeneratorSectionId, context) {
writer.openBlock("extension \$L: CodedError {", "}", operationErrorName) {
writer.write("")
writer.openBlock("public var errorCode: String? {", "}") {
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we add docs for errorCode 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.

Added

var errorShapeName = resolveErrorShapeName(errorShape)
var errorShapeType = ctx.symbolProvider.toSymbol(errorShape).name
var errorShapeEnumCase = errorShapeType.decapitalize()
writer.write("case .\$L: return \$S", errorShapeEnumCase, errorShapeName)
Copy link
Contributor

Choose a reason for hiding this comment

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

Why do we return the error type as a string here? Should we return nil instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Returning the error type as a string here prevents me from having to extend every error on the operation with its own CodedError compliance... the GetWidgetOutputError knows the right error code for every possible error it can return.

Compare this to the output error initializer which switches over the expected values of errorCode to determine what exact error to create for the operation & this method makes more sense. Here's an (abridged) initializer for an operation from an AWS service:

extension ModifyDBInstanceOutputError {
    public init(errorType: Swift.String?, httpResponse: ClientRuntime.HttpResponse, decoder: ClientRuntime.ResponseDecoder? = nil, message: Swift.String? = nil, requestID: Swift.String? = nil) throws {
        switch errorType {
        case "AuthorizationNotFound" : self = .authorizationNotFoundFault(try AuthorizationNotFoundFault(httpResponse: httpResponse, decoder: decoder, message: message, requestID: requestID))
        case "CertificateNotFound" : self = .certificateNotFoundFault(try CertificateNotFoundFault(httpResponse: httpResponse, decoder: decoder, message: message, requestID: requestID))
        default : self = .unknown(UnknownAWSHttpServiceError(httpResponse: httpResponse, message: message, requestID: requestID))
        }
    }
}

AuthorizationNotFoundFault and CertificateNotFoundFault don't need to be CodedErrors because ModifyDBInstanceOutputError returns the right error code for them.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The extension I created is basically this switch statement in reverse... convert the enum case to a string instead of convert the string to an enum case.

Copy link
Contributor

@epau epau left a comment

Choose a reason for hiding this comment

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

Looking really good! Just a few comments and questions

@jbelkins jbelkins requested a review from epau December 8, 2022 15:57
@jbelkins jbelkins changed the title feat: Add CodedError type, conform operation errors to it feat: Add WaiterTypedError type, conform operation errors to it Dec 8, 2022
Copy link
Contributor

@epau epau left a comment

Choose a reason for hiding this comment

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

Changes look great!

@jbelkins jbelkins merged commit e2b95fa into feat/waiters Dec 8, 2022
@jbelkins jbelkins deleted the jbe/coded_error branch December 8, 2022 23:06
jbelkins added a commit that referenced this pull request Dec 19, 2022
* feat: Add waiter client runtime components (#450)
* feat: Waiter extension & methods (#478)
* feat: Add WaiterTypedError type, conform operation errors to it (#491)
* feat: Code-generate Acceptors for waiters (#483)
* fix: Use correct var name in AND expression (#493)
* fix: Correct Swift compile errors for comparison expressions (#494)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants