diff --git a/aws/event-streams/src/main/java/software/amazon/smithy/java/events/aws/AwsEventShapeEncoder.java b/aws/event-streams/src/main/java/software/amazon/smithy/java/events/aws/AwsEventShapeEncoder.java index e5373dac4..3fc2cfcf3 100644 --- a/aws/event-streams/src/main/java/software/amazon/smithy/java/events/aws/AwsEventShapeEncoder.java +++ b/aws/event-streams/src/main/java/software/amazon/smithy/java/events/aws/AwsEventShapeEncoder.java @@ -14,7 +14,7 @@ import java.util.stream.Collectors; import software.amazon.eventstream.HeaderValue; import software.amazon.eventstream.Message; -import software.amazon.smithy.java.core.schema.ModeledApiException; +import software.amazon.smithy.java.core.error.ModeledException; import software.amazon.smithy.java.core.schema.Schema; import software.amazon.smithy.java.core.schema.SerializableStruct; import software.amazon.smithy.java.core.schema.TraitKey; @@ -79,7 +79,7 @@ public void writeStruct(Schema schema, SerializableStruct struct) { public AwsEventFrame encodeFailure(Throwable exception) { AwsEventFrame frame; Schema exceptionSchema; - if (exception instanceof ModeledApiException me && (exceptionSchema = possibleExceptions.get( + if (exception instanceof ModeledException me && (exceptionSchema = possibleExceptions.get( me.schema().id())) != null) { var headers = new HashMap(); headers.put(":message-type", HeaderValue.fromString("exception")); diff --git a/client-core/src/main/java/software/amazon/smithy/java/client/core/ClientPipeline.java b/client-core/src/main/java/software/amazon/smithy/java/client/core/ClientPipeline.java index a76c76f8f..39108d951 100644 --- a/client-core/src/main/java/software/amazon/smithy/java/client/core/ClientPipeline.java +++ b/client-core/src/main/java/software/amazon/smithy/java/client/core/ClientPipeline.java @@ -33,7 +33,7 @@ import software.amazon.smithy.java.client.core.interceptors.RequestHook; import software.amazon.smithy.java.client.core.interceptors.ResponseHook; import software.amazon.smithy.java.context.Context; -import software.amazon.smithy.java.core.schema.ApiException; +import software.amazon.smithy.java.core.error.CallException; import software.amazon.smithy.java.core.schema.ApiOperation; import software.amazon.smithy.java.core.schema.SerializableStruct; import software.amazon.smithy.java.logging.InternalLogger; @@ -260,7 +260,7 @@ private ResolvedSch for (var authSchemeOption : authSchemeOptions) { options.add(authSchemeOption.schemeId().toString()); } - throw new ApiException( + throw new CallException( "No auth scheme could be resolved for operation " + call.operation.schema().id() + "; protocol=" + protocol.id() + "; requestClass=" + request.getClass() diff --git a/client-core/src/main/java/software/amazon/smithy/java/client/core/ClientProtocol.java b/client-core/src/main/java/software/amazon/smithy/java/client/core/ClientProtocol.java index 149d68120..56238b987 100644 --- a/client-core/src/main/java/software/amazon/smithy/java/client/core/ClientProtocol.java +++ b/client-core/src/main/java/software/amazon/smithy/java/client/core/ClientProtocol.java @@ -9,7 +9,7 @@ import java.util.concurrent.CompletableFuture; import software.amazon.smithy.java.client.core.endpoint.Endpoint; import software.amazon.smithy.java.context.Context; -import software.amazon.smithy.java.core.schema.ApiException; +import software.amazon.smithy.java.core.error.CallException; import software.amazon.smithy.java.core.schema.ApiOperation; import software.amazon.smithy.java.core.schema.SerializableStruct; import software.amazon.smithy.java.core.serde.TypeRegistry; @@ -79,7 +79,7 @@ RequestT createRequ * @param request Request that was sent for this response. * @param response Response to deserialize. * @return the deserialized output shape. - * @throws ApiException if an error occurs, including deserialized modeled errors and protocol errors. + * @throws CallException if an error occurs, including deserialized modeled errors and protocol errors. */ CompletableFuture deserializeResponse( ApiOperation operation, diff --git a/client-core/src/main/java/software/amazon/smithy/java/client/core/plugins/ApplyModelRetryInfoPlugin.java b/client-core/src/main/java/software/amazon/smithy/java/client/core/plugins/ApplyModelRetryInfoPlugin.java index 55efe5177..71db7d535 100644 --- a/client-core/src/main/java/software/amazon/smithy/java/client/core/plugins/ApplyModelRetryInfoPlugin.java +++ b/client-core/src/main/java/software/amazon/smithy/java/client/core/plugins/ApplyModelRetryInfoPlugin.java @@ -9,8 +9,8 @@ import software.amazon.smithy.java.client.core.ClientPlugin; import software.amazon.smithy.java.client.core.interceptors.ClientInterceptor; import software.amazon.smithy.java.client.core.interceptors.OutputHook; -import software.amazon.smithy.java.core.schema.ApiException; -import software.amazon.smithy.java.core.schema.ModeledApiException; +import software.amazon.smithy.java.core.error.CallException; +import software.amazon.smithy.java.core.error.ModeledException; import software.amazon.smithy.java.core.schema.Schema; import software.amazon.smithy.java.core.schema.SerializableStruct; import software.amazon.smithy.java.core.schema.TraitKey; @@ -40,14 +40,14 @@ public O modifyBeforeAttemptCompletion( OutputHook hook, RuntimeException error ) { - if (error instanceof ApiException ae && ae.isRetrySafe() == RetrySafety.MAYBE) { - applyRetryInfoFromModel(hook.operation().schema(), ae); + if (error instanceof CallException ce && ce.isRetrySafe() == RetrySafety.MAYBE) { + applyRetryInfoFromModel(hook.operation().schema(), ce); } return hook.forward(error); } } - static void applyRetryInfoFromModel(Schema operationSchema, ApiException e) { + static void applyRetryInfoFromModel(Schema operationSchema, CallException e) { // If the operation is readonly or idempotent, then it's safe to retry (other checks can disqualify later). var isRetryable = operationSchema.hasTrait(TraitKey.READ_ONLY_TRAIT) || operationSchema.hasTrait(TraitKey.IDEMPOTENT_TRAIT); @@ -57,7 +57,7 @@ static void applyRetryInfoFromModel(Schema operationSchema, ApiException e) { } // If the exception is modeled as retryable or a throttle, then use that information. - if (e instanceof ModeledApiException mae) { + if (e instanceof ModeledException mae) { var retryTrait = mae.schema().getTrait(TraitKey.RETRYABLE_TRAIT); if (retryTrait != null) { e.isRetrySafe(RetrySafety.YES); diff --git a/client-core/src/main/java/software/amazon/smithy/java/client/core/plugins/ExceptionMapper.java b/client-core/src/main/java/software/amazon/smithy/java/client/core/plugins/ExceptionMapper.java new file mode 100644 index 000000000..4dae989ea --- /dev/null +++ b/client-core/src/main/java/software/amazon/smithy/java/client/core/plugins/ExceptionMapper.java @@ -0,0 +1,159 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.java.client.core.plugins; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.function.Function; +import software.amazon.smithy.java.client.core.ClientConfig; +import software.amazon.smithy.java.client.core.ClientPlugin; +import software.amazon.smithy.java.client.core.interceptors.ClientInterceptor; +import software.amazon.smithy.java.client.core.interceptors.OutputHook; +import software.amazon.smithy.java.core.error.CallException; +import software.amazon.smithy.java.core.schema.SerializableStruct; + +/** + * Converts exceptions so that they adhere to client-specific requirements. + * + *

This plugin allows for: + *

    + *
  • Converting specific exceptions by class equality to another exception using + * {@link Builder#convert(Class, Function)}.
  • + *
  • Throwing exceptions as-is if they extend from a specific exception using + * {@link Builder#baseApiExceptionMapper(Class, Function)}.
  • + *
  • Changing {@link CallException}s that don't extend from a specific ApiException into the desired + * ApiException subtype using {@link Builder#baseApiExceptionMapper(Class, Function)}.
  • + *
  • Converting all other totally unknown exceptions into another kind of exception type using + * {@link Builder#rootExceptionMapper}
  • + *
+ * + *
{@code
+ * ExceptionMapper mapper = ExceptionMapper.builder()
+ *     .convert(SomeSpecificError.class, e -> new OtherError(e.getMessage(), e))
+ *     .baseApiExceptionMapper(MyError.class, e -> {
+ *         return switch(e.getFault()) {
+ *             case CLIENT -> new MyClientError(e);
+ *             case SERVER -> new MyServerError(e);
+ *             default -> new MyError(e);
+ *         }
+ *     })
+ *     .rootExceptionMapper(e -> new MyClientError(e))
+ *     .build();
+ * }
+ */ +public final class ExceptionMapper implements ClientPlugin { + + private final Map, + Function> mappers = new HashMap<>(); + private final Class baseApiExceptionType; + private final Function baseApiExceptionMapper; + private final Function rootExceptionMapper; + + private ExceptionMapper(Builder builder) { + this.mappers.putAll(builder.mappers); + this.baseApiExceptionType = builder.baseApiExceptionType; + this.baseApiExceptionMapper = builder.baseApiExceptionMapper; + this.rootExceptionMapper = builder.rootExceptionMapper; + } + + @Override + public void configureClient(ClientConfig.Builder config) { + config.addInterceptor(new ExceptionInterceptor()); + } + + private final class ExceptionInterceptor implements ClientInterceptor { + @Override + public O modifyBeforeCompletion( + OutputHook hook, + RuntimeException error + ) { + if (error == null) { + return hook.output(); + } + + // Perform specific error conversions. + var mapper = mappers.get(error.getClass()); + if (mapper != null) { + throw mapper.apply(error); + } + + // Perform base API error conversions for unknown ApiExceptions. + if (baseApiExceptionType != null && error instanceof CallException a) { + throw baseApiExceptionType.isInstance(error) ? error : baseApiExceptionMapper.apply(a); + } + + throw rootExceptionMapper.apply(error); + } + } + + /** + * Builds up an exception mapper. + */ + public static final class Builder { + private final Map, + Function> mappers = new HashMap<>(); + private Class baseApiExceptionType; + private Function baseApiExceptionMapper; + private Function rootExceptionMapper = Function.identity(); + + public ExceptionMapper build() { + return new ExceptionMapper(this); + } + + /** + * Converts a specific error to another kind of error. + * + *

These explicit conversions are check first before applying {@link #baseApiExceptionMapper} or + * {@link #rootExceptionMapper}. + * + * @param exception Exception to convert. + * @param mapper The mapper used to create the converted exception. + * @return the builder. + * @param The exception to convert. + */ + @SuppressWarnings("unchecked") + public Builder convert(Class exception, Function mapper) { + mappers.put(exception, (Function) mapper); + return this; + } + + /** + * Converts instances of {@link CallException} that don't implement the given {@code baseType} into an exception + * that does extend from it using the given {@code mapper} function. + * + *

Setting a baseApiExceptionMapper is optional. This allows clients to create a base exception type for + * all API-level exceptions. + * + * @param baseType The base type ApiExceptions should extend from. + * @param mapper A mapper used to convert the found exception into a subtype of {@code baseType}. + * @return the builder. + * @param the base type. + */ + public Builder baseApiExceptionMapper( + Class baseType, + Function mapper + ) { + this.baseApiExceptionType = baseType; + this.baseApiExceptionMapper = mapper; + return this; + } + + /** + * Overrides the root exception mapper, used to convert completely unknown errors that don't extend from + * {@link CallException}. + * + *

By default, the root exception mapper simply throws the given exception as-is. + * + * @param mapper The root-level exception mapper used to convert the given error or return it as-is. + * @return the builder. + */ + public Builder rootExceptionMapper(Function mapper) { + this.rootExceptionMapper = Objects.requireNonNull(mapper, "rootExceptionMapper cannot be null"); + return this; + } + } +} diff --git a/client-core/src/test/java/software/amazon/smithy/java/client/core/plugins/ApplyModelRetryInfoPluginTest.java b/client-core/src/test/java/software/amazon/smithy/java/client/core/plugins/ApplyModelRetryInfoPluginTest.java index 875fe74f4..4ad3d62a9 100644 --- a/client-core/src/test/java/software/amazon/smithy/java/client/core/plugins/ApplyModelRetryInfoPluginTest.java +++ b/client-core/src/test/java/software/amazon/smithy/java/client/core/plugins/ApplyModelRetryInfoPluginTest.java @@ -9,8 +9,8 @@ import static org.hamcrest.Matchers.is; import org.junit.jupiter.api.Test; -import software.amazon.smithy.java.core.schema.ApiException; -import software.amazon.smithy.java.core.schema.ModeledApiException; +import software.amazon.smithy.java.core.error.CallException; +import software.amazon.smithy.java.core.error.ModeledException; import software.amazon.smithy.java.core.schema.Schema; import software.amazon.smithy.java.core.serde.ShapeSerializer; import software.amazon.smithy.java.retries.api.RetrySafety; @@ -23,7 +23,7 @@ public class ApplyModelRetryInfoPluginTest { @Test public void marksSafeWhenOperationIsReadOnly() { var schema = Schema.createOperation(ShapeId.from("com#Foo"), new ReadonlyTrait()); - var e = new ApiException("err"); + var e = new CallException("err"); ApplyModelRetryInfoPlugin.applyRetryInfoFromModel(schema, e); @@ -33,7 +33,7 @@ public void marksSafeWhenOperationIsReadOnly() { @Test public void marksSafeWhenOperationIsIdempotent() { var schema = Schema.createOperation(ShapeId.from("com#Foo"), new IdempotentTrait()); - var e = new ApiException("err"); + var e = new CallException("err"); ApplyModelRetryInfoPlugin.applyRetryInfoFromModel(schema, e); @@ -50,7 +50,7 @@ public void marksSafeWhenErrorIsRetryable() { .structureBuilder(ShapeId.from("com#Err"), RetryableTrait.builder().throttling(true).build()) .build(); - var e = new ModeledApiException(errorSchema, "err") { + var e = new ModeledException(errorSchema, "err") { @Override public void serializeMembers(ShapeSerializer serializer) { throw new UnsupportedOperationException(); diff --git a/client-http-binding/src/main/java/software/amazon/smithy/java/client/http/binding/HttpBindingClientProtocol.java b/client-http-binding/src/main/java/software/amazon/smithy/java/client/http/binding/HttpBindingClientProtocol.java index 76756ba81..cfdf56c22 100644 --- a/client-http-binding/src/main/java/software/amazon/smithy/java/client/http/binding/HttpBindingClientProtocol.java +++ b/client-http-binding/src/main/java/software/amazon/smithy/java/client/http/binding/HttpBindingClientProtocol.java @@ -10,7 +10,7 @@ import software.amazon.smithy.java.client.http.HttpClientProtocol; import software.amazon.smithy.java.client.http.HttpErrorDeserializer; import software.amazon.smithy.java.context.Context; -import software.amazon.smithy.java.core.schema.ApiException; +import software.amazon.smithy.java.core.error.CallException; import software.amazon.smithy.java.core.schema.ApiOperation; import software.amazon.smithy.java.core.schema.InputEventStreamingApiOperation; import software.amazon.smithy.java.core.schema.OutputEventStreamingApiOperation; @@ -145,7 +145,7 @@ protected boolean isSuccess(ApiOperation operation, Context context, HttpR * @return Returns the deserialized error. */ protected CompletableFuture createError( + O extends SerializableStruct> CompletableFuture createError( ApiOperation operation, Context context, TypeRegistry typeRegistry, diff --git a/client-http-binding/src/main/java/software/amazon/smithy/java/client/http/binding/HttpBindingErrorFactory.java b/client-http-binding/src/main/java/software/amazon/smithy/java/client/http/binding/HttpBindingErrorFactory.java index e0c6f96ec..f2d68fc66 100644 --- a/client-http-binding/src/main/java/software/amazon/smithy/java/client/http/binding/HttpBindingErrorFactory.java +++ b/client-http-binding/src/main/java/software/amazon/smithy/java/client/http/binding/HttpBindingErrorFactory.java @@ -8,7 +8,7 @@ import java.util.concurrent.CompletableFuture; import software.amazon.smithy.java.client.http.HttpErrorDeserializer; import software.amazon.smithy.java.context.Context; -import software.amazon.smithy.java.core.schema.ModeledApiException; +import software.amazon.smithy.java.core.error.ModeledException; import software.amazon.smithy.java.core.schema.ShapeBuilder; import software.amazon.smithy.java.core.serde.Codec; import software.amazon.smithy.java.http.api.HttpResponse; @@ -33,11 +33,11 @@ public HttpBindingErrorFactory(HttpBinding httpBinding) { } @Override - public CompletableFuture createError( + public CompletableFuture createError( Context context, Codec codec, HttpResponse response, - ShapeBuilder builder + ShapeBuilder builder ) { return httpBinding.responseDeserializer() .payloadCodec(codec) diff --git a/client-http-binding/src/test/java/software/amazon/smithy/java/client/http/binding/HttpBindingErrorDeserializerTest.java b/client-http-binding/src/test/java/software/amazon/smithy/java/client/http/binding/HttpBindingErrorDeserializerTest.java index 1f1581c12..ba449e738 100644 --- a/client-http-binding/src/test/java/software/amazon/smithy/java/client/http/binding/HttpBindingErrorDeserializerTest.java +++ b/client-http-binding/src/test/java/software/amazon/smithy/java/client/http/binding/HttpBindingErrorDeserializerTest.java @@ -16,8 +16,8 @@ import software.amazon.smithy.java.client.http.AmznErrorHeaderExtractor; import software.amazon.smithy.java.client.http.HttpErrorDeserializer; import software.amazon.smithy.java.context.Context; -import software.amazon.smithy.java.core.schema.ApiException; -import software.amazon.smithy.java.core.schema.ModeledApiException; +import software.amazon.smithy.java.core.error.CallException; +import software.amazon.smithy.java.core.error.ModeledException; import software.amazon.smithy.java.core.schema.Schema; import software.amazon.smithy.java.core.schema.ShapeBuilder; import software.amazon.smithy.java.core.serde.Codec; @@ -63,7 +63,8 @@ public void usesGenericErrorWhenPayloadTypeIsUnknown() throws Exception { .serviceId(SERVICE) .knownErrorFactory(new HttpBindingErrorFactory()) .unknownErrorFactory( - (fault, message, response) -> CompletableFuture.completedFuture(new ApiException("Hi!", fault))) + (fault, message, response) -> CompletableFuture + .completedFuture(new CallException("Hi!", fault))) .build(); var registry = TypeRegistry.builder() .putType(Baz.SCHEMA.id(), Baz.class, Baz.Builder::new) @@ -74,7 +75,7 @@ public void usesGenericErrorWhenPayloadTypeIsUnknown() throws Exception { var response = responseBuilder.build(); var result = deserializer.createError(Context.create(), OPERATION, registry, response).get(); - assertThat(result, instanceOf(ApiException.class)); + assertThat(result, instanceOf(CallException.class)); assertThat(result.getMessage(), equalTo("Hi!")); } @@ -86,7 +87,8 @@ public void usesGenericErrorWhenHeaderTypeIsUnknown() throws Exception { .knownErrorFactory(new HttpBindingErrorFactory()) .headerErrorExtractor(new AmznErrorHeaderExtractor()) .unknownErrorFactory( - (fault, message, response) -> CompletableFuture.completedFuture(new ApiException("Hi!", fault))) + (fault, message, response) -> CompletableFuture + .completedFuture(new CallException("Hi!", fault))) .build(); var registry = TypeRegistry.builder() .putType(Baz.SCHEMA.id(), Baz.class, Baz.Builder::new) @@ -104,11 +106,11 @@ public void usesGenericErrorWhenHeaderTypeIsUnknown() throws Exception { var response = responseBuilder.build(); var result = deserializer.createError(Context.create(), OPERATION, registry, response).get(); - assertThat(result, instanceOf(ApiException.class)); + assertThat(result, instanceOf(CallException.class)); assertThat(result.getMessage(), equalTo("Hi!")); } - static final class Baz extends ModeledApiException { + static final class Baz extends ModeledException { static final Schema SCHEMA = Schema .structureBuilder(ShapeId.from("com.foo#Baz"), new ErrorTrait("client")) diff --git a/client-http/src/main/java/software/amazon/smithy/java/client/http/HttpErrorDeserializer.java b/client-http/src/main/java/software/amazon/smithy/java/client/http/HttpErrorDeserializer.java index 52df6ec05..57cdca183 100644 --- a/client-http/src/main/java/software/amazon/smithy/java/client/http/HttpErrorDeserializer.java +++ b/client-http/src/main/java/software/amazon/smithy/java/client/http/HttpErrorDeserializer.java @@ -9,8 +9,9 @@ import java.util.Objects; import java.util.concurrent.CompletableFuture; import software.amazon.smithy.java.context.Context; -import software.amazon.smithy.java.core.schema.ApiException; -import software.amazon.smithy.java.core.schema.ModeledApiException; +import software.amazon.smithy.java.core.error.CallException; +import software.amazon.smithy.java.core.error.ErrorFault; +import software.amazon.smithy.java.core.error.ModeledException; import software.amazon.smithy.java.core.schema.ShapeBuilder; import software.amazon.smithy.java.core.serde.Codec; import software.amazon.smithy.java.core.serde.SerializationException; @@ -54,8 +55,8 @@ public interface HeaderErrorExtractor { */ @FunctionalInterface public interface UnknownErrorFactory { - CompletableFuture createError( - ApiException.Fault fault, + CompletableFuture createError( + ErrorFault fault, String message, HttpResponse response ); @@ -75,11 +76,11 @@ public interface KnownErrorFactory { * @param builder Builder to populate and build. * @return the created error. */ - CompletableFuture createError( + CompletableFuture createError( Context context, Codec codec, HttpResponse response, - ShapeBuilder builder + ShapeBuilder builder ); /** @@ -98,13 +99,13 @@ CompletableFuture createError( * @param builder Builder to populate and build. * @return the created error. */ - default CompletableFuture createErrorFromDocument( + default CompletableFuture createErrorFromDocument( Context context, Codec codec, HttpResponse response, ByteBuffer responsePayload, Document parsedDocument, - ShapeBuilder builder + ShapeBuilder builder ) { return createError( context, @@ -130,29 +131,29 @@ public ShapeId resolveId(HttpResponse response, String serviceNamespace, TypeReg // Throws an ApiException. private static final UnknownErrorFactory DEFAULT_UNKNOWN_FACTORY = (fault, message, response) -> CompletableFuture - .completedFuture(new ApiException(message, fault)); + .completedFuture(new CallException(message, fault)); // Deserializes without HTTP bindings. private static final KnownErrorFactory DEFAULT_KNOWN_FACTORY = new KnownErrorFactory() { @Override - public CompletableFuture createError( + public CompletableFuture createError( Context context, Codec codec, HttpResponse response, - ShapeBuilder builder + ShapeBuilder builder ) { return createDataStream(response).asByteBuffer() .thenApply(bytes -> codec.deserializeShape(bytes, builder)); } @Override - public CompletableFuture createErrorFromDocument( + public CompletableFuture createErrorFromDocument( Context context, Codec codec, HttpResponse response, ByteBuffer responsePayload, Document parsedDocument, - ShapeBuilder builder + ShapeBuilder builder ) { parsedDocument.deserializeInto(builder); var result = builder.errorCorrection().build(); @@ -184,7 +185,7 @@ public static Builder builder() { return new Builder(); } - public CompletableFuture createError( + public CompletableFuture createError( Context context, ShapeId operation, TypeRegistry typeRegistry, @@ -218,7 +219,7 @@ private static DataStream createDataStream(HttpResponse response) { return DataStream.ofPublisher(response.body(), response.contentType(), response.contentLength(-1)); } - private CompletableFuture makeErrorFromHeader( + private CompletableFuture makeErrorFromHeader( Context context, ShapeId operation, TypeRegistry typeRegistry, @@ -226,7 +227,7 @@ private CompletableFuture makeErrorFromHeader( ) { // The content can be parsed directly here rather than through an intermediate document like with __type. var id = headerErrorExtractor.resolveId(response, serviceId.getNamespace(), typeRegistry); - var builder = id == null ? null : typeRegistry.createBuilder(id, ModeledApiException.class); + var builder = id == null ? null : typeRegistry.createBuilder(id, ModeledException.class); if (builder == null) { // The header didn't match a known error, so create an error from protocol hints. @@ -236,7 +237,7 @@ private CompletableFuture makeErrorFromHeader( } } - private static CompletableFuture makeErrorFromPayload( + private static CompletableFuture makeErrorFromPayload( Context context, Codec codec, KnownErrorFactory knownErrorFactory, @@ -253,7 +254,7 @@ private static CompletableFuture makeErrorFromPayload( try { var document = codec.createDeserializer(buffer).readDocument(); var id = document.discriminator(); - var builder = typeRegistry.createBuilder(id, ModeledApiException.class); + var builder = typeRegistry.createBuilder(id, ModeledException.class); if (builder != null) { return knownErrorFactory .createErrorFromDocument(context, codec, response, buffer, document, builder) @@ -267,12 +268,12 @@ private static CompletableFuture makeErrorFromPayload( }); } - private static CompletableFuture createErrorFromHints( + private static CompletableFuture createErrorFromHints( ShapeId operationId, HttpResponse response, UnknownErrorFactory unknownErrorFactory ) { - ApiException.Fault fault = ApiException.Fault.ofHttpStatusCode(response.statusCode()); + ErrorFault fault = ErrorFault.ofHttpStatusCode(response.statusCode()); String message = switch (fault) { case CLIENT -> "Client "; case SERVER -> "Server "; @@ -349,7 +350,7 @@ public Builder knownErrorFactory(KnownErrorFactory knownErrorFactory) { /** * The factory to use to create unknown errors. * - *

By default, an {@link ApiException} is created for generic unknown errors. + *

By default, an {@link CallException} is created for generic unknown errors. * * @param unknownErrorFactory Factory used to create generic errors that don't match a known error type. * @return the builder. diff --git a/client-http/src/main/java/software/amazon/smithy/java/client/http/plugins/ApplyHttpRetryInfoPlugin.java b/client-http/src/main/java/software/amazon/smithy/java/client/http/plugins/ApplyHttpRetryInfoPlugin.java index 5e6db4f26..4ea49bdc1 100644 --- a/client-http/src/main/java/software/amazon/smithy/java/client/http/plugins/ApplyHttpRetryInfoPlugin.java +++ b/client-http/src/main/java/software/amazon/smithy/java/client/http/plugins/ApplyHttpRetryInfoPlugin.java @@ -18,7 +18,7 @@ import software.amazon.smithy.java.client.core.settings.ClockSetting; import software.amazon.smithy.java.client.http.HttpMessageExchange; import software.amazon.smithy.java.context.Context; -import software.amazon.smithy.java.core.schema.ApiException; +import software.amazon.smithy.java.core.error.CallException; import software.amazon.smithy.java.core.schema.SerializableStruct; import software.amazon.smithy.java.http.api.HttpResponse; import software.amazon.smithy.java.retries.api.RetrySafety; @@ -45,16 +45,16 @@ public O modifyBeforeAttemptCompletion( OutputHook hook, RuntimeException error ) { - if (error instanceof ApiException ae - && ae.isRetrySafe() == RetrySafety.MAYBE + if (error instanceof CallException ce + && ce.isRetrySafe() == RetrySafety.MAYBE && hook.response() instanceof HttpResponse res) { - applyRetryInfo(res, ae, hook.context()); + applyRetryInfo(res, ce, hook.context()); } return hook.forward(error); } } - static void applyRetryInfo(HttpResponse response, ApiException exception, Context context) { + static void applyRetryInfo(HttpResponse response, CallException exception, Context context) { // (1) Check with the protocol if the server explicitly wants a retry. if (!applyRetryAfterHeader(response, exception, context)) { if (!applyThrottlingStatusCodes(response, exception)) { @@ -72,7 +72,7 @@ static void applyRetryInfo(HttpResponse response, ApiException exception, Contex } // Treat 429 and 503 errors as retryable throttling errors. - private static boolean applyThrottlingStatusCodes(HttpResponse response, ApiException exception) { + private static boolean applyThrottlingStatusCodes(HttpResponse response, CallException exception) { if (response.statusCode() == 429 || response.statusCode() == 503) { exception.isRetrySafe(RetrySafety.YES); exception.isThrottle(true); @@ -82,7 +82,7 @@ private static boolean applyThrottlingStatusCodes(HttpResponse response, ApiExce } // If there's a retry-after header, then the server is telling us it's retryable. - private static boolean applyRetryAfterHeader(HttpResponse response, ApiException exception, Context context) { + private static boolean applyRetryAfterHeader(HttpResponse response, CallException exception, Context context) { var retryAfter = response.headers().firstValue("retry-after"); if (retryAfter != null) { exception.isThrottle(true); diff --git a/client-http/src/test/java/software/amazon/smithy/java/client/http/AmznErrorHeaderExtractorTest.java b/client-http/src/test/java/software/amazon/smithy/java/client/http/AmznErrorHeaderExtractorTest.java index e46032c53..3974c7976 100644 --- a/client-http/src/test/java/software/amazon/smithy/java/client/http/AmznErrorHeaderExtractorTest.java +++ b/client-http/src/test/java/software/amazon/smithy/java/client/http/AmznErrorHeaderExtractorTest.java @@ -13,7 +13,7 @@ import java.util.List; import java.util.Map; import org.junit.jupiter.api.Test; -import software.amazon.smithy.java.core.schema.ModeledApiException; +import software.amazon.smithy.java.core.error.ModeledException; import software.amazon.smithy.java.core.schema.Schema; import software.amazon.smithy.java.core.schema.ShapeBuilder; import software.amazon.smithy.java.core.serde.ShapeDeserializer; @@ -53,7 +53,7 @@ public void resolvesAbsoluteShapeIds() { .headers(HttpHeaders.of(Map.of("x-amzn-errortype", List.of("baz#Bam")))) .build(); var registry = TypeRegistry.builder() - .putType(ShapeId.from("baz#Bam"), ModeledApiException.class, ApiExceptionBuilder::new) + .putType(ShapeId.from("baz#Bam"), ModeledException.class, ApiExceptionBuilder::new) .build(); assertThat(extractor.resolveId(response, "com.foo", registry), equalTo(ShapeId.from("baz#Bam"))); @@ -71,7 +71,7 @@ public void resolvesAbsoluteShapeIdsWithColons() { List.of("baz#Bam:http://internal.amazon.com/coral/com.amazon.coral.validate/")))) .build(); var registry = TypeRegistry.builder() - .putType(ShapeId.from("baz#Bam"), ModeledApiException.class, ApiExceptionBuilder::new) + .putType(ShapeId.from("baz#Bam"), ModeledException.class, ApiExceptionBuilder::new) .build(); assertThat(extractor.resolveId(response, "com.foo", registry), equalTo(ShapeId.from("baz#Bam"))); @@ -89,7 +89,7 @@ public void resolvesRelativeShapeIds() { List.of("Bam:http://internal.amazon.com/coral/com.amazon.coral.validate/")))) .build(); var registry = TypeRegistry.builder() - .putType(ShapeId.from("com.foo#Bam"), ModeledApiException.class, ApiExceptionBuilder::new) + .putType(ShapeId.from("com.foo#Bam"), ModeledException.class, ApiExceptionBuilder::new) .build(); assertThat(extractor.resolveId(response, "com.foo", registry), equalTo(ShapeId.from("com.foo#Bam"))); @@ -107,7 +107,7 @@ public void resolvesRelativeShapeIdsWithColons() { List.of("baz#Bam:http://internal.amazon.com/coral/com.amazon.coral.validate/")))) .build(); var registry = TypeRegistry.builder() - .putType(ShapeId.from("baz#Bam"), ModeledApiException.class, ApiExceptionBuilder::new) + .putType(ShapeId.from("baz#Bam"), ModeledException.class, ApiExceptionBuilder::new) .build(); assertThat(extractor.resolveId(response, "com.foo", registry), equalTo(ShapeId.from("baz#Bam"))); @@ -125,7 +125,7 @@ public void resolvesToServiceErrorWhenAbsoluteNotFound() { List.of("other#Bam:http://internal.amazon.com/coral/com.amazon.coral.validate/")))) .build(); var registry = TypeRegistry.builder() - .putType(ShapeId.from("com.foo#Bam"), ModeledApiException.class, ApiExceptionBuilder::new) + .putType(ShapeId.from("com.foo#Bam"), ModeledException.class, ApiExceptionBuilder::new) .build(); assertThat(extractor.resolveId(response, "com.foo", registry), equalTo(ShapeId.from("com.foo#Bam"))); @@ -147,7 +147,7 @@ public void returnsNullWhenNoTypeFound() { assertThat(extractor.resolveId(response, "com.foo", registry), nullValue()); } - private static final class ApiExceptionBuilder implements ShapeBuilder { + private static final class ApiExceptionBuilder implements ShapeBuilder { @Override public Schema schema() { @@ -155,17 +155,17 @@ public Schema schema() { } @Override - public ModeledApiException build() { + public ModeledException build() { throw new UnsupportedOperationException(); } @Override - public ShapeBuilder deserialize(ShapeDeserializer decoder) { + public ShapeBuilder deserialize(ShapeDeserializer decoder) { throw new UnsupportedOperationException(); } @Override - public ShapeBuilder errorCorrection() { + public ShapeBuilder errorCorrection() { throw new UnsupportedOperationException(); } } diff --git a/client-http/src/test/java/software/amazon/smithy/java/client/http/HttpErrorDeserializerTest.java b/client-http/src/test/java/software/amazon/smithy/java/client/http/HttpErrorDeserializerTest.java index b28c9e825..8b3b790de 100644 --- a/client-http/src/test/java/software/amazon/smithy/java/client/http/HttpErrorDeserializerTest.java +++ b/client-http/src/test/java/software/amazon/smithy/java/client/http/HttpErrorDeserializerTest.java @@ -18,8 +18,8 @@ import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import software.amazon.smithy.java.context.Context; -import software.amazon.smithy.java.core.schema.ApiException; -import software.amazon.smithy.java.core.schema.ModeledApiException; +import software.amazon.smithy.java.core.error.CallException; +import software.amazon.smithy.java.core.error.ModeledException; import software.amazon.smithy.java.core.schema.Schema; import software.amazon.smithy.java.core.schema.ShapeBuilder; import software.amazon.smithy.java.core.serde.Codec; @@ -127,7 +127,8 @@ public void usesGenericErrorWhenPayloadTypeIsUnknown() throws Exception { .codec(CODEC) .serviceId(SERVICE) .unknownErrorFactory( - (fault, message, response) -> CompletableFuture.completedFuture(new ApiException("Hi!", fault))) + (fault, message, response) -> CompletableFuture + .completedFuture(new CallException("Hi!", fault))) .build(); var registry = TypeRegistry.builder() .putType(Baz.SCHEMA.id(), Baz.class, Baz.Builder::new) @@ -138,7 +139,7 @@ public void usesGenericErrorWhenPayloadTypeIsUnknown() throws Exception { var response = responseBuilder.build(); var result = deserializer.createError(Context.create(), OPERATION, registry, response).get(); - assertThat(result, instanceOf(ApiException.class)); + assertThat(result, instanceOf(CallException.class)); assertThat(result.getMessage(), equalTo("Hi!")); } @@ -149,7 +150,8 @@ public void usesGenericErrorWhenHeaderTypeIsUnknown() throws Exception { .serviceId(SERVICE) .headerErrorExtractor(new AmznErrorHeaderExtractor()) .unknownErrorFactory( - (fault, message, response) -> CompletableFuture.completedFuture(new ApiException("Hi!", fault))) + (fault, message, response) -> CompletableFuture + .completedFuture(new CallException("Hi!", fault))) .build(); var registry = TypeRegistry.builder() .putType(Baz.SCHEMA.id(), Baz.class, Baz.Builder::new) @@ -167,11 +169,11 @@ public void usesGenericErrorWhenHeaderTypeIsUnknown() throws Exception { var response = responseBuilder.build(); var result = deserializer.createError(Context.create(), OPERATION, registry, response).get(); - assertThat(result, instanceOf(ApiException.class)); + assertThat(result, instanceOf(CallException.class)); assertThat(result.getMessage(), equalTo("Hi!")); } - static final class Baz extends ModeledApiException { + static final class Baz extends ModeledException { static final Schema SCHEMA = Schema .structureBuilder(ShapeId.from("com.foo#Baz"), new ErrorTrait("client")) diff --git a/client-http/src/test/java/software/amazon/smithy/java/client/http/plugins/ApplyHttpRetryInfoPluginTest.java b/client-http/src/test/java/software/amazon/smithy/java/client/http/plugins/ApplyHttpRetryInfoPluginTest.java index 178cb128e..2305f9cdb 100644 --- a/client-http/src/test/java/software/amazon/smithy/java/client/http/plugins/ApplyHttpRetryInfoPluginTest.java +++ b/client-http/src/test/java/software/amazon/smithy/java/client/http/plugins/ApplyHttpRetryInfoPluginTest.java @@ -20,7 +20,7 @@ import software.amazon.smithy.java.client.core.CallContext; import software.amazon.smithy.java.client.core.settings.ClockSetting; import software.amazon.smithy.java.context.Context; -import software.amazon.smithy.java.core.schema.ApiException; +import software.amazon.smithy.java.core.error.CallException; import software.amazon.smithy.java.http.api.HttpHeaders; import software.amazon.smithy.java.http.api.HttpResponse; import software.amazon.smithy.java.retries.api.RetrySafety; @@ -32,7 +32,7 @@ public void appliesRetryAfterHeader() { .statusCode(500) .headers(HttpHeaders.of(Map.of("retry-after", List.of("10")))) .build(); - var e = new ApiException("err"); + var e = new CallException("err"); var context = Context.create(); ApplyHttpRetryInfoPlugin.applyRetryInfo(response, e, context); @@ -48,7 +48,7 @@ public void appliesRetryAfterHeaderDate() { .statusCode(500) .headers(HttpHeaders.of(Map.of("retry-after", List.of("Wed, 21 Oct 2015 07:28:00 GMT")))) .build(); - var e = new ApiException("err"); + var e = new CallException("err"); var context = Context.create(); context.put(ClockSetting.CLOCK, Clock.fixed(Instant.parse("2015-10-21T05:28:00Z"), ZoneId.of("UTC"))); @@ -62,7 +62,7 @@ public void appliesRetryAfterHeaderDate() { @Test public void appliesThrottlingStatusCode503() { var response = HttpResponse.builder().statusCode(503).build(); - var e = new ApiException("err"); + var e = new CallException("err"); var context = Context.create(); ApplyHttpRetryInfoPlugin.applyRetryInfo(response, e, context); @@ -75,7 +75,7 @@ public void appliesThrottlingStatusCode503() { @Test public void appliesThrottlingStatusCode429() { var response = HttpResponse.builder().statusCode(429).build(); - var e = new ApiException("err"); + var e = new CallException("err"); var context = Context.create(); ApplyHttpRetryInfoPlugin.applyRetryInfo(response, e, context); @@ -88,7 +88,7 @@ public void appliesThrottlingStatusCode429() { @Test public void retriesSafe5xx() { var response = HttpResponse.builder().statusCode(500).build(); - var e = new ApiException("err"); + var e = new CallException("err"); var context = Context.create(); context.put(CallContext.IDEMPOTENCY_TOKEN, "foo"); @@ -102,7 +102,7 @@ public void retriesSafe5xx() { @Test public void doesNotRetryUnsafe5xx() { var response = HttpResponse.builder().statusCode(500).build(); - var e = new ApiException("err"); + var e = new CallException("err"); var context = Context.create(); ApplyHttpRetryInfoPlugin.applyRetryInfo(response, e, context); @@ -115,7 +115,7 @@ public void doesNotRetryUnsafe5xx() { @Test public void doesNotRetryNormal4xx() { var response = HttpResponse.builder().statusCode(400).build(); - var e = new ApiException("err"); + var e = new CallException("err"); var context = Context.create(); ApplyHttpRetryInfoPlugin.applyRetryInfo(response, e, context); diff --git a/codegen/core/src/it/java/software/amazon/smithy/java/codegen/test/ExceptionsTest.java b/codegen/core/src/it/java/software/amazon/smithy/java/codegen/test/ExceptionsTest.java index 692e2d332..268ce5eb9 100644 --- a/codegen/core/src/it/java/software/amazon/smithy/java/codegen/test/ExceptionsTest.java +++ b/codegen/core/src/it/java/software/amazon/smithy/java/codegen/test/ExceptionsTest.java @@ -21,7 +21,7 @@ import software.amazon.smithy.java.codegen.test.model.ExceptionWithExtraStringException; import software.amazon.smithy.java.codegen.test.model.OptionalMessageException; import software.amazon.smithy.java.codegen.test.model.SimpleException; -import software.amazon.smithy.java.core.schema.ModeledApiException; +import software.amazon.smithy.java.core.error.ModeledException; import software.amazon.smithy.java.core.schema.SerializableShape; @ExtendWith(ReloadClassesExtension.class) @@ -39,7 +39,7 @@ static Stream exceptions() { @ParameterizedTest @MethodSource("exceptions") @Order(1) - void simpleExceptionToDocumentRoundTrip(ModeledApiException exception) { + void simpleExceptionToDocumentRoundTrip(ModeledException exception) { var output = Utils.pojoToDocumentRoundTrip(exception); assertEquals(exception.getMessage(), output.getMessage()); assertEquals(exception.getCause(), output.getCause()); diff --git a/codegen/core/src/main/java/software/amazon/smithy/java/codegen/generators/OperationGenerator.java b/codegen/core/src/main/java/software/amazon/smithy/java/codegen/generators/OperationGenerator.java index f85a468b6..ee8b64603 100644 --- a/codegen/core/src/main/java/software/amazon/smithy/java/codegen/generators/OperationGenerator.java +++ b/codegen/core/src/main/java/software/amazon/smithy/java/codegen/generators/OperationGenerator.java @@ -17,10 +17,10 @@ import software.amazon.smithy.java.codegen.JavaCodegenSettings; import software.amazon.smithy.java.codegen.sections.ClassSection; import software.amazon.smithy.java.codegen.writer.JavaWriter; +import software.amazon.smithy.java.core.error.ModeledException; import software.amazon.smithy.java.core.schema.ApiOperation; import software.amazon.smithy.java.core.schema.ApiResource; import software.amazon.smithy.java.core.schema.InputEventStreamingApiOperation; -import software.amazon.smithy.java.core.schema.ModeledApiException; import software.amazon.smithy.java.core.schema.OutputEventStreamingApiOperation; import software.amazon.smithy.java.core.schema.Schema; import software.amazon.smithy.java.core.schema.ShapeBuilder; @@ -161,7 +161,7 @@ public final class ${shape:T} implements ${operationType:C} { writer.putContext("list", List.class); writer.putContext("string", String.class); writer.putContext("set", Set.class); - writer.putContext("modeledApiException", ModeledApiException.class); + writer.putContext("modeledApiException", ModeledException.class); writer.putContext( "operationType", diff --git a/codegen/core/src/main/java/software/amazon/smithy/java/codegen/generators/StructureGenerator.java b/codegen/core/src/main/java/software/amazon/smithy/java/codegen/generators/StructureGenerator.java index ed572c3ec..e4736f1d0 100644 --- a/codegen/core/src/main/java/software/amazon/smithy/java/codegen/generators/StructureGenerator.java +++ b/codegen/core/src/main/java/software/amazon/smithy/java/codegen/generators/StructureGenerator.java @@ -28,7 +28,7 @@ import software.amazon.smithy.java.codegen.sections.ClassSection; import software.amazon.smithy.java.codegen.sections.GetterSection; import software.amazon.smithy.java.codegen.writer.JavaWriter; -import software.amazon.smithy.java.core.schema.ModeledApiException; +import software.amazon.smithy.java.core.error.ModeledException; import software.amazon.smithy.java.core.schema.PresenceTracker; import software.amazon.smithy.java.core.schema.SerializableStruct; import software.amazon.smithy.java.core.serde.document.Document; @@ -120,7 +120,7 @@ public final class ${shape:T} ${^isError}implements ${serializableStruct:T}${/is writer.putContext("isError", shape.hasTrait(ErrorTrait.class)); writer.putContext("shape", directive.symbol()); writer.putContext("serializableStruct", SerializableStruct.class); - writer.putContext("sdkException", ModeledApiException.class); + writer.putContext("sdkException", ModeledException.class); writer.putContext("id", new IdStringGenerator(writer, shape)); writer.putContext( "schemas", diff --git a/codegen/plugins/server/src/it/java/software/amazon/smithy/java/codegen/server/ServiceBuilderTest.java b/codegen/plugins/server/src/it/java/software/amazon/smithy/java/codegen/server/ServiceBuilderTest.java index 74ec5e156..e08ec7c1a 100644 --- a/codegen/plugins/server/src/it/java/software/amazon/smithy/java/codegen/server/ServiceBuilderTest.java +++ b/codegen/plugins/server/src/it/java/software/amazon/smithy/java/codegen/server/ServiceBuilderTest.java @@ -25,7 +25,7 @@ import smithy.java.codegen.server.test.service.GetBeerOperationAsync; import smithy.java.codegen.server.test.service.GetErrorOperationAsync; import smithy.java.codegen.server.test.service.TestService; -import software.amazon.smithy.java.core.schema.ModeledApiException; +import software.amazon.smithy.java.core.error.ModeledException; import software.amazon.smithy.java.framework.model.InternalFailureException; import software.amazon.smithy.java.framework.model.UnknownOperationException; import software.amazon.smithy.java.server.Operation; @@ -57,7 +57,7 @@ public CompletableFuture getError(GetErrorInput input, RequestCo Throwable exception; try { Class clazz = Class.forName(input.exceptionClass()); - if (ModeledApiException.class.isAssignableFrom(clazz)) { + if (ModeledException.class.isAssignableFrom(clazz)) { Object builderInstance = clazz.getDeclaredMethod("builder").invoke(null); builderInstance = builderInstance.getClass() .getMethod("message") diff --git a/core/src/main/java/software/amazon/smithy/java/core/schema/ApiException.java b/core/src/main/java/software/amazon/smithy/java/core/error/CallException.java similarity index 52% rename from core/src/main/java/software/amazon/smithy/java/core/schema/ApiException.java rename to core/src/main/java/software/amazon/smithy/java/core/error/CallException.java index 22ad28c75..ff744c350 100644 --- a/core/src/main/java/software/amazon/smithy/java/core/schema/ApiException.java +++ b/core/src/main/java/software/amazon/smithy/java/core/error/CallException.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package software.amazon.smithy.java.core.schema; +package software.amazon.smithy.java.core.error; import java.time.Duration; import java.util.Objects; @@ -17,99 +17,59 @@ * errors, and shape validation errors. It should not be used for illegal arguments, null argument validation, * or other kinds of logic errors sufficiently covered by the Java standard library. */ -public class ApiException extends RuntimeException implements RetryInfo { +public class CallException extends RuntimeException implements RetryInfo { private static final boolean GLOBAL_CAPTURE_STACK_TRACE_ENABLED = Boolean.getBoolean( "smithy.java.captureExceptionStackTraces"); - /** - * The party that is at fault for the error, if any. - * - *

This kind of enum is used rather than top-level client/server errors that services extend from because - * services generate their own dedicated error hierarchies that all extend from a common base-class for the - * service. - */ - public enum Fault { - /** - * The client is at fault for this error (e.g., it omitted a required parameter or sent an invalid request). - */ - CLIENT, - - /** - * The server is at fault (e.g., it was unable to connect to a database, or other unexpected errors occurred). - */ - SERVER, - - /** - * The fault isn't necessarily client or server. - */ - OTHER; - - /** - * Create a Fault based on an HTTP status code. - * - * @param statusCode HTTP status code to check. - * @return the created fault. - */ - public static Fault ofHttpStatusCode(int statusCode) { - if (statusCode >= 400 && statusCode <= 499) { - return ApiException.Fault.CLIENT; - } else if (statusCode >= 500 && statusCode <= 599) { - return ApiException.Fault.SERVER; - } else { - return ApiException.Fault.OTHER; - } - } - } - - private final Fault errorType; + private final ErrorFault errorType; // Mutable retryInfo private RetrySafety isRetrySafe = RetrySafety.MAYBE; private boolean isThrottle = false; private Duration retryAfter = null; - public ApiException(String message) { - this(message, Fault.OTHER, null); + public CallException(String message) { + this(message, ErrorFault.OTHER, null); } - public ApiException(String message, boolean captureStackTrace) { - this(message, Fault.OTHER, captureStackTrace); + public CallException(String message, boolean captureStackTrace) { + this(message, ErrorFault.OTHER, captureStackTrace); } - public ApiException(String message, Fault errorType) { + public CallException(String message, ErrorFault errorType) { this(message, errorType, null); } - public ApiException(String message, Fault errorType, boolean captureStackTrace) { + public CallException(String message, ErrorFault errorType, boolean captureStackTrace) { this(message, null, errorType, captureStackTrace); } - protected ApiException(String message, Fault errorType, Boolean captureStackTrace) { + protected CallException(String message, ErrorFault errorType, Boolean captureStackTrace) { this(message, null, errorType, captureStackTrace); } - public ApiException(String message, Throwable cause) { + public CallException(String message, Throwable cause) { this(message, cause, (Boolean) null); } - public ApiException(String message, Throwable cause, boolean captureStackTrace) { - this(message, cause, Fault.OTHER, captureStackTrace); + public CallException(String message, Throwable cause, boolean captureStackTrace) { + this(message, cause, ErrorFault.OTHER, captureStackTrace); } - protected ApiException(String message, Throwable cause, Boolean captureStackTrace) { - this(message, cause, Fault.OTHER, captureStackTrace); + protected CallException(String message, Throwable cause, Boolean captureStackTrace) { + this(message, cause, ErrorFault.OTHER, captureStackTrace); } - public ApiException(String message, Throwable cause, Fault errorType) { + public CallException(String message, Throwable cause, ErrorFault errorType) { this(message, cause, errorType, false); } - public ApiException(String message, Throwable cause, Fault errorType, boolean captureStackTrace) { + public CallException(String message, Throwable cause, ErrorFault errorType, boolean captureStackTrace) { this(message, cause, errorType, (Boolean) captureStackTrace); } - protected ApiException(String message, Throwable cause, Fault errorType, Boolean captureStackTrace) { + protected CallException(String message, Throwable cause, ErrorFault errorType, Boolean captureStackTrace) { super( message, cause, @@ -123,7 +83,7 @@ protected ApiException(String message, Throwable cause, Fault errorType, Boolean * * @return Returns the error type (e.g., client, server, etc). */ - public Fault getFault() { + public ErrorFault getFault() { return errorType; } diff --git a/core/src/main/java/software/amazon/smithy/java/core/error/ErrorFault.java b/core/src/main/java/software/amazon/smithy/java/core/error/ErrorFault.java new file mode 100644 index 000000000..5c5e5febd --- /dev/null +++ b/core/src/main/java/software/amazon/smithy/java/core/error/ErrorFault.java @@ -0,0 +1,46 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.java.core.error; + +/** + * The party that is at fault for the error, if any. + * + *

This kind of enum is used rather than top-level client/server errors that services extend from because + * services generate their own dedicated error hierarchies that all extend from a common base-class for the + * service. + */ +public enum ErrorFault { + /** + * The client is at fault for this error (e.g., it omitted a required parameter or sent an invalid request). + */ + CLIENT, + + /** + * The server is at fault (e.g., it was unable to connect to a database, or other unexpected errors occurred). + */ + SERVER, + + /** + * The fault isn't necessarily client or server. + */ + OTHER; + + /** + * Create a Fault based on an HTTP status code. + * + * @param statusCode HTTP status code to check. + * @return the created fault. + */ + public static ErrorFault ofHttpStatusCode(int statusCode) { + if (statusCode >= 400 && statusCode <= 499) { + return ErrorFault.CLIENT; + } else if (statusCode >= 500 && statusCode <= 599) { + return ErrorFault.SERVER; + } else { + return ErrorFault.OTHER; + } + } +} diff --git a/core/src/main/java/software/amazon/smithy/java/core/schema/ModeledApiException.java b/core/src/main/java/software/amazon/smithy/java/core/error/ModeledException.java similarity index 75% rename from core/src/main/java/software/amazon/smithy/java/core/schema/ModeledApiException.java rename to core/src/main/java/software/amazon/smithy/java/core/error/ModeledException.java index 58e9d3f13..58eef76c8 100644 --- a/core/src/main/java/software/amazon/smithy/java/core/schema/ModeledApiException.java +++ b/core/src/main/java/software/amazon/smithy/java/core/error/ModeledException.java @@ -3,35 +3,39 @@ * SPDX-License-Identifier: Apache-2.0 */ -package software.amazon.smithy.java.core.schema; +package software.amazon.smithy.java.core.error; + +import software.amazon.smithy.java.core.schema.Schema; +import software.amazon.smithy.java.core.schema.SerializableStruct; +import software.amazon.smithy.java.core.schema.TraitKey; /** * The top-level exception that should be used to throw modeled errors from clients and servers. */ -public abstract class ModeledApiException extends ApiException implements SerializableStruct { +public abstract class ModeledException extends CallException implements SerializableStruct { private final Schema schema; private boolean deserialized = false; - protected ModeledApiException(Schema schema, String message) { + protected ModeledException(Schema schema, String message) { super(message); this.schema = schema; } - protected ModeledApiException(Schema schema, String message, Fault errorType) { + protected ModeledException(Schema schema, String message, ErrorFault errorType) { super(message, null, errorType, null); this.schema = schema; } - protected ModeledApiException(Schema schema, String message, Fault errorType, Throwable cause) { + protected ModeledException(Schema schema, String message, ErrorFault errorType, Throwable cause) { super(message, cause, errorType, null); this.schema = schema; } - protected ModeledApiException( + protected ModeledException( Schema schema, String message, - Fault errorType, + ErrorFault errorType, Throwable cause, Boolean captureStackTrace, boolean deserialized @@ -41,7 +45,7 @@ protected ModeledApiException( this.deserialized = deserialized; } - protected ModeledApiException( + protected ModeledException( Schema schema, String message, Throwable cause, @@ -53,16 +57,16 @@ protected ModeledApiException( this.deserialized = deserialized; } - protected ModeledApiException(Schema schema, String message, Throwable cause) { + protected ModeledException(Schema schema, String message, Throwable cause) { super(message, cause, (Boolean) null); this.schema = schema; } - protected ModeledApiException( + protected ModeledException( Schema schema, String message, Throwable cause, - Fault errorType, + ErrorFault errorType, Boolean captureStackTrace, boolean deserialized ) { diff --git a/core/src/test/java/software/amazon/smithy/java/core/schema/ApiExceptionTest.java b/core/src/test/java/software/amazon/smithy/java/core/schema/ApiExceptionTest.java index 76c9aa43b..bd3aa6da2 100644 --- a/core/src/test/java/software/amazon/smithy/java/core/schema/ApiExceptionTest.java +++ b/core/src/test/java/software/amazon/smithy/java/core/schema/ApiExceptionTest.java @@ -12,12 +12,13 @@ import java.time.Duration; import org.junit.jupiter.api.Test; +import software.amazon.smithy.java.core.error.CallException; import software.amazon.smithy.java.retries.api.RetrySafety; public class ApiExceptionTest { @Test public void resetsRetryStateWhenSetToNotRetry() { - var e = new ApiException("foo"); + var e = new CallException("foo"); var after = Duration.ofDays(2); e.isRetrySafe(RetrySafety.YES); diff --git a/dynamic-client/src/main/java/software/amazon/smithy/java/dynamicclient/DocumentException.java b/dynamic-client/src/main/java/software/amazon/smithy/java/dynamicclient/DocumentException.java index 67b828413..be8983eda 100644 --- a/dynamic-client/src/main/java/software/amazon/smithy/java/dynamicclient/DocumentException.java +++ b/dynamic-client/src/main/java/software/amazon/smithy/java/dynamicclient/DocumentException.java @@ -5,7 +5,7 @@ package software.amazon.smithy.java.dynamicclient; -import software.amazon.smithy.java.core.schema.ModeledApiException; +import software.amazon.smithy.java.core.error.ModeledException; import software.amazon.smithy.java.core.schema.Schema; import software.amazon.smithy.java.core.schema.ShapeBuilder; import software.amazon.smithy.java.core.serde.ShapeDeserializer; @@ -14,9 +14,9 @@ import software.amazon.smithy.model.shapes.ShapeId; /** - * A {@link ModeledApiException} that provides access to the contents of the exception as a document. + * A {@link ModeledException} that provides access to the contents of the exception as a document. */ -public final class DocumentException extends ModeledApiException { +public final class DocumentException extends ModeledException { private final WrappedDocument document; @@ -49,7 +49,7 @@ public Document getContents() { return document; } - static final class SchemaGuidedExceptionBuilder implements ShapeBuilder { + static final class SchemaGuidedExceptionBuilder implements ShapeBuilder { private final Schema target; private final SchemaGuidedDocumentBuilder delegateBuilder; diff --git a/dynamic-client/src/main/java/software/amazon/smithy/java/dynamicclient/DynamicClient.java b/dynamic-client/src/main/java/software/amazon/smithy/java/dynamicclient/DynamicClient.java index ebda064d9..c4d2e9b30 100644 --- a/dynamic-client/src/main/java/software/amazon/smithy/java/dynamicclient/DynamicClient.java +++ b/dynamic-client/src/main/java/software/amazon/smithy/java/dynamicclient/DynamicClient.java @@ -22,9 +22,9 @@ import software.amazon.smithy.java.client.core.ClientProtocolFactory; import software.amazon.smithy.java.client.core.ProtocolSettings; import software.amazon.smithy.java.client.core.RequestOverrideConfig; -import software.amazon.smithy.java.core.schema.ApiException; +import software.amazon.smithy.java.core.error.CallException; +import software.amazon.smithy.java.core.error.ModeledException; import software.amazon.smithy.java.core.schema.ApiOperation; -import software.amazon.smithy.java.core.schema.ModeledApiException; import software.amazon.smithy.java.core.schema.Schema; import software.amazon.smithy.java.core.schema.SerializableStruct; import software.amazon.smithy.java.core.serde.TypeRegistry; @@ -55,7 +55,7 @@ *

  • No code generated types. You have to construct input and use output manually using document APIs.
  • *
  • No support for streaming inputs or outputs.
  • *
  • All errors are created as an {@link DocumentException} if the error is modeled, allowing document access - * to the modeled error contents. Other errors are deserialized as {@link ApiException}. + * to the modeled error contents. Other errors are deserialized as {@link CallException}. * */ public final class DynamicClient extends Client { @@ -90,7 +90,7 @@ private DynamicClient(Builder builder, ServiceShape shape, Model model) { private void registerError(ShapeId e, TypeRegistry.Builder registryBuilder) { var error = model.expectShape(e); var errorSchema = schemaConverter.getSchema(error); - registryBuilder.putType(e, ModeledApiException.class, () -> { + registryBuilder.putType(e, ModeledException.class, () -> { return new DocumentException.SchemaGuidedExceptionBuilder(service.getId(), errorSchema); }); } diff --git a/dynamic-client/src/test/java/software/amazon/smithy/java/dynamicclient/DynamicClientTest.java b/dynamic-client/src/test/java/software/amazon/smithy/java/dynamicclient/DynamicClientTest.java index 1fc7fe9a0..062b5715b 100644 --- a/dynamic-client/src/test/java/software/amazon/smithy/java/dynamicclient/DynamicClientTest.java +++ b/dynamic-client/src/test/java/software/amazon/smithy/java/dynamicclient/DynamicClientTest.java @@ -24,8 +24,8 @@ import software.amazon.smithy.java.client.core.interceptors.RequestHook; import software.amazon.smithy.java.client.http.HttpMessageExchange; import software.amazon.smithy.java.context.Context; -import software.amazon.smithy.java.core.schema.ApiException; -import software.amazon.smithy.java.core.schema.ModeledApiException; +import software.amazon.smithy.java.core.error.CallException; +import software.amazon.smithy.java.core.error.ModeledException; import software.amazon.smithy.java.core.serde.document.Document; import software.amazon.smithy.java.http.api.HttpRequest; import software.amazon.smithy.java.http.api.HttpResponse; @@ -153,11 +153,11 @@ public void readBeforeTransmit(RequestHook hook) { @Test public void deserializesDynamicErrorsWithAbsoluteId() { var client = createErrorClient("{\"__type\":\"smithy.example#InvalidSprocketId\", \"id\":\"1\"}"); - var e = Assertions.assertThrows(ApiException.class, () -> { + var e = Assertions.assertThrows(CallException.class, () -> { client.call("GetSprocket", Document.ofObject(Map.of("id", "1"))); }); - assertThat(e, instanceOf(ModeledApiException.class)); + assertThat(e, instanceOf(ModeledException.class)); assertThat(e, instanceOf(DocumentException.class)); var de = (DocumentException) e; @@ -170,11 +170,11 @@ public void deserializesDynamicErrorsWithAbsoluteId() { @Test public void deserializesDynamicErrorsWithRelativeId() { var client = createErrorClient("{\"__type\":\"InvalidSprocketId\", \"id\":\"1\"}"); - var e = Assertions.assertThrows(ApiException.class, () -> { + var e = Assertions.assertThrows(CallException.class, () -> { client.call("GetSprocket", Document.ofObject(Map.of("id", "1"))); }); - assertThat(e, instanceOf(ModeledApiException.class)); + assertThat(e, instanceOf(ModeledException.class)); assertThat(e, instanceOf(DocumentException.class)); var de = (DocumentException) e; @@ -187,11 +187,11 @@ public void deserializesDynamicErrorsWithRelativeId() { @Test public void deserializesDynamicErrorsWithRelativeIdFromService() { var client = createErrorClient("{\"__type\":\"ServiceFooError\", \"why\":\"IDK\"}"); - var e = Assertions.assertThrows(ApiException.class, () -> { + var e = Assertions.assertThrows(CallException.class, () -> { client.call("GetSprocket", Document.ofObject(Map.of("id", "1"))); }); - assertThat(e, instanceOf(ModeledApiException.class)); + assertThat(e, instanceOf(ModeledException.class)); assertThat(e, instanceOf(DocumentException.class)); var de = (DocumentException) e; diff --git a/http-binding/src/main/java/software/amazon/smithy/java/http/binding/HttpBindingSerializer.java b/http-binding/src/main/java/software/amazon/smithy/java/http/binding/HttpBindingSerializer.java index 6e6763828..4535b4fc5 100644 --- a/http-binding/src/main/java/software/amazon/smithy/java/http/binding/HttpBindingSerializer.java +++ b/http-binding/src/main/java/software/amazon/smithy/java/http/binding/HttpBindingSerializer.java @@ -14,7 +14,7 @@ import java.util.TreeMap; import java.util.concurrent.Flow; import java.util.function.BiConsumer; -import software.amazon.smithy.java.core.schema.ModeledApiException; +import software.amazon.smithy.java.core.error.ModeledException; import software.amazon.smithy.java.core.schema.Schema; import software.amazon.smithy.java.core.schema.SchemaUtils; import software.amazon.smithy.java.core.schema.SerializableStruct; @@ -105,7 +105,7 @@ public void writeStruct(Schema schema, SerializableStruct struct) { } if (isFailure) { - responseStatus = ModeledApiException.getHttpStatusCode(schema); + responseStatus = ModeledException.getHttpStatusCode(schema); // TODO: Update this to only use the full ID if the schema namespace is outside the // service namespace headers.put("X-Amzn-Errortype", List.of(schema.id().toString())); diff --git a/http-binding/src/main/java/software/amazon/smithy/java/http/binding/ResponseDeserializer.java b/http-binding/src/main/java/software/amazon/smithy/java/http/binding/ResponseDeserializer.java index 114c7b6ad..17456c7ea 100644 --- a/http-binding/src/main/java/software/amazon/smithy/java/http/binding/ResponseDeserializer.java +++ b/http-binding/src/main/java/software/amazon/smithy/java/http/binding/ResponseDeserializer.java @@ -7,7 +7,7 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentMap; -import software.amazon.smithy.java.core.schema.ModeledApiException; +import software.amazon.smithy.java.core.error.ModeledException; import software.amazon.smithy.java.core.schema.Schema; import software.amazon.smithy.java.core.schema.ShapeBuilder; import software.amazon.smithy.java.core.serde.Codec; @@ -23,7 +23,7 @@ public final class ResponseDeserializer { private final HttpBindingDeserializer.Builder deserBuilder = HttpBindingDeserializer.builder(); private ShapeBuilder outputShapeBuilder; - private ShapeBuilder errorShapeBuilder; + private ShapeBuilder errorShapeBuilder; private final ConcurrentMap bindingCache; ResponseDeserializer(ConcurrentMap bindingCache) { @@ -105,7 +105,7 @@ public > ResponseDeserializer eventDecoderFactory(EventDecode * @param errorShapeBuilder Error shape builder. * @return Returns the deserializer. */ - public ResponseDeserializer errorShapeBuilder(ShapeBuilder errorShapeBuilder) { + public ResponseDeserializer errorShapeBuilder(ShapeBuilder errorShapeBuilder) { this.errorShapeBuilder = errorShapeBuilder; outputShapeBuilder = null; return this; diff --git a/mock-client-plugin/src/test/java/software/amazon/smithy/java/client/http/mock/MockPluginTest.java b/mock-client-plugin/src/test/java/software/amazon/smithy/java/client/http/mock/MockPluginTest.java index 939530eb9..521265a85 100644 --- a/mock-client-plugin/src/test/java/software/amazon/smithy/java/client/http/mock/MockPluginTest.java +++ b/mock-client-plugin/src/test/java/software/amazon/smithy/java/client/http/mock/MockPluginTest.java @@ -23,7 +23,8 @@ import software.amazon.smithy.java.client.core.endpoint.EndpointResolver; import software.amazon.smithy.java.client.core.interceptors.ClientInterceptor; import software.amazon.smithy.java.client.core.interceptors.OutputHook; -import software.amazon.smithy.java.core.schema.ApiException; +import software.amazon.smithy.java.core.error.CallException; +import software.amazon.smithy.java.core.error.ErrorFault; import software.amazon.smithy.java.core.serde.document.Document; import software.amazon.smithy.java.dynamicclient.DynamicClient; import software.amazon.smithy.java.framework.model.InternalFailureException; @@ -115,8 +116,8 @@ public void returnsMockedInternalError() throws URISyntaxException { .authSchemeResolver(AuthSchemeResolver.NO_AUTH) .build(); - var e = Assertions.assertThrows(ApiException.class, () -> client.call("GetSprocket")); - assertThat(e.getFault(), equalTo(ApiException.Fault.SERVER)); + var e = Assertions.assertThrows(CallException.class, () -> client.call("GetSprocket")); + assertThat(e.getFault(), equalTo(ErrorFault.SERVER)); assertThat(e.isRetrySafe(), equalTo(RetrySafety.MAYBE)); assertThat(mock.getRequests(), hasSize(1)); @@ -166,8 +167,8 @@ public void readAfterExecution(OutputHook hook, RuntimeException err }) .build(); - var e = Assertions.assertThrows(ApiException.class, () -> client.call("GetSprocket")); - assertThat(e.getFault(), is(ApiException.Fault.CLIENT)); + var e = Assertions.assertThrows(CallException.class, () -> client.call("GetSprocket")); + assertThat(e.getFault(), is(ErrorFault.CLIENT)); assertThat(mock.getRequests(), hasSize(1)); diff --git a/protocol-tests/src/main/java/software/amazon/smithy/java/protocoltests/harness/HttpClientResponseProtocolTestProvider.java b/protocol-tests/src/main/java/software/amazon/smithy/java/protocoltests/harness/HttpClientResponseProtocolTestProvider.java index b12c15905..c07cb4a21 100644 --- a/protocol-tests/src/main/java/software/amazon/smithy/java/protocoltests/harness/HttpClientResponseProtocolTestProvider.java +++ b/protocol-tests/src/main/java/software/amazon/smithy/java/protocoltests/harness/HttpClientResponseProtocolTestProvider.java @@ -23,8 +23,8 @@ import software.amazon.smithy.java.client.core.auth.scheme.AuthSchemeResolver; import software.amazon.smithy.java.client.http.HttpMessageExchange; import software.amazon.smithy.java.context.Context; +import software.amazon.smithy.java.core.error.ModeledException; import software.amazon.smithy.java.core.schema.ApiOperation; -import software.amazon.smithy.java.core.schema.ModeledApiException; import software.amazon.smithy.java.core.schema.SerializableStruct; import software.amazon.smithy.java.http.api.HttpHeaders; import software.amazon.smithy.java.http.api.HttpRequest; @@ -126,7 +126,7 @@ public List getAdditionalExtensions() { fail("Expected an exception but got a successful response %s", actualOutput); } } catch (Exception e) { - if (isErrorTestCase && e instanceof ModeledApiException mae) { + if (isErrorTestCase && e instanceof ModeledException mae) { actualOutput = mae; } else { throw e; diff --git a/protocol-tests/src/main/java/software/amazon/smithy/java/protocoltests/harness/MockClient.java b/protocol-tests/src/main/java/software/amazon/smithy/java/protocoltests/harness/MockClient.java index b8e9957a0..aaba04b4a 100644 --- a/protocol-tests/src/main/java/software/amazon/smithy/java/protocoltests/harness/MockClient.java +++ b/protocol-tests/src/main/java/software/amazon/smithy/java/protocoltests/harness/MockClient.java @@ -19,7 +19,8 @@ import software.amazon.smithy.java.client.core.endpoint.EndpointResolver; import software.amazon.smithy.java.client.http.HttpMessageExchange; import software.amazon.smithy.java.context.Context; -import software.amazon.smithy.java.core.schema.ApiException; +import software.amazon.smithy.java.core.error.CallException; +import software.amazon.smithy.java.core.error.ErrorFault; import software.amazon.smithy.java.core.schema.ApiOperation; import software.amazon.smithy.java.core.schema.PreludeSchemas; import software.amazon.smithy.java.core.schema.SerializableStruct; @@ -48,8 +49,8 @@ public O clientRequ try { return call(input, operation, overrideConfig).exceptionallyCompose(exc -> { if (exc instanceof CompletionException ce - && ce.getCause() instanceof ApiException apiException - && apiException.getFault().equals(ApiException.Fault.SERVER)) { + && ce.getCause() instanceof CallException apiException + && apiException.getFault().equals(ErrorFault.SERVER)) { LOGGER.debug("Ignoring expected exception", apiException); return CompletableFuture.completedFuture(null); } else { diff --git a/protocol-tests/src/main/java/software/amazon/smithy/java/protocoltests/harness/ProtocolTestExtension.java b/protocol-tests/src/main/java/software/amazon/smithy/java/protocoltests/harness/ProtocolTestExtension.java index 58f708511..eac2f51d1 100644 --- a/protocol-tests/src/main/java/software/amazon/smithy/java/protocoltests/harness/ProtocolTestExtension.java +++ b/protocol-tests/src/main/java/software/amazon/smithy/java/protocoltests/harness/ProtocolTestExtension.java @@ -25,8 +25,8 @@ import software.amazon.smithy.java.codegen.JavaSymbolProvider; import software.amazon.smithy.java.codegen.server.ServerSymbolProperties; import software.amazon.smithy.java.codegen.server.ServiceJavaSymbolProvider; +import software.amazon.smithy.java.core.error.ModeledException; import software.amazon.smithy.java.core.schema.ApiOperation; -import software.amazon.smithy.java.core.schema.ModeledApiException; import software.amazon.smithy.java.core.schema.SerializableStruct; import software.amazon.smithy.java.core.schema.ShapeBuilder; import software.amazon.smithy.java.logging.InternalLogger; @@ -368,9 +368,9 @@ private static Supplier> getApiExcept try { var fqn = provider.toSymbol(shape).getFullName(); var exceptionClazz = CodegenUtils.getClassForName(fqn); - if (ModeledApiException.class.isAssignableFrom(exceptionClazz)) { + if (ModeledException.class.isAssignableFrom(exceptionClazz)) { var builder = exceptionClazz.getDeclaredMethod("builder").invoke(null); - return () -> (ShapeBuilder) builder; + return () -> (ShapeBuilder) builder; } else { throw new IllegalArgumentException(exceptionClazz + "is not a ModeledApiException"); } diff --git a/protocol-tests/src/main/java/software/amazon/smithy/java/protocoltests/harness/ProtocolTestProtocolProvider.java b/protocol-tests/src/main/java/software/amazon/smithy/java/protocoltests/harness/ProtocolTestProtocolProvider.java index 30384dbf7..195d23de1 100644 --- a/protocol-tests/src/main/java/software/amazon/smithy/java/protocoltests/harness/ProtocolTestProtocolProvider.java +++ b/protocol-tests/src/main/java/software/amazon/smithy/java/protocoltests/harness/ProtocolTestProtocolProvider.java @@ -11,7 +11,7 @@ import java.util.concurrent.CompletableFuture; import java.util.stream.Collectors; import software.amazon.smithy.java.context.Context; -import software.amazon.smithy.java.core.schema.ModeledApiException; +import software.amazon.smithy.java.core.error.ModeledException; import software.amazon.smithy.java.core.schema.SerializableStruct; import software.amazon.smithy.java.server.Service; import software.amazon.smithy.java.server.core.Job; @@ -105,7 +105,7 @@ public CompletableFuture serializeOutput(Job job, SerializableStruct outpu var protocol = job.request().context().get(PROTOCOL_TO_TEST); if (protocol != null) { if (isError) { - return protocol.serializeError(job, (ModeledApiException) output); + return protocol.serializeError(job, (ModeledException) output); } else { return protocol.serializeOutput(job, output); } diff --git a/server-core/src/main/java/software/amazon/smithy/java/server/core/OperationHandler.java b/server-core/src/main/java/software/amazon/smithy/java/server/core/OperationHandler.java index 2284fde0b..180469770 100644 --- a/server-core/src/main/java/software/amazon/smithy/java/server/core/OperationHandler.java +++ b/server-core/src/main/java/software/amazon/smithy/java/server/core/OperationHandler.java @@ -6,7 +6,7 @@ package software.amazon.smithy.java.server.core; import java.util.concurrent.CompletableFuture; -import software.amazon.smithy.java.core.schema.ModeledApiException; +import software.amazon.smithy.java.core.error.ModeledException; import software.amazon.smithy.java.core.schema.SerializableStruct; import software.amazon.smithy.java.framework.model.InternalFailureException; import software.amazon.smithy.java.server.Operation; @@ -26,8 +26,8 @@ public CompletableFuture before(Job job) { response.whenComplete((result, error) -> { SerializableStruct output = result; if (error != null) { - ModeledApiException modeledError; - if (error instanceof ModeledApiException e) { + ModeledException modeledError; + if (error instanceof ModeledException e) { modeledError = e; } else { modeledError = InternalFailureException.builder().withCause(error).build(); @@ -43,7 +43,7 @@ public CompletableFuture before(Job job) { SerializableStruct response; try { response = (SerializableStruct) operation.function().apply(inputShape, null); - } catch (ModeledApiException e) { + } catch (ModeledException e) { job.setFailure(e); response = e; } catch (Exception e) { diff --git a/server-core/src/main/java/software/amazon/smithy/java/server/core/ServerProtocol.java b/server-core/src/main/java/software/amazon/smithy/java/server/core/ServerProtocol.java index a4ecfc5a7..5c18001e2 100644 --- a/server-core/src/main/java/software/amazon/smithy/java/server/core/ServerProtocol.java +++ b/server-core/src/main/java/software/amazon/smithy/java/server/core/ServerProtocol.java @@ -7,7 +7,7 @@ import java.util.List; import java.util.concurrent.CompletableFuture; -import software.amazon.smithy.java.core.schema.ModeledApiException; +import software.amazon.smithy.java.core.error.ModeledException; import software.amazon.smithy.java.core.schema.SerializableStruct; import software.amazon.smithy.java.core.serde.SerializationException; import software.amazon.smithy.java.framework.model.InternalFailureException; @@ -39,11 +39,11 @@ public final CompletableFuture serializeOutput(Job job, SerializableStruct public final CompletableFuture serializeError(Job job, Throwable error) { return serializeError( job, - error instanceof ModeledApiException me ? me + error instanceof ModeledException me ? me : translate(error)); } - private static ModeledApiException translate(Throwable error) { + private static ModeledException translate(Throwable error) { if (error instanceof SerializationException se) { return MalformedRequestException.builder() .withoutStackTrace() @@ -56,7 +56,7 @@ private static ModeledApiException translate(Throwable error) { protected abstract CompletableFuture serializeOutput(Job job, SerializableStruct output, boolean isError); - public final CompletableFuture serializeError(Job job, ModeledApiException error) { + public final CompletableFuture serializeError(Job job, ModeledException error) { // Check both implicit errors and operation errors to see if modeled API exception is // defined as part of service interface. Otherwise, throw generic exception. if (!job.operation().getOwningService().typeRegistry().contains(error.schema().id()) diff --git a/server-rpcv2-cbor/src/main/java/software/amazon/smithy/java/server/protocols/rpcv2/RpcV2CborProtocol.java b/server-rpcv2-cbor/src/main/java/software/amazon/smithy/java/server/protocols/rpcv2/RpcV2CborProtocol.java index ec3732caa..5bb4363bc 100644 --- a/server-rpcv2-cbor/src/main/java/software/amazon/smithy/java/server/protocols/rpcv2/RpcV2CborProtocol.java +++ b/server-rpcv2-cbor/src/main/java/software/amazon/smithy/java/server/protocols/rpcv2/RpcV2CborProtocol.java @@ -8,7 +8,7 @@ import java.util.List; import java.util.concurrent.CompletableFuture; import software.amazon.smithy.java.cbor.Rpcv2CborCodec; -import software.amazon.smithy.java.core.schema.ModeledApiException; +import software.amazon.smithy.java.core.error.ModeledException; import software.amazon.smithy.java.core.schema.SerializableStruct; import software.amazon.smithy.java.framework.model.MalformedRequestException; import software.amazon.smithy.java.framework.model.UnknownOperationException; @@ -95,7 +95,7 @@ public CompletableFuture serializeOutput(Job job, SerializableStruct outpu var httpJob = job.asHttpJob(); final int statusCode; if (isError) { - statusCode = ModeledApiException.getHttpStatusCode(output.schema()); + statusCode = ModeledException.getHttpStatusCode(output.schema()); } else { statusCode = 200; }