diff --git a/conjure-undertow-processor/src/main/java/com/palantir/conjure/java/undertow/processor/data/ParamTypesResolver.java b/conjure-undertow-processor/src/main/java/com/palantir/conjure/java/undertow/processor/data/ParamTypesResolver.java index e6c333fac..8b7a0bee3 100644 --- a/conjure-undertow-processor/src/main/java/com/palantir/conjure/java/undertow/processor/data/ParamTypesResolver.java +++ b/conjure-undertow-processor/src/main/java/com/palantir/conjure/java/undertow/processor/data/ParamTypesResolver.java @@ -167,15 +167,15 @@ public Optional getParameterType(VariableElement variableElement, if (annotationReflector.isAnnotation(Handle.Body.class)) { return Optional.of(bodyParameter(variableElement, annotationReflector, safeLoggable)); } else if (annotationReflector.isAnnotation(Handle.Header.class)) { - return Optional.of(headerParameter(variableElement, annotationReflector, safeLoggable)); + return Optional.of(headerParameter(variableElement, parameterType, annotationReflector, safeLoggable)); } else if (annotationReflector.isAnnotation(Handle.PathParam.class)) { - return Optional.of(pathParameter(variableElement, annotationReflector, safeLoggable)); + return Optional.of(pathParameter(variableElement, annotationReflector, safeLoggable, parameterType)); } else if (annotationReflector.isAnnotation(Handle.PathMultiParam.class)) { - return Optional.of(pathMultiParameter(variableElement, annotationReflector, safeLoggable)); + return Optional.of(pathMultiParameter(variableElement, parameterType, annotationReflector, safeLoggable)); } else if (annotationReflector.isAnnotation(Handle.QueryParam.class)) { - return Optional.of(queryParameter(variableElement, annotationReflector, safeLoggable)); + return Optional.of(queryParameter(variableElement, parameterType, annotationReflector, safeLoggable)); } else if (annotationReflector.isAnnotation(Handle.Cookie.class)) { - return cookieParameter(variableElement, annotationReflector, safeLoggable); + return cookieParameter(variableElement, parameterType, annotationReflector, safeLoggable); } throw new SafeIllegalStateException("Not possible"); @@ -194,6 +194,7 @@ private ParameterType bodyParameter( private ParameterType headerParameter( VariableElement variableElement, + TypeMirror parameterType, AnnotationReflector annotationReflector, SafeLoggingAnnotation safeLoggable) { String javaParameterName = variableElement.getSimpleName().toString(); @@ -202,25 +203,27 @@ private ParameterType headerParameter( javaParameterName, annotationReflector.getAnnotationValue(String.class), deserializerName, - getCollectionParamDecoder(variableElement, annotationReflector), + getCollectionParamDecoder(variableElement, parameterType, annotationReflector), safeLoggable); } private ParameterType pathParameter( VariableElement variableElement, AnnotationReflector annotationReflector, - SafeLoggingAnnotation safeLoggable) { + SafeLoggingAnnotation safeLoggable, + TypeMirror parameterType) { String javaParameterName = variableElement.getSimpleName().toString(); String deserializerName = InstanceVariables.joinCamelCase(javaParameterName, "Deserializer"); return ParameterTypes.path( javaParameterName, deserializerName, - getParamDecoder(variableElement, annotationReflector), + getParamDecoder(variableElement, parameterType, annotationReflector), safeLoggable); } private ParameterType pathMultiParameter( VariableElement variableElement, + TypeMirror parameterType, AnnotationReflector annotationReflector, SafeLoggingAnnotation safeLoggable) { String javaParameterName = variableElement.getSimpleName().toString(); @@ -228,12 +231,13 @@ private ParameterType pathMultiParameter( return ParameterTypes.pathMulti( javaParameterName, deserializerName, - getCollectionParamDecoder(variableElement, annotationReflector), + getCollectionParamDecoder(variableElement, parameterType, annotationReflector), safeLoggable); } private ParameterType queryParameter( VariableElement variableElement, + TypeMirror parameterType, AnnotationReflector annotationReflector, SafeLoggingAnnotation safeLoggable) { String javaParameterName = variableElement.getSimpleName().toString(); @@ -243,12 +247,13 @@ private ParameterType queryParameter( javaParameterName, annotationReflector.getAnnotationValue(String.class), deserializerName, - getCollectionParamDecoder(variableElement, annotationReflector), + getCollectionParamDecoder(variableElement, parameterType, annotationReflector), safeLoggable); } private Optional cookieParameter( VariableElement variableElement, + TypeMirror parameterType, AnnotationReflector annotationReflector, SafeLoggingAnnotation safeLoggable) { String javaParameterName = variableElement.getSimpleName().toString(); @@ -270,21 +275,21 @@ private Optional cookieParameter( javaParameterName, annotationReflector.getAnnotationValue(String.class), deserializerName, - getParamDecoder(variableElement, annotationReflector), + getParamDecoder(variableElement, parameterType, annotationReflector), safeLoggable)); } - private CodeBlock getParamDecoder(VariableElement variableElement, AnnotationReflector annotationReflector) { + private CodeBlock getParamDecoder( + VariableElement variableElement, TypeMirror parameterType, AnnotationReflector annotationReflector) { // If the default marker interface is not used (overwritten by user), we want to use the user-provided decoder. TypeMirror typeMirror = annotationReflector.getAnnotationValue("decoder", TypeMirror.class); if (!context.isSameTypes(typeMirror, DefaultParamDecoder.class)) { return Instantiables.instantiate(typeMirror); } - TypeMirror variableType = variableElement.asType(); // For param decoders, we don't support list and set container types. - Optional innerOptionalType = context.getGenericInnerType(Optional.class, variableType); - TypeMirror decoderType = innerOptionalType.orElse(variableType); + Optional innerOptionalType = context.getGenericInnerType(Optional.class, parameterType); + TypeMirror decoderType = innerOptionalType.orElse(parameterType); ContainerType decoderOutputType = getOutputType(Optional.empty(), Optional.empty(), innerOptionalType); return getDefaultDecoderFactory(decoderType, ContainerType.NONE, decoderOutputType) @@ -293,27 +298,26 @@ private CodeBlock getParamDecoder(VariableElement variableElement, AnnotationRef "No default decoder exists for parameter. " + "Types with a valueOf(String) method are supported, as are conjure types", variableElement, - SafeArg.of("variableType", variableType), + SafeArg.of("variableType", parameterType), SafeArg.of("supportedTypes", SUPPORTED_CLASSES)); return CodeBlock.of("// error"); }); } private CodeBlock getCollectionParamDecoder( - VariableElement variableElement, AnnotationReflector annotationReflector) { + VariableElement variableElement, TypeMirror parameterType, AnnotationReflector annotationReflector) { // If the default marker interface is not used (overwritten by user), we want to use the user-provided decoder. TypeMirror typeMirror = annotationReflector.getAnnotationValue("decoder", TypeMirror.class); if (!context.isSameTypes(typeMirror, DefaultParamDecoder.class)) { return Instantiables.instantiate(typeMirror); } - TypeMirror variableType = variableElement.asType(); - Optional innerListType = context.getGenericInnerType(List.class, variableType); - Optional innerSetType = context.getGenericInnerType(Set.class, variableType); - Optional innerOptionalType = context.getGenericInnerType(Optional.class, variableType); + Optional innerListType = context.getGenericInnerType(List.class, parameterType); + Optional innerSetType = context.getGenericInnerType(Set.class, parameterType); + Optional innerOptionalType = context.getGenericInnerType(Optional.class, parameterType); TypeMirror decoderType = - innerListType.or(() -> innerSetType).or(() -> innerOptionalType).orElse(variableType); + innerListType.or(() -> innerSetType).or(() -> innerOptionalType).orElse(parameterType); ContainerType decoderOutputType = getOutputType(innerListType, innerSetType, innerOptionalType); return getDefaultDecoderFactory(decoderType, ContainerType.LIST, decoderOutputType) @@ -322,7 +326,7 @@ private CodeBlock getCollectionParamDecoder( "No default decoder exists for parameter. " + "Types with a valueOf(String) method are supported, as are conjure types", variableElement, - SafeArg.of("variableType", variableType), + SafeArg.of("variableType", parameterType), SafeArg.of("supportedTypes", SUPPORTED_CLASSES)); return CodeBlock.of("// error"); }); diff --git a/conjure-undertow-processor/src/test/java/com/palantir/conjure/java/undertow/processor/sample/GenericIface.java b/conjure-undertow-processor/src/test/java/com/palantir/conjure/java/undertow/processor/sample/GenericIface.java index c3c5c3ad1..236d05919 100644 --- a/conjure-undertow-processor/src/test/java/com/palantir/conjure/java/undertow/processor/sample/GenericIface.java +++ b/conjure-undertow-processor/src/test/java/com/palantir/conjure/java/undertow/processor/sample/GenericIface.java @@ -18,11 +18,16 @@ import com.palantir.conjure.java.undertow.annotations.Handle; import com.palantir.conjure.java.undertow.annotations.Handle.Body; +import com.palantir.conjure.java.undertow.annotations.Handle.PathParam; +import com.palantir.conjure.java.undertow.annotations.Handle.QueryParam; import com.palantir.conjure.java.undertow.annotations.HttpMethod; @Handle.Generate(false) -public interface GenericIface { +public interface GenericIface { @Handle(method = HttpMethod.POST, path = "/generic") - O generic(@Body I input); + TReturn generic( + @Body TBody input, + @PathParam TPathParam pathParam, + @QueryParam(value = "queryParam") TQueryParam queryParam); } diff --git a/conjure-undertow-processor/src/test/java/com/palantir/conjure/java/undertow/processor/sample/GenericImpl.java b/conjure-undertow-processor/src/test/java/com/palantir/conjure/java/undertow/processor/sample/GenericImpl.java index 9b6fe5ffd..98a0ab524 100644 --- a/conjure-undertow-processor/src/test/java/com/palantir/conjure/java/undertow/processor/sample/GenericImpl.java +++ b/conjure-undertow-processor/src/test/java/com/palantir/conjure/java/undertow/processor/sample/GenericImpl.java @@ -17,6 +17,19 @@ package com.palantir.conjure.java.undertow.processor.sample; import com.palantir.conjure.java.undertow.annotations.Handle; +import com.palantir.conjure.java.undertow.processor.sample.GenericImpl.ParamExample; @Handle.Generate -public interface GenericImpl extends GenericIface {} +public interface GenericImpl extends GenericIface { + class ParamExample { + String value; + + public ParamExample(String value) { + this.value = value; + } + + public static ParamExample valueOf(String value) { + return new ParamExample(value); + } + } +} diff --git a/conjure-undertow-processor/src/test/resources/com/palantir/conjure/java/undertow/processor/sample/GenericImplEndpoints.java.generated b/conjure-undertow-processor/src/test/resources/com/palantir/conjure/java/undertow/processor/sample/GenericImplEndpoints.java.generated index a35bde2bf..1d54ad15b 100644 --- a/conjure-undertow-processor/src/test/resources/com/palantir/conjure/java/undertow/processor/sample/GenericImplEndpoints.java.generated +++ b/conjure-undertow-processor/src/test/resources/com/palantir/conjure/java/undertow/processor/sample/GenericImplEndpoints.java.generated @@ -2,6 +2,9 @@ package com.palantir.conjure.java.undertow.processor.sample; import com.google.common.collect.ImmutableList; import com.palantir.conjure.java.undertow.annotations.DefaultSerDe; +import com.palantir.conjure.java.undertow.annotations.ParamDecoders; +import com.palantir.conjure.java.undertow.annotations.PathParamDeserializer; +import com.palantir.conjure.java.undertow.annotations.QueryParamDeserializer; import com.palantir.conjure.java.undertow.lib.Deserializer; import com.palantir.conjure.java.undertow.lib.Endpoint; import com.palantir.conjure.java.undertow.lib.ReturnValueWriter; @@ -14,6 +17,7 @@ import io.undertow.server.HttpServerExchange; import io.undertow.util.HttpString; import io.undertow.util.Methods; import java.io.IOException; +import java.lang.Boolean; import java.lang.Exception; import java.lang.Integer; import java.lang.Override; @@ -38,30 +42,41 @@ public final class GenericImplEndpoints implements UndertowService { return ImmutableList.of(new GenericEndpoint(runtime, delegate)); } - private static final class GenericEndpoint implements HttpHandler, Endpoint, ReturnValueWriter { + private static final class GenericEndpoint implements HttpHandler, Endpoint, ReturnValueWriter { private final UndertowRuntime runtime; private final GenericImpl delegate; private final Deserializer inputDeserializer; - private final Serializer genericSerializer; + private final Deserializer pathParamDeserializer; + + private final Deserializer queryParamDeserializer; + + private final Serializer genericSerializer; GenericEndpoint(UndertowRuntime runtime, GenericImpl delegate) { this.runtime = runtime; this.delegate = delegate; this.inputDeserializer = DefaultSerDe.INSTANCE.deserializer(new TypeMarker() {}, runtime, this); - this.genericSerializer = DefaultSerDe.INSTANCE.serializer(new TypeMarker() {}, runtime, this); + this.pathParamDeserializer = new PathParamDeserializer<>( + "pathParam", + ParamDecoders.complexParamDecoder(runtime.plainSerDe(), GenericImpl.ParamExample::valueOf)); + this.queryParamDeserializer = new QueryParamDeserializer<>( + "queryParam", ParamDecoders.integerCollectionParamDecoder(runtime.plainSerDe())); + this.genericSerializer = DefaultSerDe.INSTANCE.serializer(new TypeMarker() {}, runtime, this); } @Override public void handleRequest(HttpServerExchange exchange) throws Exception { String input = inputDeserializer.deserialize(exchange); - write(this.delegate.generic(input), exchange); + GenericImpl.ParamExample pathParam = this.pathParamDeserializer.deserialize(exchange); + Integer queryParam = this.queryParamDeserializer.deserialize(exchange); + write(this.delegate.generic(input, pathParam, queryParam), exchange); } @Override - public void write(Integer returnValue, HttpServerExchange exchange) throws IOException { + public void write(Boolean returnValue, HttpServerExchange exchange) throws IOException { this.genericSerializer.serialize(returnValue, exchange); }