Controller and interfaces with generic methods result in wrong generated client
SomMeri opened this issue · comments
Mária Jurčovičová commented
I have a controller that inherits form a generic interface:
public interface GenericInterface<T extends MyInterface> {
void doSomething(T thing);
}
The controller itself is using a concrete implemenations of the interface:
public void doSomething(@RequestBody MyDto thing) {}
This results in two different client methods generated by typescript generator:
doSomething$GET$something(arg0: MyDto): RestResponse<void> { }
doSomething$GET$something(arg0: MyInterface): RestResponse<void> { }
However, in java, the controller is able to deal only with MyDto
instances. The controller is NOT accepting an arbitrary MyInterface
instance. The doSomething
method is available only once - with the MyDto
parameter.
How to reproduce
// data structures
public interface MyInterface {}
public class MyDto implements MyInterface {}
// controller and its interface
public interface GenericInterface<T extends MyInterface> {
void doSomething(T thing);
}
@RequestMapping("/something")
@RestController
public class ConcreteController implements GenericInterface<MyDto> {
@GetMapping
@Override
public void doSomething(@RequestBody MyDto thing) {
}
}
Main class:
public static void main(String[] args) {
Settings settings = new Settings();
settings.outputKind = TypeScriptOutputKind.module;
settings.outputFileType = TypeScriptFileType.implementationFile;
settings.jsonLibrary = JsonLibrary.jackson2;
settings.generateSpringApplicationClient = true;
settings.generateSpringApplicationInterface = true;
TypeScriptGenerator generator = new TypeScriptGenerator(settings);
String result = generator.generateTypeScript(
Input.from(ConcreteController.class)
);
System.out.println(result);
}
Actual Result
export class RestApplicationClient implements RestApplication {
constructor(protected httpClient: HttpClient) {
}
/**
* HTTP GET /something
* Java method: com.meri.ConcreteController.doSomething
*/
doSomething$GET$something(arg0: MyDto): RestResponse<void> {
return this.httpClient.request({ method: "GET", url: uriEncoding`something`, data: arg0 });
}
/**
* HTTP GET /something
* Java method: com.meri.ConcreteController.doSomething
*/
doSomething$GET$something(arg0: MyInterface): RestResponse<void> {
return this.httpClient.request({ method: "GET", url: uriEncoding`something`, data: arg0 });
}
}
Expected Result
export class RestApplicationClient implements RestApplication {
constructor(protected httpClient: HttpClient) {
}
/**
* HTTP GET /something
* Java method: com.meri.ConcreteController.doSomething
*/
doSomething(arg0: MyDto): RestResponse<void> {
return this.httpClient.request({ method: "GET", url: uriEncoding`something`, data: arg0 });
}
}
Mária Jurčovičová commented
Workaround
Create an extension so that typescript generator ignores bridge methods:
class WorkaroundExtension extends Extension {
public List<TransformerDefinition> getTransformers() {
return Arrays.asList(new TransformerDefinition(ModelCompiler.TransformationPhase.BeforeTsModel, new Transformer()));
}
@Override
public EmitterExtensionFeatures getFeatures() {
return new EmitterExtensionFeatures();
}
}
class Transformer implements ModelTransformer {
public Model transformModel(SymbolTable symbolTable, Model model) {
List<RestApplicationModel> newRestApplications = model.getRestApplications().stream().map(restApplication ->
new RestApplicationModel(
restApplication.getType(),
restApplication.getApplicationPath(),
restApplication.getApplicationName(),
restApplication.getMethods().stream().filter(it -> !it.getOriginalMethod().isBridge()).collect(Collectors.toList()))
).collect(Collectors.toList());
return new Model(model.getBeans(), model.getEnums(), newRestApplications);
}
}
And configure it:
public static void main(String[] args) {
Settings settings = new Settings();
settings.outputKind = TypeScriptOutputKind.module;
settings.outputFileType = TypeScriptFileType.implementationFile;
settings.jsonLibrary = JsonLibrary.jackson2;
settings.generateSpringApplicationClient = true;
settings.generateSpringApplicationInterface = true;
settings.extensions.add(new WorkaroundExtension()); //<-- here
TypeScriptGenerator generator = new TypeScriptGenerator(settings);
String result = generator.generateTypeScript(
Input.from(ConcreteController.class)
);
System.out.println(result);
}