Skip to content

Commit eccb601

Browse files
authored
Merge pull request #550 from LukasDetermann/feature/optional-request-parameters
added support for Optional Request Parameters
2 parents 25d13ba + 8e70331 commit eccb601

File tree

5 files changed

+98
-5
lines changed

5 files changed

+98
-5
lines changed

http-api/src/main/java/io/avaje/http/api/PathTypeConversion.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import java.time.*;
55
import java.math.BigInteger;
66
import java.util.List;
7+
import java.util.Optional;
78
import java.util.Set;
89
import java.util.UUID;
910
import java.util.function.Function;
@@ -60,6 +61,10 @@ public static <T> Set<T> set(Function<String, T> func, List<String> params) {
6061
return params.stream().map(func).collect(Collectors.toSet());
6162
}
6263

64+
public static <T> Optional<T> optional(Function<String, T> func, String value) {
65+
return Optional.ofNullable(func.apply(value));
66+
}
67+
6368
/** Convert to int. */
6469
public static int asInt(String value) {
6570
checkNull(value);

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

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -119,18 +119,23 @@ TypeHandler initTypeHandler() {
119119
final var isMap =
120120
!isCollection && typeOp.filter(t -> t.mainType().startsWith("java.util.Map")).isPresent();
121121

122+
final var isOptional = typeOp.filter(t -> t.mainType().startsWith("java.util.Optional")).isPresent();
123+
122124
if (mainTypeEnum) {
123125
return TypeMap.enumParamHandler(typeOp.orElseThrow());
124-
} else if (isCollection) {
125-
this.isParamCollection = true;
126-
final var isEnumCollection =
126+
} else if (isCollection || isOptional) {
127+
final var isEnumContainer =
127128
typeOp
128129
.flatMap(t -> Optional.ofNullable(typeElement(t.param0())))
129130
.map(TypeElement::getKind)
130131
.filter(ElementKind.ENUM::equals)
131132
.isPresent();
132133

133-
return TypeMap.collectionHandler(typeOp.orElseThrow(), isEnumCollection);
134+
if (isOptional) {//Needs to be checked first, as 'isCollection' is too broad
135+
return TypeMap.optionalHandler(typeOp.orElseThrow(), isEnumContainer);
136+
}
137+
this.isParamCollection = true;
138+
return TypeMap.collectionHandler(typeOp.orElseThrow(), isEnumContainer);
134139
} else if (isMap) {
135140
this.isParamMap = true;
136141
return new TypeMap.StringHandler();

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

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,21 @@ static TypeHandler collectionHandler(UType type, boolean isEnum) {
6262
isEnum));
6363
}
6464

65+
static TypeHandler optionalHandler(UType type, boolean isEnum) {
66+
final var handler = types.get(type.param0());
67+
68+
if (!isEnum && handler == null) {
69+
return null;
70+
}
71+
72+
return types.computeIfAbsent(
73+
type.full(),
74+
k ->
75+
new OptionalHandler(
76+
isEnum ? enumParamHandler(type.paramRaw()) : handler,
77+
isEnum));
78+
}
79+
6580
static TypeHandler enumParamHandler(UType type) {
6681
return new EnumHandler(type);
6782
}
@@ -374,6 +389,54 @@ public String toMethod() {
374389
}
375390
}
376391

392+
static class OptionalHandler implements TypeHandler {
393+
394+
private final List<String> importTypes;
395+
private final String shortName;
396+
private String toMethod;
397+
398+
OptionalHandler(TypeHandler handler, boolean isEnum) {
399+
400+
this.importTypes = new ArrayList<>(handler.importTypes());
401+
importTypes.add("io.avaje.http.api.PathTypeConversion");
402+
this.shortName = handler.shortName();
403+
this.toMethod =
404+
"optional("
405+
+ (isEnum
406+
? "qp -> " + handler.toMethod() + " qp)"
407+
: "PathTypeConversion::as" + shortName)
408+
+ ", ";
409+
410+
this.toMethod = toMethod.replace("PathTypeConversion::asString", "Object::toString");
411+
}
412+
413+
@Override
414+
public boolean isPrimitive() {
415+
return false;
416+
}
417+
418+
@Override
419+
public List<String> importTypes() {
420+
421+
return importTypes;
422+
}
423+
424+
@Override
425+
public String shortName() {
426+
return shortName;
427+
}
428+
429+
@Override
430+
public String asMethod() {
431+
return null;
432+
}
433+
434+
@Override
435+
public String toMethod() {
436+
return toMethod;
437+
}
438+
}
439+
377440
abstract static class ObjectHandler implements TypeHandler {
378441

379442
private final String importType;

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ default String param1() {
6666
}
6767

6868
/**
69-
* Return the raw generic parameter if this UType is a Collection.
69+
* Return the raw generic parameter if this UType is a Collection or an Optional.
7070
*/
7171
default UType paramRaw() {
7272
return null;
@@ -212,6 +212,7 @@ private String extractRawParam() {
212212
switch (mainType()) {
213213
case "java.util.Set":
214214
case "java.util.List":
215+
case "java.util.Optional":
215216
case "java.util.stream.Stream":
216217
case "java.net.http.HttpResponse":
217218
case "java.util.concurrent.CompletableFuture":

tests/test-jex/src/main/java/org/example/web/myapp/WebController.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import java.time.LocalDate;
66
import java.util.ArrayList;
77
import java.util.List;
8+
import java.util.Optional;
89
import java.util.concurrent.CompletableFuture;
910
import java.util.concurrent.Executors;
1011

@@ -176,4 +177,22 @@ String controlStatusCode(Context ctx) {
176177
String takesNestedEnum(Foo.NestedEnum myEnum) {
177178
return "takesNestedEnum-" + myEnum;
178179
}
180+
181+
@Produces(value = "text/plain")
182+
@Get("takesOptional{myOptional}")
183+
String takesOptional(@QueryParam("myOptional") Optional<Long> myOptional) {
184+
return "takesOptional-" + myOptional;
185+
}
186+
187+
@Produces(value = "text/plain")
188+
@Get("takesOptionalEnum{myOptional}")
189+
String takesOptionalEnum(@QueryParam("myOptional") Optional<Foo.NestedEnum> myOptional) {
190+
return "takesOptionalEnum-" + myOptional;
191+
}
192+
193+
@Produces(value = "text/plain")
194+
@Get("takesOptionalEnum{myOptional}")
195+
String takesOptionalString(@QueryParam("myOptional") Optional<String> myOptional) {
196+
return "takesOptionalString-" + myOptional;
197+
}
179198
}

0 commit comments

Comments
 (0)