Generate typescript services and type interfaces from spring annotated @RestControllers.
Get strongly typed interfaces for your spring-boot microservices in no time.
- TypeScriptEndpoints can now be Interfaces and they recognize @RequestMapping annotated Methods in Supertypes and Interfaces
- Method suffixes in generated TypeScript files can now be configured or turned off (see @TypeScriptTemplatesConfiguration)
- Unset http-parameters won't be send to the server now
Thanks to jscharett for the provided help.
A Java annotation processor to generate a service and TypeScript types to access your spring @RestControllers.
It generates a service.ts file for every with @TypeScriptEndpoint annotated class and includes functions for every enclosed public Java Method with a @RequestMapping producing JSON. The TypeScript files are generated by populating <#FREEMARKER>-template files. You can specify your own template files or use the bundled defaults.
Just specify the dependency in your maven based build. Maven
<dependency>
<groupId>org.leandreck.endpoints</groupId>
<artifactId>annotations</artifactId>
<version>0.4.0</version>
<scope>provided</scope> <!-- the annotations and processor are only needed at compile time -->
<optional>true</optional> <!-- they need not to be transitively included in dependent artifacts -->
</dependency>
<!-- * because of spring-boot dependency handling they nevertheless get included in fat jars -->Gradle
compileOnly 'org.leandreck.endpoints:annotations:0.4.0'And annotate a Spring @Restcontroller with @TypeScriptEndpoint and your next compile will generate TypeScript-files for every Method with a RequestMapping producing "application/json".
You can configure with @TypeScriptTemplatesConfiguration if and what Method suffix are used in the default templates or provide your own. If you want to provide your own templates have a look at the default ones org.leandreck.enpoints.templates.typescript
If you want to exclude a Method you can annotate it with @TypeScriptIgnore.
You can configure the generated controllers through ServiceConfig to enabled debug mode or set the context root all request URLs.
The following snippet will produce an Angular Module.
import org.leandreck.endpoints.annotations.TypeScriptEndpoint;
import org.springframework.web.bind.annotation.*;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
@RestController
@TypeScriptEndpoint
public class Controller {
@RequestMapping(value = "/api/get", method = RequestMethod.GET, produces = APPLICATION_JSON_VALUE)
public ReturnType get(@RequestParam String someValue) {
return new ReturnType("method: get");
}
// ...
}and the produced TypeScript files from the default templates look like:
controller.generated.ts:
import { HttpClient, HttpParams, HttpRequest, HttpErrorResponse } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { ErrorObservable } from 'rxjs/observable/ErrorObservable';
import 'rxjs/add/operator/catch';
import 'rxjs/add/observable/throw';
import 'rxjs/add/operator/map';
import { ReturnType } from './returntype.model.generated';
import { ServiceConfig } from './api.module';
@Injectable()
export class Controller {
private get serviceBaseURL(): string {
return this.serviceConfig.context + '';
}
private get onError(): (error: HttpErrorResponse) => ErrorObservable {
return this.serviceConfig.onError || this.handleError.bind(this);
}
constructor(private httpClient: HttpClient, private serviceConfig: ServiceConfig) { }
/* GET */
public getGet(someValue: string): Observable<ReturnType> {
const url = this.serviceBaseURL + '/api/get';
const params = this.createHttpParams({
someValue: someValue
});
return this.httpClient.get<ReturnType>(url, {params: params})
.catch((error: HttpErrorResponse) => this.onError(error));
}
/* .. */
private createHttpParams(values: { [index: string]: any }): HttpParams {
let params: HttpParams = new HttpParams();
Object.keys(values).forEach((key: string) => {
const value: any = values[key];
if (value != undefined) { // Check for null AND undefined
params = params.set(key, String(value));
}
});
return params;
}
private handleError(error: HttpErrorResponse): ErrorObservable {
// in a real world app, we may send the error to some remote logging infrastructure
// instead of just logging it to the console
this.log('error', error);
return Observable.throw(error);
}
private log(level: string, message: any) {
if (this.serviceConfig.debug) {
console[level](message);
}
}
}returntype.model.generated.ts:
export interface ReturnType {
method: string;
}serviceconfig.ts:
import { Injectable } from '@angular/core';
import { HttpErrorResponse } from '@angular/common/http';
import { ErrorObservable } from 'rxjs/observable/ErrorObservable';
@Injectable()
export abstract class ServiceConfig {
context?: string;
debug?: boolean;
onError?(error?: HttpErrorResponse): ErrorObservable;
}api.module.ts:
import { NgModule, ModuleWithProviders } from '@angular/core';
import { Controller } from './controller.generated';
import { ServiceConfig } from './serviceconfig';
@NgModule({})
export class APIModule {
static forRoot(serviceConfig: ServiceConfig = {context: ''}): ModuleWithProviders {
return {
ngModule: APIModule,
providers: [
{provide: ServiceConfig, useValue: serviceConfig},
Controller
]
};
}
}index.ts:
export { BodyType } from './bodytype.model.generated';
export { ReturnType } from './returntype.model.generated';
export { Controller } from './controller.generated';
export { ServiceConfig } from './serviceconfig';
export { APIModule } from './api.module';Then, in your root module, import the APIModule using forRoot()
import { NgModule } from '@angular/core';
import { APIModule } from '...';
@NgModule({
declarations: [...],
imports: [APIModule.forRoot({context: '/api'}), ...],
bootstrap: [...]
})
export class AppModule {}