Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add possibility to use custom annotations to detect Spring query parameters #750

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ public class Settings {
public boolean generateSpringApplicationInterface = false;
public boolean generateSpringApplicationClient = false;
public boolean scanSpringApplication;
public List<Class<? extends Annotation>> springCustomQueryParameterAnnotations = new ArrayList<>();
public List<Class<? extends Annotation>> springCustomRequestBodyAnnotations = new ArrayList<>();
@Deprecated public RestNamespacing jaxrsNamespacing;
@Deprecated public Class<? extends Annotation> jaxrsNamespacingAnnotation = null;
@Deprecated public String jaxrsNamespacingAnnotationElement; // default is "value"
Expand Down Expand Up @@ -254,6 +256,14 @@ public void loadIncludePropertyAnnotations(ClassLoader classLoader, List<String>
this.includePropertyAnnotations = loadClasses(classLoader, includePropertyAnnotations, Annotation.class);
}

public void loadSpringCustomQueryParameterAnnotations(ClassLoader classLoader, List<String> springCustomQueryParameterAnnotations) {
this.springCustomQueryParameterAnnotations = loadClasses(classLoader, springCustomQueryParameterAnnotations, Annotation.class);
}

public void loadSpringCustomRequestBodyAnnotations(ClassLoader classLoader, List<String> springCustomRequestBodyAnnotations) {
this.springCustomRequestBodyAnnotations = loadClasses(classLoader, springCustomRequestBodyAnnotations, Annotation.class);
}

public void loadExcludePropertyAnnotations(ClassLoader classLoader, List<String> excludePropertyAnnotations) {
this.excludePropertyAnnotations = loadClasses(classLoader, excludePropertyAnnotations, Annotation.class);
}
Expand Down Expand Up @@ -291,7 +301,7 @@ public static Map<String, String> convertToMap(List<String> items, String itemNa
}
return result;
}

public void validate() {
if (classLoader == null) {
classLoader = Thread.currentThread().getContextClassLoader();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ public class GenerateTask extends DefaultTask {
public List<String> excludeClassPatterns;
public List<String> includePropertyAnnotations;
public List<String> excludePropertyAnnotations;
public List<String> springCustomQueryParameterAnnotations;
public List<String> springCustomRequestBodyAnnotations;
public JsonLibrary jsonLibrary;
public Jackson2Configuration jackson2Configuration;
public GsonConfiguration gsonConfiguration;
Expand Down Expand Up @@ -201,6 +203,8 @@ private Settings createSettings(URLClassLoader classLoader) {
settings.loadExtensions(classLoader, Utils.concat(extensionClasses, extensions), extensionsWithConfiguration);
settings.loadIncludePropertyAnnotations(classLoader, includePropertyAnnotations);
settings.loadExcludePropertyAnnotations(classLoader, excludePropertyAnnotations);
settings.loadSpringCustomQueryParameterAnnotations(classLoader, springCustomQueryParameterAnnotations);
settings.loadSpringCustomRequestBodyAnnotations(classLoader, springCustomRequestBodyAnnotations);
settings.loadOptionalAnnotations(classLoader, optionalAnnotations);
settings.loadRequiredAnnotations(classLoader, requiredAnnotations);
settings.loadNullableAnnotations(classLoader, nullableAnnotations);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -558,6 +558,22 @@ public class GenerateMojo extends AbstractMojo {
@Parameter
private boolean scanSpringApplication;

/**
* Additional annotations which are treated as optional request parameters during Spring controllers parsing when Spring's
* RequestParam or ModelAttribute is not used on a specific method's argument.
* This option may be useful when Spring controller method accepts custom resolved parameters.
*/
@Parameter
private List<String> springCustomQueryParameterAnnotations;

/**
* Additional annotations which are treated as request body during Spring controllers parsing when Spring's
* RequestBody is not used on a specific method's argument.
* This option may be useful when Spring controller method accepts custom resolved parameters.
*/
@Parameter
private List<String> springCustomRequestBodyAnnotations;

/**
* Deprecated, use {@link #restNamespacing}.
*/
Expand Down Expand Up @@ -787,7 +803,7 @@ public class GenerateMojo extends AbstractMojo {
*/
@Parameter
private String npmBuildScript;

/**
* List of additional NPM <code>dependencies</code>.<br>
* Only applicable when {@link #generateNpmPackageJson} parameter is <code>true</code> and generating implementation file (.ts).<br>
Expand All @@ -796,7 +812,7 @@ public class GenerateMojo extends AbstractMojo {
*/
@Parameter
private List<String> npmDependencies;

/**
* List of additional NPM <code>devDependencies</code>.<br>
* Only applicable when {@link #generateNpmPackageJson} parameter is <code>true</code> and generating implementation file (.ts).<br>
Expand All @@ -805,7 +821,7 @@ public class GenerateMojo extends AbstractMojo {
*/
@Parameter
private List<String> npmDevDependencies;

/**
* List of additional NPM <code>peerDependencies</code>.<br>
* Only applicable when {@link #generateNpmPackageJson} parameter is <code>true</code> and generating implementation file (.ts).<br>
Expand Down Expand Up @@ -959,6 +975,8 @@ private Settings createSettings(URLClassLoader classLoader) {
settings.loadExtensions(classLoader, extensions, extensionsWithConfiguration);
settings.loadIncludePropertyAnnotations(classLoader, includePropertyAnnotations);
settings.loadExcludePropertyAnnotations(classLoader, excludePropertyAnnotations);
settings.loadSpringCustomQueryParameterAnnotations(classLoader, springCustomQueryParameterAnnotations);
settings.loadSpringCustomRequestBodyAnnotations(classLoader, springCustomRequestBodyAnnotations);
settings.loadOptionalAnnotations(classLoader, optionalAnnotations);
settings.loadRequiredAnnotations(classLoader, requiredAnnotations);
settings.loadNullableAnnotations(classLoader, nullableAnnotations);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.lang.reflect.ParameterizedType;
Expand Down Expand Up @@ -304,6 +305,7 @@ private void parseControllerMethod(JaxrsApplicationParser.Result result, JaxrsAp
queryParams.add(new RestQueryParam.Single(new MethodParameterModel("size", Long.class), false));
queryParams.add(new RestQueryParam.Single(new MethodParameterModel("sort", String.class), false));
} else {
boolean queryParamAdded = false;
final RequestParam requestParamAnnotation = AnnotationUtils.findAnnotation(parameter, RequestParam.class);
if (requestParamAnnotation != null) {
if (parameter.getType() == MultiValueMap.class) {
Expand All @@ -315,6 +317,7 @@ private void parseControllerMethod(JaxrsApplicationParser.Result result, JaxrsAp
parameter.getName()
), parameter.getParameterizedType()), isRequired));
foundType(result, parameter.getParameterizedType(), controllerClass, method.getName());
queryParamAdded = true;
}
}

Expand All @@ -330,12 +333,26 @@ private void parseControllerMethod(JaxrsApplicationParser.Result result, JaxrsAp
propertyDescriptor.getPropertyType()
), false));
foundType(result, propertyDescriptor.getPropertyType(), controllerClass, method.getName());
queryParamAdded = true;
}
}
} catch (IntrospectionException e) {
TypeScriptGenerator.getLogger().warning(String.format("Cannot introspect '%s' class: " + e.getMessage(), parameter.getAnnotatedType()));
}
}

if(!queryParamAdded){
for(Class<? extends Annotation> customRequestParamAnnotationClass : settings.springCustomQueryParameterAnnotations){
Annotation customRequestParamAnnotation = AnnotationUtils.findAnnotation(parameter, customRequestParamAnnotationClass);
if(customRequestParamAnnotation != null){
queryParams.add(new RestQueryParam.Single(new MethodParameterModel(
parameter.getName(),
parameter.getParameterizedType()), false));
foundType(result, parameter.getParameterizedType(), controllerClass, method.getName());
break;
}
}
}
}
}

Expand Down Expand Up @@ -373,8 +390,16 @@ private MethodParameterModel getEntityParameter(Class<?> controller, Method meth
final List<Type> parameterTypes = settings.getTypeParser().getMethodParameterTypes(method);
final List<Pair<Parameter, Type>> parameters = Utils.zip(Arrays.asList(method.getParameters()), parameterTypes);
for (Pair<Parameter, Type> pair : parameters) {
final RequestBody requestBodyAnnotation = AnnotationUtils.findAnnotation(pair.getValue1(), RequestBody.class);
if (requestBodyAnnotation != null) {
boolean hasRequestBody = AnnotationUtils.findAnnotation(pair.getValue1(), RequestBody.class) != null;
if(!hasRequestBody){
for(Class<? extends Annotation> customRequestBodyAnnotationClass : settings.springCustomRequestBodyAnnotations){
if(AnnotationUtils.findAnnotation(pair.getValue1(), customRequestBodyAnnotationClass) != null){
hasRequestBody = true;
break;
}
}
}
if (hasRequestBody) {
final Type resolvedType = GenericsResolver.resolveType(controller, pair.getValue2(), method.getDeclaringClass());
return new MethodParameterModel(pair.getValue1().getName(), resolvedType);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@
import cz.habarta.typescript.generator.util.Utils;
import io.swagger.annotations.ApiOperation;
import io.swagger.v3.oas.annotations.Operation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collection;
Expand All @@ -35,7 +37,6 @@
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;


public class SpringTest {

@Test
Expand Down Expand Up @@ -169,6 +170,27 @@ public void testInheritance() {
Assertions.assertFalse(output.contains("uriEncoding`test/b`"));
}

@Test
public void testCustomQueryParameters() {
final Settings settings = TestUtils.settings();
settings.outputFileType = TypeScriptFileType.implementationFile;
settings.generateSpringApplicationClient = true;
settings.springCustomQueryParameterAnnotations = Arrays.asList(CustomRequestParam.class);
final String output = new TypeScriptGenerator(settings).generateTypeScript(Input.from(Controller8.class));
Assertions.assertTrue(output.contains("echo(queryParams?: { message?: string; }): RestResponse<string>"));
}

@Test
public void testCustomRequestBody() {
final Settings settings = TestUtils.settings();
settings.outputFileType = TypeScriptFileType.implementationFile;
settings.generateSpringApplicationClient = true;
settings.springCustomRequestBodyAnnotations = Arrays.asList(CustomRequestBody.class);
final String output = new TypeScriptGenerator(settings).generateTypeScript(Input.from(Controller9.class));
Assertions.assertTrue(output.contains("setEntity(data: Data1): RestResponse<void>"));
Assertions.assertTrue(output.contains("interface Data1"));
}

@RestController
@RequestMapping("/owners/{ownerId}")
public static class Controller1 {
Expand Down Expand Up @@ -203,6 +225,23 @@ public String echo(
}
}

@RestController
public static class Controller8 {
@RequestMapping("/echo3")
public String echo(
@CustomRequestParam String message
) {
return message;
}
}

@RestController
public static class Controller9 {
@RequestMapping(path = "/data2", method = RequestMethod.PUT)
public void setEntity(@CustomRequestBody Data1 data) {
}
}

@Test
public void testQueryParametersWithModel() {
final Settings settings = TestUtils.settings();
Expand Down Expand Up @@ -488,4 +527,16 @@ public String shouldBeExcluded() {
}
}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface CustomRequestParam {

}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface CustomRequestBody {

}

}