From 4a96f80f803f3257c7ec81e82bed62f6dc1d586b Mon Sep 17 00:00:00 2001 From: Pritham Marupaka Date: Mon, 25 Nov 2024 21:27:03 -0500 Subject: [PATCH 01/24] wip: endpoint errors - client side - result types --- .../product/DialogueCookieResultTypes.java | 6 + .../product/DialogueEmptyPathResultTypes.java | 6 + .../product/DialogueErrorEndpoints.java | 103 ++++++++++++++ .../product/DialogueErrorResultTypes.java | 38 ++++++ .../product/DialogueEteBinaryResultTypes.java | 6 + .../product/DialogueEteResultTypes.java | 6 + .../palantir/product/ErrorServiceAsync.java | 114 ++++++++++++++++ .../product/ErrorServiceBlocking.java | 114 ++++++++++++++++ .../DialogueEndpointResultTypeGenerator.java | 129 ++++++++++++++++++ .../conjure/java/services/dialogue/Names.java | 5 + 10 files changed, 527 insertions(+) create mode 100644 conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/DialogueCookieResultTypes.java create mode 100644 conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/DialogueEmptyPathResultTypes.java create mode 100644 conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/DialogueErrorEndpoints.java create mode 100644 conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/DialogueErrorResultTypes.java create mode 100644 conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/DialogueEteBinaryResultTypes.java create mode 100644 conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/DialogueEteResultTypes.java create mode 100644 conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/ErrorServiceAsync.java create mode 100644 conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/ErrorServiceBlocking.java create mode 100644 conjure-java-core/src/main/java/com/palantir/conjure/java/services/dialogue/DialogueEndpointResultTypeGenerator.java diff --git a/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/DialogueCookieResultTypes.java b/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/DialogueCookieResultTypes.java new file mode 100644 index 000000000..a660842f2 --- /dev/null +++ b/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/DialogueCookieResultTypes.java @@ -0,0 +1,6 @@ +package test.api; + +import javax.annotation.processing.Generated; + +@Generated("com.palantir.conjure.java.services.dialogue.DialogueEndpointResultTypeGenerator") +public final class DialogueCookieResultTypes {} diff --git a/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/DialogueEmptyPathResultTypes.java b/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/DialogueEmptyPathResultTypes.java new file mode 100644 index 000000000..7e13b830f --- /dev/null +++ b/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/DialogueEmptyPathResultTypes.java @@ -0,0 +1,6 @@ +package com.palantir.product; + +import javax.annotation.processing.Generated; + +@Generated("com.palantir.conjure.java.services.dialogue.DialogueEndpointResultTypeGenerator") +public final class DialogueEmptyPathResultTypes {} diff --git a/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/DialogueErrorEndpoints.java b/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/DialogueErrorEndpoints.java new file mode 100644 index 000000000..927621be6 --- /dev/null +++ b/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/DialogueErrorEndpoints.java @@ -0,0 +1,103 @@ +package 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 javax.annotation.processing.Generated; + +@Generated("com.palantir.conjure.java.services.dialogue.DialogueEndpointsGenerator") +enum DialogueErrorEndpoints implements Endpoint { + testBasicError { + private final PathTemplate pathTemplate = + PathTemplate.builder().fixed("base").fixed("basic").build(); + + @Override + public void renderPath(ListMultimap params, UrlBuilder url) { + pathTemplate.fill(params, url); + } + + @Override + public HttpMethod httpMethod() { + return HttpMethod.GET; + } + + @Override + public String serviceName() { + return "ErrorService"; + } + + @Override + public String endpointName() { + return "testBasicError"; + } + + @Override + public String version() { + return "1.2.3"; + } + }, + + testImportedError { + private final PathTemplate pathTemplate = + PathTemplate.builder().fixed("base").fixed("imported").build(); + + @Override + public void renderPath(ListMultimap params, UrlBuilder url) { + pathTemplate.fill(params, url); + } + + @Override + public HttpMethod httpMethod() { + return HttpMethod.GET; + } + + @Override + public String serviceName() { + return "ErrorService"; + } + + @Override + public String endpointName() { + return "testImportedError"; + } + + @Override + public String version() { + return "1.2.3"; + } + }, + + testMultipleErrorsAndPackages { + private final PathTemplate pathTemplate = + PathTemplate.builder().fixed("base").fixed("multiple").build(); + + @Override + public void renderPath(ListMultimap params, UrlBuilder url) { + pathTemplate.fill(params, url); + } + + @Override + public HttpMethod httpMethod() { + return HttpMethod.GET; + } + + @Override + public String serviceName() { + return "ErrorService"; + } + + @Override + public String endpointName() { + return "testMultipleErrorsAndPackages"; + } + + @Override + public String version() { + return "1.2.3"; + } + } +} diff --git a/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/DialogueErrorResultTypes.java b/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/DialogueErrorResultTypes.java new file mode 100644 index 000000000..54e72e70a --- /dev/null +++ b/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/DialogueErrorResultTypes.java @@ -0,0 +1,38 @@ +package com.palantir.product; + +import java.lang.String; +import javax.annotation.processing.Generated; + +@Generated("com.palantir.conjure.java.services.dialogue.DialogueEndpointResultTypeGenerator") +public final class DialogueErrorResultTypes { + public sealed interface TestBasicErrorResult + permits TestBasicErrorResult.Success, TestBasicErrorResult.Test_InvalidArgument { + record Success(String value) implements TestBasicErrorResult {} + + record Test_InvalidArgument() implements TestBasicErrorResult {} + } + + public sealed interface TestImportedErrorResult + permits TestImportedErrorResult.Success, TestImportedErrorResult.EndpointSpecific_EndpointError { + record Success(String value) implements TestImportedErrorResult {} + + record EndpointSpecific_EndpointError() implements TestImportedErrorResult {} + } + + public sealed interface TestMultipleErrorsAndPackagesResult + permits TestMultipleErrorsAndPackagesResult.Success, + TestMultipleErrorsAndPackagesResult.Test_InvalidArgument, + TestMultipleErrorsAndPackagesResult.Test_NotFound, + TestMultipleErrorsAndPackagesResult.EndpointSpecificTwo_DifferentNamespace, + TestMultipleErrorsAndPackagesResult.EndpointSpecific_DifferentPackage { + record Success(String value) implements TestMultipleErrorsAndPackagesResult {} + + record Test_InvalidArgument() implements TestMultipleErrorsAndPackagesResult {} + + record Test_NotFound() implements TestMultipleErrorsAndPackagesResult {} + + record EndpointSpecificTwo_DifferentNamespace() implements TestMultipleErrorsAndPackagesResult {} + + record EndpointSpecific_DifferentPackage() implements TestMultipleErrorsAndPackagesResult {} + } +} diff --git a/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/DialogueEteBinaryResultTypes.java b/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/DialogueEteBinaryResultTypes.java new file mode 100644 index 000000000..d32d44b34 --- /dev/null +++ b/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/DialogueEteBinaryResultTypes.java @@ -0,0 +1,6 @@ +package com.palantir.product; + +import javax.annotation.processing.Generated; + +@Generated("com.palantir.conjure.java.services.dialogue.DialogueEndpointResultTypeGenerator") +public final class DialogueEteBinaryResultTypes {} diff --git a/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/DialogueEteResultTypes.java b/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/DialogueEteResultTypes.java new file mode 100644 index 000000000..95a402570 --- /dev/null +++ b/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/DialogueEteResultTypes.java @@ -0,0 +1,6 @@ +package com.palantir.product; + +import javax.annotation.processing.Generated; + +@Generated("com.palantir.conjure.java.services.dialogue.DialogueEndpointResultTypeGenerator") +public final class DialogueEteResultTypes {} diff --git a/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/ErrorServiceAsync.java b/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/ErrorServiceAsync.java new file mode 100644 index 000000000..15defc907 --- /dev/null +++ b/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/ErrorServiceAsync.java @@ -0,0 +1,114 @@ +package com.palantir.product; + +import com.google.common.util.concurrent.ListenableFuture; +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.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.PlainSerDe; +import com.palantir.dialogue.Request; +import com.palantir.dialogue.TypeMarker; +import com.palantir.tokens.auth.AuthHeader; +import java.lang.Override; +import java.lang.String; +import javax.annotation.processing.Generated; + +@Generated("com.palantir.conjure.java.services.dialogue.DialogueInterfaceGenerator") +@DialogueService(ErrorServiceAsync.Factory.class) +public interface ErrorServiceAsync { + /** @apiNote {@code GET /base/basic} */ + @ClientEndpoint(method = "GET", path = "/base/basic") + ListenableFuture testBasicError(AuthHeader authHeader); + + /** @apiNote {@code GET /base/imported} */ + @ClientEndpoint(method = "GET", path = "/base/imported") + ListenableFuture testImportedError(AuthHeader authHeader); + + /** @apiNote {@code GET /base/multiple} */ + @ClientEndpoint(method = "GET", path = "/base/multiple") + ListenableFuture testMultipleErrorsAndPackages(AuthHeader authHeader); + + /** 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 EndpointChannel testBasicErrorChannel = + _endpointChannelFactory.endpoint(DialogueErrorEndpoints.testBasicError); + + private final Deserializer testBasicErrorDeserializer = + _runtime.bodySerDe().deserializer(new TypeMarker() {}); + + private final EndpointChannel testImportedErrorChannel = + _endpointChannelFactory.endpoint(DialogueErrorEndpoints.testImportedError); + + private final Deserializer testImportedErrorDeserializer = + _runtime.bodySerDe().deserializer(new TypeMarker() {}); + + private final EndpointChannel testMultipleErrorsAndPackagesChannel = + _endpointChannelFactory.endpoint(DialogueErrorEndpoints.testMultipleErrorsAndPackages); + + private final Deserializer testMultipleErrorsAndPackagesDeserializer = + _runtime.bodySerDe().deserializer(new TypeMarker() {}); + + @Override + public ListenableFuture testBasicError(AuthHeader authHeader) { + Request.Builder _request = Request.builder(); + _request.putHeaderParams("Authorization", authHeader.toString()); + return _runtime.clients().call(testBasicErrorChannel, _request.build(), testBasicErrorDeserializer); + } + + @Override + public ListenableFuture testImportedError(AuthHeader authHeader) { + Request.Builder _request = Request.builder(); + _request.putHeaderParams("Authorization", authHeader.toString()); + return _runtime.clients() + .call(testImportedErrorChannel, _request.build(), testImportedErrorDeserializer); + } + + @Override + public ListenableFuture testMultipleErrorsAndPackages(AuthHeader authHeader) { + Request.Builder _request = Request.builder(); + _request.putHeaderParams("Authorization", authHeader.toString()); + return _runtime.clients() + .call( + testMultipleErrorsAndPackagesChannel, + _request.build(), + testMultipleErrorsAndPackagesDeserializer); + } + + @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); + } + } +} diff --git a/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/ErrorServiceBlocking.java b/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/ErrorServiceBlocking.java new file mode 100644 index 000000000..9216dce66 --- /dev/null +++ b/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/ErrorServiceBlocking.java @@ -0,0 +1,114 @@ +package com.palantir.product; + +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.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.PlainSerDe; +import com.palantir.dialogue.Request; +import com.palantir.dialogue.TypeMarker; +import com.palantir.tokens.auth.AuthHeader; +import java.lang.Override; +import java.lang.String; +import javax.annotation.processing.Generated; + +@Generated("com.palantir.conjure.java.services.dialogue.DialogueInterfaceGenerator") +@DialogueService(ErrorServiceBlocking.Factory.class) +public interface ErrorServiceBlocking { + /** @apiNote {@code GET /base/basic} */ + @ClientEndpoint(method = "GET", path = "/base/basic") + String testBasicError(AuthHeader authHeader); + + /** @apiNote {@code GET /base/imported} */ + @ClientEndpoint(method = "GET", path = "/base/imported") + String testImportedError(AuthHeader authHeader); + + /** @apiNote {@code GET /base/multiple} */ + @ClientEndpoint(method = "GET", path = "/base/multiple") + String testMultipleErrorsAndPackages(AuthHeader authHeader); + + /** 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 EndpointChannel testBasicErrorChannel = + _endpointChannelFactory.endpoint(DialogueErrorEndpoints.testBasicError); + + private final Deserializer testBasicErrorDeserializer = + _runtime.bodySerDe().deserializer(new TypeMarker() {}); + + private final EndpointChannel testImportedErrorChannel = + _endpointChannelFactory.endpoint(DialogueErrorEndpoints.testImportedError); + + private final Deserializer testImportedErrorDeserializer = + _runtime.bodySerDe().deserializer(new TypeMarker() {}); + + private final EndpointChannel testMultipleErrorsAndPackagesChannel = + _endpointChannelFactory.endpoint(DialogueErrorEndpoints.testMultipleErrorsAndPackages); + + private final Deserializer testMultipleErrorsAndPackagesDeserializer = + _runtime.bodySerDe().deserializer(new TypeMarker() {}); + + @Override + public String testBasicError(AuthHeader authHeader) { + Request.Builder _request = Request.builder(); + _request.putHeaderParams("Authorization", authHeader.toString()); + return _runtime.clients() + .callBlocking(testBasicErrorChannel, _request.build(), testBasicErrorDeserializer); + } + + @Override + public String testImportedError(AuthHeader authHeader) { + Request.Builder _request = Request.builder(); + _request.putHeaderParams("Authorization", authHeader.toString()); + return _runtime.clients() + .callBlocking(testImportedErrorChannel, _request.build(), testImportedErrorDeserializer); + } + + @Override + public String testMultipleErrorsAndPackages(AuthHeader authHeader) { + Request.Builder _request = Request.builder(); + _request.putHeaderParams("Authorization", authHeader.toString()); + return _runtime.clients() + .callBlocking( + testMultipleErrorsAndPackagesChannel, + _request.build(), + testMultipleErrorsAndPackagesDeserializer); + } + + @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); + } + } +} diff --git a/conjure-java-core/src/main/java/com/palantir/conjure/java/services/dialogue/DialogueEndpointResultTypeGenerator.java b/conjure-java-core/src/main/java/com/palantir/conjure/java/services/dialogue/DialogueEndpointResultTypeGenerator.java new file mode 100644 index 000000000..bd36833b6 --- /dev/null +++ b/conjure-java-core/src/main/java/com/palantir/conjure/java/services/dialogue/DialogueEndpointResultTypeGenerator.java @@ -0,0 +1,129 @@ +/* + * (c) Copyright 2024 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 com.palantir.conjure.java.services.dialogue; + +import com.google.common.base.CaseFormat; +import com.palantir.conjure.java.ConjureAnnotations; +import com.palantir.conjure.java.Options; +import com.palantir.conjure.java.util.Packages; +import com.palantir.conjure.spec.EndpointDefinition; +import com.palantir.conjure.spec.EndpointError; +import com.palantir.conjure.spec.ServiceDefinition; +import com.palantir.javapoet.ClassName; +import com.palantir.javapoet.JavaFile; +import com.palantir.javapoet.MethodSpec; +import com.palantir.javapoet.ParameterSpec; +import com.palantir.javapoet.TypeName; +import com.palantir.javapoet.TypeSpec; +import java.util.List; +import javax.lang.model.element.Modifier; + +public final class DialogueEndpointResultTypeGenerator { + private final Options options; + private final ReturnTypeMapper returnTypeMapper; + + public DialogueEndpointResultTypeGenerator(Options options, ReturnTypeMapper returnTypeMapper) { + this.options = options; + this.returnTypeMapper = returnTypeMapper; + } + + public JavaFile generateEndpointResultTypes(ServiceDefinition serviceDefinition) { + String packageName = + Packages.getPrefixedPackage(serviceDefinition.getServiceName().getPackage(), options.packagePrefix()); + ClassName resultsClassName = Names.resultTypesClassName(serviceDefinition, options); + TypeSpec resultTypesClass = TypeSpec.classBuilder(resultsClassName) + .addModifiers(Modifier.PUBLIC, Modifier.FINAL) + .addAnnotation( + ConjureAnnotations.getConjureGeneratedAnnotation(DialogueEndpointResultTypeGenerator.class)) + .addTypes(createResultTypes(serviceDefinition, packageName, resultsClassName)) + .build(); + + return JavaFile.builder(packageName, resultTypesClass).build(); + } + + private List createResultTypes( + ServiceDefinition serviceDefinition, String packageName, ClassName resultsClassName) { + return serviceDefinition.getEndpoints().stream() + .filter(endpoint -> !endpoint.getErrors().isEmpty()) + .map(pt -> createResultTypesForEndpoint(pt, packageName, resultsClassName)) + .toList(); + } + + private TypeSpec createResultTypesForEndpoint( + EndpointDefinition endpointDefinition, String packageName, ClassName resultsClassName) { + String endpointResultName = getEndpointResultName(endpointDefinition); + TypeSpec.Builder typeBuilder = TypeSpec.interfaceBuilder(endpointResultName) + .addModifiers(Modifier.PUBLIC, Modifier.SEALED) + .addType(createSuccessRecord(endpointDefinition, packageName, resultsClassName)) + .addPermittedSubclass( + ClassName.get(packageName, resultsClassName.simpleName(), endpointResultName, "Success")) + .addTypes(endpointDefinition.getErrors().stream() + .map(error -> createErrorRecord(error, endpointDefinition, packageName, resultsClassName)) + .toList()) + .addPermittedSubclasses(endpointDefinition.getErrors().stream() + .map(error -> ClassName.get( + packageName, + resultsClassName.simpleName(), + getEndpointResultName(endpointDefinition), + getErrorName(error))) + .toList()); + // TODO(p): add permitted subclasses for all errors. + + return typeBuilder.build(); + } + + private TypeSpec createSuccessRecord( + EndpointDefinition endpointDefinition, String packageName, ClassName resultsClassName) { + TypeSpec.Builder builder = TypeSpec.recordBuilder("Success") + .addModifiers(Modifier.PUBLIC, Modifier.STATIC) + .addSuperinterface(ClassName.get( + packageName, resultsClassName.simpleName(), getEndpointResultName(endpointDefinition))); + if (endpointDefinition.getReturns().isPresent()) { + TypeName returnType = + returnTypeMapper.baseType(endpointDefinition.getReturns().get()); + builder.recordConstructor(MethodSpec.constructorBuilder() + .addParameter(ParameterSpec.builder(returnType, "value").build()) + .build()); + } + return builder.build(); + } + + private TypeSpec createErrorRecord( + EndpointError endpointError, + EndpointDefinition endpointDefinition, + String packageName, + ClassName resultsClassName) { + TypeSpec.Builder builder = TypeSpec.recordBuilder(getErrorName(endpointError)) + .addModifiers(Modifier.PUBLIC, Modifier.STATIC) + .addSuperinterface(ClassName.get( + packageName, resultsClassName.simpleName(), getEndpointResultName(endpointDefinition))); + + return builder.build(); + } + + private static String getErrorName(EndpointError endpointError) { + return String.format( + "%s_%s", + endpointError.getError().getNamespace(), + endpointError.getError().getName()); + } + + private static String getEndpointResultName(EndpointDefinition definition) { + return CaseFormat.LOWER_CAMEL.to( + CaseFormat.UPPER_CAMEL, definition.getEndpointName().get()) + "Result"; + } +} diff --git a/conjure-java-core/src/main/java/com/palantir/conjure/java/services/dialogue/Names.java b/conjure-java-core/src/main/java/com/palantir/conjure/java/services/dialogue/Names.java index a2d2c8110..ff13e1ab9 100644 --- a/conjure-java-core/src/main/java/com/palantir/conjure/java/services/dialogue/Names.java +++ b/conjure-java-core/src/main/java/com/palantir/conjure/java/services/dialogue/Names.java @@ -30,6 +30,11 @@ public static ClassName endpointsClassName(ServiceDefinition def, Options option return maybeRelocate(def, options, simpleName); } + public static ClassName resultTypesClassName(ServiceDefinition def, Options options) { + String simpleName = "Dialogue" + def.getServiceName().getName().replaceAll("Service$", "") + "ResultTypes"; + return maybeRelocate(def, options, simpleName); + } + public static ClassName blockingClassName(ServiceDefinition def, Options options) { String simpleName = def.getServiceName().getName() + "Blocking"; return maybeRelocate(def, options, simpleName); From c3de4f58322eed583c8392c101d3704dbe133429 Mon Sep 17 00:00:00 2001 From: Pritham Marupaka Date: Thu, 12 Dec 2024 13:57:23 -0500 Subject: [PATCH 02/24] wip --- .../product/ErrorServiceBlocking.java | 164 ++++++++++++++++-- .../another/EndpointSpecificErrors.java | 2 + .../product/EndpointSpecificErrors.java | 6 + .../product/EndpointSpecificServerErrors.java | 3 +- .../product/EndpointSpecificTwoErrors.java | 2 + .../com/palantir/product/TestErrors.java | 8 + .../com/palantir/conjure/java/Options.java | 9 + .../java/services/ServiceGenerators.java | 2 +- .../UndertowServiceHandlerGenerator.java | 2 +- .../UndertowServiceInterfaceGenerator.java | 2 +- .../DefaultStaticFactoryMethodGenerator.java | 74 ++++++-- .../DialogueEndpointResultTypeGenerator.java | 129 -------------- .../dialogue/DialogueInterfaceGenerator.java | 150 ++++++++++++++-- .../dialogue/DialogueServiceGenerator.java | 19 +- .../java/types/CheckedErrorGenerator.java | 6 +- .../conjure/java/types/ErrorGenerator.java | 39 ++++- .../java/util/ErrorGenerationUtils.java | 37 +++- .../palantir/conjure/java/ErrorResource.java | 45 +++++ .../conjure/java/UndertowServiceEteTest.java | 62 ++++++- .../java/lib/internal/ConjureErrors.java | 59 +++++++ versions.lock | 93 +++++----- versions.props | 2 +- 22 files changed, 687 insertions(+), 228 deletions(-) delete mode 100644 conjure-java-core/src/main/java/com/palantir/conjure/java/services/dialogue/DialogueEndpointResultTypeGenerator.java create mode 100644 conjure-java-core/src/test/java/com/palantir/conjure/java/ErrorResource.java create mode 100644 conjure-lib/src/main/java/com/palantir/conjure/java/lib/internal/ConjureErrors.java diff --git a/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/ErrorServiceBlocking.java b/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/ErrorServiceBlocking.java index 9216dce66..0e55d5684 100644 --- a/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/ErrorServiceBlocking.java +++ b/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/ErrorServiceBlocking.java @@ -1,9 +1,13 @@ package com.palantir.product; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; import com.palantir.conjure.java.lib.internal.ClientEndpoint; +import com.palantir.conjure.java.lib.internal.ConjureErrors; 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; @@ -12,6 +16,7 @@ import com.palantir.dialogue.PlainSerDe; import com.palantir.dialogue.Request; import com.palantir.dialogue.TypeMarker; +import com.palantir.logsafe.Preconditions; import com.palantir.tokens.auth.AuthHeader; import java.lang.Override; import java.lang.String; @@ -22,15 +27,15 @@ public interface ErrorServiceBlocking { /** @apiNote {@code GET /base/basic} */ @ClientEndpoint(method = "GET", path = "/base/basic") - String testBasicError(AuthHeader authHeader); + TestBasicErrorResponse testBasicError(AuthHeader authHeader); /** @apiNote {@code GET /base/imported} */ @ClientEndpoint(method = "GET", path = "/base/imported") - String testImportedError(AuthHeader authHeader); + TestImportedErrorResponse testImportedError(AuthHeader authHeader); /** @apiNote {@code GET /base/multiple} */ @ClientEndpoint(method = "GET", path = "/base/multiple") - String testMultipleErrorsAndPackages(AuthHeader authHeader); + TestMultipleErrorsAndPackagesResponse testMultipleErrorsAndPackages(AuthHeader authHeader); /** Creates a synchronous/blocking client for a ErrorService service. */ static ErrorServiceBlocking of(EndpointChannelFactory _endpointChannelFactory, ConjureRuntime _runtime) { @@ -40,23 +45,52 @@ static ErrorServiceBlocking of(EndpointChannelFactory _endpointChannelFactory, C private final EndpointChannel testBasicErrorChannel = _endpointChannelFactory.endpoint(DialogueErrorEndpoints.testBasicError); - private final Deserializer testBasicErrorDeserializer = - _runtime.bodySerDe().deserializer(new TypeMarker() {}); + 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 EndpointChannel testImportedErrorChannel = _endpointChannelFactory.endpoint(DialogueErrorEndpoints.testImportedError); - private final Deserializer testImportedErrorDeserializer = - _runtime.bodySerDe().deserializer(new TypeMarker() {}); + 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 EndpointChannel testMultipleErrorsAndPackagesChannel = _endpointChannelFactory.endpoint(DialogueErrorEndpoints.testMultipleErrorsAndPackages); - private final Deserializer testMultipleErrorsAndPackagesDeserializer = - _runtime.bodySerDe().deserializer(new TypeMarker() {}); + 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( + com.palantir.another.EndpointSpecificErrors.DIFFERENT_PACKAGE.name(), + new TypeMarker() {}) + .build()); @Override - public String testBasicError(AuthHeader authHeader) { + public TestBasicErrorResponse testBasicError(AuthHeader authHeader) { Request.Builder _request = Request.builder(); _request.putHeaderParams("Authorization", authHeader.toString()); return _runtime.clients() @@ -64,7 +98,7 @@ public String testBasicError(AuthHeader authHeader) { } @Override - public String testImportedError(AuthHeader authHeader) { + public TestImportedErrorResponse testImportedError(AuthHeader authHeader) { Request.Builder _request = Request.builder(); _request.putHeaderParams("Authorization", authHeader.toString()); return _runtime.clients() @@ -72,7 +106,7 @@ public String testImportedError(AuthHeader authHeader) { } @Override - public String testMultipleErrorsAndPackages(AuthHeader authHeader) { + public TestMultipleErrorsAndPackagesResponse testMultipleErrorsAndPackages(AuthHeader authHeader) { Request.Builder _request = Request.builder(); _request.putHeaderParams("Authorization", authHeader.toString()); return _runtime.clients() @@ -111,4 +145,110 @@ public ErrorServiceBlocking create(EndpointChannelFactory endpointChannelFactory return ErrorServiceBlocking.of(endpointChannelFactory, runtime); } } + + sealed interface TestBasicErrorResponse + permits TestBasicErrorResponse.Success, TestBasicErrorResponse.InvalidArgument { + record Success(String value) implements TestBasicErrorResponse { + public Success { + Preconditions.checkArgumentNotNull(value, "value cannot be null"); + } + } + + final class InvalidArgument extends ConjureErrors.BaseEndpointError + implements TestBasicErrorResponse { + @JsonCreator + InvalidArgument( + @JsonProperty("errorCode") String errorCode, + @JsonProperty("errorInstanceId") 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(String value) implements TestImportedErrorResponse { + public Success { + Preconditions.checkArgumentNotNull(value, "value cannot be null"); + } + } + + final class EndpointError + extends ConjureErrors.BaseEndpointError + implements TestImportedErrorResponse { + @JsonCreator + EndpointError( + @JsonProperty("errorCode") String errorCode, + @JsonProperty("errorInstanceId") 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 { + record Success(String value) implements TestMultipleErrorsAndPackagesResponse { + public Success { + Preconditions.checkArgumentNotNull(value, "value cannot be null"); + } + } + + final class InvalidArgument extends ConjureErrors.BaseEndpointError + implements TestMultipleErrorsAndPackagesResponse { + @JsonCreator + InvalidArgument( + @JsonProperty("errorCode") String errorCode, + @JsonProperty("errorInstanceId") String errorInstanceId, + @JsonProperty("parameters") TestErrors.InvalidArgumentParameters parameters) { + super(errorCode, TestErrors.INVALID_ARGUMENT.name(), errorInstanceId, parameters); + } + } + + final class NotFound extends ConjureErrors.BaseEndpointError + implements TestMultipleErrorsAndPackagesResponse { + @JsonCreator + NotFound( + @JsonProperty("errorCode") String errorCode, + @JsonProperty("errorInstanceId") String errorInstanceId, + @JsonProperty("parameters") TestErrors.NotFoundParameters parameters) { + super(errorCode, TestErrors.NOT_FOUND.name(), errorInstanceId, parameters); + } + } + + final class DifferentNamespace + extends ConjureErrors.BaseEndpointError + implements TestMultipleErrorsAndPackagesResponse { + @JsonCreator + DifferentNamespace( + @JsonProperty("errorCode") String errorCode, + @JsonProperty("errorInstanceId") String errorInstanceId, + @JsonProperty("parameters") EndpointSpecificTwoErrors.DifferentNamespaceParameters parameters) { + super(errorCode, EndpointSpecificTwoErrors.DIFFERENT_NAMESPACE.name(), errorInstanceId, parameters); + } + } + + final class DifferentPackage + extends ConjureErrors.BaseEndpointError< + com.palantir.another.EndpointSpecificErrors.DifferentPackageParameters> + implements TestMultipleErrorsAndPackagesResponse { + @JsonCreator + DifferentPackage( + @JsonProperty("errorCode") String errorCode, + @JsonProperty("errorInstanceId") String errorInstanceId, + @JsonProperty("parameters") + com.palantir.another.EndpointSpecificErrors.DifferentPackageParameters parameters) { + super( + errorCode, + com.palantir.another.EndpointSpecificErrors.DIFFERENT_PACKAGE.name(), + errorInstanceId, + parameters); + } + } + } } diff --git a/conjure-java-core/src/integrationInput/java/undertow/com/palantir/another/EndpointSpecificErrors.java b/conjure-java-core/src/integrationInput/java/undertow/com/palantir/another/EndpointSpecificErrors.java index 5ed9d792f..38a5fe579 100644 --- a/conjure-java-core/src/integrationInput/java/undertow/com/palantir/another/EndpointSpecificErrors.java +++ b/conjure-java-core/src/integrationInput/java/undertow/com/palantir/another/EndpointSpecificErrors.java @@ -18,4 +18,6 @@ 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/undertow/com/palantir/product/EndpointSpecificErrors.java b/conjure-java-core/src/integrationInput/java/undertow/com/palantir/product/EndpointSpecificErrors.java index 60616a273..48d949278 100644 --- a/conjure-java-core/src/integrationInput/java/undertow/com/palantir/product/EndpointSpecificErrors.java +++ b/conjure-java-core/src/integrationInput/java/undertow/com/palantir/product/EndpointSpecificErrors.java @@ -1,8 +1,11 @@ package undertow.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") @@ -18,4 +21,7 @@ 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/undertow/com/palantir/product/EndpointSpecificServerErrors.java b/conjure-java-core/src/integrationInput/java/undertow/com/palantir/product/EndpointSpecificServerErrors.java index 136b1bcff..26ddea7fd 100644 --- a/conjure-java-core/src/integrationInput/java/undertow/com/palantir/product/EndpointSpecificServerErrors.java +++ b/conjure-java-core/src/integrationInput/java/undertow/com/palantir/product/EndpointSpecificServerErrors.java @@ -1,5 +1,6 @@ package undertow.com.palantir.product; +import com.palantir.another.EndpointSpecificErrors; import com.palantir.conjure.java.api.errors.CheckedServiceException; import com.palantir.logsafe.Safe; import com.palantir.logsafe.SafeArg; @@ -40,7 +41,7 @@ public static void throwIfEndpointError(boolean shouldThrow, @Safe String typeNa public static final class EndpointError extends CheckedServiceException { private EndpointError(@Safe String typeName, @Unsafe Object typeDef, @Nullable Throwable cause) { super( - EndpointSpecificErrors.ENDPOINT_ERROR, + EndpointSpecificErrors.DIFFERENT_PACKAGE, cause, SafeArg.of("typeName", typeName), UnsafeArg.of("typeDef", typeDef)); diff --git a/conjure-java-core/src/integrationInput/java/undertow/com/palantir/product/EndpointSpecificTwoErrors.java b/conjure-java-core/src/integrationInput/java/undertow/com/palantir/product/EndpointSpecificTwoErrors.java index ff930dc05..dc2240282 100644 --- a/conjure-java-core/src/integrationInput/java/undertow/com/palantir/product/EndpointSpecificTwoErrors.java +++ b/conjure-java-core/src/integrationInput/java/undertow/com/palantir/product/EndpointSpecificTwoErrors.java @@ -18,4 +18,6 @@ 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/undertow/com/palantir/product/TestErrors.java b/conjure-java-core/src/integrationInput/java/undertow/com/palantir/product/TestErrors.java index e43247999..2c206a697 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 @@ -1,8 +1,11 @@ package undertow.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") @@ -25,4 +28,9 @@ 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 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/main/java/com/palantir/conjure/java/Options.java b/conjure-java-core/src/main/java/com/palantir/conjure/java/Options.java index a54b37770..160e81ffe 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,15 @@ default boolean preferObjectBuilders() { return false; } + /** + * TODO(pm): flesh this out more. For errors associated with endpoints, this option enables the generated Dialogue + * clients to return a result type. + */ + @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..97a556a0f 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.palantir.conjure.java.Options; import com.palantir.conjure.java.services.Auth; +import com.palantir.conjure.java.util.ErrorGenerationUtils; +import com.palantir.conjure.java.util.ErrorGenerationUtils.DeclaredEndpointErrors; +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; @@ -76,22 +82,26 @@ public final class DefaultStaticFactoryMethodGenerator implements StaticFactoryM private final ParameterTypeMapper parameterTypes; private final ReturnTypeMapper returnTypes; private final StaticFactoryMethodType methodType; + private final DeclaredEndpointErrors declaredEndpointErrors; public DefaultStaticFactoryMethodGenerator( Options options, TypeNameResolver typeNameResolver, ParameterTypeMapper parameterTypes, ReturnTypeMapper returnTypes, - StaticFactoryMethodType methodType) { + StaticFactoryMethodType methodType, + DeclaredEndpointErrors declaredEndpointErrors) { this.options = options; this.typeNameResolver = typeNameResolver; this.parameterTypes = parameterTypes; this.returnTypes = returnTypes; this.methodType = methodType; + this.declaredEndpointErrors = declaredEndpointErrors; } @Override public MethodSpec generate(ServiceDefinition def) { + // Need: 1) the name of the response type (static), 2) all the error types (declaredEndpointErrors) ClassName className = getClassName(def); TypeSpec.Builder impl = TypeSpec.anonymousClassBuilder("").addSuperinterface(className); @@ -108,8 +118,17 @@ 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.responseTypeName(endpoint.getEndpointName())), + endpoint, + endpoint.getEndpointName(), + endpoint.getReturns()) + .ifPresent(impl::addField); + impl.addMethod(clientImpl(className, endpoint)); }); impl.addMethod(DefaultStaticFactoryMethodGenerator.toStringMethod(className)); @@ -159,20 +178,26 @@ private Optional serializer(EndpointName endpointName, Type type) { .build()); } - private Optional deserializer(EndpointName endpointName, Optional type) { + private Optional deserializer( + TypeName responseType, EndpointDefinition endpointDef, EndpointName endpointName, Optional type) { TypeName className = Primitives.box(returnTypes.baseType(type)); if (isBinaryOrOptionalBinary(className, returnTypes)) { return Optional.empty(); } ParameterizedTypeName deserializerType = - ParameterizedTypeName.get(ClassName.get(Deserializer.class), className); + ParameterizedTypeName.get(ClassName.get(Deserializer.class), responseType); - CodeBlock realDeserializer = CodeBlock.of("deserializer(new $T<$T>() {})", TypeMarker.class, className); + // CodeBlock realDeserializer = CodeBlock.of("deserializer(new $T<$T>() {})", TypeMarker.class, className); + CodeBlock realDeserializerWithArgs = CodeBlock.builder() + .add("deserializer(") + .add(constructDeserializerArgsForEndpoint(endpointDef, responseType)) + .add(")") + .build(); CodeBlock voidDeserializer = CodeBlock.of("emptyBodyDeserializer()"); CodeBlock initializer = CodeBlock.of( "$L.bodySerDe().$L", StaticFactoryMethodGenerator.RUNTIME, - type.isPresent() ? realDeserializer : voidDeserializer); + type.isPresent() ? realDeserializerWithArgs : voidDeserializer); return Optional.of(FieldSpec.builder(deserializerType, endpointName + "Deserializer") .addModifiers(Modifier.PRIVATE, Modifier.FINAL) @@ -180,6 +205,27 @@ private Optional deserializer(EndpointName endpointName, Optionalbuilder()", DeserializerArgs.class, responseType) + .add(".baseType(new $T<>() {})", TypeMarker.class) + // TODO(pm): consider making "Success" a constant string for re-use in the record creation. + .add(".success(new $T<$T.Success>() {})", TypeMarker.class, responseType); + for (EndpointError err : endpointDef.getErrors()) { + ErrorTypeName errorTypeName = err.getError(); + String errorName = errorTypeName.getName(); + String errorType = CaseFormat.UPPER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, errorName); + ClassName errorClass = ClassName.get( + errorTypeName.getPackage(), + ErrorGenerationUtils.errorTypesClassName(errorTypeName.getNamespace()), + errorType); + retBuilder.add( + ".error($T.name(), new $T<$T.$L>() {})", errorClass, TypeMarker.class, responseType, errorName); + } + retBuilder.add(".build()"); + return retBuilder.build(); + } + private static boolean isBinaryOrOptionalBinary(TypeName className, ReturnTypeMapper returnTypes) { return isBinary(className, returnTypes) || isOptionalBinary(className, returnTypes); } @@ -193,7 +239,7 @@ private static boolean isOptionalBinary(TypeName className, ReturnTypeMapper ret returnTypes.baseType(Type.optional(OptionalType.of(Type.primitive(PrimitiveType.BINARY))))); } - 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,14 @@ private MethodSpec clientImpl(EndpointDefinition def) { .build()); } - TypeName returnType = - methodType.switchBy(returnTypes.baseType(def.getReturns()), returnTypes.async(def.getReturns())); + // TODO(pm): handle the unflagged case. Handle the async case. + TypeName returnType = ClassName.get( + className.packageName(), + className.simpleName(), + ErrorGenerationUtils.responseTypeName(def.getEndpointName())); + // TypeName returnType = + // methodType.switchBy(returnTypes.baseType(def.getReturns()), + // returnTypes.async(def.getReturns())); methodBuilder.returns(returnType); CodeBlock.Builder requestParams = CodeBlock.builder(); diff --git a/conjure-java-core/src/main/java/com/palantir/conjure/java/services/dialogue/DialogueEndpointResultTypeGenerator.java b/conjure-java-core/src/main/java/com/palantir/conjure/java/services/dialogue/DialogueEndpointResultTypeGenerator.java deleted file mode 100644 index bd36833b6..000000000 --- a/conjure-java-core/src/main/java/com/palantir/conjure/java/services/dialogue/DialogueEndpointResultTypeGenerator.java +++ /dev/null @@ -1,129 +0,0 @@ -/* - * (c) Copyright 2024 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 com.palantir.conjure.java.services.dialogue; - -import com.google.common.base.CaseFormat; -import com.palantir.conjure.java.ConjureAnnotations; -import com.palantir.conjure.java.Options; -import com.palantir.conjure.java.util.Packages; -import com.palantir.conjure.spec.EndpointDefinition; -import com.palantir.conjure.spec.EndpointError; -import com.palantir.conjure.spec.ServiceDefinition; -import com.palantir.javapoet.ClassName; -import com.palantir.javapoet.JavaFile; -import com.palantir.javapoet.MethodSpec; -import com.palantir.javapoet.ParameterSpec; -import com.palantir.javapoet.TypeName; -import com.palantir.javapoet.TypeSpec; -import java.util.List; -import javax.lang.model.element.Modifier; - -public final class DialogueEndpointResultTypeGenerator { - private final Options options; - private final ReturnTypeMapper returnTypeMapper; - - public DialogueEndpointResultTypeGenerator(Options options, ReturnTypeMapper returnTypeMapper) { - this.options = options; - this.returnTypeMapper = returnTypeMapper; - } - - public JavaFile generateEndpointResultTypes(ServiceDefinition serviceDefinition) { - String packageName = - Packages.getPrefixedPackage(serviceDefinition.getServiceName().getPackage(), options.packagePrefix()); - ClassName resultsClassName = Names.resultTypesClassName(serviceDefinition, options); - TypeSpec resultTypesClass = TypeSpec.classBuilder(resultsClassName) - .addModifiers(Modifier.PUBLIC, Modifier.FINAL) - .addAnnotation( - ConjureAnnotations.getConjureGeneratedAnnotation(DialogueEndpointResultTypeGenerator.class)) - .addTypes(createResultTypes(serviceDefinition, packageName, resultsClassName)) - .build(); - - return JavaFile.builder(packageName, resultTypesClass).build(); - } - - private List createResultTypes( - ServiceDefinition serviceDefinition, String packageName, ClassName resultsClassName) { - return serviceDefinition.getEndpoints().stream() - .filter(endpoint -> !endpoint.getErrors().isEmpty()) - .map(pt -> createResultTypesForEndpoint(pt, packageName, resultsClassName)) - .toList(); - } - - private TypeSpec createResultTypesForEndpoint( - EndpointDefinition endpointDefinition, String packageName, ClassName resultsClassName) { - String endpointResultName = getEndpointResultName(endpointDefinition); - TypeSpec.Builder typeBuilder = TypeSpec.interfaceBuilder(endpointResultName) - .addModifiers(Modifier.PUBLIC, Modifier.SEALED) - .addType(createSuccessRecord(endpointDefinition, packageName, resultsClassName)) - .addPermittedSubclass( - ClassName.get(packageName, resultsClassName.simpleName(), endpointResultName, "Success")) - .addTypes(endpointDefinition.getErrors().stream() - .map(error -> createErrorRecord(error, endpointDefinition, packageName, resultsClassName)) - .toList()) - .addPermittedSubclasses(endpointDefinition.getErrors().stream() - .map(error -> ClassName.get( - packageName, - resultsClassName.simpleName(), - getEndpointResultName(endpointDefinition), - getErrorName(error))) - .toList()); - // TODO(p): add permitted subclasses for all errors. - - return typeBuilder.build(); - } - - private TypeSpec createSuccessRecord( - EndpointDefinition endpointDefinition, String packageName, ClassName resultsClassName) { - TypeSpec.Builder builder = TypeSpec.recordBuilder("Success") - .addModifiers(Modifier.PUBLIC, Modifier.STATIC) - .addSuperinterface(ClassName.get( - packageName, resultsClassName.simpleName(), getEndpointResultName(endpointDefinition))); - if (endpointDefinition.getReturns().isPresent()) { - TypeName returnType = - returnTypeMapper.baseType(endpointDefinition.getReturns().get()); - builder.recordConstructor(MethodSpec.constructorBuilder() - .addParameter(ParameterSpec.builder(returnType, "value").build()) - .build()); - } - return builder.build(); - } - - private TypeSpec createErrorRecord( - EndpointError endpointError, - EndpointDefinition endpointDefinition, - String packageName, - ClassName resultsClassName) { - TypeSpec.Builder builder = TypeSpec.recordBuilder(getErrorName(endpointError)) - .addModifiers(Modifier.PUBLIC, Modifier.STATIC) - .addSuperinterface(ClassName.get( - packageName, resultsClassName.simpleName(), getEndpointResultName(endpointDefinition))); - - return builder.build(); - } - - private static String getErrorName(EndpointError endpointError) { - return String.format( - "%s_%s", - endpointError.getError().getNamespace(), - endpointError.getError().getName()); - } - - private static String getEndpointResultName(EndpointDefinition definition) { - return CaseFormat.LOWER_CAMEL.to( - CaseFormat.UPPER_CAMEL, definition.getEndpointName().get()) + "Result"; - } -} 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..3b9669135 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,16 +18,22 @@ import static java.util.stream.Collectors.toList; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.common.base.CaseFormat; import com.google.errorprone.annotations.MustBeClosed; import com.palantir.conjure.java.ConjureAnnotations; import com.palantir.conjure.java.Options; +import com.palantir.conjure.java.lib.internal.ConjureErrors.BaseEndpointError; import com.palantir.conjure.java.services.IsUndertowAsyncMarkerVisitor; import com.palantir.conjure.java.services.ServiceGenerators; 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.Packages; import com.palantir.conjure.spec.EndpointDefinition; +import com.palantir.conjure.spec.EndpointError; import com.palantir.conjure.spec.ServiceDefinition; import com.palantir.conjure.spec.Type; import com.palantir.conjure.visitor.TypeVisitor; @@ -43,12 +49,15 @@ 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.SafeArg; 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; @@ -67,19 +76,39 @@ public DialogueInterfaceGenerator( this.returnTypes = returnTypes; } - public JavaFile generateBlocking(ServiceDefinition def, StaticFactoryMethodGenerator methodGenerator) { - return generate(def, Names.blockingClassName(def, options), returnTypes::baseType, methodGenerator); + public JavaFile generateBlocking( + ServiceDefinition def, + StaticFactoryMethodGenerator methodGenerator, + // TODO(pm): use this flag. + boolean generateDialogueEndpointErrorResultTypes) { + return generate( + def, + Names.blockingClassName(def, options), + returnTypes::baseType, + 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, + // TODO(pm): use this flag. + boolean generateDialogueEndpointErrorResultTypes) { + return generate( + def, + Names.asyncClassName(def, options), + returnTypes::async, + methodGenerator, + generateDialogueEndpointErrorResultTypes); } private JavaFile generate( ServiceDefinition def, ClassName className, Function, TypeName> returnTypeMapper, - StaticFactoryMethodGenerator methodGenerator) { + 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 +132,14 @@ 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, returnTypeMapper)) .collect(toList())); + // Create public sealed interface for the "response" type for each of the endpoints. + serviceBuilder.addTypes(def.getEndpoints().stream() + .map(endpointDef -> responseTypeForEndpoint(packageName, className, endpointDef, returnTypeMapper)) + .toList()); + MethodSpec staticFactoryMethod = methodGenerator.generate(def); serviceBuilder.addMethod(staticFactoryMethod); @@ -145,13 +179,103 @@ private JavaFile generate( .build()) .build()); - return JavaFile.builder( - Packages.getPrefixedPackage(def.getServiceName().getPackage(), options.packagePrefix()), - serviceBuilder.build()) + return JavaFile.builder(packageName, serviceBuilder.build()).build(); + } + + private TypeSpec responseTypeForEndpoint( + String packageName, + ClassName className, + EndpointDefinition endpointDef, + Function, TypeName> returnTypeMapper) { + ClassName responseTypeName = ClassName.get( + packageName, + className.simpleName(), + ErrorGenerationUtils.responseTypeName(endpointDef.getEndpointName())); + // Create the success record + TypeSpec successRecord = TypeSpec.recordBuilder( + ClassName.get(packageName, responseTypeName.simpleName(), "Success")) + .addModifiers(Modifier.PUBLIC, Modifier.STATIC) + .recordConstructor(MethodSpec.compactConstructorBuilder() + .addParameter(ParameterSpec.builder(returnTypeMapper.apply(endpointDef.getReturns()), "value") + .build()) + .addModifiers(Modifier.PUBLIC) + .addStatement("$T.checkArgumentNotNull(value, \"value cannot be null\")", Preconditions.class) + .build()) + .addSuperinterface(responseTypeName) + .build(); + + // Create a record for each of the endpoint's errors + List errorTypes = new ArrayList<>(); + for (EndpointError endpointError : endpointDef.getErrors()) { + String errorTypeName = CaseFormat.UPPER_CAMEL.to( + CaseFormat.UPPER_UNDERSCORE, endpointError.getError().getName()); + ClassName errorTypesClassName = ClassName.get( + endpointError.getError().getPackage(), + ErrorGenerationUtils.errorTypesClassName( + endpointError.getError().getNamespace()), + errorTypeName); + ClassName parametersClassName = ClassName.get( + endpointError.getError().getPackage(), + ErrorGenerationUtils.errorTypesClassName( + endpointError.getError().getNamespace()), + ErrorGenerationUtils.errorParametersClassName( + endpointError.getError().getName())); + TypeSpec endpointErrorType = TypeSpec.classBuilder(ClassName.get( + packageName, + responseTypeName.simpleName(), + endpointError.getError().getName())) + .addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL) + .addSuperinterface(responseTypeName) + .superclass(ParameterizedTypeName.get(ClassName.get(BaseEndpointError.class), parametersClassName)) + .addMethod(errorTypeConstructor(parametersClassName, errorTypesClassName)) + .build(); + errorTypes.add(endpointErrorType); + } + + // Get type from typespec + return 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) .build(); } - private MethodSpec apiMethod(EndpointDefinition endpointDef, Function, TypeName> returnTypeMapper) { + private MethodSpec errorTypeConstructor(ClassName parametersClassName, ClassName errorTypesClassName) { + MethodSpec.Builder ctorBuilder = MethodSpec.constructorBuilder() + .addAnnotation(JsonCreator.class) + .addParameter(ParameterSpec.builder(ClassName.get(String.class), "errorCode") + .addAnnotation(AnnotationSpec.builder(JsonProperty.class) + .addMember("value", "$S", "errorCode") + .build()) + .build()) + .addParameter(ParameterSpec.builder(ClassName.get(String.class), "errorInstanceId") + .addAnnotation(AnnotationSpec.builder(JsonProperty.class) + .addMember("value", "$S", "errorInstanceId") + .build()) + .build()) + // TODO(pm): Only add the parameters if the error has them. Plumb in error definitions. + .addParameter(ParameterSpec.builder(parametersClassName, "parameters") + .addAnnotation(AnnotationSpec.builder(JsonProperty.class) + .addMember("value", "$S", "parameters") + .build()) + .build()) + .addStatement( + // TODO(pm): Perhaps these shouldn't be literals but names ($N). + "super(errorCode, $T.name(), errorInstanceId, parameters)", errorTypesClassName); + return ctorBuilder.build(); + } + + private MethodSpec apiMethod( + String packageName, + ClassName className, + EndpointDefinition endpointDef, + Function, TypeName> returnTypeMapper) { MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder( endpointDef.getEndpointName().get()) .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT) @@ -184,7 +308,11 @@ private MethodSpec apiMethod(EndpointDefinition endpointDef, Function generate(ConjureDefinition conjureDefinition) { Map types = TypeFunctions.toTypesMap(conjureDefinition); SafetyEvaluator safetyEvaluator = new SafetyEvaluator(types); DialogueEndpointsGenerator endpoints = new DialogueEndpointsGenerator(options); + // TODO(pm): only evaluate this if the option is set + DeclaredEndpointErrors declaredEndpointErrors = DeclaredEndpointErrors.from(conjureDefinition); TypeMapper parameterTypes = new TypeMapper( types, new SpecializeBinaryClassNameVisitor( @@ -75,18 +78,21 @@ public Stream generate(ConjureDefinition conjureDefinition) { typeNameResolver, parameterMapper, new ReturnTypeMapper(returnTypes), - StaticFactoryMethodType.ASYNC); + StaticFactoryMethodType.ASYNC, + declaredEndpointErrors); StaticFactoryMethodGenerator blockingGenerator = new DefaultStaticFactoryMethodGenerator( options, typeNameResolver, parameterMapper, new ReturnTypeMapper(returnTypes), - StaticFactoryMethodType.BLOCKING); + StaticFactoryMethodType.BLOCKING, + declaredEndpointErrors); return conjureDefinition.getServices().stream() .flatMap(serviceDef -> generateFilesForService( options.excludeDialogueAsyncInterfaces(), + options.generateDialogueEndpointErrorResultTypes(), serviceDef, endpoints, interfaceGenerator, @@ -96,18 +102,21 @@ public Stream generate(ConjureDefinition conjureDefinition) { private static Stream generateFilesForService( boolean excludeDialogueAsyncInterfaces, + boolean generateDialogueEndpointErrorResultTypes, ServiceDefinition serviceDef, DialogueEndpointsGenerator endpointsGenerator, DialogueInterfaceGenerator interfaceGenerator, StaticFactoryMethodGenerator blockingGenerator, StaticFactoryMethodGenerator asyncGenerator) { - List files = new ArrayList<>(/* initialCapacity= */ 3); + List files = new ArrayList<>(/* initialCapacity= */ 4); 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/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..738e2bdd9 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,48 @@ 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)) + .filter(Optional::isPresent) + .map(Optional::get) + .toList(); + } + + private static Optional generateErrorParameterRecord( + ErrorDefinition errorDefinition, TypeMapper typeMapper) { + // TODO(pm): uncomment this. + // if (errorDefinition.getSafeArgs().isEmpty() + // && errorDefinition.getUnsafeArgs().isEmpty()) { + // return Optional.empty(); + // } + 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.buildParameterWithSafetyAnnotationWithJsonProperty( + typeMapper, fieldDef, true)); + } + for (FieldDefinition fieldDef : errorDefinition.getUnsafeArgs()) { + ctorBuilder.addParameter(ErrorGenerationUtils.buildParameterWithSafetyAnnotationWithJsonProperty( + typeMapper, fieldDef, false)); + } + return Optional.of( + parametersRecordBuilder.recordConstructor(ctorBuilder.build()).build()); + } + private static MethodSpec generateExceptionFactory( TypeMapper typeMapper, ErrorDefinition entry, boolean withCause) { String methodName = CaseFormat.UPPER_CAMEL.to( @@ -194,6 +231,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..4315a9eff 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; @@ -25,6 +26,7 @@ import com.palantir.conjure.java.types.TypeMapper; import com.palantir.conjure.spec.ConjureDefinition; 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; @@ -50,14 +52,31 @@ import org.apache.commons.lang3.StringUtils; public final class ErrorGenerationUtils { + /** + * The name of the sealed interface returned by endpoints with associated errors, permitting implementations for a + * successful result and each error type. + */ + // TODO(pm): Move these methods around. + public static String responseTypeName(EndpointName endpointName) { + return CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_CAMEL, endpointName.get()) + "Response"; + } + + public static String errorParametersClassName(String errorName) { + return errorName + "Parameters"; + } + 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 record DeclaredEndpointErrors(Set errors) { public static DeclaredEndpointErrors from(ConjureDefinition definition) { return new DeclaredEndpointErrors(definition.getServices().stream() @@ -149,6 +168,22 @@ public static ParameterSpec buildParameterWithSafetyAnnotation( return parameterBuilder.build(); } + public static ParameterSpec buildParameterWithSafetyAnnotationWithJsonProperty( + 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); + ParameterSpec.Builder parameterBuilder = ParameterSpec.builder(argType, argName); + argDefinition + .getDocs() + .ifPresent(docs -> + parameterBuilder.addJavadoc("$L", StringUtils.appendIfMissing(Javadoc.render(docs), "\n"))); + parameterBuilder.addAnnotation(AnnotationSpec.builder(JsonProperty.class) + .addMember("value", "$S", argDefinition.getFieldName()) + .build()); + return parameterBuilder.build(); + } + // Conditional factory method public static MethodSpec.Builder conditionalStaticFactoryMethodBuilder( TypeMapper typeMapper, diff --git a/conjure-java-core/src/test/java/com/palantir/conjure/java/ErrorResource.java b/conjure-java-core/src/test/java/com/palantir/conjure/java/ErrorResource.java new file mode 100644 index 000000000..df7d0b595 --- /dev/null +++ b/conjure-java-core/src/test/java/com/palantir/conjure/java/ErrorResource.java @@ -0,0 +1,45 @@ +/* + * (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 com.palantir.conjure.java; + +import com.palantir.another.EndpointSpecificServerErrors.DifferentPackage; +import com.palantir.product.EndpointSpecificServerErrors.EndpointError; +import com.palantir.product.EndpointSpecificTwoServerErrors.DifferentNamespace; +import com.palantir.product.TestServerErrors.InvalidArgument; +import com.palantir.product.TestServerErrors.NotFound; +import com.palantir.product.UndertowErrorService; +import com.palantir.tokens.auth.AuthHeader; + +interface ErrorResource { + class Impl implements UndertowErrorService { + @Override + public String testBasicError(AuthHeader authHeader) { + return "HELLO"; + } + + @Override + public String testImportedError(AuthHeader authHeader) throws EndpointError { + return ""; + } + + @Override + public String testMultipleErrorsAndPackages(AuthHeader authHeader) + throws InvalidArgument, NotFound, DifferentNamespace, DifferentPackage { + return ""; + } + } +} diff --git a/conjure-java-core/src/test/java/com/palantir/conjure/java/UndertowServiceEteTest.java b/conjure-java-core/src/test/java/com/palantir/conjure/java/UndertowServiceEteTest.java index bed151e13..b58f58076 100644 --- a/conjure-java-core/src/test/java/com/palantir/conjure/java/UndertowServiceEteTest.java +++ b/conjure-java-core/src/test/java/com/palantir/conjure/java/UndertowServiceEteTest.java @@ -26,23 +26,27 @@ import com.google.common.net.HttpHeaders; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.UncheckedExecutionException; +import com.palantir.conjure.defs.Conjure; import com.palantir.conjure.java.api.errors.RemoteException; import com.palantir.conjure.java.api.errors.SerializableError; import com.palantir.conjure.java.client.jaxrs.JaxRsClient; import com.palantir.conjure.java.lib.SafeLong; import com.palantir.conjure.java.okhttp.HostMetricsRegistry; import com.palantir.conjure.java.serialization.ObjectMappers; +import com.palantir.conjure.java.services.UndertowServiceGenerator; +import com.palantir.conjure.java.types.CheckedErrorGenerator; +import com.palantir.conjure.java.types.ErrorGenerator; +import com.palantir.conjure.java.types.ObjectGenerator; import com.palantir.conjure.java.undertow.runtime.ConjureHandler; +import com.palantir.conjure.spec.ConjureDefinition; import com.palantir.dialogue.BinaryRequestBody; import com.palantir.dialogue.clients.DialogueClients; +import com.palantir.product.ErrorServiceBlocking.TestBasicErrorResponse; import com.palantir.ri.ResourceIdentifier; import com.palantir.tokens.auth.AuthHeader; import dialogue.com.palantir.product.EteBinaryServiceBlocking; import dialogue.com.palantir.product.EteServiceAsync; import dialogue.com.palantir.product.EteServiceBlocking; -import dialogue.com.palantir.product.NestedStringAliasExample; -import dialogue.com.palantir.product.SimpleEnum; -import dialogue.com.palantir.product.StringAliasExample; import io.undertow.Handlers; import io.undertow.Undertow; import io.undertow.UndertowOptions; @@ -73,10 +77,10 @@ import org.junit.jupiter.api.parallel.Execution; import org.junit.jupiter.api.parallel.ExecutionMode; import undertow.com.palantir.product.EmptyPathServiceEndpoints; +import undertow.com.palantir.product.ErrorServiceEndpoints; import undertow.com.palantir.product.EteBinaryServiceEndpoints; import undertow.com.palantir.product.EteServiceEndpoints; -// MARK(pm). @Execution(ExecutionMode.CONCURRENT) public final class UndertowServiceEteTest extends TestBase { private static final ObjectMapper CLIENT_OBJECT_MAPPER = ObjectMappers.newClientObjectMapper(); @@ -87,6 +91,8 @@ public final class UndertowServiceEteTest extends TestBase { private static Undertow server; private final EteServiceBlocking client; + // TODO(pm): this should be in a different test. + private final com.palantir.product.ErrorServiceBlocking errorServiceClient; private final EteServiceAsync asyncClient; private final EteBinaryServiceBlocking binaryClient; @@ -97,6 +103,7 @@ public UndertowServiceEteTest() { this.client = DialogueClients.create(EteServiceBlocking.class, clientConfiguration(port)); this.asyncClient = DialogueClients.create(EteServiceAsync.class, clientConfiguration(port)); this.binaryClient = DialogueClients.create(EteBinaryServiceBlocking.class, clientConfiguration(port)); + this.errorServiceClient = DialogueClients.create(com.palantir.product.ErrorServiceBlocking.class, clientConfiguration(port)); } @BeforeAll @@ -106,6 +113,7 @@ public static void before() { .services(EteServiceEndpoints.of(new UndertowEteResource())) .services(EmptyPathServiceEndpoints.of(() -> true)) .services(EteBinaryServiceEndpoints.of(new UndertowBinaryResource())) + .services(ErrorServiceEndpoints.of(new ErrorResource.Impl())) .build(); server = Undertow.builder() @@ -133,6 +141,14 @@ public void jaxrs_client_can_make_a_call_to_an_empty_path() { assertThat(emptyPathClient.emptyPath()).isTrue(); } + @Test + public void error_client_returns_result() { + TestBasicErrorResponse result = errorServiceClient.testBasicError(AuthHeader.valueOf("authHeader")); + assertThat(result).isInstanceOfSatisfying(TestBasicErrorResponse.Success.class, success -> { + assertThat(success.value()).isEqualTo("HELLO"); + }); + } + @Test public void client_can_retrieve_a_string_from_a_server() { assertThat(client.string(AuthHeader.valueOf("authHeader"))).isEqualTo("Hello, world!"); @@ -546,6 +562,44 @@ public void testListOfNull() { }); } + @BeforeAll + public static void beforeClass() throws IOException { + ConjureDefinition def = Conjure.parse(ImmutableList.of( + new File("src/test/resources/ete-service.yml"), + new File("src/test/resources/ete-binary.yml"), + new File("src/test/resources/alias-test-service.yml"), + new File("src/test/resources/external-long-test-service.yml"), + new File("src/test/resources/example-endpoint-errors.yml"))); + Options options = Options.builder() + .undertowServicePrefix(true) + .nonNullCollections(true) + .excludeEmptyOptionals(true) + .jetbrainsContractAnnotations(true) + .build(); + List files = new GenerationCoordinator( + MoreExecutors.directExecutor(), + ImmutableSet.of( + new UndertowServiceGenerator(options), + new ObjectGenerator(options), + new ErrorGenerator(options), + new CheckedErrorGenerator(options))) + .emit(def, folder); + // validateGeneratedOutput(files, Paths.get("src/integrationInput/java")); + } + + // private static void validateGeneratedOutput(List files, Path outputDir) throws IOException { + // for (Path file : files) { + // Path relativePath = folder.toPath().relativize(file); + // Path output = outputDir.resolve(relativePath); + // if (Boolean.valueOf(System.getProperty("recreate", "false"))) { + // Files.createDirectories(relativePath.getParent()); + // Files.deleteIfExists(output); + // Files.copy(file, output); + // } + // assertThat(readFromFile(file)).isEqualTo(readFromFile(output)); + // } + // } + private static HttpURLConnection openConnectionToTestApi(String path) throws IOException { URL url = new URL("http://localhost:" + port + "/test-example/api" + path); return (HttpURLConnection) url.openConnection(); diff --git a/conjure-lib/src/main/java/com/palantir/conjure/java/lib/internal/ConjureErrors.java b/conjure-lib/src/main/java/com/palantir/conjure/java/lib/internal/ConjureErrors.java new file mode 100644 index 000000000..afb51192e --- /dev/null +++ b/conjure-lib/src/main/java/com/palantir/conjure/java/lib/internal/ConjureErrors.java @@ -0,0 +1,59 @@ +/* + * (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 com.palantir.conjure.java.lib.internal; + +import com.palantir.logsafe.Safe; + +public final class ConjureErrors { + private ConjureErrors() {} + + public abstract static class BaseEndpointError { + @Safe + private final String errorCode; + + @Safe + private final String errorName; + + @Safe + private final String errorInstanceId; + + private final T params; + + protected BaseEndpointError(String errorCode, String errorName, String errorInstanceId, T params) { + this.errorCode = errorCode; + this.errorName = errorName; + this.errorInstanceId = errorInstanceId; + this.params = params; + } + + public final String getErrorCode() { + return errorCode; + } + + public final String getErrorName() { + return errorName; + } + + public final String getErrorInstanceId() { + return errorInstanceId; + } + + public final T getParams() { + return params; + } + } +} diff --git a/versions.lock b/versions.lock index d335633cf..8c9f7105c 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: 6b950530) +com.fasterxml.jackson.core:jackson-core:2.18.2 (22 constraints: 34c1632c) +com.fasterxml.jackson.core:jackson-databind:2.18.2 (35 constraints: 129b6b75) +com.fasterxml.jackson.dataformat:jackson-dataformat-cbor:2.18.2 (4 constraints: ea652851) +com.fasterxml.jackson.dataformat:jackson-dataformat-smile:2.18.2 (1 constraints: 811ca8a4) 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: f839a626) +com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.18.2 (4 constraints: 303f5992) +com.fasterxml.jackson.datatype:jackson-datatype-joda:2.18.2 (2 constraints: 322b4db0) +com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.18.2 (2 constraints: 322b4db0) +com.github.ben-manes.caffeine:caffeine:3.1.8 (11 constraints: b0c0b031) 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: 8be16a7d) 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 (37 constraints: 62af88b0) 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.conjure.java.api:errors:2.57.0 (7 constraints: 5a81073a) +com.palantir.conjure.java.api:service-config:2.57.0 (6 constraints: 4366d3dc) +com.palantir.conjure.java.api:ssl-config:2.57.0 (5 constraints: bd4f6230) +com.palantir.conjure.java.runtime:client-config:8.16.0 (6 constraints: 846ba0f0) +com.palantir.conjure.java.runtime:conjure-java-jackson-optimizations:8.16.0 (1 constraints: 851cc6a4) +com.palantir.conjure.java.runtime:conjure-java-jackson-serialization:8.16.0 (4 constraints: f5461996) +com.palantir.conjure.java.runtime:keystores:8.16.0 (3 constraints: ea297a45) 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.dialogue:dialogue-apache-hc5-client:4.8.0-rc1 (2 constraints: 442a2d5c) +com.palantir.dialogue:dialogue-blocking-channels:4.8.0-rc1 (2 constraints: dd25de90) +com.palantir.dialogue:dialogue-clients:4.8.0-rc1 (1 constraints: 4106504d) +com.palantir.dialogue:dialogue-core:4.8.0-rc1 (3 constraints: 463ffd25) +com.palantir.dialogue:dialogue-futures:4.8.0-rc1 (3 constraints: d436dd9c) +com.palantir.dialogue:dialogue-serde:4.8.0-rc1 (3 constraints: 84306a91) +com.palantir.dialogue:dialogue-target:4.8.0-rc1 (7 constraints: f57ea535) 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.ri:resource-identifier:2.8.0 (3 constraints: c92457f4) +com.palantir.safe-logging:logger:3.7.0 (24 constraints: 25a2e775) 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.safe-logging:preconditions:3.7.0 (31 constraints: 3c17f802) +com.palantir.safe-logging:safe-logging:3.7.0 (28 constraints: a1ea9f05) +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:6.20.0 (8 constraints: 4d7b1001) +com.palantir.tracing:tracing-api:6.20.0 (6 constraints: 7655474c) com.palantir.tracing:tracing-undertow:6.20.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-metrics:0.96.0 (7 constraints: 67771c68) +com.palantir.tritium:tritium-registry:0.96.0 (12 constraints: 57eab29e) 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: 9de210ce) 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.1 (3 constraints: 6439b10d) 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 (5 constraints: d447261e) 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) @@ -96,7 +97,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: 431985bb) 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 +110,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.conjure.java.api:test-utils:2.57.0 (1 constraints: 3f05513b) +com.palantir.conjure.java.runtime:conjure-java-annotations:8.16.0 (1 constraints: c5188a9e) +com.palantir.conjure.java.runtime:conjure-java-jaxrs-client:8.16.0 (1 constraints: 11052836) +com.palantir.conjure.java.runtime:conjure-java-jersey-server:8.16.0 (1 constraints: 11052836) +com.palantir.conjure.java.runtime:conjure-java-legacy-clients:8.16.0 (2 constraints: d51d2032) +com.palantir.conjure.java.runtime:refresh-utils:8.16.0 (1 constraints: c5188a9e) 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.tracing:tracing-jersey:6.20.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) diff --git a/versions.props b/versions.props index 5a8ca66fc..b27d84553 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:* = 4.8.0-rc1 com.palantir.goethe:* = 0.14.0 com.palantir.human-readable-types:* = 1.7.0 com.palantir.javapoet:javapoet = 0.5.0 From 34a8977ba9bf53ba4bc99f742992d8248d3e7868 Mon Sep 17 00:00:00 2001 From: Pritham Marupaka Date: Wed, 22 Jan 2025 14:23:35 -0500 Subject: [PATCH 03/24] update codegen with support for endpoints that return empty-body responses --- build.gradle | 1 + .../product/DialogueErrorEndpoints.java | 30 +++++++++++ .../product/ErrorServiceBlocking.java | 45 +++++++++++++++++ .../DefaultStaticFactoryMethodGenerator.java | 10 +++- .../dialogue/DialogueInterfaceGenerator.java | 50 +++++++++++++------ .../resources/example-endpoint-errors.yml | 4 ++ .../java/lib/internal/ConjureErrors.java | 24 +++++++++ 7 files changed, 148 insertions(+), 16 deletions(-) diff --git a/build.gradle b/build.gradle index f91f6cd7d..e87e3c0d8 100644 --- a/build.gradle +++ b/build.gradle @@ -58,6 +58,7 @@ allprojects { group 'com.palantir.conjure.java' repositories { + mavenLocal() mavenCentral() { metadataSources { mavenPom(); ignoreGradleMetadataRedirection() } } } diff --git a/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/DialogueErrorEndpoints.java b/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/DialogueErrorEndpoints.java index 927621be6..962366562 100644 --- a/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/DialogueErrorEndpoints.java +++ b/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/DialogueErrorEndpoints.java @@ -95,6 +95,36 @@ public String endpointName() { return "testMultipleErrorsAndPackages"; } + @Override + public String version() { + return "1.2.3"; + } + }, + + testEmptyBody { + private final PathTemplate pathTemplate = + PathTemplate.builder().fixed("base").fixed("empty").build(); + + @Override + public void renderPath(ListMultimap params, UrlBuilder url) { + pathTemplate.fill(params, url); + } + + @Override + public HttpMethod httpMethod() { + return HttpMethod.GET; + } + + @Override + public String serviceName() { + return "ErrorService"; + } + + @Override + public String endpointName() { + return "testEmptyBody"; + } + @Override public String version() { return "1.2.3"; diff --git a/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/ErrorServiceBlocking.java b/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/ErrorServiceBlocking.java index 0e55d5684..a3a95b581 100644 --- a/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/ErrorServiceBlocking.java +++ b/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/ErrorServiceBlocking.java @@ -37,6 +37,10 @@ public interface ErrorServiceBlocking { @ClientEndpoint(method = "GET", path = "/base/multiple") TestMultipleErrorsAndPackagesResponse testMultipleErrorsAndPackages(AuthHeader authHeader); + /** @apiNote {@code GET /base/empty} */ + @ClientEndpoint(method = "GET", path = "/base/empty") + TestEmptyBodyResponse testEmptyBody(AuthHeader authHeader); + /** Creates a synchronous/blocking client for a ErrorService service. */ static ErrorServiceBlocking of(EndpointChannelFactory _endpointChannelFactory, ConjureRuntime _runtime) { return new ErrorServiceBlocking() { @@ -89,6 +93,18 @@ static ErrorServiceBlocking of(EndpointChannelFactory _endpointChannelFactory, C new TypeMarker() {}) .build()); + 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()); + @Override public TestBasicErrorResponse testBasicError(AuthHeader authHeader) { Request.Builder _request = Request.builder(); @@ -116,6 +132,14 @@ public TestMultipleErrorsAndPackagesResponse testMultipleErrorsAndPackages(AuthH testMultipleErrorsAndPackagesDeserializer); } + @Override + public TestEmptyBodyResponse testEmptyBody(AuthHeader authHeader) { + Request.Builder _request = Request.builder(); + _request.putHeaderParams("Authorization", authHeader.toString()); + return _runtime.clients() + .callBlocking(testEmptyBodyChannel, _request.build(), testEmptyBodyDeserializer); + } + @Override public String toString() { return "ErrorServiceBlocking{_endpointChannelFactory=" + _endpointChannelFactory + ", runtime=" @@ -251,4 +275,25 @@ final class DifferentPackage } } } + + sealed interface TestEmptyBodyResponse + permits TestEmptyBodyResponse.Success, TestEmptyBodyResponse.InvalidArgument { + record Success() implements TestEmptyBodyResponse { + @JsonCreator + public static Success create() { + return new Success(); + } + } + + final class InvalidArgument extends ConjureErrors.BaseEndpointError + implements TestEmptyBodyResponse { + @JsonCreator + InvalidArgument( + @JsonProperty("errorCode") String errorCode, + @JsonProperty("errorInstanceId") String errorInstanceId, + @JsonProperty("parameters") TestErrors.InvalidArgumentParameters parameters) { + super(errorCode, TestErrors.INVALID_ARGUMENT.name(), errorInstanceId, parameters); + } + } + } } 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 97a556a0f..6deb74ae0 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 @@ -197,7 +197,10 @@ private Optional deserializer( CodeBlock initializer = CodeBlock.of( "$L.bodySerDe().$L", StaticFactoryMethodGenerator.RUNTIME, - type.isPresent() ? realDeserializerWithArgs : voidDeserializer); + // TODO(pm): make this nicer. + type.isPresent() || options.generateDialogueEndpointErrorResultTypes() + ? realDeserializerWithArgs + : voidDeserializer); return Optional.of(FieldSpec.builder(deserializerType, endpointName + "Deserializer") .addModifiers(Modifier.PRIVATE, Modifier.FINAL) @@ -292,7 +295,10 @@ private MethodSpec clientImpl(ClassName className, EndpointDefinition def) { .orElseGet(() -> def.getEndpointName().get() + "Deserializer")); methodBuilder.addCode(request); - methodBuilder.addCode(methodType.switchBy(def.getReturns().isPresent() ? "return " : "", "return ")); + methodBuilder.addCode(methodType.switchBy( + // TODO(pm): make this nicer. + def.getReturns().isPresent() || options.generateDialogueEndpointErrorResultTypes() ? "return " : "", + "return ")); methodBuilder.addCode(execute); return methodBuilder.build(); 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 3b9669135..10cb0483c 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 @@ -79,7 +79,6 @@ public DialogueInterfaceGenerator( public JavaFile generateBlocking( ServiceDefinition def, StaticFactoryMethodGenerator methodGenerator, - // TODO(pm): use this flag. boolean generateDialogueEndpointErrorResultTypes) { return generate( def, @@ -191,19 +190,8 @@ private TypeSpec responseTypeForEndpoint( packageName, className.simpleName(), ErrorGenerationUtils.responseTypeName(endpointDef.getEndpointName())); - // Create the success record - TypeSpec successRecord = TypeSpec.recordBuilder( - ClassName.get(packageName, responseTypeName.simpleName(), "Success")) - .addModifiers(Modifier.PUBLIC, Modifier.STATIC) - .recordConstructor(MethodSpec.compactConstructorBuilder() - .addParameter(ParameterSpec.builder(returnTypeMapper.apply(endpointDef.getReturns()), "value") - .build()) - .addModifiers(Modifier.PUBLIC) - .addStatement("$T.checkArgumentNotNull(value, \"value cannot be null\")", Preconditions.class) - .build()) - .addSuperinterface(responseTypeName) - .build(); - + TypeSpec successRecord = + createSuccessRecord(packageName, className, responseTypeName, endpointDef, returnTypeMapper); // Create a record for each of the endpoint's errors List errorTypes = new ArrayList<>(); for (EndpointError endpointError : endpointDef.getErrors()) { @@ -246,6 +234,40 @@ private TypeSpec responseTypeForEndpoint( .build(); } + private TypeSpec createSuccessRecord( + String packageName, + ClassName className, + ClassName responseTypeName, + EndpointDefinition endpointDef, + Function, TypeName> returnTypeMapper) { + 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 = returnTypeMapper.apply(endpointDef.getReturns()); + if (!returnType.equals(TypeName.VOID)) { + successCtorBuilder + .addParameter(ParameterSpec.builder(returnType, "value").build()) + .addStatement("$T.checkArgumentNotNull(value, \"value cannot be null\")", Preconditions.class); + } + + successRecordBuilder + .addModifiers(Modifier.PUBLIC, Modifier.STATIC) + .recordConstructor(successCtorBuilder.build()) + .addSuperinterface(responseTypeName); + + if (returnType.equals(TypeName.VOID)) { + successRecordBuilder.addMethod(MethodSpec.methodBuilder("create") + .addAnnotation(JsonCreator.class) + .addModifiers(Modifier.PUBLIC, Modifier.STATIC) + .returns(successTypeClassName) + .addStatement("return new $T()", successTypeClassName) + .build()); + } + return successRecordBuilder.build(); + } + private MethodSpec errorTypeConstructor(ClassName parametersClassName, ClassName errorTypesClassName) { MethodSpec.Builder ctorBuilder = MethodSpec.constructorBuilder() .addAnnotation(JsonCreator.class) 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..2c40a1a72 100644 --- a/conjure-java-core/src/test/resources/example-endpoint-errors.yml +++ b/conjure-java-core/src/test/resources/example-endpoint-errors.yml @@ -42,3 +42,7 @@ services: docs: Something was not found. - importedErrors.DifferentNamespace - importedErrors.DifferentPackage + testEmptyBody: + http: GET /empty + errors: + - InvalidArgument diff --git a/conjure-lib/src/main/java/com/palantir/conjure/java/lib/internal/ConjureErrors.java b/conjure-lib/src/main/java/com/palantir/conjure/java/lib/internal/ConjureErrors.java index afb51192e..38f12fe58 100644 --- a/conjure-lib/src/main/java/com/palantir/conjure/java/lib/internal/ConjureErrors.java +++ b/conjure-lib/src/main/java/com/palantir/conjure/java/lib/internal/ConjureErrors.java @@ -16,7 +16,11 @@ package com.palantir.conjure.java.lib.internal; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; import com.palantir.logsafe.Safe; +import com.palantir.logsafe.exceptions.SafeIllegalStateException; public final class ConjureErrors { private ConjureErrors() {} @@ -56,4 +60,24 @@ public final T getParams() { return params; } } + + public abstract static class NullToDefaultDeserializer extends JsonDeserializer { + public abstract T create(); + + /** + * If a non-null value is deserialized as `null`, throw an exception. + */ + @Override + public T deserialize(JsonParser _parser, DeserializationContext _ctxt) { + throw new SafeIllegalStateException("Attempted to deserialize non-null value as null"); + } + + /** + * When `null` is deserialized, a new object of type `T` is created. + */ + @Override + public T getNullValue(DeserializationContext _ctxt) { + return create(); + } + } } From eb5214d2d5d613b7137882b206b5c464405cbbb9 Mon Sep 17 00:00:00 2001 From: Pritham Marupaka Date: Wed, 22 Jan 2025 19:00:53 -0500 Subject: [PATCH 04/24] Add async support --- .../palantir/product/ErrorServiceAsync.java | 10 ++- .../DefaultStaticFactoryMethodGenerator.java | 64 +++++++++++-------- .../dialogue/DialogueInterfaceGenerator.java | 47 ++++++-------- .../dialogue/DialogueServiceGenerator.java | 9 +-- .../java/DialogueServiceGeneratorTests.java | 49 ++++++++++++++ 5 files changed, 110 insertions(+), 69 deletions(-) diff --git a/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/ErrorServiceAsync.java b/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/ErrorServiceAsync.java index 15defc907..aedde7df1 100644 --- a/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/ErrorServiceAsync.java +++ b/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/ErrorServiceAsync.java @@ -1,5 +1,6 @@ package com.palantir.product; + import com.google.common.util.concurrent.ListenableFuture; import com.palantir.conjure.java.lib.internal.ClientEndpoint; import com.palantir.dialogue.Channel; @@ -14,8 +15,6 @@ import com.palantir.dialogue.Request; import com.palantir.dialogue.TypeMarker; import com.palantir.tokens.auth.AuthHeader; -import java.lang.Override; -import java.lang.String; import javax.annotation.processing.Generated; @Generated("com.palantir.conjure.java.services.dialogue.DialogueInterfaceGenerator") @@ -32,26 +31,25 @@ public interface ErrorServiceAsync { /** @apiNote {@code GET /base/multiple} */ @ClientEndpoint(method = "GET", path = "/base/multiple") ListenableFuture testMultipleErrorsAndPackages(AuthHeader authHeader); - /** 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 EndpointChannel testBasicErrorChannel = - _endpointChannelFactory.endpoint(DialogueErrorEndpoints.testBasicError); + _endpointChannelFactory.endpoint(com.palantir.product.DialogueErrorEndpoints.testBasicError); private final Deserializer testBasicErrorDeserializer = _runtime.bodySerDe().deserializer(new TypeMarker() {}); private final EndpointChannel testImportedErrorChannel = - _endpointChannelFactory.endpoint(DialogueErrorEndpoints.testImportedError); + _endpointChannelFactory.endpoint(com.palantir.product.DialogueErrorEndpoints.testImportedError); private final Deserializer testImportedErrorDeserializer = _runtime.bodySerDe().deserializer(new TypeMarker() {}); private final EndpointChannel testMultipleErrorsAndPackagesChannel = - _endpointChannelFactory.endpoint(DialogueErrorEndpoints.testMultipleErrorsAndPackages); + _endpointChannelFactory.endpoint(com.palantir.product.DialogueErrorEndpoints.testMultipleErrorsAndPackages); private final Deserializer testMultipleErrorsAndPackagesDeserializer = _runtime.bodySerDe().deserializer(new TypeMarker() {}); 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 6deb74ae0..0b3ae7ec0 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 @@ -17,10 +17,10 @@ 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.ErrorGenerationUtils.DeclaredEndpointErrors; import com.palantir.conjure.java.util.Packages; import com.palantir.conjure.java.util.Primitives; import com.palantir.conjure.spec.ArgumentDefinition; @@ -82,21 +82,18 @@ public final class DefaultStaticFactoryMethodGenerator implements StaticFactoryM private final ParameterTypeMapper parameterTypes; private final ReturnTypeMapper returnTypes; private final StaticFactoryMethodType methodType; - private final DeclaredEndpointErrors declaredEndpointErrors; public DefaultStaticFactoryMethodGenerator( Options options, TypeNameResolver typeNameResolver, ParameterTypeMapper parameterTypes, ReturnTypeMapper returnTypes, - StaticFactoryMethodType methodType, - DeclaredEndpointErrors declaredEndpointErrors) { + StaticFactoryMethodType methodType) { this.options = options; this.typeNameResolver = typeNameResolver; this.parameterTypes = parameterTypes; this.returnTypes = returnTypes; this.methodType = methodType; - this.declaredEndpointErrors = declaredEndpointErrors; } @Override @@ -187,20 +184,12 @@ private Optional deserializer( ParameterizedTypeName deserializerType = ParameterizedTypeName.get(ClassName.get(Deserializer.class), responseType); - // CodeBlock realDeserializer = CodeBlock.of("deserializer(new $T<$T>() {})", TypeMarker.class, className); - CodeBlock realDeserializerWithArgs = CodeBlock.builder() - .add("deserializer(") - .add(constructDeserializerArgsForEndpoint(endpointDef, responseType)) - .add(")") - .build(); - CodeBlock voidDeserializer = CodeBlock.of("emptyBodyDeserializer()"); CodeBlock initializer = CodeBlock.of( "$L.bodySerDe().$L", StaticFactoryMethodGenerator.RUNTIME, - // TODO(pm): make this nicer. - type.isPresent() || options.generateDialogueEndpointErrorResultTypes() - ? realDeserializerWithArgs - : voidDeserializer); + options.generateDialogueEndpointErrorResultTypes() + ? constructDeserializerWithEndpointErrors(endpointDef, responseType) + : constructDeserializer(type, className)); return Optional.of(FieldSpec.builder(deserializerType, endpointName + "Deserializer") .addModifiers(Modifier.PRIVATE, Modifier.FINAL) @@ -208,7 +197,13 @@ private Optional deserializer( .build()); } - private CodeBlock constructDeserializerArgsForEndpoint(EndpointDefinition endpointDef, TypeName responseType) { + private CodeBlock constructDeserializer(Optional type, TypeName className) { + return type.isPresent() + ? CodeBlock.of("deserializer(new $T<$T>() {})", TypeMarker.class, className) + : CodeBlock.of("emptyBodyDeserializer()"); + } + + private CodeBlock constructDeserializerWithEndpointErrors(EndpointDefinition endpointDef, TypeName responseType) { CodeBlock.Builder retBuilder = CodeBlock.builder() .add("$T.<$T>builder()", DeserializerArgs.class, responseType) .add(".baseType(new $T<>() {})", TypeMarker.class) @@ -226,7 +221,11 @@ private CodeBlock constructDeserializerArgsForEndpoint(EndpointDefinition endpoi ".error($T.name(), new $T<$T.$L>() {})", errorClass, TypeMarker.class, responseType, errorName); } retBuilder.add(".build()"); - return retBuilder.build(); + return CodeBlock.builder() + .add("deserializer(") + .add(retBuilder.build()) + .add(")") + .build(); } private static boolean isBinaryOrOptionalBinary(TypeName className, ReturnTypeMapper returnTypes) { @@ -256,14 +255,7 @@ private MethodSpec clientImpl(ClassName className, EndpointDefinition def) { .build()); } - // TODO(pm): handle the unflagged case. Handle the async case. - TypeName returnType = ClassName.get( - className.packageName(), - className.simpleName(), - ErrorGenerationUtils.responseTypeName(def.getEndpointName())); - // 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(); @@ -296,14 +288,30 @@ private MethodSpec clientImpl(ClassName className, EndpointDefinition def) { methodBuilder.addCode(request); methodBuilder.addCode(methodType.switchBy( - // TODO(pm): make this nicer. - def.getReturns().isPresent() || options.generateDialogueEndpointErrorResultTypes() ? "return " : "", + def.getReturns().isPresent() + || (options.generateDialogueEndpointErrorResultTypes() + && !def.getErrors().isEmpty()) + ? "return " + : "", "return ")); methodBuilder.addCode(execute); return methodBuilder.build(); } + private TypeName getReturnType(EndpointDefinition def, ClassName className) { + if (options.generateDialogueEndpointErrorResultTypes() + && !def.getErrors().isEmpty()) { + ClassName returnType = ClassName.get( + className.packageName(), + className.simpleName(), + ErrorGenerationUtils.responseTypeName(def.getEndpointName())); + return methodType.switchBy( + returnType, ParameterizedTypeName.get(ClassName.get(ListenableFuture.class), returnType)); + } + 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 10cb0483c..b84dae138 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 @@ -21,6 +21,7 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import com.google.common.base.CaseFormat; +import com.google.common.util.concurrent.ListenableFuture; import com.google.errorprone.annotations.MustBeClosed; import com.palantir.conjure.java.ConjureAnnotations; import com.palantir.conjure.java.Options; @@ -35,7 +36,6 @@ import com.palantir.conjure.spec.EndpointDefinition; import com.palantir.conjure.spec.EndpointError; 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; @@ -58,8 +58,6 @@ 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; @@ -83,7 +81,7 @@ public JavaFile generateBlocking( return generate( def, Names.blockingClassName(def, options), - returnTypes::baseType, + StaticFactoryMethodType.BLOCKING, methodGenerator, generateDialogueEndpointErrorResultTypes); } @@ -91,12 +89,11 @@ public JavaFile generateBlocking( public JavaFile generateAsync( ServiceDefinition def, StaticFactoryMethodGenerator methodGenerator, - // TODO(pm): use this flag. boolean generateDialogueEndpointErrorResultTypes) { return generate( def, Names.asyncClassName(def, options), - returnTypes::async, + StaticFactoryMethodType.ASYNC, methodGenerator, generateDialogueEndpointErrorResultTypes); } @@ -104,9 +101,9 @@ public JavaFile generateAsync( private JavaFile generate( ServiceDefinition def, ClassName className, - Function, TypeName> returnTypeMapper, + StaticFactoryMethodType serviceCallType, StaticFactoryMethodGenerator methodGenerator, - boolean _generateDialogueEndpointErrorResultTypes) { + boolean generateDialogueEndpointErrorResultTypes) { String packageName = Packages.getPrefixedPackage(def.getServiceName().getPackage(), options.packagePrefix()); TypeSpec.Builder serviceBuilder = TypeSpec.interfaceBuilder(className) .addModifiers(Modifier.PUBLIC) @@ -131,13 +128,15 @@ private JavaFile generate( def.getDocs().ifPresent(docs -> serviceBuilder.addJavadoc("$L", StringUtils.appendIfMissing(docs.get(), "\n"))); serviceBuilder.addMethods(def.getEndpoints().stream() - .map(endpoint -> apiMethod(packageName, className, endpoint, returnTypeMapper)) + .map(endpoint -> apiMethod(packageName, className, endpoint, serviceCallType)) .collect(toList())); - // Create public sealed interface for the "response" type for each of the endpoints. - serviceBuilder.addTypes(def.getEndpoints().stream() - .map(endpointDef -> responseTypeForEndpoint(packageName, className, endpointDef, returnTypeMapper)) - .toList()); + if (generateDialogueEndpointErrorResultTypes) { + // Create public sealed interface for the "response" type for each of the endpoints. + serviceBuilder.addTypes(def.getEndpoints().stream() + .map(endpointDef -> responseTypeForEndpoint(packageName, className, endpointDef)) + .toList()); + } MethodSpec staticFactoryMethod = methodGenerator.generate(def); serviceBuilder.addMethod(staticFactoryMethod); @@ -181,17 +180,12 @@ private JavaFile generate( return JavaFile.builder(packageName, serviceBuilder.build()).build(); } - private TypeSpec responseTypeForEndpoint( - String packageName, - ClassName className, - EndpointDefinition endpointDef, - Function, TypeName> returnTypeMapper) { + private TypeSpec responseTypeForEndpoint(String packageName, ClassName className, EndpointDefinition endpointDef) { ClassName responseTypeName = ClassName.get( packageName, className.simpleName(), ErrorGenerationUtils.responseTypeName(endpointDef.getEndpointName())); - TypeSpec successRecord = - createSuccessRecord(packageName, className, responseTypeName, endpointDef, returnTypeMapper); + 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()) { @@ -235,17 +229,13 @@ private TypeSpec responseTypeForEndpoint( } private TypeSpec createSuccessRecord( - String packageName, - ClassName className, - ClassName responseTypeName, - EndpointDefinition endpointDef, - Function, TypeName> returnTypeMapper) { + 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 = returnTypeMapper.apply(endpointDef.getReturns()); + TypeName returnType = returnTypes.baseType(endpointDef.getReturns()); if (!returnType.equals(TypeName.VOID)) { successCtorBuilder .addParameter(ParameterSpec.builder(returnType, "value").build()) @@ -297,7 +287,7 @@ private MethodSpec apiMethod( String packageName, ClassName className, EndpointDefinition endpointDef, - Function, TypeName> returnTypeMapper) { + StaticFactoryMethodType serviceCallType) { MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder( endpointDef.getEndpointName().get()) .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT) @@ -335,7 +325,8 @@ private MethodSpec apiMethod( packageName, className.simpleName(), ErrorGenerationUtils.responseTypeName(endpointDef.getEndpointName())); - methodBuilder.returns(returnType); + methodBuilder.returns(serviceCallType.switchBy( + returnType, ParameterizedTypeName.get(ClassName.get(ListenableFuture.class), returnType))); if (TypeName.get(InputStream.class).equals(returnType)) { methodBuilder.addAnnotation(MustBeClosed.class); diff --git a/conjure-java-core/src/main/java/com/palantir/conjure/java/services/dialogue/DialogueServiceGenerator.java b/conjure-java-core/src/main/java/com/palantir/conjure/java/services/dialogue/DialogueServiceGenerator.java index 458b4ac60..2c82701a8 100644 --- a/conjure-java-core/src/main/java/com/palantir/conjure/java/services/dialogue/DialogueServiceGenerator.java +++ b/conjure-java-core/src/main/java/com/palantir/conjure/java/services/dialogue/DialogueServiceGenerator.java @@ -22,7 +22,6 @@ import com.palantir.conjure.java.types.SafetyEvaluator; import com.palantir.conjure.java.types.SpecializeBinaryClassNameVisitor; import com.palantir.conjure.java.types.TypeMapper; -import com.palantir.conjure.java.util.ErrorGenerationUtils.DeclaredEndpointErrors; import com.palantir.conjure.java.util.TypeFunctions; import com.palantir.conjure.spec.ConjureDefinition; import com.palantir.conjure.spec.ServiceDefinition; @@ -53,8 +52,6 @@ public Stream generate(ConjureDefinition conjureDefinition) { Map types = TypeFunctions.toTypesMap(conjureDefinition); SafetyEvaluator safetyEvaluator = new SafetyEvaluator(types); DialogueEndpointsGenerator endpoints = new DialogueEndpointsGenerator(options); - // TODO(pm): only evaluate this if the option is set - DeclaredEndpointErrors declaredEndpointErrors = DeclaredEndpointErrors.from(conjureDefinition); TypeMapper parameterTypes = new TypeMapper( types, new SpecializeBinaryClassNameVisitor( @@ -78,16 +75,14 @@ public Stream generate(ConjureDefinition conjureDefinition) { typeNameResolver, parameterMapper, new ReturnTypeMapper(returnTypes), - StaticFactoryMethodType.ASYNC, - declaredEndpointErrors); + StaticFactoryMethodType.ASYNC); StaticFactoryMethodGenerator blockingGenerator = new DefaultStaticFactoryMethodGenerator( options, typeNameResolver, parameterMapper, new ReturnTypeMapper(returnTypes), - StaticFactoryMethodType.BLOCKING, - declaredEndpointErrors); + StaticFactoryMethodType.BLOCKING); return conjureDefinition.getServices().stream() .flatMap(serviceDef -> generateFilesForService( diff --git a/conjure-java-core/src/test/java/com/palantir/conjure/java/DialogueServiceGeneratorTests.java b/conjure-java-core/src/test/java/com/palantir/conjure/java/DialogueServiceGeneratorTests.java index 9bf95a66b..86a51a84d 100644 --- a/conjure-java-core/src/test/java/com/palantir/conjure/java/DialogueServiceGeneratorTests.java +++ b/conjure-java-core/src/test/java/com/palantir/conjure/java/DialogueServiceGeneratorTests.java @@ -66,6 +66,7 @@ public void testConjureImports() throws IOException { } @Test +<<<<<<< HEAD public void testServiceGeneration_excludeDialogueAsyncInterfaces() throws IOException { Path testCaseDirectory = Paths.get(REFERENCE_FILES_FOLDER, "excludeasyncinterfaces"); try (Stream filePaths = Files.walk(testCaseDirectory)) { @@ -76,6 +77,54 @@ public void testServiceGeneration_excludeDialogueAsyncInterfaces() throws IOExce .toList(); assertThat(fileNames) .noneMatch(name -> name.toLowerCase(Locale.ROOT).contains("async")); +======= + public void generateEteServices() throws IOException { + ConjureDefinition def = Conjure.parse(ImmutableList.of( + new File("src/test/resources/cookie-service.yml"), + new File("src/test/resources/ete-service.yml"), + new File("src/test/resources/ete-binary.yml"))); + List files = new GenerationCoordinator( + MoreExecutors.directExecutor(), + ImmutableSet.of(new DialogueServiceGenerator( + Options.builder().apiVersion("1.2.3").build()))) + .emit(def, folder); + validateGeneratorOutput(files, Paths.get("src/integrationInput/java/com/palantir/product")); + } + + @Test + public void generateServiceWithResultTypes() throws IOException { + ConjureDefinition def = + Conjure.parse(ImmutableList.of(new File("src/test/resources/example-endpoint-errors.yml"))); + List files = new GenerationCoordinator( + MoreExecutors.directExecutor(), + ImmutableSet.of( + new ErrorGenerator(Options.builder() + .useImmutableBytes(true) + .excludeEmptyOptionals(true) + .jetbrainsContractAnnotations(true) + .generateDialogueEndpointErrorResultTypes(true) + .build()), + new DialogueServiceGenerator(Options.builder() + .apiVersion("1.2.3") + .excludeDialogueAsyncInterfaces(false) + .generateDialogueEndpointErrorResultTypes(true) + .build()))) + .emit(def, folder); + // validateGeneratorOutput(files, Paths.get("src/integrationInput/java/com/palantir/product")); + validateGeneratedOutput(files, Paths.get("src/integrationInput/java")); + } + + private void validateGeneratedOutput(List files, Path outputDir) throws IOException { + for (Path file : files) { + Path relativePath = folder.toPath().relativize(file); + Path output = outputDir.resolve(relativePath); + if (Boolean.valueOf(System.getProperty("recreate", "false"))) { + Files.createDirectories(relativePath.getParent()); + Files.deleteIfExists(output); + Files.copy(file, output); + } + assertThat(readFromFile(file)).isEqualTo(readFromFile(output)); +>>>>>>> c2faacd0 (Add async support) } } } From 90212adde77109828a87e87e32babbd61e20a71a Mon Sep 17 00:00:00 2001 From: Pritham Marupaka Date: Thu, 23 Jan 2025 19:13:54 -0500 Subject: [PATCH 05/24] Use deserializers for binary and optional binary enpoints --- .../product/DialogueErrorEndpoints.java | 60 +++ .../product/ErrorServiceBlocking.java | 89 ++++ .../palantir/product/ErrorServiceAsync.java | 388 ++++++++++++++++++ .../DefaultStaticFactoryMethodGenerator.java | 32 +- .../dialogue/DialogueInterfaceGenerator.java | 38 +- .../resources/example-endpoint-errors.yml | 10 + 6 files changed, 591 insertions(+), 26 deletions(-) create mode 100644 conjure-java-core/src/integrationInput/java/com/palantir/product/ErrorServiceAsync.java diff --git a/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/DialogueErrorEndpoints.java b/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/DialogueErrorEndpoints.java index 962366562..0221e4859 100644 --- a/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/DialogueErrorEndpoints.java +++ b/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/DialogueErrorEndpoints.java @@ -125,6 +125,66 @@ public String endpointName() { return "testEmptyBody"; } + @Override + public String version() { + return "1.2.3"; + } + }, + + testBinary { + private final PathTemplate pathTemplate = + PathTemplate.builder().fixed("base").fixed("binary").build(); + + @Override + public void renderPath(ListMultimap params, UrlBuilder url) { + pathTemplate.fill(params, url); + } + + @Override + public HttpMethod httpMethod() { + return HttpMethod.GET; + } + + @Override + public String serviceName() { + return "ErrorService"; + } + + @Override + public String endpointName() { + return "testBinary"; + } + + @Override + public String version() { + return "1.2.3"; + } + }, + + testOptionalBinary { + private final PathTemplate pathTemplate = + PathTemplate.builder().fixed("base").fixed("optional-binary").build(); + + @Override + public void renderPath(ListMultimap params, UrlBuilder url) { + pathTemplate.fill(params, url); + } + + @Override + public HttpMethod httpMethod() { + return HttpMethod.GET; + } + + @Override + public String serviceName() { + return "ErrorService"; + } + + @Override + public String endpointName() { + return "testOptionalBinary"; + } + @Override public String version() { return "1.2.3"; diff --git a/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/ErrorServiceBlocking.java b/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/ErrorServiceBlocking.java index a3a95b581..583bc24b5 100644 --- a/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/ErrorServiceBlocking.java +++ b/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/ErrorServiceBlocking.java @@ -2,6 +2,7 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.errorprone.annotations.MustBeClosed; import com.palantir.conjure.java.lib.internal.ClientEndpoint; import com.palantir.conjure.java.lib.internal.ConjureErrors; import com.palantir.dialogue.Channel; @@ -18,8 +19,10 @@ import com.palantir.dialogue.TypeMarker; import com.palantir.logsafe.Preconditions; import com.palantir.tokens.auth.AuthHeader; +import java.io.InputStream; 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") @@ -41,6 +44,14 @@ public interface ErrorServiceBlocking { @ClientEndpoint(method = "GET", path = "/base/empty") TestEmptyBodyResponse testEmptyBody(AuthHeader authHeader); + /** @apiNote {@code GET /base/binary} */ + @ClientEndpoint(method = "GET", path = "/base/binary") + TestBinaryResponse testBinary(AuthHeader authHeader); + + /** @apiNote {@code GET /base/optional-binary} */ + @ClientEndpoint(method = "GET", path = "/base/optional-binary") + TestOptionalBinaryResponse testOptionalBinary(AuthHeader authHeader); + /** Creates a synchronous/blocking client for a ErrorService service. */ static ErrorServiceBlocking of(EndpointChannelFactory _endpointChannelFactory, ConjureRuntime _runtime) { return new ErrorServiceBlocking() { @@ -105,6 +116,30 @@ static ErrorServiceBlocking of(EndpointChannelFactory _endpointChannelFactory, C new TypeMarker() {}) .build()); + 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 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) { Request.Builder _request = Request.builder(); @@ -140,6 +175,21 @@ public TestEmptyBodyResponse testEmptyBody(AuthHeader authHeader) { .callBlocking(testEmptyBodyChannel, _request.build(), testEmptyBodyDeserializer); } + @Override + public TestBinaryResponse testBinary(AuthHeader authHeader) { + Request.Builder _request = Request.builder(); + _request.putHeaderParams("Authorization", authHeader.toString()); + return _runtime.clients().callBlocking(testBinaryChannel, _request.build(), testBinaryDeserializer); + } + + @Override + public TestOptionalBinaryResponse testOptionalBinary(AuthHeader authHeader) { + Request.Builder _request = Request.builder(); + _request.putHeaderParams("Authorization", authHeader.toString()); + return _runtime.clients() + .callBlocking(testOptionalBinaryChannel, _request.build(), testOptionalBinaryDeserializer); + } + @Override public String toString() { return "ErrorServiceBlocking{_endpointChannelFactory=" + _endpointChannelFactory + ", runtime=" @@ -296,4 +346,43 @@ final class InvalidArgument extends ConjureErrors.BaseEndpointError + implements TestBinaryResponse { + @JsonCreator + InvalidArgument( + @JsonProperty("errorCode") String errorCode, + @JsonProperty("errorInstanceId") String errorInstanceId, + @JsonProperty("parameters") TestErrors.InvalidArgumentParameters parameters) { + super(errorCode, TestErrors.INVALID_ARGUMENT.name(), errorInstanceId, parameters); + } + } + } + + sealed interface TestOptionalBinaryResponse + permits TestOptionalBinaryResponse.Success, TestOptionalBinaryResponse.InvalidArgument { + record Success(Optional value) implements TestOptionalBinaryResponse { + public Success { + Preconditions.checkArgumentNotNull(value, "value cannot be null"); + } + } + + final class InvalidArgument extends ConjureErrors.BaseEndpointError + implements TestOptionalBinaryResponse { + @JsonCreator + InvalidArgument( + @JsonProperty("errorCode") String errorCode, + @JsonProperty("errorInstanceId") String errorInstanceId, + @JsonProperty("parameters") TestErrors.InvalidArgumentParameters parameters) { + super(errorCode, TestErrors.INVALID_ARGUMENT.name(), errorInstanceId, parameters); + } + } + } } diff --git a/conjure-java-core/src/integrationInput/java/com/palantir/product/ErrorServiceAsync.java b/conjure-java-core/src/integrationInput/java/com/palantir/product/ErrorServiceAsync.java new file mode 100644 index 000000000..87a2de728 --- /dev/null +++ b/conjure-java-core/src/integrationInput/java/com/palantir/product/ErrorServiceAsync.java @@ -0,0 +1,388 @@ +package com.palantir.product; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.errorprone.annotations.MustBeClosed; +import com.palantir.conjure.java.lib.internal.ClientEndpoint; +import com.palantir.conjure.java.lib.internal.ConjureErrors; +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.PlainSerDe; +import com.palantir.dialogue.Request; +import com.palantir.dialogue.TypeMarker; +import com.palantir.logsafe.Preconditions; +import com.palantir.tokens.auth.AuthHeader; +import java.io.InputStream; +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 GET /base/basic} */ + @ClientEndpoint(method = "GET", path = "/base/basic") + ListenableFuture testBasicError(AuthHeader authHeader); + + /** @apiNote {@code GET /base/imported} */ + @ClientEndpoint(method = "GET", path = "/base/imported") + ListenableFuture testImportedError(AuthHeader authHeader); + + /** @apiNote {@code GET /base/multiple} */ + @ClientEndpoint(method = "GET", path = "/base/multiple") + ListenableFuture testMultipleErrorsAndPackages(AuthHeader authHeader); + + /** @apiNote {@code GET /base/empty} */ + @ClientEndpoint(method = "GET", path = "/base/empty") + ListenableFuture testEmptyBody(AuthHeader authHeader); + + /** @apiNote {@code GET /base/binary} */ + @ClientEndpoint(method = "GET", path = "/base/binary") + ListenableFuture testBinary(AuthHeader authHeader); + + /** @apiNote {@code GET /base/optional-binary} */ + @ClientEndpoint(method = "GET", path = "/base/optional-binary") + ListenableFuture testOptionalBinary(AuthHeader authHeader); + + /** 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 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 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 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( + com.palantir.another.EndpointSpecificErrors.DIFFERENT_PACKAGE.name(), + new TypeMarker() {}) + .build()); + + 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 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 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) { + Request.Builder _request = Request.builder(); + _request.putHeaderParams("Authorization", authHeader.toString()); + return _runtime.clients().call(testBasicErrorChannel, _request.build(), testBasicErrorDeserializer); + } + + @Override + public ListenableFuture testImportedError(AuthHeader authHeader) { + Request.Builder _request = Request.builder(); + _request.putHeaderParams("Authorization", authHeader.toString()); + return _runtime.clients() + .call(testImportedErrorChannel, _request.build(), testImportedErrorDeserializer); + } + + @Override + public ListenableFuture testMultipleErrorsAndPackages( + AuthHeader authHeader) { + Request.Builder _request = Request.builder(); + _request.putHeaderParams("Authorization", authHeader.toString()); + return _runtime.clients() + .call( + testMultipleErrorsAndPackagesChannel, + _request.build(), + testMultipleErrorsAndPackagesDeserializer); + } + + @Override + public ListenableFuture testEmptyBody(AuthHeader authHeader) { + Request.Builder _request = Request.builder(); + _request.putHeaderParams("Authorization", authHeader.toString()); + return _runtime.clients().call(testEmptyBodyChannel, _request.build(), testEmptyBodyDeserializer); + } + + @Override + public ListenableFuture testBinary(AuthHeader authHeader) { + Request.Builder _request = Request.builder(); + _request.putHeaderParams("Authorization", authHeader.toString()); + return _runtime.clients().call(testBinaryChannel, _request.build(), testBinaryDeserializer); + } + + @Override + public ListenableFuture testOptionalBinary(AuthHeader authHeader) { + Request.Builder _request = Request.builder(); + _request.putHeaderParams("Authorization", authHeader.toString()); + 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(String value) implements TestBasicErrorResponse { + public Success { + Preconditions.checkArgumentNotNull(value, "value cannot be null"); + } + } + + final class InvalidArgument extends ConjureErrors.BaseEndpointError + implements TestBasicErrorResponse { + @JsonCreator + InvalidArgument( + @JsonProperty("errorCode") String errorCode, + @JsonProperty("errorInstanceId") 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(String value) implements TestImportedErrorResponse { + public Success { + Preconditions.checkArgumentNotNull(value, "value cannot be null"); + } + } + + final class EndpointError + extends ConjureErrors.BaseEndpointError + implements TestImportedErrorResponse { + @JsonCreator + EndpointError( + @JsonProperty("errorCode") String errorCode, + @JsonProperty("errorInstanceId") 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 { + record Success(String value) implements TestMultipleErrorsAndPackagesResponse { + public Success { + Preconditions.checkArgumentNotNull(value, "value cannot be null"); + } + } + + final class InvalidArgument extends ConjureErrors.BaseEndpointError + implements TestMultipleErrorsAndPackagesResponse { + @JsonCreator + InvalidArgument( + @JsonProperty("errorCode") String errorCode, + @JsonProperty("errorInstanceId") String errorInstanceId, + @JsonProperty("parameters") TestErrors.InvalidArgumentParameters parameters) { + super(errorCode, TestErrors.INVALID_ARGUMENT.name(), errorInstanceId, parameters); + } + } + + final class NotFound extends ConjureErrors.BaseEndpointError + implements TestMultipleErrorsAndPackagesResponse { + @JsonCreator + NotFound( + @JsonProperty("errorCode") String errorCode, + @JsonProperty("errorInstanceId") String errorInstanceId, + @JsonProperty("parameters") TestErrors.NotFoundParameters parameters) { + super(errorCode, TestErrors.NOT_FOUND.name(), errorInstanceId, parameters); + } + } + + final class DifferentNamespace + extends ConjureErrors.BaseEndpointError + implements TestMultipleErrorsAndPackagesResponse { + @JsonCreator + DifferentNamespace( + @JsonProperty("errorCode") String errorCode, + @JsonProperty("errorInstanceId") String errorInstanceId, + @JsonProperty("parameters") EndpointSpecificTwoErrors.DifferentNamespaceParameters parameters) { + super(errorCode, EndpointSpecificTwoErrors.DIFFERENT_NAMESPACE.name(), errorInstanceId, parameters); + } + } + + final class DifferentPackage + extends ConjureErrors.BaseEndpointError< + com.palantir.another.EndpointSpecificErrors.DifferentPackageParameters> + implements TestMultipleErrorsAndPackagesResponse { + @JsonCreator + DifferentPackage( + @JsonProperty("errorCode") String errorCode, + @JsonProperty("errorInstanceId") String errorInstanceId, + @JsonProperty("parameters") + com.palantir.another.EndpointSpecificErrors.DifferentPackageParameters parameters) { + super( + errorCode, + com.palantir.another.EndpointSpecificErrors.DIFFERENT_PACKAGE.name(), + errorInstanceId, + parameters); + } + } + } + + sealed interface TestEmptyBodyResponse + permits TestEmptyBodyResponse.Success, TestEmptyBodyResponse.InvalidArgument { + record Success() implements TestEmptyBodyResponse { + @JsonCreator + public static Success create() { + return new Success(); + } + } + + final class InvalidArgument extends ConjureErrors.BaseEndpointError + implements TestEmptyBodyResponse { + @JsonCreator + InvalidArgument( + @JsonProperty("errorCode") String errorCode, + @JsonProperty("errorInstanceId") String errorInstanceId, + @JsonProperty("parameters") TestErrors.InvalidArgumentParameters parameters) { + super(errorCode, TestErrors.INVALID_ARGUMENT.name(), errorInstanceId, parameters); + } + } + } + + sealed interface TestBinaryResponse permits TestBinaryResponse.Success, TestBinaryResponse.InvalidArgument { + record Success(@MustBeClosed InputStream value) implements TestBinaryResponse { + public Success { + Preconditions.checkArgumentNotNull(value, "value cannot be null"); + } + } + + final class InvalidArgument extends ConjureErrors.BaseEndpointError + implements TestBinaryResponse { + @JsonCreator + InvalidArgument( + @JsonProperty("errorCode") String errorCode, + @JsonProperty("errorInstanceId") String errorInstanceId, + @JsonProperty("parameters") TestErrors.InvalidArgumentParameters parameters) { + super(errorCode, TestErrors.INVALID_ARGUMENT.name(), errorInstanceId, parameters); + } + } + } + + sealed interface TestOptionalBinaryResponse + permits TestOptionalBinaryResponse.Success, TestOptionalBinaryResponse.InvalidArgument { + record Success(Optional value) implements TestOptionalBinaryResponse { + public Success { + Preconditions.checkArgumentNotNull(value, "value cannot be null"); + } + } + + final class InvalidArgument extends ConjureErrors.BaseEndpointError + implements TestOptionalBinaryResponse { + @JsonCreator + InvalidArgument( + @JsonProperty("errorCode") String errorCode, + @JsonProperty("errorInstanceId") String errorInstanceId, + @JsonProperty("parameters") TestErrors.InvalidArgumentParameters parameters) { + super(errorCode, TestErrors.INVALID_ARGUMENT.name(), errorInstanceId, parameters); + } + } + } +} 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 0b3ae7ec0..7b8c4bea5 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 @@ -178,7 +178,7 @@ private Optional serializer(EndpointName endpointName, Type type) { private Optional deserializer( TypeName responseType, EndpointDefinition endpointDef, EndpointName endpointName, Optional type) { TypeName className = Primitives.box(returnTypes.baseType(type)); - if (isBinaryOrOptionalBinary(className, returnTypes)) { + if (isBinaryOrOptionalBinary(className, returnTypes) && !options.generateDialogueEndpointErrorResultTypes()) { return Optional.empty(); } ParameterizedTypeName deserializerType = @@ -188,7 +188,7 @@ private Optional deserializer( "$L.bodySerDe().$L", StaticFactoryMethodGenerator.RUNTIME, options.generateDialogueEndpointErrorResultTypes() - ? constructDeserializerWithEndpointErrors(endpointDef, responseType) + ? constructDeserializerWithEndpointErrors(endpointDef, className, responseType) : constructDeserializer(type, className)); return Optional.of(FieldSpec.builder(deserializerType, endpointName + "Deserializer") @@ -203,8 +203,9 @@ private CodeBlock constructDeserializer(Optional type, TypeName className) : CodeBlock.of("emptyBodyDeserializer()"); } - private CodeBlock constructDeserializerWithEndpointErrors(EndpointDefinition endpointDef, TypeName responseType) { - CodeBlock.Builder retBuilder = CodeBlock.builder() + private CodeBlock constructDeserializerWithEndpointErrors( + EndpointDefinition endpointDef, TypeName className, TypeName responseType) { + CodeBlock.Builder deserializerArgsBuilder = CodeBlock.builder() .add("$T.<$T>builder()", DeserializerArgs.class, responseType) .add(".baseType(new $T<>() {})", TypeMarker.class) // TODO(pm): consider making "Success" a constant string for re-use in the record creation. @@ -217,15 +218,21 @@ private CodeBlock constructDeserializerWithEndpointErrors(EndpointDefinition end errorTypeName.getPackage(), ErrorGenerationUtils.errorTypesClassName(errorTypeName.getNamespace()), errorType); - retBuilder.add( + deserializerArgsBuilder.add( ".error($T.name(), new $T<$T.$L>() {})", errorClass, TypeMarker.class, responseType, errorName); } - retBuilder.add(".build()"); - return CodeBlock.builder() - .add("deserializer(") - .add(retBuilder.build()) - .add(")") - .build(); + deserializerArgsBuilder.add(".build()"); + CodeBlock.Builder deserializerBuilder = CodeBlock.builder(); + if (isBinary(className, returnTypes)) { + deserializerBuilder.add("inputStreamDeserializer("); + } else if (isOptionalBinary(className, returnTypes)) { + deserializerBuilder.add("optionalInputStreamDeserializer("); + } else { + deserializerBuilder.add("deserializer("); + } + deserializerBuilder.add(deserializerArgsBuilder.build()); + deserializerBuilder.add(")"); + return deserializerBuilder.build(); } private static boolean isBinaryOrOptionalBinary(TypeName className, ReturnTypeMapper returnTypes) { @@ -279,7 +286,8 @@ private MethodSpec clientImpl(ClassName className, EndpointDefinition def) { Names.endpointChannel(def), REQUEST, def.getReturns() - .filter(type -> isBinaryOrOptionalBinary(returnTypes.baseType(type), returnTypes)) + .filter(type -> !options.generateDialogueEndpointErrorResultTypes() + && isBinaryOrOptionalBinary(returnTypes.baseType(type), returnTypes)) .map(type -> StaticFactoryMethodGenerator.RUNTIME + (isOptionalBinary(returnTypes.baseType(type), returnTypes) ? ".bodySerDe().optionalInputStreamDeserializer()" 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 b84dae138..1a765aaf7 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 @@ -128,7 +128,8 @@ private JavaFile generate( def.getDocs().ifPresent(docs -> serviceBuilder.addJavadoc("$L", StringUtils.appendIfMissing(docs.get(), "\n"))); serviceBuilder.addMethods(def.getEndpoints().stream() - .map(endpoint -> apiMethod(packageName, className, endpoint, serviceCallType)) + .map(endpoint -> apiMethod( + packageName, className, endpoint, generateDialogueEndpointErrorResultTypes, serviceCallType)) .collect(toList())); if (generateDialogueEndpointErrorResultTypes) { @@ -237,9 +238,13 @@ private TypeSpec createSuccessRecord( MethodSpec.compactConstructorBuilder().addModifiers(Modifier.PUBLIC); TypeName returnType = returnTypes.baseType(endpointDef.getReturns()); if (!returnType.equals(TypeName.VOID)) { - successCtorBuilder - .addParameter(ParameterSpec.builder(returnType, "value").build()) - .addStatement("$T.checkArgumentNotNull(value, \"value cannot be null\")", Preconditions.class); + 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); + } + successCtorBuilder.addParameter(parameterBuilder.build()); } successRecordBuilder @@ -287,6 +292,7 @@ private MethodSpec apiMethod( String packageName, ClassName className, EndpointDefinition endpointDef, + boolean generateDialogueEndpointErrorResultTypes, StaticFactoryMethodType serviceCallType) { MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder( endpointDef.getEndpointName().get()) @@ -320,16 +326,20 @@ private MethodSpec apiMethod( endpointDef, new EndpointJavaDocGenerationOptions(RequestLineJavaDoc.INCLUDE, EndpointErrorsJavaDoc.EXCLUDE)); - // TypeName returnType = returnTypeMapper.apply(endpointDef.getReturns()); - TypeName returnType = ClassName.get( - packageName, - className.simpleName(), - ErrorGenerationUtils.responseTypeName(endpointDef.getEndpointName())); - methodBuilder.returns(serviceCallType.switchBy( - returnType, ParameterizedTypeName.get(ClassName.get(ListenableFuture.class), returnType))); - - if (TypeName.get(InputStream.class).equals(returnType)) { - methodBuilder.addAnnotation(MustBeClosed.class); + if (generateDialogueEndpointErrorResultTypes) { + TypeName returnType = ClassName.get( + packageName, + className.simpleName(), + ErrorGenerationUtils.responseTypeName(endpointDef.getEndpointName())); + methodBuilder.returns(serviceCallType.switchBy( + returnType, ParameterizedTypeName.get(ClassName.get(ListenableFuture.class), returnType))); + } else { + TypeName returnType = serviceCallType.switchBy( + returnTypes.baseType(endpointDef.getReturns()), returnTypes.async(endpointDef.getReturns())); + methodBuilder.returns(returnType); + if (TypeName.get(InputStream.class).equals(returnType)) { + methodBuilder.addAnnotation(MustBeClosed.class); + } } return methodBuilder.build(); 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 2c40a1a72..2c7a95e8f 100644 --- a/conjure-java-core/src/test/resources/example-endpoint-errors.yml +++ b/conjure-java-core/src/test/resources/example-endpoint-errors.yml @@ -46,3 +46,13 @@ services: http: GET /empty errors: - InvalidArgument + testBinary: + http: GET /binary + returns: binary + errors: + - InvalidArgument + testOptionalBinary: + http: GET /optional-binary + returns: optional + errors: + - InvalidArgument From 4eb22fb7fbd862f3427ec1c8da4cb485650c6b3b Mon Sep 17 00:00:00 2001 From: Pritham Marupaka Date: Thu, 23 Jan 2025 20:18:19 -0500 Subject: [PATCH 06/24] Don't create parameter records when there are no parameters for error --- .../product/ErrorServiceBlocking.java | 22 ++--- .../palantir/product/ErrorServiceAsync.java | 22 ++--- .../another/EndpointSpecificErrors.java | 2 - .../product/EndpointSpecificServerErrors.java | 3 +- .../product/EndpointSpecificTwoErrors.java | 2 - .../com/palantir/conjure/java/Options.java | 5 +- .../DefaultStaticFactoryMethodGenerator.java | 45 +++++---- .../dialogue/DialogueInterfaceGenerator.java | 98 ++++++++++++------- .../dialogue/DialogueServiceGenerator.java | 7 +- .../conjure/java/types/ErrorGenerator.java | 12 ++- .../java/util/ErrorGenerationUtils.java | 27 ++++- .../java/DialogueServiceGeneratorTests.java | 32 +----- .../com/palantir/conjure/java/TestBase.java | 15 +++ .../conjure/java/UndertowServiceEteTest.java | 20 +--- .../java/lib/internal/ConjureErrors.java | 29 +----- 15 files changed, 173 insertions(+), 168 deletions(-) diff --git a/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/ErrorServiceBlocking.java b/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/ErrorServiceBlocking.java index 583bc24b5..e68e08087 100644 --- a/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/ErrorServiceBlocking.java +++ b/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/ErrorServiceBlocking.java @@ -295,33 +295,31 @@ final class NotFound extends ConjureErrors.BaseEndpointError + final class DifferentNamespace extends ConjureErrors.BaseEndpointError implements TestMultipleErrorsAndPackagesResponse { @JsonCreator DifferentNamespace( @JsonProperty("errorCode") String errorCode, - @JsonProperty("errorInstanceId") String errorInstanceId, - @JsonProperty("parameters") EndpointSpecificTwoErrors.DifferentNamespaceParameters parameters) { - super(errorCode, EndpointSpecificTwoErrors.DIFFERENT_NAMESPACE.name(), errorInstanceId, parameters); + @JsonProperty("errorInstanceId") String errorInstanceId) { + super( + errorCode, + EndpointSpecificTwoErrors.DIFFERENT_NAMESPACE.name(), + errorInstanceId, + ConjureErrors.EMPTY_ERROR_PARAMETERS_INSTANCE); } } - final class DifferentPackage - extends ConjureErrors.BaseEndpointError< - com.palantir.another.EndpointSpecificErrors.DifferentPackageParameters> + final class DifferentPackage extends ConjureErrors.BaseEndpointError implements TestMultipleErrorsAndPackagesResponse { @JsonCreator DifferentPackage( @JsonProperty("errorCode") String errorCode, - @JsonProperty("errorInstanceId") String errorInstanceId, - @JsonProperty("parameters") - com.palantir.another.EndpointSpecificErrors.DifferentPackageParameters parameters) { + @JsonProperty("errorInstanceId") String errorInstanceId) { super( errorCode, com.palantir.another.EndpointSpecificErrors.DIFFERENT_PACKAGE.name(), errorInstanceId, - parameters); + ConjureErrors.EMPTY_ERROR_PARAMETERS_INSTANCE); } } } diff --git a/conjure-java-core/src/integrationInput/java/com/palantir/product/ErrorServiceAsync.java b/conjure-java-core/src/integrationInput/java/com/palantir/product/ErrorServiceAsync.java index 87a2de728..9b54e923b 100644 --- a/conjure-java-core/src/integrationInput/java/com/palantir/product/ErrorServiceAsync.java +++ b/conjure-java-core/src/integrationInput/java/com/palantir/product/ErrorServiceAsync.java @@ -295,33 +295,31 @@ final class NotFound extends ConjureErrors.BaseEndpointError + final class DifferentNamespace extends ConjureErrors.BaseEndpointError implements TestMultipleErrorsAndPackagesResponse { @JsonCreator DifferentNamespace( @JsonProperty("errorCode") String errorCode, - @JsonProperty("errorInstanceId") String errorInstanceId, - @JsonProperty("parameters") EndpointSpecificTwoErrors.DifferentNamespaceParameters parameters) { - super(errorCode, EndpointSpecificTwoErrors.DIFFERENT_NAMESPACE.name(), errorInstanceId, parameters); + @JsonProperty("errorInstanceId") String errorInstanceId) { + super( + errorCode, + EndpointSpecificTwoErrors.DIFFERENT_NAMESPACE.name(), + errorInstanceId, + ConjureErrors.EMPTY_ERROR_PARAMETERS_INSTANCE); } } - final class DifferentPackage - extends ConjureErrors.BaseEndpointError< - com.palantir.another.EndpointSpecificErrors.DifferentPackageParameters> + final class DifferentPackage extends ConjureErrors.BaseEndpointError implements TestMultipleErrorsAndPackagesResponse { @JsonCreator DifferentPackage( @JsonProperty("errorCode") String errorCode, - @JsonProperty("errorInstanceId") String errorInstanceId, - @JsonProperty("parameters") - com.palantir.another.EndpointSpecificErrors.DifferentPackageParameters parameters) { + @JsonProperty("errorInstanceId") String errorInstanceId) { super( errorCode, com.palantir.another.EndpointSpecificErrors.DIFFERENT_PACKAGE.name(), errorInstanceId, - parameters); + ConjureErrors.EMPTY_ERROR_PARAMETERS_INSTANCE); } } } diff --git a/conjure-java-core/src/integrationInput/java/undertow/com/palantir/another/EndpointSpecificErrors.java b/conjure-java-core/src/integrationInput/java/undertow/com/palantir/another/EndpointSpecificErrors.java index 38a5fe579..5ed9d792f 100644 --- a/conjure-java-core/src/integrationInput/java/undertow/com/palantir/another/EndpointSpecificErrors.java +++ b/conjure-java-core/src/integrationInput/java/undertow/com/palantir/another/EndpointSpecificErrors.java @@ -18,6 +18,4 @@ 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/undertow/com/palantir/product/EndpointSpecificServerErrors.java b/conjure-java-core/src/integrationInput/java/undertow/com/palantir/product/EndpointSpecificServerErrors.java index 26ddea7fd..136b1bcff 100644 --- a/conjure-java-core/src/integrationInput/java/undertow/com/palantir/product/EndpointSpecificServerErrors.java +++ b/conjure-java-core/src/integrationInput/java/undertow/com/palantir/product/EndpointSpecificServerErrors.java @@ -1,6 +1,5 @@ package undertow.com.palantir.product; -import com.palantir.another.EndpointSpecificErrors; import com.palantir.conjure.java.api.errors.CheckedServiceException; import com.palantir.logsafe.Safe; import com.palantir.logsafe.SafeArg; @@ -41,7 +40,7 @@ public static void throwIfEndpointError(boolean shouldThrow, @Safe String typeNa public static final class EndpointError extends CheckedServiceException { private EndpointError(@Safe String typeName, @Unsafe Object typeDef, @Nullable Throwable cause) { super( - EndpointSpecificErrors.DIFFERENT_PACKAGE, + EndpointSpecificErrors.ENDPOINT_ERROR, cause, SafeArg.of("typeName", typeName), UnsafeArg.of("typeDef", typeDef)); diff --git a/conjure-java-core/src/integrationInput/java/undertow/com/palantir/product/EndpointSpecificTwoErrors.java b/conjure-java-core/src/integrationInput/java/undertow/com/palantir/product/EndpointSpecificTwoErrors.java index dc2240282..ff930dc05 100644 --- a/conjure-java-core/src/integrationInput/java/undertow/com/palantir/product/EndpointSpecificTwoErrors.java +++ b/conjure-java-core/src/integrationInput/java/undertow/com/palantir/product/EndpointSpecificTwoErrors.java @@ -18,6 +18,4 @@ 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/main/java/com/palantir/conjure/java/Options.java b/conjure-java-core/src/main/java/com/palantir/conjure/java/Options.java index 160e81ffe..97d30726a 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 @@ -188,8 +188,9 @@ default boolean preferObjectBuilders() { } /** - * TODO(pm): flesh this out more. For errors associated with endpoints, this option enables the generated Dialogue - * clients to return a result type. + * 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.conjure.java.lib.internal.ConjureErrors.BaseEndpointError}. */ @Value.Default default boolean generateDialogueEndpointErrorResultTypes() { 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 7b8c4bea5..11dc4a5fb 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 @@ -120,7 +120,7 @@ public MethodSpec generate(ServiceDefinition def) { Packages.getPrefixedPackage( def.getServiceName().getPackage(), options.packagePrefix()), className.simpleName(), - ErrorGenerationUtils.responseTypeName(endpoint.getEndpointName())), + ErrorGenerationUtils.endpointResponseResultTypeName(endpoint.getEndpointName())), endpoint, endpoint.getEndpointName(), endpoint.getReturns()) @@ -178,16 +178,19 @@ private Optional serializer(EndpointName endpointName, Type type) { private Optional deserializer( TypeName responseType, EndpointDefinition endpointDef, EndpointName endpointName, Optional type) { TypeName className = Primitives.box(returnTypes.baseType(type)); - if (isBinaryOrOptionalBinary(className, returnTypes) && !options.generateDialogueEndpointErrorResultTypes()) { + boolean generateResultTypes = shouldGenerateDialogueEndpointErrorResultTypesForEndpoint(options, endpointDef); + + if (isBinaryOrOptionalBinary(className, returnTypes) && !generateResultTypes) { return Optional.empty(); } - ParameterizedTypeName deserializerType = - ParameterizedTypeName.get(ClassName.get(Deserializer.class), responseType); + + ParameterizedTypeName deserializerType = ParameterizedTypeName.get( + ClassName.get(Deserializer.class), generateResultTypes ? responseType : className); CodeBlock initializer = CodeBlock.of( "$L.bodySerDe().$L", StaticFactoryMethodGenerator.RUNTIME, - options.generateDialogueEndpointErrorResultTypes() + generateResultTypes ? constructDeserializerWithEndpointErrors(endpointDef, className, responseType) : constructDeserializer(type, className)); @@ -208,16 +211,14 @@ private CodeBlock constructDeserializerWithEndpointErrors( CodeBlock.Builder deserializerArgsBuilder = CodeBlock.builder() .add("$T.<$T>builder()", DeserializerArgs.class, responseType) .add(".baseType(new $T<>() {})", TypeMarker.class) - // TODO(pm): consider making "Success" a constant string for re-use in the record creation. .add(".success(new $T<$T.Success>() {})", TypeMarker.class, responseType); for (EndpointError err : endpointDef.getErrors()) { ErrorTypeName errorTypeName = err.getError(); String errorName = errorTypeName.getName(); - String errorType = CaseFormat.UPPER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, errorName); ClassName errorClass = ClassName.get( errorTypeName.getPackage(), ErrorGenerationUtils.errorTypesClassName(errorTypeName.getNamespace()), - errorType); + CaseFormat.UPPER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, errorName)); deserializerArgsBuilder.add( ".error($T.name(), new $T<$T.$L>() {})", errorClass, TypeMarker.class, responseType, errorName); } @@ -239,6 +240,12 @@ private static boolean isBinaryOrOptionalBinary(TypeName className, ReturnTypeMa return isBinary(className, returnTypes) || isOptionalBinary(className, returnTypes); } + private static boolean shouldGenerateDialogueEndpointErrorResultTypesForEndpoint( + Options options, EndpointDefinition endpointDefinition) { + return options.generateDialogueEndpointErrorResultTypes() + && !endpointDefinition.getErrors().isEmpty(); + } + private static boolean isBinary(TypeName className, ReturnTypeMapper returnTypes) { return className.equals(returnTypes.baseType(Type.primitive(PrimitiveType.BINARY))); } @@ -280,13 +287,14 @@ private MethodSpec clientImpl(ClassName className, EndpointDefinition def) { .build(); String codeBlock = methodType.switchBy( "$L.clients().callBlocking($L, $L.build(), $L);", "$L.clients().call($L, $L.build" + "(), $L);"); + boolean generateResultTypes = shouldGenerateDialogueEndpointErrorResultTypesForEndpoint(options, def); CodeBlock execute = CodeBlock.of( codeBlock, StaticFactoryMethodGenerator.RUNTIME, Names.endpointChannel(def), REQUEST, def.getReturns() - .filter(type -> !options.generateDialogueEndpointErrorResultTypes() + .filter(type -> !generateResultTypes && isBinaryOrOptionalBinary(returnTypes.baseType(type), returnTypes)) .map(type -> StaticFactoryMethodGenerator.RUNTIME + (isOptionalBinary(returnTypes.baseType(type), returnTypes) @@ -295,27 +303,22 @@ && isBinaryOrOptionalBinary(returnTypes.baseType(type), returnTypes)) .orElseGet(() -> def.getEndpointName().get() + "Deserializer")); methodBuilder.addCode(request); - methodBuilder.addCode(methodType.switchBy( - def.getReturns().isPresent() - || (options.generateDialogueEndpointErrorResultTypes() - && !def.getErrors().isEmpty()) - ? "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 (options.generateDialogueEndpointErrorResultTypes() - && !def.getErrors().isEmpty()) { - ClassName returnType = ClassName.get( + if (shouldGenerateDialogueEndpointErrorResultTypesForEndpoint(options, def)) { + ClassName responseResultTypeName = ClassName.get( className.packageName(), className.simpleName(), - ErrorGenerationUtils.responseTypeName(def.getEndpointName())); + ErrorGenerationUtils.endpointResponseResultTypeName(def.getEndpointName())); return methodType.switchBy( - returnType, ParameterizedTypeName.get(ClassName.get(ListenableFuture.class), returnType)); + responseResultTypeName, + ParameterizedTypeName.get(ClassName.get(ListenableFuture.class), responseResultTypeName)); } return methodType.switchBy(returnTypes.baseType(def.getReturns()), returnTypes.async(def.getReturns())); } 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 1a765aaf7..8e8be3e80 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 @@ -25,6 +25,7 @@ import com.google.errorprone.annotations.MustBeClosed; import com.palantir.conjure.java.ConjureAnnotations; import com.palantir.conjure.java.Options; +import com.palantir.conjure.java.lib.internal.ConjureErrors; import com.palantir.conjure.java.lib.internal.ConjureErrors.BaseEndpointError; import com.palantir.conjure.java.services.IsUndertowAsyncMarkerVisitor; import com.palantir.conjure.java.services.ServiceGenerators; @@ -32,9 +33,11 @@ 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.visitor.TypeVisitor; import com.palantir.dialogue.Channel; @@ -66,12 +69,17 @@ 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( @@ -185,34 +193,12 @@ private TypeSpec responseTypeForEndpoint(String packageName, ClassName className ClassName responseTypeName = ClassName.get( packageName, className.simpleName(), - ErrorGenerationUtils.responseTypeName(endpointDef.getEndpointName())); + 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()) { - String errorTypeName = CaseFormat.UPPER_CAMEL.to( - CaseFormat.UPPER_UNDERSCORE, endpointError.getError().getName()); - ClassName errorTypesClassName = ClassName.get( - endpointError.getError().getPackage(), - ErrorGenerationUtils.errorTypesClassName( - endpointError.getError().getNamespace()), - errorTypeName); - ClassName parametersClassName = ClassName.get( - endpointError.getError().getPackage(), - ErrorGenerationUtils.errorTypesClassName( - endpointError.getError().getNamespace()), - ErrorGenerationUtils.errorParametersClassName( - endpointError.getError().getName())); - TypeSpec endpointErrorType = TypeSpec.classBuilder(ClassName.get( - packageName, - responseTypeName.simpleName(), - endpointError.getError().getName())) - .addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL) - .addSuperinterface(responseTypeName) - .superclass(ParameterizedTypeName.get(ClassName.get(BaseEndpointError.class), parametersClassName)) - .addMethod(errorTypeConstructor(parametersClassName, errorTypesClassName)) - .build(); - errorTypes.add(endpointErrorType); + errorTypes.add(constructEndpointErrorType(endpointError, packageName, responseTypeName)); } // Get type from typespec @@ -229,6 +215,39 @@ private TypeSpec responseTypeForEndpoint(String packageName, ClassName className .build(); } + private TypeSpec constructEndpointErrorType( + EndpointError endpointError, String packageName, ClassName responseTypeName) { + String errorTypeName = CaseFormat.UPPER_CAMEL.to( + CaseFormat.UPPER_UNDERSCORE, endpointError.getError().getName()); + ClassName errorTypesClassName = ClassName.get( + endpointError.getError().getPackage(), + 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; + if (errorNameToParameterExistenceMapping.hasParameters(endpointError.getError())) { + parametersClassName = ClassName.get( + endpointError.getError().getPackage(), + ErrorGenerationUtils.errorTypesClassName( + endpointError.getError().getNamespace()), + ErrorGenerationUtils.errorParametersClassName( + endpointError.getError().getName())); + } else { + parametersClassName = ClassName.get(ConjureErrors.EmptyErrorParameters.class); + } + endpointErrorTypeBuilder + .superclass(ParameterizedTypeName.get(ClassName.get(BaseEndpointError.class), parametersClassName)) + .addMethod(errorTypeConstructor(endpointError.getError(), parametersClassName, errorTypesClassName)); + return endpointErrorTypeBuilder.build(); + } + private TypeSpec createSuccessRecord( String packageName, ClassName className, ClassName responseTypeName, EndpointDefinition endpointDef) { ClassName successTypeClassName = @@ -263,7 +282,8 @@ private TypeSpec createSuccessRecord( return successRecordBuilder.build(); } - private MethodSpec errorTypeConstructor(ClassName parametersClassName, ClassName errorTypesClassName) { + private MethodSpec errorTypeConstructor( + ErrorTypeName errorTypeName, ClassName parametersClassName, ClassName errorTypesClassName) { MethodSpec.Builder ctorBuilder = MethodSpec.constructorBuilder() .addAnnotation(JsonCreator.class) .addParameter(ParameterSpec.builder(ClassName.get(String.class), "errorCode") @@ -275,16 +295,20 @@ private MethodSpec errorTypeConstructor(ClassName parametersClassName, ClassName .addAnnotation(AnnotationSpec.builder(JsonProperty.class) .addMember("value", "$S", "errorInstanceId") .build()) - .build()) - // TODO(pm): Only add the parameters if the error has them. Plumb in error definitions. - .addParameter(ParameterSpec.builder(parametersClassName, "parameters") - .addAnnotation(AnnotationSpec.builder(JsonProperty.class) - .addMember("value", "$S", "parameters") - .build()) - .build()) - .addStatement( - // TODO(pm): Perhaps these shouldn't be literals but names ($N). - "super(errorCode, $T.name(), errorInstanceId, parameters)", errorTypesClassName); + .build()); + 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, $T.EMPTY_ERROR_PARAMETERS_INSTANCE)", + errorTypesClassName, + ClassName.get(ConjureErrors.class)); + } return ctorBuilder.build(); } @@ -330,7 +354,7 @@ private MethodSpec apiMethod( TypeName returnType = ClassName.get( packageName, className.simpleName(), - ErrorGenerationUtils.responseTypeName(endpointDef.getEndpointName())); + ErrorGenerationUtils.endpointResponseResultTypeName(endpointDef.getEndpointName())); methodBuilder.returns(serviceCallType.switchBy( returnType, ParameterizedTypeName.get(ClassName.get(ListenableFuture.class), returnType))); } else { diff --git a/conjure-java-core/src/main/java/com/palantir/conjure/java/services/dialogue/DialogueServiceGenerator.java b/conjure-java-core/src/main/java/com/palantir/conjure/java/services/dialogue/DialogueServiceGenerator.java index 2c82701a8..fef4b4196 100644 --- a/conjure-java-core/src/main/java/com/palantir/conjure/java/services/dialogue/DialogueServiceGenerator.java +++ b/conjure-java-core/src/main/java/com/palantir/conjure/java/services/dialogue/DialogueServiceGenerator.java @@ -22,6 +22,7 @@ import com.palantir.conjure.java.types.SafetyEvaluator; import com.palantir.conjure.java.types.SpecializeBinaryClassNameVisitor; import com.palantir.conjure.java.types.TypeMapper; +import com.palantir.conjure.java.util.ErrorGenerationUtils.ErrorNameToParameterExistenceMapping; import com.palantir.conjure.java.util.TypeFunctions; import com.palantir.conjure.spec.ConjureDefinition; import com.palantir.conjure.spec.ServiceDefinition; @@ -64,8 +65,10 @@ public Stream 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)); 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 738e2bdd9..d088a9b6c 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 @@ -182,13 +182,15 @@ private static List generateErrorParameterRecords( .toList(); } + /** + * If the error has parameters, create a record holding error parameters. + */ private static Optional generateErrorParameterRecord( ErrorDefinition errorDefinition, TypeMapper typeMapper) { - // TODO(pm): uncomment this. - // if (errorDefinition.getSafeArgs().isEmpty() - // && errorDefinition.getUnsafeArgs().isEmpty()) { - // return Optional.empty(); - // } + if (errorDefinition.getSafeArgs().isEmpty() + && errorDefinition.getUnsafeArgs().isEmpty()) { + return Optional.empty(); + } TypeSpec.Builder parametersRecordBuilder = TypeSpec.recordBuilder(ErrorGenerationUtils.errorParametersClassName( errorDefinition.getErrorName().getName())) .addModifiers(Modifier.PUBLIC, Modifier.STATIC); 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 4315a9eff..e0e4a121a 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 @@ -39,6 +39,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; @@ -56,8 +57,7 @@ public final class ErrorGenerationUtils { * The name of the sealed interface returned by endpoints with associated errors, permitting implementations for a * successful result and each error type. */ - // TODO(pm): Move these methods around. - public static String responseTypeName(EndpointName endpointName) { + public static String endpointResponseResultTypeName(EndpointName endpointName) { return CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_CAMEL, endpointName.get()) + "Response"; } @@ -77,6 +77,29 @@ public static String errorTypesClassName(ErrorNamespace namespace) { return namespace.get() + "Errors"; } + 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() diff --git a/conjure-java-core/src/test/java/com/palantir/conjure/java/DialogueServiceGeneratorTests.java b/conjure-java-core/src/test/java/com/palantir/conjure/java/DialogueServiceGeneratorTests.java index 86a51a84d..f93f94ec5 100644 --- a/conjure-java-core/src/test/java/com/palantir/conjure/java/DialogueServiceGeneratorTests.java +++ b/conjure-java-core/src/test/java/com/palantir/conjure/java/DialogueServiceGeneratorTests.java @@ -23,6 +23,7 @@ import com.google.common.util.concurrent.MoreExecutors; import com.palantir.conjure.defs.Conjure; import com.palantir.conjure.java.services.dialogue.DialogueServiceGenerator; +import com.palantir.conjure.java.types.ErrorGenerator; import com.palantir.conjure.spec.ConjureDefinition; import java.io.File; import java.io.IOException; @@ -66,7 +67,6 @@ public void testConjureImports() throws IOException { } @Test -<<<<<<< HEAD public void testServiceGeneration_excludeDialogueAsyncInterfaces() throws IOException { Path testCaseDirectory = Paths.get(REFERENCE_FILES_FOLDER, "excludeasyncinterfaces"); try (Stream filePaths = Files.walk(testCaseDirectory)) { @@ -77,18 +77,7 @@ public void testServiceGeneration_excludeDialogueAsyncInterfaces() throws IOExce .toList(); assertThat(fileNames) .noneMatch(name -> name.toLowerCase(Locale.ROOT).contains("async")); -======= - public void generateEteServices() throws IOException { - ConjureDefinition def = Conjure.parse(ImmutableList.of( - new File("src/test/resources/cookie-service.yml"), - new File("src/test/resources/ete-service.yml"), - new File("src/test/resources/ete-binary.yml"))); - List files = new GenerationCoordinator( - MoreExecutors.directExecutor(), - ImmutableSet.of(new DialogueServiceGenerator( - Options.builder().apiVersion("1.2.3").build()))) - .emit(def, folder); - validateGeneratorOutput(files, Paths.get("src/integrationInput/java/com/palantir/product")); + } } @Test @@ -110,21 +99,6 @@ public void generateServiceWithResultTypes() throws IOException { .generateDialogueEndpointErrorResultTypes(true) .build()))) .emit(def, folder); - // validateGeneratorOutput(files, Paths.get("src/integrationInput/java/com/palantir/product")); - validateGeneratedOutput(files, Paths.get("src/integrationInput/java")); - } - - private void validateGeneratedOutput(List files, Path outputDir) throws IOException { - for (Path file : files) { - Path relativePath = folder.toPath().relativize(file); - Path output = outputDir.resolve(relativePath); - if (Boolean.valueOf(System.getProperty("recreate", "false"))) { - Files.createDirectories(relativePath.getParent()); - Files.deleteIfExists(output); - Files.copy(file, output); - } - assertThat(readFromFile(file)).isEqualTo(readFromFile(output)); ->>>>>>> c2faacd0 (Add async support) - } + TestBase.validateGeneratedOutput(folder.toPath(), files, Paths.get("src/integrationInput/java")); } } diff --git a/conjure-java-core/src/test/java/com/palantir/conjure/java/TestBase.java b/conjure-java-core/src/test/java/com/palantir/conjure/java/TestBase.java index d14a49a02..0a2c2bb43 100644 --- a/conjure-java-core/src/test/java/com/palantir/conjure/java/TestBase.java +++ b/conjure-java-core/src/test/java/com/palantir/conjure/java/TestBase.java @@ -59,4 +59,19 @@ protected static void validateGeneratorOutput(List files, Path outputDir, assertThat(readFromFile(file)).isEqualTo(readFromFile(output)); } } + + protected static void validateGeneratedOutput(Path folder, List files, Path outputDir) throws IOException { + for (Path file : files) { + Path relativePath = folder.relativize(file); + Path output = outputDir.resolve(relativePath); + if (Boolean.valueOf(System.getProperty("recreate", "false"))) { + Files.createDirectories(relativePath.getParent()); + Files.deleteIfExists(output); + Files.copy(file, output); + } + assertThat(readFromFile(file)).isEqualTo(readFromFile(output)); + } + } + + } diff --git a/conjure-java-core/src/test/java/com/palantir/conjure/java/UndertowServiceEteTest.java b/conjure-java-core/src/test/java/com/palantir/conjure/java/UndertowServiceEteTest.java index b58f58076..0f396c780 100644 --- a/conjure-java-core/src/test/java/com/palantir/conjure/java/UndertowServiceEteTest.java +++ b/conjure-java-core/src/test/java/com/palantir/conjure/java/UndertowServiceEteTest.java @@ -103,7 +103,8 @@ public UndertowServiceEteTest() { this.client = DialogueClients.create(EteServiceBlocking.class, clientConfiguration(port)); this.asyncClient = DialogueClients.create(EteServiceAsync.class, clientConfiguration(port)); this.binaryClient = DialogueClients.create(EteBinaryServiceBlocking.class, clientConfiguration(port)); - this.errorServiceClient = DialogueClients.create(com.palantir.product.ErrorServiceBlocking.class, clientConfiguration(port)); + this.errorServiceClient = + DialogueClients.create(com.palantir.product.ErrorServiceBlocking.class, clientConfiguration(port)); } @BeforeAll @@ -584,21 +585,8 @@ public static void beforeClass() throws IOException { new ErrorGenerator(options), new CheckedErrorGenerator(options))) .emit(def, folder); - // validateGeneratedOutput(files, Paths.get("src/integrationInput/java")); - } - - // private static void validateGeneratedOutput(List files, Path outputDir) throws IOException { - // for (Path file : files) { - // Path relativePath = folder.toPath().relativize(file); - // Path output = outputDir.resolve(relativePath); - // if (Boolean.valueOf(System.getProperty("recreate", "false"))) { - // Files.createDirectories(relativePath.getParent()); - // Files.deleteIfExists(output); - // Files.copy(file, output); - // } - // assertThat(readFromFile(file)).isEqualTo(readFromFile(output)); - // } - // } + TestBase.validateGeneratedOutput(folder.toPath(), files, Paths.get("src/integrationInput/java")); + } private static HttpURLConnection openConnectionToTestApi(String path) throws IOException { URL url = new URL("http://localhost:" + port + "/test-example/api" + path); diff --git a/conjure-lib/src/main/java/com/palantir/conjure/java/lib/internal/ConjureErrors.java b/conjure-lib/src/main/java/com/palantir/conjure/java/lib/internal/ConjureErrors.java index 38f12fe58..b3d8a0363 100644 --- a/conjure-lib/src/main/java/com/palantir/conjure/java/lib/internal/ConjureErrors.java +++ b/conjure-lib/src/main/java/com/palantir/conjure/java/lib/internal/ConjureErrors.java @@ -16,15 +16,16 @@ package com.palantir.conjure.java.lib.internal; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; import com.palantir.logsafe.Safe; -import com.palantir.logsafe.exceptions.SafeIllegalStateException; public final class ConjureErrors { private ConjureErrors() {} + // This is used in generated code when generating errors which don't have any parameters. + public static final EmptyErrorParameters EMPTY_ERROR_PARAMETERS_INSTANCE = new EmptyErrorParameters(); + + public record EmptyErrorParameters() {} + public abstract static class BaseEndpointError { @Safe private final String errorCode; @@ -60,24 +61,4 @@ public final T getParams() { return params; } } - - public abstract static class NullToDefaultDeserializer extends JsonDeserializer { - public abstract T create(); - - /** - * If a non-null value is deserialized as `null`, throw an exception. - */ - @Override - public T deserialize(JsonParser _parser, DeserializationContext _ctxt) { - throw new SafeIllegalStateException("Attempted to deserialize non-null value as null"); - } - - /** - * When `null` is deserialized, a new object of type `T` is created. - */ - @Override - public T getNullValue(DeserializationContext _ctxt) { - return create(); - } - } } From 91753489ecedb0b3c4b7c0377ca998b5760e6a60 Mon Sep 17 00:00:00 2001 From: Pritham Marupaka Date: Mon, 27 Jan 2025 12:29:43 -0500 Subject: [PATCH 07/24] review changes --- .../product/DialogueErrorEndpoints.java | 24 +- .../product/ErrorServiceBlocking.java | 189 ++++++++------ .../palantir/product/ErrorServiceAsync.java | 191 +++++++++----- .../product/OptionalBinaryResponseMode.java | 239 ++++++++++++++++++ .../another/EndpointSpecificErrors.java | 2 + .../product/EndpointSpecificTwoErrors.java | 2 + .../product/ErrorServiceEndpoints.java | 184 +++++++++++++- .../product/UndertowErrorService.java | 39 ++- .../DefaultStaticFactoryMethodGenerator.java | 23 +- .../dialogue/DialogueInterfaceGenerator.java | 80 +++--- .../conjure/java/services/dialogue/Names.java | 5 - .../services/dialogue/ReturnTypeMapper.java | 14 + .../conjure/java/types/ErrorGenerator.java | 7 - .../java/DialogueServiceGeneratorTests.java | 3 + .../palantir/conjure/java/ErrorResource.java | 72 +++++- .../conjure/java/UndertowServiceEteTest.java | 210 +++++++++++++-- .../resources/example-endpoint-errors.yml | 32 ++- .../java/lib/internal/ConjureErrors.java | 64 ----- 18 files changed, 1056 insertions(+), 324 deletions(-) create mode 100644 conjure-java-core/src/integrationInput/java/com/palantir/product/OptionalBinaryResponseMode.java delete mode 100644 conjure-lib/src/main/java/com/palantir/conjure/java/lib/internal/ConjureErrors.java diff --git a/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/DialogueErrorEndpoints.java b/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/DialogueErrorEndpoints.java index 0221e4859..2017dcb8c 100644 --- a/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/DialogueErrorEndpoints.java +++ b/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/DialogueErrorEndpoints.java @@ -13,7 +13,7 @@ enum DialogueErrorEndpoints implements Endpoint { testBasicError { private final PathTemplate pathTemplate = - PathTemplate.builder().fixed("base").fixed("basic").build(); + PathTemplate.builder().fixed("errors").fixed("basic").build(); @Override public void renderPath(ListMultimap params, UrlBuilder url) { @@ -22,7 +22,7 @@ public void renderPath(ListMultimap params, UrlBuilder url) { @Override public HttpMethod httpMethod() { - return HttpMethod.GET; + return HttpMethod.POST; } @Override @@ -43,7 +43,7 @@ public String version() { testImportedError { private final PathTemplate pathTemplate = - PathTemplate.builder().fixed("base").fixed("imported").build(); + PathTemplate.builder().fixed("errors").fixed("imported").build(); @Override public void renderPath(ListMultimap params, UrlBuilder url) { @@ -52,7 +52,7 @@ public void renderPath(ListMultimap params, UrlBuilder url) { @Override public HttpMethod httpMethod() { - return HttpMethod.GET; + return HttpMethod.POST; } @Override @@ -73,7 +73,7 @@ public String version() { testMultipleErrorsAndPackages { private final PathTemplate pathTemplate = - PathTemplate.builder().fixed("base").fixed("multiple").build(); + PathTemplate.builder().fixed("errors").fixed("multiple").build(); @Override public void renderPath(ListMultimap params, UrlBuilder url) { @@ -82,7 +82,7 @@ public void renderPath(ListMultimap params, UrlBuilder url) { @Override public HttpMethod httpMethod() { - return HttpMethod.GET; + return HttpMethod.POST; } @Override @@ -103,7 +103,7 @@ public String version() { testEmptyBody { private final PathTemplate pathTemplate = - PathTemplate.builder().fixed("base").fixed("empty").build(); + PathTemplate.builder().fixed("errors").fixed("empty").build(); @Override public void renderPath(ListMultimap params, UrlBuilder url) { @@ -112,7 +112,7 @@ public void renderPath(ListMultimap params, UrlBuilder url) { @Override public HttpMethod httpMethod() { - return HttpMethod.GET; + return HttpMethod.POST; } @Override @@ -133,7 +133,7 @@ public String version() { testBinary { private final PathTemplate pathTemplate = - PathTemplate.builder().fixed("base").fixed("binary").build(); + PathTemplate.builder().fixed("errors").fixed("binary").build(); @Override public void renderPath(ListMultimap params, UrlBuilder url) { @@ -142,7 +142,7 @@ public void renderPath(ListMultimap params, UrlBuilder url) { @Override public HttpMethod httpMethod() { - return HttpMethod.GET; + return HttpMethod.POST; } @Override @@ -163,7 +163,7 @@ public String version() { testOptionalBinary { private final PathTemplate pathTemplate = - PathTemplate.builder().fixed("base").fixed("optional-binary").build(); + PathTemplate.builder().fixed("errors").fixed("optional-binary").build(); @Override public void renderPath(ListMultimap params, UrlBuilder url) { @@ -172,7 +172,7 @@ public void renderPath(ListMultimap params, UrlBuilder url) { @Override public HttpMethod httpMethod() { - return HttpMethod.GET; + return HttpMethod.POST; } @Override diff --git a/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/ErrorServiceBlocking.java b/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/ErrorServiceBlocking.java index e68e08087..c425757c9 100644 --- a/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/ErrorServiceBlocking.java +++ b/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/ErrorServiceBlocking.java @@ -2,10 +2,11 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonValue; import com.google.errorprone.annotations.MustBeClosed; import com.palantir.conjure.java.lib.internal.ClientEndpoint; -import com.palantir.conjure.java.lib.internal.ConjureErrors; import com.palantir.dialogue.Channel; +import com.palantir.dialogue.ConjureErrors; import com.palantir.dialogue.ConjureRuntime; import com.palantir.dialogue.Deserializer; import com.palantir.dialogue.DeserializerArgs; @@ -16,47 +17,53 @@ import com.palantir.dialogue.EndpointChannelFactory; 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.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 GET /base/basic} */ - @ClientEndpoint(method = "GET", path = "/base/basic") - TestBasicErrorResponse testBasicError(AuthHeader authHeader); + /** @apiNote {@code POST /errors/basic} */ + @ClientEndpoint(method = "POST", path = "/errors/basic") + TestBasicErrorResponse testBasicError(AuthHeader authHeader, boolean shouldThrowError); - /** @apiNote {@code GET /base/imported} */ - @ClientEndpoint(method = "GET", path = "/base/imported") - TestImportedErrorResponse testImportedError(AuthHeader authHeader); + /** @apiNote {@code POST /errors/imported} */ + @ClientEndpoint(method = "POST", path = "/errors/imported") + TestImportedErrorResponse testImportedError(AuthHeader authHeader, boolean shouldThrowError); - /** @apiNote {@code GET /base/multiple} */ - @ClientEndpoint(method = "GET", path = "/base/multiple") - TestMultipleErrorsAndPackagesResponse testMultipleErrorsAndPackages(AuthHeader authHeader); + /** @apiNote {@code POST /errors/multiple} */ + @ClientEndpoint(method = "POST", path = "/errors/multiple") + TestMultipleErrorsAndPackagesResponse testMultipleErrorsAndPackages( + AuthHeader authHeader, Optional errorToThrow); - /** @apiNote {@code GET /base/empty} */ - @ClientEndpoint(method = "GET", path = "/base/empty") - TestEmptyBodyResponse testEmptyBody(AuthHeader authHeader); + /** @apiNote {@code POST /errors/empty} */ + @ClientEndpoint(method = "POST", path = "/errors/empty") + TestEmptyBodyResponse testEmptyBody(AuthHeader authHeader, boolean shouldThrowError); - /** @apiNote {@code GET /base/binary} */ - @ClientEndpoint(method = "GET", path = "/base/binary") - TestBinaryResponse testBinary(AuthHeader authHeader); + /** @apiNote {@code POST /errors/binary} */ + @ClientEndpoint(method = "POST", path = "/errors/binary") + TestBinaryResponse testBinary(AuthHeader authHeader, boolean shouldThrowError); - /** @apiNote {@code GET /base/optional-binary} */ - @ClientEndpoint(method = "GET", path = "/base/optional-binary") - TestOptionalBinaryResponse testOptionalBinary(AuthHeader authHeader); + /** @apiNote {@code POST /errors/optional-binary} */ + @ClientEndpoint(method = "POST", path = "/errors/optional-binary") + 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); @@ -69,6 +76,9 @@ static ErrorServiceBlocking of(EndpointChannelFactory _endpointChannelFactory, C new TypeMarker() {}) .build()); + private final Serializer testImportedErrorSerializer = + _runtime.bodySerDe().serializer(new TypeMarker() {}); + private final EndpointChannel testImportedErrorChannel = _endpointChannelFactory.endpoint(DialogueErrorEndpoints.testImportedError); @@ -81,6 +91,9 @@ static ErrorServiceBlocking of(EndpointChannelFactory _endpointChannelFactory, C new TypeMarker() {}) .build()); + private final Serializer> testMultipleErrorsAndPackagesSerializer = + _runtime.bodySerDe().serializer(new TypeMarker>() {}); + private final EndpointChannel testMultipleErrorsAndPackagesChannel = _endpointChannelFactory.endpoint(DialogueErrorEndpoints.testMultipleErrorsAndPackages); @@ -104,6 +117,9 @@ static ErrorServiceBlocking of(EndpointChannelFactory _endpointChannelFactory, C new TypeMarker() {}) .build()); + private final Serializer testEmptyBodySerializer = + _runtime.bodySerDe().serializer(new TypeMarker() {}); + private final EndpointChannel testEmptyBodyChannel = _endpointChannelFactory.endpoint(DialogueErrorEndpoints.testEmptyBody); @@ -116,6 +132,9 @@ static ErrorServiceBlocking of(EndpointChannelFactory _endpointChannelFactory, C new TypeMarker() {}) .build()); + private final Serializer testBinarySerializer = + _runtime.bodySerDe().serializer(new TypeMarker() {}); + private final EndpointChannel testBinaryChannel = _endpointChannelFactory.endpoint(DialogueErrorEndpoints.testBinary); @@ -128,6 +147,9 @@ static ErrorServiceBlocking of(EndpointChannelFactory _endpointChannelFactory, C new TypeMarker() {}) .build()); + private final Serializer testOptionalBinarySerializer = + _runtime.bodySerDe().serializer(new TypeMarker() {}); + private final EndpointChannel testOptionalBinaryChannel = _endpointChannelFactory.endpoint(DialogueErrorEndpoints.testOptionalBinary); @@ -141,25 +163,29 @@ static ErrorServiceBlocking of(EndpointChannelFactory _endpointChannelFactory, C .build()); @Override - public TestBasicErrorResponse testBasicError(AuthHeader authHeader) { + 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) { + 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) { + 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, @@ -168,24 +194,28 @@ public TestMultipleErrorsAndPackagesResponse testMultipleErrorsAndPackages(AuthH } @Override - public TestEmptyBodyResponse testEmptyBody(AuthHeader authHeader) { + 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) { + 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) { + 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); } @@ -222,7 +252,7 @@ public ErrorServiceBlocking create(EndpointChannelFactory endpointChannelFactory sealed interface TestBasicErrorResponse permits TestBasicErrorResponse.Success, TestBasicErrorResponse.InvalidArgument { - record Success(String value) implements TestBasicErrorResponse { + record Success(@JsonValue String value) implements TestBasicErrorResponse { public Success { Preconditions.checkArgumentNotNull(value, "value cannot be null"); } @@ -230,10 +260,10 @@ record Success(String value) implements TestBasicErrorResponse { final class InvalidArgument extends ConjureErrors.BaseEndpointError implements TestBasicErrorResponse { - @JsonCreator + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) InvalidArgument( - @JsonProperty("errorCode") String errorCode, - @JsonProperty("errorInstanceId") String errorInstanceId, + @JsonProperty("errorCode") @Safe String errorCode, + @JsonProperty("errorInstanceId") @Safe String errorInstanceId, @JsonProperty("parameters") TestErrors.InvalidArgumentParameters parameters) { super(errorCode, TestErrors.INVALID_ARGUMENT.name(), errorInstanceId, parameters); } @@ -242,7 +272,7 @@ final class InvalidArgument extends ConjureErrors.BaseEndpointError implements TestImportedErrorResponse { - @JsonCreator + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) EndpointError( - @JsonProperty("errorCode") String errorCode, - @JsonProperty("errorInstanceId") String errorInstanceId, + @JsonProperty("errorCode") @Safe String errorCode, + @JsonProperty("errorInstanceId") @Safe String errorInstanceId, @JsonProperty("parameters") EndpointSpecificErrors.EndpointErrorParameters parameters) { super(errorCode, EndpointSpecificErrors.ENDPOINT_ERROR.name(), errorInstanceId, parameters); } @@ -267,7 +297,7 @@ sealed interface TestMultipleErrorsAndPackagesResponse TestMultipleErrorsAndPackagesResponse.NotFound, TestMultipleErrorsAndPackagesResponse.DifferentNamespace, TestMultipleErrorsAndPackagesResponse.DifferentPackage { - record Success(String value) implements TestMultipleErrorsAndPackagesResponse { + record Success(@JsonValue String value) implements TestMultipleErrorsAndPackagesResponse { public Success { Preconditions.checkArgumentNotNull(value, "value cannot be null"); } @@ -275,10 +305,10 @@ record Success(String value) implements TestMultipleErrorsAndPackagesResponse { final class InvalidArgument extends ConjureErrors.BaseEndpointError implements TestMultipleErrorsAndPackagesResponse { - @JsonCreator + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) InvalidArgument( - @JsonProperty("errorCode") String errorCode, - @JsonProperty("errorInstanceId") String errorInstanceId, + @JsonProperty("errorCode") @Safe String errorCode, + @JsonProperty("errorInstanceId") @Safe String errorInstanceId, @JsonProperty("parameters") TestErrors.InvalidArgumentParameters parameters) { super(errorCode, TestErrors.INVALID_ARGUMENT.name(), errorInstanceId, parameters); } @@ -286,98 +316,115 @@ final class InvalidArgument extends ConjureErrors.BaseEndpointError implements TestMultipleErrorsAndPackagesResponse { - @JsonCreator + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) NotFound( - @JsonProperty("errorCode") String errorCode, - @JsonProperty("errorInstanceId") String errorInstanceId, + @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 ConjureErrors.BaseEndpointError + final class DifferentNamespace + extends ConjureErrors.BaseEndpointError implements TestMultipleErrorsAndPackagesResponse { - @JsonCreator + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) DifferentNamespace( - @JsonProperty("errorCode") String errorCode, - @JsonProperty("errorInstanceId") String errorInstanceId) { + @JsonProperty("errorCode") @Safe String errorCode, + @JsonProperty("errorInstanceId") @Safe String errorInstanceId) { super( errorCode, EndpointSpecificTwoErrors.DIFFERENT_NAMESPACE.name(), errorInstanceId, - ConjureErrors.EMPTY_ERROR_PARAMETERS_INSTANCE); + new EndpointSpecificTwoErrors.DifferentNamespaceParameters()); } } - final class DifferentPackage extends ConjureErrors.BaseEndpointError + final class DifferentPackage + extends ConjureErrors.BaseEndpointError< + com.palantir.another.EndpointSpecificErrors.DifferentPackageParameters> implements TestMultipleErrorsAndPackagesResponse { - @JsonCreator + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) DifferentPackage( - @JsonProperty("errorCode") String errorCode, - @JsonProperty("errorInstanceId") String errorInstanceId) { + @JsonProperty("errorCode") @Safe String errorCode, + @JsonProperty("errorInstanceId") @Safe String errorInstanceId) { super( errorCode, com.palantir.another.EndpointSpecificErrors.DIFFERENT_PACKAGE.name(), errorInstanceId, - ConjureErrors.EMPTY_ERROR_PARAMETERS_INSTANCE); + new com.palantir.another.EndpointSpecificErrors.DifferentPackageParameters()); } } } sealed interface TestEmptyBodyResponse permits TestEmptyBodyResponse.Success, TestEmptyBodyResponse.InvalidArgument { - record Success() implements TestEmptyBodyResponse { - @JsonCreator - public static Success create() { - return new Success(); - } - } + record Success() implements TestEmptyBodyResponse {} final class InvalidArgument extends ConjureErrors.BaseEndpointError implements TestEmptyBodyResponse { - @JsonCreator + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) InvalidArgument( - @JsonProperty("errorCode") String errorCode, - @JsonProperty("errorInstanceId") String errorInstanceId, + @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 permits TestBinaryResponse.Success, TestBinaryResponse.InvalidArgument { - record Success(@MustBeClosed InputStream value) implements TestBinaryResponse { + 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 ConjureErrors.BaseEndpointError implements TestBinaryResponse { - @JsonCreator + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) InvalidArgument( - @JsonProperty("errorCode") String errorCode, - @JsonProperty("errorInstanceId") String errorInstanceId, + @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 + sealed interface TestOptionalBinaryResponse extends Closeable permits TestOptionalBinaryResponse.Success, TestOptionalBinaryResponse.InvalidArgument { - record Success(Optional value) implements TestOptionalBinaryResponse { + @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 ConjureErrors.BaseEndpointError implements TestOptionalBinaryResponse { - @JsonCreator + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) InvalidArgument( - @JsonProperty("errorCode") String errorCode, - @JsonProperty("errorInstanceId") String errorInstanceId, + @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/com/palantir/product/ErrorServiceAsync.java b/conjure-java-core/src/integrationInput/java/com/palantir/product/ErrorServiceAsync.java index 9b54e923b..9eed16273 100644 --- a/conjure-java-core/src/integrationInput/java/com/palantir/product/ErrorServiceAsync.java +++ b/conjure-java-core/src/integrationInput/java/com/palantir/product/ErrorServiceAsync.java @@ -2,11 +2,12 @@ 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.MustBeClosed; import com.palantir.conjure.java.lib.internal.ClientEndpoint; -import com.palantir.conjure.java.lib.internal.ConjureErrors; import com.palantir.dialogue.Channel; +import com.palantir.dialogue.ConjureErrors; import com.palantir.dialogue.ConjureRuntime; import com.palantir.dialogue.Deserializer; import com.palantir.dialogue.DeserializerArgs; @@ -17,10 +18,15 @@ import com.palantir.dialogue.EndpointChannelFactory; 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; @@ -29,35 +35,40 @@ @Generated("com.palantir.conjure.java.services.dialogue.DialogueInterfaceGenerator") @DialogueService(ErrorServiceAsync.Factory.class) public interface ErrorServiceAsync { - /** @apiNote {@code GET /base/basic} */ - @ClientEndpoint(method = "GET", path = "/base/basic") - ListenableFuture testBasicError(AuthHeader authHeader); + /** @apiNote {@code POST /errors/basic} */ + @ClientEndpoint(method = "POST", path = "/errors/basic") + ListenableFuture testBasicError(AuthHeader authHeader, boolean shouldThrowError); - /** @apiNote {@code GET /base/imported} */ - @ClientEndpoint(method = "GET", path = "/base/imported") - ListenableFuture testImportedError(AuthHeader authHeader); + /** @apiNote {@code POST /errors/imported} */ + @ClientEndpoint(method = "POST", path = "/errors/imported") + ListenableFuture testImportedError(AuthHeader authHeader, boolean shouldThrowError); - /** @apiNote {@code GET /base/multiple} */ - @ClientEndpoint(method = "GET", path = "/base/multiple") - ListenableFuture testMultipleErrorsAndPackages(AuthHeader authHeader); + /** @apiNote {@code POST /errors/multiple} */ + @ClientEndpoint(method = "POST", path = "/errors/multiple") + ListenableFuture testMultipleErrorsAndPackages( + AuthHeader authHeader, Optional errorToThrow); - /** @apiNote {@code GET /base/empty} */ - @ClientEndpoint(method = "GET", path = "/base/empty") - ListenableFuture testEmptyBody(AuthHeader authHeader); + /** @apiNote {@code POST /errors/empty} */ + @ClientEndpoint(method = "POST", path = "/errors/empty") + ListenableFuture testEmptyBody(AuthHeader authHeader, boolean shouldThrowError); - /** @apiNote {@code GET /base/binary} */ - @ClientEndpoint(method = "GET", path = "/base/binary") - ListenableFuture testBinary(AuthHeader authHeader); + /** @apiNote {@code POST /errors/binary} */ + @ClientEndpoint(method = "POST", path = "/errors/binary") + ListenableFuture testBinary(AuthHeader authHeader, boolean shouldThrowError); - /** @apiNote {@code GET /base/optional-binary} */ - @ClientEndpoint(method = "GET", path = "/base/optional-binary") - ListenableFuture testOptionalBinary(AuthHeader authHeader); + /** @apiNote {@code POST /errors/optional-binary} */ + @ClientEndpoint(method = "POST", path = "/errors/optional-binary") + 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); @@ -70,6 +81,9 @@ static ErrorServiceAsync of(EndpointChannelFactory _endpointChannelFactory, Conj new TypeMarker() {}) .build()); + private final Serializer testImportedErrorSerializer = + _runtime.bodySerDe().serializer(new TypeMarker() {}); + private final EndpointChannel testImportedErrorChannel = _endpointChannelFactory.endpoint(DialogueErrorEndpoints.testImportedError); @@ -82,6 +96,9 @@ static ErrorServiceAsync of(EndpointChannelFactory _endpointChannelFactory, Conj new TypeMarker() {}) .build()); + private final Serializer> testMultipleErrorsAndPackagesSerializer = + _runtime.bodySerDe().serializer(new TypeMarker>() {}); + private final EndpointChannel testMultipleErrorsAndPackagesChannel = _endpointChannelFactory.endpoint(DialogueErrorEndpoints.testMultipleErrorsAndPackages); @@ -105,6 +122,9 @@ static ErrorServiceAsync of(EndpointChannelFactory _endpointChannelFactory, Conj new TypeMarker() {}) .build()); + private final Serializer testEmptyBodySerializer = + _runtime.bodySerDe().serializer(new TypeMarker() {}); + private final EndpointChannel testEmptyBodyChannel = _endpointChannelFactory.endpoint(DialogueErrorEndpoints.testEmptyBody); @@ -117,6 +137,9 @@ static ErrorServiceAsync of(EndpointChannelFactory _endpointChannelFactory, Conj new TypeMarker() {}) .build()); + private final Serializer testBinarySerializer = + _runtime.bodySerDe().serializer(new TypeMarker() {}); + private final EndpointChannel testBinaryChannel = _endpointChannelFactory.endpoint(DialogueErrorEndpoints.testBinary); @@ -129,6 +152,9 @@ static ErrorServiceAsync of(EndpointChannelFactory _endpointChannelFactory, Conj new TypeMarker() {}) .build()); + private final Serializer testOptionalBinarySerializer = + _runtime.bodySerDe().serializer(new TypeMarker() {}); + private final EndpointChannel testOptionalBinaryChannel = _endpointChannelFactory.endpoint(DialogueErrorEndpoints.testOptionalBinary); @@ -142,25 +168,30 @@ static ErrorServiceAsync of(EndpointChannelFactory _endpointChannelFactory, Conj .build()); @Override - public ListenableFuture testBasicError(AuthHeader authHeader) { + 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) { + 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) { + AuthHeader authHeader, Optional errorToThrow) { Request.Builder _request = Request.builder(); _request.putHeaderParams("Authorization", authHeader.toString()); + _request.body(testMultipleErrorsAndPackagesSerializer.serialize(errorToThrow)); return _runtime.clients() .call( testMultipleErrorsAndPackagesChannel, @@ -169,23 +200,28 @@ public ListenableFuture testMultipleError } @Override - public ListenableFuture testEmptyBody(AuthHeader authHeader) { + 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) { + 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) { + 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); } @@ -222,7 +258,7 @@ public ErrorServiceAsync create(EndpointChannelFactory endpointChannelFactory, C sealed interface TestBasicErrorResponse permits TestBasicErrorResponse.Success, TestBasicErrorResponse.InvalidArgument { - record Success(String value) implements TestBasicErrorResponse { + record Success(@JsonValue String value) implements TestBasicErrorResponse { public Success { Preconditions.checkArgumentNotNull(value, "value cannot be null"); } @@ -230,10 +266,10 @@ record Success(String value) implements TestBasicErrorResponse { final class InvalidArgument extends ConjureErrors.BaseEndpointError implements TestBasicErrorResponse { - @JsonCreator + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) InvalidArgument( - @JsonProperty("errorCode") String errorCode, - @JsonProperty("errorInstanceId") String errorInstanceId, + @JsonProperty("errorCode") @Safe String errorCode, + @JsonProperty("errorInstanceId") @Safe String errorInstanceId, @JsonProperty("parameters") TestErrors.InvalidArgumentParameters parameters) { super(errorCode, TestErrors.INVALID_ARGUMENT.name(), errorInstanceId, parameters); } @@ -242,7 +278,7 @@ final class InvalidArgument extends ConjureErrors.BaseEndpointError implements TestImportedErrorResponse { - @JsonCreator + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) EndpointError( - @JsonProperty("errorCode") String errorCode, - @JsonProperty("errorInstanceId") String errorInstanceId, + @JsonProperty("errorCode") @Safe String errorCode, + @JsonProperty("errorInstanceId") @Safe String errorInstanceId, @JsonProperty("parameters") EndpointSpecificErrors.EndpointErrorParameters parameters) { super(errorCode, EndpointSpecificErrors.ENDPOINT_ERROR.name(), errorInstanceId, parameters); } @@ -267,7 +303,7 @@ sealed interface TestMultipleErrorsAndPackagesResponse TestMultipleErrorsAndPackagesResponse.NotFound, TestMultipleErrorsAndPackagesResponse.DifferentNamespace, TestMultipleErrorsAndPackagesResponse.DifferentPackage { - record Success(String value) implements TestMultipleErrorsAndPackagesResponse { + record Success(@JsonValue String value) implements TestMultipleErrorsAndPackagesResponse { public Success { Preconditions.checkArgumentNotNull(value, "value cannot be null"); } @@ -275,10 +311,10 @@ record Success(String value) implements TestMultipleErrorsAndPackagesResponse { final class InvalidArgument extends ConjureErrors.BaseEndpointError implements TestMultipleErrorsAndPackagesResponse { - @JsonCreator + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) InvalidArgument( - @JsonProperty("errorCode") String errorCode, - @JsonProperty("errorInstanceId") String errorInstanceId, + @JsonProperty("errorCode") @Safe String errorCode, + @JsonProperty("errorInstanceId") @Safe String errorInstanceId, @JsonProperty("parameters") TestErrors.InvalidArgumentParameters parameters) { super(errorCode, TestErrors.INVALID_ARGUMENT.name(), errorInstanceId, parameters); } @@ -286,98 +322,115 @@ final class InvalidArgument extends ConjureErrors.BaseEndpointError implements TestMultipleErrorsAndPackagesResponse { - @JsonCreator + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) NotFound( - @JsonProperty("errorCode") String errorCode, - @JsonProperty("errorInstanceId") String errorInstanceId, + @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 ConjureErrors.BaseEndpointError + final class DifferentNamespace + extends ConjureErrors.BaseEndpointError implements TestMultipleErrorsAndPackagesResponse { - @JsonCreator + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) DifferentNamespace( - @JsonProperty("errorCode") String errorCode, - @JsonProperty("errorInstanceId") String errorInstanceId) { + @JsonProperty("errorCode") @Safe String errorCode, + @JsonProperty("errorInstanceId") @Safe String errorInstanceId) { super( errorCode, EndpointSpecificTwoErrors.DIFFERENT_NAMESPACE.name(), errorInstanceId, - ConjureErrors.EMPTY_ERROR_PARAMETERS_INSTANCE); + new EndpointSpecificTwoErrors.DifferentNamespaceParameters()); } } - final class DifferentPackage extends ConjureErrors.BaseEndpointError + final class DifferentPackage + extends ConjureErrors.BaseEndpointError< + com.palantir.another.EndpointSpecificErrors.DifferentPackageParameters> implements TestMultipleErrorsAndPackagesResponse { - @JsonCreator + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) DifferentPackage( - @JsonProperty("errorCode") String errorCode, - @JsonProperty("errorInstanceId") String errorInstanceId) { + @JsonProperty("errorCode") @Safe String errorCode, + @JsonProperty("errorInstanceId") @Safe String errorInstanceId) { super( errorCode, com.palantir.another.EndpointSpecificErrors.DIFFERENT_PACKAGE.name(), errorInstanceId, - ConjureErrors.EMPTY_ERROR_PARAMETERS_INSTANCE); + new com.palantir.another.EndpointSpecificErrors.DifferentPackageParameters()); } } } sealed interface TestEmptyBodyResponse permits TestEmptyBodyResponse.Success, TestEmptyBodyResponse.InvalidArgument { - record Success() implements TestEmptyBodyResponse { - @JsonCreator - public static Success create() { - return new Success(); - } - } + record Success() implements TestEmptyBodyResponse {} final class InvalidArgument extends ConjureErrors.BaseEndpointError implements TestEmptyBodyResponse { - @JsonCreator + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) InvalidArgument( - @JsonProperty("errorCode") String errorCode, - @JsonProperty("errorInstanceId") String errorInstanceId, + @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 permits TestBinaryResponse.Success, TestBinaryResponse.InvalidArgument { - record Success(@MustBeClosed InputStream value) implements TestBinaryResponse { + 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 ConjureErrors.BaseEndpointError implements TestBinaryResponse { - @JsonCreator + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) InvalidArgument( - @JsonProperty("errorCode") String errorCode, - @JsonProperty("errorInstanceId") String errorInstanceId, + @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 + sealed interface TestOptionalBinaryResponse extends Closeable permits TestOptionalBinaryResponse.Success, TestOptionalBinaryResponse.InvalidArgument { - record Success(Optional value) implements TestOptionalBinaryResponse { + @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 ConjureErrors.BaseEndpointError implements TestOptionalBinaryResponse { - @JsonCreator + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) InvalidArgument( - @JsonProperty("errorCode") String errorCode, - @JsonProperty("errorInstanceId") String errorInstanceId, + @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/com/palantir/product/OptionalBinaryResponseMode.java b/conjure-java-core/src/integrationInput/java/com/palantir/product/OptionalBinaryResponseMode.java new file mode 100644 index 000000000..d733abffe --- /dev/null +++ b/conjure-java-core/src/integrationInput/java/com/palantir/product/OptionalBinaryResponseMode.java @@ -0,0 +1,239 @@ +package 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/another/EndpointSpecificErrors.java b/conjure-java-core/src/integrationInput/java/undertow/com/palantir/another/EndpointSpecificErrors.java index 5ed9d792f..38a5fe579 100644 --- a/conjure-java-core/src/integrationInput/java/undertow/com/palantir/another/EndpointSpecificErrors.java +++ b/conjure-java-core/src/integrationInput/java/undertow/com/palantir/another/EndpointSpecificErrors.java @@ -18,4 +18,6 @@ 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/undertow/com/palantir/product/EndpointSpecificTwoErrors.java b/conjure-java-core/src/integrationInput/java/undertow/com/palantir/product/EndpointSpecificTwoErrors.java index ff930dc05..dc2240282 100644 --- a/conjure-java-core/src/integrationInput/java/undertow/com/palantir/product/EndpointSpecificTwoErrors.java +++ b/conjure-java-core/src/integrationInput/java/undertow/com/palantir/product/EndpointSpecificTwoErrors.java @@ -18,4 +18,6 @@ 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/undertow/com/palantir/product/ErrorServiceEndpoints.java b/conjure-java-core/src/integrationInput/java/undertow/com/palantir/product/ErrorServiceEndpoints.java index ee6aa7f4e..3f0d6a204 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); } @@ -147,18 +165,19 @@ public void handleRequest(HttpServerExchange exchange) EndpointSpecificTwoServerErrors.DifferentNamespace, undertow.com.palantir.another.EndpointSpecificServerErrors.DifferentPackage { 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 +195,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/UndertowErrorService.java b/conjure-java-core/src/integrationInput/java/undertow/com/palantir/product/UndertowErrorService.java index 813d8a452..c2feb0067 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,58 @@ 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 */ - String testMultipleErrorsAndPackages(AuthHeader authHeader) + String testMultipleErrorsAndPackages(AuthHeader authHeader, Optional errorToThrow) throws TestServerErrors.InvalidArgument, TestServerErrors.NotFound, EndpointSpecificTwoServerErrors.DifferentNamespace, +<<<<<<< HEAD:conjure-java-core/src/integrationInput/java/undertow/com/palantir/product/UndertowErrorService.java undertow.com.palantir.another.EndpointSpecificServerErrors.DifferentPackage; +======= + com.palantir.another.EndpointSpecificServerErrors.DifferentPackage; + + /** + * @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; +>>>>>>> 2629616c (review changes):conjure-java-core/src/integrationInput/java/com/palantir/product/UndertowErrorService.java } 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 11dc4a5fb..0a86e8f32 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 @@ -180,7 +180,7 @@ private Optional deserializer( TypeName className = Primitives.box(returnTypes.baseType(type)); boolean generateResultTypes = shouldGenerateDialogueEndpointErrorResultTypesForEndpoint(options, endpointDef); - if (isBinaryOrOptionalBinary(className, returnTypes) && !generateResultTypes) { + if (returnTypes.isBinaryOrOptionalBinary(className) && !generateResultTypes) { return Optional.empty(); } @@ -224,9 +224,9 @@ private CodeBlock constructDeserializerWithEndpointErrors( } deserializerArgsBuilder.add(".build()"); CodeBlock.Builder deserializerBuilder = CodeBlock.builder(); - if (isBinary(className, returnTypes)) { + if (returnTypes.isBinary(className)) { deserializerBuilder.add("inputStreamDeserializer("); - } else if (isOptionalBinary(className, returnTypes)) { + } else if (returnTypes.isOptionalBinary(className)) { deserializerBuilder.add("optionalInputStreamDeserializer("); } else { deserializerBuilder.add("deserializer("); @@ -236,25 +236,12 @@ private CodeBlock constructDeserializerWithEndpointErrors( return deserializerBuilder.build(); } - private static boolean isBinaryOrOptionalBinary(TypeName className, ReturnTypeMapper returnTypes) { - return isBinary(className, returnTypes) || isOptionalBinary(className, returnTypes); - } - private static boolean shouldGenerateDialogueEndpointErrorResultTypesForEndpoint( Options options, EndpointDefinition endpointDefinition) { return options.generateDialogueEndpointErrorResultTypes() && !endpointDefinition.getErrors().isEmpty(); } - private static boolean isBinary(TypeName className, ReturnTypeMapper returnTypes) { - return className.equals(returnTypes.baseType(Type.primitive(PrimitiveType.BINARY))); - } - - private static boolean isOptionalBinary(TypeName className, ReturnTypeMapper returnTypes) { - return className.equals( - returnTypes.baseType(Type.optional(OptionalType.of(Type.primitive(PrimitiveType.BINARY))))); - } - private MethodSpec clientImpl(ClassName className, EndpointDefinition def) { List params = parameterTypes.implementationMethodParams(def); MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder( @@ -295,9 +282,9 @@ private MethodSpec clientImpl(ClassName className, EndpointDefinition def) { REQUEST, def.getReturns() .filter(type -> !generateResultTypes - && isBinaryOrOptionalBinary(returnTypes.baseType(type), returnTypes)) + && 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")); 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 8e8be3e80..ddc0b92cb 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 @@ -20,13 +20,12 @@ 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.MustBeClosed; import com.palantir.conjure.java.ConjureAnnotations; import com.palantir.conjure.java.Options; -import com.palantir.conjure.java.lib.internal.ConjureErrors; -import com.palantir.conjure.java.lib.internal.ConjureErrors.BaseEndpointError; import com.palantir.conjure.java.services.IsUndertowAsyncMarkerVisitor; import com.palantir.conjure.java.services.ServiceGenerators; import com.palantir.conjure.java.services.ServiceGenerators.EndpointErrorsJavaDoc; @@ -41,6 +40,7 @@ import com.palantir.conjure.spec.ServiceDefinition; import com.palantir.conjure.visitor.TypeVisitor; import com.palantir.dialogue.Channel; +import com.palantir.dialogue.ConjureErrors.BaseEndpointError; import com.palantir.dialogue.ConjureRuntime; import com.palantir.dialogue.DialogueService; import com.palantir.dialogue.DialogueServiceFactory; @@ -57,7 +57,10 @@ 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; @@ -201,8 +204,7 @@ private TypeSpec responseTypeForEndpoint(String packageName, ClassName className errorTypes.add(constructEndpointErrorType(endpointError, packageName, responseTypeName)); } - // Get type from typespec - return TypeSpec.interfaceBuilder(responseTypeName) + TypeSpec.Builder builder = TypeSpec.interfaceBuilder(responseTypeName) .addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.SEALED) .addPermittedSubclass( ClassName.get(packageName, className.simpleName(), responseTypeName.simpleName(), "Success")) @@ -211,8 +213,19 @@ private TypeSpec responseTypeForEndpoint(String packageName, ClassName className packageName, className.simpleName(), responseTypeName.simpleName(), type.name())) .toList()) .addType(successRecord) - .addTypes(errorTypes) - .build(); + .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( @@ -224,24 +237,18 @@ private TypeSpec constructEndpointErrorType( 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; - if (errorNameToParameterExistenceMapping.hasParameters(endpointError.getError())) { - parametersClassName = ClassName.get( - endpointError.getError().getPackage(), - ErrorGenerationUtils.errorTypesClassName( - endpointError.getError().getNamespace()), - ErrorGenerationUtils.errorParametersClassName( - endpointError.getError().getName())); - } else { - parametersClassName = ClassName.get(ConjureErrors.EmptyErrorParameters.class); - } + ClassName parametersClassName = ClassName.get( + endpointError.getError().getPackage(), + ErrorGenerationUtils.errorTypesClassName( + endpointError.getError().getNamespace()), + ErrorGenerationUtils.errorParametersClassName( + endpointError.getError().getName())); endpointErrorTypeBuilder .superclass(ParameterizedTypeName.get(ClassName.get(BaseEndpointError.class), parametersClassName)) .addMethod(errorTypeConstructor(endpointError.getError(), parametersClassName, errorTypesClassName)); @@ -263,6 +270,9 @@ private TypeSpec createSuccessRecord( 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()); } @@ -271,13 +281,23 @@ private TypeSpec createSuccessRecord( .recordConstructor(successCtorBuilder.build()) .addSuperinterface(responseTypeName); - if (returnType.equals(TypeName.VOID)) { - successRecordBuilder.addMethod(MethodSpec.methodBuilder("create") - .addAnnotation(JsonCreator.class) - .addModifiers(Modifier.PUBLIC, Modifier.STATIC) - .returns(successTypeClassName) - .addStatement("return new $T()", successTypeClassName) - .build()); + 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(); } @@ -285,16 +305,20 @@ private TypeSpec createSuccessRecord( private MethodSpec errorTypeConstructor( ErrorTypeName errorTypeName, ClassName parametersClassName, ClassName errorTypesClassName) { MethodSpec.Builder ctorBuilder = MethodSpec.constructorBuilder() - .addAnnotation(JsonCreator.class) + .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 (errorNameToParameterExistenceMapping.hasParameters(errorTypeName)) { ctorBuilder.addParameter(ParameterSpec.builder(parametersClassName, "parameters") @@ -305,9 +329,7 @@ private MethodSpec errorTypeConstructor( ctorBuilder.addStatement("super(errorCode, $T.name(), errorInstanceId, parameters)", errorTypesClassName); } else { ctorBuilder.addStatement( - "super(errorCode, $T.name(), errorInstanceId, $T.EMPTY_ERROR_PARAMETERS_INSTANCE)", - errorTypesClassName, - ClassName.get(ConjureErrors.class)); + "super(errorCode, $T.name(), errorInstanceId, new $T())", errorTypesClassName, parametersClassName); } return ctorBuilder.build(); } diff --git a/conjure-java-core/src/main/java/com/palantir/conjure/java/services/dialogue/Names.java b/conjure-java-core/src/main/java/com/palantir/conjure/java/services/dialogue/Names.java index ff13e1ab9..a2d2c8110 100644 --- a/conjure-java-core/src/main/java/com/palantir/conjure/java/services/dialogue/Names.java +++ b/conjure-java-core/src/main/java/com/palantir/conjure/java/services/dialogue/Names.java @@ -30,11 +30,6 @@ public static ClassName endpointsClassName(ServiceDefinition def, Options option return maybeRelocate(def, options, simpleName); } - public static ClassName resultTypesClassName(ServiceDefinition def, Options options) { - String simpleName = "Dialogue" + def.getServiceName().getName().replaceAll("Service$", "") + "ResultTypes"; - return maybeRelocate(def, options, simpleName); - } - public static ClassName blockingClassName(ServiceDefinition def, Options options) { String simpleName = def.getServiceName().getName() + "Blocking"; return maybeRelocate(def, options, simpleName); 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..d5d1e2207 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))); } + + public boolean isBinaryOrOptionalBinary(TypeName returnType) { + return isBinary(returnType) || isOptionalBinary(returnType); + } + + public boolean isOptionalBinary(TypeName returnType) { + return returnType.equals(baseType(Type.optional(OptionalType.of(Type.primitive(PrimitiveType.BINARY))))); + } + + public 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/ErrorGenerator.java b/conjure-java-core/src/main/java/com/palantir/conjure/java/types/ErrorGenerator.java index d088a9b6c..3d275da76 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 @@ -182,15 +182,8 @@ private static List generateErrorParameterRecords( .toList(); } - /** - * If the error has parameters, create a record holding error parameters. - */ private static Optional generateErrorParameterRecord( ErrorDefinition errorDefinition, TypeMapper typeMapper) { - if (errorDefinition.getSafeArgs().isEmpty() - && errorDefinition.getUnsafeArgs().isEmpty()) { - return Optional.empty(); - } TypeSpec.Builder parametersRecordBuilder = TypeSpec.recordBuilder(ErrorGenerationUtils.errorParametersClassName( errorDefinition.getErrorName().getName())) .addModifiers(Modifier.PUBLIC, Modifier.STATIC); diff --git a/conjure-java-core/src/test/java/com/palantir/conjure/java/DialogueServiceGeneratorTests.java b/conjure-java-core/src/test/java/com/palantir/conjure/java/DialogueServiceGeneratorTests.java index f93f94ec5..3ad7f6e66 100644 --- a/conjure-java-core/src/test/java/com/palantir/conjure/java/DialogueServiceGeneratorTests.java +++ b/conjure-java-core/src/test/java/com/palantir/conjure/java/DialogueServiceGeneratorTests.java @@ -24,6 +24,7 @@ import com.palantir.conjure.defs.Conjure; import com.palantir.conjure.java.services.dialogue.DialogueServiceGenerator; import com.palantir.conjure.java.types.ErrorGenerator; +import com.palantir.conjure.java.types.ObjectGenerator; import com.palantir.conjure.spec.ConjureDefinition; import java.io.File; import java.io.IOException; @@ -87,6 +88,8 @@ public void generateServiceWithResultTypes() throws IOException { List files = new GenerationCoordinator( MoreExecutors.directExecutor(), ImmutableSet.of( + // To generate the OptionalBinaryResponseMode enum used in ete tests. + new ObjectGenerator(Options.empty()), new ErrorGenerator(Options.builder() .useImmutableBytes(true) .excludeEmptyOptionals(true) diff --git a/conjure-java-core/src/test/java/com/palantir/conjure/java/ErrorResource.java b/conjure-java-core/src/test/java/com/palantir/conjure/java/ErrorResource.java index df7d0b595..a552907a7 100644 --- a/conjure-java-core/src/test/java/com/palantir/conjure/java/ErrorResource.java +++ b/conjure-java-core/src/test/java/com/palantir/conjure/java/ErrorResource.java @@ -16,30 +16,86 @@ package com.palantir.conjure.java; -import com.palantir.another.EndpointSpecificServerErrors.DifferentPackage; +import com.palantir.conjure.java.undertow.lib.BinaryResponseBody; +import com.palantir.product.EndpointSpecificServerErrors; import com.palantir.product.EndpointSpecificServerErrors.EndpointError; +import com.palantir.product.EndpointSpecificTwoServerErrors; import com.palantir.product.EndpointSpecificTwoServerErrors.DifferentNamespace; +import com.palantir.product.OptionalBinaryResponseMode; +import com.palantir.product.TestServerErrors; import com.palantir.product.TestServerErrors.InvalidArgument; import com.palantir.product.TestServerErrors.NotFound; import com.palantir.product.UndertowErrorService; import com.palantir.tokens.auth.AuthHeader; +import java.nio.charset.StandardCharsets; +import java.util.Optional; interface ErrorResource { class Impl implements UndertowErrorService { + 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("imported error", "type def"); + } + return SUCCESS; + } + + @Override + public String testMultipleErrorsAndPackages(AuthHeader authHeader, Optional errorToThrow) + throws InvalidArgument, NotFound, DifferentNamespace, + com.palantir.another.EndpointSpecificServerErrors.DifferentPackage { + 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 com.palantir.another.EndpointSpecificServerErrors.differentPackage(); + default: + throw new IllegalArgumentException("Unknown error: " + error); + } + } + return SUCCESS; + } + @Override - public String testBasicError(AuthHeader authHeader) { - return "HELLO"; + public void testEmptyBody(AuthHeader authHeader, boolean shouldThrowError) throws InvalidArgument { + if (shouldThrowError) { + throw TestServerErrors.invalidArgument("field", "value"); + } } @Override - public String testImportedError(AuthHeader authHeader) throws EndpointError { - return ""; + 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 String testMultipleErrorsAndPackages(AuthHeader authHeader) - throws InvalidArgument, NotFound, DifferentNamespace, DifferentPackage { - return ""; + 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/com/palantir/conjure/java/UndertowServiceEteTest.java b/conjure-java-core/src/test/java/com/palantir/conjure/java/UndertowServiceEteTest.java index 0f396c780..49674e11a 100644 --- a/conjure-java-core/src/test/java/com/palantir/conjure/java/UndertowServiceEteTest.java +++ b/conjure-java-core/src/test/java/com/palantir/conjure/java/UndertowServiceEteTest.java @@ -41,12 +41,30 @@ import com.palantir.conjure.spec.ConjureDefinition; import com.palantir.dialogue.BinaryRequestBody; import com.palantir.dialogue.clients.DialogueClients; +import com.palantir.product.EmptyPathService; +import com.palantir.product.EmptyPathServiceEndpoints; +import com.palantir.product.EndpointSpecificErrors; +import com.palantir.product.EndpointSpecificTwoErrors; +import com.palantir.product.ErrorServiceBlocking; import com.palantir.product.ErrorServiceBlocking.TestBasicErrorResponse; +import com.palantir.product.ErrorServiceBlocking.TestBinaryResponse; +import com.palantir.product.ErrorServiceBlocking.TestEmptyBodyResponse; +import com.palantir.product.ErrorServiceBlocking.TestImportedErrorResponse; +import com.palantir.product.ErrorServiceBlocking.TestMultipleErrorsAndPackagesResponse; +import com.palantir.product.ErrorServiceBlocking.TestOptionalBinaryResponse; +import com.palantir.product.ErrorServiceEndpoints; +import com.palantir.product.EteBinaryServiceBlocking; +import com.palantir.product.EteBinaryServiceEndpoints; +import com.palantir.product.EteServiceAsync; +import com.palantir.product.EteServiceBlocking; +import com.palantir.product.EteServiceEndpoints; +import com.palantir.product.NestedStringAliasExample; +import com.palantir.product.OptionalBinaryResponseMode; +import com.palantir.product.SimpleEnum; +import com.palantir.product.StringAliasExample; +import com.palantir.product.TestErrors; import com.palantir.ri.ResourceIdentifier; import com.palantir.tokens.auth.AuthHeader; -import dialogue.com.palantir.product.EteBinaryServiceBlocking; -import dialogue.com.palantir.product.EteServiceAsync; -import dialogue.com.palantir.product.EteServiceBlocking; import io.undertow.Handlers; import io.undertow.Undertow; import io.undertow.UndertowOptions; @@ -68,7 +86,6 @@ import java.util.Collections; import java.util.Optional; import java.util.concurrent.ThreadLocalRandom; -import jersey.com.palantir.product.EmptyPathService; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -76,14 +93,11 @@ import org.junit.jupiter.api.io.TempDir; import org.junit.jupiter.api.parallel.Execution; import org.junit.jupiter.api.parallel.ExecutionMode; -import undertow.com.palantir.product.EmptyPathServiceEndpoints; -import undertow.com.palantir.product.ErrorServiceEndpoints; -import undertow.com.palantir.product.EteBinaryServiceEndpoints; -import undertow.com.palantir.product.EteServiceEndpoints; @Execution(ExecutionMode.CONCURRENT) public final class UndertowServiceEteTest extends TestBase { private static final ObjectMapper CLIENT_OBJECT_MAPPER = ObjectMappers.newClientObjectMapper(); + private static final AuthHeader AUTH_HEADER = AuthHeader.valueOf("authHeader"); @TempDir public static File folder; @@ -91,12 +105,13 @@ public final class UndertowServiceEteTest extends TestBase { private static Undertow server; private final EteServiceBlocking client; - // TODO(pm): this should be in a different test. - private final com.palantir.product.ErrorServiceBlocking errorServiceClient; + private final EteServiceAsync asyncClient; private final EteBinaryServiceBlocking binaryClient; + private final ErrorServiceBlocking errorServiceClient; + private static int port; public UndertowServiceEteTest() { @@ -142,14 +157,6 @@ public void jaxrs_client_can_make_a_call_to_an_empty_path() { assertThat(emptyPathClient.emptyPath()).isTrue(); } - @Test - public void error_client_returns_result() { - TestBasicErrorResponse result = errorServiceClient.testBasicError(AuthHeader.valueOf("authHeader")); - assertThat(result).isInstanceOfSatisfying(TestBasicErrorResponse.Success.class, success -> { - assertThat(success.value()).isEqualTo("HELLO"); - }); - } - @Test public void client_can_retrieve_a_string_from_a_server() { assertThat(client.string(AuthHeader.valueOf("authHeader"))).isEqualTo("Hello, world!"); @@ -563,6 +570,172 @@ public void testListOfNull() { }); } + @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("type def"); + assertThat(params.typeName()).isEqualTo("imported error"); + }); + }); + } + + @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(com.palantir.another.EndpointSpecificErrors.DIFFERENT_PACKAGE + .code() + .name()); + assertThat(error.getErrorName()) + .isEqualTo(com.palantir.another.EndpointSpecificErrors.DIFFERENT_PACKAGE.name()); + }); + } + + @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"); + } + @BeforeAll public static void beforeClass() throws IOException { ConjureDefinition def = Conjure.parse(ImmutableList.of( @@ -576,6 +749,7 @@ public static void beforeClass() throws IOException { .nonNullCollections(true) .excludeEmptyOptionals(true) .jetbrainsContractAnnotations(true) + .generateDialogueEndpointErrorResultTypes(true) .build(); List files = new GenerationCoordinator( MoreExecutors.directExecutor(), 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 2c7a95e8f..f2aecf388 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,12 @@ types: importedErrors: errors-for-endpoints.yml definitions: default-package: com.palantir.product + objects: + OptionalBinaryResponseMode: + values: + - PRESENT + - ABSENT + - ERROR errors: InvalidArgument: namespace: Test @@ -21,20 +27,26 @@ services: 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 @@ -43,16 +55,22 @@ services: - importedErrors.DifferentNamespace - importedErrors.DifferentPackage testEmptyBody: - http: GET /empty + http: POST /empty + args: + shouldThrowError: boolean errors: - InvalidArgument testBinary: - http: GET /binary + http: POST /binary + args: + shouldThrowError: boolean returns: binary errors: - InvalidArgument testOptionalBinary: - http: GET /optional-binary + http: POST /optional-binary + args: + mode: OptionalBinaryResponseMode returns: optional errors: - InvalidArgument diff --git a/conjure-lib/src/main/java/com/palantir/conjure/java/lib/internal/ConjureErrors.java b/conjure-lib/src/main/java/com/palantir/conjure/java/lib/internal/ConjureErrors.java deleted file mode 100644 index b3d8a0363..000000000 --- a/conjure-lib/src/main/java/com/palantir/conjure/java/lib/internal/ConjureErrors.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * (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 com.palantir.conjure.java.lib.internal; - -import com.palantir.logsafe.Safe; - -public final class ConjureErrors { - private ConjureErrors() {} - - // This is used in generated code when generating errors which don't have any parameters. - public static final EmptyErrorParameters EMPTY_ERROR_PARAMETERS_INSTANCE = new EmptyErrorParameters(); - - public record EmptyErrorParameters() {} - - public abstract static class BaseEndpointError { - @Safe - private final String errorCode; - - @Safe - private final String errorName; - - @Safe - private final String errorInstanceId; - - private final T params; - - protected BaseEndpointError(String errorCode, String errorName, String errorInstanceId, T params) { - this.errorCode = errorCode; - this.errorName = errorName; - this.errorInstanceId = errorInstanceId; - this.params = params; - } - - public final String getErrorCode() { - return errorCode; - } - - public final String getErrorName() { - return errorName; - } - - public final String getErrorInstanceId() { - return errorInstanceId; - } - - public final T getParams() { - return params; - } - } -} From d5f3afcac8a853f5185237209e4d650d073e9680 Mon Sep 17 00:00:00 2001 From: Pritham Marupaka Date: Mon, 27 Jan 2025 17:47:12 -0500 Subject: [PATCH 08/24] nits --- .../product/ErrorServiceBlocking.java | 3 ++ .../DefaultStaticFactoryMethodGenerator.java | 29 +++++----- .../dialogue/DialogueInterfaceGenerator.java | 2 + .../conjure/java/types/ErrorGenerator.java | 14 ++--- .../java/util/ErrorGenerationUtils.java | 54 ++++++++++--------- .../palantir/conjure/java/ErrorResource.java | 2 +- .../com/palantir/conjure/java/TestBase.java | 4 +- .../conjure/java/UndertowServiceEteTest.java | 4 +- 8 files changed, 60 insertions(+), 52 deletions(-) diff --git a/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/ErrorServiceBlocking.java b/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/ErrorServiceBlocking.java index c425757c9..feccf29cf 100644 --- a/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/ErrorServiceBlocking.java +++ b/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/ErrorServiceBlocking.java @@ -25,6 +25,9 @@ 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; 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 0a86e8f32..f97ed99f5 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 @@ -208,11 +208,26 @@ private CodeBlock constructDeserializer(Optional type, TypeName className) 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, className, responseType)); + deserializerBuilder.add(")"); + return deserializerBuilder.build(); + } + + private CodeBlock buildArgsForEndpointErrorDeserializer( + EndpointDefinition endpointDefinition, TypeName className, 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 : endpointDef.getErrors()) { + for (EndpointError err : endpointDefinition.getErrors()) { ErrorTypeName errorTypeName = err.getError(); String errorName = errorTypeName.getName(); ClassName errorClass = ClassName.get( @@ -223,17 +238,7 @@ private CodeBlock constructDeserializerWithEndpointErrors( ".error($T.name(), new $T<$T.$L>() {})", errorClass, TypeMarker.class, responseType, errorName); } deserializerArgsBuilder.add(".build()"); - 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(deserializerArgsBuilder.build()); - deserializerBuilder.add(")"); - return deserializerBuilder.build(); + return deserializerArgsBuilder.build(); } private static boolean shouldGenerateDialogueEndpointErrorResultTypesForEndpoint( 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 ddc0b92cb..6c2b1c06f 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 @@ -320,6 +320,8 @@ private MethodSpec errorTypeConstructor( .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) 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 3d275da76..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 @@ -177,27 +177,23 @@ private static List generateErrorParameterRecords( List errorTypeDefinitions, TypeMapper typeMapper) { return errorTypeDefinitions.stream() .map(errorDefinition -> generateErrorParameterRecord(errorDefinition, typeMapper)) - .filter(Optional::isPresent) - .map(Optional::get) .toList(); } - private static Optional generateErrorParameterRecord( - ErrorDefinition errorDefinition, TypeMapper typeMapper) { + 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.buildParameterWithSafetyAnnotationWithJsonProperty( - typeMapper, fieldDef, true)); + ctorBuilder.addParameter( + ErrorGenerationUtils.buildParameterWithSafetyAnnotationAndJsonProperty(typeMapper, fieldDef, true)); } for (FieldDefinition fieldDef : errorDefinition.getUnsafeArgs()) { - ctorBuilder.addParameter(ErrorGenerationUtils.buildParameterWithSafetyAnnotationWithJsonProperty( + ctorBuilder.addParameter(ErrorGenerationUtils.buildParameterWithSafetyAnnotationAndJsonProperty( typeMapper, fieldDef, false)); } - return Optional.of( - parametersRecordBuilder.recordConstructor(ctorBuilder.build()).build()); + return parametersRecordBuilder.recordConstructor(ctorBuilder.build()).build(); } private static MethodSpec generateExceptionFactory( 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 e0e4a121a..ce9119dd3 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 @@ -53,18 +53,6 @@ import org.apache.commons.lang3.StringUtils; public final class ErrorGenerationUtils { - /** - * 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 String errorParametersClassName(String errorName) { - return errorName + "Parameters"; - } - public static MethodSpec privateConstructor() { return MethodSpec.constructorBuilder().addModifiers(Modifier.PRIVATE).build(); } @@ -77,6 +65,22 @@ 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"; + } + + /** + * 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() @@ -180,18 +184,21 @@ public static void addAllParametersWithSafetyAnnotationsToMethodBuilder( public static ParameterSpec buildParameterWithSafetyAnnotation( 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); - ParameterSpec.Builder parameterBuilder = ParameterSpec.builder(argType, argName); - argDefinition - .getDocs() - .ifPresent(docs -> - parameterBuilder.addJavadoc("$L", StringUtils.appendIfMissing(Javadoc.render(docs), "\n"))); + 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(); } - public static ParameterSpec buildParameterWithSafetyAnnotationWithJsonProperty( + 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(); @@ -201,10 +208,7 @@ public static ParameterSpec buildParameterWithSafetyAnnotationWithJsonProperty( .getDocs() .ifPresent(docs -> parameterBuilder.addJavadoc("$L", StringUtils.appendIfMissing(Javadoc.render(docs), "\n"))); - parameterBuilder.addAnnotation(AnnotationSpec.builder(JsonProperty.class) - .addMember("value", "$S", argDefinition.getFieldName()) - .build()); - return parameterBuilder.build(); + return parameterBuilder; } // Conditional factory method diff --git a/conjure-java-core/src/test/java/com/palantir/conjure/java/ErrorResource.java b/conjure-java-core/src/test/java/com/palantir/conjure/java/ErrorResource.java index a552907a7..9d631c53e 100644 --- a/conjure-java-core/src/test/java/com/palantir/conjure/java/ErrorResource.java +++ b/conjure-java-core/src/test/java/com/palantir/conjure/java/ErrorResource.java @@ -45,7 +45,7 @@ public String testBasicError(AuthHeader authHeader, boolean shouldThrowError) th @Override public String testImportedError(AuthHeader authHeader, boolean shouldThrowError) throws EndpointError { if (shouldThrowError) { - throw EndpointSpecificServerErrors.endpointError("imported error", "type def"); + throw EndpointSpecificServerErrors.endpointError("typeName", "typeDef"); } return SUCCESS; } diff --git a/conjure-java-core/src/test/java/com/palantir/conjure/java/TestBase.java b/conjure-java-core/src/test/java/com/palantir/conjure/java/TestBase.java index 0a2c2bb43..68d5438ca 100644 --- a/conjure-java-core/src/test/java/com/palantir/conjure/java/TestBase.java +++ b/conjure-java-core/src/test/java/com/palantir/conjure/java/TestBase.java @@ -64,7 +64,7 @@ protected static void validateGeneratedOutput(Path folder, List files, Pat for (Path file : files) { Path relativePath = folder.relativize(file); Path output = outputDir.resolve(relativePath); - if (Boolean.valueOf(System.getProperty("recreate", "false"))) { + if (Boolean.parseBoolean(System.getProperty("recreate", "false"))) { Files.createDirectories(relativePath.getParent()); Files.deleteIfExists(output); Files.copy(file, output); @@ -72,6 +72,4 @@ protected static void validateGeneratedOutput(Path folder, List files, Pat assertThat(readFromFile(file)).isEqualTo(readFromFile(output)); } } - - } diff --git a/conjure-java-core/src/test/java/com/palantir/conjure/java/UndertowServiceEteTest.java b/conjure-java-core/src/test/java/com/palantir/conjure/java/UndertowServiceEteTest.java index 49674e11a..9e95fc893 100644 --- a/conjure-java-core/src/test/java/com/palantir/conjure/java/UndertowServiceEteTest.java +++ b/conjure-java-core/src/test/java/com/palantir/conjure/java/UndertowServiceEteTest.java @@ -598,8 +598,8 @@ public void error_client_returns_imported_error() { .isEqualTo(EndpointSpecificErrors.ENDPOINT_ERROR.code().name()); assertThat(error.getErrorName()).isEqualTo(EndpointSpecificErrors.ENDPOINT_ERROR.name()); assertThat(error.getParams()).satisfies(params -> { - assertThat(params.typeDef()).isEqualTo("type def"); - assertThat(params.typeName()).isEqualTo("imported error"); + assertThat(params.typeDef()).isEqualTo("typeDef"); + assertThat(params.typeName()).isEqualTo("typeName"); }); }); } From 7bdb470b85ede5102cfecb30a39d214fa882ee10 Mon Sep 17 00:00:00 2001 From: Pritham Marupaka Date: Tue, 28 Jan 2025 11:35:00 -0500 Subject: [PATCH 09/24] ConjureErrors.BaseEndpointError moved to EndpointError --- .../product/ErrorServiceBlocking.java | 22 +++++++++---------- .../palantir/product/ErrorServiceAsync.java | 22 +++++++++---------- .../dialogue/DialogueInterfaceGenerator.java | 4 ++-- 3 files changed, 22 insertions(+), 26 deletions(-) diff --git a/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/ErrorServiceBlocking.java b/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/ErrorServiceBlocking.java index feccf29cf..f3b73ba8e 100644 --- a/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/ErrorServiceBlocking.java +++ b/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/ErrorServiceBlocking.java @@ -6,7 +6,6 @@ import com.google.errorprone.annotations.MustBeClosed; import com.palantir.conjure.java.lib.internal.ClientEndpoint; import com.palantir.dialogue.Channel; -import com.palantir.dialogue.ConjureErrors; import com.palantir.dialogue.ConjureRuntime; import com.palantir.dialogue.Deserializer; import com.palantir.dialogue.DeserializerArgs; @@ -15,6 +14,7 @@ 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; @@ -261,7 +261,7 @@ record Success(@JsonValue String value) implements TestBasicErrorResponse { } } - final class InvalidArgument extends ConjureErrors.BaseEndpointError + final class InvalidArgument extends EndpointError implements TestBasicErrorResponse { @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) InvalidArgument( @@ -282,7 +282,7 @@ record Success(@JsonValue String value) implements TestImportedErrorResponse { } final class EndpointError - extends ConjureErrors.BaseEndpointError + extends com.palantir.dialogue.EndpointError implements TestImportedErrorResponse { @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) EndpointError( @@ -306,7 +306,7 @@ record Success(@JsonValue String value) implements TestMultipleErrorsAndPackages } } - final class InvalidArgument extends ConjureErrors.BaseEndpointError + final class InvalidArgument extends EndpointError implements TestMultipleErrorsAndPackagesResponse { @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) InvalidArgument( @@ -317,7 +317,7 @@ final class InvalidArgument extends ConjureErrors.BaseEndpointError + final class NotFound extends EndpointError implements TestMultipleErrorsAndPackagesResponse { @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) NotFound( @@ -328,8 +328,7 @@ final class NotFound extends ConjureErrors.BaseEndpointError + final class DifferentNamespace extends EndpointError implements TestMultipleErrorsAndPackagesResponse { @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) DifferentNamespace( @@ -344,8 +343,7 @@ final class DifferentNamespace } final class DifferentPackage - extends ConjureErrors.BaseEndpointError< - com.palantir.another.EndpointSpecificErrors.DifferentPackageParameters> + extends EndpointError implements TestMultipleErrorsAndPackagesResponse { @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) DifferentPackage( @@ -364,7 +362,7 @@ sealed interface TestEmptyBodyResponse permits TestEmptyBodyResponse.Success, TestEmptyBodyResponse.InvalidArgument { record Success() implements TestEmptyBodyResponse {} - final class InvalidArgument extends ConjureErrors.BaseEndpointError + final class InvalidArgument extends EndpointError implements TestEmptyBodyResponse { @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) InvalidArgument( @@ -392,7 +390,7 @@ public void close() throws IOException { } } - final class InvalidArgument extends ConjureErrors.BaseEndpointError + final class InvalidArgument extends EndpointError implements TestBinaryResponse { @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) InvalidArgument( @@ -422,7 +420,7 @@ public void close() throws IOException { } } - final class InvalidArgument extends ConjureErrors.BaseEndpointError + final class InvalidArgument extends EndpointError implements TestOptionalBinaryResponse { @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) InvalidArgument( diff --git a/conjure-java-core/src/integrationInput/java/com/palantir/product/ErrorServiceAsync.java b/conjure-java-core/src/integrationInput/java/com/palantir/product/ErrorServiceAsync.java index 9eed16273..f153a526c 100644 --- a/conjure-java-core/src/integrationInput/java/com/palantir/product/ErrorServiceAsync.java +++ b/conjure-java-core/src/integrationInput/java/com/palantir/product/ErrorServiceAsync.java @@ -7,7 +7,6 @@ import com.google.errorprone.annotations.MustBeClosed; import com.palantir.conjure.java.lib.internal.ClientEndpoint; import com.palantir.dialogue.Channel; -import com.palantir.dialogue.ConjureErrors; import com.palantir.dialogue.ConjureRuntime; import com.palantir.dialogue.Deserializer; import com.palantir.dialogue.DeserializerArgs; @@ -16,6 +15,7 @@ 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; @@ -264,7 +264,7 @@ record Success(@JsonValue String value) implements TestBasicErrorResponse { } } - final class InvalidArgument extends ConjureErrors.BaseEndpointError + final class InvalidArgument extends EndpointError implements TestBasicErrorResponse { @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) InvalidArgument( @@ -285,7 +285,7 @@ record Success(@JsonValue String value) implements TestImportedErrorResponse { } final class EndpointError - extends ConjureErrors.BaseEndpointError + extends com.palantir.dialogue.EndpointError implements TestImportedErrorResponse { @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) EndpointError( @@ -309,7 +309,7 @@ record Success(@JsonValue String value) implements TestMultipleErrorsAndPackages } } - final class InvalidArgument extends ConjureErrors.BaseEndpointError + final class InvalidArgument extends EndpointError implements TestMultipleErrorsAndPackagesResponse { @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) InvalidArgument( @@ -320,7 +320,7 @@ final class InvalidArgument extends ConjureErrors.BaseEndpointError + final class NotFound extends EndpointError implements TestMultipleErrorsAndPackagesResponse { @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) NotFound( @@ -331,8 +331,7 @@ final class NotFound extends ConjureErrors.BaseEndpointError + final class DifferentNamespace extends EndpointError implements TestMultipleErrorsAndPackagesResponse { @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) DifferentNamespace( @@ -347,8 +346,7 @@ final class DifferentNamespace } final class DifferentPackage - extends ConjureErrors.BaseEndpointError< - com.palantir.another.EndpointSpecificErrors.DifferentPackageParameters> + extends EndpointError implements TestMultipleErrorsAndPackagesResponse { @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) DifferentPackage( @@ -367,7 +365,7 @@ sealed interface TestEmptyBodyResponse permits TestEmptyBodyResponse.Success, TestEmptyBodyResponse.InvalidArgument { record Success() implements TestEmptyBodyResponse {} - final class InvalidArgument extends ConjureErrors.BaseEndpointError + final class InvalidArgument extends EndpointError implements TestEmptyBodyResponse { @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) InvalidArgument( @@ -395,7 +393,7 @@ public void close() throws IOException { } } - final class InvalidArgument extends ConjureErrors.BaseEndpointError + final class InvalidArgument extends EndpointError implements TestBinaryResponse { @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) InvalidArgument( @@ -425,7 +423,7 @@ public void close() throws IOException { } } - final class InvalidArgument extends ConjureErrors.BaseEndpointError + final class InvalidArgument extends EndpointError implements TestOptionalBinaryResponse { @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) InvalidArgument( 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 6c2b1c06f..45d46ff51 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 @@ -40,7 +40,6 @@ import com.palantir.conjure.spec.ServiceDefinition; import com.palantir.conjure.visitor.TypeVisitor; import com.palantir.dialogue.Channel; -import com.palantir.dialogue.ConjureErrors.BaseEndpointError; import com.palantir.dialogue.ConjureRuntime; import com.palantir.dialogue.DialogueService; import com.palantir.dialogue.DialogueServiceFactory; @@ -250,7 +249,8 @@ private TypeSpec constructEndpointErrorType( ErrorGenerationUtils.errorParametersClassName( endpointError.getError().getName())); endpointErrorTypeBuilder - .superclass(ParameterizedTypeName.get(ClassName.get(BaseEndpointError.class), parametersClassName)) + .superclass(ParameterizedTypeName.get( + ClassName.get(com.palantir.dialogue.EndpointError.class), parametersClassName)) .addMethod(errorTypeConstructor(endpointError.getError(), parametersClassName, errorTypesClassName)); return endpointErrorTypeBuilder.build(); } From d27ea97f657046fc7b5b62790d7dcf9ce1fe98e4 Mon Sep 17 00:00:00 2001 From: Pritham Marupaka Date: Mon, 3 Feb 2025 11:17:51 -0500 Subject: [PATCH 10/24] Add flag to CLI --- .../com/palantir/conjure/java/cli/ConjureJavaCli.java | 8 ++++++++ 1 file changed, 8 insertions(+) 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(); } From 9206e8f3be0758b17a88f39d008ba695e44553ed Mon Sep 17 00:00:00 2001 From: Pritham Marupaka Date: Mon, 3 Feb 2025 11:50:31 -0500 Subject: [PATCH 11/24] Don't generate result types when endpoint does not have errors --- .../DefaultStaticFactoryMethodGenerator.java | 15 ++++++--------- .../dialogue/DialogueInterfaceGenerator.java | 16 +++++++++++----- .../conjure/java/util/ErrorGenerationUtils.java | 6 ++++++ 3 files changed, 23 insertions(+), 14 deletions(-) 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 f97ed99f5..d3f34c4e6 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 @@ -178,7 +178,8 @@ private Optional serializer(EndpointName endpointName, Type type) { private Optional deserializer( TypeName responseType, EndpointDefinition endpointDef, EndpointName endpointName, Optional type) { TypeName className = Primitives.box(returnTypes.baseType(type)); - boolean generateResultTypes = shouldGenerateDialogueEndpointErrorResultTypesForEndpoint(options, endpointDef); + boolean generateResultTypes = ErrorGenerationUtils.shouldGenerateResultTypesForEndpoint( + options.generateDialogueEndpointErrorResultTypes(), endpointDef); if (returnTypes.isBinaryOrOptionalBinary(className) && !generateResultTypes) { return Optional.empty(); @@ -241,12 +242,6 @@ private CodeBlock buildArgsForEndpointErrorDeserializer( return deserializerArgsBuilder.build(); } - private static boolean shouldGenerateDialogueEndpointErrorResultTypesForEndpoint( - Options options, EndpointDefinition endpointDefinition) { - return options.generateDialogueEndpointErrorResultTypes() - && !endpointDefinition.getErrors().isEmpty(); - } - private MethodSpec clientImpl(ClassName className, EndpointDefinition def) { List params = parameterTypes.implementationMethodParams(def); MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder( @@ -279,7 +274,8 @@ private MethodSpec clientImpl(ClassName className, EndpointDefinition def) { .build(); String codeBlock = methodType.switchBy( "$L.clients().callBlocking($L, $L.build(), $L);", "$L.clients().call($L, $L.build" + "(), $L);"); - boolean generateResultTypes = shouldGenerateDialogueEndpointErrorResultTypesForEndpoint(options, def); + boolean generateResultTypes = ErrorGenerationUtils.shouldGenerateResultTypesForEndpoint( + options.generateDialogueEndpointErrorResultTypes(), def); CodeBlock execute = CodeBlock.of( codeBlock, StaticFactoryMethodGenerator.RUNTIME, @@ -303,7 +299,8 @@ private MethodSpec clientImpl(ClassName className, EndpointDefinition def) { } private TypeName getReturnType(EndpointDefinition def, ClassName className) { - if (shouldGenerateDialogueEndpointErrorResultTypesForEndpoint(options, def)) { + if (ErrorGenerationUtils.shouldGenerateResultTypesForEndpoint( + options.generateDialogueEndpointErrorResultTypes(), def)) { ClassName responseResultTypeName = ClassName.get( className.packageName(), className.simpleName(), 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 45d46ff51..8cd02c258 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 @@ -142,11 +142,16 @@ private JavaFile generate( packageName, className, endpoint, generateDialogueEndpointErrorResultTypes, serviceCallType)) .collect(toList())); + // Create public sealed interface for the "response" type for each of the endpoints. if (generateDialogueEndpointErrorResultTypes) { - // Create public sealed interface for the "response" type for each of the endpoints. - serviceBuilder.addTypes(def.getEndpoints().stream() - .map(endpointDef -> responseTypeForEndpoint(packageName, className, endpointDef)) - .toList()); + 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); @@ -374,7 +379,8 @@ private MethodSpec apiMethod( endpointDef, new EndpointJavaDocGenerationOptions(RequestLineJavaDoc.INCLUDE, EndpointErrorsJavaDoc.EXCLUDE)); - if (generateDialogueEndpointErrorResultTypes) { + if (ErrorGenerationUtils.shouldGenerateResultTypesForEndpoint( + generateDialogueEndpointErrorResultTypes, endpointDef)) { TypeName returnType = ClassName.get( packageName, className.simpleName(), 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 ce9119dd3..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 @@ -25,6 +25,7 @@ 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; @@ -77,6 +78,11 @@ 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. From 52563e0034209e0c4d441553ec4ff7f99b088733 Mon Sep 17 00:00:00 2001 From: Pritham Marupaka Date: Tue, 4 Feb 2025 10:36:52 -0500 Subject: [PATCH 12/24] use dialogue's new RC --- build.gradle | 1 - versions.lock | 14 +++++++------- versions.props | 2 +- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/build.gradle b/build.gradle index e87e3c0d8..f91f6cd7d 100644 --- a/build.gradle +++ b/build.gradle @@ -58,7 +58,6 @@ allprojects { group 'com.palantir.conjure.java' repositories { - mavenLocal() mavenCentral() { metadataSources { mavenPom(); ignoreGradleMetadataRedirection() } } } diff --git a/versions.lock b/versions.lock index 8c9f7105c..737489610 100644 --- a/versions.lock +++ b/versions.lock @@ -29,13 +29,13 @@ com.palantir.conjure.java.runtime:conjure-java-jackson-optimizations:8.16.0 (1 c com.palantir.conjure.java.runtime:conjure-java-jackson-serialization:8.16.0 (4 constraints: f5461996) com.palantir.conjure.java.runtime:keystores:8.16.0 (3 constraints: ea297a45) com.palantir.deadlines:deadlines:0.8.0 (1 constraints: 0a050336) -com.palantir.dialogue:dialogue-apache-hc5-client:4.8.0-rc1 (2 constraints: 442a2d5c) -com.palantir.dialogue:dialogue-blocking-channels:4.8.0-rc1 (2 constraints: dd25de90) -com.palantir.dialogue:dialogue-clients:4.8.0-rc1 (1 constraints: 4106504d) -com.palantir.dialogue:dialogue-core:4.8.0-rc1 (3 constraints: 463ffd25) -com.palantir.dialogue:dialogue-futures:4.8.0-rc1 (3 constraints: d436dd9c) -com.palantir.dialogue:dialogue-serde:4.8.0-rc1 (3 constraints: 84306a91) -com.palantir.dialogue:dialogue-target:4.8.0-rc1 (7 constraints: f57ea535) +com.palantir.dialogue:dialogue-apache-hc5-client:4.8.0-rc2 (2 constraints: 452a2e5c) +com.palantir.dialogue:dialogue-blocking-channels:4.8.0-rc2 (2 constraints: df251191) +com.palantir.dialogue:dialogue-clients:4.8.0-rc2 (1 constraints: 4206514d) +com.palantir.dialogue:dialogue-core:4.8.0-rc2 (3 constraints: 483f3226) +com.palantir.dialogue:dialogue-futures:4.8.0-rc2 (3 constraints: d736729d) +com.palantir.dialogue:dialogue-serde:4.8.0-rc2 (3 constraints: 8630e391) +com.palantir.dialogue:dialogue-target:4.8.0-rc2 (7 constraints: fb7ef538) 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) diff --git a/versions.props b/versions.props index b27d84553..9769b1ea4 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:* = 4.8.0-rc1 +com.palantir.dialogue:* = 4.8.0-rc2 com.palantir.goethe:* = 0.14.0 com.palantir.human-readable-types:* = 1.7.0 com.palantir.javapoet:javapoet = 0.5.0 From e14b1efab3e4fca9dcfe1c928c0ee803e64aa07a Mon Sep 17 00:00:00 2001 From: svc-autorelease Date: Tue, 4 Feb 2025 17:07:18 +0000 Subject: [PATCH 13/24] Release 8.42.0-rc1 [skip ci] From a7837b2b951fd488f3d069aa74b8056347f22df6 Mon Sep 17 00:00:00 2001 From: Pritham Marupaka Date: Tue, 11 Feb 2025 14:34:16 -0500 Subject: [PATCH 14/24] Add @CheckReturnValue annotation to api methods that return results --- .../com/palantir/product/ErrorServiceBlocking.java | 7 +++++++ .../java/com/palantir/product/ErrorServiceAsync.java | 7 +++++++ .../java/services/dialogue/DialogueInterfaceGenerator.java | 3 +++ 3 files changed, 17 insertions(+) diff --git a/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/ErrorServiceBlocking.java b/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/ErrorServiceBlocking.java index f3b73ba8e..ae4616d66 100644 --- a/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/ErrorServiceBlocking.java +++ b/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/ErrorServiceBlocking.java @@ -3,6 +3,7 @@ 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; @@ -36,27 +37,33 @@ 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. */ diff --git a/conjure-java-core/src/integrationInput/java/com/palantir/product/ErrorServiceAsync.java b/conjure-java-core/src/integrationInput/java/com/palantir/product/ErrorServiceAsync.java index f153a526c..0e119b9f0 100644 --- a/conjure-java-core/src/integrationInput/java/com/palantir/product/ErrorServiceAsync.java +++ b/conjure-java-core/src/integrationInput/java/com/palantir/product/ErrorServiceAsync.java @@ -4,6 +4,7 @@ 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; @@ -37,27 +38,33 @@ 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); 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 8cd02c258..45bc903d6 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 @@ -23,6 +23,7 @@ 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; @@ -387,6 +388,8 @@ private MethodSpec apiMethod( ErrorGenerationUtils.endpointResponseResultTypeName(endpointDef.getEndpointName())); methodBuilder.returns(serviceCallType.switchBy( returnType, ParameterizedTypeName.get(ClassName.get(ListenableFuture.class), returnType))); + // The result type should be used by clients + methodBuilder.addAnnotation(CheckReturnValue.class); } else { TypeName returnType = serviceCallType.switchBy( returnTypes.baseType(endpointDef.getReturns()), returnTypes.async(endpointDef.getReturns())); From b30afaa6e32dc80d2cde5501b85f46de05e0c804 Mon Sep 17 00:00:00 2001 From: Pritham Marupaka Date: Mon, 24 Feb 2025 17:23:41 -0500 Subject: [PATCH 15/24] Migrate tests to new ParameterizedConjureGenerationTest --- .../another/EndpointSpecificErrors.java | 23 + .../product/DialogueErrorEndpoints.java | 198 ++++++++ .../product/EndpointSpecificErrors.java | 27 ++ .../product/EndpointSpecificTwoErrors.java | 23 + .../palantir/product/ErrorServiceAsync.java | 447 ++++++++++++++++++ .../product/ErrorServiceBlocking.java | 444 +++++++++++++++++ .../product/OptionalBinaryResponseMode.java | 239 ++++++++++ .../com/palantir/product/TestErrors.java | 36 ++ .../com/palantir/conjure/java/Options.java | 2 +- .../java/DialogueServiceGeneratorTests.java | 37 +- .../conjure/java/parameterized/TestCases.java | 12 + 11 files changed, 1464 insertions(+), 24 deletions(-) create mode 100644 conjure-java-core/src/integrationInput/java/dialogueendpointresulttypes/com/palantir/another/EndpointSpecificErrors.java create mode 100644 conjure-java-core/src/integrationInput/java/dialogueendpointresulttypes/com/palantir/product/DialogueErrorEndpoints.java create mode 100644 conjure-java-core/src/integrationInput/java/dialogueendpointresulttypes/com/palantir/product/EndpointSpecificErrors.java create mode 100644 conjure-java-core/src/integrationInput/java/dialogueendpointresulttypes/com/palantir/product/EndpointSpecificTwoErrors.java create mode 100644 conjure-java-core/src/integrationInput/java/dialogueendpointresulttypes/com/palantir/product/ErrorServiceAsync.java create mode 100644 conjure-java-core/src/integrationInput/java/dialogueendpointresulttypes/com/palantir/product/ErrorServiceBlocking.java create mode 100644 conjure-java-core/src/integrationInput/java/dialogueendpointresulttypes/com/palantir/product/OptionalBinaryResponseMode.java create mode 100644 conjure-java-core/src/integrationInput/java/dialogueendpointresulttypes/com/palantir/product/TestErrors.java 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/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/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/ErrorServiceAsync.java b/conjure-java-core/src/integrationInput/java/dialogueendpointresulttypes/com/palantir/product/ErrorServiceAsync.java new file mode 100644 index 000000000..43cbebf9b --- /dev/null +++ b/conjure-java-core/src/integrationInput/java/dialogueendpointresulttypes/com/palantir/product/ErrorServiceAsync.java @@ -0,0 +1,447 @@ +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.product.EndpointSpecificErrors; +import com.palantir.product.EndpointSpecificTwoErrors; +import com.palantir.product.TestErrors; +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( + com.palantir.another.EndpointSpecificErrors.DIFFERENT_PACKAGE.name(), + new TypeMarker() {}) + .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 { + 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 + implements TestMultipleErrorsAndPackagesResponse { + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) + DifferentPackage( + @JsonProperty("errorCode") @Safe String errorCode, + @JsonProperty("errorInstanceId") @Safe String errorInstanceId) { + super( + errorCode, + com.palantir.another.EndpointSpecificErrors.DIFFERENT_PACKAGE.name(), + errorInstanceId, + new com.palantir.another.EndpointSpecificErrors.DifferentPackageParameters()); + } + } + } + + 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..b87eff2ee --- /dev/null +++ b/conjure-java-core/src/integrationInput/java/dialogueendpointresulttypes/com/palantir/product/ErrorServiceBlocking.java @@ -0,0 +1,444 @@ +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.product.EndpointSpecificErrors; +import com.palantir.product.EndpointSpecificTwoErrors; +import com.palantir.product.TestErrors; +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( + com.palantir.another.EndpointSpecificErrors.DIFFERENT_PACKAGE.name(), + new TypeMarker() {}) + .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 { + 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 + implements TestMultipleErrorsAndPackagesResponse { + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) + DifferentPackage( + @JsonProperty("errorCode") @Safe String errorCode, + @JsonProperty("errorInstanceId") @Safe String errorInstanceId) { + super( + errorCode, + com.palantir.another.EndpointSpecificErrors.DIFFERENT_PACKAGE.name(), + errorInstanceId, + new com.palantir.another.EndpointSpecificErrors.DifferentPackageParameters()); + } + } + } + + 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/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/TestErrors.java b/conjure-java-core/src/integrationInput/java/dialogueendpointresulttypes/com/palantir/product/TestErrors.java new file mode 100644 index 000000000..7718ce9a5 --- /dev/null +++ b/conjure-java-core/src/integrationInput/java/dialogueendpointresulttypes/com/palantir/product/TestErrors.java @@ -0,0 +1,36 @@ +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 TestErrors { + 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: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 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/main/java/com/palantir/conjure/java/Options.java b/conjure-java-core/src/main/java/com/palantir/conjure/java/Options.java index 97d30726a..9c9b46c2f 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 @@ -190,7 +190,7 @@ default boolean preferObjectBuilders() { /** * 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.conjure.java.lib.internal.ConjureErrors.BaseEndpointError}. + * {@link com.palantir.dialogue.EndpointError}. */ @Value.Default default boolean generateDialogueEndpointErrorResultTypes() { diff --git a/conjure-java-core/src/test/java/com/palantir/conjure/java/DialogueServiceGeneratorTests.java b/conjure-java-core/src/test/java/com/palantir/conjure/java/DialogueServiceGeneratorTests.java index 3ad7f6e66..e03585a98 100644 --- a/conjure-java-core/src/test/java/com/palantir/conjure/java/DialogueServiceGeneratorTests.java +++ b/conjure-java-core/src/test/java/com/palantir/conjure/java/DialogueServiceGeneratorTests.java @@ -23,8 +23,6 @@ import com.google.common.util.concurrent.MoreExecutors; import com.palantir.conjure.defs.Conjure; import com.palantir.conjure.java.services.dialogue.DialogueServiceGenerator; -import com.palantir.conjure.java.types.ErrorGenerator; -import com.palantir.conjure.java.types.ObjectGenerator; import com.palantir.conjure.spec.ConjureDefinition; import java.io.File; import java.io.IOException; @@ -78,30 +76,23 @@ public void testServiceGeneration_excludeDialogueAsyncInterfaces() throws IOExce .toList(); assertThat(fileNames) .noneMatch(name -> name.toLowerCase(Locale.ROOT).contains("async")); - } + } } @Test - public void generateServiceWithResultTypes() throws IOException { - ConjureDefinition def = - Conjure.parse(ImmutableList.of(new File("src/test/resources/example-endpoint-errors.yml"))); - List files = new GenerationCoordinator( - MoreExecutors.directExecutor(), - ImmutableSet.of( - // To generate the OptionalBinaryResponseMode enum used in ete tests. - new ObjectGenerator(Options.empty()), - new ErrorGenerator(Options.builder() - .useImmutableBytes(true) - .excludeEmptyOptionals(true) - .jetbrainsContractAnnotations(true) - .generateDialogueEndpointErrorResultTypes(true) - .build()), - new DialogueServiceGenerator(Options.builder() - .apiVersion("1.2.3") - .excludeDialogueAsyncInterfaces(false) - .generateDialogueEndpointErrorResultTypes(true) - .build()))) + public void testServiceGeneration_excludeDialogueAsyncInterfaces() { + List files = getGeneratedFilesForDef( + "example-service", + Options.builder().excludeDialogueAsyncInterfaces(true).build()); + List fileNames = + files.stream().map(Path::getFileName).map(Path::toString).toList(); + assertThat(fileNames).noneMatch(name -> name.toLowerCase(Locale.ROOT).contains("async")); + } + + private List getGeneratedFilesForDef(String conjureFile, Options options) { + ConjureDefinition def = Conjure.parse(ImmutableList.of(new File("src/test/resources/" + conjureFile + ".yml"))); + return new GenerationCoordinator( + MoreExecutors.directExecutor(), ImmutableSet.of(new DialogueServiceGenerator(options))) .emit(def, folder); - TestBase.validateGeneratedOutput(folder.toPath(), files, Paths.get("src/integrationInput/java")); } } 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..92b64742c 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 @@ -22,6 +22,7 @@ import com.palantir.conjure.java.parameterized.objects.ParameterizedTestCase; import java.nio.file.Path; import java.util.List; +import java.util.Set; public final class TestCases { private static final List CASES = ImmutableList.builder() @@ -303,6 +304,17 @@ 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/example-endpoint-errors.yml"))) + .options(Options.builder() + .generateDialogueEndpointErrorResultTypes(true) + .build()) + .generatorTypes(Set.of(GeneratorType.OBJECT, GeneratorType.ERROR, GeneratorType.DIALOGUE)) + .build()) .build(); public static List get() { From e7e0c1be7fe0a9d2d1d7c21e7908dab40631a29d Mon Sep 17 00:00:00 2001 From: Pritham Marupaka Date: Mon, 24 Feb 2025 17:45:55 -0500 Subject: [PATCH 16/24] Create UndertowServiceEteTest specific to dialogue endpoint result test --- .../conjure/java/UndertowServiceEteTest.java | 208 ++------------ .../palantir/conjure/java/ErrorResource.java | 2 +- .../conjure/java/UndertowServiceEteTest.java | 256 ++++++++++++++++++ 3 files changed, 277 insertions(+), 189 deletions(-) rename conjure-java-core/src/test/java/{ => dialogueendpointresulttypes}/com/palantir/conjure/java/ErrorResource.java (98%) create mode 100644 conjure-java-core/src/test/java/dialogueendpointresulttypes/com/palantir/conjure/java/UndertowServiceEteTest.java diff --git a/conjure-java-core/src/test/java/com/palantir/conjure/java/UndertowServiceEteTest.java b/conjure-java-core/src/test/java/com/palantir/conjure/java/UndertowServiceEteTest.java index 9e95fc893..fd72fad55 100644 --- a/conjure-java-core/src/test/java/com/palantir/conjure/java/UndertowServiceEteTest.java +++ b/conjure-java-core/src/test/java/com/palantir/conjure/java/UndertowServiceEteTest.java @@ -43,26 +43,14 @@ import com.palantir.dialogue.clients.DialogueClients; import com.palantir.product.EmptyPathService; import com.palantir.product.EmptyPathServiceEndpoints; -import com.palantir.product.EndpointSpecificErrors; -import com.palantir.product.EndpointSpecificTwoErrors; -import com.palantir.product.ErrorServiceBlocking; -import com.palantir.product.ErrorServiceBlocking.TestBasicErrorResponse; -import com.palantir.product.ErrorServiceBlocking.TestBinaryResponse; -import com.palantir.product.ErrorServiceBlocking.TestEmptyBodyResponse; -import com.palantir.product.ErrorServiceBlocking.TestImportedErrorResponse; -import com.palantir.product.ErrorServiceBlocking.TestMultipleErrorsAndPackagesResponse; -import com.palantir.product.ErrorServiceBlocking.TestOptionalBinaryResponse; -import com.palantir.product.ErrorServiceEndpoints; import com.palantir.product.EteBinaryServiceBlocking; import com.palantir.product.EteBinaryServiceEndpoints; import com.palantir.product.EteServiceAsync; import com.palantir.product.EteServiceBlocking; import com.palantir.product.EteServiceEndpoints; import com.palantir.product.NestedStringAliasExample; -import com.palantir.product.OptionalBinaryResponseMode; import com.palantir.product.SimpleEnum; import com.palantir.product.StringAliasExample; -import com.palantir.product.TestErrors; import com.palantir.ri.ResourceIdentifier; import com.palantir.tokens.auth.AuthHeader; import io.undertow.Handlers; @@ -79,11 +67,15 @@ import java.net.InetSocketAddress; import java.net.URL; import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.time.Instant; import java.time.OffsetDateTime; import java.time.ZoneId; import java.time.ZoneOffset; import java.util.Collections; +import java.util.List; import java.util.Optional; import java.util.concurrent.ThreadLocalRandom; import org.junit.jupiter.api.AfterAll; @@ -94,10 +86,10 @@ import org.junit.jupiter.api.parallel.Execution; import org.junit.jupiter.api.parallel.ExecutionMode; +// MARK(pm). @Execution(ExecutionMode.CONCURRENT) public final class UndertowServiceEteTest extends TestBase { private static final ObjectMapper CLIENT_OBJECT_MAPPER = ObjectMappers.newClientObjectMapper(); - private static final AuthHeader AUTH_HEADER = AuthHeader.valueOf("authHeader"); @TempDir public static File folder; @@ -105,21 +97,16 @@ public final class UndertowServiceEteTest extends TestBase { private static Undertow server; private final EteServiceBlocking client; - private final EteServiceAsync asyncClient; - private final EteBinaryServiceBlocking binaryClient; - private final ErrorServiceBlocking errorServiceClient; - private static int port; public UndertowServiceEteTest() { this.client = DialogueClients.create(EteServiceBlocking.class, clientConfiguration(port)); this.asyncClient = DialogueClients.create(EteServiceAsync.class, clientConfiguration(port)); this.binaryClient = DialogueClients.create(EteBinaryServiceBlocking.class, clientConfiguration(port)); - this.errorServiceClient = - DialogueClients.create(com.palantir.product.ErrorServiceBlocking.class, clientConfiguration(port)); + } @BeforeAll @@ -129,7 +116,6 @@ public static void before() { .services(EteServiceEndpoints.of(new UndertowEteResource())) .services(EmptyPathServiceEndpoints.of(() -> true)) .services(EteBinaryServiceEndpoints.of(new UndertowBinaryResource())) - .services(ErrorServiceEndpoints.of(new ErrorResource.Impl())) .build(); server = Undertow.builder() @@ -570,172 +556,6 @@ public void testListOfNull() { }); } - @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(com.palantir.another.EndpointSpecificErrors.DIFFERENT_PACKAGE - .code() - .name()); - assertThat(error.getErrorName()) - .isEqualTo(com.palantir.another.EndpointSpecificErrors.DIFFERENT_PACKAGE.name()); - }); - } - - @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"); - } - @BeforeAll public static void beforeClass() throws IOException { ConjureDefinition def = Conjure.parse(ImmutableList.of( @@ -749,7 +569,6 @@ public static void beforeClass() throws IOException { .nonNullCollections(true) .excludeEmptyOptionals(true) .jetbrainsContractAnnotations(true) - .generateDialogueEndpointErrorResultTypes(true) .build(); List files = new GenerationCoordinator( MoreExecutors.directExecutor(), @@ -759,7 +578,20 @@ public static void beforeClass() throws IOException { new ErrorGenerator(options), new CheckedErrorGenerator(options))) .emit(def, folder); - TestBase.validateGeneratedOutput(folder.toPath(), files, Paths.get("src/integrationInput/java")); + validateGeneratedOutput(files, Paths.get("src/integrationInput/java")); + } + + private static void validateGeneratedOutput(List files, Path outputDir) throws IOException { + for (Path file : files) { + Path relativePath = folder.toPath().relativize(file); + Path output = outputDir.resolve(relativePath); + if (Boolean.valueOf(System.getProperty("recreate", "false"))) { + Files.createDirectories(relativePath.getParent()); + Files.deleteIfExists(output); + Files.copy(file, output); + } + assertThat(readFromFile(file)).isEqualTo(readFromFile(output)); + } } private static HttpURLConnection openConnectionToTestApi(String path) throws IOException { diff --git a/conjure-java-core/src/test/java/com/palantir/conjure/java/ErrorResource.java b/conjure-java-core/src/test/java/dialogueendpointresulttypes/com/palantir/conjure/java/ErrorResource.java similarity index 98% rename from conjure-java-core/src/test/java/com/palantir/conjure/java/ErrorResource.java rename to conjure-java-core/src/test/java/dialogueendpointresulttypes/com/palantir/conjure/java/ErrorResource.java index 9d631c53e..14b6b7a63 100644 --- a/conjure-java-core/src/test/java/com/palantir/conjure/java/ErrorResource.java +++ b/conjure-java-core/src/test/java/dialogueendpointresulttypes/com/palantir/conjure/java/ErrorResource.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.palantir.conjure.java; +package dialogueendpointresulttypes.com.palantir.conjure.java; import com.palantir.conjure.java.undertow.lib.BinaryResponseBody; import com.palantir.product.EndpointSpecificServerErrors; 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..c5116f81f --- /dev/null +++ b/conjure-java-core/src/test/java/dialogueendpointresulttypes/com/palantir/conjure/java/UndertowServiceEteTest.java @@ -0,0 +1,256 @@ +/* + * (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.product.ErrorServiceEndpoints; +import com.palantir.tokens.auth.AuthHeader; +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.OptionalBinaryResponseMode; +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.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(com.palantir.another.EndpointSpecificErrors.DIFFERENT_PACKAGE + .code() + .name()); + assertThat(error.getErrorName()) + .isEqualTo(com.palantir.another.EndpointSpecificErrors.DIFFERENT_PACKAGE.name()); + }); + } + + @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"); + } +} From 53eb3994b1e121a72a13d1a2e92206816d638477 Mon Sep 17 00:00:00 2001 From: Pritham Marupaka Date: Mon, 24 Feb 2025 17:49:27 -0500 Subject: [PATCH 17/24] remove unneeded files added to integrationInput --- .../product/ErrorServiceBlocking.java | 19 +- .../palantir/product/ErrorServiceAsync.java | 444 ------------------ .../product/OptionalBinaryResponseMode.java | 239 ---------- .../product/ErrorServiceBlocking.java | 2 +- .../another/EndpointSpecificErrors.java | 2 - .../product/EndpointSpecificErrors.java | 6 - .../product/EndpointSpecificTwoErrors.java | 2 - .../product/ErrorServiceEndpoints.java | 184 +------- .../com/palantir/product/TestErrors.java | 8 - .../product/UndertowErrorService.java | 19 +- 10 files changed, 28 insertions(+), 897 deletions(-) delete mode 100644 conjure-java-core/src/integrationInput/java/com/palantir/product/ErrorServiceAsync.java delete mode 100644 conjure-java-core/src/integrationInput/java/com/palantir/product/OptionalBinaryResponseMode.java diff --git a/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/ErrorServiceBlocking.java b/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/ErrorServiceBlocking.java index ae4616d66..4239eb879 100644 --- a/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/ErrorServiceBlocking.java +++ b/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/ErrorServiceBlocking.java @@ -23,12 +23,13 @@ import com.palantir.logsafe.Preconditions; import com.palantir.logsafe.Safe; import com.palantir.tokens.auth.AuthHeader; +import dialogueendpointresulttypes.com.palantir.product.EndpointSpecificErrors; +import dialogueendpointresulttypes.com.palantir.product.EndpointSpecificTwoErrors; +import dialogueendpointresulttypes.com.palantir.product.OptionalBinaryResponseMode; +import dialogueendpointresulttypes.com.palantir.product.TestErrors; 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; @@ -75,7 +76,7 @@ static ErrorServiceBlocking of(EndpointChannelFactory _endpointChannelFactory, C _runtime.bodySerDe().serializer(new TypeMarker() {}); private final EndpointChannel testBasicErrorChannel = - _endpointChannelFactory.endpoint(DialogueErrorEndpoints.testBasicError); + _endpointChannelFactory.endpoint(com.palantir.product.DialogueErrorEndpoints.testBasicError); private final Deserializer testBasicErrorDeserializer = _runtime.bodySerDe() .deserializer(DeserializerArgs.builder() @@ -90,7 +91,7 @@ static ErrorServiceBlocking of(EndpointChannelFactory _endpointChannelFactory, C _runtime.bodySerDe().serializer(new TypeMarker() {}); private final EndpointChannel testImportedErrorChannel = - _endpointChannelFactory.endpoint(DialogueErrorEndpoints.testImportedError); + _endpointChannelFactory.endpoint(com.palantir.product.DialogueErrorEndpoints.testImportedError); private final Deserializer testImportedErrorDeserializer = _runtime.bodySerDe() .deserializer(DeserializerArgs.builder() @@ -105,7 +106,7 @@ static ErrorServiceBlocking of(EndpointChannelFactory _endpointChannelFactory, C _runtime.bodySerDe().serializer(new TypeMarker>() {}); private final EndpointChannel testMultipleErrorsAndPackagesChannel = - _endpointChannelFactory.endpoint(DialogueErrorEndpoints.testMultipleErrorsAndPackages); + _endpointChannelFactory.endpoint(com.palantir.product.DialogueErrorEndpoints.testMultipleErrorsAndPackages); private final Deserializer testMultipleErrorsAndPackagesDeserializer = _runtime.bodySerDe() @@ -131,7 +132,7 @@ static ErrorServiceBlocking of(EndpointChannelFactory _endpointChannelFactory, C _runtime.bodySerDe().serializer(new TypeMarker() {}); private final EndpointChannel testEmptyBodyChannel = - _endpointChannelFactory.endpoint(DialogueErrorEndpoints.testEmptyBody); + _endpointChannelFactory.endpoint(com.palantir.product.DialogueErrorEndpoints.testEmptyBody); private final Deserializer testEmptyBodyDeserializer = _runtime.bodySerDe() .deserializer(DeserializerArgs.builder() @@ -146,7 +147,7 @@ static ErrorServiceBlocking of(EndpointChannelFactory _endpointChannelFactory, C _runtime.bodySerDe().serializer(new TypeMarker() {}); private final EndpointChannel testBinaryChannel = - _endpointChannelFactory.endpoint(DialogueErrorEndpoints.testBinary); + _endpointChannelFactory.endpoint(com.palantir.product.DialogueErrorEndpoints.testBinary); private final Deserializer testBinaryDeserializer = _runtime.bodySerDe() .inputStreamDeserializer(DeserializerArgs.builder() @@ -161,7 +162,7 @@ static ErrorServiceBlocking of(EndpointChannelFactory _endpointChannelFactory, C _runtime.bodySerDe().serializer(new TypeMarker() {}); private final EndpointChannel testOptionalBinaryChannel = - _endpointChannelFactory.endpoint(DialogueErrorEndpoints.testOptionalBinary); + _endpointChannelFactory.endpoint(com.palantir.product.DialogueErrorEndpoints.testOptionalBinary); private final Deserializer testOptionalBinaryDeserializer = _runtime.bodySerDe() .optionalInputStreamDeserializer(DeserializerArgs.builder() diff --git a/conjure-java-core/src/integrationInput/java/com/palantir/product/ErrorServiceAsync.java b/conjure-java-core/src/integrationInput/java/com/palantir/product/ErrorServiceAsync.java deleted file mode 100644 index 0e119b9f0..000000000 --- a/conjure-java-core/src/integrationInput/java/com/palantir/product/ErrorServiceAsync.java +++ /dev/null @@ -1,444 +0,0 @@ -package 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( - com.palantir.another.EndpointSpecificErrors.DIFFERENT_PACKAGE.name(), - new TypeMarker() {}) - .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 { - 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 - implements TestMultipleErrorsAndPackagesResponse { - @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) - DifferentPackage( - @JsonProperty("errorCode") @Safe String errorCode, - @JsonProperty("errorInstanceId") @Safe String errorInstanceId) { - super( - errorCode, - com.palantir.another.EndpointSpecificErrors.DIFFERENT_PACKAGE.name(), - errorInstanceId, - new com.palantir.another.EndpointSpecificErrors.DifferentPackageParameters()); - } - } - } - - 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/com/palantir/product/OptionalBinaryResponseMode.java b/conjure-java-core/src/integrationInput/java/com/palantir/product/OptionalBinaryResponseMode.java deleted file mode 100644 index d733abffe..000000000 --- a/conjure-java-core/src/integrationInput/java/com/palantir/product/OptionalBinaryResponseMode.java +++ /dev/null @@ -1,239 +0,0 @@ -package 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/ErrorServiceBlocking.java b/conjure-java-core/src/integrationInput/java/dialogueendpointresulttypes/com/palantir/product/ErrorServiceBlocking.java index b87eff2ee..fe1f6caa4 100644 --- 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 @@ -353,7 +353,7 @@ final class DifferentNamespace extends EndpointError + extends EndpointErrorcom.palantir.another.EndpointSpecificErrors.DifferentPackageParameters> implements TestMultipleErrorsAndPackagesResponse { @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) DifferentPackage( diff --git a/conjure-java-core/src/integrationInput/java/undertow/com/palantir/another/EndpointSpecificErrors.java b/conjure-java-core/src/integrationInput/java/undertow/com/palantir/another/EndpointSpecificErrors.java index 38a5fe579..5ed9d792f 100644 --- a/conjure-java-core/src/integrationInput/java/undertow/com/palantir/another/EndpointSpecificErrors.java +++ b/conjure-java-core/src/integrationInput/java/undertow/com/palantir/another/EndpointSpecificErrors.java @@ -18,6 +18,4 @@ 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/undertow/com/palantir/product/EndpointSpecificErrors.java b/conjure-java-core/src/integrationInput/java/undertow/com/palantir/product/EndpointSpecificErrors.java index 48d949278..60616a273 100644 --- a/conjure-java-core/src/integrationInput/java/undertow/com/palantir/product/EndpointSpecificErrors.java +++ b/conjure-java-core/src/integrationInput/java/undertow/com/palantir/product/EndpointSpecificErrors.java @@ -1,11 +1,8 @@ package undertow.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") @@ -21,7 +18,4 @@ 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/undertow/com/palantir/product/EndpointSpecificTwoErrors.java b/conjure-java-core/src/integrationInput/java/undertow/com/palantir/product/EndpointSpecificTwoErrors.java index dc2240282..ff930dc05 100644 --- a/conjure-java-core/src/integrationInput/java/undertow/com/palantir/product/EndpointSpecificTwoErrors.java +++ b/conjure-java-core/src/integrationInput/java/undertow/com/palantir/product/EndpointSpecificTwoErrors.java @@ -18,6 +18,4 @@ 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/undertow/com/palantir/product/ErrorServiceEndpoints.java b/conjure-java-core/src/integrationInput/java/undertow/com/palantir/product/ErrorServiceEndpoints.java index 3f0d6a204..ee6aa7f4e 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,8 +1,6 @@ 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; @@ -13,10 +11,8 @@ 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") @@ -36,10 +32,7 @@ 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)); + new TestMultipleErrorsAndPackagesEndpoint(runtime, delegate)); } private static final class TestBasicErrorEndpoint implements HttpHandler, Endpoint { @@ -47,33 +40,29 @@ 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); - Boolean shouldThrowError = deserializer.deserialize(exchange); - String result = delegate.testBasicError(authHeader, shouldThrowError); + String result = delegate.testBasicError(authHeader); serializer.serialize(result, exchange); } @Override public HttpString method() { - return Methods.POST; + return Methods.GET; } @Override public String template() { - return "/errors/basic"; + return "/base/basic"; } @Override @@ -97,14 +86,11 @@ 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); } @@ -112,19 +98,18 @@ private static final class TestImportedErrorEndpoint implements HttpHandler, End 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); + String result = delegate.testImportedError(authHeader); serializer.serialize(result, exchange); } @Override public HttpString method() { - return Methods.POST; + return Methods.GET; } @Override public String template() { - return "/errors/imported"; + return "/base/imported"; } @Override @@ -148,14 +133,11 @@ 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); } @@ -165,19 +147,18 @@ public void handleRequest(HttpServerExchange exchange) EndpointSpecificTwoServerErrors.DifferentNamespace, undertow.com.palantir.another.EndpointSpecificServerErrors.DifferentPackage { AuthHeader authHeader = runtime.auth().header(exchange); - Optional errorToThrow = deserializer.deserialize(exchange); - String result = delegate.testMultipleErrorsAndPackages(authHeader, errorToThrow); + String result = delegate.testMultipleErrorsAndPackages(authHeader); serializer.serialize(result, exchange); } @Override public HttpString method() { - return Methods.POST; + return Methods.GET; } @Override public String template() { - return "/errors/multiple"; + return "/base/multiple"; } @Override @@ -195,149 +176,4 @@ 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/TestErrors.java b/conjure-java-core/src/integrationInput/java/undertow/com/palantir/product/TestErrors.java index 2c206a697..e43247999 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 @@ -1,11 +1,8 @@ package undertow.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") @@ -28,9 +25,4 @@ 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 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/undertow/com/palantir/product/UndertowErrorService.java b/conjure-java-core/src/integrationInput/java/undertow/com/palantir/product/UndertowErrorService.java index c2feb0067..f8dc7d3af 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 @@ -2,38 +2,34 @@ import com.palantir.conjure.java.undertow.lib.BinaryResponseBody; import com.palantir.tokens.auth.AuthHeader; -import java.util.Optional; +import dialogueendpointresulttypes.com.palantir.product.OptionalBinaryResponseMode; import javax.annotation.processing.Generated; @Generated("com.palantir.conjure.java.services.UndertowServiceInterfaceGenerator") public interface UndertowErrorService { /** - * @apiNote {@code POST /errors/basic} + * @apiNote {@code GET /base/basic} * @throws TestServerErrors.InvalidArgument */ - String testBasicError(AuthHeader authHeader, boolean shouldThrowError) throws TestServerErrors.InvalidArgument; + String testBasicError(AuthHeader authHeader) throws TestServerErrors.InvalidArgument; /** - * @apiNote {@code POST /errors/imported} + * @apiNote {@code GET /base/imported} * @throws EndpointSpecificServerErrors.EndpointError */ - String testImportedError(AuthHeader authHeader, boolean shouldThrowError) - throws EndpointSpecificServerErrors.EndpointError; + String testImportedError(AuthHeader authHeader) throws EndpointSpecificServerErrors.EndpointError; /** - * @apiNote {@code POST /errors/multiple} + * @apiNote {@code GET /base/multiple} * @throws TestServerErrors.InvalidArgument * @throws TestServerErrors.NotFound Something was not found. * @throws EndpointSpecificTwoServerErrors.DifferentNamespace * @throws undertow.com.palantir.another.EndpointSpecificServerErrors.DifferentPackage */ - String testMultipleErrorsAndPackages(AuthHeader authHeader, Optional errorToThrow) + String testMultipleErrorsAndPackages(AuthHeader authHeader) throws TestServerErrors.InvalidArgument, TestServerErrors.NotFound, EndpointSpecificTwoServerErrors.DifferentNamespace, -<<<<<<< HEAD:conjure-java-core/src/integrationInput/java/undertow/com/palantir/product/UndertowErrorService.java undertow.com.palantir.another.EndpointSpecificServerErrors.DifferentPackage; -======= - com.palantir.another.EndpointSpecificServerErrors.DifferentPackage; /** * @apiNote {@code POST /errors/empty} @@ -54,5 +50,4 @@ BinaryResponseBody testBinary(AuthHeader authHeader, boolean shouldThrowError) */ Optional testOptionalBinary(AuthHeader authHeader, OptionalBinaryResponseMode mode) throws TestServerErrors.InvalidArgument; ->>>>>>> 2629616c (review changes):conjure-java-core/src/integrationInput/java/com/palantir/product/UndertowErrorService.java } From 643df68235d75797b0194d9822b722f4bbed0eb2 Mon Sep 17 00:00:00 2001 From: Pritham Marupaka Date: Tue, 25 Feb 2025 13:23:47 -0500 Subject: [PATCH 18/24] fix ete tests --- .../another/EndpointSpecificServerErrors.java | 35 ++ .../product/EndpointSpecificServerErrors.java | 47 +++ .../EndpointSpecificTwoServerErrors.java | 35 ++ .../com/palantir/product/ErrorService.java | 54 +++ .../palantir/product/ErrorServiceAsync.java | 29 +- .../product/ErrorServiceBlocking.java | 32 +- .../product/ErrorServiceEndpoints.java | 343 ++++++++++++++++++ .../palantir/product/TestServerErrors.java | 68 ++++ .../dialogue/DialogueInterfaceGenerator.java | 15 +- .../conjure/java/UndertowServiceEteTest.java | 4 +- .../conjure/java/parameterized/TestCases.java | 9 +- .../palantir/conjure/java/ErrorResource.java | 27 +- .../conjure/java/UndertowServiceEteTest.java | 66 ++-- 13 files changed, 679 insertions(+), 85 deletions(-) create mode 100644 conjure-java-core/src/integrationInput/java/dialogueendpointresulttypes/com/palantir/another/EndpointSpecificServerErrors.java create mode 100644 conjure-java-core/src/integrationInput/java/dialogueendpointresulttypes/com/palantir/product/EndpointSpecificServerErrors.java create mode 100644 conjure-java-core/src/integrationInput/java/dialogueendpointresulttypes/com/palantir/product/EndpointSpecificTwoServerErrors.java create mode 100644 conjure-java-core/src/integrationInput/java/dialogueendpointresulttypes/com/palantir/product/ErrorService.java create mode 100644 conjure-java-core/src/integrationInput/java/dialogueendpointresulttypes/com/palantir/product/ErrorServiceEndpoints.java create mode 100644 conjure-java-core/src/integrationInput/java/dialogueendpointresulttypes/com/palantir/product/TestServerErrors.java 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/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/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..c05960ef0 --- /dev/null +++ b/conjure-java-core/src/integrationInput/java/dialogueendpointresulttypes/com/palantir/product/ErrorService.java @@ -0,0 +1,54 @@ +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 + */ + String testMultipleErrorsAndPackages(AuthHeader authHeader, Optional errorToThrow) + throws TestServerErrors.InvalidArgument, TestServerErrors.NotFound, + EndpointSpecificTwoServerErrors.DifferentNamespace, + dialogueendpointresulttypes.com.palantir.another.EndpointSpecificServerErrors.DifferentPackage; + + /** + * @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 index 43cbebf9b..e17b0b73f 100644 --- 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 @@ -23,9 +23,6 @@ import com.palantir.dialogue.TypeMarker; import com.palantir.logsafe.Preconditions; import com.palantir.logsafe.Safe; -import com.palantir.product.EndpointSpecificErrors; -import com.palantir.product.EndpointSpecificTwoErrors; -import com.palantir.product.TestErrors; import com.palantir.tokens.auth.AuthHeader; import java.io.Closeable; import java.io.IOException; @@ -87,7 +84,7 @@ static ErrorServiceAsync of(EndpointChannelFactory _endpointChannelFactory, Conj .baseType(new TypeMarker<>() {}) .success(new TypeMarker() {}) .error( - TestErrors.INVALID_ARGUMENT.name(), + com.palantir.product.TestErrors.INVALID_ARGUMENT.name(), new TypeMarker() {}) .build()); @@ -102,7 +99,7 @@ static ErrorServiceAsync of(EndpointChannelFactory _endpointChannelFactory, Conj .baseType(new TypeMarker<>() {}) .success(new TypeMarker() {}) .error( - EndpointSpecificErrors.ENDPOINT_ERROR.name(), + com.palantir.product.EndpointSpecificErrors.ENDPOINT_ERROR.name(), new TypeMarker() {}) .build()); @@ -118,13 +115,13 @@ static ErrorServiceAsync of(EndpointChannelFactory _endpointChannelFactory, Conj .baseType(new TypeMarker<>() {}) .success(new TypeMarker() {}) .error( - TestErrors.INVALID_ARGUMENT.name(), + com.palantir.product.TestErrors.INVALID_ARGUMENT.name(), new TypeMarker() {}) .error( - TestErrors.NOT_FOUND.name(), + com.palantir.product.TestErrors.NOT_FOUND.name(), new TypeMarker() {}) .error( - EndpointSpecificTwoErrors.DIFFERENT_NAMESPACE.name(), + com.palantir.product.EndpointSpecificTwoErrors.DIFFERENT_NAMESPACE.name(), new TypeMarker< TestMultipleErrorsAndPackagesResponse.DifferentNamespace>() {}) .error( @@ -143,7 +140,7 @@ static ErrorServiceAsync of(EndpointChannelFactory _endpointChannelFactory, Conj .baseType(new TypeMarker<>() {}) .success(new TypeMarker() {}) .error( - TestErrors.INVALID_ARGUMENT.name(), + com.palantir.product.TestErrors.INVALID_ARGUMENT.name(), new TypeMarker() {}) .build()); @@ -158,7 +155,7 @@ static ErrorServiceAsync of(EndpointChannelFactory _endpointChannelFactory, Conj .baseType(new TypeMarker<>() {}) .success(new TypeMarker() {}) .error( - TestErrors.INVALID_ARGUMENT.name(), + com.palantir.product.TestErrors.INVALID_ARGUMENT.name(), new TypeMarker() {}) .build()); @@ -173,7 +170,7 @@ static ErrorServiceAsync of(EndpointChannelFactory _endpointChannelFactory, Conj .baseType(new TypeMarker<>() {}) .success(new TypeMarker() {}) .error( - TestErrors.INVALID_ARGUMENT.name(), + com.palantir.product.TestErrors.INVALID_ARGUMENT.name(), new TypeMarker() {}) .build()); @@ -356,7 +353,9 @@ final class DifferentNamespace extends EndpointError + extends EndpointError< + dialogueendpointresulttypes.com.palantir.another.EndpointSpecificErrors + .DifferentPackageParameters> implements TestMultipleErrorsAndPackagesResponse { @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) DifferentPackage( @@ -364,9 +363,11 @@ final class DifferentPackage @JsonProperty("errorInstanceId") @Safe String errorInstanceId) { super( errorCode, - com.palantir.another.EndpointSpecificErrors.DIFFERENT_PACKAGE.name(), + dialogueendpointresulttypes.com.palantir.another.EndpointSpecificErrors.DIFFERENT_PACKAGE + .name(), errorInstanceId, - new com.palantir.another.EndpointSpecificErrors.DifferentPackageParameters()); + new dialogueendpointresulttypes.com.palantir.another.EndpointSpecificErrors + .DifferentPackageParameters()); } } } 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 index fe1f6caa4..993a802cd 100644 --- 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 @@ -22,16 +22,10 @@ import com.palantir.dialogue.TypeMarker; import com.palantir.logsafe.Preconditions; import com.palantir.logsafe.Safe; -import com.palantir.product.EndpointSpecificErrors; -import com.palantir.product.EndpointSpecificTwoErrors; -import com.palantir.product.TestErrors; 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; @@ -85,7 +79,7 @@ static ErrorServiceBlocking of(EndpointChannelFactory _endpointChannelFactory, C .baseType(new TypeMarker<>() {}) .success(new TypeMarker() {}) .error( - TestErrors.INVALID_ARGUMENT.name(), + com.palantir.product.TestErrors.INVALID_ARGUMENT.name(), new TypeMarker() {}) .build()); @@ -100,7 +94,7 @@ static ErrorServiceBlocking of(EndpointChannelFactory _endpointChannelFactory, C .baseType(new TypeMarker<>() {}) .success(new TypeMarker() {}) .error( - EndpointSpecificErrors.ENDPOINT_ERROR.name(), + com.palantir.product.EndpointSpecificErrors.ENDPOINT_ERROR.name(), new TypeMarker() {}) .build()); @@ -116,13 +110,13 @@ static ErrorServiceBlocking of(EndpointChannelFactory _endpointChannelFactory, C .baseType(new TypeMarker<>() {}) .success(new TypeMarker() {}) .error( - TestErrors.INVALID_ARGUMENT.name(), + com.palantir.product.TestErrors.INVALID_ARGUMENT.name(), new TypeMarker() {}) .error( - TestErrors.NOT_FOUND.name(), + com.palantir.product.TestErrors.NOT_FOUND.name(), new TypeMarker() {}) .error( - EndpointSpecificTwoErrors.DIFFERENT_NAMESPACE.name(), + com.palantir.product.EndpointSpecificTwoErrors.DIFFERENT_NAMESPACE.name(), new TypeMarker< TestMultipleErrorsAndPackagesResponse.DifferentNamespace>() {}) .error( @@ -141,7 +135,7 @@ static ErrorServiceBlocking of(EndpointChannelFactory _endpointChannelFactory, C .baseType(new TypeMarker<>() {}) .success(new TypeMarker() {}) .error( - TestErrors.INVALID_ARGUMENT.name(), + com.palantir.product.TestErrors.INVALID_ARGUMENT.name(), new TypeMarker() {}) .build()); @@ -156,7 +150,7 @@ static ErrorServiceBlocking of(EndpointChannelFactory _endpointChannelFactory, C .baseType(new TypeMarker<>() {}) .success(new TypeMarker() {}) .error( - TestErrors.INVALID_ARGUMENT.name(), + com.palantir.product.TestErrors.INVALID_ARGUMENT.name(), new TypeMarker() {}) .build()); @@ -171,7 +165,7 @@ static ErrorServiceBlocking of(EndpointChannelFactory _endpointChannelFactory, C .baseType(new TypeMarker<>() {}) .success(new TypeMarker() {}) .error( - TestErrors.INVALID_ARGUMENT.name(), + com.palantir.product.TestErrors.INVALID_ARGUMENT.name(), new TypeMarker() {}) .build()); @@ -353,7 +347,9 @@ final class DifferentNamespace extends EndpointError + extends EndpointError< + dialogueendpointresulttypes.com.palantir.another.EndpointSpecificErrors + .DifferentPackageParameters> implements TestMultipleErrorsAndPackagesResponse { @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) DifferentPackage( @@ -361,9 +357,11 @@ final class DifferentPackage @JsonProperty("errorInstanceId") @Safe String errorInstanceId) { super( errorCode, - com.palantir.another.EndpointSpecificErrors.DIFFERENT_PACKAGE.name(), + dialogueendpointresulttypes.com.palantir.another.EndpointSpecificErrors.DIFFERENT_PACKAGE + .name(), errorInstanceId, - new com.palantir.another.EndpointSpecificErrors.DifferentPackageParameters()); + new dialogueendpointresulttypes.com.palantir.another.EndpointSpecificErrors + .DifferentPackageParameters()); } } } 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..8cd868eca --- /dev/null +++ b/conjure-java-core/src/integrationInput/java/dialogueendpointresulttypes/com/palantir/product/ErrorServiceEndpoints.java @@ -0,0 +1,343 @@ +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 { + 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/TestServerErrors.java b/conjure-java-core/src/integrationInput/java/dialogueendpointresulttypes/com/palantir/product/TestServerErrors.java new file mode 100644 index 000000000..a83f41074 --- /dev/null +++ b/conjure-java-core/src/integrationInput/java/dialogueendpointresulttypes/com/palantir/product/TestServerErrors.java @@ -0,0 +1,68 @@ +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 TestServerErrors { + private TestServerErrors() {} + + 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 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 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/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 45bc903d6..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 @@ -64,6 +64,7 @@ import java.io.InputStream; import java.util.ArrayList; import java.util.List; +import java.util.Optional; import javax.lang.model.element.Modifier; import org.apache.commons.lang3.StringUtils; @@ -206,7 +207,8 @@ private TypeSpec responseTypeForEndpoint(String packageName, ClassName className // Create a record for each of the endpoint's errors List errorTypes = new ArrayList<>(); for (EndpointError endpointError : endpointDef.getErrors()) { - errorTypes.add(constructEndpointErrorType(endpointError, packageName, responseTypeName)); + errorTypes.add( + constructEndpointErrorType(endpointError, packageName, options.packagePrefix(), responseTypeName)); } TypeSpec.Builder builder = TypeSpec.interfaceBuilder(responseTypeName) @@ -234,11 +236,16 @@ private TypeSpec responseTypeForEndpoint(String packageName, ClassName className } private TypeSpec constructEndpointErrorType( - EndpointError endpointError, String packageName, ClassName responseTypeName) { + 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( - endpointError.getError().getPackage(), + endpointErrorPackage, ErrorGenerationUtils.errorTypesClassName( endpointError.getError().getNamespace()), errorTypeName); @@ -249,7 +256,7 @@ private TypeSpec constructEndpointErrorType( .addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL) .addSuperinterface(responseTypeName); ClassName parametersClassName = ClassName.get( - endpointError.getError().getPackage(), + endpointErrorPackage, ErrorGenerationUtils.errorTypesClassName( endpointError.getError().getNamespace()), ErrorGenerationUtils.errorParametersClassName( diff --git a/conjure-java-core/src/test/java/com/palantir/conjure/java/UndertowServiceEteTest.java b/conjure-java-core/src/test/java/com/palantir/conjure/java/UndertowServiceEteTest.java index fd72fad55..b48afc80e 100644 --- a/conjure-java-core/src/test/java/com/palantir/conjure/java/UndertowServiceEteTest.java +++ b/conjure-java-core/src/test/java/com/palantir/conjure/java/UndertowServiceEteTest.java @@ -86,7 +86,6 @@ import org.junit.jupiter.api.parallel.Execution; import org.junit.jupiter.api.parallel.ExecutionMode; -// MARK(pm). @Execution(ExecutionMode.CONCURRENT) public final class UndertowServiceEteTest extends TestBase { private static final ObjectMapper CLIENT_OBJECT_MAPPER = ObjectMappers.newClientObjectMapper(); @@ -562,8 +561,7 @@ public static void beforeClass() throws IOException { new File("src/test/resources/ete-service.yml"), new File("src/test/resources/ete-binary.yml"), new File("src/test/resources/alias-test-service.yml"), - new File("src/test/resources/external-long-test-service.yml"), - new File("src/test/resources/example-endpoint-errors.yml"))); + new File("src/test/resources/external-long-test-service.yml"))); Options options = Options.builder() .undertowServicePrefix(true) .nonNullCollections(true) 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 92b64742c..61f169e1f 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 @@ -309,11 +309,16 @@ public final class TestCases { .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/example-endpoint-errors.yml"))) + Path.of("src/test/resources/errors-for-endpoints.yml"))) .options(Options.builder() .generateDialogueEndpointErrorResultTypes(true) .build()) - .generatorTypes(Set.of(GeneratorType.OBJECT, GeneratorType.ERROR, GeneratorType.DIALOGUE)) + .generatorTypes(Set.of( + GeneratorType.OBJECT, + GeneratorType.ERROR, + GeneratorType.CHECKED_ERROR, + GeneratorType.DIALOGUE, + GeneratorType.UNDERTOW)) .build()) .build(); 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 index 14b6b7a63..96c9fd1c2 100644 --- 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 @@ -17,25 +17,25 @@ package dialogueendpointresulttypes.com.palantir.conjure.java; import com.palantir.conjure.java.undertow.lib.BinaryResponseBody; -import com.palantir.product.EndpointSpecificServerErrors; -import com.palantir.product.EndpointSpecificServerErrors.EndpointError; -import com.palantir.product.EndpointSpecificTwoServerErrors; -import com.palantir.product.EndpointSpecificTwoServerErrors.DifferentNamespace; -import com.palantir.product.OptionalBinaryResponseMode; -import com.palantir.product.TestServerErrors; -import com.palantir.product.TestServerErrors.InvalidArgument; -import com.palantir.product.TestServerErrors.NotFound; -import com.palantir.product.UndertowErrorService; import com.palantir.tokens.auth.AuthHeader; +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.TestServerErrors; +import dialogueendpointresulttypes.com.palantir.product.TestServerErrors.InvalidArgument; +import dialogueendpointresulttypes.com.palantir.product.TestServerErrors.NotFound; import java.nio.charset.StandardCharsets; import java.util.Optional; interface ErrorResource { - class Impl implements UndertowErrorService { + final class Impl implements ErrorService { public static final String SUCCESS = "success!"; @Override - public String testBasicError(AuthHeader authHeader, boolean shouldThrowError) throws InvalidArgument { + public String testBasicError(AuthHeader _authHeader, boolean shouldThrowError) throws InvalidArgument { if (shouldThrowError) { throw TestServerErrors.invalidArgument("field", "value"); } @@ -53,7 +53,7 @@ public String testImportedError(AuthHeader authHeader, boolean shouldThrowError) @Override public String testMultipleErrorsAndPackages(AuthHeader authHeader, Optional errorToThrow) throws InvalidArgument, NotFound, DifferentNamespace, - com.palantir.another.EndpointSpecificServerErrors.DifferentPackage { + dialogueendpointresulttypes.com.palantir.another.EndpointSpecificServerErrors.DifferentPackage { if (errorToThrow.isPresent()) { String error = errorToThrow.get(); switch (error) { @@ -64,7 +64,8 @@ public String testMultipleErrorsAndPackages(AuthHeader authHeader, Optional - assertInvalidArgumentError( - error.getErrorCode(), - error.getErrorName(), - error.getParams().field(), - error.getParams().value()) - ); + .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(error.getParams()) + .satisfies(params -> assertThat(params.resource()).isEqualTo("resource")); }); assertThat(errorServiceClient.testMultipleErrorsAndPackages(AUTH_HEADER, Optional.of("differentNamespace"))) .isInstanceOfSatisfying(TestMultipleErrorsAndPackagesResponse.DifferentNamespace.class, error -> { @@ -169,13 +168,14 @@ public void error_client_test_empty() { @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()) - ); + assertThat(result) + .isInstanceOfSatisfying( + TestEmptyBodyResponse.InvalidArgument.class, + invalidArgument -> assertInvalidArgumentError( + invalidArgument.getErrorCode(), + invalidArgument.getErrorName(), + invalidArgument.getParams().field(), + invalidArgument.getParams().value())); } @Test @@ -198,13 +198,14 @@ public void error_client_binary_response() { @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()) - ); + assertThat(result) + .isInstanceOfSatisfying( + TestBinaryResponse.InvalidArgument.class, + invalidArgument -> assertInvalidArgumentError( + invalidArgument.getErrorCode(), + invalidArgument.getErrorName(), + invalidArgument.getParams().field(), + invalidArgument.getParams().value())); } @Test @@ -238,13 +239,14 @@ public void error_client_optional_binary_response_absent() { 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()) - ); + 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) { From 00c78db85940d2b6e66ba5da3752e9efb35cc265 Mon Sep 17 00:00:00 2001 From: Pritham Marupaka Date: Thu, 27 Feb 2025 09:55:38 -0500 Subject: [PATCH 19/24] update readme --- readme.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/readme.md b/readme.md index 3095cf099..b837afec4 100644 --- a/readme.md +++ b/readme.md @@ -51,6 +51,8 @@ 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 + For endpoints with errors associated with them, generate methods which return result types in Dialogue clients ### Known Tag Values From 032b729e309304e670e9ed284f65555031e94568 Mon Sep 17 00:00:00 2001 From: Pritham Marupaka Date: Thu, 27 Feb 2025 15:16:35 -0500 Subject: [PATCH 20/24] fixup merge --- .../product/DialogueCookieResultTypes.java | 6 - .../product/DialogueEmptyPathResultTypes.java | 6 - .../product/DialogueErrorEndpoints.java | 193 -------- .../product/DialogueErrorResultTypes.java | 38 -- .../product/DialogueEteBinaryResultTypes.java | 6 - .../product/DialogueEteResultTypes.java | 6 - .../palantir/product/ErrorServiceAsync.java | 112 ----- .../product/ErrorServiceBlocking.java | 442 ------------------ .../palantir/product/ErrorServiceAsync.java | 20 +- .../product/ErrorServiceBlocking.java | 23 +- .../product/ErrorServiceEndpoints.java | 184 +++++++- .../product/OptionalBinaryResponseMode.java | 239 ++++++++++ .../product/UndertowErrorService.java | 15 +- .../DefaultStaticFactoryMethodGenerator.java | 7 +- .../java/DialogueServiceGeneratorTests.java | 17 - .../com/palantir/conjure/java/TestBase.java | 13 - .../conjure/java/UndertowServiceEteTest.java | 70 +-- .../conjure/java/parameterized/TestCases.java | 3 +- .../palantir/conjure/java/ErrorResource.java | 102 ---- .../conjure/java/UndertowServiceEteTest.java | 258 ---------- .../src/test/resources/example-types.yml | 2 +- 21 files changed, 463 insertions(+), 1299 deletions(-) delete mode 100644 conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/DialogueCookieResultTypes.java delete mode 100644 conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/DialogueEmptyPathResultTypes.java delete mode 100644 conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/DialogueErrorEndpoints.java delete mode 100644 conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/DialogueErrorResultTypes.java delete mode 100644 conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/DialogueEteBinaryResultTypes.java delete mode 100644 conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/DialogueEteResultTypes.java delete mode 100644 conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/ErrorServiceAsync.java delete mode 100644 conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/ErrorServiceBlocking.java create mode 100644 conjure-java-core/src/integrationInput/java/undertow/com/palantir/product/OptionalBinaryResponseMode.java delete mode 100644 conjure-java-core/src/test/java/dialogueendpointresulttypes/com/palantir/conjure/java/ErrorResource.java delete mode 100644 conjure-java-core/src/test/java/dialogueendpointresulttypes/com/palantir/conjure/java/UndertowServiceEteTest.java diff --git a/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/DialogueCookieResultTypes.java b/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/DialogueCookieResultTypes.java deleted file mode 100644 index a660842f2..000000000 --- a/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/DialogueCookieResultTypes.java +++ /dev/null @@ -1,6 +0,0 @@ -package test.api; - -import javax.annotation.processing.Generated; - -@Generated("com.palantir.conjure.java.services.dialogue.DialogueEndpointResultTypeGenerator") -public final class DialogueCookieResultTypes {} diff --git a/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/DialogueEmptyPathResultTypes.java b/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/DialogueEmptyPathResultTypes.java deleted file mode 100644 index 7e13b830f..000000000 --- a/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/DialogueEmptyPathResultTypes.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.palantir.product; - -import javax.annotation.processing.Generated; - -@Generated("com.palantir.conjure.java.services.dialogue.DialogueEndpointResultTypeGenerator") -public final class DialogueEmptyPathResultTypes {} diff --git a/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/DialogueErrorEndpoints.java b/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/DialogueErrorEndpoints.java deleted file mode 100644 index 2017dcb8c..000000000 --- a/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/DialogueErrorEndpoints.java +++ /dev/null @@ -1,193 +0,0 @@ -package 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 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 "1.2.3"; - } - }, - - 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 "1.2.3"; - } - }, - - 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 "1.2.3"; - } - }, - - 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 "1.2.3"; - } - }, - - 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 "1.2.3"; - } - }, - - 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 "1.2.3"; - } - } -} diff --git a/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/DialogueErrorResultTypes.java b/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/DialogueErrorResultTypes.java deleted file mode 100644 index 54e72e70a..000000000 --- a/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/DialogueErrorResultTypes.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.palantir.product; - -import java.lang.String; -import javax.annotation.processing.Generated; - -@Generated("com.palantir.conjure.java.services.dialogue.DialogueEndpointResultTypeGenerator") -public final class DialogueErrorResultTypes { - public sealed interface TestBasicErrorResult - permits TestBasicErrorResult.Success, TestBasicErrorResult.Test_InvalidArgument { - record Success(String value) implements TestBasicErrorResult {} - - record Test_InvalidArgument() implements TestBasicErrorResult {} - } - - public sealed interface TestImportedErrorResult - permits TestImportedErrorResult.Success, TestImportedErrorResult.EndpointSpecific_EndpointError { - record Success(String value) implements TestImportedErrorResult {} - - record EndpointSpecific_EndpointError() implements TestImportedErrorResult {} - } - - public sealed interface TestMultipleErrorsAndPackagesResult - permits TestMultipleErrorsAndPackagesResult.Success, - TestMultipleErrorsAndPackagesResult.Test_InvalidArgument, - TestMultipleErrorsAndPackagesResult.Test_NotFound, - TestMultipleErrorsAndPackagesResult.EndpointSpecificTwo_DifferentNamespace, - TestMultipleErrorsAndPackagesResult.EndpointSpecific_DifferentPackage { - record Success(String value) implements TestMultipleErrorsAndPackagesResult {} - - record Test_InvalidArgument() implements TestMultipleErrorsAndPackagesResult {} - - record Test_NotFound() implements TestMultipleErrorsAndPackagesResult {} - - record EndpointSpecificTwo_DifferentNamespace() implements TestMultipleErrorsAndPackagesResult {} - - record EndpointSpecific_DifferentPackage() implements TestMultipleErrorsAndPackagesResult {} - } -} diff --git a/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/DialogueEteBinaryResultTypes.java b/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/DialogueEteBinaryResultTypes.java deleted file mode 100644 index d32d44b34..000000000 --- a/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/DialogueEteBinaryResultTypes.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.palantir.product; - -import javax.annotation.processing.Generated; - -@Generated("com.palantir.conjure.java.services.dialogue.DialogueEndpointResultTypeGenerator") -public final class DialogueEteBinaryResultTypes {} diff --git a/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/DialogueEteResultTypes.java b/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/DialogueEteResultTypes.java deleted file mode 100644 index 95a402570..000000000 --- a/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/DialogueEteResultTypes.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.palantir.product; - -import javax.annotation.processing.Generated; - -@Generated("com.palantir.conjure.java.services.dialogue.DialogueEndpointResultTypeGenerator") -public final class DialogueEteResultTypes {} diff --git a/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/ErrorServiceAsync.java b/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/ErrorServiceAsync.java deleted file mode 100644 index aedde7df1..000000000 --- a/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/ErrorServiceAsync.java +++ /dev/null @@ -1,112 +0,0 @@ -package com.palantir.product; - - -import com.google.common.util.concurrent.ListenableFuture; -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.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.PlainSerDe; -import com.palantir.dialogue.Request; -import com.palantir.dialogue.TypeMarker; -import com.palantir.tokens.auth.AuthHeader; -import javax.annotation.processing.Generated; - -@Generated("com.palantir.conjure.java.services.dialogue.DialogueInterfaceGenerator") -@DialogueService(ErrorServiceAsync.Factory.class) -public interface ErrorServiceAsync { - /** @apiNote {@code GET /base/basic} */ - @ClientEndpoint(method = "GET", path = "/base/basic") - ListenableFuture testBasicError(AuthHeader authHeader); - - /** @apiNote {@code GET /base/imported} */ - @ClientEndpoint(method = "GET", path = "/base/imported") - ListenableFuture testImportedError(AuthHeader authHeader); - - /** @apiNote {@code GET /base/multiple} */ - @ClientEndpoint(method = "GET", path = "/base/multiple") - ListenableFuture testMultipleErrorsAndPackages(AuthHeader authHeader); - /** 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 EndpointChannel testBasicErrorChannel = - _endpointChannelFactory.endpoint(com.palantir.product.DialogueErrorEndpoints.testBasicError); - - private final Deserializer testBasicErrorDeserializer = - _runtime.bodySerDe().deserializer(new TypeMarker() {}); - - private final EndpointChannel testImportedErrorChannel = - _endpointChannelFactory.endpoint(com.palantir.product.DialogueErrorEndpoints.testImportedError); - - private final Deserializer testImportedErrorDeserializer = - _runtime.bodySerDe().deserializer(new TypeMarker() {}); - - private final EndpointChannel testMultipleErrorsAndPackagesChannel = - _endpointChannelFactory.endpoint(com.palantir.product.DialogueErrorEndpoints.testMultipleErrorsAndPackages); - - private final Deserializer testMultipleErrorsAndPackagesDeserializer = - _runtime.bodySerDe().deserializer(new TypeMarker() {}); - - @Override - public ListenableFuture testBasicError(AuthHeader authHeader) { - Request.Builder _request = Request.builder(); - _request.putHeaderParams("Authorization", authHeader.toString()); - return _runtime.clients().call(testBasicErrorChannel, _request.build(), testBasicErrorDeserializer); - } - - @Override - public ListenableFuture testImportedError(AuthHeader authHeader) { - Request.Builder _request = Request.builder(); - _request.putHeaderParams("Authorization", authHeader.toString()); - return _runtime.clients() - .call(testImportedErrorChannel, _request.build(), testImportedErrorDeserializer); - } - - @Override - public ListenableFuture testMultipleErrorsAndPackages(AuthHeader authHeader) { - Request.Builder _request = Request.builder(); - _request.putHeaderParams("Authorization", authHeader.toString()); - return _runtime.clients() - .call( - testMultipleErrorsAndPackagesChannel, - _request.build(), - testMultipleErrorsAndPackagesDeserializer); - } - - @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); - } - } -} diff --git a/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/ErrorServiceBlocking.java b/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/ErrorServiceBlocking.java deleted file mode 100644 index 4239eb879..000000000 --- a/conjure-java-core/src/integrationInput/java/allexamples/com/palantir/product/ErrorServiceBlocking.java +++ /dev/null @@ -1,442 +0,0 @@ -package 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 dialogueendpointresulttypes.com.palantir.product.EndpointSpecificErrors; -import dialogueendpointresulttypes.com.palantir.product.EndpointSpecificTwoErrors; -import dialogueendpointresulttypes.com.palantir.product.OptionalBinaryResponseMode; -import dialogueendpointresulttypes.com.palantir.product.TestErrors; -import java.io.Closeable; -import java.io.IOException; -import java.io.InputStream; -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(com.palantir.product.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(com.palantir.product.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(com.palantir.product.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( - com.palantir.another.EndpointSpecificErrors.DIFFERENT_PACKAGE.name(), - new TypeMarker() {}) - .build()); - - private final Serializer testEmptyBodySerializer = - _runtime.bodySerDe().serializer(new TypeMarker() {}); - - private final EndpointChannel testEmptyBodyChannel = - _endpointChannelFactory.endpoint(com.palantir.product.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(com.palantir.product.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(com.palantir.product.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 { - 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 - implements TestMultipleErrorsAndPackagesResponse { - @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) - DifferentPackage( - @JsonProperty("errorCode") @Safe String errorCode, - @JsonProperty("errorInstanceId") @Safe String errorInstanceId) { - super( - errorCode, - com.palantir.another.EndpointSpecificErrors.DIFFERENT_PACKAGE.name(), - errorInstanceId, - new com.palantir.another.EndpointSpecificErrors.DifferentPackageParameters()); - } - } - } - - 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/ErrorServiceAsync.java b/conjure-java-core/src/integrationInput/java/dialogueendpointresulttypes/com/palantir/product/ErrorServiceAsync.java index e17b0b73f..b80c9d794 100644 --- 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 @@ -84,7 +84,7 @@ static ErrorServiceAsync of(EndpointChannelFactory _endpointChannelFactory, Conj .baseType(new TypeMarker<>() {}) .success(new TypeMarker() {}) .error( - com.palantir.product.TestErrors.INVALID_ARGUMENT.name(), + TestErrors.INVALID_ARGUMENT.name(), new TypeMarker() {}) .build()); @@ -99,7 +99,7 @@ static ErrorServiceAsync of(EndpointChannelFactory _endpointChannelFactory, Conj .baseType(new TypeMarker<>() {}) .success(new TypeMarker() {}) .error( - com.palantir.product.EndpointSpecificErrors.ENDPOINT_ERROR.name(), + EndpointSpecificErrors.ENDPOINT_ERROR.name(), new TypeMarker() {}) .build()); @@ -115,17 +115,19 @@ static ErrorServiceAsync of(EndpointChannelFactory _endpointChannelFactory, Conj .baseType(new TypeMarker<>() {}) .success(new TypeMarker() {}) .error( - com.palantir.product.TestErrors.INVALID_ARGUMENT.name(), + TestErrors.INVALID_ARGUMENT.name(), new TypeMarker() {}) .error( - com.palantir.product.TestErrors.NOT_FOUND.name(), + TestErrors.NOT_FOUND.name(), new TypeMarker() {}) .error( - com.palantir.product.EndpointSpecificTwoErrors.DIFFERENT_NAMESPACE.name(), + EndpointSpecificTwoErrors.DIFFERENT_NAMESPACE.name(), new TypeMarker< TestMultipleErrorsAndPackagesResponse.DifferentNamespace>() {}) .error( - com.palantir.another.EndpointSpecificErrors.DIFFERENT_PACKAGE.name(), + dialogueendpointresulttypes.com.palantir.another.EndpointSpecificErrors + .DIFFERENT_PACKAGE + .name(), new TypeMarker() {}) .build()); @@ -140,7 +142,7 @@ static ErrorServiceAsync of(EndpointChannelFactory _endpointChannelFactory, Conj .baseType(new TypeMarker<>() {}) .success(new TypeMarker() {}) .error( - com.palantir.product.TestErrors.INVALID_ARGUMENT.name(), + TestErrors.INVALID_ARGUMENT.name(), new TypeMarker() {}) .build()); @@ -155,7 +157,7 @@ static ErrorServiceAsync of(EndpointChannelFactory _endpointChannelFactory, Conj .baseType(new TypeMarker<>() {}) .success(new TypeMarker() {}) .error( - com.palantir.product.TestErrors.INVALID_ARGUMENT.name(), + TestErrors.INVALID_ARGUMENT.name(), new TypeMarker() {}) .build()); @@ -170,7 +172,7 @@ static ErrorServiceAsync of(EndpointChannelFactory _endpointChannelFactory, Conj .baseType(new TypeMarker<>() {}) .success(new TypeMarker() {}) .error( - com.palantir.product.TestErrors.INVALID_ARGUMENT.name(), + TestErrors.INVALID_ARGUMENT.name(), new TypeMarker() {}) .build()); 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 index 993a802cd..0b5e5da6e 100644 --- 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 @@ -26,6 +26,9 @@ 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; @@ -79,7 +82,7 @@ static ErrorServiceBlocking of(EndpointChannelFactory _endpointChannelFactory, C .baseType(new TypeMarker<>() {}) .success(new TypeMarker() {}) .error( - com.palantir.product.TestErrors.INVALID_ARGUMENT.name(), + TestErrors.INVALID_ARGUMENT.name(), new TypeMarker() {}) .build()); @@ -94,7 +97,7 @@ static ErrorServiceBlocking of(EndpointChannelFactory _endpointChannelFactory, C .baseType(new TypeMarker<>() {}) .success(new TypeMarker() {}) .error( - com.palantir.product.EndpointSpecificErrors.ENDPOINT_ERROR.name(), + EndpointSpecificErrors.ENDPOINT_ERROR.name(), new TypeMarker() {}) .build()); @@ -110,17 +113,19 @@ static ErrorServiceBlocking of(EndpointChannelFactory _endpointChannelFactory, C .baseType(new TypeMarker<>() {}) .success(new TypeMarker() {}) .error( - com.palantir.product.TestErrors.INVALID_ARGUMENT.name(), + TestErrors.INVALID_ARGUMENT.name(), new TypeMarker() {}) .error( - com.palantir.product.TestErrors.NOT_FOUND.name(), + TestErrors.NOT_FOUND.name(), new TypeMarker() {}) .error( - com.palantir.product.EndpointSpecificTwoErrors.DIFFERENT_NAMESPACE.name(), + EndpointSpecificTwoErrors.DIFFERENT_NAMESPACE.name(), new TypeMarker< TestMultipleErrorsAndPackagesResponse.DifferentNamespace>() {}) .error( - com.palantir.another.EndpointSpecificErrors.DIFFERENT_PACKAGE.name(), + dialogueendpointresulttypes.com.palantir.another.EndpointSpecificErrors + .DIFFERENT_PACKAGE + .name(), new TypeMarker() {}) .build()); @@ -135,7 +140,7 @@ static ErrorServiceBlocking of(EndpointChannelFactory _endpointChannelFactory, C .baseType(new TypeMarker<>() {}) .success(new TypeMarker() {}) .error( - com.palantir.product.TestErrors.INVALID_ARGUMENT.name(), + TestErrors.INVALID_ARGUMENT.name(), new TypeMarker() {}) .build()); @@ -150,7 +155,7 @@ static ErrorServiceBlocking of(EndpointChannelFactory _endpointChannelFactory, C .baseType(new TypeMarker<>() {}) .success(new TypeMarker() {}) .error( - com.palantir.product.TestErrors.INVALID_ARGUMENT.name(), + TestErrors.INVALID_ARGUMENT.name(), new TypeMarker() {}) .build()); @@ -165,7 +170,7 @@ static ErrorServiceBlocking of(EndpointChannelFactory _endpointChannelFactory, C .baseType(new TypeMarker<>() {}) .success(new TypeMarker() {}) .error( - com.palantir.product.TestErrors.INVALID_ARGUMENT.name(), + TestErrors.INVALID_ARGUMENT.name(), new TypeMarker() {}) .build()); 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..3f0d6a204 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); } @@ -147,18 +165,19 @@ public void handleRequest(HttpServerExchange exchange) EndpointSpecificTwoServerErrors.DifferentNamespace, undertow.com.palantir.another.EndpointSpecificServerErrors.DifferentPackage { 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 +195,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/UndertowErrorService.java b/conjure-java-core/src/integrationInput/java/undertow/com/palantir/product/UndertowErrorService.java index f8dc7d3af..9f0deedf8 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 @@ -2,31 +2,32 @@ import com.palantir.conjure.java.undertow.lib.BinaryResponseBody; import com.palantir.tokens.auth.AuthHeader; -import dialogueendpointresulttypes.com.palantir.product.OptionalBinaryResponseMode; +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 */ - String testMultipleErrorsAndPackages(AuthHeader authHeader) + String testMultipleErrorsAndPackages(AuthHeader authHeader, Optional errorToThrow) throws TestServerErrors.InvalidArgument, TestServerErrors.NotFound, EndpointSpecificTwoServerErrors.DifferentNamespace, undertow.com.palantir.another.EndpointSpecificServerErrors.DifferentPackage; 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 d3f34c4e6..2fc889433 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 @@ -98,7 +98,6 @@ public DefaultStaticFactoryMethodGenerator( @Override public MethodSpec generate(ServiceDefinition def) { - // Need: 1) the name of the response type (static), 2) all the error types (declaredEndpointErrors) ClassName className = getClassName(def); TypeSpec.Builder impl = TypeSpec.anonymousClassBuilder("").addSuperinterface(className); @@ -217,13 +216,13 @@ private CodeBlock constructDeserializerWithEndpointErrors( } else { deserializerBuilder.add("deserializer("); } - deserializerBuilder.add(buildArgsForEndpointErrorDeserializer(endpointDef, className, responseType)); + deserializerBuilder.add(buildArgsForEndpointErrorDeserializer(endpointDef, responseType)); deserializerBuilder.add(")"); return deserializerBuilder.build(); } private CodeBlock buildArgsForEndpointErrorDeserializer( - EndpointDefinition endpointDefinition, TypeName className, TypeName responseType) { + EndpointDefinition endpointDefinition, TypeName responseType) { CodeBlock.Builder deserializerArgsBuilder = CodeBlock.builder() .add("$T.<$T>builder()", DeserializerArgs.class, responseType) .add(".baseType(new $T<>() {})", TypeMarker.class) @@ -232,7 +231,7 @@ private CodeBlock buildArgsForEndpointErrorDeserializer( ErrorTypeName errorTypeName = err.getError(); String errorName = errorTypeName.getName(); ClassName errorClass = ClassName.get( - errorTypeName.getPackage(), + Packages.getPrefixedPackage(errorTypeName.getPackage(), options.packagePrefix()), ErrorGenerationUtils.errorTypesClassName(errorTypeName.getNamespace()), CaseFormat.UPPER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, errorName)); deserializerArgsBuilder.add( diff --git a/conjure-java-core/src/test/java/com/palantir/conjure/java/DialogueServiceGeneratorTests.java b/conjure-java-core/src/test/java/com/palantir/conjure/java/DialogueServiceGeneratorTests.java index e03585a98..9bf95a66b 100644 --- a/conjure-java-core/src/test/java/com/palantir/conjure/java/DialogueServiceGeneratorTests.java +++ b/conjure-java-core/src/test/java/com/palantir/conjure/java/DialogueServiceGeneratorTests.java @@ -78,21 +78,4 @@ public void testServiceGeneration_excludeDialogueAsyncInterfaces() throws IOExce .noneMatch(name -> name.toLowerCase(Locale.ROOT).contains("async")); } } - - @Test - public void testServiceGeneration_excludeDialogueAsyncInterfaces() { - List files = getGeneratedFilesForDef( - "example-service", - Options.builder().excludeDialogueAsyncInterfaces(true).build()); - List fileNames = - files.stream().map(Path::getFileName).map(Path::toString).toList(); - assertThat(fileNames).noneMatch(name -> name.toLowerCase(Locale.ROOT).contains("async")); - } - - private List getGeneratedFilesForDef(String conjureFile, Options options) { - ConjureDefinition def = Conjure.parse(ImmutableList.of(new File("src/test/resources/" + conjureFile + ".yml"))); - return new GenerationCoordinator( - MoreExecutors.directExecutor(), ImmutableSet.of(new DialogueServiceGenerator(options))) - .emit(def, folder); - } } diff --git a/conjure-java-core/src/test/java/com/palantir/conjure/java/TestBase.java b/conjure-java-core/src/test/java/com/palantir/conjure/java/TestBase.java index 68d5438ca..d14a49a02 100644 --- a/conjure-java-core/src/test/java/com/palantir/conjure/java/TestBase.java +++ b/conjure-java-core/src/test/java/com/palantir/conjure/java/TestBase.java @@ -59,17 +59,4 @@ protected static void validateGeneratorOutput(List files, Path outputDir, assertThat(readFromFile(file)).isEqualTo(readFromFile(output)); } } - - protected static void validateGeneratedOutput(Path folder, List files, Path outputDir) throws IOException { - for (Path file : files) { - Path relativePath = folder.relativize(file); - Path output = outputDir.resolve(relativePath); - if (Boolean.parseBoolean(System.getProperty("recreate", "false"))) { - Files.createDirectories(relativePath.getParent()); - Files.deleteIfExists(output); - Files.copy(file, output); - } - assertThat(readFromFile(file)).isEqualTo(readFromFile(output)); - } - } } diff --git a/conjure-java-core/src/test/java/com/palantir/conjure/java/UndertowServiceEteTest.java b/conjure-java-core/src/test/java/com/palantir/conjure/java/UndertowServiceEteTest.java index b48afc80e..bed151e13 100644 --- a/conjure-java-core/src/test/java/com/palantir/conjure/java/UndertowServiceEteTest.java +++ b/conjure-java-core/src/test/java/com/palantir/conjure/java/UndertowServiceEteTest.java @@ -26,33 +26,23 @@ import com.google.common.net.HttpHeaders; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.UncheckedExecutionException; -import com.palantir.conjure.defs.Conjure; import com.palantir.conjure.java.api.errors.RemoteException; import com.palantir.conjure.java.api.errors.SerializableError; import com.palantir.conjure.java.client.jaxrs.JaxRsClient; import com.palantir.conjure.java.lib.SafeLong; import com.palantir.conjure.java.okhttp.HostMetricsRegistry; import com.palantir.conjure.java.serialization.ObjectMappers; -import com.palantir.conjure.java.services.UndertowServiceGenerator; -import com.palantir.conjure.java.types.CheckedErrorGenerator; -import com.palantir.conjure.java.types.ErrorGenerator; -import com.palantir.conjure.java.types.ObjectGenerator; import com.palantir.conjure.java.undertow.runtime.ConjureHandler; -import com.palantir.conjure.spec.ConjureDefinition; import com.palantir.dialogue.BinaryRequestBody; import com.palantir.dialogue.clients.DialogueClients; -import com.palantir.product.EmptyPathService; -import com.palantir.product.EmptyPathServiceEndpoints; -import com.palantir.product.EteBinaryServiceBlocking; -import com.palantir.product.EteBinaryServiceEndpoints; -import com.palantir.product.EteServiceAsync; -import com.palantir.product.EteServiceBlocking; -import com.palantir.product.EteServiceEndpoints; -import com.palantir.product.NestedStringAliasExample; -import com.palantir.product.SimpleEnum; -import com.palantir.product.StringAliasExample; import com.palantir.ri.ResourceIdentifier; import com.palantir.tokens.auth.AuthHeader; +import dialogue.com.palantir.product.EteBinaryServiceBlocking; +import dialogue.com.palantir.product.EteServiceAsync; +import dialogue.com.palantir.product.EteServiceBlocking; +import dialogue.com.palantir.product.NestedStringAliasExample; +import dialogue.com.palantir.product.SimpleEnum; +import dialogue.com.palantir.product.StringAliasExample; import io.undertow.Handlers; import io.undertow.Undertow; import io.undertow.UndertowOptions; @@ -67,17 +57,14 @@ import java.net.InetSocketAddress; import java.net.URL; import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; import java.time.Instant; import java.time.OffsetDateTime; import java.time.ZoneId; import java.time.ZoneOffset; import java.util.Collections; -import java.util.List; import java.util.Optional; import java.util.concurrent.ThreadLocalRandom; +import jersey.com.palantir.product.EmptyPathService; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -85,7 +72,11 @@ import org.junit.jupiter.api.io.TempDir; import org.junit.jupiter.api.parallel.Execution; import org.junit.jupiter.api.parallel.ExecutionMode; +import undertow.com.palantir.product.EmptyPathServiceEndpoints; +import undertow.com.palantir.product.EteBinaryServiceEndpoints; +import undertow.com.palantir.product.EteServiceEndpoints; +// MARK(pm). @Execution(ExecutionMode.CONCURRENT) public final class UndertowServiceEteTest extends TestBase { private static final ObjectMapper CLIENT_OBJECT_MAPPER = ObjectMappers.newClientObjectMapper(); @@ -97,6 +88,7 @@ public final class UndertowServiceEteTest extends TestBase { private final EteServiceBlocking client; private final EteServiceAsync asyncClient; + private final EteBinaryServiceBlocking binaryClient; private static int port; @@ -105,7 +97,6 @@ public UndertowServiceEteTest() { this.client = DialogueClients.create(EteServiceBlocking.class, clientConfiguration(port)); this.asyncClient = DialogueClients.create(EteServiceAsync.class, clientConfiguration(port)); this.binaryClient = DialogueClients.create(EteBinaryServiceBlocking.class, clientConfiguration(port)); - } @BeforeAll @@ -555,43 +546,6 @@ public void testListOfNull() { }); } - @BeforeAll - public static void beforeClass() throws IOException { - ConjureDefinition def = Conjure.parse(ImmutableList.of( - new File("src/test/resources/ete-service.yml"), - new File("src/test/resources/ete-binary.yml"), - new File("src/test/resources/alias-test-service.yml"), - new File("src/test/resources/external-long-test-service.yml"))); - Options options = Options.builder() - .undertowServicePrefix(true) - .nonNullCollections(true) - .excludeEmptyOptionals(true) - .jetbrainsContractAnnotations(true) - .build(); - List files = new GenerationCoordinator( - MoreExecutors.directExecutor(), - ImmutableSet.of( - new UndertowServiceGenerator(options), - new ObjectGenerator(options), - new ErrorGenerator(options), - new CheckedErrorGenerator(options))) - .emit(def, folder); - validateGeneratedOutput(files, Paths.get("src/integrationInput/java")); - } - - private static void validateGeneratedOutput(List files, Path outputDir) throws IOException { - for (Path file : files) { - Path relativePath = folder.toPath().relativize(file); - Path output = outputDir.resolve(relativePath); - if (Boolean.valueOf(System.getProperty("recreate", "false"))) { - Files.createDirectories(relativePath.getParent()); - Files.deleteIfExists(output); - Files.copy(file, output); - } - assertThat(readFromFile(file)).isEqualTo(readFromFile(output)); - } - } - private static HttpURLConnection openConnectionToTestApi(String path) throws IOException { URL url = new URL("http://localhost:" + port + "/test-example/api" + path); return (HttpURLConnection) url.openConnection(); 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 61f169e1f..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 @@ -22,7 +22,6 @@ import com.palantir.conjure.java.parameterized.objects.ParameterizedTestCase; import java.nio.file.Path; import java.util.List; -import java.util.Set; public final class TestCases { private static final List CASES = ImmutableList.builder() @@ -313,7 +312,7 @@ public final class TestCases { .options(Options.builder() .generateDialogueEndpointErrorResultTypes(true) .build()) - .generatorTypes(Set.of( + .generatorTypes(List.of( GeneratorType.OBJECT, GeneratorType.ERROR, GeneratorType.CHECKED_ERROR, 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 deleted file mode 100644 index 96c9fd1c2..000000000 --- a/conjure-java-core/src/test/java/dialogueendpointresulttypes/com/palantir/conjure/java/ErrorResource.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * (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.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.TestServerErrors; -import dialogueendpointresulttypes.com.palantir.product.TestServerErrors.InvalidArgument; -import dialogueendpointresulttypes.com.palantir.product.TestServerErrors.NotFound; -import java.nio.charset.StandardCharsets; -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 { - 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(); - 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 deleted file mode 100644 index 4a1a4a5f7..000000000 --- a/conjure-java-core/src/test/java/dialogueendpointresulttypes/com/palantir/conjure/java/UndertowServiceEteTest.java +++ /dev/null @@ -1,258 +0,0 @@ -/* - * (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.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.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.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(com.palantir.another.EndpointSpecificErrors.DIFFERENT_PACKAGE - .code() - .name()); - assertThat(error.getErrorName()) - .isEqualTo(com.palantir.another.EndpointSpecificErrors.DIFFERENT_PACKAGE.name()); - }); - } - - @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-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>> From 2be22e7f57b513cc6f563be673e736fd08372093 Mon Sep 17 00:00:00 2001 From: Pritham Marupaka Date: Thu, 27 Feb 2025 16:44:45 -0500 Subject: [PATCH 21/24] re-add ete tests --- .../palantir/conjure/java/ErrorResource.java | 102 +++++++ .../conjure/java/UndertowServiceEteTest.java | 263 ++++++++++++++++++ 2 files changed, 365 insertions(+) create mode 100644 conjure-java-core/src/test/java/dialogueendpointresulttypes/com/palantir/conjure/java/ErrorResource.java create mode 100644 conjure-java-core/src/test/java/dialogueendpointresulttypes/com/palantir/conjure/java/UndertowServiceEteTest.java 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..96c9fd1c2 --- /dev/null +++ b/conjure-java-core/src/test/java/dialogueendpointresulttypes/com/palantir/conjure/java/ErrorResource.java @@ -0,0 +1,102 @@ +/* + * (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.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.TestServerErrors; +import dialogueendpointresulttypes.com.palantir.product.TestServerErrors.InvalidArgument; +import dialogueendpointresulttypes.com.palantir.product.TestServerErrors.NotFound; +import java.nio.charset.StandardCharsets; +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 { + 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(); + 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..10643b421 --- /dev/null +++ b/conjure-java-core/src/test/java/dialogueendpointresulttypes/com/palantir/conjure/java/UndertowServiceEteTest.java @@ -0,0 +1,263 @@ +/* + * (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.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.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.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()); + }); + } + + @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"); + } +} From f2aa7bcaa09b523d2b76cff93b0fca4a3f538a9e Mon Sep 17 00:00:00 2001 From: Pritham Marupaka Date: Fri, 28 Feb 2025 15:52:15 -0500 Subject: [PATCH 22/24] review changes --- .../src/main/java/com/palantir/conjure/java/Options.java | 5 +++++ .../java/services/dialogue/DialogueServiceGenerator.java | 2 +- .../conjure/java/services/dialogue/ReturnTypeMapper.java | 6 +++--- readme.md | 8 ++++++-- 4 files changed, 15 insertions(+), 6 deletions(-) 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 9c9b46c2f..016917971 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 @@ -188,9 +188,14 @@ default boolean preferObjectBuilders() { } /** + * Warning: this is an experimental feature. + *

* 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() { diff --git a/conjure-java-core/src/main/java/com/palantir/conjure/java/services/dialogue/DialogueServiceGenerator.java b/conjure-java-core/src/main/java/com/palantir/conjure/java/services/dialogue/DialogueServiceGenerator.java index fef4b4196..64e693107 100644 --- a/conjure-java-core/src/main/java/com/palantir/conjure/java/services/dialogue/DialogueServiceGenerator.java +++ b/conjure-java-core/src/main/java/com/palantir/conjure/java/services/dialogue/DialogueServiceGenerator.java @@ -106,7 +106,7 @@ private static Stream generateFilesForService( DialogueInterfaceGenerator interfaceGenerator, StaticFactoryMethodGenerator blockingGenerator, StaticFactoryMethodGenerator asyncGenerator) { - List files = new ArrayList<>(/* initialCapacity= */ 4); + List files = new ArrayList<>(/* initialCapacity= */ 3); if (!serviceDef.getEndpoints().isEmpty()) { files.add(endpointsGenerator.endpointsClass(serviceDef)); } 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 d5d1e2207..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 @@ -47,15 +47,15 @@ public TypeName async(Optional type) { return ParameterizedTypeName.get(LISTENABLE_FUTURE, Primitives.box(baseType(type))); } - public boolean isBinaryOrOptionalBinary(TypeName returnType) { + boolean isBinaryOrOptionalBinary(TypeName returnType) { return isBinary(returnType) || isOptionalBinary(returnType); } - public boolean isOptionalBinary(TypeName returnType) { + boolean isOptionalBinary(TypeName returnType) { return returnType.equals(baseType(Type.optional(OptionalType.of(Type.primitive(PrimitiveType.BINARY))))); } - public boolean isBinary(TypeName returnType) { + boolean isBinary(TypeName returnType) { return returnType.equals(baseType(Type.primitive(PrimitiveType.BINARY))); } } diff --git a/readme.md b/readme.md index b837afec4..b1e8c0c8e 100644 --- a/readme.md +++ b/readme.md @@ -51,8 +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 - For endpoints with errors associated with them, generate methods which return result types in Dialogue clients + --generateDialogueEndpointErrorResultTypes + This is an experimental feature. 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 From 3d88606563b8231b7f077cec42ade81c95e1cf08 Mon Sep 17 00:00:00 2001 From: Pritham Marupaka Date: Mon, 3 Mar 2025 12:18:15 -0500 Subject: [PATCH 23/24] review changes --- .../palantir/product/ComplicatedObject.java | 167 +++++++++++++++++ .../com/palantir/product/ErrorService.java | 4 +- .../palantir/product/ErrorServiceAsync.java | 18 +- .../product/ErrorServiceBlocking.java | 18 +- .../product/ErrorServiceEndpoints.java | 3 +- .../com/palantir/product/StringAlias.java | 55 ++++++ .../com/palantir/product/TestErrors.java | 13 ++ .../palantir/product/TestServerErrors.java | 32 ++++ .../palantir/product/ComplicatedObject.java | 172 ++++++++++++++++++ .../product/ErrorServiceEndpoints.java | 3 +- .../com/palantir/product/StringAlias.java | 55 ++++++ .../com/palantir/product/TestErrors.java | 9 + .../palantir/product/TestServerErrors.java | 33 ++++ .../product/UndertowErrorService.java | 4 +- .../com/palantir/conjure/java/Options.java | 2 +- .../DefaultStaticFactoryMethodGenerator.java | 10 +- .../palantir/conjure/java/ErrorResource.java | 11 +- .../conjure/java/UndertowServiceEteTest.java | 11 ++ .../resources/example-endpoint-errors.yml | 12 ++ readme.md | 10 +- 20 files changed, 623 insertions(+), 19 deletions(-) create mode 100644 conjure-java-core/src/integrationInput/java/dialogueendpointresulttypes/com/palantir/product/ComplicatedObject.java create mode 100644 conjure-java-core/src/integrationInput/java/dialogueendpointresulttypes/com/palantir/product/StringAlias.java create mode 100644 conjure-java-core/src/integrationInput/java/undertow/com/palantir/product/ComplicatedObject.java create mode 100644 conjure-java-core/src/integrationInput/java/undertow/com/palantir/product/StringAlias.java 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/ErrorService.java b/conjure-java-core/src/integrationInput/java/dialogueendpointresulttypes/com/palantir/product/ErrorService.java index c05960ef0..98ce72748 100644 --- 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 @@ -26,11 +26,13 @@ String testImportedError(AuthHeader authHeader, boolean shouldThrowError) * @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; + dialogueendpointresulttypes.com.palantir.another.EndpointSpecificServerErrors.DifferentPackage, + TestServerErrors.ComplicatedParameters; /** * @apiNote {@code POST /errors/empty} 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 index b80c9d794..54ba9f710 100644 --- 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 @@ -129,6 +129,10 @@ static ErrorServiceAsync of(EndpointChannelFactory _endpointChannelFactory, Conj .DIFFERENT_PACKAGE .name(), new TypeMarker() {}) + .error( + TestErrors.COMPLICATED_PARAMETERS.name(), + new TypeMarker< + TestMultipleErrorsAndPackagesResponse.ComplicatedParameters>() {}) .build()); private final Serializer testEmptyBodySerializer = @@ -311,7 +315,8 @@ sealed interface TestMultipleErrorsAndPackagesResponse TestMultipleErrorsAndPackagesResponse.InvalidArgument, TestMultipleErrorsAndPackagesResponse.NotFound, TestMultipleErrorsAndPackagesResponse.DifferentNamespace, - TestMultipleErrorsAndPackagesResponse.DifferentPackage { + TestMultipleErrorsAndPackagesResponse.DifferentPackage, + TestMultipleErrorsAndPackagesResponse.ComplicatedParameters { record Success(@JsonValue String value) implements TestMultipleErrorsAndPackagesResponse { public Success { Preconditions.checkArgumentNotNull(value, "value cannot be null"); @@ -372,6 +377,17 @@ final class DifferentPackage .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 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 index 0b5e5da6e..3ca2dd684 100644 --- 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 @@ -127,6 +127,10 @@ static ErrorServiceBlocking of(EndpointChannelFactory _endpointChannelFactory, C .DIFFERENT_PACKAGE .name(), new TypeMarker() {}) + .error( + TestErrors.COMPLICATED_PARAMETERS.name(), + new TypeMarker< + TestMultipleErrorsAndPackagesResponse.ComplicatedParameters>() {}) .build()); private final Serializer testEmptyBodySerializer = @@ -308,7 +312,8 @@ sealed interface TestMultipleErrorsAndPackagesResponse TestMultipleErrorsAndPackagesResponse.InvalidArgument, TestMultipleErrorsAndPackagesResponse.NotFound, TestMultipleErrorsAndPackagesResponse.DifferentNamespace, - TestMultipleErrorsAndPackagesResponse.DifferentPackage { + TestMultipleErrorsAndPackagesResponse.DifferentPackage, + TestMultipleErrorsAndPackagesResponse.ComplicatedParameters { record Success(@JsonValue String value) implements TestMultipleErrorsAndPackagesResponse { public Success { Preconditions.checkArgumentNotNull(value, "value cannot be null"); @@ -369,6 +374,17 @@ final class DifferentPackage .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 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 index 8cd868eca..709cf8f69 100644 --- 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 @@ -163,7 +163,8 @@ private static final class TestMultipleErrorsAndPackagesEndpoint implements Http public void handleRequest(HttpServerExchange exchange) throws IOException, TestServerErrors.InvalidArgument, TestServerErrors.NotFound, EndpointSpecificTwoServerErrors.DifferentNamespace, - dialogueendpointresulttypes.com.palantir.another.EndpointSpecificServerErrors.DifferentPackage { + 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); 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 index 7718ce9a5..da9da0dfd 100644 --- 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 @@ -6,10 +6,14 @@ 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"); @@ -17,6 +21,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"); @@ -29,6 +39,9 @@ public static boolean isNotFound(RemoteException remoteException) { 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) {} 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 index a83f41074..8ebc6a388 100644 --- 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 @@ -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; @@ -12,6 +13,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); } @@ -28,6 +39,20 @@ 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. * @@ -54,6 +79,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/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 3f0d6a204..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 @@ -163,7 +163,8 @@ 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); Optional errorToThrow = deserializer.deserialize(exchange); String result = delegate.testMultipleErrorsAndPackages(authHeader, errorToThrow); 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 9f0deedf8..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 @@ -26,11 +26,13 @@ String testImportedError(AuthHeader authHeader, boolean shouldThrowError) * @throws TestServerErrors.NotFound Something was not found. * @throws EndpointSpecificTwoServerErrors.DifferentNamespace * @throws undertow.com.palantir.another.EndpointSpecificServerErrors.DifferentPackage + * @throws TestServerErrors.ComplicatedParameters */ 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} 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 016917971..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 @@ -188,7 +188,7 @@ default boolean preferObjectBuilders() { } /** - * Warning: this is an experimental feature. + * 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 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 2fc889433..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 @@ -120,9 +120,7 @@ public MethodSpec generate(ServiceDefinition def) { def.getServiceName().getPackage(), options.packagePrefix()), className.simpleName(), ErrorGenerationUtils.endpointResponseResultTypeName(endpoint.getEndpointName())), - endpoint, - endpoint.getEndpointName(), - endpoint.getReturns()) + endpoint) .ifPresent(impl::addField); impl.addMethod(clientImpl(className, endpoint)); }); @@ -174,8 +172,8 @@ private Optional serializer(EndpointName endpointName, Type type) { .build()); } - private Optional deserializer( - TypeName responseType, EndpointDefinition endpointDef, EndpointName endpointName, Optional type) { + private Optional deserializer(TypeName responseType, EndpointDefinition endpointDef) { + Optional type = endpointDef.getReturns(); TypeName className = Primitives.box(returnTypes.baseType(type)); boolean generateResultTypes = ErrorGenerationUtils.shouldGenerateResultTypesForEndpoint( options.generateDialogueEndpointErrorResultTypes(), endpointDef); @@ -194,7 +192,7 @@ private Optional deserializer( ? 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()); 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 index 96c9fd1c2..ad2274e8d 100644 --- 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 @@ -18,16 +18,21 @@ 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 { @@ -53,7 +58,8 @@ public String testImportedError(AuthHeader authHeader, boolean shouldThrowError) @Override public String testMultipleErrorsAndPackages(AuthHeader authHeader, Optional errorToThrow) throws InvalidArgument, NotFound, DifferentNamespace, - dialogueendpointresulttypes.com.palantir.another.EndpointSpecificServerErrors.DifferentPackage { + dialogueendpointresulttypes.com.palantir.another.EndpointSpecificServerErrors.DifferentPackage, + ComplicatedParameters { if (errorToThrow.isPresent()) { String error = errorToThrow.get(); switch (error) { @@ -66,6 +72,9 @@ public String testMultipleErrorsAndPackages(AuthHeader authHeader, Optional { + 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 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 f2aecf388..c02a08807 100644 --- a/conjure-java-core/src/test/resources/example-endpoint-errors.yml +++ b/conjure-java-core/src/test/resources/example-endpoint-errors.yml @@ -9,6 +9,12 @@ types: - PRESENT - ABSENT - ERROR + StringAlias: + alias: string + ComplicatedObject: + fields: + fieldOne: list + fieldTwo: optional errors: InvalidArgument: namespace: Test @@ -22,6 +28,11 @@ types: code: NOT_FOUND safe-args: resource: string + ComplicatedParameters: + namespace: Test + code: INTERNAL + safe-args: + complicatedObjectMap: map services: ErrorService: name: Error Service @@ -54,6 +65,7 @@ services: docs: Something was not found. - importedErrors.DifferentNamespace - importedErrors.DifferentPackage + - ComplicatedParameters testEmptyBody: http: POST /empty args: diff --git a/readme.md b/readme.md index b1e8c0c8e..46b04c7af 100644 --- a/readme.md +++ b/readme.md @@ -52,11 +52,11 @@ The recommended way to use conjure-java is via a build tool like [gradle-conjure 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. 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. + 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 From 0840c97a840363a61424ff05f7dd61e350d7129e Mon Sep 17 00:00:00 2001 From: Pritham Marupaka Date: Mon, 3 Mar 2025 13:02:12 -0500 Subject: [PATCH 24/24] Use new dialogue version --- versions.lock | 105 +++++++++++++++++++++++++------------------------ versions.props | 2 +- 2 files changed, 54 insertions(+), 53 deletions(-) diff --git a/versions.lock b/versions.lock index 737489610..e3e48f1e5 100644 --- a/versions.lock +++ b/versions.lock @@ -1,67 +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: 6b950530) -com.fasterxml.jackson.core:jackson-core:2.18.2 (22 constraints: 34c1632c) -com.fasterxml.jackson.core:jackson-databind:2.18.2 (35 constraints: 129b6b75) -com.fasterxml.jackson.dataformat:jackson-dataformat-cbor:2.18.2 (4 constraints: ea652851) -com.fasterxml.jackson.dataformat:jackson-dataformat-smile:2.18.2 (1 constraints: 811ca8a4) +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: f839a626) -com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.18.2 (4 constraints: 303f5992) -com.fasterxml.jackson.datatype:jackson-datatype-joda:2.18.2 (2 constraints: 322b4db0) -com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.18.2 (2 constraints: 322b4db0) -com.github.ben-manes.caffeine:caffeine:3.1.8 (11 constraints: b0c0b031) +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 (29 constraints: 8be16a7d) +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.4.0-jre (37 constraints: 62af88b0) +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.57.0 (7 constraints: 5a81073a) -com.palantir.conjure.java.api:service-config:2.57.0 (6 constraints: 4366d3dc) -com.palantir.conjure.java.api:ssl-config:2.57.0 (5 constraints: bd4f6230) -com.palantir.conjure.java.runtime:client-config:8.16.0 (6 constraints: 846ba0f0) -com.palantir.conjure.java.runtime:conjure-java-jackson-optimizations:8.16.0 (1 constraints: 851cc6a4) -com.palantir.conjure.java.runtime:conjure-java-jackson-serialization:8.16.0 (4 constraints: f5461996) -com.palantir.conjure.java.runtime:keystores:8.16.0 (3 constraints: ea297a45) -com.palantir.deadlines:deadlines:0.8.0 (1 constraints: 0a050336) -com.palantir.dialogue:dialogue-apache-hc5-client:4.8.0-rc2 (2 constraints: 452a2e5c) -com.palantir.dialogue:dialogue-blocking-channels:4.8.0-rc2 (2 constraints: df251191) -com.palantir.dialogue:dialogue-clients:4.8.0-rc2 (1 constraints: 4206514d) -com.palantir.dialogue:dialogue-core:4.8.0-rc2 (3 constraints: 483f3226) -com.palantir.dialogue:dialogue-futures:4.8.0-rc2 (3 constraints: d736729d) -com.palantir.dialogue:dialogue-serde:4.8.0-rc2 (3 constraints: 8630e391) -com.palantir.dialogue:dialogue-target:4.8.0-rc2 (7 constraints: fb7ef538) +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.8.0 (3 constraints: c92457f4) -com.palantir.safe-logging:logger:3.7.0 (24 constraints: 25a2e775) -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 (31 constraints: 3c17f802) -com.palantir.safe-logging:safe-logging:3.7.0 (28 constraints: a1ea9f05) +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: 4d7b1001) -com.palantir.tracing:tracing-api:6.20.0 (6 constraints: 7655474c) -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-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: 57eab29e) +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 (28 constraints: 9de210ce) +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) @@ -69,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.3.1 (3 constraints: 6439b10d) +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 (5 constraints: d447261e) +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) @@ -83,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) @@ -97,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: 431985bb) +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) @@ -110,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.57.0 (1 constraints: 3f05513b) -com.palantir.conjure.java.runtime:conjure-java-annotations:8.16.0 (1 constraints: c5188a9e) -com.palantir.conjure.java.runtime:conjure-java-jaxrs-client:8.16.0 (1 constraints: 11052836) -com.palantir.conjure.java.runtime:conjure-java-jersey-server:8.16.0 (1 constraints: 11052836) -com.palantir.conjure.java.runtime:conjure-java-legacy-clients:8.16.0 (2 constraints: d51d2032) -com.palantir.conjure.java.runtime:refresh-utils:8.16.0 (1 constraints: c5188a9e) -com.palantir.safe-logging:preconditions-assertj:3.7.0 (1 constraints: 0c050f36) -com.palantir.tracing:tracing-jersey:6.20.0 (1 constraints: 401989bb) +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) @@ -148,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 9769b1ea4..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:* = 4.8.0-rc2 +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