lunes, 15 de junio de 2020

Apache Camel: REST CRUD Service básico

Hoy vamos a ver como hacer un CRUD service básico con Apache Camel. Pero antes, comentemos un poco sobre Apache Camel:
  • Es un framework de integración open source.
  • Permite varias formas de configuración y desarrollo. Pero mayormente esta hecho en Java. 
  • Tiene un largo conjunto de componentes que permiten la integración con otros softwares. 
  • Se puede integrar y desplegar con ayuda de Spring boot, quarkus, de forma standalone y en la nube. 
  • Permite la transformación fácil de un formato a otro. 
En este post veremos como hacer un sencillo REST service con Camel, Spring Boot y apoyandonos en REST DSL. Camel tiene varios DSL (lenguaje de dominio específico), los cuales permiten la configuración, el enroutado y la generación del servicio REST. En este servicio crearemos puntos de entrada, realizaremos determinada lógica y devolveremos el valor pertinente. Los datos los obtendremos de un mock de BBDD. 

Para empezar debemos crear una clase que extienda de RouteBuilder y sobrescribir su método configure. Dentro de ese método tendremos que realizar dos tareas principales:
  • Configurar el funcionamiento de los servicios REST a través de Apache Camel. Indicando el componente a utilizar (servlet, http-netty, jetty, etc), que producir (a través de bindingMode) y otros detalles.  
  • Crearemos los puntos de entrada (a través del método rest()). A partir de este punto de entrada podemos indicar el funcionamiento del servicio a crear. 
    • Con el método get() indicamos que vamos habilitar un servicio asociado a un método HTTP GET y con el contexto book
    • Con el método produces() indicamos que vamos a producir.
    • Con el método router() vamos a redirigir el flujo.
    • Con el método bean() podremos asociar que el mensaje sea tratado por objeto y método concreto. 
restConfiguration().component("servlet").bindingMode(RestBindingMode.json_xml);

rest().get("book").produces(MediaType.APPLICATION_JSON_VALUE).route()
   .bean(DBMockRepository.class, "getAll(})");

Ya solo nos queda invocarlo y obtener el resultado:

curl --location --request GET 'http://localhost:9090/book'

[{"id":1,"name":"Dune","author":"Frank Herbert"},
    {"id":2,"name":"The stars my destination","author":"Alfred Bester"}]

Ahora veremos como hacer el método GET de un objeto concreto. Pero en este caso devolverá un objeto XML, solo para que veamos como hacerlo.

rest().get("book/{id}").description("Details of an book by id").outType(Book.class)
                .produces(MediaType.APPLICATION_XML_VALUE).route()
                .bean(DBMockRepository.class, "findById(${header.id})");

Aquí podemos dos nuevos métodos:
  • description() con el cual podemos asociar una descripción no sólo al propio método en sí, sino también a los parámetros. Útil para documentar con mayor detalle el swagger asociado. 
  • outType() con el que podemos indicar el tipo de objeto de respuesta.
También podemos ver como pasar los path params al método de backend,  a través de ${header}. Y como devolver en formato XML la respuesta. Para esto último debemos tener en cuenta dos cuestiones:
  • Hay que indicar como dependencia la librería camel-jaxb (a igual que para JSON asociamos camel-jackson). 
  • El objeto de salida debe contener anotaciones de jaxb.
Ahora veremos un método POST y como podemos logar un mensaje:

rest().post("book").type(Book.class).produces(MediaType.APPLICATION_JSON_VALUE).route()
   .routeId("postBookRoute").bean(DBMockRepository.class, "create(${body})")
   .log("---creating a book---");

Este método post tendrá asociado un identificador de enrutado y podremos pasar el payload a través del parámetro ${body} al método de backend. 

Por último veremos el método PUT, en el cual podremos validar los parámetros de entrada a través de los métodos simple() y otherwise(). Y como podremos devolver una respuesta apropiada a través del objeto Exchange. 

@Override
public void configure() throws Exception {
    // ....
    rest().put("book/{id}").type(Book.class).produces(MediaType.APPLICATION_JSON_VALUE).route().choice()
        .when().simple("${header.id} < 1").bean(BookRouter.class, "negativeId")
        .otherwise().bean(DBMockRepository.class, "update(${header.id}, ${body})");
}
public void negativeId(final Exchange exchange) {
    exchange.getIn().setBody("{\"msg\":\"id can not be negative\"}");
    exchange.getIn().setHeader(Exchange.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
    exchange.getIn().setHeader(Exchange.HTTP_RESPONSE_CODE, 400);
}

Puedes ver todo el código aquí.

No hay comentarios:

Publicar un comentario