Skip to content

Commit 01fd7b2

Browse files
committed
#55 - Support client generation with @client and @Client.Import
Support for headers, formParams and body
1 parent 16ebf29 commit 01fd7b2

File tree

11 files changed

+192
-93
lines changed

11 files changed

+192
-93
lines changed

http-generator-client/src/main/java/io/avaje/http/generator/client/ClientMethodWriter.java

Lines changed: 67 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import io.avaje.http.generator.core.*;
44

5+
import javax.lang.model.element.TypeElement;
56
import java.util.List;
67
import java.util.Set;
78

@@ -11,6 +12,7 @@
1112
class ClientMethodWriter {
1213

1314
private static final KnownResponse KNOWN_RESPONSE = new KnownResponse();
15+
1416
private final MethodReader method;
1517
private final Append writer;
1618
private final WebMethod webMethod;
@@ -33,7 +35,7 @@ void addImportTypes(ControllerReader reader) {
3335
}
3436

3537
private void methodStart(Append writer) {
36-
writer.append(" // %s %s", method.getWebMethod(), method.getWebMethodPath()).eol();
38+
writer.append(" // %s %s", webMethod, method.getWebMethodPath()).eol();
3739
writer.append(" @Override").eol();
3840
writer.append(" public %s %s(", returnType.shortType(), method.simpleName());
3941
int count = 0;
@@ -57,21 +59,31 @@ void write() {
5759

5860
PathSegments pathSegments = method.getPathSegments();
5961
Set<PathSegments.Segment> segments = pathSegments.getSegments();
60-
if (!segments.isEmpty()) {
61-
writer.append(" ");
62-
}
63-
for (PathSegments.Segment segment : segments) {
64-
if (segment.isLiteral()) {
65-
writer.append(".path(\"").append(segment.literalSection()).append("\")");
62+
63+
writePaths(segments);
64+
writeQueryParams(pathSegments);
65+
writeFormParams();
66+
writeHeaders();
67+
writeBody();
68+
69+
WebMethod webMethod = method.getWebMethod();
70+
writer.append(" .%s()", webMethod.name().toLowerCase()).eol();
71+
if (returnType == UType.VOID) {
72+
writer.append(" .asDiscarding();").eol();
73+
} else {
74+
String known = KNOWN_RESPONSE.get(returnType.full());
75+
if (known != null) {
76+
writer.append(" %s", known).eol();
77+
} else if (isReturnList()) {
78+
writer.append(" .list(%s.class);", Util.shortName(returnType.param0())).eol();
6679
} else {
67-
writer.append(".path(").append(segment.name()).append(")");
68-
//TODO: matrix params
80+
writer.append(" .bean(%s.class);", Util.shortName(returnType.full())).eol();
6981
}
7082
}
71-
if (!segments.isEmpty()) {
72-
writer.eol();
73-
}
83+
writer.append(" }").eol().eol();
84+
}
7485

86+
private void writeQueryParams(PathSegments pathSegments) {
7587
List<MethodParam> params = method.getParams();
7688
for (MethodParam param : params) {
7789
ParamType paramType = param.getParamType();
@@ -82,24 +94,54 @@ void write() {
8294
}
8395
}
8496
}
97+
}
8598

86-
// TODO: headers, formParams, body
99+
private void writeHeaders() {
100+
for (MethodParam param : method.getParams()) {
101+
ParamType paramType = param.getParamType();
102+
if (paramType == ParamType.HEADER) {
103+
writer.append(" .header(\"%s\", %s)", param.getParamName(), param.getName()).eol();
104+
}
105+
}
106+
}
87107

88-
WebMethod webMethod = method.getWebMethod();
89-
writer.append(" .%s()", webMethod.name().toLowerCase()).eol();
90-
if (returnType == UType.VOID) {
91-
writer.append(" .asDiscarding();").eol();
92-
} else {
93-
String known = KNOWN_RESPONSE.get(returnType.full());
94-
if (known != null) {
95-
writer.append(" %s", known).eol();
96-
} else if (isReturnList()) {
97-
writer.append(" .list(%s.class);", Util.shortName(returnType.param0())).eol();
108+
private void writeFormParams() {
109+
for (MethodParam param : method.getParams()) {
110+
ParamType paramType = param.getParamType();
111+
if (paramType == ParamType.FORMPARAM) {
112+
writer.append(" .formParam(\"%s\", %s)", param.getParamName(), param.getName()).eol();
113+
} else if (paramType == ParamType.FORM) {
114+
TypeElement formBeanType = ctx.getTypeElement(param.getRawType());
115+
BeanParamReader form = new BeanParamReader(ctx, formBeanType, param.getName(), param.getShortType(), ParamType.FORMPARAM);
116+
form.writeFormParams(writer);
117+
}
118+
}
119+
}
120+
121+
private void writeBody() {
122+
for (MethodParam param : method.getParams()) {
123+
ParamType paramType = param.getParamType();
124+
if (paramType == ParamType.BODY) {
125+
writer.append(" .body(%s)", param.getName()).eol();
126+
}
127+
}
128+
}
129+
130+
private void writePaths(Set<PathSegments.Segment> segments) {
131+
if (!segments.isEmpty()) {
132+
writer.append(" ");
133+
}
134+
for (PathSegments.Segment segment : segments) {
135+
if (segment.isLiteral()) {
136+
writer.append(".path(\"").append(segment.literalSection()).append("\")");
98137
} else {
99-
writer.append(" .bean(%s.class);", Util.shortName(returnType.full())).eol();
138+
writer.append(".path(").append(segment.name()).append(")");
139+
//TODO: matrix params
100140
}
101141
}
102-
writer.append(" }").eol().eol();
142+
if (!segments.isEmpty()) {
143+
writer.eol();
144+
}
103145
}
104146

105147
private boolean isReturnList() {

http-generator-core/src/main/java/io/avaje/http/generator/core/BeanParamReader.java

Lines changed: 39 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,37 @@
11
package io.avaje.http.generator.core;
22

3-
import javax.lang.model.element.Element;
4-
import javax.lang.model.element.ExecutableElement;
5-
import javax.lang.model.element.TypeElement;
6-
import javax.lang.model.element.VariableElement;
7-
import java.util.ArrayList;
8-
import java.util.HashSet;
9-
import java.util.LinkedHashMap;
10-
import java.util.List;
11-
import java.util.Map;
12-
import java.util.Set;
13-
14-
class BeanParamReader {
3+
import javax.lang.model.element.*;
4+
import java.util.*;
5+
6+
public class BeanParamReader {
157

168
private final ProcessingContext ctx;
179
private final String beanVarName;
1810
private final String beanShortType;
1911
private final TypeElement beanType;
20-
2112
private final ParamType defaultParamType;
22-
2313
private final Set<String> setterMethods = new HashSet<>();
24-
2514
private final Map<String, FieldReader> fieldMap = new LinkedHashMap<>();
26-
2715
private final List<ExecutableElement> constructors = new ArrayList<>();
16+
private final Map<String, ExecutableElement> methodMap = new LinkedHashMap<>();
2817

29-
BeanParamReader(ProcessingContext ctx, TypeElement beanType, String beanVarName, String beanShortType, ParamType defaultParamType) {
18+
public BeanParamReader(ProcessingContext ctx, TypeElement beanType, String beanVarName, String beanShortType, ParamType defaultParamType) {
3019
this.ctx = ctx;
3120
this.beanType = beanType;
3221
this.beanVarName = beanVarName;
3322
this.beanShortType = beanShortType;
3423
this.defaultParamType = defaultParamType;
35-
3624
read();
3725
}
3826

3927
private void read() {
40-
4128
for (Element enclosedElement : beanType.getEnclosedElements()) {
4229
switch (enclosedElement.getKind()) {
4330
case CONSTRUCTOR:
4431
constructors.add((ExecutableElement) enclosedElement);
4532
break;
4633
case METHOD:
47-
readMethod(enclosedElement);
34+
readMethod((ExecutableElement) enclosedElement);
4835
break;
4936
case FIELD:
5037
readField(enclosedElement);
@@ -54,20 +41,22 @@ private void read() {
5441
}
5542

5643
private void readField(Element enclosedElement) {
57-
5844
FieldReader field = new FieldReader(ctx, enclosedElement, defaultParamType);
5945
fieldMap.put(field.getVarName(), field);
6046
}
6147

62-
private void readMethod(Element enclosedElement) {
48+
private void readMethod(ExecutableElement enclosedElement) {
6349
String simpleName = enclosedElement.getSimpleName().toString();
50+
if (enclosedElement.getParameters().isEmpty()) {
51+
// getter methods
52+
methodMap.put(simpleName, enclosedElement);
53+
}
6454
if (simpleName.startsWith("set")) {
6555
setterMethods.add(simpleName);
6656
}
6757
}
6858

6959
void write(Append writer) {
70-
7160
writer.append(" new %s(", beanShortType);
7261
final Set<String> constructorParams = writeConstructorParams(writer);
7362
writer.append(");").eol();
@@ -113,20 +102,45 @@ private Set<String> writeConstructorParams(Append writer) {
113102
return paramsUsed;
114103
}
115104

105+
public void writeFormParams(Append writer) {
106+
for (FieldReader field : fieldMap.values()) {
107+
ExecutableElement getter = findGetter(field.getVarName());
108+
if (getter != null) {
109+
writer.append(" .formParam(\"%s\", %s.%s)", field.getVarName(), beanVarName, getter.toString()).eol();
110+
} else if (field.isPublic()) {
111+
writer.append(" .formParam(\"%s\", %s.%s)", field.getVarName(), beanVarName, field.getVarName()).eol();
112+
}
113+
}
114+
}
115+
116+
private ExecutableElement findGetter(String varName) {
117+
ExecutableElement getter = methodMap.get(varName);
118+
if (getter == null) {
119+
String initCap = Util.initcapSnake(varName);
120+
getter = methodMap.get("get" + initCap);
121+
if (getter == null) {
122+
getter = methodMap.get("is" + initCap);
123+
}
124+
}
125+
return getter;
126+
}
127+
116128
static class FieldReader {
117129

118130
private final ProcessingContext ctx;
119131
private final ElementReader element;
120-
121132
private String setterMethod;
122-
123133
private boolean constructorParam;
124134

125135
FieldReader(ProcessingContext ctx, Element enclosedElement, ParamType defaultParamType) {
126136
this.ctx = ctx;
127137
this.element = new ElementReader(enclosedElement, ctx, defaultParamType, false);
128138
}
129139

140+
boolean isPublic() {
141+
return element.getElement().getModifiers().contains(Modifier.PUBLIC);
142+
}
143+
130144
String getVarName() {
131145
return element.getVarName();
132146
}

http-generator-core/src/main/java/io/avaje/http/generator/core/MethodParam.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@ public String getShortType() {
4444
return elementParam.getShortType();
4545
}
4646

47+
public String getRawType() {
48+
return elementParam.getRawType();
49+
}
50+
4751
public String getName() {
4852
return elementParam.getVarName();
4953
}

http-generator-core/src/main/java/io/avaje/http/generator/core/Util.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,14 @@ public static String snakeCase(String name) {
100100
return sb.toString();
101101
}
102102

103+
public static String initcap(String input) {
104+
if (input.length() < 2) {
105+
return input.toUpperCase();
106+
} else {
107+
return Character.toUpperCase(input.charAt(0)) + input.substring(1);
108+
}
109+
}
110+
103111
public static String initcapSnake(String input) {
104112
StringBuilder sb = new StringBuilder(input.length());
105113
int len = input.length();

http-generator-core/src/test/java/io/avaje/http/generator/core/UtilTest.java

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,10 @@
55
import static org.assertj.core.api.Assertions.assertThat;
66
import static org.junit.jupiter.api.Assertions.assertEquals;
77

8-
98
public class UtilTest {
109

1110
@Test
12-
public void combinePath() {
11+
void combinePath() {
1312

1413
assertEquals(Util.combinePath("/hello", null), "/hello");
1514

@@ -29,20 +28,20 @@ public void combinePath() {
2928
}
3029

3130
@Test
32-
public void combinePath_forRoot() {
31+
void combinePath_forRoot() {
3332
assertEquals(Util.combinePath("/", ""), "/");
3433
assertEquals(Util.combinePath("", "/"), "");
3534
}
3635

3736
@Test
38-
public void trimPath() {
37+
void trimPath() {
3938
assertEquals(Util.trimPath("/"), "/");
4039
assertEquals(Util.trimPath("/foo"), "/foo");
4140
assertEquals(Util.trimPath("/foo/"), "/foo");
4241
}
4342

4443
@Test
45-
public void snakeCase() {
44+
void snakeCase() {
4645

4746
assertThat(Util.snakeCase("lower")).isEqualTo("lower");
4847
assertThat(Util.snakeCase("fooId")).isEqualTo("foo-id");
@@ -54,8 +53,14 @@ public void snakeCase() {
5453
}
5554

5655
@Test
57-
public void initcapSnake() {
56+
void initcap() {
57+
assertThat(Util.initcapSnake("lower")).isEqualTo("Lower");
58+
assertThat(Util.initcapSnake("a")).isEqualTo("A");
59+
assertThat(Util.initcapSnake("myFoo")).isEqualTo("MyFoo");
60+
}
5861

62+
@Test
63+
void initcapSnake() {
5964
assertThat(Util.initcapSnake("lower")).isEqualTo("Lower");
6065
assertThat(Util.initcapSnake("foo-id")).isEqualTo("Foo-Id");
6166
assertThat(Util.initcapSnake("foo-bar-baz-uuid")).isEqualTo("Foo-Bar-Baz-Uuid");
@@ -64,29 +69,29 @@ public void initcapSnake() {
6469
}
6570

6671
@Test
67-
public void propertyName() {
72+
void propertyName() {
6873
assertThat(Util.propertyName("setLower")).isEqualTo("lower");
6974
assertThat(Util.propertyName("setFooBar")).isEqualTo("fooBar");
7075
}
7176

7277
@Test
73-
public void parse_basic() {
78+
void parse_basic() {
7479
UType type = Util.parse("org.example.Repo");
7580

7681
assertThat(type.importTypes()).containsExactly("org.example.Repo");
7782
assertThat(type.shortType()).isEqualTo("Repo");
7883
}
7984

8085
@Test
81-
public void parse_generic() {
86+
void parse_generic() {
8287
UType type = Util.parse("java.util.List<org.example.Repo>");
8388

8489
assertThat(type.importTypes()).containsExactly("java.util.List", "org.example.Repo");
8590
assertThat(type.shortType()).isEqualTo("List<Repo>");
8691
}
8792

8893
@Test
89-
public void parse_generic_twoParams() {
94+
void parse_generic_twoParams() {
9095
UType type = Util.parse("java.util.List<org.example.Repo, foo.Other>");
9196

9297
assertThat(type.importTypes()).containsExactly("java.util.List", "org.example.Repo", "foo.Other");

tests/test-client/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
<dependency>
3333
<groupId>io.avaje</groupId>
3434
<artifactId>avaje-http-client</artifactId>
35-
<version>1.3</version>
35+
<version>1.4-SNAPSHOT</version>
3636
</dependency>
3737

3838
<dependency>

tests/test-client/src/main/java/org/example/GitHubService.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66

77
import java.util.List;
88

9+
/**
10+
* Example Retrofit API.
11+
*/
912
public interface GitHubService {
1013

1114
@GET("users/{user}/repos")

0 commit comments

Comments
 (0)