From dc5cdfb9e0ccce61cfb1c11333dbae0750afff2b Mon Sep 17 00:00:00 2001 From: SiBorea Date: Wed, 4 Dec 2024 10:21:36 +0800 Subject: [PATCH 1/8] Migrate @SwaggerDefinition to @OpenAPIDefinition --- .../openapi/swagger/AnnotationUtils.java | 27 +++++ .../openapi/swagger/MigrateApiToTag.java | 19 +--- ...eSwaggerDefinitionToOpenAPIDefinition.java | 106 ++++++++++++++++++ .../resources/META-INF/rewrite/swagger-2.yml | 4 + .../openapi/swagger/SwaggerToOpenAPITest.java | 51 ++++++++- 5 files changed, 185 insertions(+), 22 deletions(-) create mode 100644 src/main/java/org/openrewrite/openapi/swagger/AnnotationUtils.java create mode 100644 src/main/java/org/openrewrite/openapi/swagger/MigrateSwaggerDefinitionToOpenAPIDefinition.java diff --git a/src/main/java/org/openrewrite/openapi/swagger/AnnotationUtils.java b/src/main/java/org/openrewrite/openapi/swagger/AnnotationUtils.java new file mode 100644 index 0000000..5cfd8b7 --- /dev/null +++ b/src/main/java/org/openrewrite/openapi/swagger/AnnotationUtils.java @@ -0,0 +1,27 @@ +package org.openrewrite.openapi.swagger; + +import static java.util.Collections.emptyMap; +import org.openrewrite.java.tree.Expression; +import org.openrewrite.java.tree.J; + +import java.util.HashMap; +import java.util.Map; + +public class AnnotationUtils { + public static Map extractAnnotationArgumentAssignments(J.Annotation annotation) { + if (annotation.getArguments() == null || + annotation.getArguments().isEmpty() || + annotation.getArguments().get(0) instanceof J.Empty) { + return emptyMap(); + } + Map map = new HashMap<>(); + for (Expression expression : annotation.getArguments()) { + if (expression instanceof J.Assignment) { + J.Assignment a = (J.Assignment) expression; + String simpleName = ((J.Identifier) a.getVariable()).getSimpleName(); + map.put(simpleName, a.getAssignment()); + } + } + return map; + } +} diff --git a/src/main/java/org/openrewrite/openapi/swagger/MigrateApiToTag.java b/src/main/java/org/openrewrite/openapi/swagger/MigrateApiToTag.java index 214d240..5ed7ea9 100644 --- a/src/main/java/org/openrewrite/openapi/swagger/MigrateApiToTag.java +++ b/src/main/java/org/openrewrite/openapi/swagger/MigrateApiToTag.java @@ -89,7 +89,7 @@ public TreeVisitor getVisitor() { public J.@Nullable Annotation visitAnnotation(J.Annotation annotation, ExecutionContext ctx) { J.Annotation ann = super.visitAnnotation(annotation, ctx); if (apiMatcher.matches(ann)) { - Map annotationArgumentAssignments = extractAnnotationArgumentAssignments(ann); + Map annotationArgumentAssignments = AnnotationUtils.extractAnnotationArgumentAssignments(ann); if (annotationArgumentAssignments.get("tags") != null) { // Remove @Api and add @Tag or @Tags at class level getCursor().putMessageOnFirstEnclosing(J.ClassDeclaration.class, FQN_API, annotationArgumentAssignments); @@ -102,23 +102,6 @@ public TreeVisitor getVisitor() { return ann; } - private Map extractAnnotationArgumentAssignments(J.Annotation apiAnnotation) { - if (apiAnnotation.getArguments() == null || - apiAnnotation.getArguments().isEmpty() || - apiAnnotation.getArguments().get(0) instanceof J.Empty) { - return emptyMap(); - } - Map map = new HashMap<>(); - for (Expression expression : apiAnnotation.getArguments()) { - if (expression instanceof J.Assignment) { - J.Assignment a = (J.Assignment) expression; - String simpleName = ((J.Identifier) a.getVariable()).getSimpleName(); - map.put(simpleName, a.getAssignment()); - } - } - return map; - } - @Override public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, ExecutionContext ctx) { J.ClassDeclaration cd = super.visitClassDeclaration(classDecl, ctx); diff --git a/src/main/java/org/openrewrite/openapi/swagger/MigrateSwaggerDefinitionToOpenAPIDefinition.java b/src/main/java/org/openrewrite/openapi/swagger/MigrateSwaggerDefinitionToOpenAPIDefinition.java new file mode 100644 index 0000000..188fd30 --- /dev/null +++ b/src/main/java/org/openrewrite/openapi/swagger/MigrateSwaggerDefinitionToOpenAPIDefinition.java @@ -0,0 +1,106 @@ +/* + * Copyright 2024 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.openapi.swagger; + +import org.jspecify.annotations.Nullable; +import org.openrewrite.*; +import org.openrewrite.java.*; +import org.openrewrite.java.search.UsesType; +import org.openrewrite.java.tree.Expression; +import org.openrewrite.java.tree.J; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class MigrateSwaggerDefinitionToOpenAPIDefinition extends Recipe { + + private static final String FQN_SWAGGER_DEFINITION = "io.swagger.annotations.SwaggerDefinition"; + private static final String FQN_OPENAPI_DEFINITION = "io.swagger.v3.oas.annotations.OpenAPIDefinition"; + private static final String FQN_SERVER = "io.swagger.v3.oas.annotations.servers.Server"; + + @Override + public String getDisplayName() { + return "Migrate from `@SwaggerDefinition` to `@OpenAPIDefinition`"; + } + + @Override + public String getDescription() { + return "Migrate from `@SwaggerDefinition` to `@OpenAPIDefinition`."; + } + + @Override + public TreeVisitor getVisitor() { + return Preconditions.check( + new UsesType<>(FQN_SWAGGER_DEFINITION, false), + new JavaIsoVisitor() { + private final AnnotationMatcher annotationMatcher = new AnnotationMatcher(FQN_SWAGGER_DEFINITION); + + @Override + public J.@Nullable Annotation visitAnnotation(J.Annotation annotation, ExecutionContext ctx) { + J.Annotation ann = super.visitAnnotation(annotation, ctx); + + if (annotationMatcher.matches(ann)) { + Map args = AnnotationUtils.extractAnnotationArgumentAssignments(ann); + + StringBuilder tpl = new StringBuilder("@OpenAPIDefinition(\n"); + List tplArgs = new ArrayList<>(); + + Expression basePath = args.get("basePath"); + Expression host = args.get("host"); + Expression schemes = args.get("schemes"); + String servers = ""; + if (basePath != null && host != null && schemes != null) { + tpl.append("servers = {\n"); + for (Expression scheme : ((J.NewArray) schemes).getInitializer()) { + if (!servers.isEmpty()) { + servers += ",\n"; + } + String url = host.toString() + basePath; + String schemeName = ((J.FieldAccess) scheme).getSimpleName(); + if ("HTTP".equals(schemeName)) { + servers += "@Server(url = \"http://" + url + "\")"; + } else if ("HTTPS".equals(schemeName)) { + servers += "@Server(url = \"https://" + url + "\")"; + } + } + tpl.append(servers); + tpl.append("\n}"); + } + + if (args.containsKey("info")) { + tpl.append(", \ninfo = #{any()}"); + tplArgs.add(args.get("info")); + } + tpl.append(")"); + + ann = JavaTemplate.builder(tpl.toString()) + .imports(FQN_OPENAPI_DEFINITION, FQN_SERVER) + .javaParser(JavaParser.fromJavaVersion().dependsOn(FQN_OPENAPI_DEFINITION, FQN_SERVER)) + .build() + .apply(updateCursor(ann), ann.getCoordinates().replace(), tplArgs.toArray()); + maybeRemoveImport(FQN_SWAGGER_DEFINITION); + maybeAddImport(FQN_OPENAPI_DEFINITION, false); + maybeAddImport(FQN_SERVER, false); + ann = maybeAutoFormat(annotation, ann, ctx); + } + + return ann; + } + } + ); + } +} diff --git a/src/main/resources/META-INF/rewrite/swagger-2.yml b/src/main/resources/META-INF/rewrite/swagger-2.yml index 640d1dd..9172a73 100644 --- a/src/main/resources/META-INF/rewrite/swagger-2.yml +++ b/src/main/resources/META-INF/rewrite/swagger-2.yml @@ -43,6 +43,9 @@ recipeList: - org.openrewrite.java.ChangeType: oldFullyQualifiedTypeName: io.swagger.annotations.Tag newFullyQualifiedTypeName: io.swagger.v3.oas.annotations.tags.Tag + - org.openrewrite.java.ChangeType: + oldFullyQualifiedTypeName: io.swagger.annotations.Info + newFullyQualifiedTypeName: io.swagger.v3.oas.annotations.info.Info - org.openrewrite.java.ChangeType: oldFullyQualifiedTypeName: springfox.documentation.annotations.ApiIgnore newFullyQualifiedTypeName: io.swagger.v3.oas.annotations.Hidden @@ -53,6 +56,7 @@ recipeList: - org.openrewrite.openapi.swagger.MigrateApiParamToParameter - org.openrewrite.openapi.swagger.MigrateApiModelPropertyToSchema - org.openrewrite.openapi.swagger.MigrateApiModelToSchema + - org.openrewrite.openapi.swagger.MigrateSwaggerDefinitionToOpenAPIDefinition # todo add swagger-core to common-dependencies diff --git a/src/test/java/org/openrewrite/openapi/swagger/SwaggerToOpenAPITest.java b/src/test/java/org/openrewrite/openapi/swagger/SwaggerToOpenAPITest.java index e93ef42..935e8f8 100644 --- a/src/test/java/org/openrewrite/openapi/swagger/SwaggerToOpenAPITest.java +++ b/src/test/java/org/openrewrite/openapi/swagger/SwaggerToOpenAPITest.java @@ -48,10 +48,10 @@ void shouldChangeSwaggerArtifacts() { java( """ package example.org; - + import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; - + @ApiModel(value="ApiModelExampleValue", description="ApiModelExampleDescription") class Example { @ApiModelProperty(value = "ApiModelPropertyExampleValue", position = 1) @@ -60,9 +60,9 @@ class Example { """, """ package example.org; - + import io.swagger.v3.oas.annotations.media.Schema; - + @Schema(name="ApiModelExampleValue", description="ApiModelExampleDescription") class Example { @Schema(description = "ApiModelPropertyExampleValue") @@ -130,4 +130,47 @@ class Example { ) ); } + + @Test + void migrateSwaggerDefinitionsToOpenAPIDefinition() { + rewriteRun( + //language=java + java( + """ + import io.swagger.annotations.Info; + import io.swagger.annotations.SwaggerDefinition; + import jakarta.ws.rs.core.MediaType; + + @SwaggerDefinition( + basePath = "/api", + host="example.com", + info = @Info(title = "Example", version = "V1.0"), + consumes = { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }, + produces = { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }, + schemes = { SwaggerDefinition.Scheme.HTTP, SwaggerDefinition.Scheme.HTTPS }) + class Example { + } + """, + """ + import io.swagger.v3.oas.annotations.OpenAPIDefinition; + import io.swagger.v3.oas.annotations.info.Info; + import io.swagger.v3.oas.annotations.servers.Server; + import jakarta.ws.rs.core.MediaType; + + import io.swagger.v3.oas.annotations.OpenAPIDefinition; + import io.swagger.v3.oas.annotations.info.Info; + import io.swagger.v3.oas.annotations.servers.Server; + import jakarta.ws.rs.core.MediaType; + + @OpenAPIDefinition( + servers = { + @Server(url = "http://example.com/api"), + @Server(url = "https://example.com/api") + }, + info = @Info(title = "Example", version = "V1.0")) + class Example { + } + """ + )); + } } From 42256f21d175268e494f8f03c9be433b18ff9618 Mon Sep 17 00:00:00 2001 From: SiBorea Date: Wed, 4 Dec 2024 10:28:22 +0800 Subject: [PATCH 2/8] Support all schemes --- .../MigrateSwaggerDefinitionToOpenAPIDefinition.java | 9 ++------- .../openapi/swagger/SwaggerToOpenAPITest.java | 7 ++----- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/src/main/java/org/openrewrite/openapi/swagger/MigrateSwaggerDefinitionToOpenAPIDefinition.java b/src/main/java/org/openrewrite/openapi/swagger/MigrateSwaggerDefinitionToOpenAPIDefinition.java index 188fd30..eb0f221 100644 --- a/src/main/java/org/openrewrite/openapi/swagger/MigrateSwaggerDefinitionToOpenAPIDefinition.java +++ b/src/main/java/org/openrewrite/openapi/swagger/MigrateSwaggerDefinitionToOpenAPIDefinition.java @@ -69,13 +69,8 @@ public TreeVisitor getVisitor() { if (!servers.isEmpty()) { servers += ",\n"; } - String url = host.toString() + basePath; - String schemeName = ((J.FieldAccess) scheme).getSimpleName(); - if ("HTTP".equals(schemeName)) { - servers += "@Server(url = \"http://" + url + "\")"; - } else if ("HTTPS".equals(schemeName)) { - servers += "@Server(url = \"https://" + url + "\")"; - } + String schemeName = ((J.FieldAccess) scheme).getSimpleName().toLowerCase(); + servers += "@Server(url = \"" + schemeName + "://" + host + basePath + "\")"; } tpl.append(servers); tpl.append("\n}"); diff --git a/src/test/java/org/openrewrite/openapi/swagger/SwaggerToOpenAPITest.java b/src/test/java/org/openrewrite/openapi/swagger/SwaggerToOpenAPITest.java index 935e8f8..3e7d46b 100644 --- a/src/test/java/org/openrewrite/openapi/swagger/SwaggerToOpenAPITest.java +++ b/src/test/java/org/openrewrite/openapi/swagger/SwaggerToOpenAPITest.java @@ -25,6 +25,7 @@ import static org.openrewrite.java.Assertions.java; import static org.openrewrite.maven.Assertions.pomXml; +import org.openrewrite.test.TypeValidation; class SwaggerToOpenAPITest implements RewriteTest { @Override @@ -134,6 +135,7 @@ class Example { @Test void migrateSwaggerDefinitionsToOpenAPIDefinition() { rewriteRun( + recipeSpec -> recipeSpec.afterTypeValidationOptions(TypeValidation.none()), //language=java java( """ @@ -157,11 +159,6 @@ class Example { import io.swagger.v3.oas.annotations.servers.Server; import jakarta.ws.rs.core.MediaType; - import io.swagger.v3.oas.annotations.OpenAPIDefinition; - import io.swagger.v3.oas.annotations.info.Info; - import io.swagger.v3.oas.annotations.servers.Server; - import jakarta.ws.rs.core.MediaType; - @OpenAPIDefinition( servers = { @Server(url = "http://example.com/api"), From 3b07f5bcb2eb58428a547ac1b4a70b0cdc32ee9a Mon Sep 17 00:00:00 2001 From: SiBorea Date: Wed, 4 Dec 2024 10:31:17 +0800 Subject: [PATCH 3/8] Format with IntelliJ --- .../openrewrite/openapi/swagger/SwaggerToOpenAPITest.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/test/java/org/openrewrite/openapi/swagger/SwaggerToOpenAPITest.java b/src/test/java/org/openrewrite/openapi/swagger/SwaggerToOpenAPITest.java index 3e7d46b..9405093 100644 --- a/src/test/java/org/openrewrite/openapi/swagger/SwaggerToOpenAPITest.java +++ b/src/test/java/org/openrewrite/openapi/swagger/SwaggerToOpenAPITest.java @@ -17,16 +17,15 @@ import org.junit.jupiter.api.Test; import org.openrewrite.DocumentExample; +import static org.openrewrite.java.Assertions.java; import org.openrewrite.java.JavaParser; +import static org.openrewrite.maven.Assertions.pomXml; import org.openrewrite.test.RecipeSpec; import org.openrewrite.test.RewriteTest; +import org.openrewrite.test.TypeValidation; import java.util.regex.Pattern; -import static org.openrewrite.java.Assertions.java; -import static org.openrewrite.maven.Assertions.pomXml; -import org.openrewrite.test.TypeValidation; - class SwaggerToOpenAPITest implements RewriteTest { @Override public void defaults(RecipeSpec spec) { From b0377163e102e150ebda24cbffe6b6eced38b273 Mon Sep 17 00:00:00 2001 From: SiBorea Date: Wed, 4 Dec 2024 10:47:24 +0800 Subject: [PATCH 4/8] Make it available for all args --- ...eSwaggerDefinitionToOpenAPIDefinition.java | 21 ++++++++++++------- .../openapi/swagger/SwaggerToOpenAPITest.java | 3 ++- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/openrewrite/openapi/swagger/MigrateSwaggerDefinitionToOpenAPIDefinition.java b/src/main/java/org/openrewrite/openapi/swagger/MigrateSwaggerDefinitionToOpenAPIDefinition.java index eb0f221..127aed7 100644 --- a/src/main/java/org/openrewrite/openapi/swagger/MigrateSwaggerDefinitionToOpenAPIDefinition.java +++ b/src/main/java/org/openrewrite/openapi/swagger/MigrateSwaggerDefinitionToOpenAPIDefinition.java @@ -57,7 +57,8 @@ public TreeVisitor getVisitor() { Map args = AnnotationUtils.extractAnnotationArgumentAssignments(ann); StringBuilder tpl = new StringBuilder("@OpenAPIDefinition(\n"); - List tplArgs = new ArrayList<>(); + List tplArgs = new ArrayList<>(); + List parts = new ArrayList<>(); Expression basePath = args.get("basePath"); Expression host = args.get("host"); @@ -72,15 +73,21 @@ public TreeVisitor getVisitor() { String schemeName = ((J.FieldAccess) scheme).getSimpleName().toLowerCase(); servers += "@Server(url = \"" + schemeName + "://" + host + basePath + "\")"; } - tpl.append(servers); - tpl.append("\n}"); + servers += "\n}"; + parts.add(servers); } - if (args.containsKey("info")) { - tpl.append(", \ninfo = #{any()}"); - tplArgs.add(args.get("info")); + args.remove("basePath"); + args.remove("host"); + args.remove("schemes"); + args.remove("produces"); + args.remove("consumes"); + for (Map.Entry arg : args.entrySet()) { + parts.add(arg.getKey() + " = #{any()}"); + tplArgs.add(arg.getValue()); } - tpl.append(")"); + tpl.append(String.join(",\n", parts)); + tpl.append("\n)"); ann = JavaTemplate.builder(tpl.toString()) .imports(FQN_OPENAPI_DEFINITION, FQN_SERVER) diff --git a/src/test/java/org/openrewrite/openapi/swagger/SwaggerToOpenAPITest.java b/src/test/java/org/openrewrite/openapi/swagger/SwaggerToOpenAPITest.java index 9405093..9c55f9a 100644 --- a/src/test/java/org/openrewrite/openapi/swagger/SwaggerToOpenAPITest.java +++ b/src/test/java/org/openrewrite/openapi/swagger/SwaggerToOpenAPITest.java @@ -163,7 +163,8 @@ class Example { @Server(url = "http://example.com/api"), @Server(url = "https://example.com/api") }, - info = @Info(title = "Example", version = "V1.0")) + info = @Info(title = "Example", version = "V1.0") + ) class Example { } """ From 81f82c3c56624a473ecb59398e5e3d24b4b7c7f6 Mon Sep 17 00:00:00 2001 From: SiBorea Date: Wed, 4 Dec 2024 10:50:39 +0800 Subject: [PATCH 5/8] Fix license --- .../openapi/swagger/AnnotationUtils.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/main/java/org/openrewrite/openapi/swagger/AnnotationUtils.java b/src/main/java/org/openrewrite/openapi/swagger/AnnotationUtils.java index 5cfd8b7..a3aeaf1 100644 --- a/src/main/java/org/openrewrite/openapi/swagger/AnnotationUtils.java +++ b/src/main/java/org/openrewrite/openapi/swagger/AnnotationUtils.java @@ -1,3 +1,18 @@ +/* + * Copyright 2024 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.openapi.swagger; import static java.util.Collections.emptyMap; From b2444bc03d4bc4d63c08986715fa7fc83b16b1dd Mon Sep 17 00:00:00 2001 From: SiBorea Date: Fri, 20 Dec 2024 12:53:39 +0800 Subject: [PATCH 6/8] Fix single schema issue --- build.gradle.kts | 1 + ...eSwaggerDefinitionToOpenAPIDefinition.java | 23 +++++++--- .../openapi/swagger/SwaggerToOpenAPITest.java | 45 +++++++++++++++++-- 3 files changed, 59 insertions(+), 10 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index fabd831..3665662 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -20,6 +20,7 @@ dependencies { testRuntimeOnly("io.swagger:swagger-annotations:1.6.13") testRuntimeOnly("io.swagger.core.v3:swagger-annotations:2.2.20") + testRuntimeOnly("jakarta.ws.rs:jakarta.ws.rs-api:3.1.0") testRuntimeOnly("org.gradle:gradle-tooling-api:latest.release") } diff --git a/src/main/java/org/openrewrite/openapi/swagger/MigrateSwaggerDefinitionToOpenAPIDefinition.java b/src/main/java/org/openrewrite/openapi/swagger/MigrateSwaggerDefinitionToOpenAPIDefinition.java index 127aed7..6d9f1a4 100644 --- a/src/main/java/org/openrewrite/openapi/swagger/MigrateSwaggerDefinitionToOpenAPIDefinition.java +++ b/src/main/java/org/openrewrite/openapi/swagger/MigrateSwaggerDefinitionToOpenAPIDefinition.java @@ -49,6 +49,12 @@ public TreeVisitor getVisitor() { new JavaIsoVisitor() { private final AnnotationMatcher annotationMatcher = new AnnotationMatcher(FQN_SWAGGER_DEFINITION); + @Override + public J.CompilationUnit visitCompilationUnit(J.CompilationUnit cu, ExecutionContext executionContext) { + cu = super.visitCompilationUnit(cu, executionContext); + return cu; + } + @Override public J.@Nullable Annotation visitAnnotation(J.Annotation annotation, ExecutionContext ctx) { J.Annotation ann = super.visitAnnotation(annotation, ctx); @@ -66,12 +72,16 @@ public TreeVisitor getVisitor() { String servers = ""; if (basePath != null && host != null && schemes != null) { tpl.append("servers = {\n"); - for (Expression scheme : ((J.NewArray) schemes).getInitializer()) { - if (!servers.isEmpty()) { - servers += ",\n"; + if (schemes instanceof J.FieldAccess) { + servers += "@Server(url = \"" + ((J.FieldAccess) schemes).getSimpleName().toLowerCase() + "://" + host + basePath + "\")"; + } else if (schemes instanceof J.NewArray) { + for (Expression scheme : ((J.NewArray) schemes).getInitializer()) { + if (!servers.isEmpty()) { + servers += ",\n"; + } + String schemeName = ((J.FieldAccess) scheme).getSimpleName().toLowerCase(); + servers += "@Server(url = \"" + schemeName + "://" + host + basePath + "\")"; } - String schemeName = ((J.FieldAccess) scheme).getSimpleName().toLowerCase(); - servers += "@Server(url = \"" + schemeName + "://" + host + basePath + "\")"; } servers += "\n}"; parts.add(servers); @@ -91,7 +101,7 @@ public TreeVisitor getVisitor() { ann = JavaTemplate.builder(tpl.toString()) .imports(FQN_OPENAPI_DEFINITION, FQN_SERVER) - .javaParser(JavaParser.fromJavaVersion().dependsOn(FQN_OPENAPI_DEFINITION, FQN_SERVER)) + .javaParser(JavaParser.fromJavaVersion().classpath("swagger-annotations")) .build() .apply(updateCursor(ann), ann.getCoordinates().replace(), tplArgs.toArray()); maybeRemoveImport(FQN_SWAGGER_DEFINITION); @@ -100,6 +110,7 @@ public TreeVisitor getVisitor() { ann = maybeAutoFormat(annotation, ann, ctx); } + doAfterVisit(new RemoveUnusedImports().getVisitor()); return ann; } } diff --git a/src/test/java/org/openrewrite/openapi/swagger/SwaggerToOpenAPITest.java b/src/test/java/org/openrewrite/openapi/swagger/SwaggerToOpenAPITest.java index 9c55f9a..994fdd9 100644 --- a/src/test/java/org/openrewrite/openapi/swagger/SwaggerToOpenAPITest.java +++ b/src/test/java/org/openrewrite/openapi/swagger/SwaggerToOpenAPITest.java @@ -30,7 +30,7 @@ class SwaggerToOpenAPITest implements RewriteTest { @Override public void defaults(RecipeSpec spec) { spec.recipeFromResources("org.openrewrite.openapi.swagger.SwaggerToOpenAPI") - .parser(JavaParser.fromJavaVersion().classpath("swagger-annotations-1.+", "swagger-annotations-2.+")); + .parser(JavaParser.fromJavaVersion().classpath("swagger-annotations-1.+", "swagger-annotations-2.+", "rs-api")); } @Test @@ -132,9 +132,8 @@ class Example { } @Test - void migrateSwaggerDefinitionsToOpenAPIDefinition() { + void migrateSwaggerDefinitionsToOpenAPIDefinitionSingleSchema() { rewriteRun( - recipeSpec -> recipeSpec.afterTypeValidationOptions(TypeValidation.none()), //language=java java( """ @@ -148,7 +147,8 @@ void migrateSwaggerDefinitionsToOpenAPIDefinition() { info = @Info(title = "Example", version = "V1.0"), consumes = { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }, produces = { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }, - schemes = { SwaggerDefinition.Scheme.HTTP, SwaggerDefinition.Scheme.HTTPS }) + schemes = SwaggerDefinition.Scheme.HTTPS + ) class Example { } """, @@ -156,8 +156,45 @@ class Example { import io.swagger.v3.oas.annotations.OpenAPIDefinition; import io.swagger.v3.oas.annotations.info.Info; import io.swagger.v3.oas.annotations.servers.Server; + + @OpenAPIDefinition( + servers = { + @Server(url = "https://example.com/api") + }, + info = @Info(title = "Example", version = "V1.0") + ) + class Example { + } + """ + )); + } + + @Test + void migrateSwaggerDefinitionsToOpenAPIDefinitionMultipleSchema() { + rewriteRun( +// recipeSpec -> recipeSpec.afterTypeValidationOptions(TypeValidation.none()), + //language=java + java( + """ + import io.swagger.annotations.Info; + import io.swagger.annotations.SwaggerDefinition; import jakarta.ws.rs.core.MediaType; + @SwaggerDefinition( + basePath = "/api", + host="example.com", + info = @Info(title = "Example", version = "V1.0"), + consumes = { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }, + produces = { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }, + schemes = { SwaggerDefinition.Scheme.HTTP, SwaggerDefinition.Scheme.HTTPS }) + class Example { + } + """, + """ + import io.swagger.v3.oas.annotations.OpenAPIDefinition; + import io.swagger.v3.oas.annotations.info.Info; + import io.swagger.v3.oas.annotations.servers.Server; + @OpenAPIDefinition( servers = { @Server(url = "http://example.com/api"), From 3b0332c6ee8f31203ecdbd64c5a42c89e72a539e Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Wed, 1 Jan 2025 17:59:41 +0100 Subject: [PATCH 7/8] Apply bot suggestions --- .../openapi/swagger/AnnotationUtils.java | 7 +- .../openapi/swagger/MigrateApiToTag.java | 50 +++---- ...eSwaggerDefinitionToOpenAPIDefinition.java | 123 +++++++++--------- .../openapi/swagger/SwaggerToOpenAPITest.java | 17 +-- 4 files changed, 96 insertions(+), 101 deletions(-) diff --git a/src/main/java/org/openrewrite/openapi/swagger/AnnotationUtils.java b/src/main/java/org/openrewrite/openapi/swagger/AnnotationUtils.java index a3aeaf1..c384b8a 100644 --- a/src/main/java/org/openrewrite/openapi/swagger/AnnotationUtils.java +++ b/src/main/java/org/openrewrite/openapi/swagger/AnnotationUtils.java @@ -15,18 +15,19 @@ */ package org.openrewrite.openapi.swagger; -import static java.util.Collections.emptyMap; import org.openrewrite.java.tree.Expression; import org.openrewrite.java.tree.J; import java.util.HashMap; import java.util.Map; +import static java.util.Collections.emptyMap; + public class AnnotationUtils { public static Map extractAnnotationArgumentAssignments(J.Annotation annotation) { if (annotation.getArguments() == null || - annotation.getArguments().isEmpty() || - annotation.getArguments().get(0) instanceof J.Empty) { + annotation.getArguments().isEmpty() || + annotation.getArguments().get(0) instanceof J.Empty) { return emptyMap(); } Map map = new HashMap<>(); diff --git a/src/main/java/org/openrewrite/openapi/swagger/MigrateApiToTag.java b/src/main/java/org/openrewrite/openapi/swagger/MigrateApiToTag.java index 5ed7ea9..4194bac 100644 --- a/src/main/java/org/openrewrite/openapi/swagger/MigrateApiToTag.java +++ b/src/main/java/org/openrewrite/openapi/swagger/MigrateApiToTag.java @@ -27,11 +27,9 @@ import org.openrewrite.java.tree.J; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; import java.util.Map; -import static java.util.Collections.emptyMap; import static java.util.Comparator.comparing; import static java.util.Objects.requireNonNull; @@ -42,31 +40,33 @@ public class MigrateApiToTag extends Recipe { private static final String FQN_TAGS = "io.swagger.v3.oas.annotations.tags.Tags"; @Language("java") - private static final String TAGS_CLASS = "package io.swagger.v3.oas.annotations.tags;\n" + - "import java.lang.annotation.ElementType;\n" + - "import java.lang.annotation.Retention;\n" + - "import java.lang.annotation.RetentionPolicy;\n" + - "import java.lang.annotation.Target;\n" + - "@Target({ElementType.METHOD, ElementType.TYPE, ElementType.ANNOTATION_TYPE})\n" + - "@Retention(RetentionPolicy.RUNTIME)\n" + - "public @interface Tags {\n" + - " Tag[] value() default {};\n" + - "}"; + private static final String TAGS_CLASS = + "package io.swagger.v3.oas.annotations.tags;\n" + + "import java.lang.annotation.ElementType;\n" + + "import java.lang.annotation.Retention;\n" + + "import java.lang.annotation.RetentionPolicy;\n" + + "import java.lang.annotation.Target;\n" + + "@Target({ElementType.METHOD, ElementType.TYPE, ElementType.ANNOTATION_TYPE})\n" + + "@Retention(RetentionPolicy.RUNTIME)\n" + + "public @interface Tags {\n" + + " Tag[] value() default {};\n" + + "}"; @Language("java") - private static final String TAG_CLASS = "package io.swagger.v3.oas.annotations.tags;\n" + - "import java.lang.annotation.ElementType;\n" + - "import java.lang.annotation.Repeatable;\n" + - "import java.lang.annotation.Retention;\n" + - "import java.lang.annotation.RetentionPolicy;\n" + - "import java.lang.annotation.Target;\n" + - "@Target({ElementType.METHOD, ElementType.TYPE, ElementType.ANNOTATION_TYPE})\n" + - "@Retention(RetentionPolicy.RUNTIME)\n" + - "@Repeatable(Tags.class)\n" + - "public @interface Tag {\n" + - " String name();\n" + - " String description() default \"\";\n" + - "}"; + private static final String TAG_CLASS = + "package io.swagger.v3.oas.annotations.tags;\n" + + "import java.lang.annotation.ElementType;\n" + + "import java.lang.annotation.Repeatable;\n" + + "import java.lang.annotation.Retention;\n" + + "import java.lang.annotation.RetentionPolicy;\n" + + "import java.lang.annotation.Target;\n" + + "@Target({ElementType.METHOD, ElementType.TYPE, ElementType.ANNOTATION_TYPE})\n" + + "@Retention(RetentionPolicy.RUNTIME)\n" + + "@Repeatable(Tags.class)\n" + + "public @interface Tag {\n" + + " String name();\n" + + " String description() default \"\";\n" + + "}"; @Override public String getDisplayName() { diff --git a/src/main/java/org/openrewrite/openapi/swagger/MigrateSwaggerDefinitionToOpenAPIDefinition.java b/src/main/java/org/openrewrite/openapi/swagger/MigrateSwaggerDefinitionToOpenAPIDefinition.java index 6d9f1a4..6b35f18 100644 --- a/src/main/java/org/openrewrite/openapi/swagger/MigrateSwaggerDefinitionToOpenAPIDefinition.java +++ b/src/main/java/org/openrewrite/openapi/swagger/MigrateSwaggerDefinitionToOpenAPIDefinition.java @@ -16,7 +16,10 @@ package org.openrewrite.openapi.swagger; import org.jspecify.annotations.Nullable; -import org.openrewrite.*; +import org.openrewrite.ExecutionContext; +import org.openrewrite.Preconditions; +import org.openrewrite.Recipe; +import org.openrewrite.TreeVisitor; import org.openrewrite.java.*; import org.openrewrite.java.search.UsesType; import org.openrewrite.java.tree.Expression; @@ -45,75 +48,69 @@ public String getDescription() { @Override public TreeVisitor getVisitor() { return Preconditions.check( - new UsesType<>(FQN_SWAGGER_DEFINITION, false), - new JavaIsoVisitor() { - private final AnnotationMatcher annotationMatcher = new AnnotationMatcher(FQN_SWAGGER_DEFINITION); + new UsesType<>(FQN_SWAGGER_DEFINITION, false), + new JavaIsoVisitor() { + private final AnnotationMatcher annotationMatcher = new AnnotationMatcher(FQN_SWAGGER_DEFINITION); - @Override - public J.CompilationUnit visitCompilationUnit(J.CompilationUnit cu, ExecutionContext executionContext) { - cu = super.visitCompilationUnit(cu, executionContext); - return cu; - } + @Override + public J.@Nullable Annotation visitAnnotation(J.Annotation annotation, ExecutionContext ctx) { + J.Annotation ann = super.visitAnnotation(annotation, ctx); - @Override - public J.@Nullable Annotation visitAnnotation(J.Annotation annotation, ExecutionContext ctx) { - J.Annotation ann = super.visitAnnotation(annotation, ctx); + if (annotationMatcher.matches(ann)) { + Map args = AnnotationUtils.extractAnnotationArgumentAssignments(ann); - if (annotationMatcher.matches(ann)) { - Map args = AnnotationUtils.extractAnnotationArgumentAssignments(ann); + StringBuilder tpl = new StringBuilder("@OpenAPIDefinition(\n"); + List tplArgs = new ArrayList<>(); + List parts = new ArrayList<>(); - StringBuilder tpl = new StringBuilder("@OpenAPIDefinition(\n"); - List tplArgs = new ArrayList<>(); - List parts = new ArrayList<>(); + Expression basePath = args.get("basePath"); + Expression host = args.get("host"); + Expression schemes = args.get("schemes"); + String servers = ""; + if (basePath != null && host != null && schemes != null) { + tpl.append("servers = {\n"); + if (schemes instanceof J.FieldAccess) { + servers += "@Server(url = \"" + ((J.FieldAccess) schemes).getSimpleName().toLowerCase() + "://" + host + basePath + "\")"; + } else if (schemes instanceof J.NewArray) { + for (Expression scheme : ((J.NewArray) schemes).getInitializer()) { + if (!servers.isEmpty()) { + servers += ",\n"; + } + String schemeName = ((J.FieldAccess) scheme).getSimpleName().toLowerCase(); + servers += "@Server(url = \"" + schemeName + "://" + host + basePath + "\")"; + } + } + servers += "\n}"; + parts.add(servers); + } - Expression basePath = args.get("basePath"); - Expression host = args.get("host"); - Expression schemes = args.get("schemes"); - String servers = ""; - if (basePath != null && host != null && schemes != null) { - tpl.append("servers = {\n"); - if (schemes instanceof J.FieldAccess) { - servers += "@Server(url = \"" + ((J.FieldAccess) schemes).getSimpleName().toLowerCase() + "://" + host + basePath + "\")"; - } else if (schemes instanceof J.NewArray) { - for (Expression scheme : ((J.NewArray) schemes).getInitializer()) { - if (!servers.isEmpty()) { - servers += ",\n"; - } - String schemeName = ((J.FieldAccess) scheme).getSimpleName().toLowerCase(); - servers += "@Server(url = \"" + schemeName + "://" + host + basePath + "\")"; - } - } - servers += "\n}"; - parts.add(servers); - } + args.remove("basePath"); + args.remove("host"); + args.remove("schemes"); + args.remove("produces"); + args.remove("consumes"); + for (Map.Entry arg : args.entrySet()) { + parts.add(arg.getKey() + " = #{any()}"); + tplArgs.add(arg.getValue()); + } + tpl.append(String.join(",\n", parts)); + tpl.append("\n)"); - args.remove("basePath"); - args.remove("host"); - args.remove("schemes"); - args.remove("produces"); - args.remove("consumes"); - for (Map.Entry arg : args.entrySet()) { - parts.add(arg.getKey() + " = #{any()}"); - tplArgs.add(arg.getValue()); - } - tpl.append(String.join(",\n", parts)); - tpl.append("\n)"); + ann = JavaTemplate.builder(tpl.toString()) + .imports(FQN_OPENAPI_DEFINITION, FQN_SERVER) + .javaParser(JavaParser.fromJavaVersion().classpath("swagger-annotations")) + .build() + .apply(updateCursor(ann), ann.getCoordinates().replace(), tplArgs.toArray()); + maybeRemoveImport(FQN_SWAGGER_DEFINITION); + maybeAddImport(FQN_OPENAPI_DEFINITION, false); + maybeAddImport(FQN_SERVER, false); + ann = maybeAutoFormat(annotation, ann, ctx); + } - ann = JavaTemplate.builder(tpl.toString()) - .imports(FQN_OPENAPI_DEFINITION, FQN_SERVER) - .javaParser(JavaParser.fromJavaVersion().classpath("swagger-annotations")) - .build() - .apply(updateCursor(ann), ann.getCoordinates().replace(), tplArgs.toArray()); - maybeRemoveImport(FQN_SWAGGER_DEFINITION); - maybeAddImport(FQN_OPENAPI_DEFINITION, false); - maybeAddImport(FQN_SERVER, false); - ann = maybeAutoFormat(annotation, ann, ctx); - } - - doAfterVisit(new RemoveUnusedImports().getVisitor()); - return ann; - } - } + doAfterVisit(new RemoveUnusedImports().getVisitor()); + return ann; + } + } ); } } diff --git a/src/test/java/org/openrewrite/openapi/swagger/SwaggerToOpenAPITest.java b/src/test/java/org/openrewrite/openapi/swagger/SwaggerToOpenAPITest.java index d719b04..a0f8ea4 100644 --- a/src/test/java/org/openrewrite/openapi/swagger/SwaggerToOpenAPITest.java +++ b/src/test/java/org/openrewrite/openapi/swagger/SwaggerToOpenAPITest.java @@ -17,15 +17,15 @@ import org.junit.jupiter.api.Test; import org.openrewrite.DocumentExample; -import static org.openrewrite.java.Assertions.java; import org.openrewrite.java.JavaParser; -import static org.openrewrite.maven.Assertions.pomXml; import org.openrewrite.test.RecipeSpec; import org.openrewrite.test.RewriteTest; -import org.openrewrite.test.TypeValidation; import java.util.regex.Pattern; +import static org.openrewrite.java.Assertions.java; +import static org.openrewrite.maven.Assertions.pomXml; + class SwaggerToOpenAPITest implements RewriteTest { @Override public void defaults(RecipeSpec spec) { @@ -47,8 +47,6 @@ void shouldChangeSwaggerArtifacts() { //language=java java( """ - package example.org; - import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; @@ -59,8 +57,6 @@ class Example { } """, """ - package example.org; - import io.swagger.v3.oas.annotations.media.Schema; @Schema(name="ApiModelExampleValue", description="ApiModelExampleDescription") @@ -194,13 +190,13 @@ class Example { class Example { } """ - )); + ) + ); } @Test void migrateSwaggerDefinitionsToOpenAPIDefinitionMultipleSchema() { rewriteRun( -// recipeSpec -> recipeSpec.afterTypeValidationOptions(TypeValidation.none()), //language=java java( """ @@ -233,6 +229,7 @@ class Example { class Example { } """ - )); + ) + ); } } From 9562633bd5146e46a57b1d99fa9d565021122b26 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Wed, 1 Jan 2025 18:05:25 +0100 Subject: [PATCH 8/8] Reduce visibility on AnnotationUtils --- .../java/org/openrewrite/openapi/swagger/AnnotationUtils.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/openrewrite/openapi/swagger/AnnotationUtils.java b/src/main/java/org/openrewrite/openapi/swagger/AnnotationUtils.java index c384b8a..104bc31 100644 --- a/src/main/java/org/openrewrite/openapi/swagger/AnnotationUtils.java +++ b/src/main/java/org/openrewrite/openapi/swagger/AnnotationUtils.java @@ -15,6 +15,7 @@ */ package org.openrewrite.openapi.swagger; +import lombok.experimental.UtilityClass; import org.openrewrite.java.tree.Expression; import org.openrewrite.java.tree.J; @@ -23,7 +24,8 @@ import static java.util.Collections.emptyMap; -public class AnnotationUtils { +@UtilityClass +class AnnotationUtils { public static Map extractAnnotationArgumentAssignments(J.Annotation annotation) { if (annotation.getArguments() == null || annotation.getArguments().isEmpty() ||