In my recipe I select the method parameters which have both the NotNull
and RequestParam
annotations and I want to apply the OpenRewrite recipe AddOrUpdateAnnotationAttribute on these method parameters to set the required attribute to true of the RequestParam
annotation.
I'm struggling how to apply a recipe on a specific piece of code, not on the complete Java class. Does somebody has an example?
An example of the source code before applying my recipe:
import org.springframework.web.bind.annotation.RequestParam;
import javax.validation.constraints.NotNull;
class ControllerClass {
public String sayHello (
@NotNull @RequestParam(value = "name") String name,
@RequestParam(value = "lang") String lang
) {
return "Hello";
}
}
The expected source code after applying my recipe:
import org.springframework.web.bind.annotation.RequestParam;
import javax.validation.constraints.NotNull;
class ControllerClass {
public String sayHello (
@NotNull @RequestParam(required = true, value = "name") String name,
@RequestParam(value = "lang") String lang
) {
return "Hello";
}
}
Only the first parameter name
needs to be adopted as the second parameter has no NotNull
annotation.
My (simplified) recipe:
public class MandatoryRequestParameter extends Recipe {
@Override
public @NotNull String getDisplayName() {
return "Make RequestParam mandatory";
}
@Override
protected @NotNull JavaIsoVisitor<ExecutionContext> getVisitor() {
return new MandatoryRequestParameterVisitor();
}
public class MandatoryRequestParameterVisitor extends JavaIsoVisitor<ExecutionContext> {
@Override
public @NotNull J.MethodDeclaration visitMethodDeclaration(@NotNull J.MethodDeclaration methodDeclaration, @NotNull ExecutionContext executionContext) {
J.MethodDeclaration methodDecl = super.visitMethodDeclaration(methodDeclaration, executionContext);
return methodDeclaration.withParameters(ListUtils.map(methodDecl.getParameters(), (i, p) -> makeRequestParamMandatory(p, executionContext)));
}
private Statement makeRequestParamMandatory(Statement statement, ExecutionContext executionContext) {
if (!(statement instanceof J.VariableDeclarations methodParameterDeclaration) || methodParameterDeclaration.getLeadingAnnotations().size() < 2) {
return statement;
}
AddOrUpdateAnnotationAttribute addOrUpdateAnnotationAttribute = new AddOrUpdateAnnotationAttribute(
"org.springframework.web.bind.annotation.RequestParam", "required", "true", false
);
return (Statement) methodParameterDeclaration.acceptJava(addOrUpdateAnnotationAttribute.getVisitor(), executionContext);
}
}
}
When I execute my recipe, I got following error so my implementation is not the correct way of applying a recipe.
org.openrewrite.UncaughtVisitorException: java.lang.IllegalStateException: Expected to find a matching parent for Cursor{Annotation-\>root}
at org.openrewrite.TreeVisitor.visit(TreeVisitor.java:253)
at org.openrewrite.TreeVisitor.visit(TreeVisitor.java:145)
at org.openrewrite.java.JavaTemplate.withTemplate(JavaTemplate.java:520)
at org.openrewrite.java.JavaTemplate.withTemplate(JavaTemplate.java:42)
at org.openrewrite.java.tree.J.withTemplate(J.java:87)
at org.openrewrite.java.AddOrUpdateAnnotationAttribute$1.visitAnnotation(AddOrUpdateAnnotationAttribute.java:144)
at org.openrewrite.java.AddOrUpdateAnnotationAttribute$1.visitAnnotation(AddOrUpdateAnnotationAttribute.java:78)
at org.openrewrite.java.tree.J$Annotation.acceptJava(J.java:220)
at org.openrewrite.java.tree.J.accept(J.java:60)
at org.openrewrite.TreeVisitor.visit(TreeVisitor.java:206)
at org.openrewrite.TreeVisitor.visitAndCast(TreeVisitor.java:285)
at org.openrewrite.java.JavaVisitor.lambda$visitVariableDeclarations$23(JavaVisitor.java:873)
at org.openrewrite.internal.ListUtils.lambda$map$0(ListUtils.java:141)
at org.openrewrite.internal.ListUtils.map(ListUtils.java:123)
at org.openrewrite.internal.ListUtils.map(ListUtils.java:141)
at org.openrewrite.java.JavaVisitor.visitVariableDeclarations(JavaVisitor.java:873)
at org.openrewrite.java.JavaIsoVisitor.visitVariableDeclarations(JavaIsoVisitor.java:240)
at org.openrewrite.java.JavaIsoVisitor.visitVariableDeclarations(JavaIsoVisitor.java:31)
at org.openrewrite.java.tree.J$VariableDeclarations.acceptJava(J.java:5149)
at org.springframework.sbm.jee.jaxrs.recipes.MandatoryRequestParameter$MandatoryRequestParameterVisitor.makeRequestParamMandatory(MandatoryRequestParameter.java:45)
at org.springframework.sbm.jee.jaxrs.recipes.MandatoryRequestParameter$MandatoryRequestParameterVisitor.lambda$visitMethodDeclaration$0(MandatoryRequestParameter.java:33)
You can get the results you are looking for by using a declarative recipe. You can create a rewrite.yml file in the root of a project and use the rewrite's Maven or Gradle build plugin to apply that recipe.
If using Maven, you can activate that recipe by adding the plugin to your pom.xml:
See https://docs.openrewrite.org/getting-started/getting-started for more details on how to use the build plugins.
However, if you want to restrict the change to only specific parameters, while still using the above recipe, you can write an imperative recipe.
Here is an example using OpenRewrite's test harness:
Hopefully, this helps!