Skip to content

biqasoft-retired/microservice-communicator

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

94 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Microservice communicator

Auto generate HTTP REST classes for interfaces.

Status

Travis Widget Coverage Status Widget Maven Central Widget Download Bintray Widget Javadoc Widget

Requirements

Usage

  • Add @EnableMicroserviceCommunicator to any @Configuration class. Optionally set basePackages or basePackages from @ComponentScan will be used
  • Create interface, for example
@Microservice("test-microservice") // test-microservice is id in service discovery
public interface MicroserviceUsersRepository {

    // HTTP GET - default, you can leave it
    @MicroMapping(path = "/domain/users/mock/one", method = HttpMethod.GET)
    UserAccount returnSingleObject();

    @MicroMapping("/domain/users/mock/null")
    UserAccount returnNullBodyResponse();

    @MicroMapping("/domain/users/mock/null")
    ResponseEntity<UserAccount> returnNonNullBodyResponse();

    @MicroMapping("/domain/users/mock")
    List<UserAccount> returnGenericList();

    @MicroMapping("/domain/users/mock/one")
    ResponseEntity<UserAccount> returnGenericResponseEntity();

    // russian special language
    // equals to findAllUsersInDomainMock() - GET /domain/users/mock
    @MicroMapping
    List<UserAccount> построитьПолучитьМестоDomainГдеUsersГдеMock();

    // in tests url will be /domain/users/mock/one
    @MicroMapping("/domain/{s1}/{s2}/one")
    UserAccount returnSingleObjectWithPathParam(@MicroPathVar("s1") String s,
                                                @MicroPathVar("s2") String s2);

    @MicroMapping("/domain/users/mock/one")
    JsonNode returnJson();

    @MicroMapping(path = "/domain/users/mock/one", convertResponseToMap = true)
    Map<String, Object> returnResponseAsJsonMap();

    @MicroMapping("/domain/users/mock/send_invalid_request")
    ResponseEntity<UserAccount> returnInvalidResponse();

    @MicroMapping("/domain/users/mock/send_invalid_request")
    UserAccount returnInvalidResponseException();

    // this endpoint simulate server error
    // will be 3 attempts (by default) to try again with interval (default 1100ms)
    @MicroMapping("/domain/users/mock/generate_500_http_error")
    UserAccount returnInvalidServerException();

    @MicroMapping("/domain/users/mock/generate_500_http_error")
    ResponseEntity<UserAccount> returnInvalidServerExceptionEntity();

    @MicroMapping(path = "/domain/users/mock/authenticate", method = HttpMethod.POST)
    UserAccount returnAuthenticatedUser(@MicroPayloadVar("username") String username,
                                        @MicroPayloadVar("password") String password);

    // will be POST json
    // {
    //    "username": %username%,
    //    "password": %password%,
    //    "address" : {
    //                   "country": %country%,
    //                   "city": %city%
    //                }
    // }
    // %username% etc... will be replaced by java function param
    @MicroMapping(path = "/domain/users/mock/echo", method = HttpMethod.POST)
    UserAccount returnAuthenticatedUserComplexEcho(@MicroPayloadVar("username") String username,
                                                   @MicroPayloadVar("password") String password,
                                                   @MicroPayloadVar("address.country") String country,
                                                   @MicroPayloadVar("address.city") String city);

    @MicroMapping(path = "/domain/users/mock/echo", method = HttpMethod.POST)
    byte[] returnByteArrayEcho(@MicroPayloadVar("username") String username,
                                              @MicroPayloadVar("password") String password,
                                              @MicroPayloadVar("address.country") String country,
                                              @MicroPayloadVar("address.city") String city);

    // use java 8 with javac -parameters to add parameter name and use as json node name
    // instead of annotation value with dot(.) delimiter - in parameter name - is decimeter
    @MicroMapping(path = "/domain/users/mock/echo", method = HttpMethod.POST)
    UserAccount returnPayloadFromName(@MicroPayloadVar String username,
                                      @MicroPayloadVar String password,
                                      @MicroPayloadVar String address_country,
                                      @MicroPayloadVar String address_city);

    // from server we will get json like that {username: ... , address :{city: "SomeString" } }
    // so, if we are interesting only in city field, we can immediately return it, instead of all server return data 
    @MicroMapping(path = "/domain/users/mock/echo", method = HttpMethod.POST, returnExpression = "address.city")
    String returnJsonExpression(@MicroPayloadVar String username,
                                      @MicroPayloadVar String password,
                                      @MicroPayloadVar String address_country,
                                      @MicroPayloadVar String address_city);

    @MicroMapping("/domain/users/mock/one")
    CompletableFuture<UserAccount> returnCompletableFutureSingleObject();

    @MicroMapping("/domain/users/mock")
    CompletableFuture<List<UserAccount>> returnListCompletableFutureObjects();

    @MicroMapping("/domain/users/mock/one")
    Optional<UserAccount> returnSingleOptionalObject();

    @MicroMapping("/domain/users/mock")
    Optional<List<UserAccount>> returnListOptionalObject();

    @MicroMapping("/domain/users/mock/null")
    Optional<UserAccount> returnSingleOptionalEmptyObject();

    @MicroMapping("/domain/users/mock/authorization_header")
    JsonNode sendAuthHeaderInEcho(@MicroHeader("Authorization") String authHeader);

    // default will be executed on error main request
    @MicroMapping("/domain/users/mock/generate_500_http_error")
    default UserAccount returnDefaultValue() {
        return new UserAccount("I'm default Java 8 interface");
    }

    // default will be executed on error main request
    @MicroMapping("/domain/users/mock/generate_500_http_error")
    default UserAccount returnDefaultValue(String name) {
        return new UserAccount(name);
    }
}
  • Inject MicroserviceUsersRepositoryTest to any bean
@Service
public class UsersRepository {

    @Autowired
    private MicroserviceUsersRepository microserviceUsersRepository;

    public void testReturnGenericList(){
        List<UserAccount> allUsers = microserviceUsersRepository.returnGenericList();
    }

}

Maven dependency

To use this extension on Maven-based projects, use following dependency:

<dependency>
  <groupId>com.biqasoft</groupId>
  <artifactId>microservice-communicator</artifactId>
  <version>1.2.13-RELEASE</version>
</dependency>

Return type from interface can be:

  • CompletableFuture<> - async execute request
  • Any your Data object (DTO), will be deserialize with Jackson; supported return List<SomeClass>
  • ResponseEntity<> - spring MVC object, with headers, response code, response body
  • JsonNode - if you do not want to map response to some object
  • Optional<>

How it works

Internally, library use spring bean LoadBalancerClient with default implementation of spring cloud RibbonLoadBalancerClient. So, you can use Consul, Zookeeper, Cloudfoundry.

if you have not configured service discovery in Spring Cloud, you can do it easily, for example for consul create configuration bean

@EnableDiscoveryClient
@Configuration
public class ServiceDiscoveryConfiguration {

    @Value("${spring.cloud.consul.host}")
    private String serverUrl;

    @Bean
    public ConsulClient consulClient(){
        ConsulClient client = new ConsulClient( serverUrl );
        return client;
    }

}

Demo

Exceptions

If you have return type ResponseEntity you will never have exceptions from method. For example with responseEntity.getStatusCode().is2xxSuccessful(). Also, you have access to headers and body with responseEntity.getHeaders() and responseEntity.getBody()

If you have return type some object (not ResponseEntity) following unchecked exceptions can be thrown:

  • InvalidRequestException this exceptions is throws if remote host set HTTP status 422 (Unprocessable Entity), 401, 403. When we recieve such response, we immedialty throw exception, without trying new requests to another microservices
  • InternalSeverErrorProcessingRequestException if we have non 422, 401, 403 response code, can not find suitable microservice URL in service discovery predetermined number of time, or can not retry request to microservices predetermined number of times(on error)

Java 9

Spring 4.3 requires to add custom VM option to run -addmods java.xml.bind, optionally you can use -ea -XaddExports:java.base/jdk.internal.loader=ALL-UNNAMED -addmods java.xml.bind

Jackson is used to serialize/deserialize json. So, you can customize ObjectMapper bean, by injected it with @Qualifier("defaultObjectMapperConfiguration")

License

Copyright © 2016 Nikita Bakaev. Licensed under the Apache License.

About

Auto generate spring HTTP REST beans, based on interfaces (such as retrofit, spring cloud feign)

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published