Skip to content

Commit

Permalink
Add add-feature command
Browse files Browse the repository at this point in the history
  • Loading branch information
sdelamo committed Feb 14, 2025
1 parent b5c733b commit d4b4028
Show file tree
Hide file tree
Showing 13 changed files with 245 additions and 17 deletions.
5 changes: 5 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,9 @@ 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-gradle = { module = "org.openrewrite:rewrite-gradle" }
rewrite-java-dependencies = { module = "org.openrewrite.recipe:rewrite-java-dependencies" }
rewrite-maven = { module = "org.openrewrite:rewrite-maven" }
rewrite-java = { module = "org.openrewrite:rewrite-java" }

Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
* 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.cli.command;

import io.micronaut.context.annotation.Prototype;
import io.micronaut.core.annotation.ReflectiveAccess;
import io.micronaut.starter.feature.AvailableFeatures;
import io.micronaut.starter.openrewrite.OpenRewriteAvailableFeatures;
import io.micronaut.starter.openrewrite.OpenRewriteFeature;
import picocli.CommandLine;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;

@CommandLine.Command(name = AddFeatureCommand.NAME, description = "Modifies an existing application by adding features (dependencies, configuration, etc.)")
@Prototype
public class AddFeatureCommand extends BaseCommand implements Callable<Integer> {

public static final String NAME = "add-feature";

@CommandLine.Option(names = {"-f", "--features"}, paramLabel = "FEATURE", split = ",",
description = "The features to apply. Possible values: ${COMPLETION-CANDIDATES}",
completionCandidates = OpenRewriteAvailableFeatures.class)
@ReflectiveAccess
protected List<String> features = new ArrayList<>();

protected final AvailableFeatures availableFeatures;

public AddFeatureCommand(OpenRewriteAvailableFeatures availableFeatures) {
this.availableFeatures = availableFeatures;
}

@Override
public Integer call() throws Exception {
List<String> recipeNames = new ArrayList<>();
for (String feature : features) {
recipeNames.add(availableFeatures.findFeature(feature)
.filter(OpenRewriteFeature.class::isInstance)
.map(f -> ((OpenRewriteFeature) f).getRecipeName())
.orElseThrow(() -> new CommandLine.ParameterException(spec.commandLine(), "Feature [" + feature + "] is not supported by the " + NAME + " command")));
}
return 0;
}

void applyOpenRewriteRecipes(List<String> recipeNames) {
//TODO apply OpenRewrite Recipes
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package io.micronaut.starter.cli.command

import io.micronaut.configuration.picocli.PicocliRunner
import io.micronaut.context.ApplicationContext
import io.micronaut.context.env.Environment
import spock.lang.AutoCleanup
import spock.lang.Shared
import spock.lang.Specification

class AddFeatureCommandSpec extends Specification {

@Shared
@AutoCleanup
ApplicationContext ctx = ApplicationContext.run(Environment.CLI)

void "test arm is not an OpenRewriteFeature"() {
given:
ByteArrayOutputStream baos = new ByteArrayOutputStream()
System.setErr(new PrintStream(baos))

when:
PicocliRunner.run(AddFeatureCommand, ctx, "--features", "arm")

then:
noExceptionThrown()
baos.toString().contains("Feature [arm] is not supported by the add-feature command")
}

void "apply mockito OpenRewriteFeature"() {
given:
ByteArrayOutputStream baos = new ByteArrayOutputStream()
System.setErr(new PrintStream(baos))

when:
PicocliRunner.run(AddFeatureCommand, ctx, "--features", "mockito")

then:
noExceptionThrown()
!baos.toString().contains("Feature [mockitoG] is not supported by the add-feature command")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package io.micronaut.starter.feature;

import io.micronaut.core.annotation.NonNull;
import io.micronaut.core.annotation.Nullable;
import io.micronaut.starter.application.ApplicationType;

import java.util.Iterator;
Expand All @@ -24,15 +25,24 @@
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class BaseAvailableFeatures implements AvailableFeatures {
private final Map<String, Feature> features;

public BaseAvailableFeatures(List<Feature> features, ApplicationType applicationType) {
this(features, f -> f.supports(applicationType));
}

public BaseAvailableFeatures(List<Feature> features) {
this(features, f -> true);
}

public BaseAvailableFeatures(List<Feature> features, @Nullable Predicate<Feature> predicate) {
this.features = features.stream()
.filter(f -> f.supports(applicationType))
.filter(f -> predicate == null || predicate.test(f))
.collect(Collectors.toMap(
Feature::getName,
Function.identity(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,13 @@
import io.micronaut.core.annotation.NonNull;
import io.micronaut.core.util.StringUtils;
import io.micronaut.starter.application.generator.GeneratorContext;
import io.micronaut.starter.openrewrite.OpenRewriteFeature;
import jakarta.inject.Singleton;

@Requires(property = "micronaut.starter.feature.mockito.enabled", value = StringUtils.TRUE, defaultValue = StringUtils.TRUE)
@Singleton
public class Mockito implements MockingFeature, JunitCompanionFeature {
private static final String RECIPE_ADD_DEPENDENCY_MOCKITO = "micronaut.starter.feature.mockito.AddDependencyMockito";
public class Mockito implements OpenRewriteFeature, MockingFeature, JunitCompanionFeature {
private static final String RECIPE_ADD_DEPENDENCY_MOCKITO = "micronaut.starter.feature.Mockito";

@Override
@NonNull
Expand All @@ -50,6 +51,11 @@ public String getThirdPartyDocumentation() {

@Override
public void apply(GeneratorContext generatorContext) {
generatorContext.addDependenciesByRecipeName(RECIPE_ADD_DEPENDENCY_MOCKITO);
generatorContext.addDependenciesByRecipeName(getRecipeName());
}

@Override
public String getRecipeName() {
return RECIPE_ADD_DEPENDENCY_MOCKITO;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* 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.starter.feature.BaseAvailableFeatures;
import io.micronaut.starter.feature.Feature;
import jakarta.inject.Singleton;
import java.util.List;

@Singleton
public class OpenRewriteAvailableFeatures extends BaseAvailableFeatures {

public OpenRewriteAvailableFeatures(List<Feature> features) {
super(features.stream()
.filter(OpenRewriteFeature.class::isInstance)
.toList());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* 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 io.micronaut.starter.feature.Feature;

/**
* A feature backed by an OpenRewrite recipe.
* @author Sergio del Amo
*/
public interface OpenRewriteFeature extends Feature {

@NonNull
String getRecipeName();
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package io.micronaut.starter.feature.test
import io.micronaut.starter.ApplicationContextSpec
import io.micronaut.starter.BuildBuilder
import io.micronaut.starter.feature.Category
import io.micronaut.starter.openrewrite.OpenRewriteFeature
import io.micronaut.starter.fixture.CommandOutputFixture
import io.micronaut.starter.sdk.BuildTool
import io.micronaut.starter.options.Language
Expand All @@ -16,6 +17,12 @@ class MockitoSpec extends ApplicationContextSpec implements CommandOutputFixture
@Subject
Mockito mockito = beanContext.getBean(Mockito)

void "mockito is OpenRewriteFeature"() {
expect:
mockito instanceof OpenRewriteFeature
mockito.recipeName
}

void 'test readme.md with feature mockito contains links to 3rd party docs'() {
when:
Map<String, String> output = generate(['mockito'])
Expand Down
27 changes: 24 additions & 3 deletions starter-openrewrite-recipes/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
plugins {
id("io.micronaut.internal.starter.published-module")
`java-library`
}
val micronautVersion: String by project
repositories {
mavenCentral()
maven {
url = uri("https://s01.oss.sonatype.org/content/repositories/snapshots/")
mavenContent {
snapshotsOnly()
}
}
}
dependencies {
annotationProcessor(platform("io.micronaut.platform:micronaut-platform:${micronautVersion}"))
Expand All @@ -13,12 +19,27 @@ dependencies {
api(project(":starter-sdk"))
implementation("io.micronaut:micronaut-http")
implementation(platform(libs.rewrite.recipe.bom))
implementation(libs.rewrite.core)
implementation(libs.rewrite.maven)
implementation(libs.rewrite.gradle)

implementation(libs.rewrite.java.dependencies) {
exclude(group = "org.openrewrite", module = "rewrite-groovy")
}
testAnnotationProcessor(platform("io.micronaut.platform:micronaut-platform:${micronautVersion}"))
testAnnotationProcessor("io.micronaut:micronaut-inject-java")
testImplementation(platform("io.micronaut.platform:micronaut-platform:${micronautVersion}"))

testImplementation("io.micronaut.test:micronaut-test-junit5")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine")
}
tasks.withType<Test> {
useJUnitPlatform()
}
java {
sourceCompatibility = JavaVersion.toVersion("17")
targetCompatibility = JavaVersion.toVersion("17")
}
java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ public List<Dependency> findAllByRecipeNameAndBuildTool(@NonNull String recipeNa
private static List<Dependency> findDependencies(Recipe recipe, BuildTool buildTool) {
List<Dependency> dependencies = new ArrayList<>();
for (Recipe r : recipe.getRecipeList()) {
findDependency(r).ifPresent(dependencies::add);
if (buildTool.isGradle()) {
findGradleDependency(r).ifPresent(dependencies::add);
} else if (buildTool == BuildTool.MAVEN) {
Expand All @@ -60,7 +61,27 @@ private static List<Dependency> findDependencies(Recipe recipe, BuildTool buildT
}
return dependencies;
}
private static Optional<Dependency> findDependency(Recipe recipe) {
if (recipe instanceof org.openrewrite.java.dependencies.AddDependency addDependency) {
Dependency.Builder builder = Dependency.builder()
.groupId(addDependency.getGroupId())
.artifactId(addDependency.getArtifactId());
if (StringUtils.isNotEmpty(addDependency.getVersion())) {
builder.version(addDependency.getVersion());
}
String scope = addDependency.getScope();
if (scope != null) {
ofMavenScope(scope).ifPresent(builder::scope);
}
String configuration = addDependency.getConfiguration();
if (configuration != null) {
ofGradleConfiguration(configuration).ifPresent(builder::scope);
}

return Optional.of(builder.build());
}
return Optional.empty();
}
private static Optional<Dependency> findGradleDependency(Recipe recipe) {
if (recipe instanceof org.openrewrite.gradle.AddDependency addDependency) {
Dependency.Builder builder = Dependency.builder()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
---
type: specs.openrewrite.org/v1beta/recipe
name: micronaut.starter.feature.mockito.AddDependencyMockito
name: micronaut.starter.feature.Mockito
displayName: Add Mockito dependency in the test configuration
recipeList:
- org.openrewrite.gradle.AddDependency:
- org.openrewrite.java.dependencies.AddDependency:
groupId: org.mockito
artifactId: mockito-core
configuration: testImplementation
- org.openrewrite.maven.AddDependency:
groupId: org.mockito
artifactId: mockito-core
scope: test
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,13 @@
import io.micronaut.starter.sdk.dependency.Scope;
import io.micronaut.test.extensions.junit5.annotation.MicronautTest;
import org.junit.jupiter.api.Test;
import spock.lang.Specification;

import java.util.List;

import static org.junit.jupiter.api.Assertions.assertEquals;

@MicronautTest(startApplication = false)
class RecipeDependencyFetcherTest extends Specification {
private static final String NAME = "micronaut.starter.feature.mockito.AddDependencyMockito";
class RecipeDependencyFetcherTest {
private static final String NAME = "micronaut.starter.feature.Mockito";

@Test
void testFetchDependencies(RecipeDependencyFetcher fetcher) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
@MicronautTest(startApplication = false)
class ResourceLoaderFactoryTest {

private static final String NAME = "micronaut.starter.feature.mockito.AddDependencyMockito";
private static final String NAME = "micronaut.starter.feature.Mockito";

@Test
void loadRecipes(Environment env) {
Expand Down

0 comments on commit d4b4028

Please sign in to comment.