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: Make operations correctly model service errors #1114

Merged
merged 10 commits into from
Sep 13, 2023
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ object AWSClientRuntimeTypes {

object EC2Query {
val Ec2NarrowedResponse = runtimeSymbol("Ec2NarrowedResponse")
val Ec2QueryError = runtimeSymbol("Ec2QueryError")
}
object AWSJSON {
val XAmzTargetMiddleware = runtimeSymbol("XAmzTargetMiddleware")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,50 @@ import software.amazon.smithy.swift.codegen.model.toUpperCamelCase
import software.amazon.smithy.swift.codegen.utils.errorShapeName

class AWSJsonHttpResponseBindingErrorGenerator : HttpResponseBindingErrorGeneratable {
override fun render(ctx: ProtocolGenerator.GenerationContext, op: OperationShape, unknownServiceErrorSymbol: Symbol) {
override fun renderServiceError(ctx: ProtocolGenerator.GenerationContext) {
val serviceShape = ctx.service
val serviceName = ctx.service.id.name
val rootNamespace = ctx.settings.moduleName
val fileName = "./$rootNamespace/models/$serviceName+ServiceErrorHelperMethod.swift"

ctx.delegator.useFileWriter(fileName) { writer ->
with(writer) {
addImport(AWSSwiftDependency.AWS_CLIENT_RUNTIME.target)
addImport(SwiftDependency.CLIENT_RUNTIME.target)

openBlock("extension ${ctx.symbolProvider.toSymbol(ctx.service).name}Types {", "}") {
openBlock(
"static func makeServiceError(_ httpResponse: \$N, _ decoder: \$D, _ error: \$N, _ id: String?) async throws -> \$N? {",
"}",
ClientRuntimeTypes.Http.HttpResponse,
ClientRuntimeTypes.Serde.ResponseDecoder,
AWSClientRuntimeTypes.RestJSON.RestJSONError,
SwiftTypes.Error
) {
openBlock("switch error.errorType {", "}") {
val serviceErrorShapes =
serviceShape.errors
.map { ctx.model.expectShape(it) as StructureShape }
.toSet()
.sorted()
serviceErrorShapes.forEach { errorShape ->
val errorShapeName = errorShape.errorShapeName(ctx.symbolProvider)
val errorShapeType = ctx.symbolProvider.toSymbol(errorShape).name
write(
"case \$S: return try await \$L(httpResponse: httpResponse, decoder: decoder, message: error.errorMessage, requestID: id)",
errorShapeName,
errorShapeType
)
}
write("default: return nil")
}
}
}
}
}
}

override fun renderOperationError(ctx: ProtocolGenerator.GenerationContext, op: OperationShape, unknownServiceErrorSymbol: Symbol) {
val operationErrorName = "${op.toUpperCamelCase()}OutputError"
val rootNamespace = ctx.settings.moduleName
val httpBindingSymbol = Symbol.builder()
Expand All @@ -28,33 +71,52 @@ class AWSJsonHttpResponseBindingErrorGenerator : HttpResponseBindingErrorGenerat
.build()

ctx.delegator.useShapeWriter(httpBindingSymbol) { writer ->
writer.addImport(AWSSwiftDependency.AWS_CLIENT_RUNTIME.target)
writer.addImport(SwiftDependency.CLIENT_RUNTIME.target)

writer.openBlock("public enum \$L: \$N {", "}", operationErrorName, ClientRuntimeTypes.Http.HttpResponseErrorBinding) {
writer.openBlock(
"public static func makeError(httpResponse: \$N, decoder: \$D) async throws -> \$N {", "}",
ClientRuntimeTypes.Http.HttpResponse,
ClientRuntimeTypes.Serde.ResponseDecoder,
SwiftTypes.Error
with(writer) {
addImport(AWSSwiftDependency.AWS_CLIENT_RUNTIME.target)
addImport(SwiftDependency.CLIENT_RUNTIME.target)

openBlock(
"public enum \$L: \$N {",
"}",
operationErrorName,
ClientRuntimeTypes.Http.HttpResponseErrorBinding
) {
writer.write(
"let restJSONError = try await \$N(httpResponse: httpResponse)",
AWSClientRuntimeTypes.RestJSON.RestJSONError
)
writer.write("let requestID = httpResponse.requestId")
writer.openBlock("switch restJSONError.errorType {", "}") {
val errorShapes = op.errors.map { ctx.model.expectShape(it) as StructureShape }.toSet().sorted()
for (errorShape in errorShapes) {
var errorShapeName = errorShape.errorShapeName(ctx.symbolProvider)
var errorShapeType = ctx.symbolProvider.toSymbol(errorShape).name
writer.write(
"case \$S: return try await \$L(httpResponse: httpResponse, decoder: decoder, message: restJSONError.errorMessage, requestID: requestID)",
errorShapeName,
errorShapeType
openBlock(
"public static func makeError(httpResponse: \$N, decoder: \$D) async throws -> \$N {", "}",
ClientRuntimeTypes.Http.HttpResponse,
ClientRuntimeTypes.Serde.ResponseDecoder,
SwiftTypes.Error
) {
write(
"let restJSONError = try await \$N(httpResponse: httpResponse)",
AWSClientRuntimeTypes.RestJSON.RestJSONError
)
write("let requestID = httpResponse.requestId")

if (ctx.service.errors.isNotEmpty()) {
write("let serviceError = try await ${ctx.symbolProvider.toSymbol(ctx.service).name}Types.makeServiceError(httpResponse, decoder, restJSONError, requestID)")
write("if let error = serviceError { return error }")
}

openBlock("switch restJSONError.errorType {", "}") {
val errorShapes = op.errors
.map { ctx.model.expectShape(it) as StructureShape }
.toSet()
.sorted()
errorShapes.forEach { errorShape ->
var errorShapeName = errorShape.errorShapeName(ctx.symbolProvider)
var errorShapeType = ctx.symbolProvider.toSymbol(errorShape).name
write(
"case \$S: return try await \$L(httpResponse: httpResponse, decoder: decoder, message: restJSONError.errorMessage, requestID: requestID)",
errorShapeName,
errorShapeType
)
}
write(
"default: return try await \$N.makeError(httpResponse: httpResponse, message: restJSONError.errorMessage, requestID: requestID, typeName: restJSONError.errorType)",
unknownServiceErrorSymbol
)
}
writer.write("default: return try await \$N.makeError(httpResponse: httpResponse, message: restJSONError.errorMessage, requestID: requestID, typeName: restJSONError.errorType)", unknownServiceErrorSymbol)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

package software.amazon.smithy.aws.swift.codegen.ec2query.httpResponse

import software.amazon.smithy.aws.swift.codegen.AWSClientRuntimeTypes
import software.amazon.smithy.aws.swift.codegen.AWSSwiftDependency
import software.amazon.smithy.codegen.core.Symbol
import software.amazon.smithy.model.shapes.OperationShape
Expand All @@ -18,7 +19,49 @@ import software.amazon.smithy.swift.codegen.model.toUpperCamelCase
import software.amazon.smithy.swift.codegen.utils.errorShapeName

class AWSEc2QueryHttpResponseBindingErrorGenerator : HttpResponseBindingErrorGeneratable {
override fun render(ctx: ProtocolGenerator.GenerationContext, op: OperationShape, unknownServiceErrorSymbol: Symbol) {
override fun renderServiceError(ctx: ProtocolGenerator.GenerationContext) {
val serviceShape = ctx.service
val serviceName = ctx.service.id.name
val rootNamespace = ctx.settings.moduleName
val fileName = "./$rootNamespace/models/$serviceName+ServiceErrorHelperMethod.swift"

ctx.delegator.useFileWriter(fileName) { writer ->
with(writer) {
addImport(AWSSwiftDependency.AWS_CLIENT_RUNTIME.target)
addImport(SwiftDependency.CLIENT_RUNTIME.target)
openBlock("extension ${ctx.symbolProvider.toSymbol(ctx.service).name}Types {", "}") {
openBlock(
"static func makeServiceError(_ httpResponse: \$N, _ decoder: \$D, _ error: \$N) async throws -> \$N? {",
"}",
ClientRuntimeTypes.Http.HttpResponse,
ClientRuntimeTypes.Serde.ResponseDecoder,
AWSClientRuntimeTypes.EC2Query.Ec2QueryError,
SwiftTypes.Error
) {
openBlock("switch error.errorCode {", "}") {
val serviceErrorShapes =
serviceShape.errors
.map { ctx.model.expectShape(it) as StructureShape }
.toSet()
.sorted()
serviceErrorShapes.forEach { errorShape ->
val errorShapeName = errorShape.errorShapeName(ctx.symbolProvider)
val errorShapeType = ctx.symbolProvider.toSymbol(errorShape).name
write(
"case \$S: return try await \$L(httpResponse: httpResponse, decoder: decoder, message: error.message, requestID: error.requestId)",
errorShapeName,
errorShapeType
)
}
write("default: return nil")
}
}
}
}
}
}

override fun renderOperationError(ctx: ProtocolGenerator.GenerationContext, op: OperationShape, unknownServiceErrorSymbol: Symbol) {
val operationErrorName = "${op.toUpperCamelCase()}OutputError"
val rootNamespace = ctx.settings.moduleName
val httpBindingSymbol = Symbol.builder()
Expand All @@ -27,25 +70,48 @@ class AWSEc2QueryHttpResponseBindingErrorGenerator : HttpResponseBindingErrorGen
.build()

ctx.delegator.useShapeWriter(httpBindingSymbol) { writer ->
writer.addImport(AWSSwiftDependency.AWS_CLIENT_RUNTIME.target)
writer.addImport(SwiftDependency.CLIENT_RUNTIME.target)

writer.openBlock("public enum \$L: \$N {", "}", operationErrorName, ClientRuntimeTypes.Http.HttpResponseErrorBinding) {
writer.openBlock(
"public static func makeError(httpResponse: \$N, decoder: \$D) async throws -> \$N {", "}",
ClientRuntimeTypes.Http.HttpResponse,
ClientRuntimeTypes.Serde.ResponseDecoder,
SwiftTypes.Error
with(writer) {
addImport(AWSSwiftDependency.AWS_CLIENT_RUNTIME.target)
addImport(SwiftDependency.CLIENT_RUNTIME.target)

openBlock(
"public enum \$L: \$N {",
"}",
operationErrorName,
ClientRuntimeTypes.Http.HttpResponseErrorBinding
) {
writer.write("let ec2QueryError = try await Ec2QueryError(httpResponse: httpResponse)")
writer.openBlock("switch ec2QueryError.errorCode {", "}") {
val errorShapes = op.errors.map { ctx.model.expectShape(it) as StructureShape }.toSet().sorted()
for (errorShape in errorShapes) {
var errorShapeName = errorShape.errorShapeName(ctx.symbolProvider)
var errorShapeType = ctx.symbolProvider.toSymbol(errorShape).name
writer.write("case \$S: return try await \$L(httpResponse: httpResponse, decoder: decoder, message: ec2QueryError.message, requestID: ec2QueryError.requestId)", errorShapeName, errorShapeType)
openBlock(
"public static func makeError(httpResponse: \$N, decoder: \$D) async throws -> \$N {", "}",
ClientRuntimeTypes.Http.HttpResponse,
ClientRuntimeTypes.Serde.ResponseDecoder,
SwiftTypes.Error
) {
write("let ec2QueryError = try await Ec2QueryError(httpResponse: httpResponse)")

if (ctx.service.errors.isNotEmpty()) {
write("let serviceError = try await ${ctx.symbolProvider.toSymbol(ctx.service).name}Types.makeServiceError(httpResponse, decoder, ec2QueryError)")
write("if let error = serviceError { return error }")
}

openBlock("switch ec2QueryError.errorCode {", "}") {
val errorShapes = op.errors
.map { ctx.model.expectShape(it) as StructureShape }
.toSet()
.sorted()
errorShapes.forEach { errorShape ->
var errorShapeName = errorShape.errorShapeName(ctx.symbolProvider)
var errorShapeType = ctx.symbolProvider.toSymbol(errorShape).name
write(
"case \$S: return try await \$L(httpResponse: httpResponse, decoder: decoder, message: ec2QueryError.message, requestID: ec2QueryError.requestId)",
errorShapeName,
errorShapeType
)
}
write(
"default: return try await \$N.makeError(httpResponse: httpResponse, message: ec2QueryError.message, requestID: ec2QueryError.requestId, typeName: ec2QueryError.errorCode)",
unknownServiceErrorSymbol
)
}
writer.write("default: return try await \$N.makeError(httpResponse: httpResponse, message: ec2QueryError.message, requestID: ec2QueryError.requestId, typeName: ec2QueryError.errorCode)", unknownServiceErrorSymbol)
}
}
}
Expand Down
Loading
Loading