From 99b0b006f5241b21bbf15ee78bc552a326f53e66 Mon Sep 17 00:00:00 2001 From: Patrick Koenig Date: Tue, 6 Nov 2018 10:18:23 -0800 Subject: [PATCH 1/2] Always set Accept header for retrofit clients --- .../services/Retrofit2ServiceGenerator.java | 23 ++++++++++--------- .../api/TestServiceRetrofit.java.retrofit | 9 +++++--- ...eRetrofit.java.retrofit_completable_future | 9 +++++--- ...ceRetrofit.java.retrofit_listenable_future | 9 +++++--- 4 files changed, 30 insertions(+), 20 deletions(-) diff --git a/conjure-java-core/src/main/java/com/palantir/conjure/java/services/Retrofit2ServiceGenerator.java b/conjure-java-core/src/main/java/com/palantir/conjure/java/services/Retrofit2ServiceGenerator.java index bbb1ffd41..0d4d64a03 100644 --- a/conjure-java-core/src/main/java/com/palantir/conjure/java/services/Retrofit2ServiceGenerator.java +++ b/conjure-java-core/src/main/java/com/palantir/conjure/java/services/Retrofit2ServiceGenerator.java @@ -131,25 +131,26 @@ private MethodSpec generateServiceMethod( EndpointDefinition endpointDef, TypeMapper returnTypeMapper, TypeMapper methodTypeMapper) { + boolean returnsBinary = endpointDef.getReturns() + .map(type -> type.accept(TypeVisitor.IS_BINARY)) + .orElse(false); + String returnsMediaType = returnsBinary + ? MediaType.APPLICATION_OCTET_STREAM + : MediaType.APPLICATION_JSON; + Set encodedPathArgs = extractEncodedPathArgs(endpointDef.getHttpPath()); HttpPath endpointPathWithoutRegex = replaceEncodedPathArgs(endpointDef.getHttpPath()); MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder(endpointDef.getEndpointName().get()) .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT) .addAnnotation(AnnotationSpec.builder(httpMethodToClassName(endpointDef.getHttpMethod().get().name())) .addMember("value", "$S", "." + endpointPathWithoutRegex) + .build()) + .addAnnotation(AnnotationSpec.builder(ClassName.get("retrofit2.http", "Headers")) + .addMember("value", "$S", "hr-path-template: " + endpointPathWithoutRegex) + .addMember("value", "$S", "Accept: " + returnsMediaType) .build()); - AnnotationSpec.Builder headersBuilder = AnnotationSpec.builder(ClassName.get("retrofit2.http", "Headers")) - .addMember("value", "$S", "hr-path-template: " + endpointPathWithoutRegex); - endpointDef.getReturns().ifPresent(type -> { - String mediaType = type.accept(TypeVisitor.IS_BINARY) - ? MediaType.APPLICATION_OCTET_STREAM - : MediaType.APPLICATION_JSON; - headersBuilder.addMember("value", "$S", "Accept: " + mediaType); - }); - methodBuilder.addAnnotation(headersBuilder.build()); - - if (endpointDef.getReturns().map(type -> type.accept(TypeVisitor.IS_BINARY)).orElse(false)) { + if (returnsBinary) { methodBuilder.addAnnotation(AnnotationSpec.builder(ClassName.get("retrofit2.http", "Streaming")).build()); } diff --git a/conjure-java-core/src/test/resources/test/api/TestServiceRetrofit.java.retrofit b/conjure-java-core/src/test/resources/test/api/TestServiceRetrofit.java.retrofit index 688851a2d..2ae1915f0 100644 --- a/conjure-java-core/src/test/resources/test/api/TestServiceRetrofit.java.retrofit +++ b/conjure-java-core/src/test/resources/test/api/TestServiceRetrofit.java.retrofit @@ -91,7 +91,7 @@ public interface TestServiceRetrofit { @Path("datasetRid") ResourceIdentifier datasetRid); @POST("./catalog/datasets/upload-raw") - @Headers("hr-path-template: /catalog/datasets/upload-raw") + @Headers({"hr-path-template: /catalog/datasets/upload-raw", "Accept: application/json"}) Call uploadRawData( @Header("Authorization") AuthHeader authHeader, @Body RequestBody input); @@ -150,7 +150,10 @@ public interface TestServiceRetrofit { @Query("optionalEnd") Optional optionalEnd); @POST("./catalog/test-no-response-query-params") - @Headers("hr-path-template: /catalog/test-no-response-query-params") + @Headers({ + "hr-path-template: /catalog/test-no-response-query-params", + "Accept: application/json" + }) Call testNoResponseQueryParams( @Header("Authorization") AuthHeader authHeader, @Body String query, @@ -178,7 +181,7 @@ public interface TestServiceRetrofit { @Header("Authorization") AuthHeader authHeader, @Body Optional maybeString); @GET("./catalog/optional-integer-double") - @Headers("hr-path-template: /catalog/optional-integer-double") + @Headers({"hr-path-template: /catalog/optional-integer-double", "Accept: application/json"}) Call testOptionalIntegerAndDouble( @Header("Authorization") AuthHeader authHeader, @Query("maybeInteger") OptionalInt maybeInteger, diff --git a/conjure-java-core/src/test/resources/test/api/TestServiceRetrofit.java.retrofit_completable_future b/conjure-java-core/src/test/resources/test/api/TestServiceRetrofit.java.retrofit_completable_future index a3f071ea5..77ecd8ef0 100644 --- a/conjure-java-core/src/test/resources/test/api/TestServiceRetrofit.java.retrofit_completable_future +++ b/conjure-java-core/src/test/resources/test/api/TestServiceRetrofit.java.retrofit_completable_future @@ -91,7 +91,7 @@ public interface TestServiceRetrofit { @Path("datasetRid") ResourceIdentifier datasetRid); @POST("./catalog/datasets/upload-raw") - @Headers("hr-path-template: /catalog/datasets/upload-raw") + @Headers({"hr-path-template: /catalog/datasets/upload-raw", "Accept: application/json"}) CompletableFuture uploadRawData( @Header("Authorization") AuthHeader authHeader, @Body RequestBody input); @@ -150,7 +150,10 @@ public interface TestServiceRetrofit { @Query("optionalEnd") Optional optionalEnd); @POST("./catalog/test-no-response-query-params") - @Headers("hr-path-template: /catalog/test-no-response-query-params") + @Headers({ + "hr-path-template: /catalog/test-no-response-query-params", + "Accept: application/json" + }) CompletableFuture testNoResponseQueryParams( @Header("Authorization") AuthHeader authHeader, @Body String query, @@ -178,7 +181,7 @@ public interface TestServiceRetrofit { @Header("Authorization") AuthHeader authHeader, @Body Optional maybeString); @GET("./catalog/optional-integer-double") - @Headers("hr-path-template: /catalog/optional-integer-double") + @Headers({"hr-path-template: /catalog/optional-integer-double", "Accept: application/json"}) CompletableFuture testOptionalIntegerAndDouble( @Header("Authorization") AuthHeader authHeader, @Query("maybeInteger") OptionalInt maybeInteger, diff --git a/conjure-java-core/src/test/resources/test/api/TestServiceRetrofit.java.retrofit_listenable_future b/conjure-java-core/src/test/resources/test/api/TestServiceRetrofit.java.retrofit_listenable_future index c2db14cca..1a4433311 100644 --- a/conjure-java-core/src/test/resources/test/api/TestServiceRetrofit.java.retrofit_listenable_future +++ b/conjure-java-core/src/test/resources/test/api/TestServiceRetrofit.java.retrofit_listenable_future @@ -91,7 +91,7 @@ public interface TestServiceRetrofit { @Path("datasetRid") ResourceIdentifier datasetRid); @POST("./catalog/datasets/upload-raw") - @Headers("hr-path-template: /catalog/datasets/upload-raw") + @Headers({"hr-path-template: /catalog/datasets/upload-raw", "Accept: application/json"}) ListenableFuture uploadRawData( @Header("Authorization") AuthHeader authHeader, @Body RequestBody input); @@ -150,7 +150,10 @@ public interface TestServiceRetrofit { @Query("optionalEnd") Optional optionalEnd); @POST("./catalog/test-no-response-query-params") - @Headers("hr-path-template: /catalog/test-no-response-query-params") + @Headers({ + "hr-path-template: /catalog/test-no-response-query-params", + "Accept: application/json" + }) ListenableFuture testNoResponseQueryParams( @Header("Authorization") AuthHeader authHeader, @Body String query, @@ -178,7 +181,7 @@ public interface TestServiceRetrofit { @Header("Authorization") AuthHeader authHeader, @Body Optional maybeString); @GET("./catalog/optional-integer-double") - @Headers("hr-path-template: /catalog/optional-integer-double") + @Headers({"hr-path-template: /catalog/optional-integer-double", "Accept: application/json"}) ListenableFuture testOptionalIntegerAndDouble( @Header("Authorization") AuthHeader authHeader, @Query("maybeInteger") OptionalInt maybeInteger, From 9f8ab274f86619f59668c05ee22292fc87527d0b Mon Sep 17 00:00:00 2001 From: Patrick Koenig Date: Fri, 16 Nov 2018 17:46:10 -0800 Subject: [PATCH 2/2] Correctly resolve binary aliases --- .../java/services/JerseyServiceGenerator.java | 15 ++- .../services/Retrofit2ServiceGenerator.java | 45 +++++---- ...a => BinaryResolvingClassNameVisitor.java} | 41 ++++++-- .../JerseyMethodTypeClassNameVisitor.java | 77 --------------- .../JerseyReturnTypeClassNameVisitor.java | 95 ------------------- .../Retrofit2MethodTypeClassNameVisitor.java | 79 --------------- .../conjure/java/types/TypeMapper.java | 3 +- .../java/util/BinaryReturnTypeResolver.java | 66 ------------- .../src/test/resources/example-service.yml | 7 ++ .../test/api/TestService.java.jersey | 6 ++ .../TestService.java.jersey_require_not_null | 6 ++ .../api/TestServiceRetrofit.java.retrofit | 8 +- ...eRetrofit.java.retrofit_completable_future | 8 +- ...ceRetrofit.java.retrofit_listenable_future | 8 +- 14 files changed, 105 insertions(+), 359 deletions(-) rename conjure-java-core/src/main/java/com/palantir/conjure/java/types/{Retrofit2ReturnTypeClassNameVisitor.java => BinaryResolvingClassNameVisitor.java} (63%) delete mode 100644 conjure-java-core/src/main/java/com/palantir/conjure/java/types/JerseyMethodTypeClassNameVisitor.java delete mode 100644 conjure-java-core/src/main/java/com/palantir/conjure/java/types/JerseyReturnTypeClassNameVisitor.java delete mode 100644 conjure-java-core/src/main/java/com/palantir/conjure/java/types/Retrofit2MethodTypeClassNameVisitor.java delete mode 100644 conjure-java-core/src/main/java/com/palantir/conjure/java/util/BinaryReturnTypeResolver.java diff --git a/conjure-java-core/src/main/java/com/palantir/conjure/java/services/JerseyServiceGenerator.java b/conjure-java-core/src/main/java/com/palantir/conjure/java/services/JerseyServiceGenerator.java index 0cf9a5e3e..4345cabad 100644 --- a/conjure-java-core/src/main/java/com/palantir/conjure/java/services/JerseyServiceGenerator.java +++ b/conjure-java-core/src/main/java/com/palantir/conjure/java/services/JerseyServiceGenerator.java @@ -23,8 +23,7 @@ import com.google.common.collect.Lists; import com.palantir.conjure.java.ConjureAnnotations; import com.palantir.conjure.java.FeatureFlags; -import com.palantir.conjure.java.types.JerseyMethodTypeClassNameVisitor; -import com.palantir.conjure.java.types.JerseyReturnTypeClassNameVisitor; +import com.palantir.conjure.java.types.BinaryResolvingClassNameVisitor; import com.palantir.conjure.java.types.TypeMapper; import com.palantir.conjure.spec.ArgumentDefinition; import com.palantir.conjure.spec.AuthType; @@ -53,6 +52,7 @@ import com.squareup.javapoet.MethodSpec; import com.squareup.javapoet.ParameterSpec; import com.squareup.javapoet.TypeSpec; +import java.io.InputStream; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -65,9 +65,12 @@ import java.util.stream.Collectors; import java.util.stream.IntStream; import javax.lang.model.element.Modifier; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.StreamingOutput; import org.apache.commons.lang3.StringUtils; public final class JerseyServiceGenerator implements ServiceGenerator { + private static final ClassName NOT_NULL = ClassName.get("javax.validation.constraints", "NotNull"); private final Set experimentalFeatures; @@ -82,11 +85,15 @@ public JerseyServiceGenerator(Set experimentalFeatures) { @Override public Set generate(ConjureDefinition conjureDefinition) { + Class binaryReturnType = experimentalFeatures.contains(FeatureFlags.JerseyBinaryAsResponse) + ? Response.class + : StreamingOutput.class; TypeMapper returnTypeMapper = new TypeMapper( conjureDefinition.getTypes(), - types -> new JerseyReturnTypeClassNameVisitor(types, experimentalFeatures)); + BinaryResolvingClassNameVisitor.createFactory(binaryReturnType)); TypeMapper methodTypeMapper = new TypeMapper( - conjureDefinition.getTypes(), JerseyMethodTypeClassNameVisitor::new); + conjureDefinition.getTypes(), + BinaryResolvingClassNameVisitor.createFactory(InputStream.class)); return conjureDefinition.getServices().stream() .map(serviceDef -> generateService(serviceDef, returnTypeMapper, methodTypeMapper)) .collect(Collectors.toSet()); diff --git a/conjure-java-core/src/main/java/com/palantir/conjure/java/services/Retrofit2ServiceGenerator.java b/conjure-java-core/src/main/java/com/palantir/conjure/java/services/Retrofit2ServiceGenerator.java index 0d4d64a03..f78a63d1c 100644 --- a/conjure-java-core/src/main/java/com/palantir/conjure/java/services/Retrofit2ServiceGenerator.java +++ b/conjure-java-core/src/main/java/com/palantir/conjure/java/services/Retrofit2ServiceGenerator.java @@ -23,8 +23,7 @@ import com.google.common.collect.Lists; import com.palantir.conjure.java.ConjureAnnotations; import com.palantir.conjure.java.FeatureFlags; -import com.palantir.conjure.java.types.Retrofit2MethodTypeClassNameVisitor; -import com.palantir.conjure.java.types.Retrofit2ReturnTypeClassNameVisitor; +import com.palantir.conjure.java.types.BinaryResolvingClassNameVisitor; import com.palantir.conjure.java.types.TypeMapper; import com.palantir.conjure.spec.ArgumentDefinition; import com.palantir.conjure.spec.ArgumentName; @@ -47,6 +46,7 @@ import com.squareup.javapoet.MethodSpec; import com.squareup.javapoet.ParameterSpec; import com.squareup.javapoet.ParameterizedTypeName; +import com.squareup.javapoet.TypeName; import com.squareup.javapoet.TypeSpec; import java.util.List; import java.util.Optional; @@ -68,8 +68,8 @@ public final class Retrofit2ServiceGenerator implements ServiceGenerator { private static final ClassName CALL_TYPE = ClassName.get("retrofit2", "Call"); private static final String AUTH_HEADER_NAME = "Authorization"; - private static final String JSON_MEDIA_TYPE = MediaType.APPLICATION_JSON; - private static final String OCTET_STREAM_MEDIA_TYPE = MediaType.APPLICATION_OCTET_STREAM; + private static final ClassName BINARY_METHOD_TYPE = ClassName.get("okhttp3", "RequestBody"); + private static final ClassName BINARY_RETURN_TYPE = ClassName.get("okhttp3", "ResponseBody"); private static final Logger log = LoggerFactory.getLogger(Retrofit2ServiceGenerator.class); @@ -85,10 +85,12 @@ public Retrofit2ServiceGenerator(Set experimentalFeatures) { @Override public Set generate(ConjureDefinition conjureDefinition) { - TypeMapper returnTypeMapper = - new TypeMapper(conjureDefinition.getTypes(), Retrofit2ReturnTypeClassNameVisitor::new); - TypeMapper methodTypeMapper = - new TypeMapper(conjureDefinition.getTypes(), Retrofit2MethodTypeClassNameVisitor::new); + TypeMapper returnTypeMapper = new TypeMapper( + conjureDefinition.getTypes(), + BinaryResolvingClassNameVisitor.createFactory(BINARY_RETURN_TYPE)); + TypeMapper methodTypeMapper = new TypeMapper( + conjureDefinition.getTypes(), + BinaryResolvingClassNameVisitor.createFactory(BINARY_METHOD_TYPE)); return conjureDefinition.getServices().stream() .map(serviceDef -> generateService(serviceDef, returnTypeMapper, methodTypeMapper)) .collect(Collectors.toSet()); @@ -131,12 +133,9 @@ private MethodSpec generateServiceMethod( EndpointDefinition endpointDef, TypeMapper returnTypeMapper, TypeMapper methodTypeMapper) { - boolean returnsBinary = endpointDef.getReturns() - .map(type -> type.accept(TypeVisitor.IS_BINARY)) - .orElse(false); - String returnsMediaType = returnsBinary - ? MediaType.APPLICATION_OCTET_STREAM - : MediaType.APPLICATION_JSON; + TypeName returnType = endpointDef.getReturns() + .map(returnTypeMapper::getClassName) + .orElse(ClassName.VOID); Set encodedPathArgs = extractEncodedPathArgs(endpointDef.getHttpPath()); HttpPath endpointPathWithoutRegex = replaceEncodedPathArgs(endpointDef.getHttpPath()); @@ -147,10 +146,10 @@ private MethodSpec generateServiceMethod( .build()) .addAnnotation(AnnotationSpec.builder(ClassName.get("retrofit2.http", "Headers")) .addMember("value", "$S", "hr-path-template: " + endpointPathWithoutRegex) - .addMember("value", "$S", "Accept: " + returnsMediaType) + .addMember("value", "$S", "Accept: " + getReturnMediaType(returnType)) .build()); - if (returnsBinary) { + if (returnType.equals(BINARY_RETURN_TYPE)) { methodBuilder.addAnnotation(AnnotationSpec.builder(ClassName.get("retrofit2.http", "Streaming")).build()); } @@ -160,13 +159,7 @@ private MethodSpec generateServiceMethod( ServiceGenerator.getJavaDoc(endpointDef).ifPresent( content -> methodBuilder.addJavadoc("$L", content)); - if (endpointDef.getReturns().isPresent()) { - methodBuilder.returns( - ParameterizedTypeName.get(getReturnType(), - returnTypeMapper.getClassName(endpointDef.getReturns().get()).box())); - } else { - methodBuilder.returns(ParameterizedTypeName.get(getReturnType(), ClassName.get(Void.class))); - } + methodBuilder.returns(ParameterizedTypeName.get(getReturnType(), returnType.box())); getAuthParameter(endpointDef.getAuth()).ifPresent(methodBuilder::addParameter); @@ -261,6 +254,12 @@ private Optional getAuthParameter(Optional auth) { throw new IllegalStateException("Unrecognized auth type: " + auth.get()); } + private static String getReturnMediaType(TypeName returnType) { + return returnType.equals(BINARY_RETURN_TYPE) + ? MediaType.APPLICATION_OCTET_STREAM + : MediaType.APPLICATION_JSON; + } + private static Set createMarkers(TypeMapper typeMapper, List markers) { checkArgument(markers.stream().allMatch(type -> type.accept(TypeVisitor.IS_REFERENCE)), "Markers must refer to reference types."); diff --git a/conjure-java-core/src/main/java/com/palantir/conjure/java/types/Retrofit2ReturnTypeClassNameVisitor.java b/conjure-java-core/src/main/java/com/palantir/conjure/java/types/BinaryResolvingClassNameVisitor.java similarity index 63% rename from conjure-java-core/src/main/java/com/palantir/conjure/java/types/Retrofit2ReturnTypeClassNameVisitor.java rename to conjure-java-core/src/main/java/com/palantir/conjure/java/types/BinaryResolvingClassNameVisitor.java index 27276d05d..4929db269 100644 --- a/conjure-java-core/src/main/java/com/palantir/conjure/java/types/Retrofit2ReturnTypeClassNameVisitor.java +++ b/conjure-java-core/src/main/java/com/palantir/conjure/java/types/BinaryResolvingClassNameVisitor.java @@ -16,13 +16,13 @@ package com.palantir.conjure.java.types; -import com.palantir.conjure.java.util.BinaryReturnTypeResolver; import com.palantir.conjure.spec.ExternalReference; import com.palantir.conjure.spec.ListType; import com.palantir.conjure.spec.MapType; import com.palantir.conjure.spec.OptionalType; import com.palantir.conjure.spec.PrimitiveType; import com.palantir.conjure.spec.SetType; +import com.palantir.conjure.spec.Type; import com.palantir.conjure.spec.TypeDefinition; import com.palantir.conjure.visitor.TypeDefinitionVisitor; import com.squareup.javapoet.ClassName; @@ -32,17 +32,26 @@ import java.util.function.Function; import java.util.stream.Collectors; -public final class Retrofit2ReturnTypeClassNameVisitor implements ClassNameVisitor { - - private static final ClassName RESPONSE_BODY_TYPE = ClassName.get("okhttp3", "ResponseBody"); +public final class BinaryResolvingClassNameVisitor implements ClassNameVisitor { private final DefaultClassNameVisitor delegate; private final Map types; + private final ClassName binaryClassName; + + public static Factory createFactory(Class binaryClass) { + return createFactory(ClassName.get(binaryClass)); + } + + public static Factory createFactory(ClassName binaryClassName) { + return types -> new BinaryResolvingClassNameVisitor(types, binaryClassName); + } - public Retrofit2ReturnTypeClassNameVisitor(List types) { - this.types = types.stream().collect( - Collectors.toMap(t -> t.accept(TypeDefinitionVisitor.TYPE_NAME), Function.identity())); + private BinaryResolvingClassNameVisitor(List types, ClassName binaryClassName) { this.delegate = new DefaultClassNameVisitor(types); + this.types = types.stream().collect(Collectors.toMap( + t -> t.accept(TypeDefinitionVisitor.TYPE_NAME), + Function.identity())); + this.binaryClassName = binaryClassName; } @Override @@ -63,7 +72,7 @@ public TypeName visitOptional(OptionalType type) { @Override public TypeName visitPrimitive(PrimitiveType type) { if (type.get() == PrimitiveType.Value.BINARY) { - return RESPONSE_BODY_TYPE; + return binaryClassName; } else { return delegate.visitPrimitive(type); } @@ -71,7 +80,20 @@ public TypeName visitPrimitive(PrimitiveType type) { @Override public TypeName visitReference(com.palantir.conjure.spec.TypeName type) { - return BinaryReturnTypeResolver.resolveReturnReferenceType(types, type, RESPONSE_BODY_TYPE); + if (!types.containsKey(type)) { + throw new IllegalStateException("Unknown LocalReferenceType type: " + type); + } + + TypeDefinition def = types.get(type); + if (def.accept(TypeDefinitionVisitor.IS_ALIAS)) { + Type aliasType = def.accept(TypeDefinitionVisitor.ALIAS).getAlias(); + TypeName aliasClassName = aliasType.accept(this); + if (aliasClassName.equals(binaryClassName)) { + return binaryClassName; + } + } + + return delegate.visitReference(type); } @Override @@ -83,5 +105,4 @@ public TypeName visitExternal(ExternalReference type) { public TypeName visitSet(SetType type) { return delegate.visitSet(type); } - } diff --git a/conjure-java-core/src/main/java/com/palantir/conjure/java/types/JerseyMethodTypeClassNameVisitor.java b/conjure-java-core/src/main/java/com/palantir/conjure/java/types/JerseyMethodTypeClassNameVisitor.java deleted file mode 100644 index a033805ae..000000000 --- a/conjure-java-core/src/main/java/com/palantir/conjure/java/types/JerseyMethodTypeClassNameVisitor.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * (c) Copyright 2018 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.types; - -import com.palantir.conjure.spec.ExternalReference; -import com.palantir.conjure.spec.ListType; -import com.palantir.conjure.spec.MapType; -import com.palantir.conjure.spec.OptionalType; -import com.palantir.conjure.spec.PrimitiveType; -import com.palantir.conjure.spec.SetType; -import com.palantir.conjure.spec.TypeDefinition; -import com.squareup.javapoet.ClassName; -import com.squareup.javapoet.TypeName; -import java.io.InputStream; -import java.util.List; - -public final class JerseyMethodTypeClassNameVisitor implements ClassNameVisitor { - - private final DefaultClassNameVisitor delegate; - - public JerseyMethodTypeClassNameVisitor(List types) { - this.delegate = new DefaultClassNameVisitor(types); - } - - @Override - public TypeName visitList(ListType type) { - return delegate.visitList(type); - } - - @Override - public TypeName visitMap(MapType type) { - return delegate.visitMap(type); - } - - @Override - public TypeName visitOptional(OptionalType type) { - return delegate.visitOptional(type); - } - - @Override - public TypeName visitPrimitive(PrimitiveType type) { - if (type.get() == PrimitiveType.Value.BINARY) { - return ClassName.get(InputStream.class); - } else { - return delegate.visitPrimitive(type); - } - } - - @Override - public TypeName visitReference(com.palantir.conjure.spec.TypeName type) { - return delegate.visitReference(type); - } - - @Override - public TypeName visitExternal(ExternalReference type) { - return delegate.visitExternal(type); - } - - @Override - public TypeName visitSet(SetType type) { - return delegate.visitSet(type); - } -} diff --git a/conjure-java-core/src/main/java/com/palantir/conjure/java/types/JerseyReturnTypeClassNameVisitor.java b/conjure-java-core/src/main/java/com/palantir/conjure/java/types/JerseyReturnTypeClassNameVisitor.java deleted file mode 100644 index 6088f20e2..000000000 --- a/conjure-java-core/src/main/java/com/palantir/conjure/java/types/JerseyReturnTypeClassNameVisitor.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * (c) Copyright 2018 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.types; - -import com.palantir.conjure.java.FeatureFlags; -import com.palantir.conjure.java.util.BinaryReturnTypeResolver; -import com.palantir.conjure.spec.ExternalReference; -import com.palantir.conjure.spec.ListType; -import com.palantir.conjure.spec.MapType; -import com.palantir.conjure.spec.OptionalType; -import com.palantir.conjure.spec.PrimitiveType; -import com.palantir.conjure.spec.SetType; -import com.palantir.conjure.spec.TypeDefinition; -import com.palantir.conjure.visitor.TypeDefinitionVisitor; -import com.squareup.javapoet.ClassName; -import com.squareup.javapoet.TypeName; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.function.Function; -import java.util.stream.Collectors; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.StreamingOutput; - -public final class JerseyReturnTypeClassNameVisitor implements ClassNameVisitor { - - private final boolean binaryAsResponse; - private final DefaultClassNameVisitor delegate; - private final Map types; - - public JerseyReturnTypeClassNameVisitor(List types, Set featureFlags) { - this.types = types.stream().collect( - Collectors.toMap(t -> t.accept(TypeDefinitionVisitor.TYPE_NAME), Function.identity())); - this.delegate = new DefaultClassNameVisitor(types); - this.binaryAsResponse = featureFlags.contains(FeatureFlags.JerseyBinaryAsResponse); - } - - @Override - public TypeName visitList(ListType type) { - return delegate.visitList(type); - } - - @Override - public TypeName visitMap(MapType type) { - return delegate.visitMap(type); - } - - @Override - public TypeName visitOptional(OptionalType type) { - return delegate.visitOptional(type); - } - - @Override - public TypeName visitPrimitive(PrimitiveType type) { - if (type.get() == PrimitiveType.Value.BINARY) { - if (binaryAsResponse) { - return ClassName.get(Response.class); - } else { - return ClassName.get(StreamingOutput.class); - } - } - return delegate.visitPrimitive(type); - } - - @Override - public TypeName visitReference(com.palantir.conjure.spec.TypeName type) { - return BinaryReturnTypeResolver.resolveReturnReferenceType(types, type, - binaryAsResponse ? ClassName.get(Response.class) : ClassName.get(StreamingOutput.class)); - } - - @Override - public TypeName visitExternal(ExternalReference type) { - return delegate.visitExternal(type); - } - - @Override - public TypeName visitSet(SetType type) { - return delegate.visitSet(type); - } - -} diff --git a/conjure-java-core/src/main/java/com/palantir/conjure/java/types/Retrofit2MethodTypeClassNameVisitor.java b/conjure-java-core/src/main/java/com/palantir/conjure/java/types/Retrofit2MethodTypeClassNameVisitor.java deleted file mode 100644 index 256eedc48..000000000 --- a/conjure-java-core/src/main/java/com/palantir/conjure/java/types/Retrofit2MethodTypeClassNameVisitor.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * (c) Copyright 2018 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.types; - -import com.palantir.conjure.spec.ExternalReference; -import com.palantir.conjure.spec.ListType; -import com.palantir.conjure.spec.MapType; -import com.palantir.conjure.spec.OptionalType; -import com.palantir.conjure.spec.PrimitiveType; -import com.palantir.conjure.spec.SetType; -import com.palantir.conjure.spec.TypeDefinition; -import com.squareup.javapoet.ClassName; -import com.squareup.javapoet.TypeName; -import java.util.List; - -public final class Retrofit2MethodTypeClassNameVisitor implements ClassNameVisitor { - - private static final ClassName REQUEST_BODY_TYPE = ClassName.get("okhttp3", "RequestBody"); - - private final DefaultClassNameVisitor delegate; - - public Retrofit2MethodTypeClassNameVisitor(List types) { - this.delegate = new DefaultClassNameVisitor(types); - } - - @Override - public TypeName visitList(ListType type) { - return delegate.visitList(type); - } - - @Override - public TypeName visitMap(MapType type) { - return delegate.visitMap(type); - } - - @Override - public TypeName visitOptional(OptionalType type) { - return delegate.visitOptional(type); - } - - @Override - public TypeName visitPrimitive(PrimitiveType type) { - if (type.get() == PrimitiveType.Value.BINARY) { - return REQUEST_BODY_TYPE; - } else { - return delegate.visitPrimitive(type); - } - } - - @Override - public TypeName visitReference(com.palantir.conjure.spec.TypeName type) { - return delegate.visitReference(type); - } - - @Override - public TypeName visitExternal(ExternalReference type) { - return delegate.visitExternal(type); - } - - @Override - public TypeName visitSet(SetType type) { - return delegate.visitSet(type); - } - -} diff --git a/conjure-java-core/src/main/java/com/palantir/conjure/java/types/TypeMapper.java b/conjure-java-core/src/main/java/com/palantir/conjure/java/types/TypeMapper.java index 5f850bd48..10aa8ae27 100644 --- a/conjure-java-core/src/main/java/com/palantir/conjure/java/types/TypeMapper.java +++ b/conjure-java-core/src/main/java/com/palantir/conjure/java/types/TypeMapper.java @@ -37,8 +37,7 @@ public TypeMapper(List types) { this(types, DefaultClassNameVisitor::new); } - public TypeMapper(List types, - Factory classNameVisitorFactory) { + public TypeMapper(List types, Factory classNameVisitorFactory) { this.types = types.stream().collect( Collectors.toMap(t -> t.accept(TypeDefinitionVisitor.TYPE_NAME), Function.identity())); this.classNameVisitorFactory = classNameVisitorFactory; diff --git a/conjure-java-core/src/main/java/com/palantir/conjure/java/util/BinaryReturnTypeResolver.java b/conjure-java-core/src/main/java/com/palantir/conjure/java/util/BinaryReturnTypeResolver.java deleted file mode 100644 index f79f0ae33..000000000 --- a/conjure-java-core/src/main/java/com/palantir/conjure/java/util/BinaryReturnTypeResolver.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * (c) Copyright 2018 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.util; - -import com.palantir.conjure.spec.AliasDefinition; -import com.palantir.conjure.spec.Type; -import com.palantir.conjure.spec.TypeDefinition; -import com.palantir.conjure.visitor.TypeDefinitionVisitor; -import com.palantir.conjure.visitor.TypeVisitor; -import com.squareup.javapoet.ClassName; -import com.squareup.javapoet.TypeName; -import java.util.Map; - -public final class BinaryReturnTypeResolver { - - private BinaryReturnTypeResolver() {} - - /** - * Recursively examines nested local alias references to see if the leaf node is of a binary type. If so, - * this method resolves it to a binary type supplied by the {@code binaryTypeName}, otherwise it returns the - * immediate local reference {@code {@link TypeName}}. - * - * @param types - a map of name to conjure definition - * @param type - the given type to be resolved - * @param binaryTypeName - the return binary type name if the leaf node is of a binary type - * @return resolved return reference type - */ - public static TypeName resolveReturnReferenceType( - Map types, - com.palantir.conjure.spec.TypeName type, - TypeName binaryTypeName) { - - if (!types.containsKey(type)) { - throw new IllegalStateException("Unknown LocalReferenceType type: " + type); - } - - TypeDefinition def = types.get(type); - while (def.accept(TypeDefinitionVisitor.IS_ALIAS)) { - AliasDefinition aliasDefinition = def.accept(TypeDefinitionVisitor.ALIAS); - Type conjureType = aliasDefinition.getAlias(); - if (conjureType.accept(TypeVisitor.IS_REFERENCE)) { - def = types.get(conjureType.accept(TypeVisitor.REFERENCE)); - } else if (conjureType.accept(TypeVisitor.IS_BINARY)) { - return binaryTypeName; - } else { - break; - } - } - - return ClassName.get(type.getPackage(), type.getName()); - } -} diff --git a/conjure-java-core/src/test/resources/example-service.yml b/conjure-java-core/src/test/resources/example-service.yml index 0f33d769c..ce92f9b0a 100644 --- a/conjure-java-core/src/test/resources/example-service.yml +++ b/conjure-java-core/src/test/resources/example-service.yml @@ -122,6 +122,13 @@ services: type: binary param-type: body + uploadAliasedRawData: + http: POST /datasets/upload-raw-aliased + args: + input: + type: NestedAliasedBinary + param-type: body + getBranches: http: GET /datasets/{datasetRid}/branches args: diff --git a/conjure-java-core/src/test/resources/test/api/TestService.java.jersey b/conjure-java-core/src/test/resources/test/api/TestService.java.jersey index f4205c361..f3d7a0e42 100644 --- a/conjure-java-core/src/test/resources/test/api/TestService.java.jersey +++ b/conjure-java-core/src/test/resources/test/api/TestService.java.jersey @@ -87,6 +87,12 @@ public interface TestService { @HeaderParam("Authorization") @NotNull AuthHeader authHeader, @NotNull InputStream input); + @POST + @Path("catalog/datasets/upload-raw-aliased") + void uploadAliasedRawData( + @HeaderParam("Authorization") @NotNull AuthHeader authHeader, + @NotNull InputStream input); + @GET @Path("catalog/datasets/{datasetRid}/branches") Set getBranches( diff --git a/conjure-java-core/src/test/resources/test/api/TestService.java.jersey_require_not_null b/conjure-java-core/src/test/resources/test/api/TestService.java.jersey_require_not_null index f4205c361..f3d7a0e42 100644 --- a/conjure-java-core/src/test/resources/test/api/TestService.java.jersey_require_not_null +++ b/conjure-java-core/src/test/resources/test/api/TestService.java.jersey_require_not_null @@ -87,6 +87,12 @@ public interface TestService { @HeaderParam("Authorization") @NotNull AuthHeader authHeader, @NotNull InputStream input); + @POST + @Path("catalog/datasets/upload-raw-aliased") + void uploadAliasedRawData( + @HeaderParam("Authorization") @NotNull AuthHeader authHeader, + @NotNull InputStream input); + @GET @Path("catalog/datasets/{datasetRid}/branches") Set getBranches( diff --git a/conjure-java-core/src/test/resources/test/api/TestServiceRetrofit.java.retrofit b/conjure-java-core/src/test/resources/test/api/TestServiceRetrofit.java.retrofit index 2ae1915f0..9df8b5694 100644 --- a/conjure-java-core/src/test/resources/test/api/TestServiceRetrofit.java.retrofit +++ b/conjure-java-core/src/test/resources/test/api/TestServiceRetrofit.java.retrofit @@ -66,8 +66,9 @@ public interface TestServiceRetrofit { @GET("./catalog/datasets/{datasetRid}/raw-aliased") @Headers({ "hr-path-template: /catalog/datasets/{datasetRid}/raw-aliased", - "Accept: application/json" + "Accept: application/octet-stream" }) + @Streaming Call getAliasedRawData( @Header("Authorization") AuthHeader authHeader, @Path("datasetRid") ResourceIdentifier datasetRid); @@ -95,6 +96,11 @@ public interface TestServiceRetrofit { Call uploadRawData( @Header("Authorization") AuthHeader authHeader, @Body RequestBody input); + @POST("./catalog/datasets/upload-raw-aliased") + @Headers({"hr-path-template: /catalog/datasets/upload-raw-aliased", "Accept: application/json"}) + Call uploadAliasedRawData( + @Header("Authorization") AuthHeader authHeader, @Body RequestBody input); + @GET("./catalog/datasets/{datasetRid}/branches") @Headers({ "hr-path-template: /catalog/datasets/{datasetRid}/branches", diff --git a/conjure-java-core/src/test/resources/test/api/TestServiceRetrofit.java.retrofit_completable_future b/conjure-java-core/src/test/resources/test/api/TestServiceRetrofit.java.retrofit_completable_future index 77ecd8ef0..fff62b99e 100644 --- a/conjure-java-core/src/test/resources/test/api/TestServiceRetrofit.java.retrofit_completable_future +++ b/conjure-java-core/src/test/resources/test/api/TestServiceRetrofit.java.retrofit_completable_future @@ -66,8 +66,9 @@ public interface TestServiceRetrofit { @GET("./catalog/datasets/{datasetRid}/raw-aliased") @Headers({ "hr-path-template: /catalog/datasets/{datasetRid}/raw-aliased", - "Accept: application/json" + "Accept: application/octet-stream" }) + @Streaming CompletableFuture getAliasedRawData( @Header("Authorization") AuthHeader authHeader, @Path("datasetRid") ResourceIdentifier datasetRid); @@ -95,6 +96,11 @@ public interface TestServiceRetrofit { CompletableFuture uploadRawData( @Header("Authorization") AuthHeader authHeader, @Body RequestBody input); + @POST("./catalog/datasets/upload-raw-aliased") + @Headers({"hr-path-template: /catalog/datasets/upload-raw-aliased", "Accept: application/json"}) + CompletableFuture uploadAliasedRawData( + @Header("Authorization") AuthHeader authHeader, @Body RequestBody input); + @GET("./catalog/datasets/{datasetRid}/branches") @Headers({ "hr-path-template: /catalog/datasets/{datasetRid}/branches", diff --git a/conjure-java-core/src/test/resources/test/api/TestServiceRetrofit.java.retrofit_listenable_future b/conjure-java-core/src/test/resources/test/api/TestServiceRetrofit.java.retrofit_listenable_future index 1a4433311..afcd716c3 100644 --- a/conjure-java-core/src/test/resources/test/api/TestServiceRetrofit.java.retrofit_listenable_future +++ b/conjure-java-core/src/test/resources/test/api/TestServiceRetrofit.java.retrofit_listenable_future @@ -66,8 +66,9 @@ public interface TestServiceRetrofit { @GET("./catalog/datasets/{datasetRid}/raw-aliased") @Headers({ "hr-path-template: /catalog/datasets/{datasetRid}/raw-aliased", - "Accept: application/json" + "Accept: application/octet-stream" }) + @Streaming ListenableFuture getAliasedRawData( @Header("Authorization") AuthHeader authHeader, @Path("datasetRid") ResourceIdentifier datasetRid); @@ -95,6 +96,11 @@ public interface TestServiceRetrofit { ListenableFuture uploadRawData( @Header("Authorization") AuthHeader authHeader, @Body RequestBody input); + @POST("./catalog/datasets/upload-raw-aliased") + @Headers({"hr-path-template: /catalog/datasets/upload-raw-aliased", "Accept: application/json"}) + ListenableFuture uploadAliasedRawData( + @Header("Authorization") AuthHeader authHeader, @Body RequestBody input); + @GET("./catalog/datasets/{datasetRid}/branches") @Headers({ "hr-path-template: /catalog/datasets/{datasetRid}/branches",