diff --git a/Dialects.md b/Dialects.md new file mode 100644 index 0000000..83168c4 --- /dev/null +++ b/Dialects.md @@ -0,0 +1,11 @@ +# Custom dialects + +Defining a new dialect is possible by depending on rest-ahead-processor. Required steps are as following: + +1. Create a class implementing `io.github.zskamljic.restahead.polyglot.Dialect`. +2. Create a file in `main/resources/META-INF/services` named `io.github.zskamljic.restahead.polyglot.Dialect` (note that + this is a file name, not a Java package) +3. In created file add the line with FQCN of the class created in #1. +4. In project where you want the dialect to be used add the dependency with at least `provided` scope. + +For sample implementation see Spring Dialect. \ No newline at end of file diff --git a/Readme.md b/Readme.md index 29528df..a1ed166 100644 --- a/Readme.md +++ b/Readme.md @@ -21,6 +21,7 @@ approach. - [Query](#queries) - [Responses](#response-types) - [Spring Boot](#spring-boot) +- [Dialects](#dialects) ## Introduction @@ -357,6 +358,40 @@ public interface DemoService { injection. URL property needs to be provided to have a baseUrl configured, converter property is optional and is required only if the service requires one, see [response types](#response-types). +### Dialects + +The code generator allows for usage of multiple dialects (Default being RestAhead). For example, Spring dialect can be +used, by adding the dependency: + +```xml + + + io.github.zskamljic + rest-ahead-spring-dialect + ${rest.ahead.version} + +``` + +It can then be used as following: + +```java +interface SpringService { + @GetMappin("/get") + Response performGet(@RequestHeader String header, @RequestParam String query); + + @RequestMapping(method = RequestMethod.GET, value = "/{param}") + HttpBinResponse get2(@PathVariable String param); + + @PostMapping("/multipart") + HttpBinResponse postFile(@RequestPart MultiPartFile file); + + @PostMapping("/customBody") + HttpBinResponse postFormData(@RequestPart Map body); +} +``` + +Info on how to declare a new dialect can be seen in [Dialects](Dialects.md) + ## Adding to project Add the dependencies as following: diff --git a/demo/pom.xml b/demo/pom.xml index 983a6ad..0ea5e5e 100644 --- a/demo/pom.xml +++ b/demo/pom.xml @@ -23,6 +23,12 @@ 0.3.0-SNAPSHOT provided + + io.github.zskamljic + rest-ahead-spring-dialect + 0.3.0-SNAPSHOT + provided + io.github.zskamljic rest-ahead-jackson-converter diff --git a/pom.xml b/pom.xml index c9c3e89..e17c96a 100644 --- a/pom.xml +++ b/pom.xml @@ -22,6 +22,7 @@ rest-ahead-jackson-converter test-report-aggregator rest-ahead-spring + rest-ahead-spring-dialect @@ -29,8 +30,8 @@ 17 17 5.8.2 - 4.2.0 - 3.8.1 + 4.3.1 + 3.9.0 2.22.2 0.8.7 @@ -42,6 +43,10 @@ java zskamljic-github https://sonarcloud.io + 1.1.3 + 0.19 + + 5.3.15 https://github.com/zskamljic/rest-ahead diff --git a/rest-ahead-client/src/main/java/io/github/zskamljic/restahead/conversion/MapFormConverter.java b/rest-ahead-client/src/main/java/io/github/zskamljic/restahead/conversion/MapFormConverter.java new file mode 100644 index 0000000..714d5e2 --- /dev/null +++ b/rest-ahead-client/src/main/java/io/github/zskamljic/restahead/conversion/MapFormConverter.java @@ -0,0 +1,31 @@ +package io.github.zskamljic.restahead.conversion; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * The default converter for Map classes. + */ +public final class MapFormConverter { + private MapFormConverter() { + } + + /** + * Encodes the given value in format [key]=[URL encoded value] separated by & + * @param value the values to encode + * @param type of the key + * @param type of the value + * @return the {@link InputStream} with encoded data + */ + public static InputStream formEncode(Map value) { + var stringValue = value.entrySet() + .stream() + .map(entry -> entry.getKey() + "=" + URLEncoder.encode(String.valueOf(entry.getValue()), StandardCharsets.UTF_8)) + .collect(Collectors.joining("&")); + return new ByteArrayInputStream(stringValue.getBytes(StandardCharsets.UTF_8)); + } +} diff --git a/rest-ahead-processor/pom.xml b/rest-ahead-processor/pom.xml index 3dff921..a080bd4 100644 --- a/rest-ahead-processor/pom.xml +++ b/rest-ahead-processor/pom.xml @@ -27,18 +27,6 @@ - - com.google.truth - truth - 1.1.3 - test - - - com.google.testing.compile - compile-testing - 0.19 - test - org.junit.jupiter junit-jupiter-engine @@ -87,17 +75,4 @@ - - - - testing only - - [9,) - - - --add-opens jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED - - - - \ No newline at end of file diff --git a/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/encoding/FormBodyEncoding.java b/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/encoding/FormBodyEncoding.java index c25c5c0..b9c074c 100644 --- a/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/encoding/FormBodyEncoding.java +++ b/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/encoding/FormBodyEncoding.java @@ -1,6 +1,6 @@ package io.github.zskamljic.restahead.encoding; -import io.github.zskamljic.restahead.encoding.generation.GenerationStrategy; +import io.github.zskamljic.restahead.encoding.generation.FormConversionStrategy; /** * Specifies that the type should use form encoding. @@ -8,5 +8,5 @@ * @param parameterName the name of parameter to encode * @param strategy the strategy to use when generating the conversion code */ -public record FormBodyEncoding(String parameterName, GenerationStrategy strategy) implements BodyEncoding { +public record FormBodyEncoding(String parameterName, FormConversionStrategy strategy) implements BodyEncoding { } diff --git a/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/encoding/MultiPartParameter.java b/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/encoding/MultiPartParameter.java index 87d2198..663df89 100644 --- a/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/encoding/MultiPartParameter.java +++ b/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/encoding/MultiPartParameter.java @@ -7,13 +7,15 @@ /** * A parameter that specifies a part of multipart request. * - * @param httpName the name to use in http transport - * @param name the name of function parameter - * @param type which type to use when generating the code, empty if it's already an appropriate type + * @param httpName the name to use in http transport + * @param name the name of function parameter + * @param type which type to use when generating the code, empty if it's already an appropriate type + * @param extraParameters extra parameters that should be sent to the constructor */ public record MultiPartParameter( String httpName, String name, - Optional> type + Optional> type, + Optional extraParameters ) { } diff --git a/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/encoding/generation/ClassGenerationStrategy.java b/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/encoding/generation/ClassGenerationStrategy.java index cb16bd0..872fc8a 100644 --- a/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/encoding/generation/ClassGenerationStrategy.java +++ b/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/encoding/generation/ClassGenerationStrategy.java @@ -20,9 +20,8 @@ /** * Used to generate class to form encoded string conversion. */ -public record ClassGenerationStrategy(TypeMirror type) implements GenerationStrategy { - @Override - public MethodSpec generateMethod() { +public record ClassGenerationStrategy(TypeMirror type) implements FormConversionStrategy { + public MethodSpec generate() { var builder = MethodSpec.methodBuilder(Variables.FORM_ENCODE) .addModifiers(Modifier.PUBLIC, Modifier.STATIC) .addParameter(TypeName.get(type), "value") @@ -44,7 +43,7 @@ public MethodSpec generateMethod() { * @param mirror the type for which to find a strategy * @return generation strategy if no issues were discovered, empty otherwise */ - public static Optional getIfSupported(Messager messager, Elements elements, Types types, TypeMirror mirror) { + public static Optional getIfSupported(Messager messager, Elements elements, Types types, TypeMirror mirror) { if (!(mirror instanceof DeclaredType declaredType)) return Optional.empty(); var getters = findGetters(declaredType); diff --git a/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/encoding/generation/GenerationStrategy.java b/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/encoding/generation/FormConversionStrategy.java similarity index 70% rename from rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/encoding/generation/GenerationStrategy.java rename to rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/encoding/generation/FormConversionStrategy.java index 32f3df5..3aec652 100644 --- a/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/encoding/generation/GenerationStrategy.java +++ b/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/encoding/generation/FormConversionStrategy.java @@ -1,7 +1,5 @@ package io.github.zskamljic.restahead.encoding.generation; -import com.squareup.javapoet.MethodSpec; - import javax.annotation.processing.Messager; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.Elements; @@ -12,7 +10,7 @@ /** * Outlines the generation strategy for some type. */ -public sealed interface GenerationStrategy permits ClassGenerationStrategy, MapGenerationStrategy, RecordGenerationStrategy { +public sealed interface FormConversionStrategy permits ClassGenerationStrategy, MapConversionStrategy, RecordGenerationStrategy { /** * The type that this strategy applies to. Return from this value will be used to ensure that only one converter * will be generated for each type. @@ -21,13 +19,6 @@ public sealed interface GenerationStrategy permits ClassGenerationStrategy, MapG */ TypeMirror type(); - /** - * Generate the method for this type and strategy. - * - * @return the generated convert method. - */ - MethodSpec generateMethod(); - /** * Selects an appropriate generation strategy for given type. * @@ -37,9 +28,9 @@ public sealed interface GenerationStrategy permits ClassGenerationStrategy, MapG * @param mirror the type for which to find a strategy * @return the strategy or empty if none was found */ - static Optional select(Messager messager, Elements elements, Types types, TypeMirror mirror) { + static Optional select(Messager messager, Elements elements, Types types, TypeMirror mirror) { Stream providers = Stream.of( - MapGenerationStrategy::getIfSupported, + MapConversionStrategy::getIfSupported, RecordGenerationStrategy::getIfSupported, ClassGenerationStrategy::getIfSupported ); @@ -53,6 +44,6 @@ static Optional select(Messager messager, Elements elements, */ @FunctionalInterface interface OptionalStrategyProvider { - Optional getIfSupported(Messager messager, Elements elements, Types types, TypeMirror mirror); + Optional getIfSupported(Messager messager, Elements elements, Types types, TypeMirror mirror); } } diff --git a/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/encoding/generation/MapConversionStrategy.java b/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/encoding/generation/MapConversionStrategy.java new file mode 100644 index 0000000..2394a55 --- /dev/null +++ b/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/encoding/generation/MapConversionStrategy.java @@ -0,0 +1,48 @@ +package io.github.zskamljic.restahead.encoding.generation; + +import io.github.zskamljic.restahead.modeling.TypeValidator; + +import javax.annotation.processing.Messager; +import javax.lang.model.type.DeclaredType; +import javax.lang.model.type.TypeMirror; +import javax.lang.model.util.Elements; +import javax.lang.model.util.Types; +import javax.tools.Diagnostic; +import java.util.Map; +import java.util.Optional; + +/** + * Generates a form converter for maps. + */ +public record MapConversionStrategy(TypeMirror type) implements FormConversionStrategy { + + /** + * Checks if provided type is a Map or one of the subclasses that has string representable keys and values. + * + * @param elements the elements to fetch type information from + * @param types the types utility to use for typing info + * @param mirror the type for which to find a strategy + * @return a strategy if data is valid, empty otherwise + */ + public static Optional getIfSupported(Messager messager, Elements elements, Types types, TypeMirror mirror) { + var type = elements.getTypeElement(Map.class.getCanonicalName()) + .asType(); + if (!types.isAssignable(types.erasure(mirror), type)) { + return Optional.empty(); + } + var mapType = (DeclaredType) mirror; + var genericArguments = mapType.getTypeArguments(); + if (genericArguments.size() != 2) return Optional.empty(); + + var stringValidator = new TypeValidator(elements, types); + var key = genericArguments.get(0); + var value = genericArguments.get(1); + + if (stringValidator.isUnsupportedType(key) || stringValidator.isUnsupportedType(value)) { + messager.printMessage(Diagnostic.Kind.ERROR, "Maps must consist of string representable values to be formEncoded", mapType.asElement()); + return Optional.empty(); + } + + return Optional.of(new MapConversionStrategy(types.erasure(type))); + } +} diff --git a/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/encoding/generation/MapGenerationStrategy.java b/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/encoding/generation/MapGenerationStrategy.java deleted file mode 100644 index e526abf..0000000 --- a/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/encoding/generation/MapGenerationStrategy.java +++ /dev/null @@ -1,91 +0,0 @@ -package io.github.zskamljic.restahead.encoding.generation; - -import com.squareup.javapoet.ClassName; -import com.squareup.javapoet.MethodSpec; -import com.squareup.javapoet.ParameterizedTypeName; -import com.squareup.javapoet.TypeName; -import com.squareup.javapoet.TypeVariableName; -import io.github.zskamljic.restahead.generation.Variables; -import io.github.zskamljic.restahead.modeling.TypeValidator; - -import javax.annotation.processing.Messager; -import javax.lang.model.element.Modifier; -import javax.lang.model.type.DeclaredType; -import javax.lang.model.type.TypeMirror; -import javax.lang.model.util.Elements; -import javax.lang.model.util.Types; -import javax.tools.Diagnostic; -import java.io.ByteArrayInputStream; -import java.io.InputStream; -import java.net.URLEncoder; -import java.nio.charset.StandardCharsets; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.stream.Collectors; - -/** - * Generates a form converter for maps. - */ -public record MapGenerationStrategy(TypeMirror type) implements GenerationStrategy { - - /** - * Generates the map converter, mapping keys to value. - * - * @return the generated method - */ - @Override - public MethodSpec generateMethod() { - var keyParameter = TypeVariableName.get("Key"); - var valueParameter = TypeVariableName.get("Value"); - var builder = MethodSpec.methodBuilder(Variables.FORM_ENCODE) - .addModifiers(Modifier.STATIC, Modifier.PUBLIC) - .addParameter(ParameterizedTypeName.get((ClassName) TypeName.get(type), keyParameter, valueParameter), "value") - .addTypeVariables(List.of(keyParameter, valueParameter)) - .returns(InputStream.class); - - return builder.addStatement( - """ - var stringValue = value.entrySet() - .stream() - .map(entry -> entry.getKey() + "=" + $T.encode($T.valueOf(entry.getValue()), $T.UTF_8)) - .collect($T.joining("&"))""", - URLEncoder.class, - String.class, - StandardCharsets.class, - Collectors.class - ) - .addStatement("return new $T(stringValue.getBytes())", ByteArrayInputStream.class) - .build(); - } - - /** - * Checks if provided type is a Map or one of the subclasses that has string representable keys and values. - * - * @param elements the elements to fetch type information from - * @param types the types utility to use for typing info - * @param mirror the type for which to find a strategy - * @return a strategy if data is valid, empty otherwise - */ - public static Optional getIfSupported(Messager messager, Elements elements, Types types, TypeMirror mirror) { - var type = elements.getTypeElement(Map.class.getCanonicalName()) - .asType(); - if (!types.isAssignable(types.erasure(mirror), type)) { - return Optional.empty(); - } - var mapType = (DeclaredType) mirror; - var genericArguments = mapType.getTypeArguments(); - if (genericArguments.size() != 2) return Optional.empty(); - - var stringValidator = new TypeValidator(elements, types); - var key = genericArguments.get(0); - var value = genericArguments.get(1); - - if (stringValidator.isUnsupportedType(key) || stringValidator.isUnsupportedType(value)) { - messager.printMessage(Diagnostic.Kind.ERROR, "Maps must consist of string representable values to be formEncoded", mapType.asElement()); - return Optional.empty(); - } - - return Optional.of(new MapGenerationStrategy(types.erasure(type))); - } -} diff --git a/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/encoding/generation/RecordGenerationStrategy.java b/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/encoding/generation/RecordGenerationStrategy.java index 7351dd4..7bcb54a 100644 --- a/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/encoding/generation/RecordGenerationStrategy.java +++ b/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/encoding/generation/RecordGenerationStrategy.java @@ -24,14 +24,9 @@ /** * Generates a converter for the given record. */ -public record RecordGenerationStrategy(TypeMirror type) implements GenerationStrategy { - /** - * The generated value, using component names as keys and their values as values. - * - * @return the generated method - */ - @Override - public MethodSpec generateMethod() { +public record RecordGenerationStrategy(TypeMirror type) implements FormConversionStrategy { + + public MethodSpec generate() { var builder = MethodSpec.methodBuilder(Variables.FORM_ENCODE) .addModifiers(Modifier.PUBLIC, Modifier.STATIC) .addParameter(TypeName.get(type), "value") @@ -52,7 +47,7 @@ public MethodSpec generateMethod() { * @param mirror the type for which to find a strategy * @return the strategy if it can be applied, empty otherwise */ - public static Optional getIfSupported(Messager messager, Elements elements, Types types, TypeMirror mirror) { + public static Optional getIfSupported(Messager messager, Elements elements, Types types, TypeMirror mirror) { if (!(mirror instanceof DeclaredType declaredType)) return Optional.empty(); var element = declaredType.asElement(); diff --git a/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/generation/FormConverterGenerator.java b/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/generation/FormConverterGenerator.java deleted file mode 100644 index fa98af0..0000000 --- a/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/generation/FormConverterGenerator.java +++ /dev/null @@ -1,88 +0,0 @@ -package io.github.zskamljic.restahead.generation; - -import com.squareup.javapoet.JavaFile; -import com.squareup.javapoet.TypeSpec; -import io.github.zskamljic.restahead.encoding.FormBodyEncoding; -import io.github.zskamljic.restahead.encoding.generation.GenerationStrategy; -import io.github.zskamljic.restahead.modeling.declaration.CallDeclaration; -import io.github.zskamljic.restahead.modeling.declaration.ParameterDeclaration; -import io.github.zskamljic.restahead.modeling.declaration.ServiceDeclaration; - -import javax.annotation.processing.Filer; -import javax.annotation.processing.Messager; -import javax.lang.model.element.Modifier; -import javax.lang.model.type.TypeMirror; -import javax.tools.Diagnostic; -import java.io.IOException; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Optional; - -/** - * Generates the FormConverter class if necessary. - */ -public class FormConverterGenerator { - private final Messager messager; - private final Filer filer; - - public FormConverterGenerator(Messager messager, Filer filer) { - this.messager = messager; - this.filer = filer; - } - - /** - * Checks if the class needs to be generated and generate it if needed. - * - * @param serviceDeclarations the services to check for any form data. - */ - public void generateFormEncoderIfNeeded(List serviceDeclarations) { - var formEncodableParameters = serviceDeclarations.stream() - .map(ServiceDeclaration::calls) - .flatMap(Collection::stream) - .map(CallDeclaration::parameters) - .map(ParameterDeclaration::body) - .flatMap(Optional::stream) - .filter(FormBodyEncoding.class::isInstance) - .map(FormBodyEncoding.class::cast) - .toList(); - if (formEncodableParameters.isEmpty()) { - return; - } - - var encoder = generateFormEncoder(formEncodableParameters); - var javaFile = JavaFile.builder(FormConverterGenerator.class.getPackageName(), encoder) - .indent(" ") - .build(); - - try { - javaFile.writeTo(filer); - messager.printMessage(Diagnostic.Kind.NOTE, "Generated " + encoder.name); - } catch (IOException e) { - messager.printMessage(Diagnostic.Kind.ERROR, "Unable to write class: " + e.getMessage()); - } - } - - /** - * Generates the actual implementation. - * - * @param parameters the parts that require generation - * @return the generated type. - */ - private TypeSpec generateFormEncoder(List parameters) { - var typeToStrategy = new HashMap(); - for (var parameter : parameters) { - typeToStrategy.put(parameter.strategy().type(), parameter.strategy()); - } - - var typeBuilder = TypeSpec.classBuilder(Variables.FORM_CONVERTER) - .addModifiers(Modifier.FINAL, Modifier.PUBLIC); - - var methods = typeToStrategy.values() - .stream() - .map(GenerationStrategy::generateMethod); - typeBuilder.addMethods(methods::iterator); - - return typeBuilder.build(); - } -} diff --git a/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/generation/MethodGenerator.java b/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/generation/MethodGenerator.java index 8ed3966..77c9fc1 100644 --- a/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/generation/MethodGenerator.java +++ b/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/generation/MethodGenerator.java @@ -1,25 +1,34 @@ package io.github.zskamljic.restahead.generation; -import com.squareup.javapoet.ClassName; import com.squareup.javapoet.CodeBlock; import com.squareup.javapoet.MethodSpec; import io.github.zskamljic.restahead.client.requests.MultiPartRequest; import io.github.zskamljic.restahead.client.requests.Request; import io.github.zskamljic.restahead.client.requests.Verb; +import io.github.zskamljic.restahead.client.requests.parts.MultiPart; +import io.github.zskamljic.restahead.conversion.MapFormConverter; import io.github.zskamljic.restahead.encoding.BodyEncoding; import io.github.zskamljic.restahead.encoding.ConvertBodyEncoding; import io.github.zskamljic.restahead.encoding.FormBodyEncoding; import io.github.zskamljic.restahead.encoding.MultiPartBodyEncoding; +import io.github.zskamljic.restahead.encoding.MultiPartParameter; +import io.github.zskamljic.restahead.encoding.generation.ClassGenerationStrategy; +import io.github.zskamljic.restahead.encoding.generation.FormConversionStrategy; +import io.github.zskamljic.restahead.encoding.generation.MapConversionStrategy; +import io.github.zskamljic.restahead.encoding.generation.RecordGenerationStrategy; import io.github.zskamljic.restahead.modeling.declaration.CallDeclaration; import io.github.zskamljic.restahead.modeling.declaration.ParameterDeclaration; import io.github.zskamljic.restahead.modeling.declaration.RequestParameterSpec; -import io.github.zskamljic.restahead.requests.request.RequestLine; -import io.github.zskamljic.restahead.requests.request.path.TemplatedPath; +import io.github.zskamljic.restahead.request.RequestLine; +import io.github.zskamljic.restahead.request.path.TemplatedPath; import javax.lang.model.element.Modifier; import javax.lang.model.type.TypeMirror; import java.util.List; +import java.util.Optional; +import java.util.function.Predicate; import java.util.stream.Collectors; +import java.util.stream.Stream; /** * Used to generate methods annotated with HTTP annotations. @@ -35,9 +44,36 @@ public class MethodGenerator { * @return the generated methods */ public List generateMethods(List calls) { - return calls.stream() - .map(this::generateMethodBody) - .toList(); + var callMethods = calls.stream() + .map(this::generateMethodBody); + var convertCalls = calls.stream() + .map(CallDeclaration::parameters) + .map(ParameterDeclaration::body) + .flatMap(Optional::stream) + .filter(FormBodyEncoding.class::isInstance) + .map(FormBodyEncoding.class::cast) + .map(FormBodyEncoding::strategy) + .filter(Predicate.not(MapConversionStrategy.class::isInstance)) + .distinct() + .map(this::generateConverterBody); + + return Stream.concat(callMethods, convertCalls).toList(); + } + + /** + * Generate the method that will handle type conversion for form parameter. + * + * @param strategy the strategy to use + * @return the generated method + */ + private MethodSpec generateConverterBody(FormConversionStrategy strategy) { + if (strategy instanceof ClassGenerationStrategy classStrategy) { + return classStrategy.generate(); + } else if (strategy instanceof RecordGenerationStrategy recordStrategy) { + return recordStrategy.generate(); + } else { + throw new IllegalStateException(strategy.getClass() + " can does not generate code."); + } } /** @@ -76,7 +112,7 @@ private void addSendBodyStatement( if (encoding instanceof ConvertBodyEncoding convertEncoding) { addConvertEncoding(builder, declaredExceptions, convertEncoding.exceptions(), convertEncoding.parameterName()); } else if (encoding instanceof FormBodyEncoding formEncoding) { - addFormEncoding(builder, formEncoding.parameterName()); + addFormEncoding(builder, formEncoding); } else if (encoding instanceof MultiPartBodyEncoding multipart) { addMultipartEncoding(builder, declaredExceptions, multipart); } @@ -107,16 +143,23 @@ private void addConvertEncoding( /** * Adds form encoding using the generated type and adds a Content-Type header for this body. * - * @param builder the builder to add the code to - * @param parameterName the name of the parameter + * @param builder the builder to add the code to + * @param formEncoding the encoding to use for parameter */ - private void addFormEncoding(MethodSpec.Builder builder, String parameterName) { - var className = ClassName.get(MethodGenerator.class.getPackageName(), Variables.FORM_CONVERTER); - builder.addStatement("$L.addHeader(\"Content-Type\", \"application/x-www-form-urlencoded\")", Variables.REQUEST_BUILDER) - .addStatement( + private void addFormEncoding(MethodSpec.Builder builder, FormBodyEncoding formEncoding) { + builder.addStatement("$L.addHeader(\"Content-Type\", \"application/x-www-form-urlencoded\")", Variables.REQUEST_BUILDER); + var strategy = formEncoding.strategy(); + if (strategy instanceof MapConversionStrategy) { + builder.addStatement( "$L.setBody($T.$L($L))", - Variables.REQUEST_BUILDER, className, Variables.FORM_ENCODE, parameterName + Variables.REQUEST_BUILDER, MapFormConverter.class, Variables.FORM_ENCODE, formEncoding.parameterName() ); + } else { + builder.addStatement( + "$L.setBody($L($L))", + Variables.REQUEST_BUILDER, Variables.FORM_ENCODE, formEncoding.parameterName() + ); + } } /** @@ -139,7 +182,7 @@ private void addMultipartEncoding( builder.addCode("$T.builder()\n", MultiPartRequest.class); for (var part : multipart.parts()) { part.type().ifPresentOrElse( - type -> builder.addCode("\t.addPart(new $T($S, $L))\n", type, part.httpName(), part.name()), + type -> addMultipartStatement(builder, type, part), () -> builder.addCode("\t.addPart($L)\n", part.name()) ); } @@ -148,6 +191,24 @@ private void addMultipartEncoding( ); } + /** + * Add a multipart statement, choosing the code to add based on extra parameters + * + * @param builder the builder to add the code to + * @param type the type to use + * @param part the part from which to get the data + */ + private void addMultipartStatement( + MethodSpec.Builder builder, + Class type, + MultiPartParameter part + ) { + part.extraParameters().ifPresentOrElse( + extra -> builder.addCode("\t.addPart(new $T($S, $L, $L))\n", type, part.httpName(), part.name(), extra), + () -> builder.addCode("\t.addPart(new $T($S, $L))\n", type, part.httpName(), part.name()) + ); + } + /** * Add request initialization lines. * @@ -213,7 +274,7 @@ private void addTemplatedPath( List paths ) { var pathToVariable = paths.stream() - .collect(Collectors.toMap(RequestParameterSpec::httpName, RequestParameterSpec::httpName)); + .collect(Collectors.toMap(RequestParameterSpec::httpName, RequestParameterSpec::codeName)); var replaceBlocks = CodeBlock.builder() .add(".setPath($S", path); diff --git a/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/generation/Variables.java b/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/generation/Variables.java index 9fed6d1..02b2434 100644 --- a/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/generation/Variables.java +++ b/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/generation/Variables.java @@ -4,7 +4,6 @@ * The names of the variables used in generated code, collected to prevent duplication and declaration in multiple places. */ public final class Variables { - private Variables() { } @@ -14,10 +13,10 @@ private Variables() { public static final String CONVERTED_NAME = "convertedResponse"; public static final String CONVERTER = "converter"; public static final String DESERIALIZED = "deserializedResponse"; - public static final String FORM_CONVERTER = "FormConverter"; public static final String FORM_ENCODE = "formEncode"; public static final String HEADER_ITEM = "headerItem"; public static final String QUERY_ITEM = "queryItem"; public static final String REQUEST_BUILDER = "httpRequestBuilder"; public static final String RESPONSE = "response"; + public static final String VALUE = "value"; } diff --git a/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/modeling/MethodModeler.java b/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/modeling/MethodModeler.java index 245062a..043619d 100644 --- a/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/modeling/MethodModeler.java +++ b/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/modeling/MethodModeler.java @@ -4,9 +4,9 @@ import io.github.zskamljic.restahead.modeling.declaration.CallDeclaration; import io.github.zskamljic.restahead.modeling.declaration.RequestParameterSpec; import io.github.zskamljic.restahead.modeling.validation.PathValidator; -import io.github.zskamljic.restahead.requests.VerbMapping; -import io.github.zskamljic.restahead.requests.request.RequestLine; -import io.github.zskamljic.restahead.requests.request.path.TemplatedPath; +import io.github.zskamljic.restahead.polyglot.Dialects; +import io.github.zskamljic.restahead.request.RequestLine; +import io.github.zskamljic.restahead.request.path.TemplatedPath; import javax.annotation.processing.Messager; import javax.lang.model.element.ExecutableElement; @@ -28,11 +28,13 @@ public class MethodModeler { private final ParameterModeler parameterModeler; private final PathValidator pathValidator; private final ReturnTypeModeler returnTypeModeler; + private final Dialects dialects; - public MethodModeler(Messager messager, Elements elements, Types types) { + public MethodModeler(Messager messager, Elements elements, Types types, Dialects dialects) { this.messager = messager; + this.dialects = dialects; pathValidator = new PathValidator(messager, elements, types); - this.parameterModeler = new ParameterModeler(messager, elements, types, pathValidator); + this.parameterModeler = new ParameterModeler(messager, elements, dialects, types, pathValidator); returnTypeModeler = new ReturnTypeModeler(messager, elements, types); } @@ -47,7 +49,7 @@ public Optional getCallDeclaration( ExecutableElement function, List adapters ) { - var presentAnnotations = VerbMapping.ANNOTATION_VERBS.stream() + var presentAnnotations = dialects.verbAnnotations() .map(function::getAnnotation) .filter(Objects::nonNull) .toList(); @@ -58,7 +60,7 @@ public Optional getCallDeclaration( } var annotation = presentAnnotations.get(0); - var requestLine = VerbMapping.annotationToVerb(annotation); + var requestLine = dialects.basicRequestLine(annotation); var parameters = parameterModeler.getMethodParameters(function, requestLine.allowsBody()); var updatedLine = pathValidator.validatePathAndExtractQuery(function, requestLine, parameters); diff --git a/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/modeling/ParameterModeler.java b/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/modeling/ParameterModeler.java index a5f0725..bf9cdec 100644 --- a/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/modeling/ParameterModeler.java +++ b/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/modeling/ParameterModeler.java @@ -1,11 +1,6 @@ package io.github.zskamljic.restahead.modeling; import io.github.zskamljic.restahead.annotations.form.FormUrlEncoded; -import io.github.zskamljic.restahead.annotations.form.Part; -import io.github.zskamljic.restahead.annotations.request.Body; -import io.github.zskamljic.restahead.annotations.request.Header; -import io.github.zskamljic.restahead.annotations.request.Path; -import io.github.zskamljic.restahead.annotations.request.Query; import io.github.zskamljic.restahead.client.requests.parts.FieldPart; import io.github.zskamljic.restahead.client.requests.parts.FilePart; import io.github.zskamljic.restahead.encoding.BodyEncoding; @@ -13,13 +8,14 @@ import io.github.zskamljic.restahead.encoding.FormBodyEncoding; import io.github.zskamljic.restahead.encoding.MultiPartBodyEncoding; import io.github.zskamljic.restahead.encoding.MultiPartParameter; -import io.github.zskamljic.restahead.encoding.generation.GenerationStrategy; +import io.github.zskamljic.restahead.encoding.generation.FormConversionStrategy; import io.github.zskamljic.restahead.modeling.declaration.BodyParameter; import io.github.zskamljic.restahead.modeling.declaration.ParameterDeclaration; import io.github.zskamljic.restahead.modeling.declaration.RequestParameterSpec; import io.github.zskamljic.restahead.modeling.validation.HeaderValidator; import io.github.zskamljic.restahead.modeling.validation.PathValidator; import io.github.zskamljic.restahead.modeling.validation.QueryValidator; +import io.github.zskamljic.restahead.polyglot.Dialects; import javax.annotation.processing.Messager; import javax.lang.model.element.ExecutableElement; @@ -40,13 +36,6 @@ * Used to extract parameter info from the declaration. */ public class ParameterModeler { - private static final List> REQUEST_ANNOTATIONS = List.of( - Header.class, Path.class, Query.class - ); - private static final List> BODY_ANNOTATIONS = List.of( - Body.class, FormUrlEncoded.class, Part.class - ); - private final Messager messager; private final PathValidator pathValidator; private final Elements elements; @@ -54,12 +43,20 @@ public class ParameterModeler { private final HeaderValidator headerValidator; private final QueryValidator queryValidator; private final TypeMirror ioException; - - public ParameterModeler(Messager messager, Elements elements, Types types, PathValidator pathValidator) { + private final Dialects dialects; + + public ParameterModeler( + Messager messager, + Elements elements, + Dialects dialects, + Types types, + PathValidator pathValidator + ) { this.messager = messager; - this.pathValidator = pathValidator; this.elements = elements; + this.dialects = dialects; this.types = types; + this.pathValidator = pathValidator; headerValidator = new HeaderValidator(messager, elements, types); queryValidator = new QueryValidator(messager, elements, types); ioException = elements.getTypeElement(IOException.class.getCanonicalName()) @@ -81,11 +78,11 @@ public ParameterDeclaration getMethodParameters(ExecutableElement function, bool var bodies = new ArrayList(); for (var parameter : parameters) { - var requestAnnotations = REQUEST_ANNOTATIONS.stream() + var requestAnnotations = dialects.requestAnnotations() .map(parameter::getAnnotation) .filter(Objects::nonNull) .toList(); - var bodyAnnotations = BODY_ANNOTATIONS.stream() + var bodyAnnotations = dialects.bodyAnnotations() .map(parameter::getAnnotation) .filter(Objects::nonNull) .toList(); @@ -133,7 +130,7 @@ private Optional createBodyDeclaration(ArrayList bo var parameter = body.parameter(); var parameterName = parameter.getSimpleName().toString(); if (body.type() == BodyParameter.Type.FORM) { - var strategy = GenerationStrategy.select(messager, elements, types, parameter.asType()); + var strategy = FormConversionStrategy.select(messager, elements, types, parameter.asType()); if (strategy.isEmpty()) { messager.printMessage(Diagnostic.Kind.ERROR, "Form encoding for type " + parameter.asType() + " is not supported.", parameter); return Optional.empty(); @@ -151,13 +148,20 @@ private Optional createBodyDeclaration(ArrayList bo var type = body.parameter().asType(); if (typeValidator.isFileType(type)) { exceptions.addAll(typeValidator.getPossibleException(type)); - partMap.add(new MultiPartParameter(body.httpName(), body.name(), Optional.of(FilePart.class))); + partMap.add(new MultiPartParameter(body.httpName(), body.name(), Optional.of(FilePart.class), Optional.empty())); } else if (!typeValidator.isUnsupportedType(type)) { - partMap.add(new MultiPartParameter(body.httpName(), body.name(), Optional.of(FieldPart.class))); + partMap.add(new MultiPartParameter(body.httpName(), body.name(), Optional.of(FieldPart.class), Optional.empty())); } else if (typeValidator.isDirectMultipartType(type)) { - partMap.add(new MultiPartParameter(body.httpName(), body.name(), Optional.empty())); + partMap.add(new MultiPartParameter(body.httpName(), body.name(), Optional.empty(), Optional.empty())); } else { - messager.printMessage(Diagnostic.Kind.ERROR, "Type is not supported for multipart body.", body.parameter()); + dialects.createBodyPart(elements, types, body, type) + .ifPresentOrElse( + paramWithException -> { + partMap.add(paramWithException.parameter()); + exceptions.addAll(paramWithException.exceptions()); + }, + () -> messager.printMessage(Diagnostic.Kind.ERROR, "Type is not supported for multipart body.", body.parameter()) + ); } } return Optional.of(new MultiPartBodyEncoding(partMap, exceptions.stream().toList())); @@ -185,12 +189,16 @@ private void handleRequestAnnotations( } var annotation = requestAnnotations.get(0); - if (annotation instanceof Header header) { - headerValidator.getHeaderSpec(header.value(), parameter).ifPresent(headers::add); - } else if (annotation instanceof Query query) { - queryValidator.getQuerySpec(query.value(), parameter).ifPresent(queries::add); - } else if (annotation instanceof Path path) { - pathValidator.getPathSpec(path.value(), parameter).ifPresent(paths::add); + var requestParameter = dialects.extractRequestAnnotation(annotation); + if (requestParameter.isEmpty()) { + messager.printMessage(Diagnostic.Kind.ERROR, "No dialect could parse this parameter.", parameter); + return; + } + var parameterValue = requestParameter.get(); + switch (parameterValue.type()) { + case HEADER -> headerValidator.getHeaderSpec(parameterValue.value(), parameter).ifPresent(headers::add); + case QUERY -> queryValidator.getQuerySpec(parameterValue.value(), parameter).ifPresent(queries::add); + case PATH -> pathValidator.getPathSpec(parameterValue.value(), parameter).ifPresent(paths::add); } } @@ -208,10 +216,7 @@ private void handleBodyAnnotations( ) { var hasFormUrlEncoded = bodyAnnotations.stream() .anyMatch(FormUrlEncoded.class::isInstance); - var part = bodyAnnotations.stream() - .filter(Part.class::isInstance) - .map(Part.class::cast) - .findFirst(); + var part = dialects.extractParts(bodyAnnotations); if (hasFormUrlEncoded && part.isPresent()) { messager.printMessage(Diagnostic.Kind.ERROR, "Request can't be both multipart and form encoded", parameter); return; @@ -219,8 +224,8 @@ private void handleBodyAnnotations( var parameterName = parameter.getSimpleName().toString(); if (part.isPresent()) { - var partAnnotation = part.get(); - var value = partAnnotation.value(); + var partData = part.get(); + var value = partData.value(); if (value.isBlank()) { value = parameterName; } diff --git a/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/modeling/ServiceModeler.java b/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/modeling/ServiceModeler.java index ea0fcb8..140ea74 100644 --- a/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/modeling/ServiceModeler.java +++ b/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/modeling/ServiceModeler.java @@ -3,7 +3,7 @@ import io.github.zskamljic.restahead.modeling.declaration.AdapterClassDeclaration; import io.github.zskamljic.restahead.modeling.declaration.CallDeclaration; import io.github.zskamljic.restahead.modeling.declaration.ServiceDeclaration; -import io.github.zskamljic.restahead.requests.VerbMapping; +import io.github.zskamljic.restahead.polyglot.Dialects; import javax.annotation.processing.Messager; import javax.annotation.processing.RoundEnvironment; @@ -30,11 +30,13 @@ public class ServiceModeler { private final Messager messager; private final Elements elements; private final MethodModeler methodModeler; + private final Dialects dialects; - public ServiceModeler(Messager messager, Elements elements, Types types) { + public ServiceModeler(Messager messager, Elements elements, Types types, Dialects dialects) { this.messager = messager; this.elements = elements; - methodModeler = new MethodModeler(messager, elements, types); + this.dialects = dialects; + methodModeler = new MethodModeler(messager, elements, types, dialects); } /** @@ -80,7 +82,7 @@ private boolean hasInvalidDeclarations(Map> .map(ExecutableElement.class::cast) .toList(); for (var function : functions) { - var annotation = VerbMapping.ANNOTATION_VERBS.stream() + var annotation = dialects.verbAnnotations() .map(function::getAnnotation) .filter(Objects::nonNull) .toList(); @@ -109,9 +111,19 @@ private Optional createServiceDeclaration( List functions, List adapters ) { + var originalFunctions = typeElement.getEnclosedElements() + .stream() + .filter(ExecutableElement.class::isInstance) + .map(ExecutableElement.class::cast) + .toList(); var calls = functions.stream() .map(function -> methodModeler.getCallDeclaration(function, adapters)) .flatMap(Optional::stream) + .sorted((o1, o2) -> { + var f1 = o1.function(); + var f2 = o2.function(); + return originalFunctions.indexOf(f1) - originalFunctions.indexOf(f2); + }) .toList(); // There were errors in creation of the service declaration @@ -150,22 +162,20 @@ private void findDeclaringElements( continue; } - var modifiers = executableElement.getModifiers(); - if (!modifiers.contains(Modifier.ABSTRACT)) { - messager.printMessage(Diagnostic.Kind.ERROR, "Only abstract methods can be generated.", element); - continue; - } - var parent = executableElement.getEnclosingElement(); if (!(parent instanceof TypeElement declaringType)) { messager.printMessage(Diagnostic.Kind.ERROR, "Only methods in classes can be generated", element); continue; } - if (!(declaringType.getSuperclass() instanceof NoType)) { - messager.printMessage(Diagnostic.Kind.ERROR, "Only interfaces support code generation at this time", element); + var modifiers = executableElement.getModifiers(); + var isAbstract = modifiers.contains(Modifier.ABSTRACT); + var isInterface = declaringType.getSuperclass() instanceof NoType; + if (!isAbstract && isInterface) { + messager.printMessage(Diagnostic.Kind.ERROR, "Default methods in interfaces are not supported.", element); continue; } + if (!isAbstract || !isInterface) continue; declaringElements.compute(declaringType, (typeElement, executableElements) -> { if (executableElements == null) { diff --git a/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/modeling/declaration/CallDeclaration.java b/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/modeling/declaration/CallDeclaration.java index dcc742f..a820fa8 100644 --- a/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/modeling/declaration/CallDeclaration.java +++ b/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/modeling/declaration/CallDeclaration.java @@ -1,7 +1,7 @@ package io.github.zskamljic.restahead.modeling.declaration; import io.github.zskamljic.restahead.encoding.FormBodyEncoding; -import io.github.zskamljic.restahead.requests.request.RequestLine; +import io.github.zskamljic.restahead.request.RequestLine; import javax.lang.model.element.ExecutableElement; import javax.lang.model.type.TypeMirror; diff --git a/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/modeling/declaration/ParameterDeclaration.java b/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/modeling/declaration/ParameterDeclaration.java index bd0731e..05e15c9 100644 --- a/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/modeling/declaration/ParameterDeclaration.java +++ b/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/modeling/declaration/ParameterDeclaration.java @@ -1,7 +1,7 @@ package io.github.zskamljic.restahead.modeling.declaration; import io.github.zskamljic.restahead.encoding.BodyEncoding; -import io.github.zskamljic.restahead.requests.request.PresetQuery; +import io.github.zskamljic.restahead.request.PresetQuery; import java.util.ArrayList; import java.util.List; diff --git a/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/modeling/parameters/ParameterWithExceptions.java b/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/modeling/parameters/ParameterWithExceptions.java new file mode 100644 index 0000000..8be4673 --- /dev/null +++ b/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/modeling/parameters/ParameterWithExceptions.java @@ -0,0 +1,18 @@ +package io.github.zskamljic.restahead.modeling.parameters; + +import io.github.zskamljic.restahead.encoding.MultiPartParameter; + +import javax.lang.model.type.TypeMirror; +import java.util.Set; + +/** + * Represents a parameter with exceptions that can be thrown when it's used + * + * @param parameter the parameter that is added + * @param exceptions the exceptions that can be thrown + */ +public record ParameterWithExceptions( + MultiPartParameter parameter, + Set exceptions +) { +} diff --git a/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/modeling/parameters/PartData.java b/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/modeling/parameters/PartData.java new file mode 100644 index 0000000..9ba1c84 --- /dev/null +++ b/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/modeling/parameters/PartData.java @@ -0,0 +1,7 @@ +package io.github.zskamljic.restahead.modeling.parameters; + +/** + * Contains name of the part. + */ +public record PartData(String value) { +} diff --git a/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/modeling/parameters/RequestParameter.java b/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/modeling/parameters/RequestParameter.java new file mode 100644 index 0000000..456d8ea --- /dev/null +++ b/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/modeling/parameters/RequestParameter.java @@ -0,0 +1,7 @@ +package io.github.zskamljic.restahead.modeling.parameters; + +public record RequestParameter(Type type, String value) { + public enum Type { + HEADER, QUERY, PATH + } +} diff --git a/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/modeling/validation/CommonParameterValidator.java b/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/modeling/validation/CommonParameterValidator.java index c03c02a..fefb38c 100644 --- a/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/modeling/validation/CommonParameterValidator.java +++ b/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/modeling/validation/CommonParameterValidator.java @@ -35,7 +35,7 @@ protected CommonParameterValidator(Messager messager, Elements elements, Types t * * @param parameter the parameter to fetch info from * @param value the value used for HTTP name - * @return empty for invalid setup, non empty for valid config + * @return empty for invalid setup, non-empty for valid config */ protected Optional extractSpec(VariableElement parameter, String value) { var type = isInvalidType(parameter.asType()); diff --git a/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/modeling/validation/PathValidator.java b/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/modeling/validation/PathValidator.java index cf7f925..cac6cfb 100644 --- a/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/modeling/validation/PathValidator.java +++ b/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/modeling/validation/PathValidator.java @@ -2,11 +2,11 @@ import io.github.zskamljic.restahead.modeling.declaration.ParameterDeclaration; import io.github.zskamljic.restahead.modeling.declaration.RequestParameterSpec; -import io.github.zskamljic.restahead.requests.request.BasicRequestLine; -import io.github.zskamljic.restahead.requests.request.PresetQuery; -import io.github.zskamljic.restahead.requests.request.RequestLine; -import io.github.zskamljic.restahead.requests.request.path.RequestPath; -import io.github.zskamljic.restahead.requests.request.path.StringPath; +import io.github.zskamljic.restahead.request.BasicRequestLine; +import io.github.zskamljic.restahead.request.PresetQuery; +import io.github.zskamljic.restahead.request.RequestLine; +import io.github.zskamljic.restahead.request.path.RequestPath; +import io.github.zskamljic.restahead.request.path.StringPath; import javax.annotation.processing.Messager; import javax.lang.model.element.ExecutableElement; diff --git a/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/polyglot/Dialect.java b/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/polyglot/Dialect.java new file mode 100644 index 0000000..4980bcd --- /dev/null +++ b/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/polyglot/Dialect.java @@ -0,0 +1,93 @@ +package io.github.zskamljic.restahead.polyglot; + +import io.github.zskamljic.restahead.modeling.declaration.BodyParameter; +import io.github.zskamljic.restahead.modeling.parameters.ParameterWithExceptions; +import io.github.zskamljic.restahead.modeling.parameters.PartData; +import io.github.zskamljic.restahead.modeling.parameters.RequestParameter; +import io.github.zskamljic.restahead.request.BasicRequestLine; + +import javax.lang.model.type.TypeMirror; +import javax.lang.model.util.Elements; +import javax.lang.model.util.Types; +import java.lang.annotation.Annotation; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +/** + * Represents a dialect to use with RestAhead. If META-INF/services/io.github.zskamljic.restahead.polyglot.Dialect is + * discovered with a valid subclass of Dialect then the processor will additionally use that dialect to generate the + * source for all valid services. + */ +public interface Dialect { + /** + * Return a list of all the annotations supported by this class. + * + * @return the full list of annotations. + */ + default List> allAnnotations() { + var annotations = new ArrayList<>(requestAnnotations()); + annotations.addAll(bodyAnnotations()); + annotations.addAll(verbAnnotations()); + return annotations; + } + + /** + * Return a list of request annotations, such as Query, Header, Path etc. + * + * @return the list of request annotations. + */ + List> requestAnnotations(); + + /** + * Return a list of body annotations, such as Body, FormName, Part etc. + * + * @return the list of body annotations. + */ + List> bodyAnnotations(); + + /** + * Return a list of verb annotations, such as Delete, Get, Post etc. + * + * @return the verb annotations. + */ + List> verbAnnotations(); + + /** + * Attempt to extract a {@link BasicRequestLine} from the annotation. Can be called with annotations from other + * dialects. If no data can be extracted (unknown annotation, invalid data etc.) Optional.empty() can be returned. + * + * @param annotation the annotation to get the request line from + * @return request line if annotation is valid and recognized, empty otherwise + */ + Optional getRequestLine(Annotation annotation); + + /** + * Attempts to extract a {@link RequestParameter} from the annotation. Can be called with annotations from other + * dialects. If no data can be extracted (unknown annotation, invalid data etc.) Optional.empty() can be returned. + * + * @param annotation the annotation to get the parameter from + * @return the parameter if parsed, empty otherwise + */ + Optional extractRequestAnnotation(Annotation annotation); + + /** + * Attempt to extract a {@link PartData} from the annotation. Can be called with annotations from other + * dialects. If no data can be extracted (unknown annotation, invalid data etc.) Optional.empty() can be returned. + * + * @param bodyAnnotations body annotations present on the parameter + * @return the extracted PartData if applicable + */ + Optional extractPart(List bodyAnnotations); + + /** + * Create a new body part if this dialect is familiar with the type. + * + * @param elements the elements utility to obtain references from + * @param types the types utility to help with type decision + * @param body the body information parsed from the parameter + * @param type the type of the parameter + * @return full parameter info or empty + */ + Optional createBodyPart(Elements elements, Types types, BodyParameter body, TypeMirror type); +} diff --git a/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/polyglot/Dialects.java b/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/polyglot/Dialects.java new file mode 100644 index 0000000..a71d55a --- /dev/null +++ b/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/polyglot/Dialects.java @@ -0,0 +1,170 @@ +package io.github.zskamljic.restahead.polyglot; + +import io.github.zskamljic.restahead.annotations.Adapter; +import io.github.zskamljic.restahead.modeling.declaration.BodyParameter; +import io.github.zskamljic.restahead.modeling.parameters.ParameterWithExceptions; +import io.github.zskamljic.restahead.modeling.parameters.PartData; +import io.github.zskamljic.restahead.modeling.parameters.RequestParameter; +import io.github.zskamljic.restahead.processor.RequestsProcessor; +import io.github.zskamljic.restahead.request.BasicRequestLine; + +import javax.annotation.processing.Messager; +import javax.lang.model.element.TypeElement; +import javax.lang.model.type.TypeMirror; +import javax.lang.model.util.Elements; +import javax.lang.model.util.Types; +import javax.tools.Diagnostic; +import java.lang.annotation.Annotation; +import java.util.Collection; +import java.util.List; +import java.util.Optional; +import java.util.ServiceLoader; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * Utility class that handles all dialects that are discovered by {@link ServiceLoader}. + */ +public class Dialects { + private final List availableDialects = ServiceLoader.load(Dialect.class, RequestsProcessor.class.getClassLoader()) + .stream() + .map(ServiceLoader.Provider::get) + .toList(); + + public Dialects(Messager messager) { + messager.printMessage(Diagnostic.Kind.NOTE, "Detected dialects: " + createNameString()); + } + + /** + * Returns all verb annotations from discovered dialects. + * + * @return a stream of annotation classes + */ + public Stream> verbAnnotations() { + return availableDialects.stream() + .map(Dialect::verbAnnotations) + .flatMap(Collection::stream); + } + + /** + * Returns all request annotations from discovered dialects. + * + * @return a stream of annotation classes + */ + public Stream> requestAnnotations() { + return availableDialects.stream() + .map(Dialect::requestAnnotations) + .flatMap(Collection::stream); + } + + /** + * Returns all body annotations from discovered dialects. + * + * @return a stream of annotation classes + */ + public Stream> bodyAnnotations() { + return availableDialects.stream() + .map(Dialect::bodyAnnotations) + .flatMap(Collection::stream); + } + + /** + * Attempts to create a basic request line for given annotation. + * + * @param annotation the annotation with required data + * @return the request line based on data + * @throws IllegalArgumentException if no valid {@link BasicRequestLine} could be created + */ + public BasicRequestLine basicRequestLine(Annotation annotation) { + return availableDialects.stream() + .map(dialect -> dialect.getRequestLine(annotation)) + .flatMap(Optional::stream) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException("Annotation was not a valid verb: " + annotation)); + } + + /** + * Checks if the element provided is a verb annotation. + * + * @param typeElement the element to check + * @return true if the element is a verb, false otherwise + */ + public boolean isVerbAnnotation(TypeElement typeElement) { + var name = typeElement.getSimpleName().toString(); + return availableDialects.stream() + .map(Dialect::verbAnnotations) + .flatMap(Collection::stream) + .map(Class::getSimpleName) + .anyMatch(name::equals); + } + + /** + * Get a set of all supported annotations in string form. + * + * @return the full set of annotations + */ + public Set supportedAnnotationTypes() { + return Stream.concat( + availableDialects.stream() + .map(Dialect::allAnnotations) + .flatMap(List::stream), + Stream.of(Adapter.class) + ) + .map(Class::getCanonicalName) + .collect(Collectors.toSet()); + } + + /** + * Formats all the dialects by name and returns a comma separated list. + * + * @return the comma separated list of Dialect names + */ + private String createNameString() { + return availableDialects.stream() + .map(dialect -> dialect.getClass().getSimpleName()) + .collect(Collectors.joining(", ")); + } + + /** + * Get request annotation from annotation (GET, POST etc.) + * + * @param annotation the annotation + * @return request or empty + */ + public Optional extractRequestAnnotation(Annotation annotation) { + return availableDialects.stream() + .map(dialect -> dialect.extractRequestAnnotation(annotation)) + .flatMap(Optional::stream) + .findFirst(); + } + + /** + * Extract parts of body from annotation + * + * @param bodyAnnotations the annotations + * @return part info or empty + */ + public Optional extractParts(List bodyAnnotations) { + return availableDialects.stream() + .map(dialect -> dialect.extractPart(bodyAnnotations)) + .flatMap(Optional::stream) + .findFirst(); + } + + /** + * Delegate body part creation to all detected dialect, returning first valid one + * + * @param elements the elements to get type info from + * @param types the types utility + * @param body the body to process + * @param type the type of the body + * @return first processed body or empty if no dialects return a valid value + */ + public Optional createBodyPart(Elements elements, Types types, BodyParameter body, TypeMirror type) { + return availableDialects.stream() + .map(dialect -> dialect.createBodyPart(elements, types, body, type)) + .flatMap(Optional::stream) + .findFirst(); + } +} diff --git a/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/polyglot/RestAheadDialect.java b/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/polyglot/RestAheadDialect.java new file mode 100644 index 0000000..c393378 --- /dev/null +++ b/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/polyglot/RestAheadDialect.java @@ -0,0 +1,116 @@ +package io.github.zskamljic.restahead.polyglot; + +import io.github.zskamljic.restahead.annotations.form.FormName; +import io.github.zskamljic.restahead.annotations.form.FormUrlEncoded; +import io.github.zskamljic.restahead.annotations.form.Part; +import io.github.zskamljic.restahead.annotations.request.Body; +import io.github.zskamljic.restahead.annotations.request.Header; +import io.github.zskamljic.restahead.annotations.request.Path; +import io.github.zskamljic.restahead.annotations.request.Query; +import io.github.zskamljic.restahead.annotations.verbs.Delete; +import io.github.zskamljic.restahead.annotations.verbs.Get; +import io.github.zskamljic.restahead.annotations.verbs.Patch; +import io.github.zskamljic.restahead.annotations.verbs.Post; +import io.github.zskamljic.restahead.annotations.verbs.Put; +import io.github.zskamljic.restahead.client.requests.Verb; +import io.github.zskamljic.restahead.modeling.declaration.BodyParameter; +import io.github.zskamljic.restahead.modeling.parameters.ParameterWithExceptions; +import io.github.zskamljic.restahead.modeling.parameters.PartData; +import io.github.zskamljic.restahead.modeling.parameters.RequestParameter; +import io.github.zskamljic.restahead.request.BasicRequestLine; + +import javax.lang.model.type.TypeMirror; +import javax.lang.model.util.Elements; +import javax.lang.model.util.Types; +import java.lang.annotation.Annotation; +import java.util.List; +import java.util.Optional; + +/** + * The default dialect of RestAhead. + */ +public class RestAheadDialect implements Dialect { + @Override + public List> requestAnnotations() { + return List.of( + Header.class, Path.class, Query.class + ); + } + + @Override + public List> bodyAnnotations() { + return List.of( + Body.class, FormName.class, FormUrlEncoded.class, Part.class + ); + } + + @Override + public List> verbAnnotations() { + return List.of( + Delete.class, Get.class, Patch.class, Post.class, Put.class + ); + } + + @Override + public Optional getRequestLine(Annotation annotation) { + Verb verb; + String path; + if (annotation instanceof Delete delete) { + verb = Verb.DELETE; + path = delete.value(); + } else if (annotation instanceof Get get) { + verb = Verb.GET; + path = get.value(); + } else if (annotation instanceof Patch patch) { + verb = Verb.PATCH; + path = patch.value(); + } else if (annotation instanceof Post post) { + verb = Verb.POST; + path = post.value(); + } else if (annotation instanceof Put put) { + verb = Verb.PUT; + path = put.value(); + } else { + return Optional.empty(); + } + return Optional.of(new BasicRequestLine(verb, path)); + } + + @Override + public Optional extractRequestAnnotation(Annotation annotation) { + RequestParameter.Type type; + String value; + if (annotation instanceof Header header) { + type = RequestParameter.Type.HEADER; + value = header.value(); + } else if (annotation instanceof Query query) { + type = RequestParameter.Type.QUERY; + value = query.value(); + } else if (annotation instanceof Path path) { + type = RequestParameter.Type.PATH; + value = path.value(); + } else { + return Optional.empty(); + } + return Optional.of(new RequestParameter(type, value)); + } + + @Override + public Optional extractPart(List bodyAnnotations) { + return bodyAnnotations.stream() + .filter(Part.class::isInstance) + .map(Part.class::cast) + .map(part -> new PartData(part.value())) + .findFirst(); + } + + @Override + public Optional createBodyPart( + Elements elements, + Types types, + BodyParameter body, + TypeMirror type + ) { + return Optional.empty(); + } +} diff --git a/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/processor/RequestsProcessor.java b/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/processor/RequestsProcessor.java index 0c93bc4..aeac998 100644 --- a/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/processor/RequestsProcessor.java +++ b/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/processor/RequestsProcessor.java @@ -1,18 +1,9 @@ package io.github.zskamljic.restahead.processor; -import io.github.zskamljic.restahead.annotations.Adapter; -import io.github.zskamljic.restahead.annotations.form.FormName; -import io.github.zskamljic.restahead.annotations.form.FormUrlEncoded; -import io.github.zskamljic.restahead.annotations.form.Part; -import io.github.zskamljic.restahead.annotations.request.Body; -import io.github.zskamljic.restahead.annotations.request.Header; -import io.github.zskamljic.restahead.annotations.request.Path; -import io.github.zskamljic.restahead.annotations.request.Query; -import io.github.zskamljic.restahead.generation.FormConverterGenerator; import io.github.zskamljic.restahead.generation.ServiceGenerator; import io.github.zskamljic.restahead.modeling.AdapterModeler; import io.github.zskamljic.restahead.modeling.ServiceModeler; -import io.github.zskamljic.restahead.requests.VerbMapping; +import io.github.zskamljic.restahead.polyglot.Dialects; import javax.annotation.processing.AbstractProcessor; import javax.annotation.processing.Messager; @@ -23,24 +14,18 @@ import javax.tools.Diagnostic; import java.io.PrintWriter; import java.io.StringWriter; -import java.util.List; import java.util.Set; import java.util.stream.Collectors; -import java.util.stream.Stream; /** * Processor entry point for HTTP annotations. */ public class RequestsProcessor extends AbstractProcessor { - private static final List> NON_VERB_ANNOTATIONS = List.of( - Adapter.class, Body.class, Header.class, Path.class, Query.class, FormName.class, FormUrlEncoded.class, Part.class - ); - - private FormConverterGenerator formConverterGenerator; - private ServiceModeler serviceModeler; - private ServiceGenerator serviceGenerator; - private AdapterModeler adapterModeler; private Messager messager; + private Dialects dialects; + private AdapterModeler adapterModeler; + private ServiceGenerator serviceGenerator; + private ServiceModeler serviceModeler; /** * Initialize the implementation, extracting required fields from {@link ProcessingEnvironment}. @@ -54,10 +39,10 @@ public synchronized void init(ProcessingEnvironment processingEnv) { var filer = processingEnv.getFiler(); var elements = processingEnv.getElementUtils(); var types = processingEnv.getTypeUtils(); - serviceModeler = new ServiceModeler(messager, elements, types); + dialects = new Dialects(messager); adapterModeler = new AdapterModeler(messager, elements, types); serviceGenerator = new ServiceGenerator(messager, filer); - formConverterGenerator = new FormConverterGenerator(messager, filer); + serviceModeler = new ServiceModeler(messager, elements, types, dialects); } /** @@ -73,7 +58,6 @@ public boolean process(Set annotations, RoundEnvironment var adapters = adapterModeler.findAdapters(roundEnv); var verbs = filterVerbAnnotations(annotations); var serviceDeclarations = serviceModeler.collectServices(verbs, roundEnv, adapters); - formConverterGenerator.generateFormEncoderIfNeeded(serviceDeclarations); serviceDeclarations.forEach(service -> serviceGenerator.generateService(service)); } catch (IllegalArgumentException e) { var stringWriter = new StringWriter(); @@ -91,11 +75,8 @@ public boolean process(Set annotations, RoundEnvironment * @return the filtered set */ private Set filterVerbAnnotations(Set annotations) { - var names = NON_VERB_ANNOTATIONS.stream() - .map(Class::getSimpleName) - .toList(); return annotations.stream() - .filter(type -> !names.contains(type.getSimpleName().toString())) + .filter(dialects::isVerbAnnotation) .collect(Collectors.toSet()); } @@ -116,11 +97,6 @@ public SourceVersion getSupportedSourceVersion() { */ @Override public Set getSupportedAnnotationTypes() { - return Stream.concat( - VerbMapping.ANNOTATION_VERBS.stream(), - NON_VERB_ANNOTATIONS.stream() - ) - .map(Class::getCanonicalName) - .collect(Collectors.toSet()); + return dialects.supportedAnnotationTypes(); } } diff --git a/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/requests/request/BasicRequestLine.java b/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/request/BasicRequestLine.java similarity index 85% rename from rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/requests/request/BasicRequestLine.java rename to rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/request/BasicRequestLine.java index 19ad909..44d92f6 100644 --- a/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/requests/request/BasicRequestLine.java +++ b/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/request/BasicRequestLine.java @@ -1,4 +1,4 @@ -package io.github.zskamljic.restahead.requests.request; +package io.github.zskamljic.restahead.request; import io.github.zskamljic.restahead.client.requests.Verb; diff --git a/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/requests/request/PresetQuery.java b/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/request/PresetQuery.java similarity index 76% rename from rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/requests/request/PresetQuery.java rename to rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/request/PresetQuery.java index b7e5684..bffe291 100644 --- a/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/requests/request/PresetQuery.java +++ b/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/request/PresetQuery.java @@ -1,4 +1,4 @@ -package io.github.zskamljic.restahead.requests.request; +package io.github.zskamljic.restahead.request; /** * Preset query information. diff --git a/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/requests/request/RequestLine.java b/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/request/RequestLine.java similarity index 64% rename from rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/requests/request/RequestLine.java rename to rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/request/RequestLine.java index 054c5f4..3600a6e 100644 --- a/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/requests/request/RequestLine.java +++ b/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/request/RequestLine.java @@ -1,7 +1,7 @@ -package io.github.zskamljic.restahead.requests.request; +package io.github.zskamljic.restahead.request; import io.github.zskamljic.restahead.client.requests.Verb; -import io.github.zskamljic.restahead.requests.request.path.RequestPath; +import io.github.zskamljic.restahead.request.path.RequestPath; /** * The processed request line. diff --git a/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/requests/request/RequestSpec.java b/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/request/RequestSpec.java similarity index 85% rename from rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/requests/request/RequestSpec.java rename to rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/request/RequestSpec.java index 93a2053..af26034 100644 --- a/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/requests/request/RequestSpec.java +++ b/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/request/RequestSpec.java @@ -1,4 +1,4 @@ -package io.github.zskamljic.restahead.requests.request; +package io.github.zskamljic.restahead.request; import io.github.zskamljic.restahead.modeling.declaration.ParameterDeclaration; diff --git a/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/requests/request/path/RequestPath.java b/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/request/path/RequestPath.java similarity index 95% rename from rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/requests/request/path/RequestPath.java rename to rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/request/path/RequestPath.java index 808fe8b..fad5e57 100644 --- a/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/requests/request/path/RequestPath.java +++ b/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/request/path/RequestPath.java @@ -1,4 +1,4 @@ -package io.github.zskamljic.restahead.requests.request.path; +package io.github.zskamljic.restahead.request.path; import java.net.URI; import java.net.URISyntaxException; diff --git a/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/requests/request/path/StringPath.java b/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/request/path/StringPath.java similarity index 89% rename from rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/requests/request/path/StringPath.java rename to rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/request/path/StringPath.java index e2bb7df..76b4488 100644 --- a/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/requests/request/path/StringPath.java +++ b/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/request/path/StringPath.java @@ -1,4 +1,4 @@ -package io.github.zskamljic.restahead.requests.request.path; +package io.github.zskamljic.restahead.request.path; import java.net.URI; import java.net.URISyntaxException; diff --git a/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/requests/request/path/TemplatedPath.java b/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/request/path/TemplatedPath.java similarity index 95% rename from rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/requests/request/path/TemplatedPath.java rename to rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/request/path/TemplatedPath.java index 6d86c0c..f7206c6 100644 --- a/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/requests/request/path/TemplatedPath.java +++ b/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/request/path/TemplatedPath.java @@ -1,4 +1,4 @@ -package io.github.zskamljic.restahead.requests.request.path; +package io.github.zskamljic.restahead.request.path; import java.net.URI; import java.net.URISyntaxException; diff --git a/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/requests/VerbMapping.java b/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/requests/VerbMapping.java deleted file mode 100644 index b913654..0000000 --- a/rest-ahead-processor/src/main/java/io/github/zskamljic/restahead/requests/VerbMapping.java +++ /dev/null @@ -1,59 +0,0 @@ -package io.github.zskamljic.restahead.requests; - -import io.github.zskamljic.restahead.annotations.verbs.Delete; -import io.github.zskamljic.restahead.annotations.verbs.Get; -import io.github.zskamljic.restahead.annotations.verbs.Patch; -import io.github.zskamljic.restahead.annotations.verbs.Post; -import io.github.zskamljic.restahead.annotations.verbs.Put; -import io.github.zskamljic.restahead.client.requests.Verb; -import io.github.zskamljic.restahead.requests.request.BasicRequestLine; -import io.github.zskamljic.restahead.requests.request.RequestSpec; - -import java.lang.annotation.Annotation; -import java.util.List; - -/** - * Utility to provide mappers for annotations, requests and specifications. - */ -public class VerbMapping { - public static final List> ANNOTATION_VERBS = List.of( - Delete.class, - Get.class, - Patch.class, - Post.class, - Put.class - ); - - private VerbMapping() { - } - - /** - * Extracts {@link RequestSpec} from given annotation. Only HTTP verb annotations are supported. - * - * @param annotation the annotation from which to extract the verb and path - * @return the request specification - */ - public static BasicRequestLine annotationToVerb(Annotation annotation) { - Verb verb; - String path; - if (annotation instanceof Delete delete) { - verb = Verb.DELETE; - path = delete.value(); - } else if (annotation instanceof Get get) { - verb = Verb.GET; - path = get.value(); - } else if (annotation instanceof Patch patch) { - verb = Verb.PATCH; - path = patch.value(); - } else if (annotation instanceof Post post) { - verb = Verb.POST; - path = post.value(); - } else if (annotation instanceof Put put) { - verb = Verb.PUT; - path = put.value(); - } else { - throw new IllegalArgumentException("Annotation was not a valid verb: " + annotation); - } - return new BasicRequestLine(verb, path); - } -} diff --git a/rest-ahead-processor/src/main/resources/META-INF/services/io.github.zskamljic.restahead.polyglot.Dialect b/rest-ahead-processor/src/main/resources/META-INF/services/io.github.zskamljic.restahead.polyglot.Dialect new file mode 100644 index 0000000..3ff23b1 --- /dev/null +++ b/rest-ahead-processor/src/main/resources/META-INF/services/io.github.zskamljic.restahead.polyglot.Dialect @@ -0,0 +1 @@ +io.github.zskamljic.restahead.polyglot.RestAheadDialect \ No newline at end of file diff --git a/rest-ahead-processor/src/test/java/io/github/zskamljic/restahead/generation/methods/PathValidatorTest.java b/rest-ahead-processor/src/test/java/io/github/zskamljic/restahead/generation/methods/PathValidatorTest.java index 2eeafa5..9a2568a 100644 --- a/rest-ahead-processor/src/test/java/io/github/zskamljic/restahead/generation/methods/PathValidatorTest.java +++ b/rest-ahead-processor/src/test/java/io/github/zskamljic/restahead/generation/methods/PathValidatorTest.java @@ -3,7 +3,7 @@ import io.github.zskamljic.restahead.client.requests.Verb; import io.github.zskamljic.restahead.modeling.declaration.ParameterDeclaration; import io.github.zskamljic.restahead.modeling.validation.PathValidator; -import io.github.zskamljic.restahead.requests.request.BasicRequestLine; +import io.github.zskamljic.restahead.request.BasicRequestLine; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.params.ParameterizedTest; diff --git a/rest-ahead-processor/src/test/java/io/github/zskamljic/restahead/requests/request/path/TemplatedPathTest.java b/rest-ahead-processor/src/test/java/io/github/zskamljic/restahead/request/path/TemplatedPathTest.java similarity index 95% rename from rest-ahead-processor/src/test/java/io/github/zskamljic/restahead/requests/request/path/TemplatedPathTest.java rename to rest-ahead-processor/src/test/java/io/github/zskamljic/restahead/request/path/TemplatedPathTest.java index 958b865..39c79ce 100644 --- a/rest-ahead-processor/src/test/java/io/github/zskamljic/restahead/requests/request/path/TemplatedPathTest.java +++ b/rest-ahead-processor/src/test/java/io/github/zskamljic/restahead/request/path/TemplatedPathTest.java @@ -1,4 +1,4 @@ -package io.github.zskamljic.restahead.requests.request.path; +package io.github.zskamljic.restahead.request.path; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; diff --git a/rest-ahead-spring-dialect/pom.xml b/rest-ahead-spring-dialect/pom.xml new file mode 100644 index 0000000..d7560c3 --- /dev/null +++ b/rest-ahead-spring-dialect/pom.xml @@ -0,0 +1,66 @@ + + + + RestAhead + io.github.zskamljic + 0.3.0-SNAPSHOT + + 4.0.0 + + rest-ahead-spring-dialect + + + 17 + 17 + + + + + io.github.zskamljic + rest-ahead-processor + 0.3.0-SNAPSHOT + + + org.springframework + spring-web + ${spring.version} + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${compiler.plugin.version} + + + default-compile + + -proc:none + + + + + + org.apache.maven.plugins + maven-surefire-plugin + ${surefire.version} + + + + + + + testing only + + [9,) + + + --add-opens jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED + + + + \ No newline at end of file diff --git a/rest-ahead-spring-dialect/src/main/java/io/github/zskamljic/restahead/spring/dialect/SpringDialect.java b/rest-ahead-spring-dialect/src/main/java/io/github/zskamljic/restahead/spring/dialect/SpringDialect.java new file mode 100644 index 0000000..49977b4 --- /dev/null +++ b/rest-ahead-spring-dialect/src/main/java/io/github/zskamljic/restahead/spring/dialect/SpringDialect.java @@ -0,0 +1,158 @@ +package io.github.zskamljic.restahead.spring.dialect; + +import io.github.zskamljic.restahead.client.requests.Verb; +import io.github.zskamljic.restahead.client.requests.parts.FilePart; +import io.github.zskamljic.restahead.encoding.MultiPartParameter; +import io.github.zskamljic.restahead.modeling.declaration.BodyParameter; +import io.github.zskamljic.restahead.modeling.parameters.ParameterWithExceptions; +import io.github.zskamljic.restahead.modeling.parameters.PartData; +import io.github.zskamljic.restahead.modeling.parameters.RequestParameter; +import io.github.zskamljic.restahead.polyglot.Dialect; +import io.github.zskamljic.restahead.request.BasicRequestLine; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PatchMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RequestPart; +import org.springframework.web.multipart.MultipartFile; + +import javax.lang.model.type.TypeMirror; +import javax.lang.model.util.Elements; +import javax.lang.model.util.Types; +import java.io.IOException; +import java.lang.annotation.Annotation; +import java.util.List; +import java.util.Optional; +import java.util.Set; + +/** + * Represents the spring dialect for RestAhead. + */ +public class SpringDialect implements Dialect { + @Override + public List> requestAnnotations() { + return List.of( + RequestHeader.class, PathVariable.class, RequestParam.class + ); + } + + @Override + public List> bodyAnnotations() { + return List.of( + RequestBody.class, RequestPart.class + ); + } + + @Override + public List> verbAnnotations() { + return List.of( + DeleteMapping.class, GetMapping.class, PatchMapping.class, PostMapping.class, PutMapping.class, RequestMapping.class + ); + } + + @Override + public Optional getRequestLine(Annotation annotation) { + Verb verb; + String path; + if (annotation instanceof DeleteMapping deleteMapping) { + verb = Verb.DELETE; + path = deleteMapping.value()[0]; + } else if (annotation instanceof GetMapping getMapping) { + verb = Verb.GET; + path = getMapping.value()[0]; + } else if (annotation instanceof PatchMapping patchMapping) { + verb = Verb.PATCH; + path = patchMapping.value()[0]; + } else if (annotation instanceof PostMapping postMapping) { + verb = Verb.POST; + path = postMapping.value()[0]; + } else if (annotation instanceof PutMapping putMapping) { + verb = Verb.PUT; + path = putMapping.value()[0]; + } else if (annotation instanceof RequestMapping requestMapping) { + var requestVerb = getVerb(requestMapping.method()); + if (requestVerb.isEmpty()) return Optional.empty(); + + verb = requestVerb.get(); + path = requestMapping.value()[0]; + } else { + return Optional.empty(); + } + return Optional.of(new BasicRequestLine(verb, path)); + } + + @Override + public Optional extractRequestAnnotation(Annotation annotation) { + RequestParameter.Type type; + String value; + if (annotation instanceof RequestHeader header) { + type = RequestParameter.Type.HEADER; + value = header.value(); + } else if (annotation instanceof RequestParam query) { + type = RequestParameter.Type.QUERY; + value = query.value(); + } else if (annotation instanceof PathVariable path) { + type = RequestParameter.Type.PATH; + value = path.value(); + } else { + return Optional.empty(); + } + return Optional.of(new RequestParameter(type, value)); + } + + @Override + public Optional extractPart(List bodyAnnotations) { + return bodyAnnotations.stream() + .filter(RequestPart.class::isInstance) + .map(RequestPart.class::cast) + .map(part -> new PartData(part.value())) + .findFirst(); + } + + @Override + public Optional createBodyPart( + Elements elements, + Types types, + BodyParameter body, + TypeMirror type + ) { + var multiPartType = elements.getTypeElement(MultipartFile.class.getName()).asType(); + if (!types.isSameType(type, multiPartType)) return Optional.empty(); + + var part = new MultiPartParameter( + body.httpName(), + body.name() + ".getOriginalFilename()", + Optional.of(FilePart.class), + Optional.of(body.name() + ".getInputStream()") + ); + var exceptions = elements.getTypeElement(IOException.class.getName()).asType(); + return Optional.of(new ParameterWithExceptions(part, Set.of(exceptions))); + } + + /** + * Used to map {@link RequestMethod} to {@link Verb}. If more than one is provided no verb is returned. + * + * @param methods the methods to check + * @return the verb if applicable, empty otherwise + */ + private Optional getVerb(RequestMethod[] methods) { + if (methods.length != 1) return Optional.empty(); + + var verb = switch (methods[0]) { + case DELETE -> Verb.DELETE; + case GET -> Verb.GET; + case PATCH -> Verb.PATCH; + case POST -> Verb.POST; + case PUT -> Verb.PUT; + default -> null; + }; + return Optional.ofNullable(verb); + } +} diff --git a/rest-ahead-spring-dialect/src/main/resources/META-INF/services/io.github.zskamljic.restahead.polyglot.Dialect b/rest-ahead-spring-dialect/src/main/resources/META-INF/services/io.github.zskamljic.restahead.polyglot.Dialect new file mode 100644 index 0000000..cd91d20 --- /dev/null +++ b/rest-ahead-spring-dialect/src/main/resources/META-INF/services/io.github.zskamljic.restahead.polyglot.Dialect @@ -0,0 +1 @@ +io.github.zskamljic.restahead.spring.dialect.SpringDialect \ No newline at end of file diff --git a/rest-ahead-spring/pom.xml b/rest-ahead-spring/pom.xml index c00edc7..c5fc250 100644 --- a/rest-ahead-spring/pom.xml +++ b/rest-ahead-spring/pom.xml @@ -20,7 +20,7 @@ org.springframework spring-context - 5.3.15 + ${spring.version} io.github.zskamljic diff --git a/rest-ahead-spring/src/main/java/io/github/zskamljic/restahead/spring/RestAheadRegistrar.java b/rest-ahead-spring/src/main/java/io/github/zskamljic/restahead/spring/RestAheadRegistrar.java index 0dbbc62..63c4db6 100644 --- a/rest-ahead-spring/src/main/java/io/github/zskamljic/restahead/spring/RestAheadRegistrar.java +++ b/rest-ahead-spring/src/main/java/io/github/zskamljic/restahead/spring/RestAheadRegistrar.java @@ -117,7 +117,7 @@ private T instantiateService(BeanDefinitionRegistry registry, Maprest-ahead-spring 0.3.0-SNAPSHOT + + io.github.zskamljic + rest-ahead-spring-dialect + 0.3.0-SNAPSHOT + + + + + com.google.truth + truth + ${truth.version} + test + + + com.google.testing.compile + compile-testing + ${testing.compile.version} + test + + + org.junit.jupiter + junit-jupiter-engine + ${junit.version} + test + + + org.junit.jupiter + junit-jupiter-params + ${junit.version} + test + + + org.mockito + mockito-core + ${mockito.version} + test + + + org.mockito + mockito-junit-jupiter + ${mockito.version} + test + + + org.apache.maven.plugins + maven-compiler-plugin + ${compiler.plugin.version} + org.apache.maven.plugins maven-surefire-plugin @@ -67,4 +115,16 @@ + + + + testing only + + [9,) + + + --add-opens jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED + + + \ No newline at end of file diff --git a/rest-ahead-processor/src/test/java/io/github/zskamljic/restahead/processor/CommonProcessorTest.java b/test-report-aggregator/src/test/java/io/github/zskamljic/restahead/processor/CommonProcessorTest.java similarity index 100% rename from rest-ahead-processor/src/test/java/io/github/zskamljic/restahead/processor/CommonProcessorTest.java rename to test-report-aggregator/src/test/java/io/github/zskamljic/restahead/processor/CommonProcessorTest.java diff --git a/rest-ahead-processor/src/test/java/io/github/zskamljic/restahead/processor/UnclaimedProcessor.java b/test-report-aggregator/src/test/java/io/github/zskamljic/restahead/processor/UnclaimedProcessor.java similarity index 100% rename from rest-ahead-processor/src/test/java/io/github/zskamljic/restahead/processor/UnclaimedProcessor.java rename to test-report-aggregator/src/test/java/io/github/zskamljic/restahead/processor/UnclaimedProcessor.java diff --git a/test-report-aggregator/src/test/java/io/github/zskamljic/restahead/processor/spring/SpringDialectTest.java b/test-report-aggregator/src/test/java/io/github/zskamljic/restahead/processor/spring/SpringDialectTest.java new file mode 100644 index 0000000..ba89d8a --- /dev/null +++ b/test-report-aggregator/src/test/java/io/github/zskamljic/restahead/processor/spring/SpringDialectTest.java @@ -0,0 +1,15 @@ +package io.github.zskamljic.restahead.processor.spring; + +import com.google.testing.compile.JavaFileObjects; +import io.github.zskamljic.restahead.processor.CommonProcessorTest; +import org.junit.jupiter.api.Test; + +class SpringDialectTest extends CommonProcessorTest { + @Test + void processorGeneratesForSpring() { + commonCompilationAssertion("spring/SpringService.java") + .compilesWithoutWarnings() + .and() + .generatesSources(JavaFileObjects.forResource("spring/SpringService$Impl.java")); + } +} diff --git a/rest-ahead-processor/src/test/java/io/github/zskamljic/restahead/processor/AdapterProcessorTest.java b/test-report-aggregator/src/test/java/io/github/zskamljic/restahead/processor/stock/AdapterProcessorTest.java similarity index 77% rename from rest-ahead-processor/src/test/java/io/github/zskamljic/restahead/processor/AdapterProcessorTest.java rename to test-report-aggregator/src/test/java/io/github/zskamljic/restahead/processor/stock/AdapterProcessorTest.java index 715fd10..281e364 100644 --- a/rest-ahead-processor/src/test/java/io/github/zskamljic/restahead/processor/AdapterProcessorTest.java +++ b/test-report-aggregator/src/test/java/io/github/zskamljic/restahead/processor/stock/AdapterProcessorTest.java @@ -1,6 +1,7 @@ -package io.github.zskamljic.restahead.processor; +package io.github.zskamljic.restahead.processor.stock; import com.google.testing.compile.JavaFileObjects; +import io.github.zskamljic.restahead.processor.CommonProcessorTest; import org.junit.jupiter.api.Test; class AdapterProcessorTest extends CommonProcessorTest { diff --git a/rest-ahead-processor/src/test/java/io/github/zskamljic/restahead/processor/BodyProcessorTest.java b/test-report-aggregator/src/test/java/io/github/zskamljic/restahead/processor/stock/BodyProcessorTest.java similarity index 85% rename from rest-ahead-processor/src/test/java/io/github/zskamljic/restahead/processor/BodyProcessorTest.java rename to test-report-aggregator/src/test/java/io/github/zskamljic/restahead/processor/stock/BodyProcessorTest.java index 5552652..9e3e6e9 100644 --- a/rest-ahead-processor/src/test/java/io/github/zskamljic/restahead/processor/BodyProcessorTest.java +++ b/test-report-aggregator/src/test/java/io/github/zskamljic/restahead/processor/stock/BodyProcessorTest.java @@ -1,6 +1,7 @@ -package io.github.zskamljic.restahead.processor; +package io.github.zskamljic.restahead.processor.stock; import com.google.testing.compile.JavaFileObjects; +import io.github.zskamljic.restahead.processor.CommonProcessorTest; import org.junit.jupiter.api.Test; class BodyProcessorTest extends CommonProcessorTest { @@ -39,10 +40,7 @@ void validRecordCompiles() { commonCompilationAssertion("parameters/FormOnRecord.java") .compilesWithoutWarnings() .and() - .generatesSources( - JavaFileObjects.forResource("parameters/FormOnRecord$Impl.java"), - JavaFileObjects.forResource("parameters/record/FormConverter.java") - ); + .generatesSources(JavaFileObjects.forResource("parameters/FormOnRecord$Impl.java")); } @Test @@ -57,10 +55,7 @@ void validClassCompiles() { commonCompilationAssertion("parameters/FormOnClass.java") .compilesWithoutWarnings() .and() - .generatesSources( - JavaFileObjects.forResource("parameters/FormOnClass$Impl.java"), - JavaFileObjects.forResource("parameters/class/FormConverter.java") - ); + .generatesSources(JavaFileObjects.forResource("parameters/FormOnClass$Impl.java")); } @Test diff --git a/rest-ahead-processor/src/test/java/io/github/zskamljic/restahead/processor/HeaderProcessorTest.java b/test-report-aggregator/src/test/java/io/github/zskamljic/restahead/processor/stock/HeaderProcessorTest.java similarity index 94% rename from rest-ahead-processor/src/test/java/io/github/zskamljic/restahead/processor/HeaderProcessorTest.java rename to test-report-aggregator/src/test/java/io/github/zskamljic/restahead/processor/stock/HeaderProcessorTest.java index 01ab220..8e29662 100644 --- a/rest-ahead-processor/src/test/java/io/github/zskamljic/restahead/processor/HeaderProcessorTest.java +++ b/test-report-aggregator/src/test/java/io/github/zskamljic/restahead/processor/stock/HeaderProcessorTest.java @@ -1,5 +1,6 @@ -package io.github.zskamljic.restahead.processor; +package io.github.zskamljic.restahead.processor.stock; +import io.github.zskamljic.restahead.processor.CommonProcessorTest; import org.junit.jupiter.api.Test; class HeaderProcessorTest extends CommonProcessorTest { diff --git a/rest-ahead-processor/src/test/java/io/github/zskamljic/restahead/processor/ParametersProcessorTest.java b/test-report-aggregator/src/test/java/io/github/zskamljic/restahead/processor/stock/ParametersProcessorTest.java similarity index 72% rename from rest-ahead-processor/src/test/java/io/github/zskamljic/restahead/processor/ParametersProcessorTest.java rename to test-report-aggregator/src/test/java/io/github/zskamljic/restahead/processor/stock/ParametersProcessorTest.java index e29240b..97addd7 100644 --- a/rest-ahead-processor/src/test/java/io/github/zskamljic/restahead/processor/ParametersProcessorTest.java +++ b/test-report-aggregator/src/test/java/io/github/zskamljic/restahead/processor/stock/ParametersProcessorTest.java @@ -1,5 +1,6 @@ -package io.github.zskamljic.restahead.processor; +package io.github.zskamljic.restahead.processor.stock; +import io.github.zskamljic.restahead.processor.CommonProcessorTest; import org.junit.jupiter.api.Test; class ParametersProcessorTest extends CommonProcessorTest { diff --git a/rest-ahead-processor/src/test/java/io/github/zskamljic/restahead/processor/PathProcessorTest.java b/test-report-aggregator/src/test/java/io/github/zskamljic/restahead/processor/stock/PathProcessorTest.java similarity index 92% rename from rest-ahead-processor/src/test/java/io/github/zskamljic/restahead/processor/PathProcessorTest.java rename to test-report-aggregator/src/test/java/io/github/zskamljic/restahead/processor/stock/PathProcessorTest.java index 840e3c0..858d3b9 100644 --- a/rest-ahead-processor/src/test/java/io/github/zskamljic/restahead/processor/PathProcessorTest.java +++ b/test-report-aggregator/src/test/java/io/github/zskamljic/restahead/processor/stock/PathProcessorTest.java @@ -1,6 +1,7 @@ -package io.github.zskamljic.restahead.processor; +package io.github.zskamljic.restahead.processor.stock; import com.google.testing.compile.JavaFileObjects; +import io.github.zskamljic.restahead.processor.CommonProcessorTest; import org.junit.jupiter.api.Test; class PathProcessorTest extends CommonProcessorTest { diff --git a/rest-ahead-processor/src/test/java/io/github/zskamljic/restahead/processor/QueryProcessorTest.java b/test-report-aggregator/src/test/java/io/github/zskamljic/restahead/processor/stock/QueryProcessorTest.java similarity index 90% rename from rest-ahead-processor/src/test/java/io/github/zskamljic/restahead/processor/QueryProcessorTest.java rename to test-report-aggregator/src/test/java/io/github/zskamljic/restahead/processor/stock/QueryProcessorTest.java index 403ec33..288567d 100644 --- a/rest-ahead-processor/src/test/java/io/github/zskamljic/restahead/processor/QueryProcessorTest.java +++ b/test-report-aggregator/src/test/java/io/github/zskamljic/restahead/processor/stock/QueryProcessorTest.java @@ -1,5 +1,6 @@ -package io.github.zskamljic.restahead.processor; +package io.github.zskamljic.restahead.processor.stock; +import io.github.zskamljic.restahead.processor.CommonProcessorTest; import org.junit.jupiter.api.Test; class QueryProcessorTest extends CommonProcessorTest { diff --git a/rest-ahead-processor/src/test/java/io/github/zskamljic/restahead/processor/RequestsProcessorTest.java b/test-report-aggregator/src/test/java/io/github/zskamljic/restahead/processor/stock/RequestsProcessorTest.java similarity index 79% rename from rest-ahead-processor/src/test/java/io/github/zskamljic/restahead/processor/RequestsProcessorTest.java rename to test-report-aggregator/src/test/java/io/github/zskamljic/restahead/processor/stock/RequestsProcessorTest.java index 6e04517..882eeb9 100644 --- a/rest-ahead-processor/src/test/java/io/github/zskamljic/restahead/processor/RequestsProcessorTest.java +++ b/test-report-aggregator/src/test/java/io/github/zskamljic/restahead/processor/stock/RequestsProcessorTest.java @@ -1,6 +1,7 @@ -package io.github.zskamljic.restahead.processor; +package io.github.zskamljic.restahead.processor.stock; import com.google.testing.compile.JavaFileObjects; +import io.github.zskamljic.restahead.processor.CommonProcessorTest; import org.junit.jupiter.api.Test; class RequestsProcessorTest extends CommonProcessorTest { @@ -11,24 +12,22 @@ void generateServiceSucceeds() { } @Test - void generateServiceFailsForAbstractClass() { + void generateServiceIgnoresAbstractClass() { commonCompilationAssertion("basic/MethodClass.java") - .failsToCompile() - .withErrorContaining("interfaces"); + .compilesWithoutWarnings(); } @Test void interfaceWithDefaultFailsToCompile() { commonCompilationAssertion("basic/MethodService.java") .failsToCompile() - .withErrorContaining("abstract"); + .withErrorContaining("Default methods in interfaces"); } @Test - void classWithMethodFailsToCompile() { + void classWithMethodIgnoresClass() { commonCompilationAssertion("basic/NormalClassMethod.java") - .failsToCompile() - .withErrorContaining("abstract"); + .compilesWithoutWarnings(); } @Test diff --git a/rest-ahead-processor/src/test/java/io/github/zskamljic/restahead/processor/ResponseProcessorTest.java b/test-report-aggregator/src/test/java/io/github/zskamljic/restahead/processor/stock/ResponseProcessorTest.java similarity index 93% rename from rest-ahead-processor/src/test/java/io/github/zskamljic/restahead/processor/ResponseProcessorTest.java rename to test-report-aggregator/src/test/java/io/github/zskamljic/restahead/processor/stock/ResponseProcessorTest.java index 3f41875..2dccb2e 100644 --- a/rest-ahead-processor/src/test/java/io/github/zskamljic/restahead/processor/ResponseProcessorTest.java +++ b/test-report-aggregator/src/test/java/io/github/zskamljic/restahead/processor/stock/ResponseProcessorTest.java @@ -1,6 +1,7 @@ -package io.github.zskamljic.restahead.processor; +package io.github.zskamljic.restahead.processor.stock; import com.google.testing.compile.JavaFileObjects; +import io.github.zskamljic.restahead.processor.CommonProcessorTest; import org.junit.jupiter.api.Test; class ResponseProcessorTest extends CommonProcessorTest { diff --git a/rest-ahead-processor/src/test/resources/ValidService.java b/test-report-aggregator/src/test/resources/ValidService.java similarity index 100% rename from rest-ahead-processor/src/test/resources/ValidService.java rename to test-report-aggregator/src/test/resources/ValidService.java diff --git a/rest-ahead-processor/src/test/resources/adapters/AdapterService$Impl.java b/test-report-aggregator/src/test/resources/adapters/AdapterService$Impl.java similarity index 100% rename from rest-ahead-processor/src/test/resources/adapters/AdapterService$Impl.java rename to test-report-aggregator/src/test/resources/adapters/AdapterService$Impl.java diff --git a/rest-ahead-processor/src/test/resources/adapters/AdapterService.java b/test-report-aggregator/src/test/resources/adapters/AdapterService.java similarity index 100% rename from rest-ahead-processor/src/test/resources/adapters/AdapterService.java rename to test-report-aggregator/src/test/resources/adapters/AdapterService.java diff --git a/rest-ahead-processor/src/test/resources/basic/InterfaceWithNotAnnotatedMethod.java b/test-report-aggregator/src/test/resources/basic/InterfaceWithNotAnnotatedMethod.java similarity index 100% rename from rest-ahead-processor/src/test/resources/basic/InterfaceWithNotAnnotatedMethod.java rename to test-report-aggregator/src/test/resources/basic/InterfaceWithNotAnnotatedMethod.java diff --git a/rest-ahead-processor/src/test/resources/basic/InterfaceWithThrows$Impl.java b/test-report-aggregator/src/test/resources/basic/InterfaceWithThrows$Impl.java similarity index 100% rename from rest-ahead-processor/src/test/resources/basic/InterfaceWithThrows$Impl.java rename to test-report-aggregator/src/test/resources/basic/InterfaceWithThrows$Impl.java diff --git a/rest-ahead-processor/src/test/resources/basic/InterfaceWithThrows.java b/test-report-aggregator/src/test/resources/basic/InterfaceWithThrows.java similarity index 100% rename from rest-ahead-processor/src/test/resources/basic/InterfaceWithThrows.java rename to test-report-aggregator/src/test/resources/basic/InterfaceWithThrows.java diff --git a/rest-ahead-processor/src/test/resources/basic/MethodClass.java b/test-report-aggregator/src/test/resources/basic/MethodClass.java similarity index 100% rename from rest-ahead-processor/src/test/resources/basic/MethodClass.java rename to test-report-aggregator/src/test/resources/basic/MethodClass.java diff --git a/rest-ahead-processor/src/test/resources/basic/MethodService.java b/test-report-aggregator/src/test/resources/basic/MethodService.java similarity index 100% rename from rest-ahead-processor/src/test/resources/basic/MethodService.java rename to test-report-aggregator/src/test/resources/basic/MethodService.java diff --git a/rest-ahead-processor/src/test/resources/basic/MultipleAnnotations.java b/test-report-aggregator/src/test/resources/basic/MultipleAnnotations.java similarity index 100% rename from rest-ahead-processor/src/test/resources/basic/MultipleAnnotations.java rename to test-report-aggregator/src/test/resources/basic/MultipleAnnotations.java diff --git a/rest-ahead-processor/src/test/resources/basic/NormalClassMethod.java b/test-report-aggregator/src/test/resources/basic/NormalClassMethod.java similarity index 100% rename from rest-ahead-processor/src/test/resources/basic/NormalClassMethod.java rename to test-report-aggregator/src/test/resources/basic/NormalClassMethod.java diff --git a/rest-ahead-processor/src/test/resources/headers/BoxedHeader.java b/test-report-aggregator/src/test/resources/headers/BoxedHeader.java similarity index 100% rename from rest-ahead-processor/src/test/resources/headers/BoxedHeader.java rename to test-report-aggregator/src/test/resources/headers/BoxedHeader.java diff --git a/rest-ahead-processor/src/test/resources/headers/EmptyHeader.java b/test-report-aggregator/src/test/resources/headers/EmptyHeader.java similarity index 100% rename from rest-ahead-processor/src/test/resources/headers/EmptyHeader.java rename to test-report-aggregator/src/test/resources/headers/EmptyHeader.java diff --git a/rest-ahead-processor/src/test/resources/headers/InvalidArrayHeader.java b/test-report-aggregator/src/test/resources/headers/InvalidArrayHeader.java similarity index 100% rename from rest-ahead-processor/src/test/resources/headers/InvalidArrayHeader.java rename to test-report-aggregator/src/test/resources/headers/InvalidArrayHeader.java diff --git a/rest-ahead-processor/src/test/resources/headers/InvalidCollectionHeader.java b/test-report-aggregator/src/test/resources/headers/InvalidCollectionHeader.java similarity index 100% rename from rest-ahead-processor/src/test/resources/headers/InvalidCollectionHeader.java rename to test-report-aggregator/src/test/resources/headers/InvalidCollectionHeader.java diff --git a/rest-ahead-processor/src/test/resources/headers/InvalidHeader.java b/test-report-aggregator/src/test/resources/headers/InvalidHeader.java similarity index 100% rename from rest-ahead-processor/src/test/resources/headers/InvalidHeader.java rename to test-report-aggregator/src/test/resources/headers/InvalidHeader.java diff --git a/rest-ahead-processor/src/test/resources/headers/PrimitiveHeader.java b/test-report-aggregator/src/test/resources/headers/PrimitiveHeader.java similarity index 100% rename from rest-ahead-processor/src/test/resources/headers/PrimitiveHeader.java rename to test-report-aggregator/src/test/resources/headers/PrimitiveHeader.java diff --git a/rest-ahead-processor/src/test/resources/headers/StringHeader.java b/test-report-aggregator/src/test/resources/headers/StringHeader.java similarity index 100% rename from rest-ahead-processor/src/test/resources/headers/StringHeader.java rename to test-report-aggregator/src/test/resources/headers/StringHeader.java diff --git a/rest-ahead-processor/src/test/resources/headers/UuidHeader.java b/test-report-aggregator/src/test/resources/headers/UuidHeader.java similarity index 100% rename from rest-ahead-processor/src/test/resources/headers/UuidHeader.java rename to test-report-aggregator/src/test/resources/headers/UuidHeader.java diff --git a/rest-ahead-processor/src/test/resources/headers/ValidArrayHeader.java b/test-report-aggregator/src/test/resources/headers/ValidArrayHeader.java similarity index 100% rename from rest-ahead-processor/src/test/resources/headers/ValidArrayHeader.java rename to test-report-aggregator/src/test/resources/headers/ValidArrayHeader.java diff --git a/rest-ahead-processor/src/test/resources/headers/ValidCollectionHeader.java b/test-report-aggregator/src/test/resources/headers/ValidCollectionHeader.java similarity index 100% rename from rest-ahead-processor/src/test/resources/headers/ValidCollectionHeader.java rename to test-report-aggregator/src/test/resources/headers/ValidCollectionHeader.java diff --git a/rest-ahead-processor/src/test/resources/parameters/BodyService$Impl.java b/test-report-aggregator/src/test/resources/parameters/BodyService$Impl.java similarity index 100% rename from rest-ahead-processor/src/test/resources/parameters/BodyService$Impl.java rename to test-report-aggregator/src/test/resources/parameters/BodyService$Impl.java diff --git a/rest-ahead-processor/src/test/resources/parameters/BodyService.java b/test-report-aggregator/src/test/resources/parameters/BodyService.java similarity index 100% rename from rest-ahead-processor/src/test/resources/parameters/BodyService.java rename to test-report-aggregator/src/test/resources/parameters/BodyService.java diff --git a/rest-ahead-processor/src/test/resources/parameters/FormAndPartSameField.java b/test-report-aggregator/src/test/resources/parameters/FormAndPartSameField.java similarity index 100% rename from rest-ahead-processor/src/test/resources/parameters/FormAndPartSameField.java rename to test-report-aggregator/src/test/resources/parameters/FormAndPartSameField.java diff --git a/rest-ahead-processor/src/test/resources/parameters/FormOnClass$Impl.java b/test-report-aggregator/src/test/resources/parameters/FormOnClass$Impl.java similarity index 74% rename from rest-ahead-processor/src/test/resources/parameters/FormOnClass$Impl.java rename to test-report-aggregator/src/test/resources/parameters/FormOnClass$Impl.java index 5195035..9ef5afd 100644 --- a/rest-ahead-processor/src/test/resources/parameters/FormOnClass$Impl.java +++ b/test-report-aggregator/src/test/resources/parameters/FormOnClass$Impl.java @@ -5,10 +5,13 @@ import io.github.zskamljic.restahead.client.requests.Request; import io.github.zskamljic.restahead.client.requests.Verb; import io.github.zskamljic.restahead.exceptions.RestException; -import io.github.zskamljic.restahead.generation.FormConverter; +import java.io.ByteArrayInputStream; +import java.io.InputStream; import java.lang.InterruptedException; import java.lang.Override; import java.lang.String; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; import java.util.concurrent.ExecutionException; import javax.annotation.processing.Generated; @@ -33,7 +36,7 @@ public final void post(FormOnClass.Sample body) { .setBaseUrl(baseUrl) .setPath(""); httpRequestBuilder.addHeader("Content-Type", "application/x-www-form-urlencoded"); - httpRequestBuilder.setBody(FormConverter.formEncode(body)); + httpRequestBuilder.setBody(formEncode(body)); var response = client.execute(httpRequestBuilder.build()); try { defaultAdapters.syncVoidAdapter(response); @@ -41,4 +44,10 @@ public final void post(FormOnClass.Sample body) { throw RestException.getAppropriateException(exception); } } + + public static InputStream formEncode(FormOnClass.Sample value) { + var stringValue = "first=" + URLEncoder.encode(String.valueOf(value.getFirst()), StandardCharsets.UTF_8) + + "&smth=" + URLEncoder.encode(String.valueOf(value.getSecond()), StandardCharsets.UTF_8); + return new ByteArrayInputStream(stringValue.getBytes()); + } } \ No newline at end of file diff --git a/rest-ahead-processor/src/test/resources/parameters/FormOnClass.java b/test-report-aggregator/src/test/resources/parameters/FormOnClass.java similarity index 100% rename from rest-ahead-processor/src/test/resources/parameters/FormOnClass.java rename to test-report-aggregator/src/test/resources/parameters/FormOnClass.java diff --git a/rest-ahead-processor/src/test/resources/parameters/FormOnClassInvalid.java b/test-report-aggregator/src/test/resources/parameters/FormOnClassInvalid.java similarity index 100% rename from rest-ahead-processor/src/test/resources/parameters/FormOnClassInvalid.java rename to test-report-aggregator/src/test/resources/parameters/FormOnClassInvalid.java diff --git a/rest-ahead-processor/src/test/resources/parameters/FormOnInvalid.java b/test-report-aggregator/src/test/resources/parameters/FormOnInvalid.java similarity index 100% rename from rest-ahead-processor/src/test/resources/parameters/FormOnInvalid.java rename to test-report-aggregator/src/test/resources/parameters/FormOnInvalid.java diff --git a/rest-ahead-processor/src/test/resources/parameters/FormOnInvalidMap.java b/test-report-aggregator/src/test/resources/parameters/FormOnInvalidMap.java similarity index 100% rename from rest-ahead-processor/src/test/resources/parameters/FormOnInvalidMap.java rename to test-report-aggregator/src/test/resources/parameters/FormOnInvalidMap.java diff --git a/rest-ahead-processor/src/test/resources/parameters/FormOnRecord$Impl.java b/test-report-aggregator/src/test/resources/parameters/FormOnRecord$Impl.java similarity index 56% rename from rest-ahead-processor/src/test/resources/parameters/FormOnRecord$Impl.java rename to test-report-aggregator/src/test/resources/parameters/FormOnRecord$Impl.java index 17f5f00..046eafd 100644 --- a/rest-ahead-processor/src/test/resources/parameters/FormOnRecord$Impl.java +++ b/test-report-aggregator/src/test/resources/parameters/FormOnRecord$Impl.java @@ -5,10 +5,13 @@ import io.github.zskamljic.restahead.client.requests.Request; import io.github.zskamljic.restahead.client.requests.Verb; import io.github.zskamljic.restahead.exceptions.RestException; -import io.github.zskamljic.restahead.generation.FormConverter; +import java.io.ByteArrayInputStream; +import java.io.InputStream; import java.lang.InterruptedException; import java.lang.Override; import java.lang.String; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; import java.util.concurrent.ExecutionException; import javax.annotation.processing.Generated; @@ -33,7 +36,7 @@ public final void post(FormOnRecord.Sample body) { .setBaseUrl(baseUrl) .setPath(""); httpRequestBuilder.addHeader("Content-Type", "application/x-www-form-urlencoded"); - httpRequestBuilder.setBody(FormConverter.formEncode(body)); + httpRequestBuilder.setBody(formEncode(body)); var response = client.execute(httpRequestBuilder.build()); try { defaultAdapters.syncVoidAdapter(response); @@ -41,4 +44,26 @@ public final void post(FormOnRecord.Sample body) { throw RestException.getAppropriateException(exception); } } -} + + @Override + public final void post2(FormOnRecord.Sample body) { + var httpRequestBuilder = new Request.Builder() + .setVerb(Verb.POST) + .setBaseUrl(baseUrl) + .setPath(""); + httpRequestBuilder.addHeader("Content-Type", "application/x-www-form-urlencoded"); + httpRequestBuilder.setBody(formEncode(body)); + var response = client.execute(httpRequestBuilder.build()); + try { + defaultAdapters.syncVoidAdapter(response); + } catch (ExecutionException | InterruptedException exception) { + throw RestException.getAppropriateException(exception); + } + } + + public static InputStream formEncode(FormOnRecord.Sample value) { + var stringValue = "first=" + URLEncoder.encode(String.valueOf(value.first()), StandardCharsets.UTF_8) + + "&2nd=" + URLEncoder.encode(String.valueOf(value.second()), StandardCharsets.UTF_8); + return new ByteArrayInputStream(stringValue.getBytes()); + } +} \ No newline at end of file diff --git a/rest-ahead-processor/src/test/resources/parameters/FormOnRecord.java b/test-report-aggregator/src/test/resources/parameters/FormOnRecord.java similarity index 89% rename from rest-ahead-processor/src/test/resources/parameters/FormOnRecord.java rename to test-report-aggregator/src/test/resources/parameters/FormOnRecord.java index 63b75f7..8b1c74e 100644 --- a/rest-ahead-processor/src/test/resources/parameters/FormOnRecord.java +++ b/test-report-aggregator/src/test/resources/parameters/FormOnRecord.java @@ -9,6 +9,9 @@ public interface FormOnRecord { @Post void post(@FormUrlEncoded @Body Sample body); + @Post + void post2(@FormUrlEncoded Sample body); + record Sample(String first, @FormName("2nd") String second) { } } \ No newline at end of file diff --git a/rest-ahead-processor/src/test/resources/parameters/FormOnRecordInvalid.java b/test-report-aggregator/src/test/resources/parameters/FormOnRecordInvalid.java similarity index 100% rename from rest-ahead-processor/src/test/resources/parameters/FormOnRecordInvalid.java rename to test-report-aggregator/src/test/resources/parameters/FormOnRecordInvalid.java diff --git a/rest-ahead-processor/src/test/resources/parameters/FormOnValidMap$Impl.java b/test-report-aggregator/src/test/resources/parameters/FormOnValidMap$Impl.java similarity index 90% rename from rest-ahead-processor/src/test/resources/parameters/FormOnValidMap$Impl.java rename to test-report-aggregator/src/test/resources/parameters/FormOnValidMap$Impl.java index f1495ec..3368cfb 100644 --- a/rest-ahead-processor/src/test/resources/parameters/FormOnValidMap$Impl.java +++ b/test-report-aggregator/src/test/resources/parameters/FormOnValidMap$Impl.java @@ -4,8 +4,8 @@ import io.github.zskamljic.restahead.client.Client; import io.github.zskamljic.restahead.client.requests.Request; import io.github.zskamljic.restahead.client.requests.Verb; +import io.github.zskamljic.restahead.conversion.MapFormConverter; import io.github.zskamljic.restahead.exceptions.RestException; -import io.github.zskamljic.restahead.generation.FormConverter; import java.lang.Double; import java.lang.Integer; import java.lang.InterruptedException; @@ -38,7 +38,7 @@ public final void post(Map body) { .setBaseUrl(baseUrl) .setPath(""); httpRequestBuilder.addHeader("Content-Type", "application/x-www-form-urlencoded"); - httpRequestBuilder.setBody(FormConverter.formEncode(body)); + httpRequestBuilder.setBody(MapFormConverter.formEncode(body)); var response = client.execute(httpRequestBuilder.build()); try { defaultAdapters.syncVoidAdapter(response); @@ -54,7 +54,7 @@ public final void post2(Map body) { .setBaseUrl(baseUrl) .setPath(""); httpRequestBuilder.addHeader("Content-Type", "application/x-www-form-urlencoded"); - httpRequestBuilder.setBody(FormConverter.formEncode(body)); + httpRequestBuilder.setBody(MapFormConverter.formEncode(body)); var response = client.execute(httpRequestBuilder.build()); try { defaultAdapters.syncVoidAdapter(response); @@ -70,7 +70,7 @@ public final void post3(Map body) { .setBaseUrl(baseUrl) .setPath(""); httpRequestBuilder.addHeader("Content-Type", "application/x-www-form-urlencoded"); - httpRequestBuilder.setBody(FormConverter.formEncode(body)); + httpRequestBuilder.setBody(MapFormConverter.formEncode(body)); var response = client.execute(httpRequestBuilder.build()); try { defaultAdapters.syncVoidAdapter(response); @@ -86,7 +86,7 @@ public final void post4(HashMap body) { .setBaseUrl(baseUrl) .setPath(""); httpRequestBuilder.addHeader("Content-Type", "application/x-www-form-urlencoded"); - httpRequestBuilder.setBody(FormConverter.formEncode(body)); + httpRequestBuilder.setBody(MapFormConverter.formEncode(body)); var response = client.execute(httpRequestBuilder.build()); try { defaultAdapters.syncVoidAdapter(response); @@ -94,4 +94,4 @@ public final void post4(HashMap body) { throw RestException.getAppropriateException(exception); } } -} +} \ No newline at end of file diff --git a/rest-ahead-processor/src/test/resources/parameters/FormOnValidMap.java b/test-report-aggregator/src/test/resources/parameters/FormOnValidMap.java similarity index 100% rename from rest-ahead-processor/src/test/resources/parameters/FormOnValidMap.java rename to test-report-aggregator/src/test/resources/parameters/FormOnValidMap.java diff --git a/rest-ahead-processor/src/test/resources/parameters/FormOnWithWrongField.java b/test-report-aggregator/src/test/resources/parameters/FormOnWithWrongField.java similarity index 100% rename from rest-ahead-processor/src/test/resources/parameters/FormOnWithWrongField.java rename to test-report-aggregator/src/test/resources/parameters/FormOnWithWrongField.java diff --git a/rest-ahead-processor/src/test/resources/parameters/FormWithPart$Impl.java b/test-report-aggregator/src/test/resources/parameters/FormWithPart$Impl.java similarity index 100% rename from rest-ahead-processor/src/test/resources/parameters/FormWithPart$Impl.java rename to test-report-aggregator/src/test/resources/parameters/FormWithPart$Impl.java diff --git a/rest-ahead-processor/src/test/resources/parameters/FormWithPart.java b/test-report-aggregator/src/test/resources/parameters/FormWithPart.java similarity index 100% rename from rest-ahead-processor/src/test/resources/parameters/FormWithPart.java rename to test-report-aggregator/src/test/resources/parameters/FormWithPart.java diff --git a/rest-ahead-processor/src/test/resources/parameters/FormWithPartInvalidType.java b/test-report-aggregator/src/test/resources/parameters/FormWithPartInvalidType.java similarity index 100% rename from rest-ahead-processor/src/test/resources/parameters/FormWithPartInvalidType.java rename to test-report-aggregator/src/test/resources/parameters/FormWithPartInvalidType.java diff --git a/rest-ahead-processor/src/test/resources/parameters/Parameters.java b/test-report-aggregator/src/test/resources/parameters/Parameters.java similarity index 100% rename from rest-ahead-processor/src/test/resources/parameters/Parameters.java rename to test-report-aggregator/src/test/resources/parameters/Parameters.java diff --git a/rest-ahead-processor/src/test/resources/parameters/class/FormConverter.java b/test-report-aggregator/src/test/resources/parameters/class/FormConverter.java similarity index 100% rename from rest-ahead-processor/src/test/resources/parameters/class/FormConverter.java rename to test-report-aggregator/src/test/resources/parameters/class/FormConverter.java diff --git a/rest-ahead-processor/src/test/resources/parameters/record/FormConverter.java b/test-report-aggregator/src/test/resources/parameters/record/FormConverter.java similarity index 100% rename from rest-ahead-processor/src/test/resources/parameters/record/FormConverter.java rename to test-report-aggregator/src/test/resources/parameters/record/FormConverter.java diff --git a/rest-ahead-processor/src/test/resources/path/InvalidPath.java b/test-report-aggregator/src/test/resources/path/InvalidPath.java similarity index 100% rename from rest-ahead-processor/src/test/resources/path/InvalidPath.java rename to test-report-aggregator/src/test/resources/path/InvalidPath.java diff --git a/rest-ahead-processor/src/test/resources/path/PathAnnotation$Impl.java b/test-report-aggregator/src/test/resources/path/PathAnnotation$Impl.java similarity index 100% rename from rest-ahead-processor/src/test/resources/path/PathAnnotation$Impl.java rename to test-report-aggregator/src/test/resources/path/PathAnnotation$Impl.java diff --git a/rest-ahead-processor/src/test/resources/path/PathAnnotation.java b/test-report-aggregator/src/test/resources/path/PathAnnotation.java similarity index 100% rename from rest-ahead-processor/src/test/resources/path/PathAnnotation.java rename to test-report-aggregator/src/test/resources/path/PathAnnotation.java diff --git a/rest-ahead-processor/src/test/resources/path/PathAnnotationCodeName$Impl.java b/test-report-aggregator/src/test/resources/path/PathAnnotationCodeName$Impl.java similarity index 100% rename from rest-ahead-processor/src/test/resources/path/PathAnnotationCodeName$Impl.java rename to test-report-aggregator/src/test/resources/path/PathAnnotationCodeName$Impl.java diff --git a/rest-ahead-processor/src/test/resources/path/PathAnnotationCodeName.java b/test-report-aggregator/src/test/resources/path/PathAnnotationCodeName.java similarity index 100% rename from rest-ahead-processor/src/test/resources/path/PathAnnotationCodeName.java rename to test-report-aggregator/src/test/resources/path/PathAnnotationCodeName.java diff --git a/rest-ahead-processor/src/test/resources/path/PathWithDuplicates.java b/test-report-aggregator/src/test/resources/path/PathWithDuplicates.java similarity index 100% rename from rest-ahead-processor/src/test/resources/path/PathWithDuplicates.java rename to test-report-aggregator/src/test/resources/path/PathWithDuplicates.java diff --git a/rest-ahead-processor/src/test/resources/path/PathWithIterable.java b/test-report-aggregator/src/test/resources/path/PathWithIterable.java similarity index 100% rename from rest-ahead-processor/src/test/resources/path/PathWithIterable.java rename to test-report-aggregator/src/test/resources/path/PathWithIterable.java diff --git a/rest-ahead-processor/src/test/resources/path/PathWithoutPlaceholder.java b/test-report-aggregator/src/test/resources/path/PathWithoutPlaceholder.java similarity index 100% rename from rest-ahead-processor/src/test/resources/path/PathWithoutPlaceholder.java rename to test-report-aggregator/src/test/resources/path/PathWithoutPlaceholder.java diff --git a/rest-ahead-processor/src/test/resources/query/CollectionAndArray.java b/test-report-aggregator/src/test/resources/query/CollectionAndArray.java similarity index 100% rename from rest-ahead-processor/src/test/resources/query/CollectionAndArray.java rename to test-report-aggregator/src/test/resources/query/CollectionAndArray.java diff --git a/rest-ahead-processor/src/test/resources/query/MissingName.java b/test-report-aggregator/src/test/resources/query/MissingName.java similarity index 100% rename from rest-ahead-processor/src/test/resources/query/MissingName.java rename to test-report-aggregator/src/test/resources/query/MissingName.java diff --git a/rest-ahead-processor/src/test/resources/query/MissingQueryValue.java b/test-report-aggregator/src/test/resources/query/MissingQueryValue.java similarity index 100% rename from rest-ahead-processor/src/test/resources/query/MissingQueryValue.java rename to test-report-aggregator/src/test/resources/query/MissingQueryValue.java diff --git a/rest-ahead-processor/src/test/resources/query/QueryInPath.java b/test-report-aggregator/src/test/resources/query/QueryInPath.java similarity index 100% rename from rest-ahead-processor/src/test/resources/query/QueryInPath.java rename to test-report-aggregator/src/test/resources/query/QueryInPath.java diff --git a/rest-ahead-processor/src/test/resources/query/ValidCombinedQuery.java b/test-report-aggregator/src/test/resources/query/ValidCombinedQuery.java similarity index 100% rename from rest-ahead-processor/src/test/resources/query/ValidCombinedQuery.java rename to test-report-aggregator/src/test/resources/query/ValidCombinedQuery.java diff --git a/rest-ahead-processor/src/test/resources/query/ValidQuery.java b/test-report-aggregator/src/test/resources/query/ValidQuery.java similarity index 100% rename from rest-ahead-processor/src/test/resources/query/ValidQuery.java rename to test-report-aggregator/src/test/resources/query/ValidQuery.java diff --git a/rest-ahead-processor/src/test/resources/response/FutureGenericResponse$Impl.java b/test-report-aggregator/src/test/resources/response/FutureGenericResponse$Impl.java similarity index 100% rename from rest-ahead-processor/src/test/resources/response/FutureGenericResponse$Impl.java rename to test-report-aggregator/src/test/resources/response/FutureGenericResponse$Impl.java diff --git a/rest-ahead-processor/src/test/resources/response/FutureGenericResponse.java b/test-report-aggregator/src/test/resources/response/FutureGenericResponse.java similarity index 100% rename from rest-ahead-processor/src/test/resources/response/FutureGenericResponse.java rename to test-report-aggregator/src/test/resources/response/FutureGenericResponse.java diff --git a/rest-ahead-processor/src/test/resources/response/ResponseWithBody$Impl.java b/test-report-aggregator/src/test/resources/response/ResponseWithBody$Impl.java similarity index 100% rename from rest-ahead-processor/src/test/resources/response/ResponseWithBody$Impl.java rename to test-report-aggregator/src/test/resources/response/ResponseWithBody$Impl.java diff --git a/rest-ahead-processor/src/test/resources/response/ResponseWithBody.java b/test-report-aggregator/src/test/resources/response/ResponseWithBody.java similarity index 100% rename from rest-ahead-processor/src/test/resources/response/ResponseWithBody.java rename to test-report-aggregator/src/test/resources/response/ResponseWithBody.java diff --git a/rest-ahead-processor/src/test/resources/response/ResponseWithErrorBody$Impl.java b/test-report-aggregator/src/test/resources/response/ResponseWithErrorBody$Impl.java similarity index 100% rename from rest-ahead-processor/src/test/resources/response/ResponseWithErrorBody$Impl.java rename to test-report-aggregator/src/test/resources/response/ResponseWithErrorBody$Impl.java diff --git a/rest-ahead-processor/src/test/resources/response/ResponseWithErrorBody.java b/test-report-aggregator/src/test/resources/response/ResponseWithErrorBody.java similarity index 100% rename from rest-ahead-processor/src/test/resources/response/ResponseWithErrorBody.java rename to test-report-aggregator/src/test/resources/response/ResponseWithErrorBody.java diff --git a/rest-ahead-processor/src/test/resources/response/ServiceWithGenericResponse$Impl.java b/test-report-aggregator/src/test/resources/response/ServiceWithGenericResponse$Impl.java similarity index 100% rename from rest-ahead-processor/src/test/resources/response/ServiceWithGenericResponse$Impl.java rename to test-report-aggregator/src/test/resources/response/ServiceWithGenericResponse$Impl.java diff --git a/rest-ahead-processor/src/test/resources/response/ServiceWithGenericResponse.java b/test-report-aggregator/src/test/resources/response/ServiceWithGenericResponse.java similarity index 100% rename from rest-ahead-processor/src/test/resources/response/ServiceWithGenericResponse.java rename to test-report-aggregator/src/test/resources/response/ServiceWithGenericResponse.java diff --git a/rest-ahead-processor/src/test/resources/response/ServiceWithResponse.java b/test-report-aggregator/src/test/resources/response/ServiceWithResponse.java similarity index 100% rename from rest-ahead-processor/src/test/resources/response/ServiceWithResponse.java rename to test-report-aggregator/src/test/resources/response/ServiceWithResponse.java diff --git a/rest-ahead-processor/src/test/resources/response/ServiceWithUnknownResponse$Impl.java b/test-report-aggregator/src/test/resources/response/ServiceWithUnknownResponse$Impl.java similarity index 100% rename from rest-ahead-processor/src/test/resources/response/ServiceWithUnknownResponse$Impl.java rename to test-report-aggregator/src/test/resources/response/ServiceWithUnknownResponse$Impl.java diff --git a/rest-ahead-processor/src/test/resources/response/ServiceWithUnknownResponse.java b/test-report-aggregator/src/test/resources/response/ServiceWithUnknownResponse.java similarity index 100% rename from rest-ahead-processor/src/test/resources/response/ServiceWithUnknownResponse.java rename to test-report-aggregator/src/test/resources/response/ServiceWithUnknownResponse.java diff --git a/test-report-aggregator/src/test/resources/spring/SpringService$Impl.java b/test-report-aggregator/src/test/resources/spring/SpringService$Impl.java new file mode 100644 index 0000000..595e44f --- /dev/null +++ b/test-report-aggregator/src/test/resources/spring/SpringService$Impl.java @@ -0,0 +1,139 @@ +package restahead.spring; + +import io.github.zskamljic.restahead.adapter.DefaultAdapters; +import io.github.zskamljic.restahead.client.Client; +import io.github.zskamljic.restahead.client.requests.MultiPartRequest; +import io.github.zskamljic.restahead.client.requests.Request; +import io.github.zskamljic.restahead.client.requests.Verb; +import io.github.zskamljic.restahead.client.requests.parts.FieldPart; +import io.github.zskamljic.restahead.client.requests.parts.FilePart; +import io.github.zskamljic.restahead.client.responses.Response; +import io.github.zskamljic.restahead.conversion.Converter; +import io.github.zskamljic.restahead.exceptions.RestException; +import java.io.IOException; +import java.lang.InterruptedException; +import java.lang.Override; +import java.lang.String; +import java.util.Map; +import java.util.concurrent.ExecutionException; +import javax.annotation.processing.Generated; +import org.springframework.web.multipart.MultipartFile; + +@Generated("Generated by RestAhead") +public final class SpringService$Impl implements SpringService { + private final String baseUrl; + + private final Client client; + + private final Converter converter; + + private final DefaultAdapters defaultAdapters; + + public SpringService$Impl(String baseUrl, Client client, Converter converter, + DefaultAdapters defaultAdapters) { + this.baseUrl = baseUrl; + this.client = client; + this.converter = converter; + this.defaultAdapters = defaultAdapters; + } + + @Override + public final Response performDelete(String header1, String header2) { + var httpRequestBuilder = new Request.Builder() + .setVerb(Verb.DELETE) + .setBaseUrl(baseUrl) + .setPath("/delete"); + httpRequestBuilder.addHeader("custom", String.valueOf(header1)); + httpRequestBuilder.addHeader("header2", String.valueOf(header2)); + var response = client.execute(httpRequestBuilder.build()); + try { + return defaultAdapters.syncAdapter(response); + } catch (ExecutionException | InterruptedException exception) { + throw RestException.getAppropriateException(exception); + } + } + + @Override + public final Response performGet(String get, String second) { + var httpRequestBuilder = new Request.Builder() + .setVerb(Verb.GET) + .setBaseUrl(baseUrl) + .setPath("/{get}/{hello}".replace("{get}", String.valueOf(get)) + .replace("{hello}", String.valueOf(second))); + var response = client.execute(httpRequestBuilder.build()); + try { + return defaultAdapters.syncAdapter(response); + } catch (ExecutionException | InterruptedException exception) { + throw RestException.getAppropriateException(exception); + } + } + + @Override + public final Response performPatch(Map body) { + var httpRequestBuilder = new Request.Builder() + .setVerb(Verb.PATCH) + .setBaseUrl(baseUrl) + .setPath("/patch"); + try { + httpRequestBuilder.setBody(converter.serialize(body)); + } catch (IOException exception) { + throw RestException.getAppropriateException(exception); + } + var response = client.execute(httpRequestBuilder.build()); + try { + return defaultAdapters.syncAdapter(response); + } catch (ExecutionException | InterruptedException exception) { + throw RestException.getAppropriateException(exception); + } + } + + @Override + public final Response performPost(String part, MultipartFile file) { + var httpRequestBuilder = new Request.Builder() + .setVerb(Verb.POST) + .setBaseUrl(baseUrl) + .setPath("/post"); + try { + MultiPartRequest.builder() + .addPart(new FieldPart("part", part)) + .addPart(new FilePart("file", file.getOriginalFilename(), file.getInputStream())) + .buildInto(httpRequestBuilder); + } catch (IOException exception) { + throw RestException.getAppropriateException(exception); + } + var response = client.execute(httpRequestBuilder.build()); + try { + return defaultAdapters.syncAdapter(response); + } catch (ExecutionException | InterruptedException exception) { + throw RestException.getAppropriateException(exception); + } + } + + @Override + public final Response performPut() { + var httpRequestBuilder = new Request.Builder() + .setVerb(Verb.PUT) + .setBaseUrl(baseUrl) + .setPath("/put"); + var response = client.execute(httpRequestBuilder.build()); + try { + return defaultAdapters.syncAdapter(response); + } catch (ExecutionException | InterruptedException exception) { + throw RestException.getAppropriateException(exception); + } + } + + @Override + public final Response customGet() { + var httpRequestBuilder = new Request.Builder() + .setVerb(Verb.GET) + .setBaseUrl(baseUrl) + .setPath("/get"); + var response = client.execute(httpRequestBuilder.build()); + try { + return defaultAdapters.syncAdapter(response); + } catch (ExecutionException | InterruptedException exception) { + throw RestException.getAppropriateException(exception); + } + } +} \ No newline at end of file diff --git a/test-report-aggregator/src/test/resources/spring/SpringService.java b/test-report-aggregator/src/test/resources/spring/SpringService.java new file mode 100644 index 0000000..b775836 --- /dev/null +++ b/test-report-aggregator/src/test/resources/spring/SpringService.java @@ -0,0 +1,48 @@ +package restahead.spring; + +import io.github.zskamljic.restahead.client.responses.Response; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PatchMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestPart; +import org.springframework.web.multipart.MultipartFile; + +import java.util.Map; + +interface SpringService { + @DeleteMapping("/delete") + Response performDelete( + @RequestHeader("custom") String header1, + @RequestHeader("header2") String header2 + ); + + @GetMapping("/{get}/{hello}") + Response performGet( + @PathVariable String get, + @PathVariable("hello") String second + ); + + @PatchMapping("/patch") + Response performPatch( + @RequestBody Map body + ); + + @PostMapping("/post") + Response performPost( + @RequestPart String part, + @RequestPart MultipartFile file + ); + + @PutMapping("/put") + Response performPut(); + + @RequestMapping(method = RequestMethod.GET, value = "/get") + Response customGet(); +} \ No newline at end of file