Skip to content

Commit 8e29e56

Browse files
committed
Add possibility to use custom annotations to detect Spring query parameters
Adds possibility to detect Spring request parameters using custom annotations Adds possibility to detect Spring request body using custom annotations Fixes vojtechhabarta#747 Signed-off-by: Oleksandr Porunov <alexandr.porunov@gmail.com>
1 parent 7a33aa0 commit 8e29e56

5 files changed

Lines changed: 115 additions & 7 deletions

File tree

  • typescript-generator-core/src/main/java/cz/habarta/typescript/generator
  • typescript-generator-gradle-plugin/src/main/java/cz/habarta/typescript/generator/gradle
  • typescript-generator-maven-plugin/src/main/java/cz/habarta/typescript/generator/maven
  • typescript-generator-spring/src

typescript-generator-core/src/main/java/cz/habarta/typescript/generator/Settings.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,8 @@ public class Settings {
100100
public boolean generateSpringApplicationInterface = false;
101101
public boolean generateSpringApplicationClient = false;
102102
public boolean scanSpringApplication;
103+
public List<Class<? extends Annotation>> springCustomQueryParameterAnnotations = new ArrayList<>();
104+
public List<Class<? extends Annotation>> springCustomRequestBodyAnnotations = new ArrayList<>();
103105
@Deprecated public RestNamespacing jaxrsNamespacing;
104106
@Deprecated public Class<? extends Annotation> jaxrsNamespacingAnnotation = null;
105107
@Deprecated public String jaxrsNamespacingAnnotationElement; // default is "value"
@@ -255,6 +257,14 @@ public void loadIncludePropertyAnnotations(ClassLoader classLoader, List<String>
255257
this.includePropertyAnnotations = loadClasses(classLoader, includePropertyAnnotations, Annotation.class);
256258
}
257259

260+
public void loadSpringCustomQueryParameterAnnotations(ClassLoader classLoader, List<String> springCustomQueryParameterAnnotations) {
261+
this.springCustomQueryParameterAnnotations = loadClasses(classLoader, springCustomQueryParameterAnnotations, Annotation.class);
262+
}
263+
264+
public void loadSpringCustomRequestBodyAnnotations(ClassLoader classLoader, List<String> springCustomRequestBodyAnnotations) {
265+
this.springCustomRequestBodyAnnotations = loadClasses(classLoader, springCustomRequestBodyAnnotations, Annotation.class);
266+
}
267+
258268
public void loadExcludePropertyAnnotations(ClassLoader classLoader, List<String> excludePropertyAnnotations) {
259269
this.excludePropertyAnnotations = loadClasses(classLoader, excludePropertyAnnotations, Annotation.class);
260270
}
@@ -292,7 +302,7 @@ public static Map<String, String> convertToMap(List<String> items, String itemNa
292302
}
293303
return result;
294304
}
295-
305+
296306
public void validate() {
297307
if (classLoader == null) {
298308
classLoader = Thread.currentThread().getContextClassLoader();

typescript-generator-gradle-plugin/src/main/java/cz/habarta/typescript/generator/gradle/GenerateTask.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ public class GenerateTask extends DefaultTask {
5959
public List<String> excludeClassPatterns;
6060
public List<String> includePropertyAnnotations;
6161
public List<String> excludePropertyAnnotations;
62+
public List<String> springCustomQueryParameterAnnotations;
63+
public List<String> springCustomRequestBodyAnnotations;
6264
public JsonLibrary jsonLibrary;
6365
public Jackson2Configuration jackson2Configuration;
6466
public GsonConfiguration gsonConfiguration;
@@ -204,6 +206,8 @@ private Settings createSettings(URLClassLoader classLoader) {
204206
settings.loadExtensions(classLoader, Utils.concat(extensionClasses, extensions), extensionsWithConfiguration);
205207
settings.loadIncludePropertyAnnotations(classLoader, includePropertyAnnotations);
206208
settings.loadExcludePropertyAnnotations(classLoader, excludePropertyAnnotations);
209+
settings.loadSpringCustomQueryParameterAnnotations(classLoader, springCustomQueryParameterAnnotations);
210+
settings.loadSpringCustomRequestBodyAnnotations(classLoader, springCustomRequestBodyAnnotations);
207211
settings.loadOptionalAnnotations(classLoader, optionalAnnotations);
208212
settings.loadRequiredAnnotations(classLoader, requiredAnnotations);
209213
settings.loadNullableAnnotations(classLoader, nullableAnnotations);

typescript-generator-maven-plugin/src/main/java/cz/habarta/typescript/generator/maven/GenerateMojo.java

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -571,6 +571,22 @@ public class GenerateMojo extends AbstractMojo {
571571
@Parameter
572572
private boolean scanSpringApplication;
573573

574+
/**
575+
* Additional annotations which are treated as optional request parameters during Spring controllers parsing when Spring's
576+
* RequestParam or ModelAttribute is not used on a specific method's argument.
577+
* This option may be useful when Spring controller method accepts custom resolved parameters.
578+
*/
579+
@Parameter
580+
private List<String> springCustomQueryParameterAnnotations;
581+
582+
/**
583+
* Additional annotations which are treated as request body during Spring controllers parsing when Spring's
584+
* RequestBody is not used on a specific method's argument.
585+
* This option may be useful when Spring controller method accepts custom resolved parameters.
586+
*/
587+
@Parameter
588+
private List<String> springCustomRequestBodyAnnotations;
589+
574590
/**
575591
* Deprecated, use {@link #restNamespacing}.
576592
*/
@@ -800,7 +816,7 @@ public class GenerateMojo extends AbstractMojo {
800816
*/
801817
@Parameter
802818
private String npmBuildScript;
803-
819+
804820
/**
805821
* List of additional NPM <code>dependencies</code>.<br>
806822
* Only applicable when {@link #generateNpmPackageJson} parameter is <code>true</code> and generating implementation file (.ts).<br>
@@ -809,7 +825,7 @@ public class GenerateMojo extends AbstractMojo {
809825
*/
810826
@Parameter
811827
private List<String> npmDependencies;
812-
828+
813829
/**
814830
* List of additional NPM <code>devDependencies</code>.<br>
815831
* Only applicable when {@link #generateNpmPackageJson} parameter is <code>true</code> and generating implementation file (.ts).<br>
@@ -818,7 +834,7 @@ public class GenerateMojo extends AbstractMojo {
818834
*/
819835
@Parameter
820836
private List<String> npmDevDependencies;
821-
837+
822838
/**
823839
* List of additional NPM <code>peerDependencies</code>.<br>
824840
* Only applicable when {@link #generateNpmPackageJson} parameter is <code>true</code> and generating implementation file (.ts).<br>
@@ -973,6 +989,8 @@ private Settings createSettings(URLClassLoader classLoader) {
973989
settings.loadExtensions(classLoader, extensions, extensionsWithConfiguration);
974990
settings.loadIncludePropertyAnnotations(classLoader, includePropertyAnnotations);
975991
settings.loadExcludePropertyAnnotations(classLoader, excludePropertyAnnotations);
992+
settings.loadSpringCustomQueryParameterAnnotations(classLoader, springCustomQueryParameterAnnotations);
993+
settings.loadSpringCustomRequestBodyAnnotations(classLoader, springCustomRequestBodyAnnotations);
976994
settings.loadOptionalAnnotations(classLoader, optionalAnnotations);
977995
settings.loadRequiredAnnotations(classLoader, requiredAnnotations);
978996
settings.loadNullableAnnotations(classLoader, nullableAnnotations);

typescript-generator-spring/src/main/java/cz/habarta/typescript/generator/spring/SpringApplicationParser.java

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import java.beans.IntrospectionException;
2727
import java.beans.Introspector;
2828
import java.beans.PropertyDescriptor;
29+
import java.lang.annotation.Annotation;
2930
import java.lang.reflect.Method;
3031
import java.lang.reflect.Parameter;
3132
import java.lang.reflect.ParameterizedType;
@@ -304,6 +305,7 @@ private void parseControllerMethod(JaxrsApplicationParser.Result result, JaxrsAp
304305
queryParams.add(new RestQueryParam.Single(new MethodParameterModel("size", Long.class), false));
305306
queryParams.add(new RestQueryParam.Single(new MethodParameterModel("sort", String.class), false));
306307
} else {
308+
boolean queryParamAdded = false;
307309
final RequestParam requestParamAnnotation = AnnotationUtils.findAnnotation(parameter, RequestParam.class);
308310
if (requestParamAnnotation != null) {
309311
if (parameter.getType() == MultiValueMap.class) {
@@ -315,6 +317,7 @@ private void parseControllerMethod(JaxrsApplicationParser.Result result, JaxrsAp
315317
parameter.getName()
316318
), parameter.getParameterizedType()), isRequired));
317319
foundType(result, parameter.getParameterizedType(), controllerClass, method.getName());
320+
queryParamAdded = true;
318321
}
319322
}
320323

@@ -330,12 +333,26 @@ private void parseControllerMethod(JaxrsApplicationParser.Result result, JaxrsAp
330333
propertyDescriptor.getPropertyType()
331334
), false));
332335
foundType(result, propertyDescriptor.getPropertyType(), controllerClass, method.getName());
336+
queryParamAdded = true;
333337
}
334338
}
335339
} catch (IntrospectionException e) {
336340
TypeScriptGenerator.getLogger().warning(String.format("Cannot introspect '%s' class: " + e.getMessage(), parameter.getAnnotatedType()));
337341
}
338342
}
343+
344+
if(!queryParamAdded){
345+
for(Class<? extends Annotation> customRequestParamAnnotationClass : settings.springCustomQueryParameterAnnotations){
346+
Annotation customRequestParamAnnotation = AnnotationUtils.findAnnotation(parameter, customRequestParamAnnotationClass);
347+
if(customRequestParamAnnotation != null){
348+
queryParams.add(new RestQueryParam.Single(new MethodParameterModel(
349+
parameter.getName(),
350+
parameter.getParameterizedType()), false));
351+
foundType(result, parameter.getParameterizedType(), controllerClass, method.getName());
352+
break;
353+
}
354+
}
355+
}
339356
}
340357
}
341358

@@ -373,8 +390,16 @@ private MethodParameterModel getEntityParameter(Class<?> controller, Method meth
373390
final List<Type> parameterTypes = settings.getTypeParser().getMethodParameterTypes(method);
374391
final List<Pair<Parameter, Type>> parameters = Utils.zip(Arrays.asList(method.getParameters()), parameterTypes);
375392
for (Pair<Parameter, Type> pair : parameters) {
376-
final RequestBody requestBodyAnnotation = AnnotationUtils.findAnnotation(pair.getValue1(), RequestBody.class);
377-
if (requestBodyAnnotation != null) {
393+
boolean hasRequestBody = AnnotationUtils.findAnnotation(pair.getValue1(), RequestBody.class) != null;
394+
if(!hasRequestBody){
395+
for(Class<? extends Annotation> customRequestBodyAnnotationClass : settings.springCustomRequestBodyAnnotations){
396+
if(AnnotationUtils.findAnnotation(pair.getValue1(), customRequestBodyAnnotationClass) != null){
397+
hasRequestBody = true;
398+
break;
399+
}
400+
}
401+
}
402+
if (hasRequestBody) {
378403
final Type resolvedType = GenericsResolver.resolveType(controller, pair.getValue2(), method.getDeclaringClass());
379404
return new MethodParameterModel(pair.getValue1().getName(), resolvedType);
380405
}

typescript-generator-spring/src/test/java/cz/habarta/typescript/generator/spring/SpringTest.java

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@
99
import cz.habarta.typescript.generator.util.Utils;
1010
import io.swagger.annotations.ApiOperation;
1111
import io.swagger.v3.oas.annotations.Operation;
12+
import java.lang.annotation.ElementType;
1213
import java.lang.annotation.Retention;
1314
import java.lang.annotation.RetentionPolicy;
15+
import java.lang.annotation.Target;
1416
import java.lang.reflect.Method;
1517
import java.util.Arrays;
1618
import java.util.Collection;
@@ -35,7 +37,6 @@
3537
import org.springframework.web.bind.annotation.RequestParam;
3638
import org.springframework.web.bind.annotation.RestController;
3739

38-
3940
public class SpringTest {
4041

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

173+
@Test
174+
public void testCustomQueryParameters() {
175+
final Settings settings = TestUtils.settings();
176+
settings.outputFileType = TypeScriptFileType.implementationFile;
177+
settings.generateSpringApplicationClient = true;
178+
settings.springCustomQueryParameterAnnotations = Arrays.asList(CustomRequestParam.class);
179+
final String output = new TypeScriptGenerator(settings).generateTypeScript(Input.from(Controller8.class));
180+
Assertions.assertTrue(output.contains("echo(queryParams?: { message?: string; }): RestResponse<string>"));
181+
}
182+
183+
@Test
184+
public void testCustomRequestBody() {
185+
final Settings settings = TestUtils.settings();
186+
settings.outputFileType = TypeScriptFileType.implementationFile;
187+
settings.generateSpringApplicationClient = true;
188+
settings.springCustomRequestBodyAnnotations = Arrays.asList(CustomRequestBody.class);
189+
final String output = new TypeScriptGenerator(settings).generateTypeScript(Input.from(Controller9.class));
190+
Assertions.assertTrue(output.contains("setEntity(data: Data1): RestResponse<void>"));
191+
Assertions.assertTrue(output.contains("interface Data1"));
192+
}
193+
172194
@RestController
173195
@RequestMapping("/owners/{ownerId}")
174196
public static class Controller1 {
@@ -203,6 +225,23 @@ public String echo(
203225
}
204226
}
205227

228+
@RestController
229+
public static class Controller8 {
230+
@RequestMapping("/echo3")
231+
public String echo(
232+
@CustomRequestParam String message
233+
) {
234+
return message;
235+
}
236+
}
237+
238+
@RestController
239+
public static class Controller9 {
240+
@RequestMapping(path = "/data2", method = RequestMethod.PUT)
241+
public void setEntity(@CustomRequestBody Data1 data) {
242+
}
243+
}
244+
206245
@Test
207246
public void testQueryParametersWithModel() {
208247
final Settings settings = TestUtils.settings();
@@ -488,4 +527,16 @@ public String shouldBeExcluded() {
488527
}
489528
}
490529

530+
@Retention(RetentionPolicy.RUNTIME)
531+
@Target(ElementType.PARAMETER)
532+
public @interface CustomRequestParam {
533+
534+
}
535+
536+
@Retention(RetentionPolicy.RUNTIME)
537+
@Target(ElementType.PARAMETER)
538+
public @interface CustomRequestBody {
539+
540+
}
541+
491542
}

0 commit comments

Comments
 (0)