diff --git a/src/main/java/org/openrewrite/openapi/swagger/MigrateApiIgnoreParameterToParameterHidden.java b/src/main/java/org/openrewrite/openapi/swagger/MigrateApiIgnoreParameterToParameterHidden.java new file mode 100644 index 0000000..950c81a --- /dev/null +++ b/src/main/java/org/openrewrite/openapi/swagger/MigrateApiIgnoreParameterToParameterHidden.java @@ -0,0 +1,70 @@ +/* + * Copyright 2026 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 lombok.Getter; +import org.openrewrite.ExecutionContext; +import org.openrewrite.Preconditions; +import org.openrewrite.Recipe; +import org.openrewrite.TreeVisitor; +import org.openrewrite.java.AnnotationMatcher; +import org.openrewrite.java.JavaIsoVisitor; +import org.openrewrite.java.JavaParser; +import org.openrewrite.java.JavaTemplate; +import org.openrewrite.java.search.UsesType; +import org.openrewrite.java.tree.J; + +public class MigrateApiIgnoreParameterToParameterHidden extends Recipe { + + private static final String FQN_API_IGNORE = "springfox.documentation.annotations.ApiIgnore"; + private static final String FQN_PARAMETER = "io.swagger.v3.oas.annotations.Parameter"; + private static final AnnotationMatcher API_IGNORE_MATCHER = new AnnotationMatcher("@" + FQN_API_IGNORE); + + @Getter + final String displayName = "Replace springfox `@ApiIgnore` on method parameters with `@Parameter(hidden = true)`"; + + @Getter + final String description = "Springfox's `@ApiIgnore` is commonly placed on framework-injected controller parameters " + + "(`Principal`, `HttpServletRequest`, `Pageable`, ...). A flat `ChangeType` to `io.swagger.v3.oas.annotations.Hidden` " + + "produces code that does not compile, because `@Hidden` cannot target parameters. Convert parameter usages directly " + + "to `@io.swagger.v3.oas.annotations.Parameter(hidden = true)` and leave method/class-level `@ApiIgnore` for the " + + "subsequent `ChangeType` step."; + + @Override + public TreeVisitor getVisitor() { + return Preconditions.check(new UsesType<>(FQN_API_IGNORE, false), new JavaIsoVisitor() { + @Override + public J.Annotation visitAnnotation(J.Annotation annotation, ExecutionContext ctx) { + J.Annotation a = super.visitAnnotation(annotation, ctx); + if (!API_IGNORE_MATCHER.matches(a)) { + return a; + } + Object parent = getCursor().getParentTreeCursor().getValue(); + if (!(parent instanceof J.VariableDeclarations) || + !(getCursor().getParentTreeCursor().getParentTreeCursor().getValue() instanceof J.MethodDeclaration)) { + return a; + } + maybeRemoveImport(FQN_API_IGNORE); + maybeAddImport(FQN_PARAMETER); + return JavaTemplate.builder("@Parameter(hidden = true)") + .imports(FQN_PARAMETER) + .javaParser(JavaParser.fromJavaVersion().classpathFromResources(ctx, "swagger-annotations-2")) + .build() + .apply(updateCursor(a), a.getCoordinates().replace()); + } + }); + } +} diff --git a/src/main/resources/META-INF/rewrite/examples.yml b/src/main/resources/META-INF/rewrite/examples.yml index 1ada652..a57e613 100644 --- a/src/main/resources/META-INF/rewrite/examples.yml +++ b/src/main/resources/META-INF/rewrite/examples.yml @@ -103,6 +103,31 @@ examples: language: java --- type: specs.openrewrite.org/v1beta/example +recipeName: org.openrewrite.openapi.swagger.MigrateApiIgnoreParameterToParameterHidden +examples: +- description: '`MigrateApiIgnoreParameterToParameterHiddenTest#replacesApiIgnoreOnParameter`' + sources: + - before: | + import springfox.documentation.annotations.ApiIgnore; + + import java.security.Principal; + + class Example { + void handler(@ApiIgnore Principal principal) { + } + } + after: | + import io.swagger.v3.oas.annotations.Parameter; + + import java.security.Principal; + + class Example { + void handler(@Parameter(hidden = true) Principal principal) { + } + } + language: java +--- +type: specs.openrewrite.org/v1beta/example recipeName: org.openrewrite.openapi.swagger.SwaggerToOpenAPI examples: - description: '`SwaggerToOpenAPITest#shouldChangeSwaggerArtifacts`' diff --git a/src/main/resources/META-INF/rewrite/recipes.csv b/src/main/resources/META-INF/rewrite/recipes.csv index 787ad3f..d653759 100644 --- a/src/main/resources/META-INF/rewrite/recipes.csv +++ b/src/main/resources/META-INF/rewrite/recipes.csv @@ -1,17 +1,18 @@ ecosystem,packageName,name,displayName,description,recipeCount,category1,category2,category1Description,category2Description -maven,org.openrewrite.recipe:rewrite-openapi,org.openrewrite.openapi.swagger.ConvertApiResponseCodesToStrings,Convert API response codes to strings,Convert API response codes to strings.,1,Swagger,OpenAPI,Recipes to perform [Swagger](https://swagger.io/) migration tasks.,Recipes to perform [OpenAPI](https://www.openapis.org/) migration tasks. -maven,org.openrewrite.recipe:rewrite-openapi,org.openrewrite.openapi.swagger.ConvertApiResponseHeadersToHeaders,Convert API responseHeaders to headers,Add `headers = @Header(name = ...)` to `@ApiResponse`.,1,Swagger,OpenAPI,Recipes to perform [Swagger](https://swagger.io/) migration tasks.,Recipes to perform [OpenAPI](https://www.openapis.org/) migration tasks. -maven,org.openrewrite.recipe:rewrite-openapi,org.openrewrite.openapi.swagger.ConvertApiResponseToContent,Convert API response to content annotation,Add `content = @Content(mediaType = ...)` and `schema` to `@ApiResponse`.,1,Swagger,OpenAPI,Recipes to perform [Swagger](https://swagger.io/) migration tasks.,Recipes to perform [OpenAPI](https://www.openapis.org/) migration tasks. maven,org.openrewrite.recipe:rewrite-openapi,org.openrewrite.openapi.swagger.MigrateApiImplicitParam,Migrate `@ApiImplicitParam` to `@Parameter`,Migrate `@ApiImplicitParam` to `@Parameter`.,1,Swagger,OpenAPI,Recipes to perform [Swagger](https://swagger.io/) migration tasks.,Recipes to perform [OpenAPI](https://www.openapis.org/) migration tasks. -maven,org.openrewrite.recipe:rewrite-openapi,org.openrewrite.openapi.swagger.MigrateApiModelToSchema,Migrate from `@ApiModel` to `@Schema`,Converts the `@ApiModel` annotation to `@Schema` and converts the "value" attribute to "name".,1,Swagger,OpenAPI,Recipes to perform [Swagger](https://swagger.io/) migration tasks.,Recipes to perform [OpenAPI](https://www.openapis.org/) migration tasks. maven,org.openrewrite.recipe:rewrite-openapi,org.openrewrite.openapi.swagger.MigrateApiParamAllowableValues,Migrate `@ApiParam(allowableValues)` to `@Parameter(schema)`,Migrate `@ApiParam(allowableValues)` to `@Parameter(schema = @Schema(allowableValues))`.,1,Swagger,OpenAPI,Recipes to perform [Swagger](https://swagger.io/) migration tasks.,Recipes to perform [OpenAPI](https://www.openapis.org/) migration tasks. -maven,org.openrewrite.recipe:rewrite-openapi,org.openrewrite.openapi.swagger.MigrateApiParamDefaultValue,Migrate `@ApiParam(defaultValue)` to `@Parameter(schema)`,Migrate `@ApiParam(defaultValue)` to `@Parameter(schema = @Schema(defaultValue))`.,1,Swagger,OpenAPI,Recipes to perform [Swagger](https://swagger.io/) migration tasks.,Recipes to perform [OpenAPI](https://www.openapis.org/) migration tasks. -maven,org.openrewrite.recipe:rewrite-openapi,org.openrewrite.openapi.swagger.MigrateApiToTag,Migrate from `@Api` to `@Tag`,Converts `@Api` to `@Tag` annotation and converts the directly mappable attributes and removes the others.,3,Swagger,OpenAPI,Recipes to perform [Swagger](https://swagger.io/) migration tasks.,Recipes to perform [OpenAPI](https://www.openapis.org/) migration tasks. +maven,org.openrewrite.recipe:rewrite-openapi,org.openrewrite.openapi.swagger.ConvertApiResponseCodesToStrings,Convert API response codes to strings,"Convert API response codes to strings. Handles literal integers, local constant references, and external constant field accesses.",1,Swagger,OpenAPI,Recipes to perform [Swagger](https://swagger.io/) migration tasks.,Recipes to perform [OpenAPI](https://www.openapis.org/) migration tasks. +maven,org.openrewrite.recipe:rewrite-openapi,org.openrewrite.openapi.swagger.MigrateApiIgnoreParameterToParameterHidden,Replace springfox `@ApiIgnore` on method parameters with `@Parameter(hidden = true)`,"Springfox's `@ApiIgnore` is commonly placed on framework-injected controller parameters (`Principal`, `HttpServletRequest`, `Pageable`, ...). A flat `ChangeType` to `io.swagger.v3.oas.annotations.Hidden` produces code that does not compile, because `@Hidden` cannot target parameters. Convert parameter usages directly to `@io.swagger.v3.oas.annotations.Parameter(hidden = true)` and leave method/class-level `@ApiIgnore` for the subsequent `ChangeType` step.",1,Swagger,OpenAPI,Recipes to perform [Swagger](https://swagger.io/) migration tasks.,Recipes to perform [OpenAPI](https://www.openapis.org/) migration tasks. +maven,org.openrewrite.recipe:rewrite-openapi,org.openrewrite.openapi.swagger.MigrateApiModelToSchema,Migrate from `@ApiModel` to `@Schema`,Converts the `@ApiModel` annotation to `@Schema` and converts the "value" attribute to "name".,1,Swagger,OpenAPI,Recipes to perform [Swagger](https://swagger.io/) migration tasks.,Recipes to perform [OpenAPI](https://www.openapis.org/) migration tasks. +maven,org.openrewrite.recipe:rewrite-openapi,org.openrewrite.openapi.swagger.ConvertApiResponseHeadersToHeaders,Convert API responseHeaders to headers,Add `headers = @Header(name = ...)` to `@ApiResponse`.,1,Swagger,OpenAPI,Recipes to perform [Swagger](https://swagger.io/) migration tasks.,Recipes to perform [OpenAPI](https://www.openapis.org/) migration tasks. +maven,org.openrewrite.recipe:rewrite-openapi,org.openrewrite.openapi.swagger.ConvertApiResponseToContent,Convert API response to content annotation,Add `content = @Content(mediaType = ...)` and `schema` to `@ApiResponse`.,1,Swagger,OpenAPI,Recipes to perform [Swagger](https://swagger.io/) migration tasks.,Recipes to perform [OpenAPI](https://www.openapis.org/) migration tasks. maven,org.openrewrite.recipe:rewrite-openapi,org.openrewrite.openapi.swagger.MigrateSwaggerDefinitionToOpenAPIDefinition,Migrate from `@SwaggerDefinition` to `@OpenAPIDefinition`,Migrate from `@SwaggerDefinition` to `@OpenAPIDefinition`.,1,Swagger,OpenAPI,Recipes to perform [Swagger](https://swagger.io/) migration tasks.,Recipes to perform [OpenAPI](https://www.openapis.org/) migration tasks. -maven,org.openrewrite.recipe:rewrite-openapi,org.openrewrite.openapi.swagger.SwaggerToOpenAPI,Migrate from Swagger to OpenAPI,Migrate from Swagger to OpenAPI.,95,Swagger,OpenAPI,Recipes to perform [Swagger](https://swagger.io/) migration tasks.,Recipes to perform [OpenAPI](https://www.openapis.org/) migration tasks. -maven,org.openrewrite.recipe:rewrite-openapi,org.openrewrite.openapi.swagger.UseJakartaSwaggerArtifacts,Use Jakarta Swagger Artifacts,Migrate from javax Swagger artifacts to Jakarta versions.,21,Swagger,OpenAPI,Recipes to perform [Swagger](https://swagger.io/) migration tasks.,Recipes to perform [OpenAPI](https://www.openapis.org/) migration tasks. -maven,org.openrewrite.recipe:rewrite-openapi,org.openrewrite.openapi.swagger.MigrateApiOperationToOperation,Migrate from `@ApiOperation` to `@Operation`,Converts the `@ApiOperation` annotation to `@Operation` and converts the directly mappable attributes and removes the others.,17,Swagger,OpenAPI,Recipes to perform [Swagger](https://swagger.io/) migration tasks.,Recipes to perform [OpenAPI](https://www.openapis.org/) migration tasks. -maven,org.openrewrite.recipe:rewrite-openapi,org.openrewrite.openapi.swagger.MigrateApiResponsesToApiResponses,Migrate from `@ApiResponses` to `@ApiResponses`,"Changes the namespace of the `@ApiResponses` and `@ApiResponse` annotations and converts its attributes (ex. code -> responseCode, message -> description, response -> content).",15,Swagger,OpenAPI,Recipes to perform [Swagger](https://swagger.io/) migration tasks.,Recipes to perform [OpenAPI](https://www.openapis.org/) migration tasks. -maven,org.openrewrite.recipe:rewrite-openapi,org.openrewrite.openapi.swagger.MigrateApiImplicitParamsToParameters,Migrate from `@ApiImplicitParams` to `@Parameters`,Converts `@ApiImplicitParams` to `@Parameters` and the `@ApiImplicitParam` annotation to `@Parameter` and converts the directly mappable attributes and removes the others.,15,Swagger,OpenAPI,Recipes to perform [Swagger](https://swagger.io/) migration tasks.,Recipes to perform [OpenAPI](https://www.openapis.org/) migration tasks. -maven,org.openrewrite.recipe:rewrite-openapi,org.openrewrite.openapi.swagger.MigrateApiParamToParameter,Migrate from `@ApiParam` to `@Parameter`,Converts the `@ApiParam` annotation to `@Parameter` and converts the directly mappable attributes.,11,Swagger,OpenAPI,Recipes to perform [Swagger](https://swagger.io/) migration tasks.,Recipes to perform [OpenAPI](https://www.openapis.org/) migration tasks. -maven,org.openrewrite.recipe:rewrite-openapi,org.openrewrite.openapi.swagger.MigrateApiModelPropertyToSchema,Migrate from `@ApiModelProperty` to `@Schema`,Converts the `@ApiModelProperty` annotation to `@Schema` and converts the "value" attribute to "description".,13,Swagger,OpenAPI,Recipes to perform [Swagger](https://swagger.io/) migration tasks.,Recipes to perform [OpenAPI](https://www.openapis.org/) migration tasks. +maven,org.openrewrite.recipe:rewrite-openapi,org.openrewrite.openapi.swagger.MigrateApiParamDefaultValue,Migrate `@ApiParam(defaultValue)` to `@Parameter(schema)`,Migrate `@ApiParam(defaultValue)` to `@Parameter(schema = @Schema(defaultValue))`.,1,Swagger,OpenAPI,Recipes to perform [Swagger](https://swagger.io/) migration tasks.,Recipes to perform [OpenAPI](https://www.openapis.org/) migration tasks. +maven,org.openrewrite.recipe:rewrite-openapi,org.openrewrite.openapi.swagger.MigrateApiToTag,Migrate from `@Api` to `@Tag`,Converts `@Api` to `@Tag` annotation and converts the directly mappable attributes and removes the others.,2,Swagger,OpenAPI,Recipes to perform [Swagger](https://swagger.io/) migration tasks.,Recipes to perform [OpenAPI](https://www.openapis.org/) migration tasks. +maven,org.openrewrite.recipe:rewrite-openapi,org.openrewrite.openapi.swagger.SwaggerToOpenAPI,Migrate from Swagger to OpenAPI,Migrate from Swagger to OpenAPI.,49,Swagger,OpenAPI,Recipes to perform [Swagger](https://swagger.io/) migration tasks.,Recipes to perform [OpenAPI](https://www.openapis.org/) migration tasks. +maven,org.openrewrite.recipe:rewrite-openapi,org.openrewrite.openapi.swagger.UseJakartaSwaggerArtifacts,Use Jakarta Swagger Artifacts,Migrate from javax Swagger artifacts to Jakarta versions.,11,Swagger,OpenAPI,Recipes to perform [Swagger](https://swagger.io/) migration tasks.,Recipes to perform [OpenAPI](https://www.openapis.org/) migration tasks. +maven,org.openrewrite.recipe:rewrite-openapi,org.openrewrite.openapi.swagger.MigrateApiOperationToOperation,Migrate from `@ApiOperation` to `@Operation`,Converts the `@ApiOperation` annotation to `@Operation` and converts the directly mappable attributes and removes the others.,9,Swagger,OpenAPI,Recipes to perform [Swagger](https://swagger.io/) migration tasks.,Recipes to perform [OpenAPI](https://www.openapis.org/) migration tasks. +maven,org.openrewrite.recipe:rewrite-openapi,org.openrewrite.openapi.swagger.MigrateApiResponsesToApiResponses,Migrate from `@ApiResponses` to `@ApiResponses`,"Changes the namespace of the `@ApiResponses` and `@ApiResponse` annotations and converts its attributes (ex. code -> responseCode, message -> description, response -> content).",8,Swagger,OpenAPI,Recipes to perform [Swagger](https://swagger.io/) migration tasks.,Recipes to perform [OpenAPI](https://www.openapis.org/) migration tasks. +maven,org.openrewrite.recipe:rewrite-openapi,org.openrewrite.openapi.swagger.MigrateApiImplicitParamsToParameters,Migrate from `@ApiImplicitParams` to `@Parameters`,Converts `@ApiImplicitParams` to `@Parameters` and the `@ApiImplicitParam` annotation to `@Parameter` and converts the directly mappable attributes and removes the others.,8,Swagger,OpenAPI,Recipes to perform [Swagger](https://swagger.io/) migration tasks.,Recipes to perform [OpenAPI](https://www.openapis.org/) migration tasks. +maven,org.openrewrite.recipe:rewrite-openapi,org.openrewrite.openapi.swagger.MigrateApiParamToParameter,Migrate from `@ApiParam` to `@Parameter`,Converts the `@ApiParam` annotation to `@Parameter` and converts the directly mappable attributes.,6,Swagger,OpenAPI,Recipes to perform [Swagger](https://swagger.io/) migration tasks.,Recipes to perform [OpenAPI](https://www.openapis.org/) migration tasks. +maven,org.openrewrite.recipe:rewrite-openapi,org.openrewrite.openapi.swagger.MigrateApiModelPropertyToSchema,Migrate from `@ApiModelProperty` to `@Schema`,Converts the `@ApiModelProperty` annotation to `@Schema` and converts the "value" attribute to "description".,7,Swagger,OpenAPI,Recipes to perform [Swagger](https://swagger.io/) migration tasks.,Recipes to perform [OpenAPI](https://www.openapis.org/) migration tasks. diff --git a/src/main/resources/META-INF/rewrite/swagger-2.yml b/src/main/resources/META-INF/rewrite/swagger-2.yml index 0559c0f..2e03920 100644 --- a/src/main/resources/META-INF/rewrite/swagger-2.yml +++ b/src/main/resources/META-INF/rewrite/swagger-2.yml @@ -45,6 +45,7 @@ recipeList: - org.openrewrite.java.ChangeType: oldFullyQualifiedTypeName: io.swagger.annotations.Info newFullyQualifiedTypeName: io.swagger.v3.oas.annotations.info.Info + - org.openrewrite.openapi.swagger.MigrateApiIgnoreParameterToParameterHidden - org.openrewrite.java.ChangeType: oldFullyQualifiedTypeName: springfox.documentation.annotations.ApiIgnore newFullyQualifiedTypeName: io.swagger.v3.oas.annotations.Hidden diff --git a/src/test/java/org/openrewrite/openapi/swagger/MigrateApiIgnoreParameterToParameterHiddenTest.java b/src/test/java/org/openrewrite/openapi/swagger/MigrateApiIgnoreParameterToParameterHiddenTest.java new file mode 100644 index 0000000..8b78e48 --- /dev/null +++ b/src/test/java/org/openrewrite/openapi/swagger/MigrateApiIgnoreParameterToParameterHiddenTest.java @@ -0,0 +1,141 @@ +/* + * Copyright 2026 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.junit.jupiter.api.Test; +import org.openrewrite.DocumentExample; +import org.openrewrite.InMemoryExecutionContext; +import org.openrewrite.java.JavaParser; +import org.openrewrite.test.RecipeSpec; +import org.openrewrite.test.RewriteTest; + +import static org.openrewrite.java.Assertions.java; + +class MigrateApiIgnoreParameterToParameterHiddenTest implements RewriteTest { + + @Override + public void defaults(RecipeSpec spec) { + spec.recipe(new MigrateApiIgnoreParameterToParameterHidden()) + .parser(JavaParser.fromJavaVersion() + .classpathFromResources(new InMemoryExecutionContext(), "swagger-annotations-2") + .dependsOn( + //language=java + """ + package springfox.documentation.annotations; + import java.lang.annotation.ElementType; + import java.lang.annotation.Target; + @Target({ElementType.PARAMETER, ElementType.METHOD, ElementType.TYPE, ElementType.FIELD, ElementType.ANNOTATION_TYPE}) + public @interface ApiIgnore { + String value() default ""; + } + """ + ) + ); + } + + @DocumentExample + @Test + void replacesApiIgnoreOnParameter() { + rewriteRun( + //language=java + java( + """ + import springfox.documentation.annotations.ApiIgnore; + + import java.security.Principal; + + class Example { + void handler(@ApiIgnore Principal principal) { + } + } + """, + """ + import io.swagger.v3.oas.annotations.Parameter; + + import java.security.Principal; + + class Example { + void handler(@Parameter(hidden = true) Principal principal) { + } + } + """ + ) + ); + } + + @Test + void leavesApiIgnoreOnMethodAlone() { + rewriteRun( + //language=java + java( + """ + import springfox.documentation.annotations.ApiIgnore; + + class Example { + @ApiIgnore + void method() { + } + } + """ + ) + ); + } + + @Test + void leavesApiIgnoreOnClassAlone() { + rewriteRun( + //language=java + java( + """ + import springfox.documentation.annotations.ApiIgnore; + + @ApiIgnore + class Example { + } + """ + ) + ); + } + + @Test + void onlyTouchesMatchingParameter() { + rewriteRun( + //language=java + java( + """ + import springfox.documentation.annotations.ApiIgnore; + + import java.security.Principal; + + class Example { + void handler(String pathVar, @ApiIgnore Principal principal, String body) { + } + } + """, + """ + import io.swagger.v3.oas.annotations.Parameter; + + import java.security.Principal; + + class Example { + void handler(String pathVar, @Parameter(hidden = true) Principal principal, String body) { + } + } + """ + ) + ); + } +}