diff --git a/http-api/src/main/java/io/avaje/http/api/Headers.java b/http-api/src/main/java/io/avaje/http/api/Headers.java new file mode 100644 index 000000000..47486a084 --- /dev/null +++ b/http-api/src/main/java/io/avaje/http/api/Headers.java @@ -0,0 +1,32 @@ +package io.avaje.http.api; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + * Headers for an Http @Client interface method. + * + *

We can put this on a method or the interface to add preset headers to the generated + * implementation bean property. + * + *

{@code
+ * @Headers({
+ * "Accept: application/vnd.github.v3.full+json",
+ * "User-Agent: Avaje-Sample-App"
+ * })
+ * @Get("users/{username}")
+ * User getUser(@Path("username") String username);
+ *
+ * }
+ */ +@Retention(RUNTIME) +@Target({TYPE, METHOD}) +public @interface Headers { + + /** The array of headers */ + String[] value(); +} 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 a9175d406..ed838d217 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 @@ -7,11 +7,15 @@ import javax.lang.model.util.ElementFilter; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.Optional; import java.util.Set; +import java.util.function.Function; import java.util.stream.Collectors; +import java.util.stream.Stream; import static io.avaje.http.generator.core.ProcessingContext.*; +import static java.util.stream.Collectors.toList; import static java.util.stream.Collectors.toMap; /** @@ -35,6 +39,7 @@ final class ClientMethodWriter { 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) { this.method = method; @@ -50,6 +55,24 @@ final class ClientMethodWriter { .filter(Segment::isProperty) .collect(toMap(Segment::name, s -> Util.sanitizeName(s.name()).toUpperCase())); this.propertyConstants = propertyConstants; + var element = method.element(); + + this.presetHeaders = + Stream.concat( + HeadersPrism.getOptionalOn(element).stream(), + HeadersPrism.getOptionalOn(element.getEnclosingElement()).stream()) + .map(HeadersPrism::value) + .map(List::stream) + .flatMap(Function.identity()) + .peek( + s -> { + if (!s.contains(":")) { + logError(element, "@Headers value must have a \":\"", method); + } + }) + .map(s -> s.split(":", 2)) + .filter(a -> a.length == 2) + .map(a -> Map.entry(a[0].trim(), a[1].trim())).collect(toList()); } void addImportTypes(ControllerReader reader) { @@ -272,6 +295,8 @@ private void writeHeaders() { } } } + presetHeaders.forEach(e -> + writer.append(" .header(\"%s\", \"%s\")", e.getKey(), e.getValue().replace("\\", "\\\\")).eol()); } private void writeBeanParams(PathSegments segments) { 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 05b14fc04..99a3d2512 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 @@ -21,7 +21,9 @@ import io.avaje.http.generator.core.ControllerReader; import io.avaje.http.generator.core.ImportPrism; import io.avaje.http.generator.core.ProcessingContext; +import io.avaje.prism.GeneratePrism; +@GeneratePrism(io.avaje.http.api.Headers.class) @SupportedAnnotationTypes({ClientPrism.PRISM_TYPE, ImportPrism.PRISM_TYPE}) public class ClientProcessor extends AbstractProcessor { diff --git a/http-generator-client/src/test/java/io/avaje/http/generator/client/clients/TitanFall.java b/http-generator-client/src/test/java/io/avaje/http/generator/client/clients/TitanFall.java index 918c685fa..64d32e84f 100644 --- a/http-generator-client/src/test/java/io/avaje/http/generator/client/clients/TitanFall.java +++ b/http-generator-client/src/test/java/io/avaje/http/generator/client/clients/TitanFall.java @@ -2,14 +2,18 @@ import io.avaje.http.api.Client; import io.avaje.http.api.Get; +import io.avaje.http.api.Headers; @Client +@Headers("Content-Type: applicaton/json") public interface TitanFall { @Get("/${titan}/${drop.point}") + @Headers("Something: \\invalid\n\t") Titan titanFall(); @Get("/${titan}/copium") + @Headers(" Accept : applicaton/json") Titan titanFall3(); } \ No newline at end of file