Skip to content
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -92,14 +92,15 @@ recipeDependencies {
parserClasspath("io.micrometer:micrometer-commons:1.11.+")
parserClasspath("io.micrometer:micrometer-core:1.11.+")
parserClasspath("io.micrometer:micrometer-observation:1.11.+")
parserClasspath("io.springfox:springfox-swagger2:3.+")
parserClasspath("io.swagger.core.v3:swagger-models:2.+")

testParserClasspath("com.nimbusds:nimbus-jose-jwt:9.13")
testParserClasspath("io.projectreactor:reactor-core:3.6.3")
testParserClasspath("io.springfox:springfox-core:3.+")
testParserClasspath("io.springfox:springfox-spring-web:3.+")
testParserClasspath("io.springfox:springfox-spi:3.+")
testParserClasspath("io.springfox:springfox-bean-validators:3.+")
testParserClasspath("io.swagger.core.v3:swagger-models:2.+")
testParserClasspath("jakarta.persistence:jakarta.persistence-api:2.2.3")
testParserClasspath("jakarta.validation:jakarta.validation-api:2.0.2")
testParserClasspath("jakarta.validation:jakarta.validation-api:3.0.+")
Expand Down
52 changes: 52 additions & 0 deletions src/main/java/org/openrewrite/java/spring/swagger/RemoveBuild.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* Copyright 2025 the original author or authors.
* <p>
* Licensed under the Moderne Source Available License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* https://docs.moderne.io/licensing/moderne-source-available-license
* <p>
* 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.java.spring.swagger;

import org.openrewrite.ExecutionContext;
import org.openrewrite.Preconditions;
import org.openrewrite.Recipe;
import org.openrewrite.TreeVisitor;
import org.openrewrite.java.JavaVisitor;
import org.openrewrite.java.MethodMatcher;
import org.openrewrite.java.search.UsesMethod;
import org.openrewrite.java.tree.J;

public class RemoveBuild extends Recipe {
private static final MethodMatcher BUILD_MATCHER = new MethodMatcher("springfox.documentation.builders.ApiInfoBuilder build()");

@Override
public String getDisplayName() {
return "Remove `ApiInfoBuilder.build()`";
}

@Override
public String getDescription() {
return "Remove SpringFox's `ApiInfoBuilder.build()` ahead of migration to Swagger's `Info`.";
}

@Override
public TreeVisitor<?, ExecutionContext> getVisitor() {
return Preconditions.check(new UsesMethod<>(BUILD_MATCHER), new JavaVisitor<ExecutionContext>() {
@Override
public J visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) {
if (BUILD_MATCHER.matches(method)) {
return autoFormat(method.getSelect().withPrefix(method.getPrefix()), ctx);
}
return super.visitMethodInvocation(method, ctx);
}
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* Copyright 2025 the original author or authors.
* <p>
* Licensed under the Moderne Source Available License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* https://docs.moderne.io/licensing/moderne-source-available-license
* <p>
* 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.java.spring.swagger;

import org.openrewrite.ExecutionContext;
import org.openrewrite.Preconditions;
import org.openrewrite.Recipe;
import org.openrewrite.TreeVisitor;
import org.openrewrite.java.JavaParser;
import org.openrewrite.java.JavaTemplate;
import org.openrewrite.java.JavaVisitor;
import org.openrewrite.java.MethodMatcher;
import org.openrewrite.java.search.UsesMethod;
import org.openrewrite.java.tree.J;

public class ReplaceContact extends Recipe {
private static final MethodMatcher CONTACT_MATCHER = new MethodMatcher("springfox.documentation.service.Contact <constructor>(String, String, String)");

@Override
public String getDisplayName() {
return "Replace SpringFox's `Contact` with Swagger's `Contact`";
}

@Override
public String getDescription() {
return "Replace three argument constructor with immutable builder.";
}

@Override
public TreeVisitor<?, ExecutionContext> getVisitor() {
return Preconditions.check(new UsesMethod<>(CONTACT_MATCHER), new JavaVisitor<ExecutionContext>() {
// Replace `Contact` constructor
@Override
public J visitNewClass(J.NewClass newClass, ExecutionContext ctx) {
if (CONTACT_MATCHER.matches(newClass)) {
maybeRemoveImport("springfox.documentation.service.Contact");
maybeAddImport("io.swagger.v3.oas.models.info.Contact");
return JavaTemplate.builder("new Contact().name(#{any(String)}).url(#{any(String)}).email(#{any(String)})")
.imports("io.swagger.v3.oas.models.info.Contact")
.javaParser(JavaParser.fromJavaVersion().classpathFromResources(ctx, "swagger-models"))
.build()
.apply(getCursor(), newClass.getCoordinates().replace(), newClass.getArguments().toArray());
}
return super.visitNewClass(newClass, ctx);
}
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
/*
* Copyright 2025 the original author or authors.
* <p>
* Licensed under the Moderne Source Available License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* https://docs.moderne.io/licensing/moderne-source-available-license
* <p>
* 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.java.spring.swagger;

import org.jspecify.annotations.Nullable;
import org.openrewrite.*;
import org.openrewrite.java.*;
import org.openrewrite.java.search.UsesMethod;
import org.openrewrite.java.tree.*;
import org.openrewrite.marker.Markers;

import java.util.UUID;

public class ReplaceLicenseUrl extends Recipe {
private static final MethodMatcher LICENSE_MATCHER = new MethodMatcher("springfox.documentation.builders.ApiInfoBuilder license(String)");
private static final MethodMatcher LICENSEURL_MATCHER = new MethodMatcher("springfox.documentation.builders.ApiInfoBuilder licenseUrl(String)");

@Override
public String getDisplayName() {
return "Replace SpringFox's `license` and `licenseUrl`";
}

@Override
public String getDescription() {
return "Replace SpringFox's license methods with Swaggers immutable `new License().name(String).url(String)`.";
}

@Override
public TreeVisitor<?, ExecutionContext> getVisitor() {
TreeVisitor<?, ExecutionContext> preconditions = Preconditions.or(new UsesMethod<>(LICENSE_MATCHER), new UsesMethod<>(LICENSEURL_MATCHER));
return Preconditions.check(preconditions, new JavaVisitor<ExecutionContext>() {
@Override
public J visitMethodDeclaration(J.MethodDeclaration method, ExecutionContext ctx) {
// Extract and store values or cursor
Cursor mdCursor = getCursor();
new JavaIsoVisitor<ExecutionContext>() {
@Override
public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) {
J.MethodInvocation mi = super.visitMethodInvocation(method, ctx);
if (LICENSE_MATCHER.matches(mi)) {
mdCursor.putMessage("LICENSE", mi.getArguments().get(0));
} else if (LICENSEURL_MATCHER.matches(mi)) {
mdCursor.putMessage("LICENSE_URL", mi.getArguments().get(0));
}
return mi;
}
}.visit(method, ctx, getCursor().getParentOrThrow());

// Now replace any downstream method invocations
return super.visitMethodDeclaration(method, ctx);
}

@Override
public J visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) {
J.MethodInvocation mi = (J.MethodInvocation) super.visitMethodInvocation(method, ctx);

Expression license;
Expression licenseUrl;
if (LICENSE_MATCHER.matches(mi)) {
license = mi.getArguments().get(0);
licenseUrl = getCursor().pollNearestMessage("LICENSE_URL");
if (licenseUrl == null) {
return replaceLicense(mi, ctx, nameOnyTemplate(), mi.getSelect(), license);
}
// Combine license and url
return replaceLicense(mi, ctx, fullTemplate(), mi.getSelect(), license, licenseUrl);
}
if (LICENSEURL_MATCHER.matches(mi)) {
license = getCursor().pollNearestMessage("LICENSE");
licenseUrl = mi.getArguments().get(0);
if (license == null) {
return replaceLicense(mi, ctx, urlOnlyTemplate(), mi.getSelect(), licenseUrl);
}
// Remove the method itself already
return mi.getSelect().withPrefix(mi.getPrefix());
}
return mi;
}

private String fullTemplate() {
return makeTemplate(true, true);
}

private String nameOnyTemplate() {
return makeTemplate(true, false);
}

private String urlOnlyTemplate() {
return makeTemplate(false, true);
}

private String makeTemplate(boolean withName, boolean withUrl) {
StringBuilder sb = new StringBuilder("#{any(io.swagger.v3.oas.models.info.Info)}\n.license(new License()");
if (withName) {
sb.append(".name(#{any(String)})");
}
if (withUrl) {
sb.append(".url(#{any(String)})");
}
sb.append(')');
return sb.toString();
}

private J.MethodInvocation replaceLicense(J.MethodInvocation mi, ExecutionContext ctx, String template, Object... args) {
maybeAddImport("io.swagger.v3.oas.models.info.License");
return JavaTemplate.builder(template)
.imports("io.swagger.v3.oas.models.info.License")
.javaParser(JavaParser.fromJavaVersion().classpathFromResources(ctx, "swagger-models"))
.build()
.apply(getCursor(), mi.getCoordinates().replace(), args);
}
});
}
}
Loading
Loading