diff --git a/conjure-java-core/src/integrationInput/java/dialogueendpointresulttypes/com/palantir/another/EndpointSpecificErrors.java b/conjure-java-core/src/integrationInput/java/dialogueendpointresulttypes/com/palantir/another/EndpointSpecificErrors.java new file mode 100644 index 000000000..0ff5a4c08 --- /dev/null +++ b/conjure-java-core/src/integrationInput/java/dialogueendpointresulttypes/com/palantir/another/EndpointSpecificErrors.java @@ -0,0 +1,23 @@ +package dialogueendpointresulttypes.com.palantir.another; + +import com.palantir.conjure.java.api.errors.ErrorType; +import com.palantir.conjure.java.api.errors.RemoteException; +import com.palantir.logsafe.Preconditions; +import javax.annotation.processing.Generated; + +@Generated("com.palantir.conjure.java.types.ErrorGenerator") +public final class EndpointSpecificErrors { + /** An error in a different package. */ + public static final ErrorType DIFFERENT_PACKAGE = + ErrorType.create(ErrorType.Code.INTERNAL, "EndpointSpecific:DifferentPackage"); + + private EndpointSpecificErrors() {} + + /** Returns true if the {@link RemoteException} is named EndpointSpecific:DifferentPackage */ + public static boolean isDifferentPackage(RemoteException remoteException) { + Preconditions.checkNotNull(remoteException, "remote exception must not be null"); + return DIFFERENT_PACKAGE.name().equals(remoteException.getError().errorName()); + } + + public static record DifferentPackageParameters() {} +} diff --git a/conjure-java-core/src/integrationInput/java/dialogueendpointresulttypes/com/palantir/another/EndpointSpecificServerErrors.java b/conjure-java-core/src/integrationInput/java/dialogueendpointresulttypes/com/palantir/another/EndpointSpecificServerErrors.java new file mode 100644 index 000000000..d5cbde9f1 --- /dev/null +++ b/conjure-java-core/src/integrationInput/java/dialogueendpointresulttypes/com/palantir/another/EndpointSpecificServerErrors.java @@ -0,0 +1,35 @@ +package dialogueendpointresulttypes.com.palantir.another; + +import com.palantir.conjure.java.api.errors.CheckedServiceException; +import javax.annotation.Nullable; +import javax.annotation.processing.Generated; + +@Generated("com.palantir.conjure.java.types.CheckedErrorGenerator") +public final class EndpointSpecificServerErrors { + private EndpointSpecificServerErrors() {} + + public static DifferentPackage differentPackage() { + return new DifferentPackage(null); + } + + public static DifferentPackage differentPackage(@Nullable Throwable cause) { + return new DifferentPackage(cause); + } + + /** + * Throws a {@link DifferentPackage} when {@code shouldThrow} is true. + * + * @param shouldThrow Cause the method to throw when true + */ + public static void throwIfDifferentPackage(boolean shouldThrow) throws DifferentPackage { + if (shouldThrow) { + throw differentPackage(); + } + } + + public static final class DifferentPackage extends CheckedServiceException { + private DifferentPackage(@Nullable Throwable cause) { + super(EndpointSpecificErrors.DIFFERENT_PACKAGE, cause); + } + } +} diff --git a/conjure-java-core/src/integrationInput/java/dialogueendpointresulttypes/com/palantir/product/ComplicatedObject.java b/conjure-java-core/src/integrationInput/java/dialogueendpointresulttypes/com/palantir/product/ComplicatedObject.java new file mode 100644 index 000000000..ad7518fc3 --- /dev/null +++ b/conjure-java-core/src/integrationInput/java/dialogueendpointresulttypes/com/palantir/product/ComplicatedObject.java @@ -0,0 +1,167 @@ +package dialogueendpointresulttypes.com.palantir.product; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.google.errorprone.annotations.CheckReturnValue; +import com.palantir.conjure.java.lib.internal.ConjureCollections; +import com.palantir.logsafe.Preconditions; +import com.palantir.logsafe.SafeArg; +import com.palantir.logsafe.exceptions.SafeIllegalArgumentException; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.annotation.processing.Generated; + +@JsonDeserialize(builder = ComplicatedObject.Builder.class) +@Generated("com.palantir.conjure.java.types.BeanGenerator") +public final class ComplicatedObject { + private final List fieldOne; + + private final Optional fieldTwo; + + private int memoizedHashCode; + + private ComplicatedObject(List fieldOne, Optional fieldTwo) { + validateFields(fieldOne, fieldTwo); + this.fieldOne = ConjureCollections.unmodifiableList(fieldOne); + this.fieldTwo = fieldTwo; + } + + @JsonProperty("fieldOne") + public List getFieldOne() { + return this.fieldOne; + } + + @JsonProperty("fieldTwo") + public Optional getFieldTwo() { + return this.fieldTwo; + } + + @Override + public boolean equals(@Nullable Object other) { + return this == other || (other instanceof ComplicatedObject && equalTo((ComplicatedObject) other)); + } + + private boolean equalTo(ComplicatedObject other) { + if (this.memoizedHashCode != 0 + && other.memoizedHashCode != 0 + && this.memoizedHashCode != other.memoizedHashCode) { + return false; + } + return this.fieldOne.equals(other.fieldOne) && this.fieldTwo.equals(other.fieldTwo); + } + + @Override + public int hashCode() { + int result = memoizedHashCode; + if (result == 0) { + int hash = 1; + hash = 31 * hash + this.fieldOne.hashCode(); + hash = 31 * hash + this.fieldTwo.hashCode(); + result = hash; + memoizedHashCode = result; + } + return result; + } + + @Override + public String toString() { + return "ComplicatedObject{fieldOne: " + fieldOne + ", fieldTwo: " + fieldTwo + '}'; + } + + public static ComplicatedObject of(List fieldOne, StringAlias fieldTwo) { + return builder().fieldOne(fieldOne).fieldTwo(Optional.of(fieldTwo)).build(); + } + + private static void validateFields(List fieldOne, Optional fieldTwo) { + List missingFields = null; + missingFields = addFieldIfMissing(missingFields, fieldOne, "fieldOne"); + missingFields = addFieldIfMissing(missingFields, fieldTwo, "fieldTwo"); + if (missingFields != null) { + throw new SafeIllegalArgumentException( + "Some required fields have not been set", SafeArg.of("missingFields", missingFields)); + } + } + + private static List addFieldIfMissing(List prev, Object fieldValue, String fieldName) { + List missingFields = prev; + if (fieldValue == null) { + if (missingFields == null) { + missingFields = new ArrayList<>(2); + } + missingFields.add(fieldName); + } + return missingFields; + } + + public static Builder builder() { + return new Builder(); + } + + @Generated("com.palantir.conjure.java.types.BeanBuilderGenerator") + @JsonIgnoreProperties(ignoreUnknown = true) + public static final class Builder { + boolean _buildInvoked; + + private List fieldOne = ConjureCollections.newList(); + + private Optional fieldTwo = Optional.empty(); + + private Builder() {} + + public Builder from(ComplicatedObject other) { + checkNotBuilt(); + fieldOne(other.getFieldOne()); + fieldTwo(other.getFieldTwo()); + return this; + } + + @JsonSetter(value = "fieldOne", nulls = Nulls.SKIP) + public Builder fieldOne(@Nonnull Iterable fieldOne) { + checkNotBuilt(); + this.fieldOne = ConjureCollections.newList(Preconditions.checkNotNull(fieldOne, "fieldOne cannot be null")); + return this; + } + + public Builder addAllFieldOne(@Nonnull Iterable fieldOne) { + checkNotBuilt(); + ConjureCollections.addAll(this.fieldOne, Preconditions.checkNotNull(fieldOne, "fieldOne cannot be null")); + return this; + } + + public Builder fieldOne(String fieldOne) { + checkNotBuilt(); + this.fieldOne.add(fieldOne); + return this; + } + + @JsonSetter(value = "fieldTwo", nulls = Nulls.SKIP) + public Builder fieldTwo(@Nonnull Optional fieldTwo) { + checkNotBuilt(); + this.fieldTwo = Preconditions.checkNotNull(fieldTwo, "fieldTwo cannot be null"); + return this; + } + + public Builder fieldTwo(@Nonnull StringAlias fieldTwo) { + checkNotBuilt(); + this.fieldTwo = Optional.of(Preconditions.checkNotNull(fieldTwo, "fieldTwo cannot be null")); + return this; + } + + @CheckReturnValue + public ComplicatedObject build() { + checkNotBuilt(); + this._buildInvoked = true; + return new ComplicatedObject(fieldOne, fieldTwo); + } + + private void checkNotBuilt() { + Preconditions.checkState(!_buildInvoked, "Build has already been called"); + } + } +} diff --git a/conjure-java-core/src/integrationInput/java/dialogueendpointresulttypes/com/palantir/product/DialogueErrorEndpoints.java b/conjure-java-core/src/integrationInput/java/dialogueendpointresulttypes/com/palantir/product/DialogueErrorEndpoints.java new file mode 100644 index 000000000..501f547c9 --- /dev/null +++ b/conjure-java-core/src/integrationInput/java/dialogueendpointresulttypes/com/palantir/product/DialogueErrorEndpoints.java @@ -0,0 +1,198 @@ +package dialogueendpointresulttypes.com.palantir.product; + +import com.google.common.collect.ListMultimap; +import com.palantir.dialogue.Endpoint; +import com.palantir.dialogue.HttpMethod; +import com.palantir.dialogue.PathTemplate; +import com.palantir.dialogue.UrlBuilder; +import java.lang.Override; +import java.lang.String; +import java.util.Optional; +import javax.annotation.processing.Generated; + +@Generated("com.palantir.conjure.java.services.dialogue.DialogueEndpointsGenerator") +enum DialogueErrorEndpoints implements Endpoint { + testBasicError { + private final PathTemplate pathTemplate = + PathTemplate.builder().fixed("errors").fixed("basic").build(); + + @Override + public void renderPath(ListMultimap params, UrlBuilder url) { + pathTemplate.fill(params, url); + } + + @Override + public HttpMethod httpMethod() { + return HttpMethod.POST; + } + + @Override + public String serviceName() { + return "ErrorService"; + } + + @Override + public String endpointName() { + return "testBasicError"; + } + + @Override + public String version() { + return VERSION; + } + }, + + testImportedError { + private final PathTemplate pathTemplate = + PathTemplate.builder().fixed("errors").fixed("imported").build(); + + @Override + public void renderPath(ListMultimap params, UrlBuilder url) { + pathTemplate.fill(params, url); + } + + @Override + public HttpMethod httpMethod() { + return HttpMethod.POST; + } + + @Override + public String serviceName() { + return "ErrorService"; + } + + @Override + public String endpointName() { + return "testImportedError"; + } + + @Override + public String version() { + return VERSION; + } + }, + + testMultipleErrorsAndPackages { + private final PathTemplate pathTemplate = + PathTemplate.builder().fixed("errors").fixed("multiple").build(); + + @Override + public void renderPath(ListMultimap params, UrlBuilder url) { + pathTemplate.fill(params, url); + } + + @Override + public HttpMethod httpMethod() { + return HttpMethod.POST; + } + + @Override + public String serviceName() { + return "ErrorService"; + } + + @Override + public String endpointName() { + return "testMultipleErrorsAndPackages"; + } + + @Override + public String version() { + return VERSION; + } + }, + + testEmptyBody { + private final PathTemplate pathTemplate = + PathTemplate.builder().fixed("errors").fixed("empty").build(); + + @Override + public void renderPath(ListMultimap params, UrlBuilder url) { + pathTemplate.fill(params, url); + } + + @Override + public HttpMethod httpMethod() { + return HttpMethod.POST; + } + + @Override + public String serviceName() { + return "ErrorService"; + } + + @Override + public String endpointName() { + return "testEmptyBody"; + } + + @Override + public String version() { + return VERSION; + } + }, + + testBinary { + private final PathTemplate pathTemplate = + PathTemplate.builder().fixed("errors").fixed("binary").build(); + + @Override + public void renderPath(ListMultimap params, UrlBuilder url) { + pathTemplate.fill(params, url); + } + + @Override + public HttpMethod httpMethod() { + return HttpMethod.POST; + } + + @Override + public String serviceName() { + return "ErrorService"; + } + + @Override + public String endpointName() { + return "testBinary"; + } + + @Override + public String version() { + return VERSION; + } + }, + + testOptionalBinary { + private final PathTemplate pathTemplate = + PathTemplate.builder().fixed("errors").fixed("optional-binary").build(); + + @Override + public void renderPath(ListMultimap params, UrlBuilder url) { + pathTemplate.fill(params, url); + } + + @Override + public HttpMethod httpMethod() { + return HttpMethod.POST; + } + + @Override + public String serviceName() { + return "ErrorService"; + } + + @Override + public String endpointName() { + return "testOptionalBinary"; + } + + @Override + public String version() { + return VERSION; + } + }; + + private static final String VERSION = Optional.ofNullable( + DialogueErrorEndpoints.class.getPackage().getImplementationVersion()) + .orElse("0.0.0"); +} diff --git a/conjure-java-core/src/integrationInput/java/dialogueendpointresulttypes/com/palantir/product/EndpointSpecificErrors.java b/conjure-java-core/src/integrationInput/java/dialogueendpointresulttypes/com/palantir/product/EndpointSpecificErrors.java new file mode 100644 index 000000000..786773769 --- /dev/null +++ b/conjure-java-core/src/integrationInput/java/dialogueendpointresulttypes/com/palantir/product/EndpointSpecificErrors.java @@ -0,0 +1,27 @@ +package dialogueendpointresulttypes.com.palantir.product; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.palantir.conjure.java.api.errors.ErrorType; +import com.palantir.conjure.java.api.errors.RemoteException; +import com.palantir.logsafe.Preconditions; +import com.palantir.logsafe.Safe; +import com.palantir.logsafe.Unsafe; +import javax.annotation.processing.Generated; + +@Generated("com.palantir.conjure.java.types.ErrorGenerator") +public final class EndpointSpecificErrors { + /** Docs for an endpoint error. */ + public static final ErrorType ENDPOINT_ERROR = + ErrorType.create(ErrorType.Code.INVALID_ARGUMENT, "EndpointSpecific:EndpointError"); + + private EndpointSpecificErrors() {} + + /** Returns true if the {@link RemoteException} is named EndpointSpecific:EndpointError */ + public static boolean isEndpointError(RemoteException remoteException) { + Preconditions.checkNotNull(remoteException, "remote exception must not be null"); + return ENDPOINT_ERROR.name().equals(remoteException.getError().errorName()); + } + + public static record EndpointErrorParameters( + @JsonProperty("typeName") @Safe String typeName, @JsonProperty("typeDef") @Unsafe Object typeDef) {} +} diff --git a/conjure-java-core/src/integrationInput/java/dialogueendpointresulttypes/com/palantir/product/EndpointSpecificServerErrors.java b/conjure-java-core/src/integrationInput/java/dialogueendpointresulttypes/com/palantir/product/EndpointSpecificServerErrors.java new file mode 100644 index 000000000..ea02501d1 --- /dev/null +++ b/conjure-java-core/src/integrationInput/java/dialogueendpointresulttypes/com/palantir/product/EndpointSpecificServerErrors.java @@ -0,0 +1,47 @@ +package dialogueendpointresulttypes.com.palantir.product; + +import com.palantir.conjure.java.api.errors.CheckedServiceException; +import com.palantir.logsafe.Safe; +import com.palantir.logsafe.SafeArg; +import com.palantir.logsafe.Unsafe; +import com.palantir.logsafe.UnsafeArg; +import javax.annotation.Nullable; +import javax.annotation.processing.Generated; + +@Generated("com.palantir.conjure.java.types.CheckedErrorGenerator") +public final class EndpointSpecificServerErrors { + private EndpointSpecificServerErrors() {} + + public static EndpointError endpointError(@Safe String typeName, @Unsafe Object typeDef) { + return new EndpointError(typeName, typeDef, null); + } + + public static EndpointError endpointError( + @Safe String typeName, @Unsafe Object typeDef, @Nullable Throwable cause) { + return new EndpointError(typeName, typeDef, cause); + } + + /** + * Throws a {@link EndpointError} when {@code shouldThrow} is true. + * + * @param shouldThrow Cause the method to throw when true + * @param typeName + * @param typeDef + */ + public static void throwIfEndpointError(boolean shouldThrow, @Safe String typeName, @Unsafe Object typeDef) + throws EndpointError { + if (shouldThrow) { + throw endpointError(typeName, typeDef); + } + } + + public static final class EndpointError extends CheckedServiceException { + private EndpointError(@Safe String typeName, @Unsafe Object typeDef, @Nullable Throwable cause) { + super( + EndpointSpecificErrors.ENDPOINT_ERROR, + cause, + SafeArg.of("typeName", typeName), + UnsafeArg.of("typeDef", typeDef)); + } + } +} diff --git a/conjure-java-core/src/integrationInput/java/dialogueendpointresulttypes/com/palantir/product/EndpointSpecificTwoErrors.java b/conjure-java-core/src/integrationInput/java/dialogueendpointresulttypes/com/palantir/product/EndpointSpecificTwoErrors.java new file mode 100644 index 000000000..0672bbd3e --- /dev/null +++ b/conjure-java-core/src/integrationInput/java/dialogueendpointresulttypes/com/palantir/product/EndpointSpecificTwoErrors.java @@ -0,0 +1,23 @@ +package dialogueendpointresulttypes.com.palantir.product; + +import com.palantir.conjure.java.api.errors.ErrorType; +import com.palantir.conjure.java.api.errors.RemoteException; +import com.palantir.logsafe.Preconditions; +import javax.annotation.processing.Generated; + +@Generated("com.palantir.conjure.java.types.ErrorGenerator") +public final class EndpointSpecificTwoErrors { + /** An error in a different namespace. */ + public static final ErrorType DIFFERENT_NAMESPACE = + ErrorType.create(ErrorType.Code.INTERNAL, "EndpointSpecificTwo:DifferentNamespace"); + + private EndpointSpecificTwoErrors() {} + + /** Returns true if the {@link RemoteException} is named EndpointSpecificTwo:DifferentNamespace */ + public static boolean isDifferentNamespace(RemoteException remoteException) { + Preconditions.checkNotNull(remoteException, "remote exception must not be null"); + return DIFFERENT_NAMESPACE.name().equals(remoteException.getError().errorName()); + } + + public static record DifferentNamespaceParameters() {} +} diff --git a/conjure-java-core/src/integrationInput/java/dialogueendpointresulttypes/com/palantir/product/EndpointSpecificTwoServerErrors.java b/conjure-java-core/src/integrationInput/java/dialogueendpointresulttypes/com/palantir/product/EndpointSpecificTwoServerErrors.java new file mode 100644 index 000000000..7b1bb5e0d --- /dev/null +++ b/conjure-java-core/src/integrationInput/java/dialogueendpointresulttypes/com/palantir/product/EndpointSpecificTwoServerErrors.java @@ -0,0 +1,35 @@ +package dialogueendpointresulttypes.com.palantir.product; + +import com.palantir.conjure.java.api.errors.CheckedServiceException; +import javax.annotation.Nullable; +import javax.annotation.processing.Generated; + +@Generated("com.palantir.conjure.java.types.CheckedErrorGenerator") +public final class EndpointSpecificTwoServerErrors { + private EndpointSpecificTwoServerErrors() {} + + public static DifferentNamespace differentNamespace() { + return new DifferentNamespace(null); + } + + public static DifferentNamespace differentNamespace(@Nullable Throwable cause) { + return new DifferentNamespace(cause); + } + + /** + * Throws a {@link DifferentNamespace} when {@code shouldThrow} is true. + * + * @param shouldThrow Cause the method to throw when true + */ + public static void throwIfDifferentNamespace(boolean shouldThrow) throws DifferentNamespace { + if (shouldThrow) { + throw differentNamespace(); + } + } + + public static final class DifferentNamespace extends CheckedServiceException { + private DifferentNamespace(@Nullable Throwable cause) { + super(EndpointSpecificTwoErrors.DIFFERENT_NAMESPACE, cause); + } + } +} diff --git a/conjure-java-core/src/integrationInput/java/dialogueendpointresulttypes/com/palantir/product/ErrorService.java b/conjure-java-core/src/integrationInput/java/dialogueendpointresulttypes/com/palantir/product/ErrorService.java new file mode 100644 index 000000000..98ce72748 --- /dev/null +++ b/conjure-java-core/src/integrationInput/java/dialogueendpointresulttypes/com/palantir/product/ErrorService.java @@ -0,0 +1,56 @@ +package dialogueendpointresulttypes.com.palantir.product; + +import com.palantir.conjure.java.undertow.lib.BinaryResponseBody; +import com.palantir.tokens.auth.AuthHeader; +import java.util.Optional; +import javax.annotation.processing.Generated; + +@Generated("com.palantir.conjure.java.services.UndertowServiceInterfaceGenerator") +public interface ErrorService { + /** + * @apiNote {@code POST /errors/basic} + * @throws TestServerErrors.InvalidArgument + */ + String testBasicError(AuthHeader authHeader, boolean shouldThrowError) throws TestServerErrors.InvalidArgument; + + /** + * @apiNote {@code POST /errors/imported} + * @throws EndpointSpecificServerErrors.EndpointError + */ + String testImportedError(AuthHeader authHeader, boolean shouldThrowError) + throws EndpointSpecificServerErrors.EndpointError; + + /** + * @apiNote {@code POST /errors/multiple} + * @throws TestServerErrors.InvalidArgument + * @throws TestServerErrors.NotFound Something was not found. + * @throws EndpointSpecificTwoServerErrors.DifferentNamespace + * @throws dialogueendpointresulttypes.com.palantir.another.EndpointSpecificServerErrors.DifferentPackage + * @throws TestServerErrors.ComplicatedParameters + */ + String testMultipleErrorsAndPackages(AuthHeader authHeader, Optional errorToThrow) + throws TestServerErrors.InvalidArgument, TestServerErrors.NotFound, + EndpointSpecificTwoServerErrors.DifferentNamespace, + dialogueendpointresulttypes.com.palantir.another.EndpointSpecificServerErrors.DifferentPackage, + TestServerErrors.ComplicatedParameters; + + /** + * @apiNote {@code POST /errors/empty} + * @throws TestServerErrors.InvalidArgument + */ + void testEmptyBody(AuthHeader authHeader, boolean shouldThrowError) throws TestServerErrors.InvalidArgument; + + /** + * @apiNote {@code POST /errors/binary} + * @throws TestServerErrors.InvalidArgument + */ + BinaryResponseBody testBinary(AuthHeader authHeader, boolean shouldThrowError) + throws TestServerErrors.InvalidArgument; + + /** + * @apiNote {@code POST /errors/optional-binary} + * @throws TestServerErrors.InvalidArgument + */ + Optional testOptionalBinary(AuthHeader authHeader, OptionalBinaryResponseMode mode) + throws TestServerErrors.InvalidArgument; +} diff --git a/conjure-java-core/src/integrationInput/java/dialogueendpointresulttypes/com/palantir/product/ErrorServiceAsync.java b/conjure-java-core/src/integrationInput/java/dialogueendpointresulttypes/com/palantir/product/ErrorServiceAsync.java new file mode 100644 index 000000000..54ba9f710 --- /dev/null +++ b/conjure-java-core/src/integrationInput/java/dialogueendpointresulttypes/com/palantir/product/ErrorServiceAsync.java @@ -0,0 +1,466 @@ +package dialogueendpointresulttypes.com.palantir.product; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonValue; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.errorprone.annotations.CheckReturnValue; +import com.google.errorprone.annotations.MustBeClosed; +import com.palantir.conjure.java.lib.internal.ClientEndpoint; +import com.palantir.dialogue.Channel; +import com.palantir.dialogue.ConjureRuntime; +import com.palantir.dialogue.Deserializer; +import com.palantir.dialogue.DeserializerArgs; +import com.palantir.dialogue.DialogueService; +import com.palantir.dialogue.DialogueServiceFactory; +import com.palantir.dialogue.Endpoint; +import com.palantir.dialogue.EndpointChannel; +import com.palantir.dialogue.EndpointChannelFactory; +import com.palantir.dialogue.EndpointError; +import com.palantir.dialogue.PlainSerDe; +import com.palantir.dialogue.Request; +import com.palantir.dialogue.Serializer; +import com.palantir.dialogue.TypeMarker; +import com.palantir.logsafe.Preconditions; +import com.palantir.logsafe.Safe; +import com.palantir.tokens.auth.AuthHeader; +import java.io.Closeable; +import java.io.IOException; +import java.io.InputStream; +import java.lang.Boolean; +import java.lang.Override; +import java.lang.String; +import java.util.Optional; +import javax.annotation.processing.Generated; + +@Generated("com.palantir.conjure.java.services.dialogue.DialogueInterfaceGenerator") +@DialogueService(ErrorServiceAsync.Factory.class) +public interface ErrorServiceAsync { + /** @apiNote {@code POST /errors/basic} */ + @ClientEndpoint(method = "POST", path = "/errors/basic") + @CheckReturnValue + ListenableFuture testBasicError(AuthHeader authHeader, boolean shouldThrowError); + + /** @apiNote {@code POST /errors/imported} */ + @ClientEndpoint(method = "POST", path = "/errors/imported") + @CheckReturnValue + ListenableFuture testImportedError(AuthHeader authHeader, boolean shouldThrowError); + + /** @apiNote {@code POST /errors/multiple} */ + @ClientEndpoint(method = "POST", path = "/errors/multiple") + @CheckReturnValue + ListenableFuture testMultipleErrorsAndPackages( + AuthHeader authHeader, Optional errorToThrow); + + /** @apiNote {@code POST /errors/empty} */ + @ClientEndpoint(method = "POST", path = "/errors/empty") + @CheckReturnValue + ListenableFuture testEmptyBody(AuthHeader authHeader, boolean shouldThrowError); + + /** @apiNote {@code POST /errors/binary} */ + @ClientEndpoint(method = "POST", path = "/errors/binary") + @CheckReturnValue + ListenableFuture testBinary(AuthHeader authHeader, boolean shouldThrowError); + + /** @apiNote {@code POST /errors/optional-binary} */ + @ClientEndpoint(method = "POST", path = "/errors/optional-binary") + @CheckReturnValue + ListenableFuture testOptionalBinary( + AuthHeader authHeader, OptionalBinaryResponseMode mode); + + /** Creates an asynchronous/non-blocking client for a ErrorService service. */ + static ErrorServiceAsync of(EndpointChannelFactory _endpointChannelFactory, ConjureRuntime _runtime) { + return new ErrorServiceAsync() { + private final PlainSerDe _plainSerDe = _runtime.plainSerDe(); + + private final Serializer testBasicErrorSerializer = + _runtime.bodySerDe().serializer(new TypeMarker() {}); + + private final EndpointChannel testBasicErrorChannel = + _endpointChannelFactory.endpoint(DialogueErrorEndpoints.testBasicError); + + private final Deserializer testBasicErrorDeserializer = _runtime.bodySerDe() + .deserializer(DeserializerArgs.builder() + .baseType(new TypeMarker<>() {}) + .success(new TypeMarker() {}) + .error( + TestErrors.INVALID_ARGUMENT.name(), + new TypeMarker() {}) + .build()); + + private final Serializer testImportedErrorSerializer = + _runtime.bodySerDe().serializer(new TypeMarker() {}); + + private final EndpointChannel testImportedErrorChannel = + _endpointChannelFactory.endpoint(DialogueErrorEndpoints.testImportedError); + + private final Deserializer testImportedErrorDeserializer = _runtime.bodySerDe() + .deserializer(DeserializerArgs.builder() + .baseType(new TypeMarker<>() {}) + .success(new TypeMarker() {}) + .error( + EndpointSpecificErrors.ENDPOINT_ERROR.name(), + new TypeMarker() {}) + .build()); + + private final Serializer> testMultipleErrorsAndPackagesSerializer = + _runtime.bodySerDe().serializer(new TypeMarker>() {}); + + private final EndpointChannel testMultipleErrorsAndPackagesChannel = + _endpointChannelFactory.endpoint(DialogueErrorEndpoints.testMultipleErrorsAndPackages); + + private final Deserializer + testMultipleErrorsAndPackagesDeserializer = _runtime.bodySerDe() + .deserializer(DeserializerArgs.builder() + .baseType(new TypeMarker<>() {}) + .success(new TypeMarker() {}) + .error( + TestErrors.INVALID_ARGUMENT.name(), + new TypeMarker() {}) + .error( + TestErrors.NOT_FOUND.name(), + new TypeMarker() {}) + .error( + EndpointSpecificTwoErrors.DIFFERENT_NAMESPACE.name(), + new TypeMarker< + TestMultipleErrorsAndPackagesResponse.DifferentNamespace>() {}) + .error( + dialogueendpointresulttypes.com.palantir.another.EndpointSpecificErrors + .DIFFERENT_PACKAGE + .name(), + new TypeMarker() {}) + .error( + TestErrors.COMPLICATED_PARAMETERS.name(), + new TypeMarker< + TestMultipleErrorsAndPackagesResponse.ComplicatedParameters>() {}) + .build()); + + private final Serializer testEmptyBodySerializer = + _runtime.bodySerDe().serializer(new TypeMarker() {}); + + private final EndpointChannel testEmptyBodyChannel = + _endpointChannelFactory.endpoint(DialogueErrorEndpoints.testEmptyBody); + + private final Deserializer testEmptyBodyDeserializer = _runtime.bodySerDe() + .deserializer(DeserializerArgs.builder() + .baseType(new TypeMarker<>() {}) + .success(new TypeMarker() {}) + .error( + TestErrors.INVALID_ARGUMENT.name(), + new TypeMarker() {}) + .build()); + + private final Serializer testBinarySerializer = + _runtime.bodySerDe().serializer(new TypeMarker() {}); + + private final EndpointChannel testBinaryChannel = + _endpointChannelFactory.endpoint(DialogueErrorEndpoints.testBinary); + + private final Deserializer testBinaryDeserializer = _runtime.bodySerDe() + .inputStreamDeserializer(DeserializerArgs.builder() + .baseType(new TypeMarker<>() {}) + .success(new TypeMarker() {}) + .error( + TestErrors.INVALID_ARGUMENT.name(), + new TypeMarker() {}) + .build()); + + private final Serializer testOptionalBinarySerializer = + _runtime.bodySerDe().serializer(new TypeMarker() {}); + + private final EndpointChannel testOptionalBinaryChannel = + _endpointChannelFactory.endpoint(DialogueErrorEndpoints.testOptionalBinary); + + private final Deserializer testOptionalBinaryDeserializer = _runtime.bodySerDe() + .optionalInputStreamDeserializer(DeserializerArgs.builder() + .baseType(new TypeMarker<>() {}) + .success(new TypeMarker() {}) + .error( + TestErrors.INVALID_ARGUMENT.name(), + new TypeMarker() {}) + .build()); + + @Override + public ListenableFuture testBasicError( + AuthHeader authHeader, boolean shouldThrowError) { + Request.Builder _request = Request.builder(); + _request.putHeaderParams("Authorization", authHeader.toString()); + _request.body(testBasicErrorSerializer.serialize(shouldThrowError)); + return _runtime.clients().call(testBasicErrorChannel, _request.build(), testBasicErrorDeserializer); + } + + @Override + public ListenableFuture testImportedError( + AuthHeader authHeader, boolean shouldThrowError) { + Request.Builder _request = Request.builder(); + _request.putHeaderParams("Authorization", authHeader.toString()); + _request.body(testImportedErrorSerializer.serialize(shouldThrowError)); + return _runtime.clients() + .call(testImportedErrorChannel, _request.build(), testImportedErrorDeserializer); + } + + @Override + public ListenableFuture testMultipleErrorsAndPackages( + AuthHeader authHeader, Optional errorToThrow) { + Request.Builder _request = Request.builder(); + _request.putHeaderParams("Authorization", authHeader.toString()); + _request.body(testMultipleErrorsAndPackagesSerializer.serialize(errorToThrow)); + return _runtime.clients() + .call( + testMultipleErrorsAndPackagesChannel, + _request.build(), + testMultipleErrorsAndPackagesDeserializer); + } + + @Override + public ListenableFuture testEmptyBody( + AuthHeader authHeader, boolean shouldThrowError) { + Request.Builder _request = Request.builder(); + _request.putHeaderParams("Authorization", authHeader.toString()); + _request.body(testEmptyBodySerializer.serialize(shouldThrowError)); + return _runtime.clients().call(testEmptyBodyChannel, _request.build(), testEmptyBodyDeserializer); + } + + @Override + public ListenableFuture testBinary(AuthHeader authHeader, boolean shouldThrowError) { + Request.Builder _request = Request.builder(); + _request.putHeaderParams("Authorization", authHeader.toString()); + _request.body(testBinarySerializer.serialize(shouldThrowError)); + return _runtime.clients().call(testBinaryChannel, _request.build(), testBinaryDeserializer); + } + + @Override + public ListenableFuture testOptionalBinary( + AuthHeader authHeader, OptionalBinaryResponseMode mode) { + Request.Builder _request = Request.builder(); + _request.putHeaderParams("Authorization", authHeader.toString()); + _request.body(testOptionalBinarySerializer.serialize(mode)); + return _runtime.clients() + .call(testOptionalBinaryChannel, _request.build(), testOptionalBinaryDeserializer); + } + + @Override + public String toString() { + return "ErrorServiceAsync{_endpointChannelFactory=" + _endpointChannelFactory + ", runtime=" + _runtime + + '}'; + } + }; + } + + /** Creates an asynchronous/non-blocking client for a ErrorService service. */ + static ErrorServiceAsync of(Channel _channel, ConjureRuntime _runtime) { + if (_channel instanceof EndpointChannelFactory) { + return of((EndpointChannelFactory) _channel, _runtime); + } + return of( + new EndpointChannelFactory() { + @Override + public EndpointChannel endpoint(Endpoint endpoint) { + return _runtime.clients().bind(_channel, endpoint); + } + }, + _runtime); + } + + final class Factory implements DialogueServiceFactory { + @Override + public ErrorServiceAsync create(EndpointChannelFactory endpointChannelFactory, ConjureRuntime runtime) { + return ErrorServiceAsync.of(endpointChannelFactory, runtime); + } + } + + sealed interface TestBasicErrorResponse + permits TestBasicErrorResponse.Success, TestBasicErrorResponse.InvalidArgument { + record Success(@JsonValue String value) implements TestBasicErrorResponse { + public Success { + Preconditions.checkArgumentNotNull(value, "value cannot be null"); + } + } + + final class InvalidArgument extends EndpointError + implements TestBasicErrorResponse { + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) + InvalidArgument( + @JsonProperty("errorCode") @Safe String errorCode, + @JsonProperty("errorInstanceId") @Safe String errorInstanceId, + @JsonProperty("parameters") TestErrors.InvalidArgumentParameters parameters) { + super(errorCode, TestErrors.INVALID_ARGUMENT.name(), errorInstanceId, parameters); + } + } + } + + sealed interface TestImportedErrorResponse + permits TestImportedErrorResponse.Success, TestImportedErrorResponse.EndpointError { + record Success(@JsonValue String value) implements TestImportedErrorResponse { + public Success { + Preconditions.checkArgumentNotNull(value, "value cannot be null"); + } + } + + final class EndpointError + extends com.palantir.dialogue.EndpointError + implements TestImportedErrorResponse { + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) + EndpointError( + @JsonProperty("errorCode") @Safe String errorCode, + @JsonProperty("errorInstanceId") @Safe String errorInstanceId, + @JsonProperty("parameters") EndpointSpecificErrors.EndpointErrorParameters parameters) { + super(errorCode, EndpointSpecificErrors.ENDPOINT_ERROR.name(), errorInstanceId, parameters); + } + } + } + + sealed interface TestMultipleErrorsAndPackagesResponse + permits TestMultipleErrorsAndPackagesResponse.Success, + TestMultipleErrorsAndPackagesResponse.InvalidArgument, + TestMultipleErrorsAndPackagesResponse.NotFound, + TestMultipleErrorsAndPackagesResponse.DifferentNamespace, + TestMultipleErrorsAndPackagesResponse.DifferentPackage, + TestMultipleErrorsAndPackagesResponse.ComplicatedParameters { + record Success(@JsonValue String value) implements TestMultipleErrorsAndPackagesResponse { + public Success { + Preconditions.checkArgumentNotNull(value, "value cannot be null"); + } + } + + final class InvalidArgument extends EndpointError + implements TestMultipleErrorsAndPackagesResponse { + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) + InvalidArgument( + @JsonProperty("errorCode") @Safe String errorCode, + @JsonProperty("errorInstanceId") @Safe String errorInstanceId, + @JsonProperty("parameters") TestErrors.InvalidArgumentParameters parameters) { + super(errorCode, TestErrors.INVALID_ARGUMENT.name(), errorInstanceId, parameters); + } + } + + final class NotFound extends EndpointError + implements TestMultipleErrorsAndPackagesResponse { + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) + NotFound( + @JsonProperty("errorCode") @Safe String errorCode, + @JsonProperty("errorInstanceId") @Safe String errorInstanceId, + @JsonProperty("parameters") TestErrors.NotFoundParameters parameters) { + super(errorCode, TestErrors.NOT_FOUND.name(), errorInstanceId, parameters); + } + } + + final class DifferentNamespace extends EndpointError + implements TestMultipleErrorsAndPackagesResponse { + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) + DifferentNamespace( + @JsonProperty("errorCode") @Safe String errorCode, + @JsonProperty("errorInstanceId") @Safe String errorInstanceId) { + super( + errorCode, + EndpointSpecificTwoErrors.DIFFERENT_NAMESPACE.name(), + errorInstanceId, + new EndpointSpecificTwoErrors.DifferentNamespaceParameters()); + } + } + + final class DifferentPackage + extends EndpointError< + dialogueendpointresulttypes.com.palantir.another.EndpointSpecificErrors + .DifferentPackageParameters> + implements TestMultipleErrorsAndPackagesResponse { + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) + DifferentPackage( + @JsonProperty("errorCode") @Safe String errorCode, + @JsonProperty("errorInstanceId") @Safe String errorInstanceId) { + super( + errorCode, + dialogueendpointresulttypes.com.palantir.another.EndpointSpecificErrors.DIFFERENT_PACKAGE + .name(), + errorInstanceId, + new dialogueendpointresulttypes.com.palantir.another.EndpointSpecificErrors + .DifferentPackageParameters()); + } + } + + final class ComplicatedParameters extends EndpointError + implements TestMultipleErrorsAndPackagesResponse { + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) + ComplicatedParameters( + @JsonProperty("errorCode") @Safe String errorCode, + @JsonProperty("errorInstanceId") @Safe String errorInstanceId, + @JsonProperty("parameters") TestErrors.ComplicatedParametersParameters parameters) { + super(errorCode, TestErrors.COMPLICATED_PARAMETERS.name(), errorInstanceId, parameters); + } + } + } + + sealed interface TestEmptyBodyResponse + permits TestEmptyBodyResponse.Success, TestEmptyBodyResponse.InvalidArgument { + record Success() implements TestEmptyBodyResponse {} + + final class InvalidArgument extends EndpointError + implements TestEmptyBodyResponse { + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) + InvalidArgument( + @JsonProperty("errorCode") @Safe String errorCode, + @JsonProperty("errorInstanceId") @Safe String errorInstanceId, + @JsonProperty("parameters") TestErrors.InvalidArgumentParameters parameters) { + super(errorCode, TestErrors.INVALID_ARGUMENT.name(), errorInstanceId, parameters); + } + } + } + + sealed interface TestBinaryResponse extends Closeable + permits TestBinaryResponse.Success, TestBinaryResponse.InvalidArgument { + @Override + default void close() throws IOException {} + + record Success(@MustBeClosed @JsonValue InputStream value) implements TestBinaryResponse { + public Success { + Preconditions.checkArgumentNotNull(value, "value cannot be null"); + } + + @Override + public void close() throws IOException { + value.close(); + } + } + + final class InvalidArgument extends EndpointError + implements TestBinaryResponse { + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) + InvalidArgument( + @JsonProperty("errorCode") @Safe String errorCode, + @JsonProperty("errorInstanceId") @Safe String errorInstanceId, + @JsonProperty("parameters") TestErrors.InvalidArgumentParameters parameters) { + super(errorCode, TestErrors.INVALID_ARGUMENT.name(), errorInstanceId, parameters); + } + } + } + + sealed interface TestOptionalBinaryResponse extends Closeable + permits TestOptionalBinaryResponse.Success, TestOptionalBinaryResponse.InvalidArgument { + @Override + default void close() throws IOException {} + + record Success(@JsonValue Optional value) implements TestOptionalBinaryResponse { + public Success { + Preconditions.checkArgumentNotNull(value, "value cannot be null"); + } + + @Override + public void close() throws IOException { + if (value.isPresent()) { + value.get().close(); + } + } + } + + final class InvalidArgument extends EndpointError + implements TestOptionalBinaryResponse { + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) + InvalidArgument( + @JsonProperty("errorCode") @Safe String errorCode, + @JsonProperty("errorInstanceId") @Safe String errorInstanceId, + @JsonProperty("parameters") TestErrors.InvalidArgumentParameters parameters) { + super(errorCode, TestErrors.INVALID_ARGUMENT.name(), errorInstanceId, parameters); + } + } + } +} diff --git a/conjure-java-core/src/integrationInput/java/dialogueendpointresulttypes/com/palantir/product/ErrorServiceBlocking.java b/conjure-java-core/src/integrationInput/java/dialogueendpointresulttypes/com/palantir/product/ErrorServiceBlocking.java new file mode 100644 index 000000000..3ca2dd684 --- /dev/null +++ b/conjure-java-core/src/integrationInput/java/dialogueendpointresulttypes/com/palantir/product/ErrorServiceBlocking.java @@ -0,0 +1,463 @@ +package dialogueendpointresulttypes.com.palantir.product; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonValue; +import com.google.errorprone.annotations.CheckReturnValue; +import com.google.errorprone.annotations.MustBeClosed; +import com.palantir.conjure.java.lib.internal.ClientEndpoint; +import com.palantir.dialogue.Channel; +import com.palantir.dialogue.ConjureRuntime; +import com.palantir.dialogue.Deserializer; +import com.palantir.dialogue.DeserializerArgs; +import com.palantir.dialogue.DialogueService; +import com.palantir.dialogue.DialogueServiceFactory; +import com.palantir.dialogue.Endpoint; +import com.palantir.dialogue.EndpointChannel; +import com.palantir.dialogue.EndpointChannelFactory; +import com.palantir.dialogue.EndpointError; +import com.palantir.dialogue.PlainSerDe; +import com.palantir.dialogue.Request; +import com.palantir.dialogue.Serializer; +import com.palantir.dialogue.TypeMarker; +import com.palantir.logsafe.Preconditions; +import com.palantir.logsafe.Safe; +import com.palantir.tokens.auth.AuthHeader; +import java.io.Closeable; +import java.io.IOException; +import java.io.InputStream; +import java.lang.Boolean; +import java.lang.Override; +import java.lang.String; +import java.util.Optional; +import javax.annotation.processing.Generated; + +@Generated("com.palantir.conjure.java.services.dialogue.DialogueInterfaceGenerator") +@DialogueService(ErrorServiceBlocking.Factory.class) +public interface ErrorServiceBlocking { + /** @apiNote {@code POST /errors/basic} */ + @ClientEndpoint(method = "POST", path = "/errors/basic") + @CheckReturnValue + TestBasicErrorResponse testBasicError(AuthHeader authHeader, boolean shouldThrowError); + + /** @apiNote {@code POST /errors/imported} */ + @ClientEndpoint(method = "POST", path = "/errors/imported") + @CheckReturnValue + TestImportedErrorResponse testImportedError(AuthHeader authHeader, boolean shouldThrowError); + + /** @apiNote {@code POST /errors/multiple} */ + @ClientEndpoint(method = "POST", path = "/errors/multiple") + @CheckReturnValue + TestMultipleErrorsAndPackagesResponse testMultipleErrorsAndPackages( + AuthHeader authHeader, Optional errorToThrow); + + /** @apiNote {@code POST /errors/empty} */ + @ClientEndpoint(method = "POST", path = "/errors/empty") + @CheckReturnValue + TestEmptyBodyResponse testEmptyBody(AuthHeader authHeader, boolean shouldThrowError); + + /** @apiNote {@code POST /errors/binary} */ + @ClientEndpoint(method = "POST", path = "/errors/binary") + @CheckReturnValue + TestBinaryResponse testBinary(AuthHeader authHeader, boolean shouldThrowError); + + /** @apiNote {@code POST /errors/optional-binary} */ + @ClientEndpoint(method = "POST", path = "/errors/optional-binary") + @CheckReturnValue + TestOptionalBinaryResponse testOptionalBinary(AuthHeader authHeader, OptionalBinaryResponseMode mode); + + /** Creates a synchronous/blocking client for a ErrorService service. */ + static ErrorServiceBlocking of(EndpointChannelFactory _endpointChannelFactory, ConjureRuntime _runtime) { + return new ErrorServiceBlocking() { + private final PlainSerDe _plainSerDe = _runtime.plainSerDe(); + + private final Serializer testBasicErrorSerializer = + _runtime.bodySerDe().serializer(new TypeMarker() {}); + + private final EndpointChannel testBasicErrorChannel = + _endpointChannelFactory.endpoint(DialogueErrorEndpoints.testBasicError); + + private final Deserializer testBasicErrorDeserializer = _runtime.bodySerDe() + .deserializer(DeserializerArgs.builder() + .baseType(new TypeMarker<>() {}) + .success(new TypeMarker() {}) + .error( + TestErrors.INVALID_ARGUMENT.name(), + new TypeMarker() {}) + .build()); + + private final Serializer testImportedErrorSerializer = + _runtime.bodySerDe().serializer(new TypeMarker() {}); + + private final EndpointChannel testImportedErrorChannel = + _endpointChannelFactory.endpoint(DialogueErrorEndpoints.testImportedError); + + private final Deserializer testImportedErrorDeserializer = _runtime.bodySerDe() + .deserializer(DeserializerArgs.builder() + .baseType(new TypeMarker<>() {}) + .success(new TypeMarker() {}) + .error( + EndpointSpecificErrors.ENDPOINT_ERROR.name(), + new TypeMarker() {}) + .build()); + + private final Serializer> testMultipleErrorsAndPackagesSerializer = + _runtime.bodySerDe().serializer(new TypeMarker>() {}); + + private final EndpointChannel testMultipleErrorsAndPackagesChannel = + _endpointChannelFactory.endpoint(DialogueErrorEndpoints.testMultipleErrorsAndPackages); + + private final Deserializer + testMultipleErrorsAndPackagesDeserializer = _runtime.bodySerDe() + .deserializer(DeserializerArgs.builder() + .baseType(new TypeMarker<>() {}) + .success(new TypeMarker() {}) + .error( + TestErrors.INVALID_ARGUMENT.name(), + new TypeMarker() {}) + .error( + TestErrors.NOT_FOUND.name(), + new TypeMarker() {}) + .error( + EndpointSpecificTwoErrors.DIFFERENT_NAMESPACE.name(), + new TypeMarker< + TestMultipleErrorsAndPackagesResponse.DifferentNamespace>() {}) + .error( + dialogueendpointresulttypes.com.palantir.another.EndpointSpecificErrors + .DIFFERENT_PACKAGE + .name(), + new TypeMarker() {}) + .error( + TestErrors.COMPLICATED_PARAMETERS.name(), + new TypeMarker< + TestMultipleErrorsAndPackagesResponse.ComplicatedParameters>() {}) + .build()); + + private final Serializer testEmptyBodySerializer = + _runtime.bodySerDe().serializer(new TypeMarker() {}); + + private final EndpointChannel testEmptyBodyChannel = + _endpointChannelFactory.endpoint(DialogueErrorEndpoints.testEmptyBody); + + private final Deserializer testEmptyBodyDeserializer = _runtime.bodySerDe() + .deserializer(DeserializerArgs.builder() + .baseType(new TypeMarker<>() {}) + .success(new TypeMarker() {}) + .error( + TestErrors.INVALID_ARGUMENT.name(), + new TypeMarker() {}) + .build()); + + private final Serializer testBinarySerializer = + _runtime.bodySerDe().serializer(new TypeMarker() {}); + + private final EndpointChannel testBinaryChannel = + _endpointChannelFactory.endpoint(DialogueErrorEndpoints.testBinary); + + private final Deserializer testBinaryDeserializer = _runtime.bodySerDe() + .inputStreamDeserializer(DeserializerArgs.builder() + .baseType(new TypeMarker<>() {}) + .success(new TypeMarker() {}) + .error( + TestErrors.INVALID_ARGUMENT.name(), + new TypeMarker() {}) + .build()); + + private final Serializer testOptionalBinarySerializer = + _runtime.bodySerDe().serializer(new TypeMarker() {}); + + private final EndpointChannel testOptionalBinaryChannel = + _endpointChannelFactory.endpoint(DialogueErrorEndpoints.testOptionalBinary); + + private final Deserializer testOptionalBinaryDeserializer = _runtime.bodySerDe() + .optionalInputStreamDeserializer(DeserializerArgs.builder() + .baseType(new TypeMarker<>() {}) + .success(new TypeMarker() {}) + .error( + TestErrors.INVALID_ARGUMENT.name(), + new TypeMarker() {}) + .build()); + + @Override + public TestBasicErrorResponse testBasicError(AuthHeader authHeader, boolean shouldThrowError) { + Request.Builder _request = Request.builder(); + _request.putHeaderParams("Authorization", authHeader.toString()); + _request.body(testBasicErrorSerializer.serialize(shouldThrowError)); + return _runtime.clients() + .callBlocking(testBasicErrorChannel, _request.build(), testBasicErrorDeserializer); + } + + @Override + public TestImportedErrorResponse testImportedError(AuthHeader authHeader, boolean shouldThrowError) { + Request.Builder _request = Request.builder(); + _request.putHeaderParams("Authorization", authHeader.toString()); + _request.body(testImportedErrorSerializer.serialize(shouldThrowError)); + return _runtime.clients() + .callBlocking(testImportedErrorChannel, _request.build(), testImportedErrorDeserializer); + } + + @Override + public TestMultipleErrorsAndPackagesResponse testMultipleErrorsAndPackages( + AuthHeader authHeader, Optional errorToThrow) { + Request.Builder _request = Request.builder(); + _request.putHeaderParams("Authorization", authHeader.toString()); + _request.body(testMultipleErrorsAndPackagesSerializer.serialize(errorToThrow)); + return _runtime.clients() + .callBlocking( + testMultipleErrorsAndPackagesChannel, + _request.build(), + testMultipleErrorsAndPackagesDeserializer); + } + + @Override + public TestEmptyBodyResponse testEmptyBody(AuthHeader authHeader, boolean shouldThrowError) { + Request.Builder _request = Request.builder(); + _request.putHeaderParams("Authorization", authHeader.toString()); + _request.body(testEmptyBodySerializer.serialize(shouldThrowError)); + return _runtime.clients() + .callBlocking(testEmptyBodyChannel, _request.build(), testEmptyBodyDeserializer); + } + + @Override + public TestBinaryResponse testBinary(AuthHeader authHeader, boolean shouldThrowError) { + Request.Builder _request = Request.builder(); + _request.putHeaderParams("Authorization", authHeader.toString()); + _request.body(testBinarySerializer.serialize(shouldThrowError)); + return _runtime.clients().callBlocking(testBinaryChannel, _request.build(), testBinaryDeserializer); + } + + @Override + public TestOptionalBinaryResponse testOptionalBinary( + AuthHeader authHeader, OptionalBinaryResponseMode mode) { + Request.Builder _request = Request.builder(); + _request.putHeaderParams("Authorization", authHeader.toString()); + _request.body(testOptionalBinarySerializer.serialize(mode)); + return _runtime.clients() + .callBlocking(testOptionalBinaryChannel, _request.build(), testOptionalBinaryDeserializer); + } + + @Override + public String toString() { + return "ErrorServiceBlocking{_endpointChannelFactory=" + _endpointChannelFactory + ", runtime=" + + _runtime + '}'; + } + }; + } + + /** Creates an asynchronous/non-blocking client for a ErrorService service. */ + static ErrorServiceBlocking of(Channel _channel, ConjureRuntime _runtime) { + if (_channel instanceof EndpointChannelFactory) { + return of((EndpointChannelFactory) _channel, _runtime); + } + return of( + new EndpointChannelFactory() { + @Override + public EndpointChannel endpoint(Endpoint endpoint) { + return _runtime.clients().bind(_channel, endpoint); + } + }, + _runtime); + } + + final class Factory implements DialogueServiceFactory { + @Override + public ErrorServiceBlocking create(EndpointChannelFactory endpointChannelFactory, ConjureRuntime runtime) { + return ErrorServiceBlocking.of(endpointChannelFactory, runtime); + } + } + + sealed interface TestBasicErrorResponse + permits TestBasicErrorResponse.Success, TestBasicErrorResponse.InvalidArgument { + record Success(@JsonValue String value) implements TestBasicErrorResponse { + public Success { + Preconditions.checkArgumentNotNull(value, "value cannot be null"); + } + } + + final class InvalidArgument extends EndpointError + implements TestBasicErrorResponse { + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) + InvalidArgument( + @JsonProperty("errorCode") @Safe String errorCode, + @JsonProperty("errorInstanceId") @Safe String errorInstanceId, + @JsonProperty("parameters") TestErrors.InvalidArgumentParameters parameters) { + super(errorCode, TestErrors.INVALID_ARGUMENT.name(), errorInstanceId, parameters); + } + } + } + + sealed interface TestImportedErrorResponse + permits TestImportedErrorResponse.Success, TestImportedErrorResponse.EndpointError { + record Success(@JsonValue String value) implements TestImportedErrorResponse { + public Success { + Preconditions.checkArgumentNotNull(value, "value cannot be null"); + } + } + + final class EndpointError + extends com.palantir.dialogue.EndpointError + implements TestImportedErrorResponse { + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) + EndpointError( + @JsonProperty("errorCode") @Safe String errorCode, + @JsonProperty("errorInstanceId") @Safe String errorInstanceId, + @JsonProperty("parameters") EndpointSpecificErrors.EndpointErrorParameters parameters) { + super(errorCode, EndpointSpecificErrors.ENDPOINT_ERROR.name(), errorInstanceId, parameters); + } + } + } + + sealed interface TestMultipleErrorsAndPackagesResponse + permits TestMultipleErrorsAndPackagesResponse.Success, + TestMultipleErrorsAndPackagesResponse.InvalidArgument, + TestMultipleErrorsAndPackagesResponse.NotFound, + TestMultipleErrorsAndPackagesResponse.DifferentNamespace, + TestMultipleErrorsAndPackagesResponse.DifferentPackage, + TestMultipleErrorsAndPackagesResponse.ComplicatedParameters { + record Success(@JsonValue String value) implements TestMultipleErrorsAndPackagesResponse { + public Success { + Preconditions.checkArgumentNotNull(value, "value cannot be null"); + } + } + + final class InvalidArgument extends EndpointError + implements TestMultipleErrorsAndPackagesResponse { + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) + InvalidArgument( + @JsonProperty("errorCode") @Safe String errorCode, + @JsonProperty("errorInstanceId") @Safe String errorInstanceId, + @JsonProperty("parameters") TestErrors.InvalidArgumentParameters parameters) { + super(errorCode, TestErrors.INVALID_ARGUMENT.name(), errorInstanceId, parameters); + } + } + + final class NotFound extends EndpointError + implements TestMultipleErrorsAndPackagesResponse { + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) + NotFound( + @JsonProperty("errorCode") @Safe String errorCode, + @JsonProperty("errorInstanceId") @Safe String errorInstanceId, + @JsonProperty("parameters") TestErrors.NotFoundParameters parameters) { + super(errorCode, TestErrors.NOT_FOUND.name(), errorInstanceId, parameters); + } + } + + final class DifferentNamespace extends EndpointError + implements TestMultipleErrorsAndPackagesResponse { + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) + DifferentNamespace( + @JsonProperty("errorCode") @Safe String errorCode, + @JsonProperty("errorInstanceId") @Safe String errorInstanceId) { + super( + errorCode, + EndpointSpecificTwoErrors.DIFFERENT_NAMESPACE.name(), + errorInstanceId, + new EndpointSpecificTwoErrors.DifferentNamespaceParameters()); + } + } + + final class DifferentPackage + extends EndpointError< + dialogueendpointresulttypes.com.palantir.another.EndpointSpecificErrors + .DifferentPackageParameters> + implements TestMultipleErrorsAndPackagesResponse { + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) + DifferentPackage( + @JsonProperty("errorCode") @Safe String errorCode, + @JsonProperty("errorInstanceId") @Safe String errorInstanceId) { + super( + errorCode, + dialogueendpointresulttypes.com.palantir.another.EndpointSpecificErrors.DIFFERENT_PACKAGE + .name(), + errorInstanceId, + new dialogueendpointresulttypes.com.palantir.another.EndpointSpecificErrors + .DifferentPackageParameters()); + } + } + + final class ComplicatedParameters extends EndpointError + implements TestMultipleErrorsAndPackagesResponse { + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) + ComplicatedParameters( + @JsonProperty("errorCode") @Safe String errorCode, + @JsonProperty("errorInstanceId") @Safe String errorInstanceId, + @JsonProperty("parameters") TestErrors.ComplicatedParametersParameters parameters) { + super(errorCode, TestErrors.COMPLICATED_PARAMETERS.name(), errorInstanceId, parameters); + } + } + } + + sealed interface TestEmptyBodyResponse + permits TestEmptyBodyResponse.Success, TestEmptyBodyResponse.InvalidArgument { + record Success() implements TestEmptyBodyResponse {} + + final class InvalidArgument extends EndpointError + implements TestEmptyBodyResponse { + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) + InvalidArgument( + @JsonProperty("errorCode") @Safe String errorCode, + @JsonProperty("errorInstanceId") @Safe String errorInstanceId, + @JsonProperty("parameters") TestErrors.InvalidArgumentParameters parameters) { + super(errorCode, TestErrors.INVALID_ARGUMENT.name(), errorInstanceId, parameters); + } + } + } + + sealed interface TestBinaryResponse extends Closeable + permits TestBinaryResponse.Success, TestBinaryResponse.InvalidArgument { + @Override + default void close() throws IOException {} + + record Success(@MustBeClosed @JsonValue InputStream value) implements TestBinaryResponse { + public Success { + Preconditions.checkArgumentNotNull(value, "value cannot be null"); + } + + @Override + public void close() throws IOException { + value.close(); + } + } + + final class InvalidArgument extends EndpointError + implements TestBinaryResponse { + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) + InvalidArgument( + @JsonProperty("errorCode") @Safe String errorCode, + @JsonProperty("errorInstanceId") @Safe String errorInstanceId, + @JsonProperty("parameters") TestErrors.InvalidArgumentParameters parameters) { + super(errorCode, TestErrors.INVALID_ARGUMENT.name(), errorInstanceId, parameters); + } + } + } + + sealed interface TestOptionalBinaryResponse extends Closeable + permits TestOptionalBinaryResponse.Success, TestOptionalBinaryResponse.InvalidArgument { + @Override + default void close() throws IOException {} + + record Success(@JsonValue Optional value) implements TestOptionalBinaryResponse { + public Success { + Preconditions.checkArgumentNotNull(value, "value cannot be null"); + } + + @Override + public void close() throws IOException { + if (value.isPresent()) { + value.get().close(); + } + } + } + + final class InvalidArgument extends EndpointError + implements TestOptionalBinaryResponse { + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) + InvalidArgument( + @JsonProperty("errorCode") @Safe String errorCode, + @JsonProperty("errorInstanceId") @Safe String errorInstanceId, + @JsonProperty("parameters") TestErrors.InvalidArgumentParameters parameters) { + super(errorCode, TestErrors.INVALID_ARGUMENT.name(), errorInstanceId, parameters); + } + } + } +} diff --git a/conjure-java-core/src/integrationInput/java/dialogueendpointresulttypes/com/palantir/product/ErrorServiceEndpoints.java b/conjure-java-core/src/integrationInput/java/dialogueendpointresulttypes/com/palantir/product/ErrorServiceEndpoints.java new file mode 100644 index 000000000..709cf8f69 --- /dev/null +++ b/conjure-java-core/src/integrationInput/java/dialogueendpointresulttypes/com/palantir/product/ErrorServiceEndpoints.java @@ -0,0 +1,344 @@ +package dialogueendpointresulttypes.com.palantir.product; + +import com.google.common.collect.ImmutableList; +import com.palantir.conjure.java.undertow.lib.BinaryResponseBody; +import com.palantir.conjure.java.undertow.lib.Deserializer; +import com.palantir.conjure.java.undertow.lib.Endpoint; +import com.palantir.conjure.java.undertow.lib.Serializer; +import com.palantir.conjure.java.undertow.lib.TypeMarker; +import com.palantir.conjure.java.undertow.lib.UndertowRuntime; +import com.palantir.conjure.java.undertow.lib.UndertowService; +import com.palantir.tokens.auth.AuthHeader; +import io.undertow.server.HttpHandler; +import io.undertow.server.HttpServerExchange; +import io.undertow.util.HttpString; +import io.undertow.util.Methods; +import io.undertow.util.StatusCodes; +import java.io.IOException; +import java.util.List; +import java.util.Optional; +import javax.annotation.processing.Generated; + +@Generated("com.palantir.conjure.java.services.UndertowServiceHandlerGenerator") +public final class ErrorServiceEndpoints implements UndertowService { + private final ErrorService delegate; + + private ErrorServiceEndpoints(ErrorService delegate) { + this.delegate = delegate; + } + + public static UndertowService of(ErrorService delegate) { + return new ErrorServiceEndpoints(delegate); + } + + @Override + public List endpoints(UndertowRuntime runtime) { + return ImmutableList.of( + new TestBasicErrorEndpoint(runtime, delegate), + new TestImportedErrorEndpoint(runtime, delegate), + new TestMultipleErrorsAndPackagesEndpoint(runtime, delegate), + new TestEmptyBodyEndpoint(runtime, delegate), + new TestBinaryEndpoint(runtime, delegate), + new TestOptionalBinaryEndpoint(runtime, delegate)); + } + + private static final class TestBasicErrorEndpoint implements HttpHandler, Endpoint { + private final UndertowRuntime runtime; + + private final ErrorService delegate; + + private final Deserializer deserializer; + + private final Serializer serializer; + + TestBasicErrorEndpoint(UndertowRuntime runtime, ErrorService delegate) { + this.runtime = runtime; + this.delegate = delegate; + this.deserializer = runtime.bodySerDe().deserializer(new TypeMarker() {}, this); + this.serializer = runtime.bodySerDe().serializer(new TypeMarker() {}, this); + } + + @Override + public void handleRequest(HttpServerExchange exchange) throws IOException, TestServerErrors.InvalidArgument { + AuthHeader authHeader = runtime.auth().header(exchange); + Boolean shouldThrowError = deserializer.deserialize(exchange); + String result = delegate.testBasicError(authHeader, shouldThrowError); + serializer.serialize(result, exchange); + } + + @Override + public HttpString method() { + return Methods.POST; + } + + @Override + public String template() { + return "/errors/basic"; + } + + @Override + public String serviceName() { + return "ErrorService"; + } + + @Override + public String name() { + return "testBasicError"; + } + + @Override + public HttpHandler handler() { + return this; + } + } + + private static final class TestImportedErrorEndpoint implements HttpHandler, Endpoint { + private final UndertowRuntime runtime; + + private final ErrorService delegate; + + private final Deserializer deserializer; + + private final Serializer serializer; + + TestImportedErrorEndpoint(UndertowRuntime runtime, ErrorService delegate) { + this.runtime = runtime; + this.delegate = delegate; + this.deserializer = runtime.bodySerDe().deserializer(new TypeMarker() {}, this); + this.serializer = runtime.bodySerDe().serializer(new TypeMarker() {}, this); + } + + @Override + public void handleRequest(HttpServerExchange exchange) + throws IOException, EndpointSpecificServerErrors.EndpointError { + AuthHeader authHeader = runtime.auth().header(exchange); + Boolean shouldThrowError = deserializer.deserialize(exchange); + String result = delegate.testImportedError(authHeader, shouldThrowError); + serializer.serialize(result, exchange); + } + + @Override + public HttpString method() { + return Methods.POST; + } + + @Override + public String template() { + return "/errors/imported"; + } + + @Override + public String serviceName() { + return "ErrorService"; + } + + @Override + public String name() { + return "testImportedError"; + } + + @Override + public HttpHandler handler() { + return this; + } + } + + private static final class TestMultipleErrorsAndPackagesEndpoint implements HttpHandler, Endpoint { + private final UndertowRuntime runtime; + + private final ErrorService delegate; + + private final Deserializer> deserializer; + + private final Serializer serializer; + + TestMultipleErrorsAndPackagesEndpoint(UndertowRuntime runtime, ErrorService delegate) { + this.runtime = runtime; + this.delegate = delegate; + this.deserializer = runtime.bodySerDe().deserializer(new TypeMarker>() {}, this); + this.serializer = runtime.bodySerDe().serializer(new TypeMarker() {}, this); + } + + @Override + public void handleRequest(HttpServerExchange exchange) + throws IOException, TestServerErrors.InvalidArgument, TestServerErrors.NotFound, + EndpointSpecificTwoServerErrors.DifferentNamespace, + dialogueendpointresulttypes.com.palantir.another.EndpointSpecificServerErrors.DifferentPackage, + TestServerErrors.ComplicatedParameters { + AuthHeader authHeader = runtime.auth().header(exchange); + Optional errorToThrow = deserializer.deserialize(exchange); + String result = delegate.testMultipleErrorsAndPackages(authHeader, errorToThrow); + serializer.serialize(result, exchange); + } + + @Override + public HttpString method() { + return Methods.POST; + } + + @Override + public String template() { + return "/errors/multiple"; + } + + @Override + public String serviceName() { + return "ErrorService"; + } + + @Override + public String name() { + return "testMultipleErrorsAndPackages"; + } + + @Override + public HttpHandler handler() { + return this; + } + } + + private static final class TestEmptyBodyEndpoint implements HttpHandler, Endpoint { + private final UndertowRuntime runtime; + + private final ErrorService delegate; + + private final Deserializer deserializer; + + TestEmptyBodyEndpoint(UndertowRuntime runtime, ErrorService delegate) { + this.runtime = runtime; + this.delegate = delegate; + this.deserializer = runtime.bodySerDe().deserializer(new TypeMarker() {}, this); + } + + @Override + public void handleRequest(HttpServerExchange exchange) throws IOException, TestServerErrors.InvalidArgument { + AuthHeader authHeader = runtime.auth().header(exchange); + Boolean shouldThrowError = deserializer.deserialize(exchange); + delegate.testEmptyBody(authHeader, shouldThrowError); + exchange.setStatusCode(StatusCodes.NO_CONTENT); + } + + @Override + public HttpString method() { + return Methods.POST; + } + + @Override + public String template() { + return "/errors/empty"; + } + + @Override + public String serviceName() { + return "ErrorService"; + } + + @Override + public String name() { + return "testEmptyBody"; + } + + @Override + public HttpHandler handler() { + return this; + } + } + + private static final class TestBinaryEndpoint implements HttpHandler, Endpoint { + private final UndertowRuntime runtime; + + private final ErrorService delegate; + + private final Deserializer deserializer; + + TestBinaryEndpoint(UndertowRuntime runtime, ErrorService delegate) { + this.runtime = runtime; + this.delegate = delegate; + this.deserializer = runtime.bodySerDe().deserializer(new TypeMarker() {}, this); + } + + @Override + public void handleRequest(HttpServerExchange exchange) throws IOException, TestServerErrors.InvalidArgument { + AuthHeader authHeader = runtime.auth().header(exchange); + Boolean shouldThrowError = deserializer.deserialize(exchange); + BinaryResponseBody result = delegate.testBinary(authHeader, shouldThrowError); + runtime.bodySerDe().serialize(result, exchange); + } + + @Override + public HttpString method() { + return Methods.POST; + } + + @Override + public String template() { + return "/errors/binary"; + } + + @Override + public String serviceName() { + return "ErrorService"; + } + + @Override + public String name() { + return "testBinary"; + } + + @Override + public HttpHandler handler() { + return this; + } + } + + private static final class TestOptionalBinaryEndpoint implements HttpHandler, Endpoint { + private final UndertowRuntime runtime; + + private final ErrorService delegate; + + private final Deserializer deserializer; + + TestOptionalBinaryEndpoint(UndertowRuntime runtime, ErrorService delegate) { + this.runtime = runtime; + this.delegate = delegate; + this.deserializer = runtime.bodySerDe().deserializer(new TypeMarker() {}, this); + } + + @Override + public void handleRequest(HttpServerExchange exchange) throws IOException, TestServerErrors.InvalidArgument { + AuthHeader authHeader = runtime.auth().header(exchange); + OptionalBinaryResponseMode mode = deserializer.deserialize(exchange); + Optional result = delegate.testOptionalBinary(authHeader, mode); + if (result.isPresent()) { + runtime.bodySerDe().serialize(result.get(), exchange); + } else { + exchange.setStatusCode(StatusCodes.NO_CONTENT); + } + } + + @Override + public HttpString method() { + return Methods.POST; + } + + @Override + public String template() { + return "/errors/optional-binary"; + } + + @Override + public String serviceName() { + return "ErrorService"; + } + + @Override + public String name() { + return "testOptionalBinary"; + } + + @Override + public HttpHandler handler() { + return this; + } + } +} diff --git a/conjure-java-core/src/integrationInput/java/dialogueendpointresulttypes/com/palantir/product/OptionalBinaryResponseMode.java b/conjure-java-core/src/integrationInput/java/dialogueendpointresulttypes/com/palantir/product/OptionalBinaryResponseMode.java new file mode 100644 index 000000000..c18100aed --- /dev/null +++ b/conjure-java-core/src/integrationInput/java/dialogueendpointresulttypes/com/palantir/product/OptionalBinaryResponseMode.java @@ -0,0 +1,239 @@ +package dialogueendpointresulttypes.com.palantir.product; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import com.google.errorprone.annotations.Immutable; +import com.palantir.logsafe.Preconditions; +import com.palantir.logsafe.Safe; +import com.palantir.logsafe.SafeArg; +import com.palantir.logsafe.exceptions.SafeIllegalArgumentException; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Locale; +import java.util.function.Function; +import java.util.function.Supplier; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.annotation.processing.Generated; + +/** + * This class is used instead of a native enum to support unknown values. Rather than throw an exception, the + * {@link OptionalBinaryResponseMode#valueOf} method defaults to a new instantiation of + * {@link OptionalBinaryResponseMode} where {@link OptionalBinaryResponseMode#get} will return + * {@link OptionalBinaryResponseMode.Value#UNKNOWN}. + * + *

For example, {@code OptionalBinaryResponseMode.valueOf("corrupted value").get()} will return + * {@link OptionalBinaryResponseMode.Value#UNKNOWN}, but {@link OptionalBinaryResponseMode#toString} will return + * "corrupted value". + * + *

There is no method to access all instantiations of this class, since they cannot be known at compile time. + */ +@Generated("com.palantir.conjure.java.types.EnumGenerator") +@Safe +@Immutable +public final class OptionalBinaryResponseMode { + public static final OptionalBinaryResponseMode PRESENT = new OptionalBinaryResponseMode(Value.PRESENT, "PRESENT"); + + public static final OptionalBinaryResponseMode ABSENT = new OptionalBinaryResponseMode(Value.ABSENT, "ABSENT"); + + public static final OptionalBinaryResponseMode ERROR = new OptionalBinaryResponseMode(Value.ERROR, "ERROR"); + + private static final List values = + Collections.unmodifiableList(Arrays.asList(PRESENT, ABSENT, ERROR)); + + private final Value value; + + private final String string; + + private OptionalBinaryResponseMode(Value value, String string) { + this.value = value; + this.string = string; + } + + public Value get() { + return this.value; + } + + @Override + @JsonValue + public String toString() { + return this.string; + } + + @Override + public boolean equals(@Nullable Object other) { + return (this == other) + || (this.value == Value.UNKNOWN + && other instanceof OptionalBinaryResponseMode + && this.string.equals(((OptionalBinaryResponseMode) other).string)); + } + + @Override + public int hashCode() { + return this.string.hashCode(); + } + + @JsonCreator(mode = JsonCreator.Mode.DELEGATING) + public static OptionalBinaryResponseMode valueOf(@Nonnull @Safe String value) { + Preconditions.checkNotNull(value, "value cannot be null"); + String upperCasedValue = value.toUpperCase(Locale.ROOT); + switch (upperCasedValue) { + case "PRESENT": + return PRESENT; + case "ABSENT": + return ABSENT; + case "ERROR": + return ERROR; + default: + return new OptionalBinaryResponseMode(Value.UNKNOWN, upperCasedValue); + } + } + + public T accept(Visitor visitor) { + switch (value) { + case PRESENT: + return visitor.visitPresent(); + case ABSENT: + return visitor.visitAbsent(); + case ERROR: + return visitor.visitError(); + default: + return visitor.visitUnknown(string); + } + } + + public static List values() { + return values; + } + + @Generated("com.palantir.conjure.java.types.EnumGenerator") + public enum Value { + PRESENT, + + ABSENT, + + ERROR, + + UNKNOWN + } + + @Generated("com.palantir.conjure.java.types.EnumGenerator") + public interface Visitor { + T visitPresent(); + + T visitAbsent(); + + T visitError(); + + T visitUnknown(String unknownValue); + + static PresentStageVisitorBuilder builder() { + return new VisitorBuilder(); + } + } + + private static final class VisitorBuilder + implements PresentStageVisitorBuilder, + AbsentStageVisitorBuilder, + ErrorStageVisitorBuilder, + UnknownStageVisitorBuilder, + Completed_StageVisitorBuilder { + private Supplier presentVisitor; + + private Supplier absentVisitor; + + private Supplier errorVisitor; + + private Function<@Safe String, T> unknownVisitor; + + @Override + public AbsentStageVisitorBuilder visitPresent(@Nonnull Supplier presentVisitor) { + Preconditions.checkNotNull(presentVisitor, "presentVisitor cannot be null"); + this.presentVisitor = presentVisitor; + return this; + } + + @Override + public ErrorStageVisitorBuilder visitAbsent(@Nonnull Supplier absentVisitor) { + Preconditions.checkNotNull(absentVisitor, "absentVisitor cannot be null"); + this.absentVisitor = absentVisitor; + return this; + } + + @Override + public UnknownStageVisitorBuilder visitError(@Nonnull Supplier errorVisitor) { + Preconditions.checkNotNull(errorVisitor, "errorVisitor cannot be null"); + this.errorVisitor = errorVisitor; + return this; + } + + @Override + public Completed_StageVisitorBuilder visitUnknown(@Nonnull Function<@Safe String, T> unknownVisitor) { + Preconditions.checkNotNull(unknownVisitor, "unknownVisitor cannot be null"); + this.unknownVisitor = unknownType -> unknownVisitor.apply(unknownType); + return this; + } + + @Override + public Completed_StageVisitorBuilder throwOnUnknown() { + this.unknownVisitor = unknownType -> { + throw new SafeIllegalArgumentException( + "Unknown variant of the 'OptionalBinaryResponseMode' union", + SafeArg.of("unknownType", unknownType)); + }; + return this; + } + + @Override + public Visitor build() { + final Supplier presentVisitor = this.presentVisitor; + final Supplier absentVisitor = this.absentVisitor; + final Supplier errorVisitor = this.errorVisitor; + final Function<@Safe String, T> unknownVisitor = this.unknownVisitor; + return new Visitor() { + @Override + public T visitPresent() { + return presentVisitor.get(); + } + + @Override + public T visitAbsent() { + return absentVisitor.get(); + } + + @Override + public T visitError() { + return errorVisitor.get(); + } + + @Override + public T visitUnknown(String unknownType) { + return unknownVisitor.apply(unknownType); + } + }; + } + } + + public interface PresentStageVisitorBuilder { + AbsentStageVisitorBuilder visitPresent(@Nonnull Supplier presentVisitor); + } + + public interface AbsentStageVisitorBuilder { + ErrorStageVisitorBuilder visitAbsent(@Nonnull Supplier absentVisitor); + } + + public interface ErrorStageVisitorBuilder { + UnknownStageVisitorBuilder visitError(@Nonnull Supplier errorVisitor); + } + + public interface UnknownStageVisitorBuilder { + Completed_StageVisitorBuilder visitUnknown(@Nonnull Function<@Safe String, T> unknownVisitor); + + Completed_StageVisitorBuilder throwOnUnknown(); + } + + public interface Completed_StageVisitorBuilder { + Visitor build(); + } +} diff --git a/conjure-java-core/src/integrationInput/java/dialogueendpointresulttypes/com/palantir/product/StringAlias.java b/conjure-java-core/src/integrationInput/java/dialogueendpointresulttypes/com/palantir/product/StringAlias.java new file mode 100644 index 000000000..f48400af9 --- /dev/null +++ b/conjure-java-core/src/integrationInput/java/dialogueendpointresulttypes/com/palantir/product/StringAlias.java @@ -0,0 +1,55 @@ +package dialogueendpointresulttypes.com.palantir.product; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import com.palantir.logsafe.Preconditions; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.annotation.processing.Generated; + +@Generated("com.palantir.conjure.java.types.AliasGenerator") +public final class StringAlias implements Comparable { + private final String value; + + private StringAlias(@Nonnull String value) { + this.value = Preconditions.checkNotNull(value, "value cannot be null"); + } + + @JsonValue + public String get() { + return value; + } + + @Override + public String toString() { + return value.toString(); + } + + @Override + public boolean equals(@Nullable Object other) { + return this == other || (other instanceof StringAlias && equalTo((StringAlias) other)); + } + + private boolean equalTo(StringAlias other) { + return this.value.equals(other.value); + } + + @Override + public int hashCode() { + return this.value.hashCode(); + } + + @Override + public int compareTo(StringAlias other) { + return value.compareTo(other.get()); + } + + public static StringAlias valueOf(String value) { + return of(value); + } + + @JsonCreator(mode = JsonCreator.Mode.DELEGATING) + public static StringAlias of(@Nonnull String value) { + return new StringAlias(value); + } +} diff --git a/conjure-java-core/src/integrationInput/java/dialogueendpointresulttypes/com/palantir/product/TestErrors.java b/conjure-java-core/src/integrationInput/java/dialogueendpointresulttypes/com/palantir/product/TestErrors.java new file mode 100644 index 000000000..da9da0dfd --- /dev/null +++ b/conjure-java-core/src/integrationInput/java/dialogueendpointresulttypes/com/palantir/product/TestErrors.java @@ -0,0 +1,49 @@ +package dialogueendpointresulttypes.com.palantir.product; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.palantir.conjure.java.api.errors.ErrorType; +import com.palantir.conjure.java.api.errors.RemoteException; +import com.palantir.logsafe.Preconditions; +import com.palantir.logsafe.Safe; +import com.palantir.logsafe.Unsafe; +import java.util.Map; +import javax.annotation.processing.Generated; + +@Generated("com.palantir.conjure.java.types.ErrorGenerator") +public final class TestErrors { + public static final ErrorType COMPLICATED_PARAMETERS = + ErrorType.create(ErrorType.Code.INTERNAL, "Test:ComplicatedParameters"); + + public static final ErrorType INVALID_ARGUMENT = + ErrorType.create(ErrorType.Code.INVALID_ARGUMENT, "Test:InvalidArgument"); + + public static final ErrorType NOT_FOUND = ErrorType.create(ErrorType.Code.NOT_FOUND, "Test:NotFound"); + + private TestErrors() {} + + /** Returns true if the {@link RemoteException} is named Test:ComplicatedParameters */ + public static boolean isComplicatedParameters(RemoteException remoteException) { + Preconditions.checkNotNull(remoteException, "remote exception must not be null"); + return COMPLICATED_PARAMETERS.name().equals(remoteException.getError().errorName()); + } + + /** Returns true if the {@link RemoteException} is named Test:InvalidArgument */ + public static boolean isInvalidArgument(RemoteException remoteException) { + Preconditions.checkNotNull(remoteException, "remote exception must not be null"); + return INVALID_ARGUMENT.name().equals(remoteException.getError().errorName()); + } + + /** Returns true if the {@link RemoteException} is named Test:NotFound */ + public static boolean isNotFound(RemoteException remoteException) { + Preconditions.checkNotNull(remoteException, "remote exception must not be null"); + return NOT_FOUND.name().equals(remoteException.getError().errorName()); + } + + public static record ComplicatedParametersParameters( + @JsonProperty("complicatedObjectMap") @Safe Map complicatedObjectMap) {} + + public static record InvalidArgumentParameters( + @JsonProperty("field") @Safe String field, @JsonProperty("value") @Unsafe String value) {} + + public static record NotFoundParameters(@JsonProperty("resource") @Safe String resource) {} +} diff --git a/conjure-java-core/src/integrationInput/java/dialogueendpointresulttypes/com/palantir/product/TestServerErrors.java b/conjure-java-core/src/integrationInput/java/dialogueendpointresulttypes/com/palantir/product/TestServerErrors.java new file mode 100644 index 000000000..8ebc6a388 --- /dev/null +++ b/conjure-java-core/src/integrationInput/java/dialogueendpointresulttypes/com/palantir/product/TestServerErrors.java @@ -0,0 +1,100 @@ +package dialogueendpointresulttypes.com.palantir.product; + +import com.palantir.conjure.java.api.errors.CheckedServiceException; +import com.palantir.logsafe.Safe; +import com.palantir.logsafe.SafeArg; +import com.palantir.logsafe.Unsafe; +import com.palantir.logsafe.UnsafeArg; +import java.util.Map; +import javax.annotation.Nullable; +import javax.annotation.processing.Generated; + +@Generated("com.palantir.conjure.java.types.CheckedErrorGenerator") +public final class TestServerErrors { + private TestServerErrors() {} + + public static ComplicatedParameters complicatedParameters( + @Safe Map complicatedObjectMap) { + return new ComplicatedParameters(complicatedObjectMap, null); + } + + public static ComplicatedParameters complicatedParameters( + @Safe Map complicatedObjectMap, @Nullable Throwable cause) { + return new ComplicatedParameters(complicatedObjectMap, cause); + } + + public static InvalidArgument invalidArgument(@Safe String field, @Unsafe String value) { + return new InvalidArgument(field, value, null); + } + + public static InvalidArgument invalidArgument(@Safe String field, @Unsafe String value, @Nullable Throwable cause) { + return new InvalidArgument(field, value, cause); + } + + public static NotFound notFound(@Safe String resource) { + return new NotFound(resource, null); + } + + public static NotFound notFound(@Safe String resource, @Nullable Throwable cause) { + return new NotFound(resource, cause); + } + + /** + * Throws a {@link ComplicatedParameters} when {@code shouldThrow} is true. + * + * @param shouldThrow Cause the method to throw when true + * @param complicatedObjectMap + */ + public static void throwIfComplicatedParameters( + boolean shouldThrow, @Safe Map complicatedObjectMap) + throws ComplicatedParameters { + if (shouldThrow) { + throw complicatedParameters(complicatedObjectMap); + } + } + + /** + * Throws a {@link InvalidArgument} when {@code shouldThrow} is true. + * + * @param shouldThrow Cause the method to throw when true + * @param field + * @param value + */ + public static void throwIfInvalidArgument(boolean shouldThrow, @Safe String field, @Unsafe String value) + throws InvalidArgument { + if (shouldThrow) { + throw invalidArgument(field, value); + } + } + + /** + * Throws a {@link NotFound} when {@code shouldThrow} is true. + * + * @param shouldThrow Cause the method to throw when true + * @param resource + */ + public static void throwIfNotFound(boolean shouldThrow, @Safe String resource) throws NotFound { + if (shouldThrow) { + throw notFound(resource); + } + } + + public static final class ComplicatedParameters extends CheckedServiceException { + private ComplicatedParameters( + @Safe Map complicatedObjectMap, @Nullable Throwable cause) { + super(TestErrors.COMPLICATED_PARAMETERS, cause, SafeArg.of("complicatedObjectMap", complicatedObjectMap)); + } + } + + public static final class InvalidArgument extends CheckedServiceException { + private InvalidArgument(@Safe String field, @Unsafe String value, @Nullable Throwable cause) { + super(TestErrors.INVALID_ARGUMENT, cause, SafeArg.of("field", field), UnsafeArg.of("value", value)); + } + } + + public static final class NotFound extends CheckedServiceException { + private NotFound(@Safe String resource, @Nullable Throwable cause) { + super(TestErrors.NOT_FOUND, cause, SafeArg.of("resource", resource)); + } + } +} diff --git a/conjure-java-core/src/integrationInput/java/undertow/com/palantir/product/ComplicatedObject.java b/conjure-java-core/src/integrationInput/java/undertow/com/palantir/product/ComplicatedObject.java new file mode 100644 index 000000000..4cab70ea6 --- /dev/null +++ b/conjure-java-core/src/integrationInput/java/undertow/com/palantir/product/ComplicatedObject.java @@ -0,0 +1,172 @@ +package undertow.com.palantir.product; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.google.errorprone.annotations.CheckReturnValue; +import com.palantir.conjure.java.lib.internal.ConjureCollections; +import com.palantir.logsafe.Preconditions; +import com.palantir.logsafe.SafeArg; +import com.palantir.logsafe.exceptions.SafeIllegalArgumentException; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.annotation.processing.Generated; + +@JsonDeserialize(builder = ComplicatedObject.Builder.class) +@Generated("com.palantir.conjure.java.types.BeanGenerator") +public final class ComplicatedObject { + private final List fieldOne; + + private final Optional fieldTwo; + + private int memoizedHashCode; + + private ComplicatedObject(List fieldOne, Optional fieldTwo) { + validateFields(fieldOne, fieldTwo); + this.fieldOne = ConjureCollections.unmodifiableList(fieldOne); + this.fieldTwo = fieldTwo; + } + + @JsonProperty("fieldOne") + public List getFieldOne() { + return this.fieldOne; + } + + @JsonProperty("fieldTwo") + @JsonInclude(JsonInclude.Include.NON_ABSENT) + public Optional getFieldTwo() { + return this.fieldTwo; + } + + @Override + public boolean equals(@Nullable Object other) { + return this == other || (other instanceof ComplicatedObject && equalTo((ComplicatedObject) other)); + } + + private boolean equalTo(ComplicatedObject other) { + if (this.memoizedHashCode != 0 + && other.memoizedHashCode != 0 + && this.memoizedHashCode != other.memoizedHashCode) { + return false; + } + return this.fieldOne.equals(other.fieldOne) && this.fieldTwo.equals(other.fieldTwo); + } + + @Override + public int hashCode() { + int result = memoizedHashCode; + if (result == 0) { + int hash = 1; + hash = 31 * hash + this.fieldOne.hashCode(); + hash = 31 * hash + this.fieldTwo.hashCode(); + result = hash; + memoizedHashCode = result; + } + return result; + } + + @Override + public String toString() { + return "ComplicatedObject{fieldOne: " + fieldOne + ", fieldTwo: " + fieldTwo + '}'; + } + + public static ComplicatedObject of(List fieldOne, StringAlias fieldTwo) { + return builder().fieldOne(fieldOne).fieldTwo(Optional.of(fieldTwo)).build(); + } + + private static void validateFields(List fieldOne, Optional fieldTwo) { + List missingFields = null; + missingFields = addFieldIfMissing(missingFields, fieldOne, "fieldOne"); + missingFields = addFieldIfMissing(missingFields, fieldTwo, "fieldTwo"); + if (missingFields != null) { + throw new SafeIllegalArgumentException( + "Some required fields have not been set", SafeArg.of("missingFields", missingFields)); + } + } + + private static List addFieldIfMissing(List prev, Object fieldValue, String fieldName) { + List missingFields = prev; + if (fieldValue == null) { + if (missingFields == null) { + missingFields = new ArrayList<>(2); + } + missingFields.add(fieldName); + } + return missingFields; + } + + public static Builder builder() { + return new Builder(); + } + + @Generated("com.palantir.conjure.java.types.BeanBuilderGenerator") + @JsonIgnoreProperties(ignoreUnknown = true) + public static final class Builder { + boolean _buildInvoked; + + private List fieldOne = ConjureCollections.newNonNullList(); + + private Optional fieldTwo = Optional.empty(); + + private Builder() {} + + public Builder from(ComplicatedObject other) { + checkNotBuilt(); + fieldOne(other.getFieldOne()); + fieldTwo(other.getFieldTwo()); + return this; + } + + @JsonSetter(value = "fieldOne", nulls = Nulls.SKIP, contentNulls = Nulls.FAIL) + public Builder fieldOne(@Nonnull Iterable fieldOne) { + checkNotBuilt(); + this.fieldOne = + ConjureCollections.newNonNullList(Preconditions.checkNotNull(fieldOne, "fieldOne cannot be null")); + return this; + } + + public Builder addAllFieldOne(@Nonnull Iterable fieldOne) { + checkNotBuilt(); + ConjureCollections.addAllAndCheckNonNull( + this.fieldOne, Preconditions.checkNotNull(fieldOne, "fieldOne cannot be null")); + return this; + } + + public Builder fieldOne(String fieldOne) { + checkNotBuilt(); + Preconditions.checkNotNull(fieldOne, "fieldOne cannot be null"); + this.fieldOne.add(fieldOne); + return this; + } + + @JsonSetter(value = "fieldTwo", nulls = Nulls.SKIP) + public Builder fieldTwo(@Nonnull Optional fieldTwo) { + checkNotBuilt(); + this.fieldTwo = Preconditions.checkNotNull(fieldTwo, "fieldTwo cannot be null"); + return this; + } + + public Builder fieldTwo(@Nonnull StringAlias fieldTwo) { + checkNotBuilt(); + this.fieldTwo = Optional.of(Preconditions.checkNotNull(fieldTwo, "fieldTwo cannot be null")); + return this; + } + + @CheckReturnValue + public ComplicatedObject build() { + checkNotBuilt(); + this._buildInvoked = true; + return new ComplicatedObject(fieldOne, fieldTwo); + } + + private void checkNotBuilt() { + Preconditions.checkState(!_buildInvoked, "Build has already been called"); + } + } +} diff --git a/conjure-java-core/src/integrationInput/java/undertow/com/palantir/product/ErrorServiceEndpoints.java b/conjure-java-core/src/integrationInput/java/undertow/com/palantir/product/ErrorServiceEndpoints.java index ee6aa7f4e..51863aa31 100644 --- a/conjure-java-core/src/integrationInput/java/undertow/com/palantir/product/ErrorServiceEndpoints.java +++ b/conjure-java-core/src/integrationInput/java/undertow/com/palantir/product/ErrorServiceEndpoints.java @@ -1,6 +1,8 @@ package undertow.com.palantir.product; import com.google.common.collect.ImmutableList; +import com.palantir.conjure.java.undertow.lib.BinaryResponseBody; +import com.palantir.conjure.java.undertow.lib.Deserializer; import com.palantir.conjure.java.undertow.lib.Endpoint; import com.palantir.conjure.java.undertow.lib.Serializer; import com.palantir.conjure.java.undertow.lib.TypeMarker; @@ -11,8 +13,10 @@ import io.undertow.server.HttpServerExchange; import io.undertow.util.HttpString; import io.undertow.util.Methods; +import io.undertow.util.StatusCodes; import java.io.IOException; import java.util.List; +import java.util.Optional; import javax.annotation.processing.Generated; @Generated("com.palantir.conjure.java.services.UndertowServiceHandlerGenerator") @@ -32,7 +36,10 @@ public List endpoints(UndertowRuntime runtime) { return ImmutableList.of( new TestBasicErrorEndpoint(runtime, delegate), new TestImportedErrorEndpoint(runtime, delegate), - new TestMultipleErrorsAndPackagesEndpoint(runtime, delegate)); + new TestMultipleErrorsAndPackagesEndpoint(runtime, delegate), + new TestEmptyBodyEndpoint(runtime, delegate), + new TestBinaryEndpoint(runtime, delegate), + new TestOptionalBinaryEndpoint(runtime, delegate)); } private static final class TestBasicErrorEndpoint implements HttpHandler, Endpoint { @@ -40,29 +47,33 @@ private static final class TestBasicErrorEndpoint implements HttpHandler, Endpoi private final UndertowErrorService delegate; + private final Deserializer deserializer; + private final Serializer serializer; TestBasicErrorEndpoint(UndertowRuntime runtime, UndertowErrorService delegate) { this.runtime = runtime; this.delegate = delegate; + this.deserializer = runtime.bodySerDe().deserializer(new TypeMarker() {}, this); this.serializer = runtime.bodySerDe().serializer(new TypeMarker() {}, this); } @Override public void handleRequest(HttpServerExchange exchange) throws IOException, TestServerErrors.InvalidArgument { AuthHeader authHeader = runtime.auth().header(exchange); - String result = delegate.testBasicError(authHeader); + Boolean shouldThrowError = deserializer.deserialize(exchange); + String result = delegate.testBasicError(authHeader, shouldThrowError); serializer.serialize(result, exchange); } @Override public HttpString method() { - return Methods.GET; + return Methods.POST; } @Override public String template() { - return "/base/basic"; + return "/errors/basic"; } @Override @@ -86,11 +97,14 @@ private static final class TestImportedErrorEndpoint implements HttpHandler, End private final UndertowErrorService delegate; + private final Deserializer deserializer; + private final Serializer serializer; TestImportedErrorEndpoint(UndertowRuntime runtime, UndertowErrorService delegate) { this.runtime = runtime; this.delegate = delegate; + this.deserializer = runtime.bodySerDe().deserializer(new TypeMarker() {}, this); this.serializer = runtime.bodySerDe().serializer(new TypeMarker() {}, this); } @@ -98,18 +112,19 @@ private static final class TestImportedErrorEndpoint implements HttpHandler, End public void handleRequest(HttpServerExchange exchange) throws IOException, EndpointSpecificServerErrors.EndpointError { AuthHeader authHeader = runtime.auth().header(exchange); - String result = delegate.testImportedError(authHeader); + Boolean shouldThrowError = deserializer.deserialize(exchange); + String result = delegate.testImportedError(authHeader, shouldThrowError); serializer.serialize(result, exchange); } @Override public HttpString method() { - return Methods.GET; + return Methods.POST; } @Override public String template() { - return "/base/imported"; + return "/errors/imported"; } @Override @@ -133,11 +148,14 @@ private static final class TestMultipleErrorsAndPackagesEndpoint implements Http private final UndertowErrorService delegate; + private final Deserializer> deserializer; + private final Serializer serializer; TestMultipleErrorsAndPackagesEndpoint(UndertowRuntime runtime, UndertowErrorService delegate) { this.runtime = runtime; this.delegate = delegate; + this.deserializer = runtime.bodySerDe().deserializer(new TypeMarker>() {}, this); this.serializer = runtime.bodySerDe().serializer(new TypeMarker() {}, this); } @@ -145,20 +163,22 @@ private static final class TestMultipleErrorsAndPackagesEndpoint implements Http public void handleRequest(HttpServerExchange exchange) throws IOException, TestServerErrors.InvalidArgument, TestServerErrors.NotFound, EndpointSpecificTwoServerErrors.DifferentNamespace, - undertow.com.palantir.another.EndpointSpecificServerErrors.DifferentPackage { + undertow.com.palantir.another.EndpointSpecificServerErrors.DifferentPackage, + TestServerErrors.ComplicatedParameters { AuthHeader authHeader = runtime.auth().header(exchange); - String result = delegate.testMultipleErrorsAndPackages(authHeader); + Optional errorToThrow = deserializer.deserialize(exchange); + String result = delegate.testMultipleErrorsAndPackages(authHeader, errorToThrow); serializer.serialize(result, exchange); } @Override public HttpString method() { - return Methods.GET; + return Methods.POST; } @Override public String template() { - return "/base/multiple"; + return "/errors/multiple"; } @Override @@ -176,4 +196,149 @@ public HttpHandler handler() { return this; } } + + private static final class TestEmptyBodyEndpoint implements HttpHandler, Endpoint { + private final UndertowRuntime runtime; + + private final UndertowErrorService delegate; + + private final Deserializer deserializer; + + TestEmptyBodyEndpoint(UndertowRuntime runtime, UndertowErrorService delegate) { + this.runtime = runtime; + this.delegate = delegate; + this.deserializer = runtime.bodySerDe().deserializer(new TypeMarker() {}, this); + } + + @Override + public void handleRequest(HttpServerExchange exchange) throws IOException, TestServerErrors.InvalidArgument { + AuthHeader authHeader = runtime.auth().header(exchange); + Boolean shouldThrowError = deserializer.deserialize(exchange); + delegate.testEmptyBody(authHeader, shouldThrowError); + exchange.setStatusCode(StatusCodes.NO_CONTENT); + } + + @Override + public HttpString method() { + return Methods.POST; + } + + @Override + public String template() { + return "/errors/empty"; + } + + @Override + public String serviceName() { + return "ErrorService"; + } + + @Override + public String name() { + return "testEmptyBody"; + } + + @Override + public HttpHandler handler() { + return this; + } + } + + private static final class TestBinaryEndpoint implements HttpHandler, Endpoint { + private final UndertowRuntime runtime; + + private final UndertowErrorService delegate; + + private final Deserializer deserializer; + + TestBinaryEndpoint(UndertowRuntime runtime, UndertowErrorService delegate) { + this.runtime = runtime; + this.delegate = delegate; + this.deserializer = runtime.bodySerDe().deserializer(new TypeMarker() {}, this); + } + + @Override + public void handleRequest(HttpServerExchange exchange) throws IOException, TestServerErrors.InvalidArgument { + AuthHeader authHeader = runtime.auth().header(exchange); + Boolean shouldThrowError = deserializer.deserialize(exchange); + BinaryResponseBody result = delegate.testBinary(authHeader, shouldThrowError); + runtime.bodySerDe().serialize(result, exchange); + } + + @Override + public HttpString method() { + return Methods.POST; + } + + @Override + public String template() { + return "/errors/binary"; + } + + @Override + public String serviceName() { + return "ErrorService"; + } + + @Override + public String name() { + return "testBinary"; + } + + @Override + public HttpHandler handler() { + return this; + } + } + + private static final class TestOptionalBinaryEndpoint implements HttpHandler, Endpoint { + private final UndertowRuntime runtime; + + private final UndertowErrorService delegate; + + private final Deserializer deserializer; + + TestOptionalBinaryEndpoint(UndertowRuntime runtime, UndertowErrorService delegate) { + this.runtime = runtime; + this.delegate = delegate; + this.deserializer = runtime.bodySerDe().deserializer(new TypeMarker() {}, this); + } + + @Override + public void handleRequest(HttpServerExchange exchange) throws IOException, TestServerErrors.InvalidArgument { + AuthHeader authHeader = runtime.auth().header(exchange); + OptionalBinaryResponseMode mode = deserializer.deserialize(exchange); + Optional result = delegate.testOptionalBinary(authHeader, mode); + if (result.isPresent()) { + runtime.bodySerDe().serialize(result.get(), exchange); + } else { + exchange.setStatusCode(StatusCodes.NO_CONTENT); + } + } + + @Override + public HttpString method() { + return Methods.POST; + } + + @Override + public String template() { + return "/errors/optional-binary"; + } + + @Override + public String serviceName() { + return "ErrorService"; + } + + @Override + public String name() { + return "testOptionalBinary"; + } + + @Override + public HttpHandler handler() { + return this; + } + } } diff --git a/conjure-java-core/src/integrationInput/java/undertow/com/palantir/product/OptionalBinaryResponseMode.java b/conjure-java-core/src/integrationInput/java/undertow/com/palantir/product/OptionalBinaryResponseMode.java new file mode 100644 index 000000000..92d7a9089 --- /dev/null +++ b/conjure-java-core/src/integrationInput/java/undertow/com/palantir/product/OptionalBinaryResponseMode.java @@ -0,0 +1,239 @@ +package undertow.com.palantir.product; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import com.google.errorprone.annotations.Immutable; +import com.palantir.logsafe.Preconditions; +import com.palantir.logsafe.Safe; +import com.palantir.logsafe.SafeArg; +import com.palantir.logsafe.exceptions.SafeIllegalArgumentException; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Locale; +import java.util.function.Function; +import java.util.function.Supplier; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.annotation.processing.Generated; + +/** + * This class is used instead of a native enum to support unknown values. Rather than throw an exception, the + * {@link OptionalBinaryResponseMode#valueOf} method defaults to a new instantiation of + * {@link OptionalBinaryResponseMode} where {@link OptionalBinaryResponseMode#get} will return + * {@link OptionalBinaryResponseMode.Value#UNKNOWN}. + * + *

For example, {@code OptionalBinaryResponseMode.valueOf("corrupted value").get()} will return + * {@link OptionalBinaryResponseMode.Value#UNKNOWN}, but {@link OptionalBinaryResponseMode#toString} will return + * "corrupted value". + * + *

There is no method to access all instantiations of this class, since they cannot be known at compile time. + */ +@Generated("com.palantir.conjure.java.types.EnumGenerator") +@Safe +@Immutable +public final class OptionalBinaryResponseMode { + public static final OptionalBinaryResponseMode PRESENT = new OptionalBinaryResponseMode(Value.PRESENT, "PRESENT"); + + public static final OptionalBinaryResponseMode ABSENT = new OptionalBinaryResponseMode(Value.ABSENT, "ABSENT"); + + public static final OptionalBinaryResponseMode ERROR = new OptionalBinaryResponseMode(Value.ERROR, "ERROR"); + + private static final List values = + Collections.unmodifiableList(Arrays.asList(PRESENT, ABSENT, ERROR)); + + private final Value value; + + private final String string; + + private OptionalBinaryResponseMode(Value value, String string) { + this.value = value; + this.string = string; + } + + public Value get() { + return this.value; + } + + @Override + @JsonValue + public String toString() { + return this.string; + } + + @Override + public boolean equals(@Nullable Object other) { + return (this == other) + || (this.value == Value.UNKNOWN + && other instanceof OptionalBinaryResponseMode + && this.string.equals(((OptionalBinaryResponseMode) other).string)); + } + + @Override + public int hashCode() { + return this.string.hashCode(); + } + + @JsonCreator(mode = JsonCreator.Mode.DELEGATING) + public static OptionalBinaryResponseMode valueOf(@Nonnull @Safe String value) { + Preconditions.checkNotNull(value, "value cannot be null"); + String upperCasedValue = value.toUpperCase(Locale.ROOT); + switch (upperCasedValue) { + case "PRESENT": + return PRESENT; + case "ABSENT": + return ABSENT; + case "ERROR": + return ERROR; + default: + return new OptionalBinaryResponseMode(Value.UNKNOWN, upperCasedValue); + } + } + + public T accept(Visitor visitor) { + switch (value) { + case PRESENT: + return visitor.visitPresent(); + case ABSENT: + return visitor.visitAbsent(); + case ERROR: + return visitor.visitError(); + default: + return visitor.visitUnknown(string); + } + } + + public static List values() { + return values; + } + + @Generated("com.palantir.conjure.java.types.EnumGenerator") + public enum Value { + PRESENT, + + ABSENT, + + ERROR, + + UNKNOWN + } + + @Generated("com.palantir.conjure.java.types.EnumGenerator") + public interface Visitor { + T visitPresent(); + + T visitAbsent(); + + T visitError(); + + T visitUnknown(String unknownValue); + + static PresentStageVisitorBuilder builder() { + return new VisitorBuilder(); + } + } + + private static final class VisitorBuilder + implements PresentStageVisitorBuilder, + AbsentStageVisitorBuilder, + ErrorStageVisitorBuilder, + UnknownStageVisitorBuilder, + Completed_StageVisitorBuilder { + private Supplier presentVisitor; + + private Supplier absentVisitor; + + private Supplier errorVisitor; + + private Function<@Safe String, T> unknownVisitor; + + @Override + public AbsentStageVisitorBuilder visitPresent(@Nonnull Supplier presentVisitor) { + Preconditions.checkNotNull(presentVisitor, "presentVisitor cannot be null"); + this.presentVisitor = presentVisitor; + return this; + } + + @Override + public ErrorStageVisitorBuilder visitAbsent(@Nonnull Supplier absentVisitor) { + Preconditions.checkNotNull(absentVisitor, "absentVisitor cannot be null"); + this.absentVisitor = absentVisitor; + return this; + } + + @Override + public UnknownStageVisitorBuilder visitError(@Nonnull Supplier errorVisitor) { + Preconditions.checkNotNull(errorVisitor, "errorVisitor cannot be null"); + this.errorVisitor = errorVisitor; + return this; + } + + @Override + public Completed_StageVisitorBuilder visitUnknown(@Nonnull Function<@Safe String, T> unknownVisitor) { + Preconditions.checkNotNull(unknownVisitor, "unknownVisitor cannot be null"); + this.unknownVisitor = unknownType -> unknownVisitor.apply(unknownType); + return this; + } + + @Override + public Completed_StageVisitorBuilder throwOnUnknown() { + this.unknownVisitor = unknownType -> { + throw new SafeIllegalArgumentException( + "Unknown variant of the 'OptionalBinaryResponseMode' union", + SafeArg.of("unknownType", unknownType)); + }; + return this; + } + + @Override + public Visitor build() { + final Supplier presentVisitor = this.presentVisitor; + final Supplier absentVisitor = this.absentVisitor; + final Supplier errorVisitor = this.errorVisitor; + final Function<@Safe String, T> unknownVisitor = this.unknownVisitor; + return new Visitor() { + @Override + public T visitPresent() { + return presentVisitor.get(); + } + + @Override + public T visitAbsent() { + return absentVisitor.get(); + } + + @Override + public T visitError() { + return errorVisitor.get(); + } + + @Override + public T visitUnknown(String unknownType) { + return unknownVisitor.apply(unknownType); + } + }; + } + } + + public interface PresentStageVisitorBuilder { + AbsentStageVisitorBuilder visitPresent(@Nonnull Supplier presentVisitor); + } + + public interface AbsentStageVisitorBuilder { + ErrorStageVisitorBuilder visitAbsent(@Nonnull Supplier absentVisitor); + } + + public interface ErrorStageVisitorBuilder { + UnknownStageVisitorBuilder visitError(@Nonnull Supplier errorVisitor); + } + + public interface UnknownStageVisitorBuilder { + Completed_StageVisitorBuilder visitUnknown(@Nonnull Function<@Safe String, T> unknownVisitor); + + Completed_StageVisitorBuilder throwOnUnknown(); + } + + public interface Completed_StageVisitorBuilder { + Visitor build(); + } +} diff --git a/conjure-java-core/src/integrationInput/java/undertow/com/palantir/product/StringAlias.java b/conjure-java-core/src/integrationInput/java/undertow/com/palantir/product/StringAlias.java new file mode 100644 index 000000000..14b3ef384 --- /dev/null +++ b/conjure-java-core/src/integrationInput/java/undertow/com/palantir/product/StringAlias.java @@ -0,0 +1,55 @@ +package undertow.com.palantir.product; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import com.palantir.logsafe.Preconditions; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.annotation.processing.Generated; + +@Generated("com.palantir.conjure.java.types.AliasGenerator") +public final class StringAlias implements Comparable { + private final String value; + + private StringAlias(@Nonnull String value) { + this.value = Preconditions.checkNotNull(value, "value cannot be null"); + } + + @JsonValue + public String get() { + return value; + } + + @Override + public String toString() { + return value.toString(); + } + + @Override + public boolean equals(@Nullable Object other) { + return this == other || (other instanceof StringAlias && equalTo((StringAlias) other)); + } + + private boolean equalTo(StringAlias other) { + return this.value.equals(other.value); + } + + @Override + public int hashCode() { + return this.value.hashCode(); + } + + @Override + public int compareTo(StringAlias other) { + return value.compareTo(other.get()); + } + + public static StringAlias valueOf(String value) { + return of(value); + } + + @JsonCreator(mode = JsonCreator.Mode.DELEGATING) + public static StringAlias of(@Nonnull String value) { + return new StringAlias(value); + } +} diff --git a/conjure-java-core/src/integrationInput/java/undertow/com/palantir/product/TestErrors.java b/conjure-java-core/src/integrationInput/java/undertow/com/palantir/product/TestErrors.java index e43247999..9f5db9eae 100644 --- a/conjure-java-core/src/integrationInput/java/undertow/com/palantir/product/TestErrors.java +++ b/conjure-java-core/src/integrationInput/java/undertow/com/palantir/product/TestErrors.java @@ -7,6 +7,9 @@ @Generated("com.palantir.conjure.java.types.ErrorGenerator") public final class TestErrors { + public static final ErrorType COMPLICATED_PARAMETERS = + ErrorType.create(ErrorType.Code.INTERNAL, "Test:ComplicatedParameters"); + public static final ErrorType INVALID_ARGUMENT = ErrorType.create(ErrorType.Code.INVALID_ARGUMENT, "Test:InvalidArgument"); @@ -14,6 +17,12 @@ public final class TestErrors { private TestErrors() {} + /** Returns true if the {@link RemoteException} is named Test:ComplicatedParameters */ + public static boolean isComplicatedParameters(RemoteException remoteException) { + Preconditions.checkNotNull(remoteException, "remote exception must not be null"); + return COMPLICATED_PARAMETERS.name().equals(remoteException.getError().errorName()); + } + /** Returns true if the {@link RemoteException} is named Test:InvalidArgument */ public static boolean isInvalidArgument(RemoteException remoteException) { Preconditions.checkNotNull(remoteException, "remote exception must not be null"); diff --git a/conjure-java-core/src/integrationInput/java/undertow/com/palantir/product/TestServerErrors.java b/conjure-java-core/src/integrationInput/java/undertow/com/palantir/product/TestServerErrors.java index 6c4afd3ff..642dfc9b2 100644 --- a/conjure-java-core/src/integrationInput/java/undertow/com/palantir/product/TestServerErrors.java +++ b/conjure-java-core/src/integrationInput/java/undertow/com/palantir/product/TestServerErrors.java @@ -5,6 +5,7 @@ import com.palantir.logsafe.SafeArg; import com.palantir.logsafe.Unsafe; import com.palantir.logsafe.UnsafeArg; +import java.util.Map; import javax.annotation.Nullable; import javax.annotation.processing.Generated; import org.jetbrains.annotations.Contract; @@ -13,6 +14,16 @@ public final class TestServerErrors { private TestServerErrors() {} + public static ComplicatedParameters complicatedParameters( + @Safe Map complicatedObjectMap) { + return new ComplicatedParameters(complicatedObjectMap, null); + } + + public static ComplicatedParameters complicatedParameters( + @Safe Map complicatedObjectMap, @Nullable Throwable cause) { + return new ComplicatedParameters(complicatedObjectMap, cause); + } + public static InvalidArgument invalidArgument(@Safe String field, @Unsafe String value) { return new InvalidArgument(field, value, null); } @@ -29,6 +40,21 @@ public static NotFound notFound(@Safe String resource, @Nullable Throwable cause return new NotFound(resource, cause); } + /** + * Throws a {@link ComplicatedParameters} when {@code shouldThrow} is true. + * + * @param shouldThrow Cause the method to throw when true + * @param complicatedObjectMap + */ + @Contract("true, _ -> fail") + public static void throwIfComplicatedParameters( + boolean shouldThrow, @Safe Map complicatedObjectMap) + throws ComplicatedParameters { + if (shouldThrow) { + throw complicatedParameters(complicatedObjectMap); + } + } + /** * Throws a {@link InvalidArgument} when {@code shouldThrow} is true. * @@ -57,6 +83,13 @@ public static void throwIfNotFound(boolean shouldThrow, @Safe String resource) t } } + public static final class ComplicatedParameters extends CheckedServiceException { + private ComplicatedParameters( + @Safe Map complicatedObjectMap, @Nullable Throwable cause) { + super(TestErrors.COMPLICATED_PARAMETERS, cause, SafeArg.of("complicatedObjectMap", complicatedObjectMap)); + } + } + public static final class InvalidArgument extends CheckedServiceException { private InvalidArgument(@Safe String field, @Unsafe String value, @Nullable Throwable cause) { super(TestErrors.INVALID_ARGUMENT, cause, SafeArg.of("field", field), UnsafeArg.of("value", value)); diff --git a/conjure-java-core/src/integrationInput/java/undertow/com/palantir/product/UndertowErrorService.java b/conjure-java-core/src/integrationInput/java/undertow/com/palantir/product/UndertowErrorService.java index 813d8a452..827466ab0 100644 --- a/conjure-java-core/src/integrationInput/java/undertow/com/palantir/product/UndertowErrorService.java +++ b/conjure-java-core/src/integrationInput/java/undertow/com/palantir/product/UndertowErrorService.java @@ -1,31 +1,56 @@ package undertow.com.palantir.product; +import com.palantir.conjure.java.undertow.lib.BinaryResponseBody; import com.palantir.tokens.auth.AuthHeader; +import java.util.Optional; import javax.annotation.processing.Generated; @Generated("com.palantir.conjure.java.services.UndertowServiceInterfaceGenerator") public interface UndertowErrorService { /** - * @apiNote {@code GET /base/basic} + * @apiNote {@code POST /errors/basic} * @throws TestServerErrors.InvalidArgument */ - String testBasicError(AuthHeader authHeader) throws TestServerErrors.InvalidArgument; + String testBasicError(AuthHeader authHeader, boolean shouldThrowError) throws TestServerErrors.InvalidArgument; /** - * @apiNote {@code GET /base/imported} + * @apiNote {@code POST /errors/imported} * @throws EndpointSpecificServerErrors.EndpointError */ - String testImportedError(AuthHeader authHeader) throws EndpointSpecificServerErrors.EndpointError; + String testImportedError(AuthHeader authHeader, boolean shouldThrowError) + throws EndpointSpecificServerErrors.EndpointError; /** - * @apiNote {@code GET /base/multiple} + * @apiNote {@code POST /errors/multiple} * @throws TestServerErrors.InvalidArgument * @throws TestServerErrors.NotFound Something was not found. * @throws EndpointSpecificTwoServerErrors.DifferentNamespace * @throws undertow.com.palantir.another.EndpointSpecificServerErrors.DifferentPackage + * @throws TestServerErrors.ComplicatedParameters */ - String testMultipleErrorsAndPackages(AuthHeader authHeader) + String testMultipleErrorsAndPackages(AuthHeader authHeader, Optional errorToThrow) throws TestServerErrors.InvalidArgument, TestServerErrors.NotFound, EndpointSpecificTwoServerErrors.DifferentNamespace, - undertow.com.palantir.another.EndpointSpecificServerErrors.DifferentPackage; + undertow.com.palantir.another.EndpointSpecificServerErrors.DifferentPackage, + TestServerErrors.ComplicatedParameters; + + /** + * @apiNote {@code POST /errors/empty} + * @throws TestServerErrors.InvalidArgument + */ + void testEmptyBody(AuthHeader authHeader, boolean shouldThrowError) throws TestServerErrors.InvalidArgument; + + /** + * @apiNote {@code POST /errors/binary} + * @throws TestServerErrors.InvalidArgument + */ + BinaryResponseBody testBinary(AuthHeader authHeader, boolean shouldThrowError) + throws TestServerErrors.InvalidArgument; + + /** + * @apiNote {@code POST /errors/optional-binary} + * @throws TestServerErrors.InvalidArgument + */ + Optional testOptionalBinary(AuthHeader authHeader, OptionalBinaryResponseMode mode) + throws TestServerErrors.InvalidArgument; } diff --git a/conjure-java-core/src/main/java/com/palantir/conjure/java/Options.java b/conjure-java-core/src/main/java/com/palantir/conjure/java/Options.java index a54b37770..ec7ab6183 100644 --- a/conjure-java-core/src/main/java/com/palantir/conjure/java/Options.java +++ b/conjure-java-core/src/main/java/com/palantir/conjure/java/Options.java @@ -187,6 +187,21 @@ default boolean preferObjectBuilders() { return false; } + /** + * Warning: This is an experimental feature that may cause compilation failures! + *

+ * If enabled, endpoints that have associated errors will return a result type: a sealed interface permitting + * subclasses for the endpoint's return value, and each endpoint error. Each endpoint error is a subclass of + * {@link com.palantir.dialogue.EndpointError}. + *

+ * Producing JARs with this feature enabled will result in a compile-time break when consumers bump their dependency + * on the JAR. This is because the return types of every endpoint with associated errors will change. + */ + @Value.Default + default boolean generateDialogueEndpointErrorResultTypes() { + return false; + } + Optional packagePrefix(); Optional apiVersion(); diff --git a/conjure-java-core/src/main/java/com/palantir/conjure/java/services/ServiceGenerators.java b/conjure-java-core/src/main/java/com/palantir/conjure/java/services/ServiceGenerators.java index b09d1d51b..7d6abc50c 100644 --- a/conjure-java-core/src/main/java/com/palantir/conjure/java/services/ServiceGenerators.java +++ b/conjure-java-core/src/main/java/com/palantir/conjure/java/services/ServiceGenerators.java @@ -70,7 +70,7 @@ private static void addJavaDocForEndpointDefinitionInternal( ClassName.get( Packages.getPrefixedPackage( endpointError.getError().getPackage(), maybePackagePrefix), - ErrorGenerationUtils.errorExceptionsClassName( + ErrorGenerationUtils.serverErrorsClassName( endpointError.getError().getNamespace()), endpointError.getError().getName()), endpointError diff --git a/conjure-java-core/src/main/java/com/palantir/conjure/java/services/UndertowServiceHandlerGenerator.java b/conjure-java-core/src/main/java/com/palantir/conjure/java/services/UndertowServiceHandlerGenerator.java index 6685b5195..4c84a46ce 100644 --- a/conjure-java-core/src/main/java/com/palantir/conjure/java/services/UndertowServiceHandlerGenerator.java +++ b/conjure-java-core/src/main/java/com/palantir/conjure/java/services/UndertowServiceHandlerGenerator.java @@ -238,7 +238,7 @@ private TypeSpec generateEndpointHandler( ErrorTypeName errorTypeName = endpointError.getError(); return ClassName.get( Packages.getPrefixedPackage(errorTypeName.getPackage(), options.packagePrefix()), - ErrorGenerationUtils.errorExceptionsClassName(errorTypeName.getNamespace()), + ErrorGenerationUtils.serverErrorsClassName(errorTypeName.getNamespace()), errorTypeName.getName()); }) .toList()) diff --git a/conjure-java-core/src/main/java/com/palantir/conjure/java/services/UndertowServiceInterfaceGenerator.java b/conjure-java-core/src/main/java/com/palantir/conjure/java/services/UndertowServiceInterfaceGenerator.java index 105c06d48..c1b6210c8 100644 --- a/conjure-java-core/src/main/java/com/palantir/conjure/java/services/UndertowServiceInterfaceGenerator.java +++ b/conjure-java-core/src/main/java/com/palantir/conjure/java/services/UndertowServiceInterfaceGenerator.java @@ -118,7 +118,7 @@ private MethodSpec generateServiceInterfaceMethod( ErrorTypeName errorTypeName = endpointError.getError(); return ClassName.get( Packages.getPrefixedPackage(errorTypeName.getPackage(), options.packagePrefix()), - ErrorGenerationUtils.errorExceptionsClassName(errorTypeName.getNamespace()), + ErrorGenerationUtils.serverErrorsClassName(errorTypeName.getNamespace()), errorTypeName.getName()); }) .collect(Collectors.toList())); diff --git a/conjure-java-core/src/main/java/com/palantir/conjure/java/services/dialogue/DefaultStaticFactoryMethodGenerator.java b/conjure-java-core/src/main/java/com/palantir/conjure/java/services/dialogue/DefaultStaticFactoryMethodGenerator.java index 11e952e49..df69d5c73 100644 --- a/conjure-java-core/src/main/java/com/palantir/conjure/java/services/dialogue/DefaultStaticFactoryMethodGenerator.java +++ b/conjure-java-core/src/main/java/com/palantir/conjure/java/services/dialogue/DefaultStaticFactoryMethodGenerator.java @@ -13,19 +13,24 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.palantir.conjure.java.services.dialogue; +import com.google.common.base.CaseFormat; import com.google.common.collect.ImmutableMap; +import com.google.common.util.concurrent.ListenableFuture; import com.palantir.conjure.java.Options; import com.palantir.conjure.java.services.Auth; +import com.palantir.conjure.java.util.ErrorGenerationUtils; +import com.palantir.conjure.java.util.Packages; import com.palantir.conjure.java.util.Primitives; import com.palantir.conjure.spec.ArgumentDefinition; import com.palantir.conjure.spec.AuthType; import com.palantir.conjure.spec.BodyParameterType; import com.palantir.conjure.spec.CookieAuthType; import com.palantir.conjure.spec.EndpointDefinition; +import com.palantir.conjure.spec.EndpointError; import com.palantir.conjure.spec.EndpointName; +import com.palantir.conjure.spec.ErrorTypeName; import com.palantir.conjure.spec.ExternalReference; import com.palantir.conjure.spec.HeaderAuthType; import com.palantir.conjure.spec.HeaderParameterType; @@ -45,6 +50,7 @@ import com.palantir.conjure.visitor.TypeVisitor; import com.palantir.dialogue.ConjureRuntime; import com.palantir.dialogue.Deserializer; +import com.palantir.dialogue.DeserializerArgs; import com.palantir.dialogue.EndpointChannel; import com.palantir.dialogue.EndpointChannelFactory; import com.palantir.dialogue.PlainSerDe; @@ -108,8 +114,15 @@ public MethodSpec generate(ServiceDefinition def) { .ifPresent(impl::addField); impl.addField(bindEndpointChannel(def, endpoint)); - deserializer(endpoint.getEndpointName(), endpoint.getReturns()).ifPresent(impl::addField); - impl.addMethod(clientImpl(endpoint)); + deserializer( + ClassName.get( + Packages.getPrefixedPackage( + def.getServiceName().getPackage(), options.packagePrefix()), + className.simpleName(), + ErrorGenerationUtils.endpointResponseResultTypeName(endpoint.getEndpointName())), + endpoint) + .ifPresent(impl::addField); + impl.addMethod(clientImpl(className, endpoint)); }); impl.addMethod(DefaultStaticFactoryMethodGenerator.toStringMethod(className)); @@ -159,41 +172,74 @@ private Optional serializer(EndpointName endpointName, Type type) { .build()); } - private Optional deserializer(EndpointName endpointName, Optional type) { + private Optional deserializer(TypeName responseType, EndpointDefinition endpointDef) { + Optional type = endpointDef.getReturns(); TypeName className = Primitives.box(returnTypes.baseType(type)); - if (isBinaryOrOptionalBinary(className, returnTypes)) { + boolean generateResultTypes = ErrorGenerationUtils.shouldGenerateResultTypesForEndpoint( + options.generateDialogueEndpointErrorResultTypes(), endpointDef); + + if (returnTypes.isBinaryOrOptionalBinary(className) && !generateResultTypes) { return Optional.empty(); } - ParameterizedTypeName deserializerType = - ParameterizedTypeName.get(ClassName.get(Deserializer.class), className); - CodeBlock realDeserializer = CodeBlock.of("deserializer(new $T<$T>() {})", TypeMarker.class, className); - CodeBlock voidDeserializer = CodeBlock.of("emptyBodyDeserializer()"); + ParameterizedTypeName deserializerType = ParameterizedTypeName.get( + ClassName.get(Deserializer.class), generateResultTypes ? responseType : className); + CodeBlock initializer = CodeBlock.of( "$L.bodySerDe().$L", StaticFactoryMethodGenerator.RUNTIME, - type.isPresent() ? realDeserializer : voidDeserializer); + generateResultTypes + ? constructDeserializerWithEndpointErrors(endpointDef, className, responseType) + : constructDeserializer(type, className)); - return Optional.of(FieldSpec.builder(deserializerType, endpointName + "Deserializer") + return Optional.of(FieldSpec.builder(deserializerType, endpointDef.getEndpointName() + "Deserializer") .addModifiers(Modifier.PRIVATE, Modifier.FINAL) .initializer(initializer) .build()); } - private static boolean isBinaryOrOptionalBinary(TypeName className, ReturnTypeMapper returnTypes) { - return isBinary(className, returnTypes) || isOptionalBinary(className, returnTypes); + private CodeBlock constructDeserializer(Optional type, TypeName className) { + return type.isPresent() + ? CodeBlock.of("deserializer(new $T<$T>() {})", TypeMarker.class, className) + : CodeBlock.of("emptyBodyDeserializer()"); } - private static boolean isBinary(TypeName className, ReturnTypeMapper returnTypes) { - return className.equals(returnTypes.baseType(Type.primitive(PrimitiveType.BINARY))); + private CodeBlock constructDeserializerWithEndpointErrors( + EndpointDefinition endpointDef, TypeName className, TypeName responseType) { + CodeBlock.Builder deserializerBuilder = CodeBlock.builder(); + if (returnTypes.isBinary(className)) { + deserializerBuilder.add("inputStreamDeserializer("); + } else if (returnTypes.isOptionalBinary(className)) { + deserializerBuilder.add("optionalInputStreamDeserializer("); + } else { + deserializerBuilder.add("deserializer("); + } + deserializerBuilder.add(buildArgsForEndpointErrorDeserializer(endpointDef, responseType)); + deserializerBuilder.add(")"); + return deserializerBuilder.build(); } - private static boolean isOptionalBinary(TypeName className, ReturnTypeMapper returnTypes) { - return className.equals( - returnTypes.baseType(Type.optional(OptionalType.of(Type.primitive(PrimitiveType.BINARY))))); + private CodeBlock buildArgsForEndpointErrorDeserializer( + EndpointDefinition endpointDefinition, TypeName responseType) { + CodeBlock.Builder deserializerArgsBuilder = CodeBlock.builder() + .add("$T.<$T>builder()", DeserializerArgs.class, responseType) + .add(".baseType(new $T<>() {})", TypeMarker.class) + .add(".success(new $T<$T.Success>() {})", TypeMarker.class, responseType); + for (EndpointError err : endpointDefinition.getErrors()) { + ErrorTypeName errorTypeName = err.getError(); + String errorName = errorTypeName.getName(); + ClassName errorClass = ClassName.get( + Packages.getPrefixedPackage(errorTypeName.getPackage(), options.packagePrefix()), + ErrorGenerationUtils.errorTypesClassName(errorTypeName.getNamespace()), + CaseFormat.UPPER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, errorName)); + deserializerArgsBuilder.add( + ".error($T.name(), new $T<$T.$L>() {})", errorClass, TypeMarker.class, responseType, errorName); + } + deserializerArgsBuilder.add(".build()"); + return deserializerArgsBuilder.build(); } - private MethodSpec clientImpl(EndpointDefinition def) { + private MethodSpec clientImpl(ClassName className, EndpointDefinition def) { List params = parameterTypes.implementationMethodParams(def); MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder( def.getEndpointName().get()) @@ -207,8 +253,7 @@ private MethodSpec clientImpl(EndpointDefinition def) { .build()); } - TypeName returnType = - methodType.switchBy(returnTypes.baseType(def.getReturns()), returnTypes.async(def.getReturns())); + TypeName returnType = getReturnType(def, className); methodBuilder.returns(returnType); CodeBlock.Builder requestParams = CodeBlock.builder(); @@ -226,26 +271,44 @@ private MethodSpec clientImpl(EndpointDefinition def) { .build(); String codeBlock = methodType.switchBy( "$L.clients().callBlocking($L, $L.build(), $L);", "$L.clients().call($L, $L.build" + "(), $L);"); + boolean generateResultTypes = ErrorGenerationUtils.shouldGenerateResultTypesForEndpoint( + options.generateDialogueEndpointErrorResultTypes(), def); CodeBlock execute = CodeBlock.of( codeBlock, StaticFactoryMethodGenerator.RUNTIME, Names.endpointChannel(def), REQUEST, def.getReturns() - .filter(type -> isBinaryOrOptionalBinary(returnTypes.baseType(type), returnTypes)) + .filter(type -> !generateResultTypes + && returnTypes.isBinaryOrOptionalBinary(returnTypes.baseType(type))) .map(type -> StaticFactoryMethodGenerator.RUNTIME - + (isOptionalBinary(returnTypes.baseType(type), returnTypes) + + (returnTypes.isOptionalBinary(returnTypes.baseType(type)) ? ".bodySerDe().optionalInputStreamDeserializer()" : ".bodySerDe().inputStreamDeserializer()")) .orElseGet(() -> def.getEndpointName().get() + "Deserializer")); methodBuilder.addCode(request); - methodBuilder.addCode(methodType.switchBy(def.getReturns().isPresent() ? "return " : "", "return ")); + methodBuilder.addCode( + methodType.switchBy(def.getReturns().isPresent() || generateResultTypes ? "return " : "", "return ")); methodBuilder.addCode(execute); return methodBuilder.build(); } + private TypeName getReturnType(EndpointDefinition def, ClassName className) { + if (ErrorGenerationUtils.shouldGenerateResultTypesForEndpoint( + options.generateDialogueEndpointErrorResultTypes(), def)) { + ClassName responseResultTypeName = ClassName.get( + className.packageName(), + className.simpleName(), + ErrorGenerationUtils.endpointResponseResultTypeName(def.getEndpointName())); + return methodType.switchBy( + responseResultTypeName, + ParameterizedTypeName.get(ClassName.get(ListenableFuture.class), responseResultTypeName)); + } + return methodType.switchBy(returnTypes.baseType(def.getReturns()), returnTypes.async(def.getReturns())); + } + private CodeBlock generateParam(String endpointName, ArgumentDefinition param) { return param.getParamType().accept(new ParameterType.Visitor() { @Override diff --git a/conjure-java-core/src/main/java/com/palantir/conjure/java/services/dialogue/DialogueInterfaceGenerator.java b/conjure-java-core/src/main/java/com/palantir/conjure/java/services/dialogue/DialogueInterfaceGenerator.java index a52af6527..98cd645cf 100644 --- a/conjure-java-core/src/main/java/com/palantir/conjure/java/services/dialogue/DialogueInterfaceGenerator.java +++ b/conjure-java-core/src/main/java/com/palantir/conjure/java/services/dialogue/DialogueInterfaceGenerator.java @@ -18,6 +18,12 @@ import static java.util.stream.Collectors.toList; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonValue; +import com.google.common.base.CaseFormat; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.errorprone.annotations.CheckReturnValue; import com.google.errorprone.annotations.MustBeClosed; import com.palantir.conjure.java.ConjureAnnotations; import com.palantir.conjure.java.Options; @@ -26,10 +32,13 @@ import com.palantir.conjure.java.services.ServiceGenerators.EndpointErrorsJavaDoc; import com.palantir.conjure.java.services.ServiceGenerators.EndpointJavaDocGenerationOptions; import com.palantir.conjure.java.services.ServiceGenerators.RequestLineJavaDoc; +import com.palantir.conjure.java.util.ErrorGenerationUtils; +import com.palantir.conjure.java.util.ErrorGenerationUtils.ErrorNameToParameterExistenceMapping; import com.palantir.conjure.java.util.Packages; import com.palantir.conjure.spec.EndpointDefinition; +import com.palantir.conjure.spec.EndpointError; +import com.palantir.conjure.spec.ErrorTypeName; import com.palantir.conjure.spec.ServiceDefinition; -import com.palantir.conjure.spec.Type; import com.palantir.conjure.visitor.TypeVisitor; import com.palantir.dialogue.Channel; import com.palantir.dialogue.ConjureRuntime; @@ -43,14 +52,19 @@ import com.palantir.javapoet.CodeBlock; import com.palantir.javapoet.JavaFile; import com.palantir.javapoet.MethodSpec; +import com.palantir.javapoet.ParameterSpec; import com.palantir.javapoet.ParameterizedTypeName; import com.palantir.javapoet.TypeName; import com.palantir.javapoet.TypeSpec; import com.palantir.logsafe.Preconditions; +import com.palantir.logsafe.Safe; import com.palantir.logsafe.SafeArg; +import java.io.Closeable; +import java.io.IOException; import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; import java.util.Optional; -import java.util.function.Function; import javax.lang.model.element.Modifier; import org.apache.commons.lang3.StringUtils; @@ -59,27 +73,50 @@ public final class DialogueInterfaceGenerator { private final Options options; private final ParameterTypeMapper parameterTypes; private final ReturnTypeMapper returnTypes; + private final ErrorNameToParameterExistenceMapping errorNameToParameterExistenceMapping; public DialogueInterfaceGenerator( - Options options, ParameterTypeMapper parameterTypes, ReturnTypeMapper returnTypes) { + Options options, + ParameterTypeMapper parameterTypes, + ReturnTypeMapper returnTypes, + ErrorNameToParameterExistenceMapping errorNameToParameterExistenceMapping) { this.options = options; this.parameterTypes = parameterTypes; this.returnTypes = returnTypes; + this.errorNameToParameterExistenceMapping = errorNameToParameterExistenceMapping; } - public JavaFile generateBlocking(ServiceDefinition def, StaticFactoryMethodGenerator methodGenerator) { - return generate(def, Names.blockingClassName(def, options), returnTypes::baseType, methodGenerator); + public JavaFile generateBlocking( + ServiceDefinition def, + StaticFactoryMethodGenerator methodGenerator, + boolean generateDialogueEndpointErrorResultTypes) { + return generate( + def, + Names.blockingClassName(def, options), + StaticFactoryMethodType.BLOCKING, + methodGenerator, + generateDialogueEndpointErrorResultTypes); } - public JavaFile generateAsync(ServiceDefinition def, StaticFactoryMethodGenerator methodGenerator) { - return generate(def, Names.asyncClassName(def, options), returnTypes::async, methodGenerator); + public JavaFile generateAsync( + ServiceDefinition def, + StaticFactoryMethodGenerator methodGenerator, + boolean generateDialogueEndpointErrorResultTypes) { + return generate( + def, + Names.asyncClassName(def, options), + StaticFactoryMethodType.ASYNC, + methodGenerator, + generateDialogueEndpointErrorResultTypes); } private JavaFile generate( ServiceDefinition def, ClassName className, - Function, TypeName> returnTypeMapper, - StaticFactoryMethodGenerator methodGenerator) { + StaticFactoryMethodType serviceCallType, + StaticFactoryMethodGenerator methodGenerator, + boolean generateDialogueEndpointErrorResultTypes) { + String packageName = Packages.getPrefixedPackage(def.getServiceName().getPackage(), options.packagePrefix()); TypeSpec.Builder serviceBuilder = TypeSpec.interfaceBuilder(className) .addModifiers(Modifier.PUBLIC) .addAnnotation(ConjureAnnotations.getConjureGeneratedAnnotation(DialogueInterfaceGenerator.class)) @@ -103,9 +140,22 @@ private JavaFile generate( def.getDocs().ifPresent(docs -> serviceBuilder.addJavadoc("$L", StringUtils.appendIfMissing(docs.get(), "\n"))); serviceBuilder.addMethods(def.getEndpoints().stream() - .map(endpoint -> apiMethod(endpoint, returnTypeMapper)) + .map(endpoint -> apiMethod( + packageName, className, endpoint, generateDialogueEndpointErrorResultTypes, serviceCallType)) .collect(toList())); + // Create public sealed interface for the "response" type for each of the endpoints. + if (generateDialogueEndpointErrorResultTypes) { + List resultTypes = new ArrayList<>(); + for (EndpointDefinition endpointDef : def.getEndpoints()) { + if (endpointDef.getErrors().isEmpty()) { + continue; + } + resultTypes.add(responseTypeForEndpoint(packageName, className, endpointDef)); + } + serviceBuilder.addTypes(resultTypes); + } + MethodSpec staticFactoryMethod = methodGenerator.generate(def); serviceBuilder.addMethod(staticFactoryMethod); @@ -145,13 +195,166 @@ private JavaFile generate( .build()) .build()); - return JavaFile.builder( - Packages.getPrefixedPackage(def.getServiceName().getPackage(), options.packagePrefix()), - serviceBuilder.build()) - .build(); + return JavaFile.builder(packageName, serviceBuilder.build()).build(); + } + + private TypeSpec responseTypeForEndpoint(String packageName, ClassName className, EndpointDefinition endpointDef) { + ClassName responseTypeName = ClassName.get( + packageName, + className.simpleName(), + ErrorGenerationUtils.endpointResponseResultTypeName(endpointDef.getEndpointName())); + TypeSpec successRecord = createSuccessRecord(packageName, className, responseTypeName, endpointDef); + // Create a record for each of the endpoint's errors + List errorTypes = new ArrayList<>(); + for (EndpointError endpointError : endpointDef.getErrors()) { + errorTypes.add( + constructEndpointErrorType(endpointError, packageName, options.packagePrefix(), responseTypeName)); + } + + TypeSpec.Builder builder = TypeSpec.interfaceBuilder(responseTypeName) + .addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.SEALED) + .addPermittedSubclass( + ClassName.get(packageName, className.simpleName(), responseTypeName.simpleName(), "Success")) + .addPermittedSubclasses(errorTypes.stream() + .map(type -> ClassName.get( + packageName, className.simpleName(), responseTypeName.simpleName(), type.name())) + .toList()) + .addType(successRecord) + .addTypes(errorTypes); + + if (returnTypes.isBinaryOrOptionalBinary(returnTypes.baseType(endpointDef.getReturns()))) { + builder.addSuperinterface(ClassName.get(Closeable.class)) + .addMethod(MethodSpec.methodBuilder("close") + .addAnnotation(Override.class) + .addModifiers(Modifier.PUBLIC, Modifier.DEFAULT) + .returns(TypeName.VOID) + .addException(IOException.class) + .build()); + } + + return builder.build(); + } + + private TypeSpec constructEndpointErrorType( + EndpointError endpointError, + String packageName, + Optional packagePrefix, + ClassName responseTypeName) { + String errorTypeName = CaseFormat.UPPER_CAMEL.to( + CaseFormat.UPPER_UNDERSCORE, endpointError.getError().getName()); + String endpointErrorPackage = + Packages.getPrefixedPackage(endpointError.getError().getPackage(), packagePrefix); + ClassName errorTypesClassName = ClassName.get( + endpointErrorPackage, + ErrorGenerationUtils.errorTypesClassName( + endpointError.getError().getNamespace()), + errorTypeName); + TypeSpec.Builder endpointErrorTypeBuilder = TypeSpec.classBuilder(ClassName.get( + packageName, + responseTypeName.simpleName(), + endpointError.getError().getName())) + .addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL) + .addSuperinterface(responseTypeName); + ClassName parametersClassName = ClassName.get( + endpointErrorPackage, + ErrorGenerationUtils.errorTypesClassName( + endpointError.getError().getNamespace()), + ErrorGenerationUtils.errorParametersClassName( + endpointError.getError().getName())); + endpointErrorTypeBuilder + .superclass(ParameterizedTypeName.get( + ClassName.get(com.palantir.dialogue.EndpointError.class), parametersClassName)) + .addMethod(errorTypeConstructor(endpointError.getError(), parametersClassName, errorTypesClassName)); + return endpointErrorTypeBuilder.build(); + } + + private TypeSpec createSuccessRecord( + String packageName, ClassName className, ClassName responseTypeName, EndpointDefinition endpointDef) { + ClassName successTypeClassName = + ClassName.get(packageName, className.simpleName(), responseTypeName.simpleName(), "Success"); + TypeSpec.Builder successRecordBuilder = TypeSpec.recordBuilder(successTypeClassName); + MethodSpec.Builder successCtorBuilder = + MethodSpec.compactConstructorBuilder().addModifiers(Modifier.PUBLIC); + TypeName returnType = returnTypes.baseType(endpointDef.getReturns()); + if (!returnType.equals(TypeName.VOID)) { + successCtorBuilder.addStatement( + "$T.checkArgumentNotNull(value, \"value cannot be null\")", Preconditions.class); + ParameterSpec.Builder parameterBuilder = ParameterSpec.builder(returnType, "value"); + if (TypeName.get(InputStream.class).equals(returnType)) { + parameterBuilder.addAnnotation(MustBeClosed.class); + } + // The @JsonValue annotation ensures that deserialization delegates to the type of "value". + // https://github.com/FasterXML/jackson-databind/issues/3180 + parameterBuilder.addAnnotation(JsonValue.class); + successCtorBuilder.addParameter(parameterBuilder.build()); + } + + successRecordBuilder + .addModifiers(Modifier.PUBLIC, Modifier.STATIC) + .recordConstructor(successCtorBuilder.build()) + .addSuperinterface(responseTypeName); + + if (returnTypes.isBinaryOrOptionalBinary(returnType)) { + MethodSpec.Builder closeOverride = MethodSpec.methodBuilder("close") + .addAnnotation(Override.class) + .addModifiers(Modifier.PUBLIC) + .returns(TypeName.VOID) + .addException(IOException.class); + if (returnTypes.isBinary(returnType)) { + closeOverride.addCode( + CodeBlock.builder().addStatement("value.close()").build()); + } else { + closeOverride.addCode(CodeBlock.builder() + .beginControlFlow("if (value.isPresent())") + .addStatement("value.get().close()") + .endControlFlow() + .build()); + } + successRecordBuilder.addMethod(closeOverride.build()); + } + return successRecordBuilder.build(); } - private MethodSpec apiMethod(EndpointDefinition endpointDef, Function, TypeName> returnTypeMapper) { + private MethodSpec errorTypeConstructor( + ErrorTypeName errorTypeName, ClassName parametersClassName, ClassName errorTypesClassName) { + MethodSpec.Builder ctorBuilder = MethodSpec.constructorBuilder() + .addAnnotation(AnnotationSpec.builder(JsonCreator.class) + .addMember("mode", "$T.$L", JsonCreator.Mode.class, JsonCreator.Mode.PROPERTIES) + .build()) + .addParameter(ParameterSpec.builder(ClassName.get(String.class), "errorCode") + .addAnnotation(AnnotationSpec.builder(JsonProperty.class) + .addMember("value", "$S", "errorCode") + .build()) + .addAnnotation(Safe.class) + .build()) + .addParameter(ParameterSpec.builder(ClassName.get(String.class), "errorInstanceId") + .addAnnotation(AnnotationSpec.builder(JsonProperty.class) + .addMember("value", "$S", "errorInstanceId") + .build()) + .addAnnotation(Safe.class) + .build()); + // If the definition for the error does not specify any parameters, we do not need to attempt to deserialize the + // parameters field. + if (errorNameToParameterExistenceMapping.hasParameters(errorTypeName)) { + ctorBuilder.addParameter(ParameterSpec.builder(parametersClassName, "parameters") + .addAnnotation(AnnotationSpec.builder(JsonProperty.class) + .addMember("value", "$S", "parameters") + .build()) + .build()); + ctorBuilder.addStatement("super(errorCode, $T.name(), errorInstanceId, parameters)", errorTypesClassName); + } else { + ctorBuilder.addStatement( + "super(errorCode, $T.name(), errorInstanceId, new $T())", errorTypesClassName, parametersClassName); + } + return ctorBuilder.build(); + } + + private MethodSpec apiMethod( + String packageName, + ClassName className, + EndpointDefinition endpointDef, + boolean generateDialogueEndpointErrorResultTypes, + StaticFactoryMethodType serviceCallType) { MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder( endpointDef.getEndpointName().get()) .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT) @@ -184,11 +387,23 @@ private MethodSpec apiMethod(EndpointDefinition endpointDef, Function generate(ConjureDefinition conjureDefinition) { new DefaultClassNameVisitor(types.keySet(), options), types, ClassName.get(InputStream.class))); ParameterTypeMapper parameterMapper = new ParameterTypeMapper(parameterTypes, safetyEvaluator); - DialogueInterfaceGenerator interfaceGenerator = - new DialogueInterfaceGenerator(options, parameterMapper, new ReturnTypeMapper(returnTypes)); + ErrorNameToParameterExistenceMapping errorNameToParameterExistenceMapping = + ErrorNameToParameterExistenceMapping.from(conjureDefinition); + DialogueInterfaceGenerator interfaceGenerator = new DialogueInterfaceGenerator( + options, parameterMapper, new ReturnTypeMapper(returnTypes), errorNameToParameterExistenceMapping); TypeNameResolver typeNameResolver = typeName -> Preconditions.checkNotNull( types.get(typeName), "Referenced unknown TypeName", SafeArg.of("typeName", typeName)); @@ -87,6 +90,7 @@ public Stream generate(ConjureDefinition conjureDefinition) { return conjureDefinition.getServices().stream() .flatMap(serviceDef -> generateFilesForService( options.excludeDialogueAsyncInterfaces(), + options.generateDialogueEndpointErrorResultTypes(), serviceDef, endpoints, interfaceGenerator, @@ -96,6 +100,7 @@ public Stream generate(ConjureDefinition conjureDefinition) { private static Stream generateFilesForService( boolean excludeDialogueAsyncInterfaces, + boolean generateDialogueEndpointErrorResultTypes, ServiceDefinition serviceDef, DialogueEndpointsGenerator endpointsGenerator, DialogueInterfaceGenerator interfaceGenerator, @@ -105,9 +110,11 @@ private static Stream generateFilesForService( if (!serviceDef.getEndpoints().isEmpty()) { files.add(endpointsGenerator.endpointsClass(serviceDef)); } - files.add(interfaceGenerator.generateBlocking(serviceDef, blockingGenerator)); + files.add(interfaceGenerator.generateBlocking( + serviceDef, blockingGenerator, generateDialogueEndpointErrorResultTypes)); if (!excludeDialogueAsyncInterfaces) { - files.add(interfaceGenerator.generateAsync(serviceDef, asyncGenerator)); + files.add(interfaceGenerator.generateAsync( + serviceDef, asyncGenerator, generateDialogueEndpointErrorResultTypes)); } return files.stream(); } diff --git a/conjure-java-core/src/main/java/com/palantir/conjure/java/services/dialogue/ReturnTypeMapper.java b/conjure-java-core/src/main/java/com/palantir/conjure/java/services/dialogue/ReturnTypeMapper.java index 3883459f4..70dbbd3e9 100644 --- a/conjure-java-core/src/main/java/com/palantir/conjure/java/services/dialogue/ReturnTypeMapper.java +++ b/conjure-java-core/src/main/java/com/palantir/conjure/java/services/dialogue/ReturnTypeMapper.java @@ -19,6 +19,8 @@ import com.google.common.util.concurrent.ListenableFuture; import com.palantir.conjure.java.types.TypeMapper; import com.palantir.conjure.java.util.Primitives; +import com.palantir.conjure.spec.OptionalType; +import com.palantir.conjure.spec.PrimitiveType; import com.palantir.conjure.spec.Type; import com.palantir.javapoet.ClassName; import com.palantir.javapoet.ParameterizedTypeName; @@ -44,4 +46,16 @@ public TypeName baseType(Optional type) { public TypeName async(Optional type) { return ParameterizedTypeName.get(LISTENABLE_FUTURE, Primitives.box(baseType(type))); } + + boolean isBinaryOrOptionalBinary(TypeName returnType) { + return isBinary(returnType) || isOptionalBinary(returnType); + } + + boolean isOptionalBinary(TypeName returnType) { + return returnType.equals(baseType(Type.optional(OptionalType.of(Type.primitive(PrimitiveType.BINARY))))); + } + + boolean isBinary(TypeName returnType) { + return returnType.equals(baseType(Type.primitive(PrimitiveType.BINARY))); + } } diff --git a/conjure-java-core/src/main/java/com/palantir/conjure/java/types/CheckedErrorGenerator.java b/conjure-java-core/src/main/java/com/palantir/conjure/java/types/CheckedErrorGenerator.java index ee356f130..533ba95f2 100644 --- a/conjure-java-core/src/main/java/com/palantir/conjure/java/types/CheckedErrorGenerator.java +++ b/conjure-java-core/src/main/java/com/palantir/conjure/java/types/CheckedErrorGenerator.java @@ -89,7 +89,7 @@ private JavaFile generateErrorExceptionsForNamespace( }) .toList(); TypeSpec.Builder typeBuilder = TypeSpec.classBuilder( - ClassName.get(conjurePackage, ErrorGenerationUtils.errorExceptionsClassName(namespace))) + ClassName.get(conjurePackage, ErrorGenerationUtils.serverErrorsClassName(namespace))) .addModifiers(Modifier.PUBLIC, Modifier.FINAL) .addMethod(ErrorGenerationUtils.privateConstructor()) .addMethods(constructors) @@ -112,7 +112,7 @@ private static MethodSpec generateExceptionFactory( ClassName exceptionClass = ClassName.get( conjurePackage, - ErrorGenerationUtils.errorExceptionsClassName(errorDefinition.getNamespace()), + ErrorGenerationUtils.serverErrorsClassName(errorDefinition.getNamespace()), errorDefinition.getErrorName().getName()); MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder(methodName) @@ -156,7 +156,7 @@ private static List generateConditionalExceptionFactories( .map(errorDefinition -> { ClassName exceptionClassName = ClassName.get( conjurePackage, - ErrorGenerationUtils.errorExceptionsClassName(errorDefinition.getNamespace()), + ErrorGenerationUtils.serverErrorsClassName(errorDefinition.getNamespace()), errorDefinition.getErrorName().getName()); return ErrorGenerationUtils.conditionalStaticFactoryMethodBuilder( typeMapper, diff --git a/conjure-java-core/src/main/java/com/palantir/conjure/java/types/ErrorGenerator.java b/conjure-java-core/src/main/java/com/palantir/conjure/java/types/ErrorGenerator.java index bc0b826c1..5f8629e64 100644 --- a/conjure-java-core/src/main/java/com/palantir/conjure/java/types/ErrorGenerator.java +++ b/conjure-java-core/src/main/java/com/palantir/conjure/java/types/ErrorGenerator.java @@ -31,6 +31,7 @@ import com.palantir.conjure.spec.ConjureDefinition; import com.palantir.conjure.spec.ErrorDefinition; import com.palantir.conjure.spec.ErrorNamespace; +import com.palantir.conjure.spec.FieldDefinition; import com.palantir.conjure.spec.TypeDefinition; import com.palantir.javapoet.ClassName; import com.palantir.javapoet.CodeBlock; @@ -162,12 +163,39 @@ private JavaFile generateErrorTypesForNamespace( .addMethods(isRemoteExceptionDefinitions) .addAnnotation(ConjureAnnotations.getConjureGeneratedAnnotation(ErrorGenerator.class)); + if (options.generateDialogueEndpointErrorResultTypes()) { + typeBuilder.addTypes(generateErrorParameterRecords(errorTypeDefinitions, typeMapper)); + } + return JavaFile.builder(conjurePackage, typeBuilder.build()) .skipJavaLangImports(true) .indent(" ") .build(); } + private static List generateErrorParameterRecords( + List errorTypeDefinitions, TypeMapper typeMapper) { + return errorTypeDefinitions.stream() + .map(errorDefinition -> generateErrorParameterRecord(errorDefinition, typeMapper)) + .toList(); + } + + private static TypeSpec generateErrorParameterRecord(ErrorDefinition errorDefinition, TypeMapper typeMapper) { + TypeSpec.Builder parametersRecordBuilder = TypeSpec.recordBuilder(ErrorGenerationUtils.errorParametersClassName( + errorDefinition.getErrorName().getName())) + .addModifiers(Modifier.PUBLIC, Modifier.STATIC); + MethodSpec.Builder ctorBuilder = MethodSpec.constructorBuilder(); + for (FieldDefinition fieldDef : errorDefinition.getSafeArgs()) { + ctorBuilder.addParameter( + ErrorGenerationUtils.buildParameterWithSafetyAnnotationAndJsonProperty(typeMapper, fieldDef, true)); + } + for (FieldDefinition fieldDef : errorDefinition.getUnsafeArgs()) { + ctorBuilder.addParameter(ErrorGenerationUtils.buildParameterWithSafetyAnnotationAndJsonProperty( + typeMapper, fieldDef, false)); + } + return parametersRecordBuilder.recordConstructor(ctorBuilder.build()).build(); + } + private static MethodSpec generateExceptionFactory( TypeMapper typeMapper, ErrorDefinition entry, boolean withCause) { String methodName = CaseFormat.UPPER_CAMEL.to( @@ -194,6 +222,6 @@ private static MethodSpec generateExceptionFactory( } static ClassName errorTypesClassName(String conjurePackage, ErrorNamespace namespace) { - return ClassName.get(conjurePackage, namespace.get() + "Errors"); + return ClassName.get(conjurePackage, ErrorGenerationUtils.errorTypesClassName(namespace)); } } diff --git a/conjure-java-core/src/main/java/com/palantir/conjure/java/util/ErrorGenerationUtils.java b/conjure-java-core/src/main/java/com/palantir/conjure/java/util/ErrorGenerationUtils.java index 8afea775a..d9cfce7c8 100644 --- a/conjure-java-core/src/main/java/com/palantir/conjure/java/util/ErrorGenerationUtils.java +++ b/conjure-java-core/src/main/java/com/palantir/conjure/java/util/ErrorGenerationUtils.java @@ -16,6 +16,7 @@ package com.palantir.conjure.java.util; +import com.fasterxml.jackson.annotation.JsonProperty; import com.google.common.base.CaseFormat; import com.google.common.collect.ImmutableList; import com.palantir.conjure.java.ConjureAnnotations; @@ -24,7 +25,9 @@ import com.palantir.conjure.java.types.SafetyEvaluator; import com.palantir.conjure.java.types.TypeMapper; import com.palantir.conjure.spec.ConjureDefinition; +import com.palantir.conjure.spec.EndpointDefinition; import com.palantir.conjure.spec.EndpointError; +import com.palantir.conjure.spec.EndpointName; import com.palantir.conjure.spec.ErrorDefinition; import com.palantir.conjure.spec.ErrorNamespace; import com.palantir.conjure.spec.ErrorTypeName; @@ -37,6 +40,7 @@ import com.palantir.javapoet.TypeName; import com.palantir.logsafe.SafeArg; import com.palantir.logsafe.UnsafeArg; +import com.palantir.logsafe.exceptions.SafeIllegalStateException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -54,10 +58,58 @@ public static MethodSpec privateConstructor() { return MethodSpec.constructorBuilder().addModifiers(Modifier.PRIVATE).build(); } - public static String errorExceptionsClassName(ErrorNamespace namespace) { + public static String serverErrorsClassName(ErrorNamespace namespace) { return namespace.get() + "ServerErrors"; } + public static String errorTypesClassName(ErrorNamespace namespace) { + return namespace.get() + "Errors"; + } + + public static String errorParametersClassName(String errorName) { + return errorName + "Parameters"; + } + + /** + * The name of the sealed interface returned by endpoints with associated errors, permitting implementations for a + * successful result and each error type. + */ + public static String endpointResponseResultTypeName(EndpointName endpointName) { + return CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_CAMEL, endpointName.get()) + "Response"; + } + + public static boolean shouldGenerateResultTypesForEndpoint( + boolean generateResultTypesFlag, EndpointDefinition endpointDefinition) { + return generateResultTypesFlag && !endpointDefinition.getErrors().isEmpty(); + } + + /** + * This mapping from an error type to whether the error has parameters is used when constructing error records in + * Dialogue response types. + */ + public record ErrorNameToParameterExistenceMapping(Map errorTypeToHasParameters) { + public static ErrorNameToParameterExistenceMapping from(ConjureDefinition definition) { + return new ErrorNameToParameterExistenceMapping(definition.getErrors().stream() + .collect(Collectors.toMap( + error -> ErrorTypeName.builder() + .name(error.getErrorName().getName()) + .package_(error.getErrorName().getPackage()) + .namespace(error.getNamespace()) + .build(), + error -> !error.getUnsafeArgs().isEmpty() + || !error.getSafeArgs().isEmpty()))); + } + + public boolean hasParameters(ErrorTypeName errorTypeName) { + try { + return errorTypeToHasParameters.get(errorTypeName); + } catch (NullPointerException e) { + throw new SafeIllegalStateException( + "Error type definition not found", e, SafeArg.of("errorTypeName", errorTypeName)); + } + } + } + public record DeclaredEndpointErrors(Set errors) { public static DeclaredEndpointErrors from(ConjureDefinition definition) { return new DeclaredEndpointErrors(definition.getServices().stream() @@ -138,6 +190,22 @@ public static void addAllParametersWithSafetyAnnotationsToMethodBuilder( public static ParameterSpec buildParameterWithSafetyAnnotation( TypeMapper typeMapper, FieldDefinition argDefinition, boolean isSafe) { + return buildParameterWithSafetyAnnotationInternal(typeMapper, argDefinition, isSafe) + .build(); + } + + public static ParameterSpec buildParameterWithSafetyAnnotationAndJsonProperty( + TypeMapper typeMapper, FieldDefinition argDefinition, boolean isSafe) { + ParameterSpec.Builder parameterBuilder = + buildParameterWithSafetyAnnotationInternal(typeMapper, argDefinition, isSafe); + parameterBuilder.addAnnotation(AnnotationSpec.builder(JsonProperty.class) + .addMember("value", "$S", argDefinition.getFieldName()) + .build()); + return parameterBuilder.build(); + } + + private static ParameterSpec.Builder buildParameterWithSafetyAnnotationInternal( + TypeMapper typeMapper, FieldDefinition argDefinition, boolean isSafe) { Optional safety = Optional.of(isSafe ? LogSafety.SAFE : LogSafety.UNSAFE); String argName = argDefinition.getFieldName().get(); TypeName argType = ConjureAnnotations.withSafety(typeMapper.getClassName(argDefinition.getType()), safety); @@ -146,7 +214,7 @@ public static ParameterSpec buildParameterWithSafetyAnnotation( .getDocs() .ifPresent(docs -> parameterBuilder.addJavadoc("$L", StringUtils.appendIfMissing(Javadoc.render(docs), "\n"))); - return parameterBuilder.build(); + return parameterBuilder; } // Conditional factory method diff --git a/conjure-java-core/src/test/java/com/palantir/conjure/java/parameterized/TestCases.java b/conjure-java-core/src/test/java/com/palantir/conjure/java/parameterized/TestCases.java index a2ea80008..4df63b376 100644 --- a/conjure-java-core/src/test/java/com/palantir/conjure/java/parameterized/TestCases.java +++ b/conjure-java-core/src/test/java/com/palantir/conjure/java/parameterized/TestCases.java @@ -303,6 +303,22 @@ public final class TestCases { .build()) .generatorTypes(List.of(GeneratorType.UNDERTOW, GeneratorType.DIALOGUE, GeneratorType.OBJECT)) .build()) + .add(ParameterizedTestCase.builder() + .name("dialogue-endpoint-result-types") + .docs("Test generating dialogue interfaces with methods which return endpoint results") + .files(List.of( + Path.of("src/test/resources/example-endpoint-errors.yml"), + Path.of("src/test/resources/errors-for-endpoints.yml"))) + .options(Options.builder() + .generateDialogueEndpointErrorResultTypes(true) + .build()) + .generatorTypes(List.of( + GeneratorType.OBJECT, + GeneratorType.ERROR, + GeneratorType.CHECKED_ERROR, + GeneratorType.DIALOGUE, + GeneratorType.UNDERTOW)) + .build()) .build(); public static List get() { diff --git a/conjure-java-core/src/test/java/dialogueendpointresulttypes/com/palantir/conjure/java/ErrorResource.java b/conjure-java-core/src/test/java/dialogueendpointresulttypes/com/palantir/conjure/java/ErrorResource.java new file mode 100644 index 000000000..ad2274e8d --- /dev/null +++ b/conjure-java-core/src/test/java/dialogueendpointresulttypes/com/palantir/conjure/java/ErrorResource.java @@ -0,0 +1,111 @@ +/* + * (c) Copyright 2025 Palantir Technologies Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dialogueendpointresulttypes.com.palantir.conjure.java; + +import com.palantir.conjure.java.undertow.lib.BinaryResponseBody; +import com.palantir.tokens.auth.AuthHeader; +import dialogueendpointresulttypes.com.palantir.product.ComplicatedObject; +import dialogueendpointresulttypes.com.palantir.product.EndpointSpecificServerErrors; +import dialogueendpointresulttypes.com.palantir.product.EndpointSpecificServerErrors.EndpointError; +import dialogueendpointresulttypes.com.palantir.product.EndpointSpecificTwoServerErrors; +import dialogueendpointresulttypes.com.palantir.product.EndpointSpecificTwoServerErrors.DifferentNamespace; +import dialogueendpointresulttypes.com.palantir.product.ErrorService; +import dialogueendpointresulttypes.com.palantir.product.OptionalBinaryResponseMode; +import dialogueendpointresulttypes.com.palantir.product.StringAlias; +import dialogueendpointresulttypes.com.palantir.product.TestServerErrors; +import dialogueendpointresulttypes.com.palantir.product.TestServerErrors.ComplicatedParameters; +import dialogueendpointresulttypes.com.palantir.product.TestServerErrors.InvalidArgument; +import dialogueendpointresulttypes.com.palantir.product.TestServerErrors.NotFound; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +interface ErrorResource { + final class Impl implements ErrorService { + public static final String SUCCESS = "success!"; + + @Override + public String testBasicError(AuthHeader _authHeader, boolean shouldThrowError) throws InvalidArgument { + if (shouldThrowError) { + throw TestServerErrors.invalidArgument("field", "value"); + } + return SUCCESS; + } + + @Override + public String testImportedError(AuthHeader authHeader, boolean shouldThrowError) throws EndpointError { + if (shouldThrowError) { + throw EndpointSpecificServerErrors.endpointError("typeName", "typeDef"); + } + return SUCCESS; + } + + @Override + public String testMultipleErrorsAndPackages(AuthHeader authHeader, Optional errorToThrow) + throws InvalidArgument, NotFound, DifferentNamespace, + dialogueendpointresulttypes.com.palantir.another.EndpointSpecificServerErrors.DifferentPackage, + ComplicatedParameters { + if (errorToThrow.isPresent()) { + String error = errorToThrow.get(); + switch (error) { + case "invalidArgument": + throw TestServerErrors.invalidArgument("field", "value"); + case "notFound": + throw TestServerErrors.notFound("resource"); + case "differentNamespace": + throw EndpointSpecificTwoServerErrors.differentNamespace(); + case "differentPackage": + throw dialogueendpointresulttypes.com.palantir.another.EndpointSpecificServerErrors + .differentPackage(); + case "complicatedParameters": + throw TestServerErrors.complicatedParameters( + Map.of(1, ComplicatedObject.of(List.of("string"), StringAlias.of("alias")))); + default: + throw new IllegalArgumentException("Unknown error: " + error); + } + } + return SUCCESS; + } + + @Override + public void testEmptyBody(AuthHeader authHeader, boolean shouldThrowError) throws InvalidArgument { + if (shouldThrowError) { + throw TestServerErrors.invalidArgument("field", "value"); + } + } + + @Override + public BinaryResponseBody testBinary(AuthHeader authHeader, boolean shouldThrowError) throws InvalidArgument { + if (shouldThrowError) { + throw TestServerErrors.invalidArgument("field", "value"); + } + return output -> output.write(SUCCESS.getBytes(StandardCharsets.UTF_8)); + } + + @Override + public Optional testOptionalBinary(AuthHeader authHeader, OptionalBinaryResponseMode mode) + throws InvalidArgument { + if (mode.equals(OptionalBinaryResponseMode.ERROR)) { + throw TestServerErrors.invalidArgument("field", "value"); + } else if (mode.equals(OptionalBinaryResponseMode.PRESENT)) { + return Optional.of(output -> output.write(SUCCESS.getBytes(StandardCharsets.UTF_8))); + } + return Optional.empty(); + } + } +} diff --git a/conjure-java-core/src/test/java/dialogueendpointresulttypes/com/palantir/conjure/java/UndertowServiceEteTest.java b/conjure-java-core/src/test/java/dialogueendpointresulttypes/com/palantir/conjure/java/UndertowServiceEteTest.java new file mode 100644 index 000000000..52db3987a --- /dev/null +++ b/conjure-java-core/src/test/java/dialogueendpointresulttypes/com/palantir/conjure/java/UndertowServiceEteTest.java @@ -0,0 +1,274 @@ +/* + * (c) Copyright 2025 Palantir Technologies Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dialogueendpointresulttypes.com.palantir.conjure.java; + +import static com.palantir.conjure.java.EteTestServer.clientConfiguration; +import static org.assertj.core.api.Assertions.assertThat; + +import com.google.common.collect.Iterables; +import com.palantir.conjure.java.TestBase; +import com.palantir.conjure.java.undertow.runtime.ConjureHandler; +import com.palantir.dialogue.clients.DialogueClients; +import com.palantir.tokens.auth.AuthHeader; +import dialogueendpointresulttypes.com.palantir.product.ComplicatedObject; +import dialogueendpointresulttypes.com.palantir.product.EndpointSpecificErrors; +import dialogueendpointresulttypes.com.palantir.product.EndpointSpecificTwoErrors; +import dialogueendpointresulttypes.com.palantir.product.ErrorServiceBlocking; +import dialogueendpointresulttypes.com.palantir.product.ErrorServiceBlocking.TestBasicErrorResponse; +import dialogueendpointresulttypes.com.palantir.product.ErrorServiceBlocking.TestBinaryResponse; +import dialogueendpointresulttypes.com.palantir.product.ErrorServiceBlocking.TestEmptyBodyResponse; +import dialogueendpointresulttypes.com.palantir.product.ErrorServiceBlocking.TestImportedErrorResponse; +import dialogueendpointresulttypes.com.palantir.product.ErrorServiceBlocking.TestMultipleErrorsAndPackagesResponse; +import dialogueendpointresulttypes.com.palantir.product.ErrorServiceBlocking.TestOptionalBinaryResponse; +import dialogueendpointresulttypes.com.palantir.product.ErrorServiceEndpoints; +import dialogueendpointresulttypes.com.palantir.product.OptionalBinaryResponseMode; +import dialogueendpointresulttypes.com.palantir.product.StringAlias; +import dialogueendpointresulttypes.com.palantir.product.TestErrors; +import io.undertow.Handlers; +import io.undertow.Undertow; +import io.undertow.UndertowOptions; +import io.undertow.server.HttpHandler; +import java.io.IOException; +import java.io.InputStream; +import java.net.InetSocketAddress; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.Optional; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.parallel.Execution; +import org.junit.jupiter.api.parallel.ExecutionMode; + +@Execution(ExecutionMode.CONCURRENT) +public final class UndertowServiceEteTest extends TestBase { + private static final AuthHeader AUTH_HEADER = AuthHeader.valueOf("authHeader"); + private static Undertow server; + private static int port; + private final ErrorServiceBlocking errorServiceClient; + + public UndertowServiceEteTest() { + this.errorServiceClient = DialogueClients.create(ErrorServiceBlocking.class, clientConfiguration(port)); + } + + @BeforeAll + public static void before() { + + HttpHandler handler = ConjureHandler.builder() + .services(ErrorServiceEndpoints.of(new ErrorResource.Impl())) + .build(); + + server = Undertow.builder() + .setServerOption(UndertowOptions.DECODE_URL, false) + .addHttpListener(0, "0.0.0.0") + .setHandler(Handlers.path().addPrefixPath("/test-example/api", handler)) + .build(); + server.start(); + port = ((InetSocketAddress) + Iterables.getOnlyElement(server.getListenerInfo()).getAddress()) + .getPort(); + } + + @AfterAll + public static void after() { + if (server != null) { + server.stop(); + } + } + + @Test + public void error_client_returns_basic_result() { + TestBasicErrorResponse result = errorServiceClient.testBasicError(AUTH_HEADER, false); + assertThat(result) + .isInstanceOfSatisfying(TestBasicErrorResponse.Success.class, success -> assertThat(success.value()) + .isEqualTo(ErrorResource.Impl.SUCCESS)); + } + + @Test + public void error_client_returns_basic_error() { + TestBasicErrorResponse result = errorServiceClient.testBasicError(AUTH_HEADER, true); + assertThat(result) + .isInstanceOfSatisfying( + TestBasicErrorResponse.InvalidArgument.class, + error -> assertInvalidArgumentError( + error.getErrorCode(), + error.getErrorName(), + error.getParams().field(), + error.getParams().value())); + } + + @Test + public void error_client_returns_imported_error() { + TestImportedErrorResponse result = errorServiceClient.testImportedError(AUTH_HEADER, true); + assertThat(result).isInstanceOfSatisfying(TestImportedErrorResponse.EndpointError.class, error -> { + assertThat(error.getErrorCode()) + .isEqualTo(EndpointSpecificErrors.ENDPOINT_ERROR.code().name()); + assertThat(error.getErrorName()).isEqualTo(EndpointSpecificErrors.ENDPOINT_ERROR.name()); + assertThat(error.getParams()).satisfies(params -> { + assertThat(params.typeDef()).isEqualTo("typeDef"); + assertThat(params.typeName()).isEqualTo("typeName"); + }); + }); + } + + @Test + public void error_client_returns_one_of_many_errors() { + assertThat(errorServiceClient.testMultipleErrorsAndPackages(AUTH_HEADER, Optional.of("invalidArgument"))) + .isInstanceOfSatisfying( + TestMultipleErrorsAndPackagesResponse.InvalidArgument.class, + error -> assertInvalidArgumentError( + error.getErrorCode(), + error.getErrorName(), + error.getParams().field(), + error.getParams().value())); + assertThat(errorServiceClient.testMultipleErrorsAndPackages(AUTH_HEADER, Optional.of("notFound"))) + .isInstanceOfSatisfying(TestMultipleErrorsAndPackagesResponse.NotFound.class, error -> { + assertThat(error.getErrorCode()) + .isEqualTo(TestErrors.NOT_FOUND.code().name()); + assertThat(error.getErrorName()).isEqualTo(TestErrors.NOT_FOUND.name()); + assertThat(error.getParams()) + .satisfies(params -> assertThat(params.resource()).isEqualTo("resource")); + }); + assertThat(errorServiceClient.testMultipleErrorsAndPackages(AUTH_HEADER, Optional.of("differentNamespace"))) + .isInstanceOfSatisfying(TestMultipleErrorsAndPackagesResponse.DifferentNamespace.class, error -> { + assertThat(error.getErrorCode()) + .isEqualTo(EndpointSpecificTwoErrors.DIFFERENT_NAMESPACE + .code() + .name()); + assertThat(error.getErrorName()).isEqualTo(EndpointSpecificTwoErrors.DIFFERENT_NAMESPACE.name()); + }); + assertThat(errorServiceClient.testMultipleErrorsAndPackages(AUTH_HEADER, Optional.of("differentPackage"))) + .isInstanceOfSatisfying(TestMultipleErrorsAndPackagesResponse.DifferentPackage.class, error -> { + assertThat(error.getErrorCode()) + .isEqualTo( + dialogueendpointresulttypes.com.palantir.another.EndpointSpecificErrors + .DIFFERENT_PACKAGE + .code() + .name()); + assertThat(error.getErrorName()) + .isEqualTo( + dialogueendpointresulttypes.com.palantir.another.EndpointSpecificErrors + .DIFFERENT_PACKAGE + .name()); + }); + assertThat(errorServiceClient.testMultipleErrorsAndPackages(AUTH_HEADER, Optional.of("complicatedParameters"))) + .isInstanceOfSatisfying(TestMultipleErrorsAndPackagesResponse.ComplicatedParameters.class, error -> { + assertThat(error.getErrorCode()) + .isEqualTo(TestErrors.COMPLICATED_PARAMETERS.code().name()); + assertThat(error.getErrorName()).isEqualTo(TestErrors.COMPLICATED_PARAMETERS.name()); + assertThat(error.getParams()).satisfies(params -> assertThat(params.complicatedObjectMap()) + .containsEntry(1, ComplicatedObject.of(List.of("string"), StringAlias.of("alias")))); + }); + } + + @Test + public void error_client_test_empty() { + TestEmptyBodyResponse result = errorServiceClient.testEmptyBody(AUTH_HEADER, false); + assertThat(result).isInstanceOf(TestEmptyBodyResponse.Success.class); + } + + @Test + public void error_client_test_empty_error() { + TestEmptyBodyResponse result = errorServiceClient.testEmptyBody(AUTH_HEADER, true); + assertThat(result) + .isInstanceOfSatisfying( + TestEmptyBodyResponse.InvalidArgument.class, + invalidArgument -> assertInvalidArgumentError( + invalidArgument.getErrorCode(), + invalidArgument.getErrorName(), + invalidArgument.getParams().field(), + invalidArgument.getParams().value())); + } + + @Test + public void error_client_binary_response() { + try (TestBinaryResponse result = errorServiceClient.testBinary(AUTH_HEADER, false)) { + assertThat(result).isInstanceOfSatisfying(TestBinaryResponse.Success.class, success -> { + try (InputStream is = success.value()) { + assertThat(is.readAllBytes()) + .isEqualTo(ErrorResource.Impl.SUCCESS.getBytes(StandardCharsets.UTF_8)); + + } catch (IOException e) { + throw new RuntimeException(e); + } + }); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Test + public void error_client_binary_response_error() { + TestBinaryResponse result = errorServiceClient.testBinary(AUTH_HEADER, true); + assertThat(result) + .isInstanceOfSatisfying( + TestBinaryResponse.InvalidArgument.class, + invalidArgument -> assertInvalidArgumentError( + invalidArgument.getErrorCode(), + invalidArgument.getErrorName(), + invalidArgument.getParams().field(), + invalidArgument.getParams().value())); + } + + @Test + public void error_client_optional_binary_response() { + try (TestOptionalBinaryResponse result = + errorServiceClient.testOptionalBinary(AUTH_HEADER, OptionalBinaryResponseMode.PRESENT)) { + assertThat(result).isInstanceOfSatisfying(TestOptionalBinaryResponse.Success.class, success -> { + assertThat(success.value()).isPresent(); + try (InputStream is = success.value().get()) { + assertThat(is.readAllBytes()) + .isEqualTo(ErrorResource.Impl.SUCCESS.getBytes(StandardCharsets.UTF_8)); + } catch (IOException e) { + throw new RuntimeException(e); + } + }); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Test + public void error_client_optional_binary_response_absent() { + TestOptionalBinaryResponse result = + errorServiceClient.testOptionalBinary(AUTH_HEADER, OptionalBinaryResponseMode.ABSENT); + assertThat(result) + .isInstanceOfSatisfying(TestOptionalBinaryResponse.Success.class, success -> assertThat(success.value()) + .isEmpty()); + } + + @Test + public void error_client_optional_binary_response_error() { + TestOptionalBinaryResponse result = + errorServiceClient.testOptionalBinary(AUTH_HEADER, OptionalBinaryResponseMode.ERROR); + assertThat(result) + .isInstanceOfSatisfying( + TestOptionalBinaryResponse.InvalidArgument.class, + invalidArgument -> assertInvalidArgumentError( + invalidArgument.getErrorCode(), + invalidArgument.getErrorName(), + invalidArgument.getParams().field(), + invalidArgument.getParams().value())); + } + + private static void assertInvalidArgumentError(String errorCode, String errorName, String field, String value) { + assertThat(errorCode).isEqualTo(TestErrors.INVALID_ARGUMENT.code().name()); + assertThat(errorName).isEqualTo(TestErrors.INVALID_ARGUMENT.name()); + assertThat(field).isEqualTo("field"); + assertThat(value).isEqualTo("value"); + } +} diff --git a/conjure-java-core/src/test/resources/example-endpoint-errors.yml b/conjure-java-core/src/test/resources/example-endpoint-errors.yml index 7f686c164..c02a08807 100644 --- a/conjure-java-core/src/test/resources/example-endpoint-errors.yml +++ b/conjure-java-core/src/test/resources/example-endpoint-errors.yml @@ -3,6 +3,18 @@ types: importedErrors: errors-for-endpoints.yml definitions: default-package: com.palantir.product + objects: + OptionalBinaryResponseMode: + values: + - PRESENT + - ABSENT + - ERROR + StringAlias: + alias: string + ComplicatedObject: + fields: + fieldOne: list + fieldTwo: optional errors: InvalidArgument: namespace: Test @@ -16,25 +28,36 @@ types: code: NOT_FOUND safe-args: resource: string + ComplicatedParameters: + namespace: Test + code: INTERNAL + safe-args: + complicatedObjectMap: map services: ErrorService: name: Error Service package: com.palantir.product default-auth: header - base-path: /base + base-path: /errors endpoints: testBasicError: - http: GET /basic + http: POST /basic + args: + shouldThrowError: boolean returns: string errors: - InvalidArgument testImportedError: - http: GET /imported + http: POST /imported + args: + shouldThrowError: boolean returns: string errors: - importedErrors.EndpointError testMultipleErrorsAndPackages: - http: GET /multiple + http: POST /multiple + args: + errorToThrow: optional returns: string errors: - InvalidArgument @@ -42,3 +65,24 @@ services: docs: Something was not found. - importedErrors.DifferentNamespace - importedErrors.DifferentPackage + - ComplicatedParameters + testEmptyBody: + http: POST /empty + args: + shouldThrowError: boolean + errors: + - InvalidArgument + testBinary: + http: POST /binary + args: + shouldThrowError: boolean + returns: binary + errors: + - InvalidArgument + testOptionalBinary: + http: POST /optional-binary + args: + mode: OptionalBinaryResponseMode + returns: optional + errors: + - InvalidArgument diff --git a/conjure-java-core/src/test/resources/example-types.yml b/conjure-java-core/src/test/resources/example-types.yml index 917ff5b87..4af183f6f 100644 --- a/conjure-java-core/src/test/resources/example-types.yml +++ b/conjure-java-core/src/test/resources/example-types.yml @@ -534,4 +534,4 @@ types: u100: string NestedOptionalSetExample: fields: - set: set>> \ No newline at end of file + set: set>> diff --git a/conjure-java/src/main/java/com/palantir/conjure/java/cli/ConjureJavaCli.java b/conjure-java/src/main/java/com/palantir/conjure/java/cli/ConjureJavaCli.java index 0dbea56d3..1e45637e5 100644 --- a/conjure-java/src/main/java/com/palantir/conjure/java/cli/ConjureJavaCli.java +++ b/conjure-java/src/main/java/com/palantir/conjure/java/cli/ConjureJavaCli.java @@ -232,6 +232,13 @@ public static final class GenerateCommand implements Runnable { + "that for objects without any fields, this will still generate the static factory method.") private boolean preferObjectBuilders; + @CommandLine.Option( + names = "--generateDialogueEndpointErrorResultTypes", + defaultValue = "false", + description = + "Generate result types in Dialogue clients for endpoints with errors associated with them.") + private boolean generateDialogueEndpointErrorResultTypes; + @SuppressWarnings("unused") @CommandLine.Unmatched private List unmatchedOptions; @@ -303,6 +310,7 @@ CliConfiguration getConfiguration() { .unionsWithUnknownValues(unionsWithUnknownValues) .externalFallbackTypes(externalFallbackTypes) .preferObjectBuilders(preferObjectBuilders) + .generateDialogueEndpointErrorResultTypes(generateDialogueEndpointErrorResultTypes) .build()) .build(); } diff --git a/readme.md b/readme.md index 3095cf099..46b04c7af 100644 --- a/readme.md +++ b/readme.md @@ -51,6 +51,12 @@ The recommended way to use conjure-java is via a build tool like [gradle-conjure --preferObjectBuilders Exclude static factory methods from generated objects with one or more fields. Note that for objects without any fields, this will still generate the static factory method. + --generateDialogueEndpointErrorResultTypes + This is an experimental feature that may cause compilation failures! If enabled, endpoints that + have associated errors will return a result type: a sealed interface permitting subclasses for the + endpoint's return value, and each endpoint error. Producing JARs with this feature enabled will + result in a compile-time break when consumers bump their dependency on the JAR. This is because the + return types of every endpoint with associated errors will change. ### Known Tag Values diff --git a/versions.lock b/versions.lock index d335633cf..e3e48f1e5 100644 --- a/versions.lock +++ b/versions.lock @@ -1,66 +1,67 @@ # Run ./gradlew writeVersionsLocks to regenerate this file com.atlassian.commonmark:commonmark:0.12.1 (1 constraints: 36052a3b) -com.fasterxml.jackson.core:jackson-annotations:2.18.2 (24 constraints: 6695c722) -com.fasterxml.jackson.core:jackson-core:2.18.2 (22 constraints: 31c1bf29) -com.fasterxml.jackson.core:jackson-databind:2.18.2 (35 constraints: 0f9bef6e) -com.fasterxml.jackson.dataformat:jackson-dataformat-cbor:2.18.2 (4 constraints: ea651651) -com.fasterxml.jackson.dataformat:jackson-dataformat-smile:2.18.2 (1 constraints: 811ca2a4) +com.fasterxml.jackson.core:jackson-annotations:2.18.2 (24 constraints: 6f955f3c) +com.fasterxml.jackson.core:jackson-core:2.18.2 (22 constraints: 3ac13534) +com.fasterxml.jackson.core:jackson-databind:2.18.2 (35 constraints: 1e9be198) +com.fasterxml.jackson.dataformat:jackson-dataformat-cbor:2.18.2 (4 constraints: f065d052) +com.fasterxml.jackson.dataformat:jackson-dataformat-smile:2.18.2 (1 constraints: 831caaa4) com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.18.2 (3 constraints: 3f25a610) -com.fasterxml.jackson.datatype:jackson-datatype-guava:2.18.2 (3 constraints: f839a026) -com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.18.2 (4 constraints: 303f5392) -com.fasterxml.jackson.datatype:jackson-datatype-joda:2.18.2 (2 constraints: 322b47b0) -com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.18.2 (2 constraints: 322b47b0) -com.github.ben-manes.caffeine:caffeine:3.1.8 (10 constraints: 63b008f3) +com.fasterxml.jackson.datatype:jackson-datatype-guava:2.18.2 (3 constraints: fa395027) +com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.18.2 (4 constraints: 323faf92) +com.fasterxml.jackson.datatype:jackson-datatype-joda:2.18.2 (2 constraints: 342ba3b0) +com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.18.2 (2 constraints: 342ba3b0) +com.github.ben-manes.caffeine:caffeine:3.2.0 (11 constraints: 9bc0541c) com.google.auto:auto-common:1.2.2 (2 constraints: 1d175731) com.google.code.findbugs:jsr305:3.0.2 (35 constraints: b94940f6) -com.google.errorprone:error_prone_annotations:2.7.1 (28 constraints: 0fd13479) +com.google.errorprone:error_prone_annotations:2.7.1 (29 constraints: 9fe1eda8) com.google.guava:failureaccess:1.0.2 (1 constraints: 150ae2b4) -com.google.guava:guava:33.3.1-jre (36 constraints: 5d9d1a31) +com.google.guava:guava:33.4.0-jre (38 constraints: 93c35a1c) com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava (1 constraints: bd17c918) com.google.j2objc:j2objc-annotations:3.0.0 (1 constraints: 150aeab4) com.palantir.common:streams:2.3.0 (1 constraints: 0705fe35) com.palantir.conjure:conjure-api-objects:4.50.0 (3 constraints: 9327531f) com.palantir.conjure:conjure-generator-common:4.50.0 (2 constraints: fd13a782) -com.palantir.conjure.java.api:errors:2.56.0 (6 constraints: e3712dad) -com.palantir.conjure.java.api:service-config:2.56.0 (6 constraints: 35660ed9) -com.palantir.conjure.java.api:ssl-config:2.56.0 (5 constraints: b64f1a2f) -com.palantir.conjure.java.runtime:client-config:8.9.0 (6 constraints: 9e6a9c2e) -com.palantir.conjure.java.runtime:conjure-java-jackson-optimizations:8.9.0 (1 constraints: 571c4b88) -com.palantir.conjure.java.runtime:conjure-java-jackson-serialization:8.9.0 (4 constraints: 6b46b1dd) -com.palantir.conjure.java.runtime:keystores:8.9.0 (3 constraints: 8e29f2f9) -com.palantir.deadlines:deadlines:0.8.0 (1 constraints: 0a050336) -com.palantir.dialogue:dialogue-apache-hc5-client:3.135.0 (2 constraints: d6299c4c) -com.palantir.dialogue:dialogue-blocking-channels:3.135.0 (2 constraints: 372426f4) -com.palantir.dialogue:dialogue-clients:3.135.0 (1 constraints: 6e05b840) -com.palantir.dialogue:dialogue-core:3.135.0 (3 constraints: 053e0d80) -com.palantir.dialogue:dialogue-futures:3.135.0 (3 constraints: 5b347844) -com.palantir.dialogue:dialogue-serde:3.135.0 (3 constraints: 432f2c13) -com.palantir.dialogue:dialogue-target:3.135.0 (7 constraints: cc7787ea) +com.palantir.conjure.java.api:errors:2.59.0 (7 constraints: 69819942) +com.palantir.conjure.java.api:service-config:2.59.0 (6 constraints: 4f6664e2) +com.palantir.conjure.java.api:ssl-config:2.59.0 (5 constraints: c74f7b33) +com.palantir.conjure.java.runtime:client-config:8.17.0 (6 constraints: 896baef2) +com.palantir.conjure.java.runtime:conjure-java-jackson-optimizations:8.17.0 (1 constraints: 861cc9a4) +com.palantir.conjure.java.runtime:conjure-java-jackson-serialization:8.17.0 (4 constraints: f846c496) +com.palantir.conjure.java.runtime:keystores:8.17.0 (3 constraints: ec29b045) +com.palantir.deadlines:deadlines:0.8.0 (2 constraints: 4314578a) +com.palantir.dialogue:dialogue-apache-hc5-client:5.4.0 (2 constraints: 1429feb5) +com.palantir.dialogue:dialogue-blocking-channels:5.4.0 (2 constraints: 71235972) +com.palantir.dialogue:dialogue-clients:5.4.0 (1 constraints: 0b051036) +com.palantir.dialogue:dialogue-core:5.4.0 (3 constraints: e03cb33f) +com.palantir.dialogue:dialogue-futures:5.4.0 (3 constraints: 32336736) +com.palantir.dialogue:dialogue-serde:5.4.0 (3 constraints: 1e2eb92f) +com.palantir.dialogue:dialogue-target:5.4.0 (7 constraints: b777ba3c) com.palantir.goethe:goethe:0.14.0 (1 constraints: 37052f3b) com.palantir.human-readable-types:human-readable-types:1.7.0 (1 constraints: 0a050536) com.palantir.javapoet:javapoet:0.5.0 (2 constraints: c7103ed1) com.palantir.nylon:nylon-threads:0.4.0 (1 constraints: 0c10fa91) com.palantir.refreshable:refreshable:2.5.0 (3 constraints: a2334171) -com.palantir.ri:resource-identifier:2.7.0 (3 constraints: c72423f4) -com.palantir.safe-logging:logger:3.7.0 (23 constraints: da912a96) -com.palantir.safe-logging:logger-slf4j:3.7.0 (1 constraints: 050e6842) -com.palantir.safe-logging:logger-spi:3.7.0 (2 constraints: 191ea27b) -com.palantir.safe-logging:preconditions:3.7.0 (29 constraints: 7ef7eb6c) -com.palantir.safe-logging:safe-logging:3.7.0 (27 constraints: 56da9e33) -com.palantir.safethreadlocalrandom:safe-thread-local-random:0.1.0 (3 constraints: 933225f9) +com.palantir.ri:resource-identifier:2.8.0 (3 constraints: c92457f4) +com.palantir.safe-logging:logger:3.8.0 (24 constraints: 2ba2ab81) +com.palantir.safe-logging:logger-slf4j:3.8.0 (1 constraints: 060e6b42) +com.palantir.safe-logging:logger-spi:3.8.0 (2 constraints: 1b1ed77b) +com.palantir.safe-logging:preconditions:3.8.0 (31 constraints: 44170b1a) +com.palantir.safe-logging:safe-logging:3.8.0 (28 constraints: abea9b1a) +com.palantir.safethreadlocalrandom:safe-thread-local-random:0.3.0 (3 constraints: 993243fa) com.palantir.syntactic-paths:syntactic-paths:0.9.0 (2 constraints: 9d137b61) com.palantir.tokens:auth-tokens:3.18.0 (5 constraints: 685148e7) -com.palantir.tracing:tracing:6.20.0 (8 constraints: 697b4213) -com.palantir.tracing:tracing-api:6.20.0 (6 constraints: 8455c654) -com.palantir.tracing:tracing-undertow:6.20.0 (1 constraints: 41055f3b) +com.palantir.tracing:tracing:6.21.0 (8 constraints: 537bdf03) +com.palantir.tracing:tracing-api:6.21.0 (6 constraints: 7b55124e) +com.palantir.tracing:tracing-undertow:6.21.0 (1 constraints: 41055f3b) com.palantir.tritium:tritium-api:0.96.0 (2 constraints: 3f1f48be) +com.palantir.tritium:tritium-caffeine:0.96.0 (1 constraints: ba104cbe) com.palantir.tritium:tritium-core:0.96.0 (1 constraints: 47105ea2) -com.palantir.tritium:tritium-ids:0.95.0 (1 constraints: d10fb396) -com.palantir.tritium:tritium-metrics:0.96.0 (6 constraints: ec6625d2) -com.palantir.tritium:tritium-registry:0.96.0 (11 constraints: e4d95f02) +com.palantir.tritium:tritium-ids:0.96.0 (1 constraints: d20fb696) +com.palantir.tritium:tritium-metrics:0.96.0 (7 constraints: 67771c68) +com.palantir.tritium:tritium-registry:0.96.0 (12 constraints: 5bea80a6) com.squareup:javapoet:1.13.0 (1 constraints: f50b65f7) info.picocli:picocli:4.7.5 (1 constraints: 12051936) -io.dropwizard.metrics:metrics-core:4.2.30 (27 constraints: ffd1481b) +io.dropwizard.metrics:metrics-core:4.2.30 (28 constraints: 81e2866e) io.undertow:undertow-core:2.2.37.Final (2 constraints: e91966fe) javax.annotation:javax.annotation-api:1.3.2 (7 constraints: 396987f4) javax.inject:javax.inject:1 (12 constraints: b6c96528) @@ -68,9 +69,9 @@ javax.ws.rs:javax.ws.rs-api:2.1.1 (14 constraints: b4f4b54a) joda-time:joda-time:2.12.7 (3 constraints: c329a724) org.apache.commons:commons-lang3:3.17.0 (4 constraints: 4d301dc7) org.apache.httpcomponents.client5:httpclient5:5.3.1 (1 constraints: cd13996e) -org.apache.httpcomponents.core5:httpcore5:5.2.4 (3 constraints: 6639870e) +org.apache.httpcomponents.core5:httpcore5:5.3.3 (3 constraints: 6639890e) org.apache.httpcomponents.core5:httpcore5-h2:5.2.4 (1 constraints: 3f130d3c) -org.checkerframework:checker-qual:3.43.0 (4 constraints: 5937f041) +org.checkerframework:checker-qual:3.43.0 (4 constraints: a237011e) org.derive4j:derive4j-annotation:1.1.1 (1 constraints: 0505f435) org.eclipse.collections:eclipse-collections:11.1.0 (1 constraints: 35052f3b) org.eclipse.collections:eclipse-collections-api:11.1.0 (2 constraints: 12187676) @@ -82,7 +83,8 @@ org.jboss.logging:jboss-logging:3.4.1.Final (4 constraints: a24557a8) org.jboss.threads:jboss-threads:3.5.0.Final (3 constraints: b52a2fe5) org.jboss.xnio:xnio-api:3.8.16.Final (2 constraints: d71a2474) org.jboss.xnio:xnio-nio:3.8.16.Final (1 constraints: f80dc53d) -org.jetbrains:annotations:24.1.0 (4 constraints: 013a3c97) +org.jetbrains:annotations:26.0.2 (4 constraints: 043a4597) +org.jspecify:jspecify:1.0.0 (1 constraints: f70f549f) org.mpierce.metrics.reservoir:hdrhistogram-metrics-reservoir:1.1.3 (1 constraints: 0d10f991) org.slf4j:slf4j-api:1.7.36 (26 constraints: 07714e2c) org.slf4j:slf4j-simple:1.7.36 (1 constraints: 43054b3b) @@ -96,7 +98,7 @@ ch.qos.logback:logback-classic:1.2.11 (6 constraints: de520609) ch.qos.logback:logback-core:1.2.11 (5 constraints: f74abe3c) com.fasterxml:classmate:1.5.1 (3 constraints: 8530e55a) com.fasterxml.jackson.jaxrs:jackson-jaxrs-base:2.18.2 (2 constraints: e32ea06a) -com.fasterxml.jackson.jaxrs:jackson-jaxrs-cbor-provider:2.18.2 (1 constraints: 43197fbb) +com.fasterxml.jackson.jaxrs:jackson-jaxrs-cbor-provider:2.18.2 (1 constraints: 451987bb) com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider:2.18.2 (3 constraints: 5a22f1f5) com.fasterxml.jackson.module:jackson-module-afterburner:2.18.2 (1 constraints: b20e5a5e) com.fasterxml.jackson.module:jackson-module-jaxb-annotations:2.18.2 (2 constraints: e32ea06a) @@ -109,14 +111,14 @@ com.helger:profiler:1.1.1 (1 constraints: e21053b8) com.netflix.feign:feign-core:8.18.0 (3 constraints: de3f76e0) com.netflix.feign:feign-jackson:8.18.0 (1 constraints: c718909e) com.palantir.conjure:conjure-core:4.50.0 (1 constraints: 3b054b3b) -com.palantir.conjure.java.api:test-utils:2.56.0 (1 constraints: 3f05513b) -com.palantir.conjure.java.runtime:conjure-java-annotations:8.9.0 (1 constraints: 9718cf85) -com.palantir.conjure.java.runtime:conjure-java-jaxrs-client:8.9.0 (1 constraints: 11052836) -com.palantir.conjure.java.runtime:conjure-java-jersey-server:8.9.0 (1 constraints: 11052836) -com.palantir.conjure.java.runtime:conjure-java-legacy-clients:8.9.0 (2 constraints: a71d5514) -com.palantir.conjure.java.runtime:refresh-utils:8.9.0 (1 constraints: 9718cf85) -com.palantir.safe-logging:preconditions-assertj:3.7.0 (1 constraints: 0c050f36) -com.palantir.tracing:tracing-jersey:6.20.0 (1 constraints: 47199dbb) +com.palantir.conjure.java.api:test-utils:2.59.0 (1 constraints: 3f05513b) +com.palantir.conjure.java.runtime:conjure-java-annotations:8.17.0 (1 constraints: c6188d9e) +com.palantir.conjure.java.runtime:conjure-java-jaxrs-client:8.17.0 (1 constraints: 11052836) +com.palantir.conjure.java.runtime:conjure-java-jersey-server:8.17.0 (1 constraints: 11052836) +com.palantir.conjure.java.runtime:conjure-java-legacy-clients:8.17.0 (2 constraints: d61d2332) +com.palantir.conjure.java.runtime:refresh-utils:8.17.0 (1 constraints: c6188d9e) +com.palantir.safe-logging:preconditions-assertj:3.8.0 (1 constraints: 0c050f36) +com.palantir.tracing:tracing-jersey:6.21.0 (1 constraints: 401989bb) com.palantir.websecurity:dropwizard-web-security:1.1.0 (1 constraints: 0405f335) io.dropwizard:dropwizard-configuration:2.0.34 (2 constraints: 3a1c49c1) io.dropwizard:dropwizard-core:2.0.34 (3 constraints: d6281894) @@ -147,12 +149,12 @@ javax.servlet:javax.servlet-api:4.0.1 (8 constraints: e381a7be) javax.validation:validation-api:2.0.1.Final (13 constraints: e5c5c6d1) javax.xml.bind:jaxb-api:2.3.1 (3 constraints: 0242851b) junit:junit:4.13.2 (2 constraints: de1ce71a) -net.bytebuddy:byte-buddy:1.14.18 (2 constraints: f716b767) +net.bytebuddy:byte-buddy:1.15.11 (2 constraints: f116e266) net.bytebuddy:byte-buddy-agent:1.14.12 (1 constraints: 730bb2e9) net.sourceforge.argparse4j:argparse4j:0.8.1 (2 constraints: da1b0490) org.apache.commons:commons-text:1.10.0 (1 constraints: 3b114ace) org.apiguardian:apiguardian-api:1.1.2 (6 constraints: 5366ce6e) -org.assertj:assertj-core:3.26.3 (3 constraints: ef2ad6a1) +org.assertj:assertj-core:3.27.3 (3 constraints: f42a1aa2) org.eclipse.jetty:jetty-continuation:9.4.49.v20220914 (1 constraints: e21047f8) org.eclipse.jetty:jetty-http:9.4.49.v20220914 (5 constraints: 9f57507b) org.eclipse.jetty:jetty-io:9.4.49.v20220914 (7 constraints: 4d741ea9) diff --git a/versions.props b/versions.props index 5a8ca66fc..6aa8351fd 100644 --- a/versions.props +++ b/versions.props @@ -11,7 +11,7 @@ com.palantir.conjure.java.runtime:* = 8.7.0 com.palantir.conjure.verification:* = 0.19.0 com.palantir.conjure:* = 4.50.0 com.palantir.deadlines:* = 0.8.0 -com.palantir.dialogue:* = 3.135.0 +com.palantir.dialogue:* = 5.4.0 com.palantir.goethe:* = 0.14.0 com.palantir.human-readable-types:* = 1.7.0 com.palantir.javapoet:javapoet = 0.5.0