From 5f6a3a107540e507dc15eb941626390c58bff813 Mon Sep 17 00:00:00 2001 From: Josiah Noel <32279667+SentryMan@users.noreply.github.com> Date: Wed, 22 Jan 2025 12:14:30 -0500 Subject: [PATCH 1/3] [client] support generics with jackson --- .../avaje/http/client/JacksonBodyAdapter.java | 47 +++++++++++++++---- .../generator/client/ClientMethodWriter.java | 39 ++++++++++----- .../generator/client/ClientProcessor.java | 5 +- .../http/generator/client/ClientWriter.java | 7 +-- 4 files changed, 70 insertions(+), 28 deletions(-) diff --git a/http-client/src/main/java/io/avaje/http/client/JacksonBodyAdapter.java b/http-client/src/main/java/io/avaje/http/client/JacksonBodyAdapter.java index 269eec9e..d773377f 100644 --- a/http-client/src/main/java/io/avaje/http/client/JacksonBodyAdapter.java +++ b/http-client/src/main/java/io/avaje/http/client/JacksonBodyAdapter.java @@ -1,15 +1,21 @@ package io.avaje.http.client; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.*; -import com.fasterxml.jackson.databind.type.CollectionType; - import java.io.IOException; import java.io.UncheckedIOException; +import java.lang.reflect.Type; import java.util.List; import java.util.concurrent.ConcurrentHashMap; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectReader; +import com.fasterxml.jackson.databind.ObjectWriter; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.databind.type.CollectionType; + /** * Jackson BodyAdapter to read and write beans as JSON. * @@ -26,9 +32,9 @@ public final class JacksonBodyAdapter implements BodyAdapter { private final ObjectMapper mapper; - private final ConcurrentHashMap, BodyWriter> beanWriterCache = new ConcurrentHashMap<>(); - private final ConcurrentHashMap, BodyReader> beanReaderCache = new ConcurrentHashMap<>(); - private final ConcurrentHashMap, BodyReader> listReaderCache = new ConcurrentHashMap<>(); + private final ConcurrentHashMap> beanWriterCache = new ConcurrentHashMap<>(); + private final ConcurrentHashMap> beanReaderCache = new ConcurrentHashMap<>(); + private final ConcurrentHashMap> listReaderCache = new ConcurrentHashMap<>(); /** * Create passing the ObjectMapper to use. @@ -72,6 +78,31 @@ public BodyReader beanReader(Class cls) { }); } + @SuppressWarnings("unchecked") + @Override + public BodyWriter beanWriter(Type cls) { + + return (BodyWriter) beanWriterCache.computeIfAbsent(cls, aClass -> { + try { + return new JWriter<>(mapper.writerFor(mapper.getTypeFactory().constructType(cls))); + } catch (Exception e) { + throw new RuntimeException(e); + } + }); + } + + @SuppressWarnings("unchecked") + @Override + public BodyReader beanReader(Type cls) { + return (BodyReader) beanReaderCache.computeIfAbsent(cls, aClass -> { + try { + return new JReader<>(mapper.readerFor(mapper.getTypeFactory().constructType(cls))); + } catch (Exception e) { + throw new RuntimeException(e); + } + }); + } + @SuppressWarnings("unchecked") @Override public BodyReader> listReader(Class cls) { diff --git a/http-generator-client/src/main/java/io/avaje/http/generator/client/ClientMethodWriter.java b/http-generator-client/src/main/java/io/avaje/http/generator/client/ClientMethodWriter.java index fd07d316..57fff3da 100644 --- a/http-generator-client/src/main/java/io/avaje/http/generator/client/ClientMethodWriter.java +++ b/http-generator-client/src/main/java/io/avaje/http/generator/client/ClientMethodWriter.java @@ -21,7 +21,6 @@ * Write code to register Web route for a given controller method. */ final class ClientMethodWriter { - private static final KnownResponse KNOWN_RESPONSE = new KnownResponse(); private static final String BODY_HANDLER = "java.net.http.HttpResponse.BodyHandler"; private static final String COMPLETABLE_FUTURE = "java.util.concurrent.CompletableFuture"; @@ -33,19 +32,23 @@ final class ClientMethodWriter { private final UType returnType; private MethodParam bodyHandlerParam; private String methodGenericParams = ""; - private final boolean useJsonb; + private static final boolean useJsonb = APContext.typeElement("io.avaje.jsonb.Types") != null; + private static final boolean useJackson = + APContext.typeElement("com.fasterxml.jackson.core.type.TypeReference") != null; + private static final boolean useInject = + APContext.typeElement("io.avaje.inject.spi.GenericType") != null; + private final Optional timeout; private final boolean useConfig; private final Map segmentPropertyMap; private final Set propertyConstants; private final List> presetHeaders; - ClientMethodWriter(MethodReader method, Append writer, boolean useJsonb, Set propertyConstants) { + ClientMethodWriter(MethodReader method, Append writer, Set propertyConstants) { this.method = method; this.writer = writer; this.webMethod = method.webMethod(); this.returnType = Util.parseType(method.returnType()); - this.useJsonb = useJsonb; this.timeout = method.timeout(); this.useConfig = ProcessingContext.typeElement("io.avaje.config.Config") != null; @@ -73,6 +76,13 @@ final class ClientMethodWriter { } void addImportTypes(ControllerReader reader) { + if (useJsonb) { + reader.addImportType("io.avaje.jsonb.Types"); + } else if (useJackson) { + reader.addImportType("com.fasterxml.jackson.core.type.TypeReference"); + } else if (useInject) { + reader.addImportType("io.avaje.inject.spi.GenericType"); + } reader.addImportTypes(returnType.importTypes()); method.throwsList().stream() .map(UType::parse) @@ -240,13 +250,20 @@ private void writeResponse(UType type) { } void writeGeneric(UType type) { - if (useJsonb && type.isGeneric()) { - final var params = type.importTypes().stream() - .skip(1) - .map(Util::shortName) - .collect(Collectors.joining(".class, ")); - - writer.append("Types.newParameterizedType(%s.class, %s.class)", Util.shortName(type.mainType()), params); + if (type.isGeneric() && useJsonb) { + final var params = + type.importTypes().stream() + .skip(1) + .map(Util::shortName) + .collect(Collectors.joining(".class, ")); + + writer.append( + "Types.newParameterizedType(%s.class, %s.class)", + Util.shortName(type.mainType()), params); + } else if (type.isGeneric() && useJackson) { + writer.append("new TypeReference<%s>() {}.getType()", type.shortType()); + } else if (type.isGeneric() && useInject) { + writer.append("new GenericType<%s>() {}.getType()", type.shortType()); } else { writer.append("%s.class", Util.shortName(type.mainType())); } diff --git a/http-generator-client/src/main/java/io/avaje/http/generator/client/ClientProcessor.java b/http-generator-client/src/main/java/io/avaje/http/generator/client/ClientProcessor.java index 2ba48724..cc7cd5f4 100644 --- a/http-generator-client/src/main/java/io/avaje/http/generator/client/ClientProcessor.java +++ b/http-generator-client/src/main/java/io/avaje/http/generator/client/ClientProcessor.java @@ -30,8 +30,6 @@ public class ClientProcessor extends AbstractProcessor { private final ComponentMetaData metaData = new ComponentMetaData(); - private boolean useJsonB; - private SimpleComponentWriter componentWriter; private boolean readModuleInfo; @@ -48,7 +46,6 @@ public synchronized void init(ProcessingEnvironment processingEnv) { APContext.init(processingEnv); ProcessingContext.init(processingEnv, new ClientPlatformAdapter(), false); this.componentWriter = new SimpleComponentWriter(metaData); - useJsonB = ProcessingContext.useJsonb(); } @Override @@ -103,7 +100,7 @@ private void writeClient(Element controller) { protected String writeClientAdapter(ControllerReader reader) throws IOException { var suffix = ClientSuffix.fromInterface(reader.beanType().getQualifiedName().toString()); - return new ClientWriter(reader, suffix, useJsonB).write(); + return new ClientWriter(reader, suffix).write(); } private void initialiseComponent() { diff --git a/http-generator-client/src/main/java/io/avaje/http/generator/client/ClientWriter.java b/http-generator-client/src/main/java/io/avaje/http/generator/client/ClientWriter.java index 64352513..115b6939 100644 --- a/http-generator-client/src/main/java/io/avaje/http/generator/client/ClientWriter.java +++ b/http-generator-client/src/main/java/io/avaje/http/generator/client/ClientWriter.java @@ -24,17 +24,14 @@ final class ClientWriter extends BaseControllerWriter { private static final String AT_GENERATED = "@Generated(\"avaje-http-client-generator\")"; private final List methodList = new ArrayList<>(); - private final boolean useJsonb; private final Set propertyConstants = new HashSet<>(); private final String suffix; - ClientWriter(ControllerReader reader, String suffix, boolean useJsonB) throws IOException { + ClientWriter(ControllerReader reader, String suffix) throws IOException { super(reader, suffix); this.suffix = suffix; reader.addImportType(HTTP_CLIENT); - this.useJsonb = useJsonB; readMethods(); - if (useJsonB) reader.addImportType("io.avaje.jsonb.Types"); } @Override @@ -50,7 +47,7 @@ protected String initPackageName(String originName) { private void readMethods() { for (final MethodReader method : reader.methods()) { if (method.isWebMethod()) { - final var methodWriter = new ClientMethodWriter(method, writer, useJsonb, propertyConstants); + final var methodWriter = new ClientMethodWriter(method, writer, propertyConstants); methodWriter.addImportTypes(reader); methodList.add(methodWriter); } From 4d78823e39d9ee874cb04abc700a354d6eb8d0f0 Mon Sep 17 00:00:00 2001 From: Josiah Noel <32279667+SentryMan@users.noreply.github.com> Date: Sat, 25 Jan 2025 23:44:35 -0500 Subject: [PATCH 2/3] test jackson adapter --- .../avaje/http/client/JacksonBodyAdapter.java | 15 ++++ tests/pom.xml | 2 +- tests/test-client/pom.xml | 14 +++ .../src/main/java/example/github/Generic.java | 18 ++++ .../main/java/example/github/GenericData.java | 13 +++ .../src/main/java/example/github/Repo.java | 22 ++++- .../src/main/java/example/github/Simple.java | 13 ++- .../httpclient/GeneratedHttpComponent.java | 15 ---- .../github/httpclient/Simple$HttpClient.java | 31 ------- .../src/main/java/module-info.java | 1 + .../io.avaje.http.client.HttpApiProvider | 1 - ....http.client.HttpClient$GeneratedComponent | 1 + .../test/java/example/github/GithubTest.java | 90 ++++++++++++++++--- 13 files changed, 166 insertions(+), 70 deletions(-) create mode 100644 tests/test-client/src/main/java/example/github/Generic.java create mode 100644 tests/test-client/src/main/java/example/github/GenericData.java delete mode 100644 tests/test-client/src/main/java/example/github/httpclient/GeneratedHttpComponent.java delete mode 100644 tests/test-client/src/main/java/example/github/httpclient/Simple$HttpClient.java delete mode 100644 tests/test-client/src/main/resources/META-INF/services/io.avaje.http.client.HttpApiProvider create mode 100644 tests/test-client/src/main/resources/META-INF/services/io.avaje.http.client.HttpClient$GeneratedComponent diff --git a/http-client/src/main/java/io/avaje/http/client/JacksonBodyAdapter.java b/http-client/src/main/java/io/avaje/http/client/JacksonBodyAdapter.java index d773377f..770bf13c 100644 --- a/http-client/src/main/java/io/avaje/http/client/JacksonBodyAdapter.java +++ b/http-client/src/main/java/io/avaje/http/client/JacksonBodyAdapter.java @@ -117,6 +117,21 @@ public BodyReader> listReader(Class cls) { }); } + @SuppressWarnings("unchecked") + @Override + public BodyReader> listReader(Type type) { + return (BodyReader>) listReaderCache.computeIfAbsent(type, aType -> { + try { + var javaType = mapper.getTypeFactory().constructType(aType); + final CollectionType collectionType = mapper.getTypeFactory().constructCollectionType(List.class, javaType); + final ObjectReader reader = mapper.readerFor(collectionType); + return new JReader<>(reader); + } catch (Exception e) { + throw new RuntimeException(e); + } + }); + } + private static class JReader implements BodyReader { private final ObjectReader reader; diff --git a/tests/pom.xml b/tests/pom.xml index 9dc38791..16ab60ba 100644 --- a/tests/pom.xml +++ b/tests/pom.xml @@ -24,7 +24,6 @@ test-javalin test-javalin-jsonb - test-client test-sigma @@ -35,6 +34,7 @@ [21,) + test-client test-nima test-jex test-nima-jsonb diff --git a/tests/test-client/pom.xml b/tests/test-client/pom.xml index 0c40e256..46855762 100644 --- a/tests/test-client/pom.xml +++ b/tests/test-client/pom.xml @@ -12,6 +12,7 @@ test-client + 21 UTF-8 @@ -54,6 +55,19 @@ 1.0 + + io.avaje + avaje-jex + 3.0-RC14 + test + + + + io.avaje + avaje-http-client-generator + ${project.version} + + diff --git a/tests/test-client/src/main/java/example/github/Generic.java b/tests/test-client/src/main/java/example/github/Generic.java new file mode 100644 index 00000000..4995d36d --- /dev/null +++ b/tests/test-client/src/main/java/example/github/Generic.java @@ -0,0 +1,18 @@ +package example.github; + +import java.util.List; + +import io.avaje.http.api.Client; +import io.avaje.http.api.Get; +import io.avaje.http.api.Post; +//import io.avaje.http.api.Get; +//import io.avaje.http.api.Path; +import io.avaje.http.client.HttpException; + +@Client +public interface Generic { + + @Post("/generic") + List> post(GenericData repo) throws HttpException; + +} diff --git a/tests/test-client/src/main/java/example/github/GenericData.java b/tests/test-client/src/main/java/example/github/GenericData.java new file mode 100644 index 00000000..065018a3 --- /dev/null +++ b/tests/test-client/src/main/java/example/github/GenericData.java @@ -0,0 +1,13 @@ +package example.github; + +public class GenericData { + private T data; + + public T getData() { + return data; + } + + public void setData(T data) { + this.data = data; + } +} diff --git a/tests/test-client/src/main/java/example/github/Repo.java b/tests/test-client/src/main/java/example/github/Repo.java index 73564f76..1ee81299 100644 --- a/tests/test-client/src/main/java/example/github/Repo.java +++ b/tests/test-client/src/main/java/example/github/Repo.java @@ -1,6 +1,24 @@ package example.github; +import com.fasterxml.jackson.annotation.JsonCreator; + public class Repo { - public long id; - public String name; + private long id; + private String name; + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } } diff --git a/tests/test-client/src/main/java/example/github/Simple.java b/tests/test-client/src/main/java/example/github/Simple.java index f39f9638..ad1a1667 100644 --- a/tests/test-client/src/main/java/example/github/Simple.java +++ b/tests/test-client/src/main/java/example/github/Simple.java @@ -1,15 +1,14 @@ package example.github; -//import io.avaje.http.api.Get; -//import io.avaje.http.api.Path; -import io.avaje.http.client.HttpException; - import java.util.List; -//@Path("/") +import io.avaje.http.api.Client; +import io.avaje.http.api.Get; +import io.avaje.http.client.HttpException; + +@Client public interface Simple { - //@Get("users/{user}/repos") + @Get("users/{user}/repos") List listRepos(String user, String other) throws HttpException; - } diff --git a/tests/test-client/src/main/java/example/github/httpclient/GeneratedHttpComponent.java b/tests/test-client/src/main/java/example/github/httpclient/GeneratedHttpComponent.java deleted file mode 100644 index b7517518..00000000 --- a/tests/test-client/src/main/java/example/github/httpclient/GeneratedHttpComponent.java +++ /dev/null @@ -1,15 +0,0 @@ -package example.github.httpclient; - -import java.util.Map; - -import example.github.Simple; -import io.avaje.http.client.HttpApiProvider; -import io.avaje.http.client.HttpClient; - -public class GeneratedHttpComponent implements HttpClient.GeneratedComponent { - - @Override - public void register(Map, HttpApiProvider> providerMap) { - providerMap.put(Simple.class, Simple$HttpClient::new); - } -} diff --git a/tests/test-client/src/main/java/example/github/httpclient/Simple$HttpClient.java b/tests/test-client/src/main/java/example/github/httpclient/Simple$HttpClient.java deleted file mode 100644 index 1a8a351a..00000000 --- a/tests/test-client/src/main/java/example/github/httpclient/Simple$HttpClient.java +++ /dev/null @@ -1,31 +0,0 @@ -package example.github.httpclient; - -import java.util.List; - -import example.github.Repo; -import example.github.Simple; -import io.avaje.http.client.HttpClient; -import io.avaje.http.client.HttpException; - -/** This code could be generated from the interface definition. */ -public class Simple$HttpClient implements Simple { - - private final HttpClient context; - - public Simple$HttpClient(HttpClient context) { - this.context = context; - } - - // @Get("users/{user}/repos") - @Override - public List listRepos(String user, String other) throws HttpException { - return context - .request() - .path("users") - .path(user) - .path("repos") - .queryParam("other", other) - .GET() - .list(Repo.class); - } -} diff --git a/tests/test-client/src/main/java/module-info.java b/tests/test-client/src/main/java/module-info.java index f8165faa..a925d770 100644 --- a/tests/test-client/src/main/java/module-info.java +++ b/tests/test-client/src/main/java/module-info.java @@ -1,6 +1,7 @@ open module test { requires io.avaje.http.client; + requires io.avaje.http.api; requires com.fasterxml.jackson.databind; requires com.google.gson; diff --git a/tests/test-client/src/main/resources/META-INF/services/io.avaje.http.client.HttpApiProvider b/tests/test-client/src/main/resources/META-INF/services/io.avaje.http.client.HttpApiProvider deleted file mode 100644 index e15a847c..00000000 --- a/tests/test-client/src/main/resources/META-INF/services/io.avaje.http.client.HttpApiProvider +++ /dev/null @@ -1 +0,0 @@ -example.github.SimpleHttpClient diff --git a/tests/test-client/src/main/resources/META-INF/services/io.avaje.http.client.HttpClient$GeneratedComponent b/tests/test-client/src/main/resources/META-INF/services/io.avaje.http.client.HttpClient$GeneratedComponent new file mode 100644 index 00000000..d419efe2 --- /dev/null +++ b/tests/test-client/src/main/resources/META-INF/services/io.avaje.http.client.HttpClient$GeneratedComponent @@ -0,0 +1 @@ +example.github.httpclient.GeneratedHttpComponent \ No newline at end of file diff --git a/tests/test-client/src/test/java/example/github/GithubTest.java b/tests/test-client/src/test/java/example/github/GithubTest.java index 36e501f1..9e9aa47b 100644 --- a/tests/test-client/src/test/java/example/github/GithubTest.java +++ b/tests/test-client/src/test/java/example/github/GithubTest.java @@ -1,18 +1,57 @@ package example.github; +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.List; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + import com.google.gson.Gson; + import io.avaje.http.client.BodyAdapter; import io.avaje.http.client.HttpClient; import io.avaje.http.client.JacksonBodyAdapter; import io.avaje.http.client.gson.GsonBodyAdapter; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; +import io.avaje.jex.Jex; -import java.util.List; +class GithubTest { -import static org.assertj.core.api.Assertions.assertThat; + static Jex.Server server = null; + String url = "http://localhost:" + server.port(); -class GithubTest { + @BeforeAll + static void startServer() { + server = + Jex.create() + .get( + "/users/{user}/repos", + ctx -> { + var repo = new Repo(); + repo.setId(1); + repo.setName("something"); + ctx.json(List.of(new Repo())); + }) + .post( + "/generic", + ctx -> { + var repo = new Repo(); + repo.setId(1); + repo.setName("something"); + var data = new GenericData(); + data.setData(repo); + + ctx.json(List.>of(data)); + }) + .port(0) + .start(); + } + + @AfterAll + static void stop() { + server.shutdown(); + } @Test void test_create() { @@ -22,25 +61,29 @@ void test_create() { assertThat(simple).isNotNull(); } - @Disabled @Test void test_with_jackson() { assertListRepos(jacksonBodyAdapter()); } - @Disabled + @Test + void testGeneric_with_jackson() { + assertGeneric(jacksonBodyAdapter()); + } + @Test void test_with_gson() { assertListRepos(gsonBodyAdapter()); } private void assertListRepos(BodyAdapter bodyAdapter) { - final HttpClient client = HttpClient.builder() - .baseUrl("https://api.github.com") - .bodyAdapter(bodyAdapter) -// .requestLogging(false) -// .requestListener(new RequestLogger()) - .build(); + final HttpClient client = + HttpClient.builder() + .baseUrl(url) + .bodyAdapter(bodyAdapter) + // .requestLogging(false) + // .requestListener(new RequestLogger()) + .build(); final Simple simple = client.create(Simple.class); @@ -48,6 +91,27 @@ private void assertListRepos(BodyAdapter bodyAdapter) { assertThat(repos).isNotEmpty(); } + private void assertGeneric(BodyAdapter bodyAdapter) { + final HttpClient client = + HttpClient.builder() + .baseUrl(url) + .bodyAdapter(bodyAdapter) + // .requestLogging(false) + // .requestListener(new RequestLogger()) + .build(); + + final var generic = client.create(Generic.class); + + var repo = new Repo(); + repo.setId(1); + repo.setName("something"); + var data = new GenericData(); + data.setData(repo); + + final var repos = generic.post(data); + assertThat(repos).isNotEmpty(); + } + private BodyAdapter jacksonBodyAdapter() { return new JacksonBodyAdapter(); } From 084005979f0437a38580e18e37f9e739c83b2a5b Mon Sep 17 00:00:00 2001 From: Rob Bygrave Date: Sun, 26 Jan 2025 18:38:05 +1300 Subject: [PATCH 3/3] Format only changes --- .../avaje/http/client/JacksonBodyAdapter.java | 6 +- .../generator/client/ClientMethodWriter.java | 20 ++- .../src/main/java/example/github/Generic.java | 3 - .../test/java/example/github/GithubTest.java | 4 - .../src/main/resources/public/openapi.json | 117 ++++++++++++++++++ 5 files changed, 127 insertions(+), 23 deletions(-) diff --git a/http-client/src/main/java/io/avaje/http/client/JacksonBodyAdapter.java b/http-client/src/main/java/io/avaje/http/client/JacksonBodyAdapter.java index 770bf13c..a28c97a3 100644 --- a/http-client/src/main/java/io/avaje/http/client/JacksonBodyAdapter.java +++ b/http-client/src/main/java/io/avaje/http/client/JacksonBodyAdapter.java @@ -8,7 +8,6 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectReader; @@ -81,7 +80,6 @@ public BodyReader beanReader(Class cls) { @SuppressWarnings("unchecked") @Override public BodyWriter beanWriter(Type cls) { - return (BodyWriter) beanWriterCache.computeIfAbsent(cls, aClass -> { try { return new JWriter<>(mapper.writerFor(mapper.getTypeFactory().constructType(cls))); @@ -132,7 +130,7 @@ public BodyReader> listReader(Type type) { }); } - private static class JReader implements BodyReader { + private static final class JReader implements BodyReader { private final ObjectReader reader; @@ -159,7 +157,7 @@ public T read(BodyContent bodyContent) { } } - private static class JWriter implements BodyWriter { + private static final class JWriter implements BodyWriter { private final ObjectWriter writer; diff --git a/http-generator-client/src/main/java/io/avaje/http/generator/client/ClientMethodWriter.java b/http-generator-client/src/main/java/io/avaje/http/generator/client/ClientMethodWriter.java index 57fff3da..6d193ded 100644 --- a/http-generator-client/src/main/java/io/avaje/http/generator/client/ClientMethodWriter.java +++ b/http-generator-client/src/main/java/io/avaje/http/generator/client/ClientMethodWriter.java @@ -33,10 +33,8 @@ final class ClientMethodWriter { private MethodParam bodyHandlerParam; private String methodGenericParams = ""; private static final boolean useJsonb = APContext.typeElement("io.avaje.jsonb.Types") != null; - private static final boolean useJackson = - APContext.typeElement("com.fasterxml.jackson.core.type.TypeReference") != null; - private static final boolean useInject = - APContext.typeElement("io.avaje.inject.spi.GenericType") != null; + private static final boolean useJackson = APContext.typeElement("com.fasterxml.jackson.core.type.TypeReference") != null; + private static final boolean useInject = APContext.typeElement("io.avaje.inject.spi.GenericType") != null; private final Optional timeout; private final boolean useConfig; @@ -252,14 +250,12 @@ private void writeResponse(UType type) { void writeGeneric(UType type) { if (type.isGeneric() && useJsonb) { final var params = - type.importTypes().stream() - .skip(1) - .map(Util::shortName) - .collect(Collectors.joining(".class, ")); - - writer.append( - "Types.newParameterizedType(%s.class, %s.class)", - Util.shortName(type.mainType()), params); + type.importTypes().stream() + .skip(1) + .map(Util::shortName) + .collect(Collectors.joining(".class, ")); + + writer.append("Types.newParameterizedType(%s.class, %s.class)", Util.shortName(type.mainType()), params); } else if (type.isGeneric() && useJackson) { writer.append("new TypeReference<%s>() {}.getType()", type.shortType()); } else if (type.isGeneric() && useInject) { diff --git a/tests/test-client/src/main/java/example/github/Generic.java b/tests/test-client/src/main/java/example/github/Generic.java index 4995d36d..c3478759 100644 --- a/tests/test-client/src/main/java/example/github/Generic.java +++ b/tests/test-client/src/main/java/example/github/Generic.java @@ -3,10 +3,7 @@ import java.util.List; import io.avaje.http.api.Client; -import io.avaje.http.api.Get; import io.avaje.http.api.Post; -//import io.avaje.http.api.Get; -//import io.avaje.http.api.Path; import io.avaje.http.client.HttpException; @Client diff --git a/tests/test-client/src/test/java/example/github/GithubTest.java b/tests/test-client/src/test/java/example/github/GithubTest.java index 9e9aa47b..2099c849 100644 --- a/tests/test-client/src/test/java/example/github/GithubTest.java +++ b/tests/test-client/src/test/java/example/github/GithubTest.java @@ -81,8 +81,6 @@ private void assertListRepos(BodyAdapter bodyAdapter) { HttpClient.builder() .baseUrl(url) .bodyAdapter(bodyAdapter) - // .requestLogging(false) - // .requestListener(new RequestLogger()) .build(); final Simple simple = client.create(Simple.class); @@ -96,8 +94,6 @@ private void assertGeneric(BodyAdapter bodyAdapter) { HttpClient.builder() .baseUrl(url) .bodyAdapter(bodyAdapter) - // .requestLogging(false) - // .requestListener(new RequestLogger()) .build(); final var generic = client.create(Generic.class); diff --git a/tests/test-jex/src/main/resources/public/openapi.json b/tests/test-jex/src/main/resources/public/openapi.json index b747803d..e4b99b6d 100644 --- a/tests/test-jex/src/main/resources/public/openapi.json +++ b/tests/test-jex/src/main/resources/public/openapi.json @@ -720,6 +720,96 @@ } } }, + "/hello/takesOptional" : { + "get" : { + "tags" : [ + + ], + "summary" : "", + "description" : "", + "parameters" : [ + { + "name" : "myOptional", + "in" : "query", + "schema" : { + "$ref" : "#/components/schemas/Long>" + } + } + ], + "responses" : { + "200" : { + "description" : "", + "content" : { + "text/plain" : { + "schema" : { + "type" : "string" + } + } + } + } + } + } + }, + "/hello/takesOptionalEnum" : { + "get" : { + "tags" : [ + + ], + "summary" : "", + "description" : "", + "parameters" : [ + { + "name" : "myOptional", + "in" : "query", + "schema" : { + "$ref" : "#/components/schemas/NestedEnum>" + } + } + ], + "responses" : { + "200" : { + "description" : "", + "content" : { + "text/plain" : { + "schema" : { + "type" : "string" + } + } + } + } + } + } + }, + "/hello/takesOptionalString" : { + "get" : { + "tags" : [ + + ], + "summary" : "", + "description" : "", + "parameters" : [ + { + "name" : "myOptional", + "in" : "query", + "schema" : { + "$ref" : "#/components/schemas/String>" + } + } + ], + "responses" : { + "200" : { + "description" : "", + "content" : { + "text/plain" : { + "schema" : { + "type" : "string" + } + } + } + } + } + } + }, "/hello/withMatrix/{year_segment}/{other}" : { "get" : { "tags" : [ @@ -1426,6 +1516,33 @@ } } }, + "Long>" : { + "type" : "object", + "properties" : { + "value" : { + "$ref" : "#/components/schemas/T" + } + } + }, + "NestedEnum>" : { + "type" : "object", + "properties" : { + "value" : { + "$ref" : "#/components/schemas/T" + } + } + }, + "String>" : { + "type" : "object", + "properties" : { + "value" : { + "$ref" : "#/components/schemas/T" + } + } + }, + "T" : { + "type" : "object" + }, "ViewHome" : { "type" : "object", "properties" : {