9 jun 2019

Microservicios y Swagger (I)

Microservicios hoy


Todos tenemos claro que las aplicaciones monolíticas, como las habíamos conocido hasta ahora, digamos que ... "han cambiado".
Este tipo de aplicaciones son (fueron) buenas soluciones en determinados momentos, pero cuando estas han querido evolucionar, escalar, se han encontrado ante lo que los que llevamos ya tiempo en el sector, hemos ido sufriendo: codigo spaguetti, falta de documentación, mala organización del codigo, falta de aplicar patrones, refactorización difícil, alto acoplamiento, rendimiento que se degrada en cada versión, etc.
No voy a detallar las bondades que seguramente todos conocemos de las arquitecturas orientadas a microservicios, y tampoco creo ser la persona más entendida para hacerlo, pero sí que todos entendemos que este tipo de arquitecturas nacieron para mejorar precisamente el tener esas aplicaciones monolíticas infumables, que parece no querer morir nunca y que cada nuevo desarrollo se intenta pegar como un chicle pegajoso para seguir engordando la bola.
Pero amigo, los microservicios no son la panacea, si no ponemos un poco de orden, no será muy diferente la situación.

SWAGGER

Resultado de imagen de swagger
Swagger nos ayuda un poco al respecto, como dicen en su web: "Diseñe, describa y documente su API en el primer editor de código abierto completamente dedicado a las API basadas en OpenAPI".
Y eso es lo importante, tener un contrato con el que la otra parte, la que consume, sepa que esperar. Esto es muy cool, porque ambas partes (quien genera el servicio y quien lo consume - Front y Back por ejemplo) pueden desarrollar en paralelo siempre que  se respete ese contrato.

Swagger se puede integrar con las API REST de las siguientes maneras:
  1. Un enfoque de arriba hacia abajo: primero la especificación de API y luego generación de código 
  2. Un enfoque sencillo: primero el código de la API y luego la integración Swagger. Esto es bastante familiar y sobre todo útil cuando ya hay una API REST existente incorporada y la documentación de Swagger debe integrarse.

Ejemplo practico (caso 2)


Tienes el código en mi github


Configuracion del pom.xml



    <properties>
        <java.version>1.8</java.version>
        <springfox.version>2.4.0</springfox.version>
    </properties>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>${springfox.version}</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>${springfox.version}</version>
        </dependency>



Configuración de Swagger

Con la anotacion @EnableSwagger2 en nuestra aplicacion SpringBoot ya deberíamos tener todo lo necesario.

@SpringBootApplication
@EnableSwagger2
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

}

Si navegamos a la url de nuestro microservicio deberíamos poder verlo:
http://localhost:9000/swagger-ui.html


Configuracion ad-hoc de Swagger

Podemos crear una configuracion particular, porque no queramos tener todo lo que nos añade swagger de la manera más genérica.
Para ello debemos añadir cada "feature" a la configuracion.


package com.mrknight.jpaexample.topic.config;

import static springfox.documentation.builders.PathSelectors.regex;

import com.google.common.base.Predicate;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

@EnableSwagger2
@Configuration
public class SwaggerConfiguration {

/**
* Publish a bean to generate swagger2 endpoints
*
* @return a swagger configuration bean
*/
@Bean
public Docket topicsApi() {
return new Docket(DocumentationType.SWAGGER_2).apiInfo(topicsApiInfo()).select().paths(topicPaths())
.apis(RequestHandlerSelectors.any()).build().useDefaultResponseMessages(false);
}

/**
* Api info
*
* @return ApiInfo
*/
private ApiInfo topicsApiInfo() {
return new ApiInfoBuilder().title("Service Topic").version("2.0").license("Apache License Version 2.0").build();
}

/**
* Config paths.
*
* @return the predicate
*/
private Predicate<String> topicPaths() {
return regex("/topic.*");
}
}


Configuracion del modelo


Modificamos el modelo para añadir la configuracion.
Añadimos las anotaciones  @ApiModel y @ApiModelProperty.

package com.mrknight.jpaexample.topic;

import javax.persistence.Entity;
import javax.persistence.Id;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;

@Entity
@ApiModel("Model Topic")
public class Topic {

@Id
@ApiModelProperty(value = "the topic's id", required = true)
private String id;
@ApiModelProperty(value = "the topic's name", required = false)
private String name;
@ApiModelProperty(value = "the topic's descr", required = false)
private String descr;

public String getId() {
return id;
}

public void setId(String id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getDescr() {
return descr;
}

public void setDescr(String descr) {
this.descr = descr;
}

@Override
public String toString() {
return "Topic [descr=" + descr + ", id=" + id + ", name=" + name + "]";
}

public Topic(String id, String name, String descr) {
this.id = id;
this.name = name;
this.descr = descr;
}

public Topic() {

}

}


Configuracion del Controller


Por ultimo añadimos la config del controller.
Tenemos las anotaciones @Api y @ApiOperation


package com.mrknight.jpaexample.topic;

import java.util.List;
import java.util.Optional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;

@RestController
@Api(value = "Topics microservice", description = "This API has a CRUD for topics")
public class TopicController {

@Autowired
private TopicService topicsvc;

@GetMapping("topics")
@ApiOperation(value = "Find all topics", notes = "Return a list")
public List<Topic> getAllTopics() {
return topicsvc.getAllTopics();
}

@PostMapping("topics")
@ApiOperation(value = "Save a topic", notes = "Topic included in body")
public void addTopics(@RequestBody Topic topic) {
topicsvc.addTopic(topic);

}

@GetMapping("topics/{id}")
@ApiOperation(value = "Find a topic by Id", notes = "Return topic")
public Optional<Topic> getTopic(@PathVariable("id") String id) {
return topicsvc.getTopic(id);

}

@PutMapping("topics/{id}")
@ApiOperation(value = "Update topic by Id", notes = "Update topic in body")
public void updateTopic(@PathVariable("id") String id, @RequestBody Topic t) {
topicsvc.updateTopic(t, id);
}

@DeleteMapping("topics/{id}")
@ApiOperation(value = "Delete topic by Id", notes = "Del topic by id")
public void delTopic(@PathVariable("id") String id) {
topicsvc.deleteTopic(id);
}
}



Visualizando


navegamos a la url anterior:
http://localhost:9000/swagger-ui.html

Y ahora vemos como ha cambiado ;) El primer controller sería el que nops genera por defecto y el segundo el que hemos generado nosotros.



Hasta aquí la primera parte de como hacer funcionar vuestra API con swagger.
¿Qué sería lo siguiente? Usar las bondades de swagger para autogenerar ese contrato y que ese código podamos importarlo en nuestro proyecto.


Share This!



No hay comentarios:

Publicar un comentario

Nota: solo los miembros de este blog pueden publicar comentarios.