From 9493760bd57292f18157683429dc483c768cecfe Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Sat, 30 Sep 2023 15:42:27 +0200 Subject: [PATCH] Maven parent POM insight through data table (#3583) --- .../maven/search/ParentPomInsight.java | 88 ++++++++ .../maven/table/ParentPomsInUse.java | 64 ++++++ .../maven/search/ParentPomInsightTest.java | 205 ++++++++++++++++++ 3 files changed, 357 insertions(+) create mode 100644 rewrite-maven/src/main/java/org/openrewrite/maven/search/ParentPomInsight.java create mode 100644 rewrite-maven/src/main/java/org/openrewrite/maven/table/ParentPomsInUse.java create mode 100644 rewrite-maven/src/test/java/org/openrewrite/maven/search/ParentPomInsightTest.java diff --git a/rewrite-maven/src/main/java/org/openrewrite/maven/search/ParentPomInsight.java b/rewrite-maven/src/main/java/org/openrewrite/maven/search/ParentPomInsight.java new file mode 100644 index 00000000000..ac90b23414b --- /dev/null +++ b/rewrite-maven/src/main/java/org/openrewrite/maven/search/ParentPomInsight.java @@ -0,0 +1,88 @@ +/* + * Copyright 2023 the original author or 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 org.openrewrite.maven.search; + +import lombok.EqualsAndHashCode; +import lombok.Value; +import org.openrewrite.ExecutionContext; +import org.openrewrite.Option; +import org.openrewrite.Recipe; +import org.openrewrite.TreeVisitor; +import org.openrewrite.java.marker.JavaProject; +import org.openrewrite.marker.SearchResult; +import org.openrewrite.maven.MavenIsoVisitor; +import org.openrewrite.maven.table.ParentPomsInUse; +import org.openrewrite.maven.tree.ResolvedPom; +import org.openrewrite.xml.tree.Xml; + +import java.util.UUID; + +import static org.openrewrite.Tree.randomId; +import static org.openrewrite.internal.StringUtils.matchesGlob; + +@EqualsAndHashCode(callSuper = true) +@Value +public class ParentPomInsight extends Recipe { + transient ParentPomsInUse inUse = new ParentPomsInUse(this); + + @Option(displayName = "Group pattern", + description = "Group glob pattern used to match dependencies.", + example = "org.springframework.boot") + String groupIdPattern; + + @Option(displayName = "Artifact pattern", + description = "Artifact glob pattern used to match dependencies.", + example = "spring-boot-starter-*") + String artifactIdPattern; + + UUID searchId = randomId(); + + @Override + public String getDisplayName() { + return "Maven parent insight"; + } + + @Override + public String getDescription() { + return "Find Maven parents matching a `groupId` and `artifactId`."; + } + + @Override + public TreeVisitor getVisitor() { + return new MavenIsoVisitor() { + @Override + public Xml.Tag visitTag(Xml.Tag tag, ExecutionContext ctx) { + Xml.Tag t = super.visitTag(tag, ctx); + if (isParentTag()) { + ResolvedPom resolvedPom = getResolutionResult().getPom(); + String groupId = resolvedPom.getValue(tag.getChildValue("groupId").orElse(null)); + String artifactId = resolvedPom.getValue(tag.getChildValue("artifactId").orElse(null)); + if (matchesGlob(groupId, groupIdPattern) && matchesGlob(artifactId, artifactIdPattern)) { + String version = resolvedPom.getValue(tag.getChildValue("version").orElse(null)); + String projectName = getCursor().firstEnclosingOrThrow(Xml.Document.class) + .getMarkers().findFirst(JavaProject.class) + .map(JavaProject::getProjectName).orElse(""); + String relativePath = tag.getChildValue("relativePath").orElse(null); + inUse.insertRow(ctx, new ParentPomsInUse.Row( + projectName, groupId, artifactId, version, resolvedPom.getDatedSnapshotVersion(), relativePath)); + return SearchResult.found(t); + } + } + return t; + } + }; + } +} diff --git a/rewrite-maven/src/main/java/org/openrewrite/maven/table/ParentPomsInUse.java b/rewrite-maven/src/main/java/org/openrewrite/maven/table/ParentPomsInUse.java new file mode 100644 index 00000000000..29e9bc450d8 --- /dev/null +++ b/rewrite-maven/src/main/java/org/openrewrite/maven/table/ParentPomsInUse.java @@ -0,0 +1,64 @@ +/* + * Copyright 2023 the original author or 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 org.openrewrite.maven.table; + +import com.fasterxml.jackson.annotation.JsonIgnoreType; +import lombok.Value; +import org.openrewrite.Column; +import org.openrewrite.DataTable; +import org.openrewrite.Recipe; +import org.openrewrite.internal.lang.Nullable; + +@JsonIgnoreType +public class ParentPomsInUse extends DataTable { + + public ParentPomsInUse(Recipe recipe) { + super(recipe, Row.class, + ParentPomsInUse.class.getName(), + "Maven parent POMs in use", "Projects, GAVs and relativePaths for Maven parent POMs in use."); + } + + @Value + public static class Row { + @Column(displayName = "Project name", + description = "The name of the project that contains the parent.") + String projectName; + + @Column(displayName = "Group", + description = "The first part of a parent coordinate `org.springframework.boot`.") + String groupId; + + @Column(displayName = "Artifact", + description = "The second part of a parent coordinate `spring-boot-starter-*`.") + String artifactId; + + @Column(displayName = "Version", + description = "The resolved version.") + @Nullable + String version; + + @Column(displayName = "Dated snapshot version", + description = "The resolved dated snapshot version or `null` if this parent is not a snapshot.") + @Nullable + String datedSnapshotVersion; + + @Column(displayName = "Relative path", + description = "The relative path to the parent.") + @Nullable + String relativePath; + + } +} diff --git a/rewrite-maven/src/test/java/org/openrewrite/maven/search/ParentPomInsightTest.java b/rewrite-maven/src/test/java/org/openrewrite/maven/search/ParentPomInsightTest.java new file mode 100644 index 00000000000..7f7f735224c --- /dev/null +++ b/rewrite-maven/src/test/java/org/openrewrite/maven/search/ParentPomInsightTest.java @@ -0,0 +1,205 @@ +/* + * Copyright 2023 the original author or 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 org.openrewrite.maven.search; + +import org.junit.jupiter.api.Test; +import org.openrewrite.DocumentExample; +import org.openrewrite.maven.table.ParentPomsInUse; +import org.openrewrite.test.RecipeSpec; +import org.openrewrite.test.RewriteTest; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.openrewrite.java.Assertions.mavenProject; +import static org.openrewrite.maven.Assertions.pomXml; + +class ParentPomInsightTest implements RewriteTest { + @Override + public void defaults(RecipeSpec spec) { + spec.recipe(new ParentPomInsight("org.springframework.boot", "spring-boot-starter-parent")); + } + + @DocumentExample + @Test + void findParent() { + rewriteRun( + spec -> spec.dataTable(ParentPomsInUse.Row.class, rows -> { + assertThat(rows).singleElement().satisfies(row -> { + assertThat(row.getProjectName()).isEqualTo("demo"); + assertThat(row.getGroupId()).isEqualTo("org.springframework.boot"); + assertThat(row.getArtifactId()).isEqualTo("spring-boot-starter-parent"); + assertThat(row.getVersion()).isEqualTo("3.1.4"); + assertThat(row.getDatedSnapshotVersion()).isNull(); + assertThat(row.getRelativePath()).isNull(); + }); + }), + mavenProject("demo", + pomXml( + """ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 3.1.4 + + + com.example + demo + 0.0.1-SNAPSHOT + + """, + """ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 3.1.4 + + + com.example + demo + 0.0.1-SNAPSHOT + + """ + ) + ) + ); + } + + @Test + void multiModuleOnlyRoot() { + rewriteRun( + spec -> spec + .recipe(new ParentPomInsight("*", "*")) + .dataTableAsCsv(ParentPomsInUse.class.getName(), """ + projectName,groupId,artifactId,version,datedSnapshotVersion,relativePath + sample,org.springframework.boot,"spring-boot-starter-parent",2.5.0,, + module1,org.sample,sample,1.0.0,,../ + module2,org.sample,sample,1.0.0,,../ + """), + mavenProject("sample", + pomXml( + """ + + + 4.0.0 + org.sample + sample + 1.0.0 + + + org.springframework.boot + spring-boot-starter-parent + 2.5.0 + + + + module1 + module2 + + + """, + """ + + + 4.0.0 + org.sample + sample + 1.0.0 + + + org.springframework.boot + spring-boot-starter-parent + 2.5.0 + + + + module1 + module2 + + + """ + ), + mavenProject("module1", + pomXml( + """ + + + 4.0.0 + + org.sample + sample + 1.0.0 + ../ + + module1 + + """, + """ + + + 4.0.0 + + org.sample + sample + 1.0.0 + ../ + + module1 + + """, + spec -> spec.path("module1/pom.xml") + )), + mavenProject("module2", + pomXml( + """ + + + 4.0.0 + + org.sample + sample + 1.0.0 + ../ + + module2 + + """, + """ + + + 4.0.0 + + org.sample + sample + 1.0.0 + ../ + + module2 + + """, + spec -> spec.path("module2/pom.xml") + ) + ) + ) + ); + } +}