From d71a1e77ab4da85a2cf74a78830d89f18ff70a6e Mon Sep 17 00:00:00 2001 From: Sergio del Amo Date: Mon, 17 Feb 2025 18:29:09 +0100 Subject: [PATCH] example of feature contributing configuration and dependency --- gradle/libs.versions.toml | 1 + .../starter/application/ContextFactory.java | 8 +++- .../generator/GeneratorContext.java | 15 +++++- .../starter/feature/migration/Liquibase.java | 13 +++--- .../io/micronaut/starter/BuildBuilder.groovy | 3 +- .../starter/feature/FeatureSpec.groovy | 8 +++- starter-openrewrite-recipes/build.gradle.kts | 1 + .../DefaultRecipePropertiesFetcher.java | 46 +++++++++++++++++++ .../openrewrite/RecipePropertiesFetcher.java | 26 +++++++++++ .../resources/META-INF/rewrite/rewrite.yml | 13 ++++++ .../RecipePropertiesFetcherTest.java | 20 ++++++++ 11 files changed, 142 insertions(+), 12 deletions(-) create mode 100644 starter-openrewrite-recipes/src/main/java/io/micronaut/starter/openrewrite/DefaultRecipePropertiesFetcher.java create mode 100644 starter-openrewrite-recipes/src/main/java/io/micronaut/starter/openrewrite/RecipePropertiesFetcher.java create mode 100644 starter-openrewrite-recipes/src/test/java/io/micronaut/starter/openrewrite/RecipePropertiesFetcherTest.java diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 8c370be5e4..004edb39dd 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -4,6 +4,7 @@ rewrite-recipe-bom = "3.2.0" [libraries] rewrite-recipe-bom = { module = "org.openrewrite.recipe:rewrite-recipe-bom", version.ref = "rewrite-recipe-bom" } rewrite-core = { module = "org.openrewrite:rewrite-core" } +rewrite-properties = { module = "org.openrewrite:rewrite-properties" } rewrite-gradle = { module = "org.openrewrite:rewrite-gradle" } rewrite-java-dependencies = { module = "org.openrewrite.recipe:rewrite-java-dependencies" } rewrite-maven = { module = "org.openrewrite:rewrite-maven" } diff --git a/starter-core/src/main/java/io/micronaut/starter/application/ContextFactory.java b/starter-core/src/main/java/io/micronaut/starter/application/ContextFactory.java index 009eb2f20d..fbef96133b 100644 --- a/starter-core/src/main/java/io/micronaut/starter/application/ContextFactory.java +++ b/starter-core/src/main/java/io/micronaut/starter/application/ContextFactory.java @@ -26,6 +26,7 @@ import io.micronaut.starter.feature.validation.ProjectNameValidator; import io.micronaut.starter.io.ConsoleOutput; import io.micronaut.starter.openrewrite.RecipeDependencyFetcher; +import io.micronaut.starter.openrewrite.RecipePropertiesFetcher; import io.micronaut.starter.sdk.BuildTool; import io.micronaut.starter.options.Language; import io.micronaut.starter.options.Options; @@ -46,15 +47,18 @@ public class ContextFactory { private final DefaultCoordinateResolver coordinateResolver; private final RecipeDependencyFetcher recipeDependencyFetcher; + private final RecipePropertiesFetcher recipePropertiesFetcher; public ContextFactory(FeatureValidator featureValidator, DefaultCoordinateResolver coordinateResolver, ProjectNameValidator projectNameValidator, - RecipeDependencyFetcher recipeDependencyFetcher) { + RecipeDependencyFetcher recipeDependencyFetcher, + RecipePropertiesFetcher recipePropertiesFetcher) { this.featureValidator = featureValidator; this.coordinateResolver = coordinateResolver; this.projectNameValidator = projectNameValidator; this.recipeDependencyFetcher = recipeDependencyFetcher; + this.recipePropertiesFetcher = recipePropertiesFetcher; } public FeatureContext createFeatureContext(AvailableFeatures availableFeatures, @@ -97,7 +101,7 @@ public GeneratorContext createGeneratorContext(Project project, featureValidator.validatePostProcessing(featureContext.getOptions(), featureContext.getApplicationType(), featureList); - return new GeneratorContext(project, featureContext.getApplicationType(), featureContext.getOptions(), featureContext.getOperatingSystem(), featureList, coordinateResolver, recipeDependencyFetcher); + return new GeneratorContext(project, featureContext.getApplicationType(), featureContext.getOptions(), featureContext.getOperatingSystem(), featureList, coordinateResolver, recipeDependencyFetcher, recipePropertiesFetcher); } Language determineLanguage(Language language, Set features) { diff --git a/starter-core/src/main/java/io/micronaut/starter/application/generator/GeneratorContext.java b/starter-core/src/main/java/io/micronaut/starter/application/generator/GeneratorContext.java index 0d56d2d6bd..12f5831f4d 100644 --- a/starter-core/src/main/java/io/micronaut/starter/application/generator/GeneratorContext.java +++ b/starter-core/src/main/java/io/micronaut/starter/application/generator/GeneratorContext.java @@ -31,6 +31,7 @@ import io.micronaut.starter.feature.config.Configuration; import io.micronaut.starter.feature.other.template.markdownLink; import io.micronaut.starter.openrewrite.RecipeDependencyFetcher; +import io.micronaut.starter.openrewrite.RecipePropertiesFetcher; import io.micronaut.starter.sdk.BuildTool; import io.micronaut.starter.options.JdkVersion; import io.micronaut.starter.options.Language; @@ -87,6 +88,7 @@ public class GeneratorContext implements DependencyContext { private final Set profiles = new HashSet<>(); private final Set buildPlugins = new HashSet<>(); private final RecipeDependencyFetcher recipeDependencyFetcher; + private final RecipePropertiesFetcher recipePropertiesFetcher; public GeneratorContext(Project project, ApplicationType type, @@ -94,7 +96,8 @@ public GeneratorContext(Project project, @Nullable OperatingSystem operatingSystem, Set features, CoordinateResolver coordinateResolver, - RecipeDependencyFetcher recipeDependencyFetcher) { + RecipeDependencyFetcher recipeDependencyFetcher, + RecipePropertiesFetcher recipePropertiesFetcher) { this.command = type; this.project = project; this.operatingSystem = operatingSystem; @@ -111,6 +114,7 @@ public GeneratorContext(Project project, this.coordinateResolver = coordinateResolver; this.dependencyContext = new DependencyContextImpl(coordinateResolver); this.recipeDependencyFetcher = recipeDependencyFetcher; + this.recipePropertiesFetcher = recipePropertiesFetcher; } /** @@ -460,4 +464,13 @@ public void addDependenciesByRecipeName(String recipeName) { } } } + + public void addConfigurationByRecipeName(@NonNull String recipeName) { + Configuration config = getConfiguration(); + recipePropertiesFetcher.findPropertiesByRecipeName(recipeName).ifPresent(properties -> { + for (Map.Entry entry : properties.entrySet()) { + config.addNested(entry.getKey().toString(), entry.getValue()); + } + }); + } } diff --git a/starter-core/src/main/java/io/micronaut/starter/feature/migration/Liquibase.java b/starter-core/src/main/java/io/micronaut/starter/feature/migration/Liquibase.java index 065d165947..9f3dc3167b 100644 --- a/starter-core/src/main/java/io/micronaut/starter/feature/migration/Liquibase.java +++ b/starter-core/src/main/java/io/micronaut/starter/feature/migration/Liquibase.java @@ -18,6 +18,7 @@ import io.micronaut.context.annotation.Requires; import io.micronaut.core.util.StringUtils; import io.micronaut.starter.application.generator.GeneratorContext; +import io.micronaut.starter.openrewrite.OpenRewriteFeature; import io.micronaut.starter.sdk.dependency.Dependency; import io.micronaut.starter.feature.FeatureContext; import io.micronaut.starter.feature.logging.LiquibaseSlf4j; @@ -29,7 +30,7 @@ @Requires(property = "micronaut.starter.feature.liquibase.enabled", value = StringUtils.TRUE, defaultValue = StringUtils.TRUE) @Singleton -public class Liquibase implements MigrationFeature { +public class Liquibase implements MigrationFeature, OpenRewriteFeature { public static final String NAME = "liquibase"; @@ -74,11 +75,9 @@ public void apply(GeneratorContext generatorContext) { liquibaseChangelog.template())); generatorContext.addTemplate("liquibaseSchema", new RockerTemplate("src/main/resources/db/changelog/01-schema.xml", liquibaseSchema.template())); - generatorContext.addDependency(Dependency.builder() - .groupId("io.micronaut.liquibase") - .artifactId("micronaut-liquibase") - .compile()); - generatorContext.getConfiguration().addNested( - "liquibase.datasources.default.change-log", "classpath:db/liquibase-changelog.xml"); + + generatorContext.addDependenciesByRecipeName(getRecipeName()); + generatorContext.addConfigurationByRecipeName(getRecipeName()); + } } diff --git a/starter-core/src/test/groovy/io/micronaut/starter/BuildBuilder.groovy b/starter-core/src/test/groovy/io/micronaut/starter/BuildBuilder.groovy index 156faf2882..508a4e3177 100644 --- a/starter-core/src/test/groovy/io/micronaut/starter/BuildBuilder.groovy +++ b/starter-core/src/test/groovy/io/micronaut/starter/BuildBuilder.groovy @@ -19,6 +19,7 @@ import io.micronaut.starter.feature.build.maven.templates.pom import io.micronaut.starter.fixture.ContextFixture import io.micronaut.starter.fixture.ProjectFixture import io.micronaut.starter.openrewrite.RecipeDependencyFetcher +import io.micronaut.starter.openrewrite.RecipePropertiesFetcher import io.micronaut.starter.options.* import io.micronaut.starter.sdk.BuildTool import io.micronaut.starter.sdk.Project @@ -139,7 +140,7 @@ class BuildBuilder implements ProjectFixture, ContextFixture { } GeneratorContext createGeneratorContextAndApplyFeatures(Options options, Features features, Project project, ApplicationType type) { - GeneratorContext ctx = new GeneratorContext(project, type, options, null, features.features, ctx.getBean(CoordinateResolver), ctx.getBean(RecipeDependencyFetcher)) + GeneratorContext ctx = new GeneratorContext(project, type, options, null, features.features, ctx.getBean(CoordinateResolver), ctx.getBean(RecipeDependencyFetcher), ctx.getBean(RecipePropertiesFetcher)) ctx.applyFeatures() ctx } diff --git a/starter-core/src/test/groovy/io/micronaut/starter/feature/FeatureSpec.groovy b/starter-core/src/test/groovy/io/micronaut/starter/feature/FeatureSpec.groovy index 68dc58ee3d..95b1fe0ebe 100644 --- a/starter-core/src/test/groovy/io/micronaut/starter/feature/FeatureSpec.groovy +++ b/starter-core/src/test/groovy/io/micronaut/starter/feature/FeatureSpec.groovy @@ -6,6 +6,7 @@ import io.micronaut.starter.application.ApplicationType import io.micronaut.starter.application.OperatingSystem import io.micronaut.starter.application.generator.GeneratorContext import io.micronaut.starter.openrewrite.RecipeDependencyFetcher +import io.micronaut.starter.openrewrite.RecipePropertiesFetcher import io.micronaut.starter.sdk.BuildTool import io.micronaut.starter.sdk.dependency.Dependency import io.micronaut.starter.sdk.dependency.DependencyCoordinate @@ -90,7 +91,12 @@ class FeatureSpec extends BeanContextSpec { List findAllByRecipeNameAndBuildTool(@NonNull String recipe, @NonNull BuildTool b) { return Collections.emptyList() } - } + }, new RecipePropertiesFetcher() { + @Override + Optional findPropertiesByRecipeName(@NonNull String recipe) { + return Optional.empty() + } + } ) commandCtx.applyFeatures() diff --git a/starter-openrewrite-recipes/build.gradle.kts b/starter-openrewrite-recipes/build.gradle.kts index 7d46f96cc2..5c1162b000 100644 --- a/starter-openrewrite-recipes/build.gradle.kts +++ b/starter-openrewrite-recipes/build.gradle.kts @@ -25,6 +25,7 @@ dependencies { implementation(libs.rewrite.java.dependencies) { exclude(group = "org.openrewrite", module = "rewrite-groovy") } + implementation(libs.rewrite.properties) testAnnotationProcessor(platform("io.micronaut.platform:micronaut-platform:${micronautVersion}")) testAnnotationProcessor("io.micronaut:micronaut-inject-java") testImplementation(platform("io.micronaut.platform:micronaut-platform:${micronautVersion}")) diff --git a/starter-openrewrite-recipes/src/main/java/io/micronaut/starter/openrewrite/DefaultRecipePropertiesFetcher.java b/starter-openrewrite-recipes/src/main/java/io/micronaut/starter/openrewrite/DefaultRecipePropertiesFetcher.java new file mode 100644 index 0000000000..c0248613b5 --- /dev/null +++ b/starter-openrewrite-recipes/src/main/java/io/micronaut/starter/openrewrite/DefaultRecipePropertiesFetcher.java @@ -0,0 +1,46 @@ +package io.micronaut.starter.openrewrite; + +import io.micronaut.context.exceptions.ConfigurationException; +import io.micronaut.core.annotation.NonNull; +import jakarta.inject.Singleton; +import org.openrewrite.Recipe; +import org.openrewrite.RecipeException; +import org.openrewrite.config.Environment; + +import java.util.Optional; +import java.util.Properties; + +import org.openrewrite.properties.AddProperty; + +@Singleton +public class DefaultRecipePropertiesFetcher implements RecipePropertiesFetcher { + private final Environment env; + + public DefaultRecipePropertiesFetcher(Environment env) { + this.env = env; + } + + @Override + @NonNull + public Optional findPropertiesByRecipeName(@NonNull String recipeName) { + try { + var recipe = env.activateRecipes(recipeName); + return findProperties(recipe); + } catch (RecipeException e) { + throw new ConfigurationException("Error activating recipe: " + recipeName, e); + } + } + + private @NonNull Optional findProperties(@NonNull Recipe recipe) { + Properties properties = new Properties(); + for (Recipe r : recipe.getRecipeList()) { + if (r instanceof AddProperty addProperty) { + properties.put(addProperty.getProperty(), addProperty.getValue()); + } + } + if (properties.isEmpty()) { + return Optional.empty(); + } + return Optional.of(properties); + } +} diff --git a/starter-openrewrite-recipes/src/main/java/io/micronaut/starter/openrewrite/RecipePropertiesFetcher.java b/starter-openrewrite-recipes/src/main/java/io/micronaut/starter/openrewrite/RecipePropertiesFetcher.java new file mode 100644 index 0000000000..828e18af4b --- /dev/null +++ b/starter-openrewrite-recipes/src/main/java/io/micronaut/starter/openrewrite/RecipePropertiesFetcher.java @@ -0,0 +1,26 @@ +/* + * Copyright 2017-2022 original authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.micronaut.starter.openrewrite; + +import io.micronaut.core.annotation.NonNull; +import java.util.Optional; +import java.util.Properties; + +public interface RecipePropertiesFetcher { + + @NonNull + Optional findPropertiesByRecipeName(@NonNull String recipe); +} diff --git a/starter-openrewrite-recipes/src/main/resources/META-INF/rewrite/rewrite.yml b/starter-openrewrite-recipes/src/main/resources/META-INF/rewrite/rewrite.yml index 8fe1fe4134..1227316504 100644 --- a/starter-openrewrite-recipes/src/main/resources/META-INF/rewrite/rewrite.yml +++ b/starter-openrewrite-recipes/src/main/resources/META-INF/rewrite/rewrite.yml @@ -1,5 +1,18 @@ --- type: specs.openrewrite.org/v1beta/recipe +name: io.micronaut.starter.feature.liquibase +displayName: Adds Micronaut liquibase dependency in the compile classpath +recipeList: + - org.openrewrite.java.dependencies.AddDependency: + groupId: io.micronaut.liquibase + artifactId: micronaut-liquibase + configuration: implementation + scope: compile + - org.openrewrite.properties.AddProperty: #TODO create a recipe to add a property to a Micronaut configuration file + property: liquibase.datasources.default.change-log + value: classpath:db/liquibase-changelog.xml +--- +type: specs.openrewrite.org/v1beta/recipe name: io.micronaut.starter.feature.mockito displayName: Add Mockito dependency in the test configuration recipeList: diff --git a/starter-openrewrite-recipes/src/test/java/io/micronaut/starter/openrewrite/RecipePropertiesFetcherTest.java b/starter-openrewrite-recipes/src/test/java/io/micronaut/starter/openrewrite/RecipePropertiesFetcherTest.java new file mode 100644 index 0000000000..5907d97211 --- /dev/null +++ b/starter-openrewrite-recipes/src/test/java/io/micronaut/starter/openrewrite/RecipePropertiesFetcherTest.java @@ -0,0 +1,20 @@ +package io.micronaut.starter.openrewrite; + +import io.micronaut.test.extensions.junit5.annotation.MicronautTest; +import org.junit.jupiter.api.Test; +import java.util.Optional; +import java.util.Properties; + +import static org.junit.jupiter.api.Assertions.*; + +@MicronautTest(startApplication = false) +class RecipePropertiesFetcherTest { + private static final String NAME = "io.micronaut.starter.feature.liquibase"; + @Test + void testFetchProperties(RecipePropertiesFetcher fetcher) { + Optional propertiesOptional = fetcher.findPropertiesByRecipeName(NAME); + assertTrue(propertiesOptional.isPresent()); + Properties properties = propertiesOptional.get(); + assertEquals("classpath:db/liquibase-changelog.xml", properties.get("liquibase.datasources.default.change-log")); + } +} \ No newline at end of file