diff --git a/http-api/src/main/java/io/avaje/http/api/MappedParam.java b/http-api/src/main/java/io/avaje/http/api/MappedParam.java
new file mode 100644
index 00000000..023eb2a7
--- /dev/null
+++ b/http-api/src/main/java/io/avaje/http/api/MappedParam.java
@@ -0,0 +1,50 @@
+package io.avaje.http.api;
+
+import static java.lang.annotation.ElementType.MODULE;
+import static java.lang.annotation.ElementType.PACKAGE;
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Repeatable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Marks a type to be mapped.
+ *
+ * The type needs to have a single constructor argument that is a String type,
+ * or have a factory method that has a single argument that is a String type.
+ */
+@Retention(RetentionPolicy.CLASS)
+@Target({ElementType.TYPE})
+public @interface MappedParam {
+
+ /** Factory method name used to construct the type. Empty means use a constructor */
+ String factoryMethod() default "";
+
+ /**
+ * Import a type to be mapped.
+ *
+ * The type needs to have a single constructor argument that is a String type,
+ * or have a factory method that has a single argument that is a String type.
+ */
+ @Repeatable(MappedParam.Import.Imports.class)
+ @Retention(SOURCE)
+ @Target({TYPE, PACKAGE, MODULE})
+ @interface Import {
+
+ Class> value();
+
+ /** Factory method name used to construct the type. Empty means use a constructor */
+ String factoryMethod() default "";
+
+ @Retention(SOURCE)
+ @Target({TYPE, PACKAGE, MODULE})
+ @interface Imports {
+
+ Import[] value();
+ }
+ }
+}
diff --git a/http-api/src/main/java/io/avaje/http/api/PathTypeConversion.java b/http-api/src/main/java/io/avaje/http/api/PathTypeConversion.java
index 55706c0b..7db576a0 100644
--- a/http-api/src/main/java/io/avaje/http/api/PathTypeConversion.java
+++ b/http-api/src/main/java/io/avaje/http/api/PathTypeConversion.java
@@ -98,6 +98,12 @@ public static int asInt(String value) {
}
}
+ /** Convert to type (not nullable). */
+ public static T asType(Function typeConversion, String value) {
+ checkNull(value);
+ return typeConversion.apply(value);
+ }
+
/**
* Convert to enum.
*/
@@ -288,9 +294,15 @@ public static Integer asInteger(String value) {
}
}
- /**
- * Convert to enum of the given type.
- */
+ /** Convert to type */
+ public static T toType(Function typeConversion, String value) {
+ if (isNullOrEmpty(value)) {
+ return null;
+ }
+ return typeConversion.apply(value);
+ }
+
+ /** Convert to enum of the given type. */
@SuppressWarnings({"rawtypes"})
public static Enum toEnum(Class clazz, String value) {
if (isNullOrEmpty(value)) {
diff --git a/http-generator-core/src/main/java/io/avaje/http/generator/core/BaseProcessor.java b/http-generator-core/src/main/java/io/avaje/http/generator/core/BaseProcessor.java
index 8ea2f187..3cb1e347 100644
--- a/http-generator-core/src/main/java/io/avaje/http/generator/core/BaseProcessor.java
+++ b/http-generator-core/src/main/java/io/avaje/http/generator/core/BaseProcessor.java
@@ -1,10 +1,6 @@
package io.avaje.http.generator.core;
-import static io.avaje.http.generator.core.ProcessingContext.doc;
-import static io.avaje.http.generator.core.ProcessingContext.elements;
-import static io.avaje.http.generator.core.ProcessingContext.isOpenApiAvailable;
-import static io.avaje.http.generator.core.ProcessingContext.logError;
-import static io.avaje.http.generator.core.ProcessingContext.typeElement;
+import static io.avaje.http.generator.core.ProcessingContext.*;
import static java.util.stream.Collectors.toMap;
import java.io.IOException;
@@ -14,6 +10,7 @@
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
+import java.util.Optional;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
@@ -22,9 +19,12 @@
import javax.annotation.processing.SupportedOptions;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.ElementFilter;
+import io.avaje.http.generator.core.TypeMap.CustomHandler;
import io.avaje.prism.GenerateAPContext;
import io.avaje.prism.GenerateModuleInfoReader;
@@ -54,7 +54,11 @@ public SourceVersion getSupportedSourceVersion() {
@Override
public Set getSupportedAnnotationTypes() {
return Set.of(
- PathPrism.PRISM_TYPE, ControllerPrism.PRISM_TYPE, OpenAPIDefinitionPrism.PRISM_TYPE);
+ PathPrism.PRISM_TYPE,
+ ControllerPrism.PRISM_TYPE,
+ OpenAPIDefinitionPrism.PRISM_TYPE,
+ MappedParamPrism.PRISM_TYPE,
+ MapImportPrism.PRISM_TYPE);
}
@Override
@@ -65,7 +69,6 @@ public synchronized void init(ProcessingEnvironment processingEnv) {
try {
var txtFilePath = APContext.getBuildResource(HTTP_CONTROLLERS_TXT);
-
if (txtFilePath.toFile().exists()) {
Files.lines(txtFilePath).forEach(clientFQNs::add);
}
@@ -75,8 +78,8 @@ public synchronized void init(ProcessingEnvironment processingEnv) {
}
}
} catch (IOException e) {
- e.printStackTrace();
// not worth failing over
+ logWarn("Error reading test controllers %s", e);
}
}
@@ -88,6 +91,17 @@ public boolean process(Set extends TypeElement> annotations, RoundEnvironment
if (round.errorRaised()) {
return false;
}
+
+ for (final var type : ElementFilter.typesIn(getElements(round, MappedParamPrism.PRISM_TYPE))) {
+ var prism = MappedParamPrism.getInstanceOn(type);
+ registerParamMapping(type, prism.factoryMethod());
+ }
+
+ for (final var type : getElements(round, MapImportPrism.PRISM_TYPE)) {
+ var prism = MapImportPrism.getInstanceOn(type);
+ registerParamMapping(APContext.asTypeElement(prism.value()), prism.factoryMethod());
+ }
+
var pathElements = round.getElementsAnnotatedWith(typeElement(PathPrism.PRISM_TYPE));
APContext.setProjectModuleElement(annotations, round);
if (contextPathString == null) {
@@ -111,9 +125,7 @@ public boolean process(Set extends TypeElement> annotations, RoundEnvironment
readSecuritySchemes(round);
}
- for (final var controller :
- ElementFilter.typesIn(
- round.getElementsAnnotatedWith(typeElement(ControllerPrism.PRISM_TYPE)))) {
+ for (final var controller : ElementFilter.typesIn(round.getElementsAnnotatedWith(typeElement(ControllerPrism.PRISM_TYPE)))) {
writeAdapter(controller);
}
@@ -136,9 +148,33 @@ public boolean process(Set extends TypeElement> annotations, RoundEnvironment
return false;
}
+ private Set extends Element> getElements(RoundEnvironment round, String name) {
+ return Optional.ofNullable(typeElement(name))
+ .map(round::getElementsAnnotatedWith)
+ .orElse(Set.of());
+ }
+
+ private final void registerParamMapping(final TypeElement type, String factoryMethod) {
+ if (factoryMethod.isBlank()) {
+ Util.stringConstructor(type)
+ .ifPresentOrElse(
+ c -> TypeMap.add(new CustomHandler(UType.parse(type.asType()), "")),
+ () -> logError(type, "Missing constructor %s(String s)"));
+
+ } else {
+ ElementFilter.methodsIn(type.getEnclosedElements()).stream()
+ .filter(m -> m.getSimpleName().contentEquals(factoryMethod)
+ && m.getModifiers().contains(Modifier.STATIC)
+ && Util.singleStringParam(m))
+ .findAny()
+ .ifPresentOrElse(
+ c -> TypeMap.add(new CustomHandler(UType.parse(type.asType()), factoryMethod)),
+ () -> logError(type, "Missing static factory method %s(String s)", factoryMethod));
+ }
+ }
+
private void readOpenApiDefinition(RoundEnvironment round) {
- for (final Element element :
- round.getElementsAnnotatedWith(typeElement(OpenAPIDefinitionPrism.PRISM_TYPE))) {
+ for (final Element element : round.getElementsAnnotatedWith(typeElement(OpenAPIDefinitionPrism.PRISM_TYPE))) {
doc().readApiDefinition(element);
}
}
@@ -147,19 +183,16 @@ private void readTagDefinitions(RoundEnvironment round) {
for (final Element element : round.getElementsAnnotatedWith(typeElement(TagPrism.PRISM_TYPE))) {
doc().addTagDefinition(element);
}
- for (final Element element :
- round.getElementsAnnotatedWith(typeElement(TagsPrism.PRISM_TYPE))) {
+ for (final Element element : round.getElementsAnnotatedWith(typeElement(TagsPrism.PRISM_TYPE))) {
doc().addTagsDefinition(element);
}
}
private void readSecuritySchemes(RoundEnvironment round) {
- for (final Element element :
- round.getElementsAnnotatedWith(typeElement(SecuritySchemePrism.PRISM_TYPE))) {
+ for (final Element element : round.getElementsAnnotatedWith(typeElement(SecuritySchemePrism.PRISM_TYPE))) {
doc().addSecurityScheme(element);
}
- for (final Element element :
- round.getElementsAnnotatedWith(typeElement(SecuritySchemesPrism.PRISM_TYPE))) {
+ for (final Element element : round.getElementsAnnotatedWith(typeElement(SecuritySchemesPrism.PRISM_TYPE))) {
doc().addSecuritySchemes(element);
}
}
@@ -174,7 +207,6 @@ private void writeAdapter(TypeElement controller) {
final var reader = new ControllerReader(controller, contextPath);
reader.read(true);
try {
-
writeControllerAdapter(reader);
writeClientAdapter(reader);
@@ -184,7 +216,6 @@ private void writeAdapter(TypeElement controller) {
}
private void writeClientAdapter(ControllerReader reader) {
-
try {
if (reader.beanType().getInterfaces().isEmpty()
&& "java.lang.Object".equals(reader.beanType().getSuperclass().toString())
diff --git a/http-generator-core/src/main/java/io/avaje/http/generator/core/ElementReader.java b/http-generator-core/src/main/java/io/avaje/http/generator/core/ElementReader.java
index d030763d..1c322b07 100644
--- a/http-generator-core/src/main/java/io/avaje/http/generator/core/ElementReader.java
+++ b/http-generator-core/src/main/java/io/avaje/http/generator/core/ElementReader.java
@@ -14,6 +14,7 @@
import javax.lang.model.element.*;
import javax.lang.model.type.TypeMirror;
+import io.avaje.http.generator.core.TypeMap.CustomHandler;
import io.avaje.http.generator.core.openapi.MethodDocBuilder;
import io.avaje.http.generator.core.openapi.MethodParamDocBuilder;
@@ -76,7 +77,9 @@ public class ElementReader {
if (!contextType) {
readAnnotations(element, defaultType);
useValidation = useValidation();
- HttpValidPrism.getOptionalOn(element.getEnclosingElement()).map(HttpValidPrism::groups).stream()
+ HttpValidPrism.getOptionalOn(element.getEnclosingElement())
+ .map(HttpValidPrism::groups)
+ .stream()
.flatMap(List::stream)
.map(TypeMirror::toString)
.forEach(validationGroups::add);
@@ -101,37 +104,58 @@ private void beanParamImports(String rawType) {
}
TypeHandler initTypeHandler() {
- if (specialParam) {
- final var typeOp = Optional.ofNullable(type).or(() -> Optional.of(UType.parse(element.asType())));
+ var handler = TypeMap.get(rawType);
+ final var typeOp = Optional.ofNullable(type).or(() -> Optional.of(UType.parse(element.asType())));
+
+ var customType = typeOp.orElseThrow();
+ var actual = customType.isGeneric() ? UType.parse(customType.param0()) : customType;
+
+ if (handler == null) {
+ Optional.ofNullable(APContext.typeElement(customType.full()))
+ .flatMap(MappedParamPrism::getOptionalOn)
+ .ifPresent(p -> TypeMap.add(new CustomHandler(actual, p.factoryMethod())));
+ handler = TypeMap.get(rawType);
+ }
+
+ if (handler == null && ParamPrism.isPresent(element)) {
+ handler =
+ Optional.ofNullable(APContext.typeElement(customType.full()))
+ .flatMap(Util::stringConstructor)
+ .map(m -> new CustomHandler(actual, ""))
+ .orElse(null);
+ }
+
+ if (specialParam) {
final var mainTypeEnum =
- typeOp
- .flatMap(t -> Optional.ofNullable(typeElement(t.mainType())))
- .map(TypeElement::getKind)
- .filter(ElementKind.ENUM::equals)
- .isPresent();
+ typeOp
+ .flatMap(t -> Optional.ofNullable(typeElement(t.mainType())))
+ .map(TypeElement::getKind)
+ .filter(ElementKind.ENUM::equals)
+ .isPresent();
final var isCollection =
- typeOp
- .filter(t -> t.isGeneric() && !t.mainType().startsWith("java.util.Map"))
- .isPresent();
+ typeOp
+ .filter(t -> t.isGeneric() && !t.mainType().startsWith("java.util.Map"))
+ .isPresent();
final var isMap =
- !isCollection && typeOp.filter(t -> t.mainType().startsWith("java.util.Map")).isPresent();
+ !isCollection && typeOp.filter(t -> t.mainType().startsWith("java.util.Map")).isPresent();
- final var isOptional = typeOp.filter(t -> t.mainType().startsWith("java.util.Optional")).isPresent();
+ final var isOptional =
+ typeOp.filter(t -> t.mainType().startsWith("java.util.Optional")).isPresent();
if (mainTypeEnum) {
return TypeMap.enumParamHandler(typeOp.orElseThrow());
} else if (isCollection || isOptional) {
final var isEnumContainer =
- typeOp
- .flatMap(t -> Optional.ofNullable(typeElement(t.param0())))
- .map(TypeElement::getKind)
- .filter(ElementKind.ENUM::equals)
- .isPresent();
+ typeOp
+ .flatMap(t -> Optional.ofNullable(typeElement(t.param0())))
+ .map(TypeElement::getKind)
+ .filter(ElementKind.ENUM::equals)
+ .isPresent();
- if (isOptional) {//Needs to be checked first, as 'isCollection' is too broad
+ if (isOptional) { // needs to be checked first, as 'isCollection' is too broad
return TypeMap.optionalHandler(typeOp.orElseThrow(), isEnumContainer);
}
this.isParamCollection = true;
@@ -142,7 +166,7 @@ TypeHandler initTypeHandler() {
}
}
- return TypeMap.get(rawType);
+ return handler;
}
private boolean useValidation() {
@@ -312,7 +336,7 @@ void writeValidate(Append writer) {
}
void writeCtxGet(Append writer, PathSegments segments) {
- if (isPlatformContext() || (paramType == ParamType.BODY && platform().isBodyMethodParam())) {
+ if (isPlatformContext() || paramType == ParamType.BODY && platform().isBodyMethodParam()) {
// body passed as method parameter (Helidon)
return;
}
@@ -347,9 +371,9 @@ private boolean setValue(Append writer, PathSegments segments, String shortType)
// path or matrix parameter
final boolean requiredParam = segment.isRequired(varName);
final String asMethod =
- (typeHandler == null)
- ? null
- : (requiredParam) ? typeHandler.asMethod() : typeHandler.toMethod();
+ typeHandler == null
+ ? null
+ : requiredParam ? typeHandler.asMethod() : typeHandler.toMethod();
if (asMethod != null) {
writer.append(asMethod);
}
@@ -362,7 +386,7 @@ private boolean setValue(Append writer, PathSegments segments, String shortType)
}
}
- final String asMethod = (typeHandler == null) ? null : typeHandler.toMethod();
+ final String asMethod = typeHandler == null ? null : typeHandler.toMethod();
if (asMethod != null) {
writer.append(asMethod);
}
@@ -382,7 +406,8 @@ private boolean setValue(Append writer, PathSegments segments, String shortType)
} else if (hasParamDefault()) {
platform().writeReadParameter(writer, paramType, paramName, paramDefault.get(0));
} else {
- final var checkNull = notNullKotlin || (paramType == ParamType.FORMPARAM && typeHandler.isPrimitive());
+ final var checkNull =
+ notNullKotlin || paramType == ParamType.FORMPARAM && typeHandler.isPrimitive();
if (checkNull) {
writer.append("checkNull(");
}
diff --git a/http-generator-core/src/main/java/io/avaje/http/generator/core/ParamPrism.java b/http-generator-core/src/main/java/io/avaje/http/generator/core/ParamPrism.java
new file mode 100644
index 00000000..29a433f8
--- /dev/null
+++ b/http-generator-core/src/main/java/io/avaje/http/generator/core/ParamPrism.java
@@ -0,0 +1,41 @@
+package io.avaje.http.generator.core;
+
+import java.util.Optional;
+
+import javax.lang.model.element.Element;
+
+import io.avaje.prism.GeneratePrism;
+
+@GeneratePrism(
+ value = io.avaje.http.api.QueryParam.class,
+ publicAccess = true,
+ superInterfaces = ParamPrism.class)
+@GeneratePrism(
+ value = io.avaje.http.api.Cookie.class,
+ publicAccess = true,
+ superInterfaces = ParamPrism.class)
+@GeneratePrism(
+ value = io.avaje.http.api.FormParam.class,
+ publicAccess = true,
+ superInterfaces = ParamPrism.class)
+@GeneratePrism(
+ value = io.avaje.http.api.Header.class,
+ publicAccess = true,
+ superInterfaces = ParamPrism.class)
+@GeneratePrism(
+ value = io.avaje.http.api.MatrixParam.class,
+ publicAccess = true,
+ superInterfaces = ParamPrism.class)
+public interface ParamPrism {
+
+ static boolean isPresent(Element e) {
+ return Optional.empty()
+ .or(() -> QueryParamPrism.getOptionalOn(e))
+ .or(() -> CookiePrism.getOptionalOn(e))
+ .or(() -> FormParamPrism.getOptionalOn(e))
+ .or(() -> HeaderPrism.getOptionalOn(e))
+ .or(() -> MatrixParamPrism.getOptionalOn(e))
+ .isPresent();
+ }
+
+}
diff --git a/http-generator-core/src/main/java/io/avaje/http/generator/core/TypeMap.java b/http-generator-core/src/main/java/io/avaje/http/generator/core/TypeMap.java
index 8b6452f5..a7741f2f 100644
--- a/http-generator-core/src/main/java/io/avaje/http/generator/core/TypeMap.java
+++ b/http-generator-core/src/main/java/io/avaje/http/generator/core/TypeMap.java
@@ -14,7 +14,7 @@ final class TypeMap {
private static final Map types = new HashMap<>();
- private static void add(TypeHandler h) {
+ static void add(TypeHandler h) {
types.put(h.importTypes().get(0), h);
}
@@ -342,13 +342,12 @@ private CollectionHandler(TypeHandler handler, boolean set, boolean isEnum) {
this.importTypes.add("io.avaje.http.api.PathTypeConversion");
this.shortName = handler.shortName();
String _toMethod =
- (set ? "set" : "list")
- + "("
- + (isEnum
- ? "qp -> " + handler.toMethod() + " qp)"
- : "PathTypeConversion::as" + shortName)
- + ", ";
-
+ (set ? "set" : "list")
+ + "("
+ + (isEnum || handler instanceof CustomHandler
+ ? "qp -> " + handler.toMethod() + " qp)"
+ : "PathTypeConversion::as" + shortName)
+ + ", ";
this.toMethod = _toMethod.replace("PathTypeConversion::asString", "Object::toString");
}
@@ -397,7 +396,7 @@ private OptionalHandler(TypeHandler handler, boolean isEnum) {
}
static String buildToMethod(TypeHandler handler, boolean isEnum) {
- if (isEnum) {
+ if (isEnum || handler instanceof CustomHandler) {
return "optional(qp -> " + handler.toMethod() + " qp), ";
}
if ("String".equals(handler.shortName())) {
@@ -433,6 +432,32 @@ public String toMethod() {
}
}
+ static final class CustomHandler extends ObjectHandler {
+
+ private final UType type;
+ private final String factory;
+
+ CustomHandler(UType type, String factory) {
+ super(type.mainType(), type.shortName());
+ this.type = type;
+ this.factory = factory;
+ }
+
+ @Override
+ public String toMethod() {
+ return "toType("
+ + type.shortTypeNested()
+ + "::"
+ + (factory.isBlank() ? "new" : factory)
+ + ", ";
+ }
+
+ @Override
+ public String asMethod() {
+ return "asType(" + type.shortTypeNested() + "::" + (factory.isBlank() ? "new" : factory);
+ }
+ }
+
static abstract class ObjectHandler implements TypeHandler {
private final String importType;
diff --git a/http-generator-core/src/main/java/io/avaje/http/generator/core/Util.java b/http-generator-core/src/main/java/io/avaje/http/generator/core/Util.java
index 3a6a79b6..8ba9204a 100644
--- a/http-generator-core/src/main/java/io/avaje/http/generator/core/Util.java
+++ b/http-generator-core/src/main/java/io/avaje/http/generator/core/Util.java
@@ -1,20 +1,25 @@
package io.avaje.http.generator.core;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.ElementFilter;
import javax.lang.model.util.SimpleAnnotationValueVisitor8;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-public class Util {
+public final class Util {
// whitespace not in quotes
private static final Pattern WHITE_SPACE_REGEX =
Pattern.compile("\\s+(?=([^\"]*\"[^\"]*\")*[^\"]*$)");
@@ -266,4 +271,22 @@ public List visitEnumConstant(VariableElement roleEnum, Object o) {
return fullRoles;
}
}
+
+ static Optional stringConstructor(TypeElement typeElement) {
+ return ElementFilter.constructorsIn(typeElement.getEnclosedElements()).stream()
+ .filter(Util::singleStringParam)
+ .findAny();
+ }
+
+ static boolean singleStringParam(ExecutableElement m) {
+ return m.getParameters().size() == 1 && firstParamIsString(m);
+ }
+
+ static boolean firstParamIsString(ExecutableElement m) {
+ return m.getParameters()
+ .get(0)
+ .asType()
+ .toString()
+ .equals(String.class.getTypeName());
+ }
}
diff --git a/http-generator-core/src/main/java/io/avaje/http/generator/core/package-info.java b/http-generator-core/src/main/java/io/avaje/http/generator/core/package-info.java
index 50ff9161..e71d98d9 100644
--- a/http-generator-core/src/main/java/io/avaje/http/generator/core/package-info.java
+++ b/http-generator-core/src/main/java/io/avaje/http/generator/core/package-info.java
@@ -1,18 +1,15 @@
-/** Generate the prisms to access annotation info */
+/**
+ * Generate the prisms to access annotation info
+ */
@GeneratePrism(value = io.avaje.http.api.Controller.class, publicAccess = true, superInterfaces = WebAPIPrism.class)
@GeneratePrism(value = io.avaje.http.api.Client.class, publicAccess = true, superInterfaces = WebAPIPrism.class)
@GeneratePrism(value = io.avaje.http.api.BeanParam.class, publicAccess = true)
@GeneratePrism(value = io.avaje.http.api.Ignore.class, publicAccess = true)
-@GeneratePrism(value = io.avaje.http.api.QueryParam.class, publicAccess = true)
-@GeneratePrism(value = io.avaje.http.api.Cookie.class, publicAccess = true)
@GeneratePrism(value = io.avaje.http.api.BodyString.class, publicAccess = true)
@GeneratePrism(value = io.avaje.http.api.Default.class, publicAccess = true)
@GeneratePrism(value = io.avaje.http.api.Delete.class, publicAccess = true)
@GeneratePrism(value = io.avaje.http.api.Form.class, publicAccess = true)
-@GeneratePrism(value = io.avaje.http.api.FormParam.class, publicAccess = true)
@GeneratePrism(value = io.avaje.http.api.Get.class, publicAccess = true)
-@GeneratePrism(value = io.avaje.http.api.Header.class, publicAccess = true)
-@GeneratePrism(value = io.avaje.http.api.MatrixParam.class, publicAccess = true)
@GeneratePrism(value = io.avaje.http.api.Patch.class, publicAccess = true)
@GeneratePrism(value = io.avaje.http.api.Path.class, publicAccess = true)
@GeneratePrism(value = io.avaje.http.api.Post.class, publicAccess = true)
@@ -22,6 +19,8 @@
@GeneratePrism(value = io.avaje.http.api.Filter.class)
@GeneratePrism(value = io.avaje.http.api.InstrumentServerContext.class)
@GeneratePrism(value = io.avaje.http.api.ExceptionHandler.class)
+@GeneratePrism(value = io.avaje.http.api.MappedParam.class)
+@GeneratePrism(value = io.avaje.http.api.MappedParam.Import.class, name = "MapImportPrism")
@GeneratePrism(value = io.swagger.v3.oas.annotations.OpenAPIDefinition.class, publicAccess = true)
@GeneratePrism(value = io.swagger.v3.oas.annotations.Operation.class, publicAccess = true)
@GeneratePrism(value = io.swagger.v3.oas.annotations.tags.Tag.class, publicAccess = true)
diff --git a/tests/test-nima-jsonb/src/main/java/org/example/TestController.java b/tests/test-nima-jsonb/src/main/java/org/example/TestController.java
index bbb0db33..afcd656b 100644
--- a/tests/test-nima-jsonb/src/main/java/org/example/TestController.java
+++ b/tests/test-nima-jsonb/src/main/java/org/example/TestController.java
@@ -1,7 +1,10 @@
package org.example;
import java.io.InputStream;
-import java.util.*;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
import io.avaje.http.api.BodyString;
import io.avaje.http.api.Controller;
@@ -12,6 +15,7 @@
import io.avaje.http.api.FormParam;
import io.avaje.http.api.Get;
import io.avaje.http.api.InstrumentServerContext;
+import io.avaje.http.api.MappedParam;
import io.avaje.http.api.Path;
import io.avaje.http.api.Post;
import io.avaje.http.api.Produces;
@@ -162,4 +166,45 @@ Person maybePerson(boolean maybe) {
List maybePersonList(boolean maybe) {
return maybe ? List.of(new Person(9, "hi")) : null; // Collections.emptyList();
}
+
+ @MappedParam
+ @MappedParam.Import(Simple2.class)
+ record Simple(String name) {}
+
+ record Simple2(String name) {}
+
+ @Form
+ @Get("/typeForm")
+ String typeForm(Simple s, Simple2 type) {
+ return type.name();
+ }
+
+ @MappedParam(factoryMethod = "build")
+ record Static(String name) {
+ static Static build(String name) {
+ return new Static(name);
+ }
+ }
+
+ @Get("/typeFormParam")
+ String typeFormParam(@FormParam String s, @FormParam Static type) {
+ return type.name();
+ }
+
+ @Get("/typeQuery")
+ String typeQuery(@QueryParam @Default("FFA") Static type) {
+ return type.name();
+ }
+
+ @Get("/typeQuery2")
+ String typeMultiQuery(@QueryParam @Default({"FFA", "PROXY"}) Set type) {
+ return type.toString();
+ }
+
+ record Implied(String name) {}
+
+ @Post("/typeQueryImplied")
+ String typeQueryImplied(String s, @QueryParam Implied type) {
+ return type.name();
+ }
}