From f21e0c55c6f33194d89e503dde811a76913c2d9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=BE=D0=B1=D0=BE=D0=BB=D0=B5=D0=B2=20=D0=92=D0=B0?= =?UTF-8?q?=D1=81=D0=B8=D0=BB=D0=B8=D0=B9?= Date: Sat, 17 Aug 2024 21:51:50 +0300 Subject: [PATCH] Added Camel support --- README.md | 7 +- build.gradle | 8 +- .../plugins/mapstruct/MapstructPlugin.java | 127 +-------- .../plugins/mapstruct/PluginDependency.java | 122 --------- .../dependency/AdditionalDependency.java | 49 ++++ .../dependency/MarkerDependency.java | 37 +++ .../dependency/PluginDependency.java | 40 +++ .../manager/CompilerArgsManager.java | 64 +++++ .../mapstruct/manager/DependencyManager.java | 131 +++++++++ .../gradle/plugins/mapstruct/BaseTest.java | 57 ++++ .../plugins/mapstruct/CompilerArgsTest.java | 112 ++++++++ ...uctPluginTest.java => DependencyTest.java} | 248 ++++++++---------- 12 files changed, 618 insertions(+), 384 deletions(-) delete mode 100644 src/main/java/com/github/akazver/gradle/plugins/mapstruct/PluginDependency.java create mode 100644 src/main/java/com/github/akazver/gradle/plugins/mapstruct/dependency/AdditionalDependency.java create mode 100644 src/main/java/com/github/akazver/gradle/plugins/mapstruct/dependency/MarkerDependency.java create mode 100644 src/main/java/com/github/akazver/gradle/plugins/mapstruct/dependency/PluginDependency.java create mode 100644 src/main/java/com/github/akazver/gradle/plugins/mapstruct/manager/CompilerArgsManager.java create mode 100644 src/main/java/com/github/akazver/gradle/plugins/mapstruct/manager/DependencyManager.java create mode 100644 src/test/java/com/github/akazver/gradle/plugins/mapstruct/BaseTest.java create mode 100644 src/test/java/com/github/akazver/gradle/plugins/mapstruct/CompilerArgsTest.java rename src/test/java/com/github/akazver/gradle/plugins/mapstruct/{MapstructPluginTest.java => DependencyTest.java} (53%) diff --git a/README.md b/README.md index c2ea256..f476ccf 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ Gradle plugin for easy [MapStruct](https://mapstruct.org/) setup Usage: ```groovy plugins { - id 'com.github.akazver.mapstruct' version '1.0.7' + id 'com.github.akazver.mapstruct' version '1.0.8' } ``` @@ -31,6 +31,11 @@ plugins { - [mapstruct-spring-extensions](https://mvnrepository.com/artifact/org.mapstruct.extensions.spring/mapstruct-spring-extensions) (annotationProcessor) - [mapstruct-spring-test-extensions](https://mvnrepository.com/artifact/org.mapstruct.extensions.spring/mapstruct-spring-test-extensions) (testImplementation) +**Camel** (optional) +- [camel-mapstruct](https://mvnrepository.com/artifact/org.apache.camel/camel-mapstruct) (implementation) +- [camel-mapstruct-starter](https://mvnrepository.com/artifact/org.apache.camel.springboot/camel-mapstruct-starter) (implementation) +- [camel-quarkus-mapstruct](https://mvnrepository.com/artifact/org.apache.camel.quarkus/camel-quarkus-mapstruct) (implementation) + ## Config Plugin adds configuration block which looks like this: ```groovy diff --git a/build.gradle b/build.gradle index 2dd2295..90fc668 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,7 @@ plugins { } group = 'com.github.akazver.mapstruct' -version = '1.0.7' +version = '1.0.8' gradlePlugin { website = 'https://github.com/AkaZver' @@ -20,7 +20,7 @@ gradlePlugin { displayName = 'MapStruct Plugin' description = 'Automatic MapStruct configuration' implementationClass = 'com.github.akazver.gradle.plugins.mapstruct.MapstructPlugin' - tags = ['mapstruct'] + tags = ['mapstruct', 'mapping', 'lombok', 'spring', 'quarkus', 'camel'] } } } @@ -33,10 +33,10 @@ repositories { dependencies { testImplementation 'io.freefair.gradle:lombok-plugin:8.7.1' testImplementation 'org.assertj:assertj-core:3.26.3' - testImplementation 'org.mockito:mockito-core:5.12.0' - testImplementation 'org.mockito:mockito-junit-jupiter:5.12.0' testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.11.0' testImplementation 'org.junit.jupiter:junit-jupiter-params:5.11.0' + testImplementation 'org.mockito:mockito-core:5.12.0' + testImplementation 'org.mockito:mockito-junit-jupiter:5.12.0' } test { diff --git a/src/main/java/com/github/akazver/gradle/plugins/mapstruct/MapstructPlugin.java b/src/main/java/com/github/akazver/gradle/plugins/mapstruct/MapstructPlugin.java index 58a4416..80fe03a 100644 --- a/src/main/java/com/github/akazver/gradle/plugins/mapstruct/MapstructPlugin.java +++ b/src/main/java/com/github/akazver/gradle/plugins/mapstruct/MapstructPlugin.java @@ -1,25 +1,10 @@ package com.github.akazver.gradle.plugins.mapstruct; +import com.github.akazver.gradle.plugins.mapstruct.manager.CompilerArgsManager; +import com.github.akazver.gradle.plugins.mapstruct.manager.DependencyManager; import org.gradle.api.Plugin; import org.gradle.api.Project; -import org.gradle.api.artifacts.ConfigurationContainer; -import org.gradle.api.artifacts.Dependency; -import org.gradle.api.artifacts.dsl.DependencyHandler; -import org.gradle.api.logging.Logger; -import org.gradle.api.logging.Logging; -import org.gradle.api.tasks.compile.CompileOptions; -import org.gradle.api.tasks.compile.JavaCompile; - -import java.beans.IntrospectionException; -import java.beans.PropertyDescriptor; -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.util.Arrays; -import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import static com.github.akazver.gradle.plugins.mapstruct.PluginDependency.*; +import org.jetbrains.annotations.NotNull; /** * Plugin implementation which adds necessary dependencies and compiler options @@ -28,110 +13,18 @@ */ public class MapstructPlugin implements Plugin { - private static final Logger LOGGER = Logging.getLogger(MapstructPlugin.class); - private static final String ADDING_MESSAGE = "Adding {} dependencies"; - private static final String COMPILER_ARG_PATTERN = "-Amapstruct.%s=%s"; - @Override - public void apply(Project project) { + public void apply(@NotNull Project project) { + DependencyManager dependencyManager = new DependencyManager(project); + CompilerArgsManager compilerArgsManager = new CompilerArgsManager(project); + project.getExtensions().create("mapstruct", MapstructExtension.class); project.afterEvaluate(it -> { - addRequiredDependencies(it); - addOptionalDependencies(it); - addCompilerArgs(it); + dependencyManager.addRequiredDependencies(); + dependencyManager.addOptionalDependencies(); + compilerArgsManager.addCompilerArgs(); }); } - private void addDependencies(Project project, List pluginDependencies) { - DependencyHandler projectDependencies = project.getDependencies(); - - pluginDependencies.forEach(pluginDependency -> { - LOGGER.lifecycle("- {}", pluginDependency.getId()); - projectDependencies.add(pluginDependency.getConfiguration(), pluginDependency.getId()); - }); - } - - private void addRequiredDependencies(Project project) { - LOGGER.lifecycle(ADDING_MESSAGE, "MapStruct"); - addDependencies(project, MAPSTRUCT_DEPENDENCIES); - } - - private void addOptionalDependencies(Project project) { - ConfigurationContainer configurations = project.getConfigurations(); - - if (hasLombok(configurations) && !hasBinding(configurations)) { - LOGGER.lifecycle(ADDING_MESSAGE, "Lombok"); - addDependencies(project, LOMBOK_DEPENDENCIES); - } - - if (hasSpring(configurations)) { - LOGGER.lifecycle(ADDING_MESSAGE, "Spring"); - addDependencies(project, SPRING_DEPENDENCIES); - } - } - - private boolean hasLombok(ConfigurationContainer configurations) { - return hasConfiguration(configurations, "lombok") - || hasDependency(configurations, LOMBOK); - } - - private boolean hasBinding(ConfigurationContainer configurations) { - return hasDependency(configurations, LOMBOK_MAPSTRUCT_BINDING); - } - - private boolean hasSpring(ConfigurationContainer configurations) { - return hasConfiguration(configurations, "bootArchives") - || hasDependency(configurations, SPRING_CORE); - } - - private boolean isNeededDependency(Dependency dependency, PluginDependency pluginDependency) { - return pluginDependency.getGroup().equals(dependency.getGroup()) - && pluginDependency.getName().equals(dependency.getName()); - } - - private boolean hasDependency(ConfigurationContainer configurations, PluginDependency pluginDependency) { - return configurations.getByName(pluginDependency.getConfiguration()) - .getAllDependencies() - .stream() - .anyMatch(dependency -> isNeededDependency(dependency, pluginDependency)); - } - - private boolean hasConfiguration(ConfigurationContainer configurations, String configurationName) { - return configurations.findByName(configurationName) != null; - } - - private void addCompilerArgs(Project project) { - MapstructExtension extension = project.getExtensions().getByType(MapstructExtension.class); - - CompileOptions compileOptions = project.getTasks() - .withType(JavaCompile.class) - .getByName("compileJava") - .getOptions(); - - Stream projectCompilerArgs = compileOptions.getCompilerArgs().stream(); - - Stream mapstructCompilerArgs = Arrays.stream(MapstructExtension.class.getDeclaredFields()) - .filter(field -> !field.isSynthetic()) - .map(Field::getName) - .map(name -> fetchCompilerArg(extension, name)); - - List compilerArgs = Stream.concat(projectCompilerArgs, mapstructCompilerArgs) - .collect(Collectors.toList()); - - compileOptions.setCompilerArgs(compilerArgs); - } - - private String fetchCompilerArg(MapstructExtension extension, String name) { - try { - PropertyDescriptor descriptor = new PropertyDescriptor(name, MapstructExtension.class); - Object value = descriptor.getReadMethod().invoke(extension); - - return String.format(COMPILER_ARG_PATTERN, name, value); - } catch (IllegalAccessException | InvocationTargetException | IntrospectionException exception) { - String message = String.format("Can't fetch compiler argument for '%s'", name); - throw new MapstructPluginException(message, exception); - } - } - } diff --git a/src/main/java/com/github/akazver/gradle/plugins/mapstruct/PluginDependency.java b/src/main/java/com/github/akazver/gradle/plugins/mapstruct/PluginDependency.java deleted file mode 100644 index 68ffb72..0000000 --- a/src/main/java/com/github/akazver/gradle/plugins/mapstruct/PluginDependency.java +++ /dev/null @@ -1,122 +0,0 @@ -package com.github.akazver.gradle.plugins.mapstruct; - -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.ToString; - -import java.util.ArrayList; -import java.util.List; - -/** - * Description for all plugin dependencies - * - * @author Vasiliy Sobolev - */ -@Getter -@ToString -@EqualsAndHashCode -@SuppressWarnings("java:S1192") -public class PluginDependency { - - public static final PluginDependency LOMBOK = new PluginDependency( - "annotationProcessor", - "org.projectlombok", - "lombok", - "1.18.32" - ); - - public static final PluginDependency LOMBOK_MAPSTRUCT_BINDING = new PluginDependency( - "annotationProcessor", - "org.projectlombok", - "lombok-mapstruct-binding", - "0.2.0" - ); - - public static final PluginDependency MAPSTRUCT = new PluginDependency( - "implementation", - "org.mapstruct", - "mapstruct", - "1.6.0" - ); - - public static final PluginDependency MAPSTRUCT_PROCESSOR = new PluginDependency( - "annotationProcessor", - "org.mapstruct", - "mapstruct-processor", - "1.6.0" - ); - - public static final PluginDependency MAPSTRUCT_SPRING_EXTENSIONS = new PluginDependency( - "annotationProcessor", - "org.mapstruct.extensions.spring", - "mapstruct-spring-extensions", - "1.1.1" - ); - - public static final PluginDependency MAPSTRUCT_SPRING_ANNOTATIONS = new PluginDependency( - "implementation", - "org.mapstruct.extensions.spring", - "mapstruct-spring-annotations", - "1.1.1" - ); - - public static final PluginDependency MAPSTRUCT_SPRING_TEST_EXTENSIONS = new PluginDependency( - "testImplementation", - "org.mapstruct.extensions.spring", - "mapstruct-spring-test-extensions", - "1.1.1" - ); - - public static final PluginDependency SPRING_CORE = new PluginDependency( - "implementation", - "org.springframework", - "spring-core", - "6.1.12" - ); - - protected static final List MAPSTRUCT_DEPENDENCIES = new ArrayList<>(); - protected static final List LOMBOK_DEPENDENCIES = new ArrayList<>(); - protected static final List SPRING_DEPENDENCIES = new ArrayList<>(); - - static { - MAPSTRUCT_DEPENDENCIES.add(MAPSTRUCT); - MAPSTRUCT_DEPENDENCIES.add(MAPSTRUCT_PROCESSOR); - - LOMBOK_DEPENDENCIES.add(LOMBOK_MAPSTRUCT_BINDING); - - SPRING_DEPENDENCIES.add(MAPSTRUCT_SPRING_EXTENSIONS); - SPRING_DEPENDENCIES.add(MAPSTRUCT_SPRING_ANNOTATIONS); - SPRING_DEPENDENCIES.add(MAPSTRUCT_SPRING_TEST_EXTENSIONS); - } - - private final String configuration; - private final String group; - private final String name; - private final String version; - private final String id; - - public PluginDependency(String configuration, String group, String name, String version) { - this.configuration = configuration; - this.group = group; - this.name = name; - this.version = version; - this.id = String.join(":", group, name, version); - } - - public PluginDependency(String configuration, String id) { - this.configuration = configuration; - this.id = id; - - String[] split = id.split(":"); - - if (split.length != 3) { - String message = String.format("Dependency id '%s' is invalid", id); - throw new MapstructPluginException(message); - } - - this.group = split[0]; - this.name = split[1]; - this.version = split[2]; - } - -} diff --git a/src/main/java/com/github/akazver/gradle/plugins/mapstruct/dependency/AdditionalDependency.java b/src/main/java/com/github/akazver/gradle/plugins/mapstruct/dependency/AdditionalDependency.java new file mode 100644 index 0000000..680b7ef --- /dev/null +++ b/src/main/java/com/github/akazver/gradle/plugins/mapstruct/dependency/AdditionalDependency.java @@ -0,0 +1,49 @@ +package com.github.akazver.gradle.plugins.mapstruct.dependency; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.ToString; + +/** + * Dependencies added by the plugin + * + * @author Vasiliy Sobolev + */ +@Getter +@ToString +@SuppressWarnings("java:S1192") +@EqualsAndHashCode(callSuper = true) +public class AdditionalDependency extends PluginDependency { + + public static final AdditionalDependency MAPSTRUCT = + new AdditionalDependency("implementation", "org.mapstruct:mapstruct:1.6.0"); + + public static final AdditionalDependency MAPSTRUCT_PROCESSOR = + new AdditionalDependency("annotationProcessor", "org.mapstruct:mapstruct-processor:1.6.0"); + + public static final AdditionalDependency LOMBOK_MAPSTRUCT_BINDING = + new AdditionalDependency("annotationProcessor", "org.projectlombok:lombok-mapstruct-binding:0.2.0"); + + public static final AdditionalDependency MAPSTRUCT_SPRING_EXTENSIONS = + new AdditionalDependency("annotationProcessor", "org.mapstruct.extensions.spring:mapstruct-spring-extensions:1.1.1"); + + public static final AdditionalDependency MAPSTRUCT_SPRING_ANNOTATIONS = + new AdditionalDependency("implementation", "org.mapstruct.extensions.spring:mapstruct-spring-annotations:1.1.1"); + + public static final AdditionalDependency MAPSTRUCT_SPRING_TEST_EXTENSIONS = + new AdditionalDependency("testImplementation", "org.mapstruct.extensions.spring:mapstruct-spring-test-extensions:1.1.1"); + + public static final AdditionalDependency CAMEL_MAPSTRUCT = + new AdditionalDependency("implementation", "org.apache.camel:camel-mapstruct:4.7.0"); + + public static final AdditionalDependency CAMEL_MAPSTRUCT_STARTER = + new AdditionalDependency("implementation", "org.apache.camel.springboot:camel-mapstruct-starter:4.7.0"); + + public static final AdditionalDependency CAMEL_QUARKUS_MAPSTRUCT = + new AdditionalDependency("implementation", "org.apache.camel.quarkus:camel-quarkus-mapstruct:3.13.0"); + + public AdditionalDependency(String configuration, String id) { + super(configuration, id); + } + +} diff --git a/src/main/java/com/github/akazver/gradle/plugins/mapstruct/dependency/MarkerDependency.java b/src/main/java/com/github/akazver/gradle/plugins/mapstruct/dependency/MarkerDependency.java new file mode 100644 index 0000000..a61a5d6 --- /dev/null +++ b/src/main/java/com/github/akazver/gradle/plugins/mapstruct/dependency/MarkerDependency.java @@ -0,0 +1,37 @@ +package com.github.akazver.gradle.plugins.mapstruct.dependency; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.ToString; + +/** + * Marker dependencies for different frameworks + * + * @author Vasiliy Sobolev + */ +@Getter +@ToString +@SuppressWarnings("java:S1192") +@EqualsAndHashCode(callSuper = true) +public class MarkerDependency extends PluginDependency { + + public static final MarkerDependency LOMBOK = + new MarkerDependency("annotationProcessor", "org.projectlombok:lombok:1.18.32"); + + public static final MarkerDependency SPRING_CORE = + new MarkerDependency("implementation", "org.springframework:spring-core:6.1.12"); + + public static final MarkerDependency SPRING_BOOT = + new MarkerDependency("implementation", "org.springframework.boot:spring-boot:3.3.2"); + + public static final MarkerDependency CAMEL_CORE = + new MarkerDependency("implementation", "org.apache.camel:camel-core:4.7.0"); + + public static final MarkerDependency QUARKUS_CORE = + new MarkerDependency("implementation", "io.quarkus:quarkus-core:3.13.2"); + + public MarkerDependency(String configuration, String id) { + super(configuration, id); + } + +} diff --git a/src/main/java/com/github/akazver/gradle/plugins/mapstruct/dependency/PluginDependency.java b/src/main/java/com/github/akazver/gradle/plugins/mapstruct/dependency/PluginDependency.java new file mode 100644 index 0000000..fe39f21 --- /dev/null +++ b/src/main/java/com/github/akazver/gradle/plugins/mapstruct/dependency/PluginDependency.java @@ -0,0 +1,40 @@ +package com.github.akazver.gradle.plugins.mapstruct.dependency; + +import com.github.akazver.gradle.plugins.mapstruct.MapstructPluginException; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.ToString; + +/** + * Description for dependencies used by plugin + * + * @author Vasiliy Sobolev + */ +@Getter +@ToString +@EqualsAndHashCode +public class PluginDependency { + + private final String configuration; + private final String group; + private final String name; + private final String version; + private final String id; + + public PluginDependency(String configuration, String id) { + this.configuration = configuration; + this.id = id; + + String[] split = id.split(":"); + + if (split.length != 3) { + String message = String.format("Dependency id '%s' is invalid", id); + throw new MapstructPluginException(message); + } + + this.group = split[0]; + this.name = split[1]; + this.version = split[2]; + } + +} diff --git a/src/main/java/com/github/akazver/gradle/plugins/mapstruct/manager/CompilerArgsManager.java b/src/main/java/com/github/akazver/gradle/plugins/mapstruct/manager/CompilerArgsManager.java new file mode 100644 index 0000000..8be908a --- /dev/null +++ b/src/main/java/com/github/akazver/gradle/plugins/mapstruct/manager/CompilerArgsManager.java @@ -0,0 +1,64 @@ +package com.github.akazver.gradle.plugins.mapstruct.manager; + +import com.github.akazver.gradle.plugins.mapstruct.MapstructExtension; +import com.github.akazver.gradle.plugins.mapstruct.MapstructPluginException; +import lombok.RequiredArgsConstructor; +import org.gradle.api.Project; +import org.gradle.api.tasks.compile.CompileOptions; +import org.gradle.api.tasks.compile.JavaCompile; + +import java.beans.IntrospectionException; +import java.beans.PropertyDescriptor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * Manages correct compiler arguments building + * + * @author Vasiliy Sobolev + */ +@RequiredArgsConstructor +public class CompilerArgsManager { + + private static final String COMPILER_ARG_PATTERN = "-Amapstruct.%s=%s"; + + private final Project project; + + public void addCompilerArgs() { + MapstructExtension extension = project.getExtensions().getByType(MapstructExtension.class); + + CompileOptions compileOptions = project.getTasks() + .withType(JavaCompile.class) + .getByName("compileJava") + .getOptions(); + + Stream projectCompilerArgs = compileOptions.getCompilerArgs().stream(); + + Stream mapstructCompilerArgs = Arrays.stream(MapstructExtension.class.getDeclaredFields()) + .filter(field -> !field.isSynthetic()) + .map(Field::getName) + .map(name -> fetchCompilerArg(extension, name)); + + List compilerArgs = Stream.concat(projectCompilerArgs, mapstructCompilerArgs) + .collect(Collectors.toList()); + + compileOptions.setCompilerArgs(compilerArgs); + } + + private String fetchCompilerArg(MapstructExtension extension, String name) { + try { + PropertyDescriptor descriptor = new PropertyDescriptor(name, MapstructExtension.class); + Object value = descriptor.getReadMethod().invoke(extension); + + return String.format(COMPILER_ARG_PATTERN, name, value); + } catch (IllegalAccessException | InvocationTargetException | IntrospectionException exception) { + String message = String.format("Can't fetch compiler argument for '%s'", name); + throw new MapstructPluginException(message, exception); + } + } + +} diff --git a/src/main/java/com/github/akazver/gradle/plugins/mapstruct/manager/DependencyManager.java b/src/main/java/com/github/akazver/gradle/plugins/mapstruct/manager/DependencyManager.java new file mode 100644 index 0000000..1bc8125 --- /dev/null +++ b/src/main/java/com/github/akazver/gradle/plugins/mapstruct/manager/DependencyManager.java @@ -0,0 +1,131 @@ +package com.github.akazver.gradle.plugins.mapstruct.manager; + +import com.github.akazver.gradle.plugins.mapstruct.dependency.PluginDependency; +import org.gradle.api.Project; +import org.gradle.api.artifacts.ConfigurationContainer; +import org.gradle.api.artifacts.Dependency; +import org.gradle.api.artifacts.dsl.DependencyHandler; +import org.gradle.api.logging.Logger; +import org.gradle.api.logging.Logging; +import org.gradle.api.plugins.ExtensionContainer; + +import static com.github.akazver.gradle.plugins.mapstruct.dependency.AdditionalDependency.*; +import static com.github.akazver.gradle.plugins.mapstruct.dependency.MarkerDependency.*; + +/** + * Manages required and optional dependencies addition + * + * @author Vasiliy Sobolev + */ +public class DependencyManager { + + private static final Logger LOGGER = Logging.getLogger(DependencyManager.class); + private static final String ADDING_MESSAGE = "Adding {} dependencies"; + private static final String DEPENDENCY_PREFIX = "- {}"; + + private final ConfigurationContainer configurations; + private final ExtensionContainer extensions; + private final DependencyHandler dependencies; + + public DependencyManager(Project project) { + this.configurations = project.getConfigurations(); + this.extensions = project.getExtensions(); + this.dependencies = project.getDependencies(); + } + + public void addRequiredDependencies() { + LOGGER.lifecycle(ADDING_MESSAGE, "MapStruct"); + addDependencies(MAPSTRUCT, MAPSTRUCT_PROCESSOR); + } + + public void addOptionalDependencies() { + addLombokBinding(); + addSpringExtensions(); + addCamelExtensions(); + } + + private boolean hasLombok() { + return hasConfiguration("lombok") || hasDependency(LOMBOK); + } + + private boolean hasBinding() { + return hasDependency(LOMBOK_MAPSTRUCT_BINDING); + } + + private void addLombokBinding() { + if (hasLombok() && !hasBinding()) { + LOGGER.lifecycle(ADDING_MESSAGE, "Lombok"); + addDependency(LOMBOK_MAPSTRUCT_BINDING); + } + } + + private boolean hasSpringBoot() { + return hasConfiguration("bootArchives") || hasDependency(SPRING_BOOT); + } + + private boolean hasSpring() { + return hasDependency(SPRING_CORE); + } + + private void addSpringExtensions() { + if (hasSpringBoot() || hasSpring()) { + LOGGER.lifecycle(ADDING_MESSAGE, "Spring"); + addDependencies(MAPSTRUCT_SPRING_EXTENSIONS, MAPSTRUCT_SPRING_ANNOTATIONS, MAPSTRUCT_SPRING_TEST_EXTENSIONS); + } + } + + private boolean hasCamel() { + return hasDependency(CAMEL_CORE); + } + + private boolean hasQuarkus() { + return hasExtension("quarkus") || hasDependency(QUARKUS_CORE); + } + + private void addCamelExtensions() { + if (hasCamel()) { + LOGGER.lifecycle(ADDING_MESSAGE, "Camel"); + + if (hasSpringBoot()) { + addDependency(CAMEL_MAPSTRUCT_STARTER); + } else if (hasQuarkus()) { + addDependency(CAMEL_QUARKUS_MAPSTRUCT); + } else { + addDependency(CAMEL_MAPSTRUCT); + } + } + } + + private boolean isNeededDependency(Dependency dependency, PluginDependency pluginDependency) { + return pluginDependency.getGroup().equals(dependency.getGroup()) + && pluginDependency.getName().equals(dependency.getName()); + } + + private boolean hasDependency(PluginDependency pluginDependency) { + return configurations.getByName(pluginDependency.getConfiguration()) + .getAllDependencies() + .stream() + .anyMatch(dependency -> isNeededDependency(dependency, pluginDependency)); + } + + private boolean hasConfiguration(String configurationName) { + return configurations.findByName(configurationName) != null; + } + + @SuppressWarnings("SameParameterValue") + private boolean hasExtension(String extensionName) { + return extensions.findByName(extensionName) != null; + } + + private void addDependency(PluginDependency pluginDependency) { + LOGGER.lifecycle(DEPENDENCY_PREFIX, pluginDependency.getId()); + dependencies.add(pluginDependency.getConfiguration(), pluginDependency.getId()); + } + + private void addDependencies(PluginDependency... pluginDependencies) { + for (PluginDependency pluginDependency : pluginDependencies) { + addDependency(pluginDependency); + } + } + +} diff --git a/src/test/java/com/github/akazver/gradle/plugins/mapstruct/BaseTest.java b/src/test/java/com/github/akazver/gradle/plugins/mapstruct/BaseTest.java new file mode 100644 index 0000000..8f61dac --- /dev/null +++ b/src/test/java/com/github/akazver/gradle/plugins/mapstruct/BaseTest.java @@ -0,0 +1,57 @@ +package com.github.akazver.gradle.plugins.mapstruct; + +import com.github.akazver.gradle.plugins.mapstruct.dependency.PluginDependency; +import org.gradle.api.Project; +import org.gradle.api.artifacts.dsl.DependencyHandler; +import org.gradle.api.internal.project.DefaultProject; +import org.gradle.api.plugins.JavaPlugin; +import org.gradle.api.plugins.PluginManager; +import org.gradle.testfixtures.ProjectBuilder; +import org.junit.jupiter.api.io.TempDir; + +import java.io.File; + +/** + * Base functionality for other tests + * + * @author Vasiliy Sobolev + */ +abstract class BaseTest { + + @TempDir + private File tempDirectory; + + protected Project fetchProject() { + return evaluate(fetchBaseProject()); + } + + protected Project fetchProject(PluginDependency... pluginDependencies) { + Project project = fetchBaseProject(); + DependencyHandler dependencies = project.getDependencies(); + + for (PluginDependency pluginDependency : pluginDependencies) { + dependencies.add(pluginDependency.getConfiguration(), pluginDependency.getId()); + } + + return evaluate(project); + } + + protected Project fetchBaseProject() { + Project project = ProjectBuilder.builder() + .withProjectDir(tempDirectory) + .withName("test-project") + .build(); + + PluginManager pluginManager = project.getPluginManager(); + pluginManager.apply(JavaPlugin.class); + pluginManager.apply(MapstructPlugin.class); + + return project; + } + + protected Project evaluate(Project project) { + ((DefaultProject) project).evaluate(); + return project; + } + +} diff --git a/src/test/java/com/github/akazver/gradle/plugins/mapstruct/CompilerArgsTest.java b/src/test/java/com/github/akazver/gradle/plugins/mapstruct/CompilerArgsTest.java new file mode 100644 index 0000000..cb3c23a --- /dev/null +++ b/src/test/java/com/github/akazver/gradle/plugins/mapstruct/CompilerArgsTest.java @@ -0,0 +1,112 @@ +package com.github.akazver.gradle.plugins.mapstruct; + +import com.github.akazver.gradle.plugins.mapstruct.manager.CompilerArgsManager; +import org.gradle.api.Project; +import org.gradle.api.internal.plugins.ExtensionContainerInternal; +import org.gradle.api.plugins.ExtensionContainer; +import org.gradle.api.tasks.compile.JavaCompile; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.Mockito.*; + +/** + * Tests for compiler arguments addition + * + * @author Vasiliy Sobolev + */ +class CompilerArgsTest extends BaseTest { + + @Test + @DisplayName("Add compiler arguments with default values success") + void addCompilerArgumentsWithDefaultValuesSuccess() { + Project project = fetchProject(); + + String[] expectedCompilerArgs = { + "-Amapstruct.suppressGeneratorTimestamp=false", + "-Amapstruct.verbose=false", + "-Amapstruct.suppressGeneratorVersionInfoComment=false", + "-Amapstruct.defaultComponentModel=default", + "-Amapstruct.defaultInjectionStrategy=field", + "-Amapstruct.unmappedTargetPolicy=WARN", + "-Amapstruct.unmappedSourcePolicy=WARN", + "-Amapstruct.disableBuilders=false", + "-Amapstruct.nullValueIterableMappingStrategy=RETURN_NULL", + "-Amapstruct.nullValueMapMappingStrategy=RETURN_NULL" + }; + + assertThat(fetchActualCompilerArgs(project)) + .isEqualTo(expectedCompilerArgs); + } + + @Test + @DisplayName("Add compiler arguments with new values success") + void addCompilerArgumentsWithNewValuesSuccess() { + Project project = fetchBaseProject(); + MapstructExtension extension = project.getExtensions().getByType(MapstructExtension.class); + + extension.setSuppressGeneratorTimestamp(true); + extension.setVerbose(true); + extension.setSuppressGeneratorVersionInfoComment(true); + extension.setDefaultComponentModel("spring"); + extension.setDefaultInjectionStrategy("constructor"); + extension.setUnmappedTargetPolicy("ERROR"); + extension.setUnmappedSourcePolicy("ERROR"); + extension.setDisableBuilders(true); + extension.setNullValueIterableMappingStrategy("RETURN_DEFAULT"); + extension.setNullValueMapMappingStrategy("RETURN_DEFAULT"); + + evaluate(project); + + String[] expectedCompilerArgs = { + "-Amapstruct.suppressGeneratorTimestamp=true", + "-Amapstruct.verbose=true", + "-Amapstruct.suppressGeneratorVersionInfoComment=true", + "-Amapstruct.defaultComponentModel=spring", + "-Amapstruct.defaultInjectionStrategy=constructor", + "-Amapstruct.unmappedTargetPolicy=ERROR", + "-Amapstruct.unmappedSourcePolicy=ERROR", + "-Amapstruct.disableBuilders=true", + "-Amapstruct.nullValueIterableMappingStrategy=RETURN_DEFAULT", + "-Amapstruct.nullValueMapMappingStrategy=RETURN_DEFAULT" + }; + + assertThat(fetchActualCompilerArgs(project)) + .isEqualTo(expectedCompilerArgs); + } + + @Test + @DisplayName("Add compiler arguments fail") + void addCompilerArgumentsFail() { + Project project = spy(fetchBaseProject()); + ExtensionContainer extensions = mock(ExtensionContainerInternal.class); + MapstructExtension extension = spy(MapstructExtension.class); + CompilerArgsManager compilerArgsManager = new CompilerArgsManager(project); + + when(project.getExtensions()) + .thenReturn(extensions); + + when(extensions.getByType(MapstructExtension.class)) + .thenReturn(extension); + + when(extension.getVerbose()) + .thenThrow(new RuntimeException("Test exception")); + + assertThatThrownBy(compilerArgsManager::addCompilerArgs) + .isInstanceOf(MapstructPluginException.class) + .extracting(Throwable::getMessage) + .isEqualTo("Can't fetch compiler argument for 'verbose'"); + } + + private String[] fetchActualCompilerArgs(Project project) { + return project.getTasks() + .withType(JavaCompile.class) + .getByName("compileJava") + .getOptions() + .getCompilerArgs() + .toArray(new String[0]); + } + +} diff --git a/src/test/java/com/github/akazver/gradle/plugins/mapstruct/MapstructPluginTest.java b/src/test/java/com/github/akazver/gradle/plugins/mapstruct/DependencyTest.java similarity index 53% rename from src/test/java/com/github/akazver/gradle/plugins/mapstruct/MapstructPluginTest.java rename to src/test/java/com/github/akazver/gradle/plugins/mapstruct/DependencyTest.java index d48c0e4..06d6e24 100644 --- a/src/test/java/com/github/akazver/gradle/plugins/mapstruct/MapstructPluginTest.java +++ b/src/test/java/com/github/akazver/gradle/plugins/mapstruct/DependencyTest.java @@ -1,42 +1,30 @@ package com.github.akazver.gradle.plugins.mapstruct; +import com.github.akazver.gradle.plugins.mapstruct.dependency.PluginDependency; import io.freefair.gradle.plugins.lombok.LombokPlugin; import org.gradle.api.Project; -import org.gradle.api.ProjectConfigurationException; import org.gradle.api.artifacts.Dependency; import org.gradle.api.artifacts.DependencySet; -import org.gradle.api.artifacts.dsl.DependencyHandler; -import org.gradle.api.internal.plugins.ExtensionContainerInternal; -import org.gradle.api.internal.project.DefaultProject; -import org.gradle.api.plugins.ExtensionContainer; -import org.gradle.api.plugins.JavaPlugin; -import org.gradle.api.plugins.PluginManager; -import org.gradle.api.tasks.compile.JavaCompile; -import org.gradle.testfixtures.ProjectBuilder; +import org.gradle.api.plugins.BasePluginExtension; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.io.TempDir; -import java.io.File; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; import java.util.stream.StreamSupport; -import static com.github.akazver.gradle.plugins.mapstruct.PluginDependency.*; +import static com.github.akazver.gradle.plugins.mapstruct.dependency.AdditionalDependency.*; +import static com.github.akazver.gradle.plugins.mapstruct.dependency.MarkerDependency.*; import static org.assertj.core.api.Assertions.*; -import static org.mockito.Mockito.*; /** - * Tests for {@link MapstructPlugin} + * Tests for required and optional dependencies addition * * @author Vasiliy Sobolev */ -class MapstructPluginTest { - - @TempDir - private File tempDirectory; +class DependencyTest extends BaseTest { @Test @DisplayName("Add required dependencies") @@ -57,22 +45,19 @@ void addRequiredDependencies() { .isEqualTo(MAPSTRUCT.getId()); } - private void baseOptionalDependenciesTest(Project project, String[] expectedAnnotationProcessor, String[] expectedImplementation) { - String[] expectedTestImplementation = - Stream.concat(Arrays.stream(expectedImplementation), Stream.of(MAPSTRUCT_SPRING_TEST_EXTENSIONS.getId())) - .toArray(String[]::new); - - assertThat(fetchDependencyIds(project, "annotationProcessor")) - .hasSize(expectedAnnotationProcessor.length) - .containsExactlyInAnyOrder(expectedAnnotationProcessor); - - assertThat(fetchDependencyIds(project, "implementation")) - .hasSize(expectedImplementation.length) - .containsExactlyInAnyOrder(expectedImplementation); + @Test + @DisplayName("Correct plugin dependency") + void correctPluginDependency() { + assertThatCode(() -> new PluginDependency("implementation", "absolutely:not-broken:1.0.0")) + .doesNotThrowAnyException(); + } - assertThat(fetchDependencyIds(project, "testImplementation")) - .hasSize(expectedTestImplementation.length) - .containsExactlyInAnyOrder(expectedTestImplementation); + @Test + @DisplayName("Incorrect plugin dependency") + void incorrectPluginDependency() { + assertThatThrownBy(() -> new PluginDependency("implementation", "broken")) + .isInstanceOf(MapstructPluginException.class) + .hasMessage("Dependency id 'broken' is invalid"); } @Test @@ -90,7 +75,7 @@ void addOptionalDependenciesWithoutBinding() { MAPSTRUCT_SPRING_ANNOTATIONS.getId() }; - baseOptionalDependenciesTest(project, expectedAnnotationProcessor, expectedImplementation); + optionalDependenciesTest(project, expectedAnnotationProcessor, expectedImplementation); } @Test @@ -108,7 +93,7 @@ void addOptionalDependenciesWithBinding() { MAPSTRUCT_SPRING_ANNOTATIONS.getId() }; - baseOptionalDependenciesTest(project, expectedAnnotationProcessor, expectedImplementation); + optionalDependenciesTest(project, expectedAnnotationProcessor, expectedImplementation); } @Test @@ -164,146 +149,129 @@ void addOptionalDependenciesWithSpringBootPlugin() { MAPSTRUCT_SPRING_ANNOTATIONS.getId() }; - baseOptionalDependenciesTest(project, expectedAnnotationProcessor, expectedImplementation); - } - - private String[] fetchActualCompilerArgs(Project project) { - return project.getTasks() - .withType(JavaCompile.class) - .getByName("compileJava") - .getOptions() - .getCompilerArgs() - .toArray(new String[0]); + optionalDependenciesTest(project, expectedAnnotationProcessor, expectedImplementation); } @Test - @DisplayName("Add compiler arguments with default values success") - void addCompilerArgumentsWithDefaultValuesSuccess() { - Project project = fetchProject(); + @DisplayName("Add optional dependencies with Spring Boot") + void addOptionalDependenciesWithSpringBoot() { + Project project = fetchBaseProject(); + project.getDependencies().add(SPRING_BOOT.getConfiguration(), SPRING_BOOT.getId()); + evaluate(project); - String[] expectedCompilerArgs = { - "-Amapstruct.suppressGeneratorTimestamp=false", - "-Amapstruct.verbose=false", - "-Amapstruct.suppressGeneratorVersionInfoComment=false", - "-Amapstruct.defaultComponentModel=default", - "-Amapstruct.defaultInjectionStrategy=field", - "-Amapstruct.unmappedTargetPolicy=WARN", - "-Amapstruct.unmappedSourcePolicy=WARN", - "-Amapstruct.disableBuilders=false", - "-Amapstruct.nullValueIterableMappingStrategy=RETURN_NULL", - "-Amapstruct.nullValueMapMappingStrategy=RETURN_NULL" + String[] expectedAnnotationProcessor = { + MAPSTRUCT_PROCESSOR.getId(), + MAPSTRUCT_SPRING_EXTENSIONS.getId() }; - assertThat(fetchActualCompilerArgs(project)) - .isEqualTo(expectedCompilerArgs); + String[] expectedImplementation = { + SPRING_BOOT.getId(), MAPSTRUCT.getId(), + MAPSTRUCT_SPRING_ANNOTATIONS.getId() + }; + + optionalDependenciesTest(project, expectedAnnotationProcessor, expectedImplementation); } @Test - @DisplayName("Add compiler arguments with new values success") - void addCompilerArgumentsWithNewValuesSuccess() { + @DisplayName("Add optional dependencies with Camel") + void addOptionalDependenciesWithCamel() { Project project = fetchBaseProject(); - MapstructExtension extension = project.getExtensions().getByType(MapstructExtension.class); - - extension.setSuppressGeneratorTimestamp(true); - extension.setVerbose(true); - extension.setSuppressGeneratorVersionInfoComment(true); - extension.setDefaultComponentModel("spring"); - extension.setDefaultInjectionStrategy("constructor"); - extension.setUnmappedTargetPolicy("ERROR"); - extension.setUnmappedSourcePolicy("ERROR"); - extension.setDisableBuilders(true); - extension.setNullValueIterableMappingStrategy("RETURN_DEFAULT"); - extension.setNullValueMapMappingStrategy("RETURN_DEFAULT"); - + project.getDependencies().add(CAMEL_CORE.getConfiguration(), CAMEL_CORE.getId()); evaluate(project); - String[] expectedCompilerArgs = { - "-Amapstruct.suppressGeneratorTimestamp=true", - "-Amapstruct.verbose=true", - "-Amapstruct.suppressGeneratorVersionInfoComment=true", - "-Amapstruct.defaultComponentModel=spring", - "-Amapstruct.defaultInjectionStrategy=constructor", - "-Amapstruct.unmappedTargetPolicy=ERROR", - "-Amapstruct.unmappedSourcePolicy=ERROR", - "-Amapstruct.disableBuilders=true", - "-Amapstruct.nullValueIterableMappingStrategy=RETURN_DEFAULT", - "-Amapstruct.nullValueMapMappingStrategy=RETURN_DEFAULT" + String[] expectedAnnotationProcessor = { + MAPSTRUCT_PROCESSOR.getId() + }; + String[] expectedImplementation = { + CAMEL_CORE.getId(), CAMEL_MAPSTRUCT.getId(), + MAPSTRUCT.getId() }; - assertThat(fetchActualCompilerArgs(project)) - .isEqualTo(expectedCompilerArgs); + optionalDependenciesWithoutSpringTest(project, expectedAnnotationProcessor, expectedImplementation); } @Test - @DisplayName("Add compiler arguments fail") - void addCompilerArgumentsFail() { - Project project = spy(fetchBaseProject()); - ExtensionContainer extensions = mock(ExtensionContainerInternal.class); - MapstructExtension extension = spy(MapstructExtension.class); - - when(project.getExtensions()) - .thenReturn(extensions); - - when(extensions.getByType(MapstructExtension.class)) - .thenReturn(extension); + @DisplayName("Add optional dependencies with Camel and Spring Boot") + void addOptionalDependenciesWithCamelAndSpringBoot() { + Project project = fetchBaseProject(); + project.getDependencies().add(SPRING_BOOT.getConfiguration(), SPRING_BOOT.getId()); + project.getDependencies().add(CAMEL_CORE.getConfiguration(), CAMEL_CORE.getId()); + evaluate(project); - when(extension.getVerbose()) - .thenThrow(new RuntimeException("Test exception")); + String[] expectedAnnotationProcessor = { + MAPSTRUCT_PROCESSOR.getId(), + MAPSTRUCT_SPRING_EXTENSIONS.getId() + }; + String[] expectedImplementation = { + SPRING_BOOT.getId(), MAPSTRUCT.getId(), + CAMEL_CORE.getId(), CAMEL_MAPSTRUCT_STARTER.getId(), + MAPSTRUCT_SPRING_ANNOTATIONS.getId() + }; - assertThatThrownBy(() -> evaluate(project)) - .isInstanceOf(ProjectConfigurationException.class) - .extracting(Throwable::getCause) - .isInstanceOf(MapstructPluginException.class) - .extracting(Throwable::getMessage) - .isEqualTo("Can't fetch compiler argument for 'verbose'"); + optionalDependenciesTest(project, expectedAnnotationProcessor, expectedImplementation); } @Test - @DisplayName("Correct plugin dependency") - void correctPluginDependency() { - assertThatCode(() -> new PluginDependency("implementation", "absolutely:not-broken:1.0.0")) - .doesNotThrowAnyException(); - } + @DisplayName("Add optional dependencies with Camel and Quarkus") + void addOptionalDependenciesWithCamelAndQuarkus() { + Project project = fetchBaseProject(); + project.getDependencies().add(QUARKUS_CORE.getConfiguration(), QUARKUS_CORE.getId()); + project.getDependencies().add(CAMEL_CORE.getConfiguration(), CAMEL_CORE.getId()); + evaluate(project); - @Test - @DisplayName("Incorrect plugin dependency") - void incorrectPluginDependency() { - assertThatThrownBy(() -> new PluginDependency("implementation", "broken")) - .isInstanceOf(MapstructPluginException.class) - .hasMessage("Dependency id 'broken' is invalid"); - } + String[] expectedAnnotationProcessor = { + MAPSTRUCT_PROCESSOR.getId() + }; - private Project fetchProject() { - return evaluate(fetchBaseProject()); + String[] expectedImplementation = { + MAPSTRUCT.getId(), CAMEL_CORE.getId(), + QUARKUS_CORE.getId(), CAMEL_QUARKUS_MAPSTRUCT.getId() + }; + + optionalDependenciesWithoutSpringTest(project, expectedAnnotationProcessor, expectedImplementation); } - private Project fetchProject(PluginDependency... pluginDependencies) { + // Can't use real io.quarkus:io.quarkus.gradle.plugin:3.13.2 dependency because of Java 17 + @Test + @DisplayName("Add optional dependencies with Camel and Quarkus plugin") + void addOptionalDependenciesWithCamelAndQuarkusPlugin() { Project project = fetchBaseProject(); - DependencyHandler dependencies = project.getDependencies(); + project.getExtensions().create("quarkus", BasePluginExtension.class); + project.getDependencies().add(CAMEL_CORE.getConfiguration(), CAMEL_CORE.getId()); + evaluate(project); + + String[] expectedAnnotationProcessor = { + MAPSTRUCT_PROCESSOR.getId() + }; - for (PluginDependency pluginDependency : pluginDependencies) { - dependencies.add(pluginDependency.getConfiguration(), pluginDependency.getId()); - } + String[] expectedImplementation = { + MAPSTRUCT.getId(), CAMEL_CORE.getId(), + CAMEL_QUARKUS_MAPSTRUCT.getId() + }; - return evaluate(project); + optionalDependenciesWithoutSpringTest(project, expectedAnnotationProcessor, expectedImplementation); } - private Project fetchBaseProject() { - Project project = ProjectBuilder.builder() - .withProjectDir(tempDirectory) - .withName("test-project") - .build(); + private void optionalDependenciesTest(Project project, String[] expectedAnnotationProcessor, String[] expectedImplementation) { + String[] expectedTestImplementation = + Stream.concat(Arrays.stream(expectedImplementation), Stream.of(MAPSTRUCT_SPRING_TEST_EXTENSIONS.getId())) + .toArray(String[]::new); - PluginManager pluginManager = project.getPluginManager(); - pluginManager.apply(JavaPlugin.class); - pluginManager.apply(MapstructPlugin.class); + optionalDependenciesWithoutSpringTest(project, expectedAnnotationProcessor, expectedImplementation); - return project; + assertThat(fetchDependencyIds(project, "testImplementation")) + .hasSize(expectedTestImplementation.length) + .containsExactlyInAnyOrder(expectedTestImplementation); } - private Project evaluate(Project project) { - ((DefaultProject) project).evaluate(); - return project; + private void optionalDependenciesWithoutSpringTest(Project project, String[] expectedAnnotationProcessor, String[] expectedImplementation) { + assertThat(fetchDependencyIds(project, "annotationProcessor")) + .hasSize(expectedAnnotationProcessor.length) + .containsExactlyInAnyOrder(expectedAnnotationProcessor); + + assertThat(fetchDependencyIds(project, "implementation")) + .hasSize(expectedImplementation.length) + .containsExactlyInAnyOrder(expectedImplementation); } private List fetchDependencyIds(Project project, String name) {