Skip to content

clamothe/spring-typescript-services

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Build Status Coverity Scan Build Status codecov Passing Tests Quality Gate Tech Debt Maven Status Known Vulnerabilities Codacy Badge license

spring-typescript-services

Generate typescript services and type interfaces from spring annotated @RestControllers.

Get strongly typed interfaces for your spring-boot microservices in no time.

Release 0.4.0 is here, What's new?

  • 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.

What is it?

A Java annotation processor to generate a service and TypeScript types to access your spring @RestControllers.

How does it work?

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.

Getting started

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".

Customization

Configuration and custom Templates

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.

ServiceConfig

You can configure the generated controllers through ServiceConfig to enabled debug mode or set the context root all request URLs.

Example

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 {}

About

Generate typescript services and type interfaces from spring annotated restcontrollers.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • Java 56.1%
  • Groovy 27.3%
  • FreeMarker 16.6%