View on GitHub

spring-rest-2-ts

spring rest 2 ts is typescript generator which produces data model and services in typescript based on Spring MVC annotations. It supports generation for Angular and React

Spring rest2ts generator

spring-rest2ts-generator generates TypeScript code based on Spring MVC REST controllers and data model for HTTP requests and responses.

Installation

To add a dependency on spring-rest2ts-generator using Maven, use the following:

<dependency>
    <groupId>com.blue-veery</groupId>
    <artifactId>spring-rest2ts-generator</artifactId>
    <version>1.2.1</version>
</dependency>
<dependency>
  <groupId>com.blue-veery</groupId>
  <artifactId>spring-rest2ts-spring</artifactId>
  <version>1.2.1</version>
</dependency>
<dependency>
  <groupId>com.blue-veery</groupId>
  <artifactId>spring-rest2ts-jackson</artifactId>
  <version>1.2.1</version>
</dependency>

Configuration

Due to greater flexibility typescript generator is configured by code, no configuration files are needed. This gives possibility to easily extend generator in places where it is needed Here is the simplest generator configurator:

    Rest2tsGenerator tsGenerator = new Rest2tsGenerator();
    
    // Java Classes filtering
    tsGenerator.setModelClassesCondition(new ExtendsJavaTypeFilter(BaseDTO.class));
    tsGenerator.setRestClassesCondition(new ExtendsJavaTypeFilter(BaseCtrl.class));
    
    // Java model classes converter setup
    JacksonObjectMapper jacksonObjectMapper = new JacksonObjectMapper();
    jacksonObjectMapper.setFieldsVisibility(JsonAutoDetect.Visibility.ANY);
    modelClassesConverter = new ModelClassesToTsInterfacesConverter(jacksonObjectMapper);
    tsGenerator.setModelClassesConverter(modelClassesConverter);
    
    // Spring REST controllers converter
    restClassesConverter = new SpringRestToTsConverter(new Angular4ImplementationGenerator());
    tsGenerator.setRestClassesConverter(restClassesConverter);
    
    // set of java root packages for class scanning
    javaPackageSet = Collections.singleton("com.blueveery.springrest2ts.examples");
    tsGenerator.generate(javaPackageSet, Paths.get("../target/ts-code"));

Examples

Module spring-rest2ts-examples contains few model classes and REST controllers, class TsCodeGenerationsTest and ExtendedTsCodeGenerationsTest contains few ready to run configuration examples (they are not unit tests, just examples), each example generates code to directory target/classes/test-webapp/src, its parent directory target/classes/test-webapp contains webpack and npm setup which is valid for all generator configurations apart from configurableTsModulesConverter which has different entry points due to changed module names. To compile generated TypeScript code just execute following commands in folder target/classes/test-webapp:

    npm install
    npm run build
    

or just review generated code in Your favourite IDE which supports TypeScript

Features

From model classes there are generated TypeScript interfaces and from REST controllers full working Angular services based on Observable API or plain JavaScript services based on Promises API. The main idea is that Spring annotations describing REST endpoints have enough information to compose HTTP request and call REST endpoints so TypeScript code could be automatically generated. Code created by typescript generator reduces the amount of handwritten code on the frontend and gives type-safe API to the backend, changes in URL path are hidden, in case of REST endpoint refactoring, generated code will reflect these changes which will cause compile-time error in the web app which reduces time on testing

Supported features:

Basic Configuration

Java Classes filtering

Generator needs two filters to select java classes for which TypeScript types will be generated, these filters allows to skip Java classes in given packages for which TypeScript generation is not needed. By default Rest2tsGenerator for model classes and rest controllers has RejectJavaTypeFilter which rejects all classes in a given java packages. Such filter could be used to skip generation for REST controllers when only Typescript types are needed for Java model classes. There are following filters which allows to build complex conditions:

Java model classes converter

Java classes which describe payload model are generated to TypeScript interfaces. During model serialization to JSON, object mapper is applying some mappings(some of them are required for example in JSON, Date type must be represented as string or number) based on some default configuration or dedicated annotations. Currently Jackson Object mapper is supported. JacksonObjectMapper allows to set fields, getters and setters visibility. Based on this Typescript interface fields are generated. There are supported following Jackson annotations :

Java collections are converted into JavaScript arrays, Java Map is converted into object where key has a string type and value is converted Typescript Type

Spring REST controllers converter

From REST classes there is generated working implementation in TypeScript which based on Spring annotations builds complete HTTP request. Such implementation depends also used JavaScript framework, there is different approach to HTTP calls in Angular and React so SpringRestToTsConverter requires instance of ImplementationGenerator. There are available two such implementation generators

There are supported following Spring annotations:

Final step in generator configuration

Generator takes as input set of java packages which should be scanned for java classes and output path where TypesScript modules will be generated. There are required only root packages in case if following packages are present:

Advanced configuration

Modules converters

Modules converter defines, in which TypeScript module, should be placed generated TypeScript type for given Java class There are two types of modules converters:

Imports between generated types are using path from root so in tsconfig.json, in the web project, in which generated code is used, baseUrl must be defined as it is in examples.

ConfigurableTsModulesConverter

ConfigurableTsModulesConverter takes as an input maping from java packages to typeScript module which allows to specify name of each generated module. This generator optionally as an input takes also TsModuleCreatorConverter to generate modules for packages which are missing in the mapping

    HashMap<String, TSModule> packagesMap = new HashMap<>();
    packagesMap.put("com.blueveery.springrest2ts.examples.model", new TSModule("model", Paths.get("app/sdk/model"), false));
    TSModule servicesModule = new TSModule("services", Paths.get("app/sdk/services"), false);
    packagesMap.put("com.blueveery.springrest2ts.examples.ctrls.core", servicesModule);
    packagesMap.put("com.blueveery.springrest2ts.examples.ctrls", servicesModule);
    ConfigurableTsModulesConverter moduleConverter = new ConfigurableTsModulesConverter(packagesMap);
    tsGenerator.setJavaPackageToTsModuleConverter(moduleConverter);

Enums converters

Java enums could be serialized as strings or ordinals. If they are serialized to strings in TypesScript union of enum values could be used, if enums are serialized to ordinals TypesScript enums could be generated. To handle this there are provided two enums converters

and configured in code:

    tsGenerator.setEnumConverter(new JavaEnumToTsUnionConverter());

or

    tsGenerator.setEnumConverter(new JavaEnumToTsEnumConverter());

Type names mapping

Between Java and TypeScript there are different naming conventions for example model classes could end with postfix “DTO” which could be unwanted in generated Typescript model, REST controllers could have “Ctrl” postfix where generated Angular services should have postfix “Service”. To handle this situation type name mappers are provided. Currently there is only one implementation SubstringClassNameMapper but any one could crete new one by implementing interface ‘ClassNameMapper’ Each class converter is using name mapper so they can be defined for enum, model and REST classes

    modelClassesConverter.setClassNameMapper(new SubstringClassNameMapper("DTO", ""));
    restClassesConverter.setClassNameMapper(new SubstringClassNameMapper("Ctrl", "Service"));

Custom type mapping

During serialization some types are converted by default, such conversion does not depends on any annotations to learn how convert types. For example UUID could be converted to string, Date to number or string/array of numbers. To handle such situation generator has map of custom mapping which allows to define such new mappings:

    tsGenerator.getCustomTypeMapping().put(UUID.class, TypeMapper.tsString);
    tsGenerator.getCustomTypeMapping().put(BigInteger.class, TypeMapper.tsNumber);
    tsGenerator.getCustomTypeMapping().put(LocalDateTime.class, TypeMapper.tsNumber);
    tsGenerator.getCustomTypeMapping().put(LocalDate.class, new TSArray(TypeMapper.tsNumber));

Class TypeMapper has static fields for all base TypeScript types, and for other cases types could be created using TS model like for array of numbers new TSArray(TypeMapper.tsNumber)

Nullable types

TypeScript has s great feature to warn about cases where value could be null. To use it TS compiler option must be set strictNullChecks and proper fields in model set as nullable or proper parameters in methods which make REST calls. Generators support this by marking TS fields based on information taken from corresponding java classes. Generator is using NullableTypesStrategy with default implementation DefaultNullableTypesStrategy which marks types as nullable if

DefaultNullableTypesStrategy has settings which allows to configure which of above option use, by default all they are used

    DefaultNullableTypesStrategy nullableTypesStrategy = new DefaultNullableTypesStrategy();
    nullableTypesStrategy.setUsePrimitiveTypesWrappers(false);
    tsGenerator.setNullableTypesStrategy(nullableTypesStrategy);

Conversion Listeners

Each converter( enum, model classes, rest classes) allows for registering conversion listeners, such listeners give possibility to extend conversion with new functionality. Now there is one, such listener provided SwaggerConversionListener which adds comments to TypeScript types based on swagger 2.0 io.swagger.oas.annotations.Operation annotation

    restClassesConverter.getConversionListener().getConversionListenerSet().add(new SwaggerConversionListener());

Java compiler setup

Java compiler by default optimizes methods parameters names, to have readable parameters names in Typescript at least for REST controllers modules this optimization should be switched off

        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.1</version>
            <configuration>
                <source>1.8</source>
                <target>1.8</target>
                <compilerArgs>
                    <compilerArg>-parameters</compilerArg>
                </compilerArgs>
            </configuration>
        </plugin>

Some special cases

Model class getter and setter types differs

In most cases such situation is just a bug but there could be a situations in which this required to handle this TypeScript filed is an union of getter and setter type, in this order. Special case of this situation is when getter or setter is missing. For missing setter field in TypeScript it is marked as readonly. For missing getter (for example we send password but do not read them from server) TypeScript field is union of undefined | <setter type> because after read from server such fields will have value of undefined

Java REST controllers overloaded methods

Java allows for overloaded methods what is not supported by TypeScript/JavaScript. To solve this problems if overloaded methods are met in REST controller, generated method names in TypeScript are changed by appending in first round HTTP methods to TypeScript method name if they differ, if not URL path is appended splitted on / and joined with _

Unsupported mappings, coming soon…