4. (1,5 puntos) Servicio REST

En esta sesión continuaremos trabajando con la parte del servidor del proyecto web. Implementaremos la capa REST del proyecto, utilizando JAX-RS para definir el API y basándonos en las funcionalidades implementadas en la capa de negocio de la sesión anterior. Las características básicas del API REST a desarrollar serán:

  • Interfaz REST para las funcionalidades implementadas en la capa de negocio por las clases de servicio (UsuarioServicio y LibroServicio)

  • Trabajamos con objetos JSON

  • Implementación del login y las restricciones de seguridad usando el método BASIC

  • Conversión de las excepciones generadas por las capas inferiores en códigos de error HTTP

Todas las clases necesarias las crearemos en el paquete org.expertojava.jbibrest.rest.

Todas las URIs de las peticiones tendrán el prefijo /jbib-rest/api/. Por ejemplo: GET http://<servidor>/jbib-rest/api/libros/0321127420, en donde <servidor> será el host y puerto desde donde se servirán las peticiones REST. Por ejemplo si ejecutamos el proyecto en nuestra máquina tendrá el valor localhost:8080.

4.1. Pantallas y esquema de navegación del cliente

Para poner en contexto el API REST que vamos a desarrollar, es conveniente considerar cómo va a ser utilizada desde la aplicación cliente. La aplicación cliente tendrá las siguientes pantallas:

  • Login: pantalla inicial en la que accederemos a la biblioteca mediante un login y password

  • Listado de libros: muestra los libros pertenecientes a la biblioteca,

  • Detalle de libro: muestra información detallada sobre un libro en concreto

  • Listado de préstamos activos y multa activa: muestra el listado de préstamos y multa activa del usuario

Se podrá navegar entre ellas según el siguiente esquema:

La capa rest estará formada por dos recursos: libros y usuarios, que implementarán los servicios a los que se accederá desde las pantallas anteriores.

Pasemos ahora a comentar cómo se podrá "navegar" por dichas pantallas, según la información que proporcionan los servicios rest.

4.1.1. Acceso a la bibilioteca: pantalla de login

Para acceder a la biblioteca, el usuario tendrá que proporcionar su login y password. Desde esta pantalla de inicio, se invocará al método GET /usuarios/{usuario}. {usuario} es el login del usuario, no su ID. Si devuelve error 401 nos mantenemos en la página. Cualquier error 401 en cualquier otra petición redirige aquí, Si devuelve información del usuario, vamos a la siguiente pantalla de listado de libros.

4.1.2. Listado de libros

Se muestran todos los libros de la biblioteca y se hace la paginación en el cliente. La información sobre los libros la proporciona la operación GET /libros, como se aprecia en la siguiente figura:

A partir de dicho listado, el usuario puede que quiera solicitar información más detallada de alguno de los libros, lo que nos llevará a la pantalla en la que mostramos el detalle de un libro.

4.1.3. Detalle de libro

En esta pantalla se muestran los detalles de uno de los libros, y además una lista de recomendaciones (libros recomendados por los lectores del libro mostrado). La información mencionada será proporcionada por las operaciones GET /libros/{id} y GET /libros/{id}/recomendaciones?numrec=3 respectivamente. Aprovecharemos el mismo componente del listado de libros para las recomendaciones.

El botón de pedir préstamo sólo aparecerá si el libro tiene ejemplares disponibles (lo debe indicar la información devuelta por la operación GET /libros), y si el usuario logeado en el cliente no tiene multas pendientes. En este caso la petición de un préstamo estará implementada con una operación POST /usuarios/{id}/prestamos Si la petición POST a prestamos devuelve un error se mostrará un alert indicando el motivo del error.

4.1.4. Libros prestados y multa activa

A esta pantalla podemos acceder desde cualquiera de las anteriores. Muestra el listado de préstamos activos del usuario, así como la multa activa (si la hubiese). En la tabla, saldrán en rojo aquellos libros cuya fecha de devolución ha vencido, y en amarillo aquellos cuya fecha de devolución sea igual o inferior a tres días.

La información de esta pantalla será proporcionada por la operación GET /usuarios/{id}/prestamos

4.2. API REST

Las operaciones sobre el recurso Libros son:

  • GET /libros?autor=nombre

  • GET /libros?titulo=tituloLibro

  • GET /libros/{id}

  • GET /libros/{id}/recomendaciones?num=1

Las operaciones sobre el recurso Usuarios son:

  • GET /usuarios/{id}

  • GET /usuarios/{id}/prestamos

  • POST /usuarios/{id}/prestamos

  • POST /usuarios/{id}/devoluciones

4.2.1. Recurso Libros

Veamos con más detalle cada una de las operaciones sobre este recurso.

  • GET /libros: Devuelve la lista de todos los libros. Cada ítem de libro contiene la URI poder acceder a dicho libro.

Es posible solicitar un listado de libros de un autor (GET /libros?autor=autorLibro). También se puede pedir solamente un listado de libros cuyo título contenga el valor del parámetro "titulo" (GET /libros?título=tituloLibro).

No se puede solicitar un listado por título y autor a la vez, es decir GET /libros?titulo=tituloLibro&?autor=autorLibro. En este caso generaremos la excepción RestException.BUSQUEDA_POR_TITULO_Y_AUTOR_NO_IMPLEMENTADA, con el mensaje "Búsqueda por título y autor no implementada"

Se podrá devolver una lista vacía en el caso de que no haya libros en la biblioteca

Ejemplo:

Solicitud del listado de libros del autor "Martin Fowler"
GET http://localhost:8080/jbib-rest/api/libros?autor=Martin%20Fowler

Resultado:

[
  {
   "id": 1
   "self": "http://localhost:8080/jbib-rest/api/libros/1",
   "isbn": "0321127420",
   "titulo": "Patterns Of Enterprise Application Architecture",
   "autor": "Martin Fowler",
   "image": "http://localhost:8080/jbib-rest/media/0321127420-small.png",
 },
 {
   "id":2
   "self": "http://localhost:8080/jbib-rest/api/libros/2",
   "isbn": "0132350882",
    "titulo": "Clean code",
    "autor": "Robert C. Martin",
    "image": "http://localhost:8080/jbib-rest/media/0132350882-small.png"
  }
]

Cada ítem de libro incluye la url de la carátula del libro, en versión "small". Todas las carátulas estarán accesibles a través de la URL base "http://localhost:8080/jbib-rest/media/". El nombre del fichero que contiene la carátula en versión pequeña se obtiene añadiendo el sufijo "-small" al nombre del fichero del atributo portadaURI de la clase `Libro de la capa de negocio.

  • GET /libros/{id}: Devuelve el detalle de un libro, contiene, además de la información anterior, el número de ejemplares totales y número de ejemplares disponibles.

Ejemplo:

Solicitud del detalle del libro con id 1
http://localhost:8080/jbib-rest/api/libros/1

Resultado:

Resultado Json de la petición: http://localhost:8080/jbib-rest/api/libros/1
{
    "id": 1,
    "self": "http://localhost:8080/jbib-rest/api/libros/1",
    "isbn": "0321127420",
    "titulo": "Patterns Of Enterprise Application Architecture",
    "autor": "Martin Fowler",
    "image": "http://localhost:8080/jbib-rest/media/img/0321127420.png",
    "ejemplares": 4,
    "disponibles": 2
}

Si el libro con id = {id} no es encontrado en la biblioteca (se obtiene un valor de libro null), entonces lanzaremos la excepción RestException.LIBRO_NO_EXISTENTE, con el mensaje "Libro no existente".

  • GET /libros/{id}/recomendaciones?num=: Devuelve una lista de libros recomendados relacionados con un libro dado. Por defecto se muestra una recomendación, pero este número puede variar en función del parámetro de consulta num

Solicitud de recomendaciones del libro con id 2
 http://localhost:8080/jbib-rest/api/libros/2/recomendaciones?num=2

Resultado:

 [
  {
    "id": 3
    "self": "http://localhost:8080/jbib-rest/api/libros/3",
    "isbn": "0321146530",
    "titulo": "Test Driven Development",
    "autor": "Kent Beck",
    "imagen": "http://expertojava.ua.es/media/0321146530-small.jpg"
  },
  {
    "id": 1
    "self": "http://localhost:8080/jbib-rest/api/libros/1",
    "isbn": "0321127420",
    "titulo": "Patterns Of Enterprise Application Architecture",
    "autor": "Martin Fowler",
    "imagen": "http://expertojava.ua.es/media/0321127420-small.jpg"
  }
]

Si no hay recomendaciones para el libro referenciado en la petición, se mostrará una lista vacía

Si el libro con id = {id} no es encontrado en la biblioteca (se obtiene un valor de libro null), entonces lanzaremos la excepción 404 Not Found, con el mensaje "Libro no existente"

4.2.2. Recurso Usuario

Veamos con más detalle cada una de las operaciones sobre este recurso.

  • GET /usuarios/{id}: Devuelve información detallada de un usuario, dado su login. Se trata de una operación restringida al usuario. El usuario debe estar registrado en el sistema, por lo que se requiere enviar la autorización en la cabecera. Utilizaremos seguridad Basic.

Ejemplo:

GET http://localhost:8080/jbib-rest/api/usuarios/antonio.perez
Authorization: Basic YW50b25pby5wZXJlejphbnRvbmlv

Resultado:

{
  "id": 2,
  "login": "antonio.perez",
  "eMail": null,
  "nombre": "Antonio",
  "apellido1": "Perez",
  "apellido2": "Sierra",
  "multa": {
    "hasta": "2015-12-12"
  },
  "num_prestamos": 1,
  "tipo_usuario": "Alumno",
  "estado": "MULTADO"
}

Si el usuario con login = {usuario} no está registrado como usuario de la biblioteca y con el mismo login, lanzaremos la excepción RestException.USUARIO_NO_ES_EL_LOGUEADO, con el mensaje ""El usuario no es el logueado".

Si no se encuentra la información de dicho usuario, lanzaremos la excepción RestException.USUARIO_NO_EXISTENTE, con el mensaje "Usuario no existente".

  • GET /usuarios/{id}/prestamos: Devuelve la lista de libros que tiene prestados el usuario. Para cada libro se indica la URI con la que acceder a dicho recurso. Se trata de una operación restringida al usuario. El usuario debe estar registrado en el sistema, por lo que se requiere enviar la autorización en la cabecera. Utilizaremos seguridad Basic.

Ejemplo:

GET http://localhost:8080/jbib-rest/api/usuarios/1/prestamos
Authorization: Basic dmljZW50ZS5jYXNhbWF5b3I6dmljZW50ZQ==

Resultado:

[
  {
    "id_prestamo": 3,
    "fecha_prestamo": "2014-11-01,00:00",
    "fecha_devolucion": "2014-11-30,00:00",
    "libro": {
      "id": 1
      "self": "http://localhost:8080/jbib-rest/api/libros/1",
      "titulo": "Patterns Of Enterprise Application Architecture",
      "ejemplar_id": 2
    }
  },
  {
    "id_prestamo": 4,
    "fecha_prestamo": "2014-11-01,00:00",
    "fecha_devolucion": "2014-11-30,00:00",
    "libro": {
      "id": 3
      "self": "http://localhost:8080/jbib-rest/api/libros/3",
      "titulo": "Clean Code",
      "ejemplar_id": 3
    }
  }
]

Si el usuario con identificador = {id} no está registrado como usuario de la biblioteca y con el mismo login, lanzaremos la excepción RestException.USUARIO_NO_ES_EL_LOGUEADO, con el mensaje "El usuario no es el logueado".

Si no se encuentra la información de dicho usuario, lanzaremos la excepción RestException.USUARIO_NO_EXISTENTE, con el mensaje "Usuario no existente".

  • POST /usuarios/{id}/prestamos: Se trata de una operación restringida al usuario. Se envía en el cuerpo de la petición el identificador del libro que se quiere tomar prestado. El usuario debe estar registrado en el sistema, por lo que se requiere enviar la autorización en la cabecera. Utilizaremos seguridad Basic.

Ejemplo:

POST http://localhost:8080/jbib-rest/api/usuarios/3/prestamos
Authorization: Basic YW5hYmVsLmdhcmNpYTphbmFiZWw=

Cuerpo del mensaje:
{
    "id": 1
}

Resultado:

 {
  "id_prestamo": 5,
  "fecha_prestamo": "2015-12-17,19:00",
  "fecha_devolucion": "2015-12-24,19:00",
  "libro": {
    "id": "1",
    "resource_uri": "http://localhost:8080/jbib-rest/api/libros/1",
    "ejemplar_id": 1
}

Devolvemos en la cabecera Location la URI del nuevo recurso creado. Para este ejemplo tendrá el valor: http://localhost:8080/jbib-rest/api/usuarios/3/prestamos/5

Si el usuario con identificador = {id} no está registrado como usuario de la biblioteca y con el mismo login, lanzaremos la excepción RestException.USUARIO_NO_ES_EL_LOGUEADO, con el mensaje ""El usuario no es el logueado".

Si no se encuentra la información de dicho usuario, lanzaremos la excepción RestException.USUARIO_NO_EXISTENTE, con el mensaje "Usuario no existente".

  • POST /usuarios/{id}/devoluciones: Restringida al usuario. Se envía por POST el identificador del ejemplar y se realiza la devolución.

    En este caso, y dado que estamos realizando una implementación parcial y preliminar, hemos optado por utilizar el método POST, aunque no vamos a devolver en la cabecera location (de momento) la URI del nuevo recurso creado.

    En el cuerpo de la respuesta devolveremos un objeto Json sencillo informando del resultado de la devolución.

Ejemplo:

POST http://localhost:8080/jbib-rest/api/usuarios/3/devoluciones
Authorization: Basic YW5hYmVsLmdhcmNpYTphbmFiZWw=

Cuerpo del mensaje:
{
    "id": 2
}

Resultado:

{
 "resultado": "DEVOLUCION_CORRECTA"
 }

Si nos hemos retrasado en devolver el libro, el resultado de la devolución mostrará el mensaje: "DEVOLUCION_FUERA_DE_PLAZO"

4.2.3. Representaciones JSON

El API REST trabajará con representaciones JSON de los objetos. Utilizaremos JAXB para mapear JSON en objetos y definiremos las clases necesarias en el paquete org.expertojava.jbibrest.rest.dto. (dto = data transfer object).

Recomendamos usar clases específicas para cada caso de uso, nombrándolas con el nombre utilizado en el modelo como prefijo, y un sufijo relacionado con el caso de uso o pantalla de la aplicación cliente en la que se van a mostrar los datos.

Por ejemplo: LibroDetalle para los datos devueltos por GET /libros/{isbn} o LibroItem para los elementos de la colección devuelta por GET /libros.

Para que los objetos de tipo Date se serialicen con el formato YYYY-MM-dd tenemos que utilizar la anotación com.fasterxml.jackson.annotation.JsonFormat con el atributo correspondiente de tipo Date, de la siguiente forma:

@JsonFormat(shape=JsonFormat.Shape.STRING, pattern="yyyy-MM-dd,HH:00", timezone="CET")
public Date fecha;

Para poder utilizar la anotación JsonFormat debemos incluir en el pom.xml la siguiente dependencia:

Dependencia para poder serializar el tipo Date a "yyy-MM-dd"
<!--Librería para serializar el tipo Date a "yyy-MM-dd" -->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-annotations</artifactId>
    <version>2.6.4</version>
    <scope>provided</scope>
</dependency>

4.2.4. Seguridad

Utilizaremos la autenticación HTTP BASIC en cada petición restringida al usuario, enviando una cabecera Authorization con la cadena usuario:contraseña codificada en base64.

Ejemplo: Authorization: Basic bG9naW46cGFzc3dvcmQ=.

Tendrás que realizar los cambios necesarios en el fichero /src/main/webapp/WEB-INF/web.xml para configurar la autentificación y autorización de los usuarios.

Recuerda que también tendrás que añadir los login y password de los usuarios en el ApplicationRealm de Wildlfy, con el grupo "usuario" (con el comando add-user.sh).

Por ejemplo, y suponiendo que hemos realizado las peticiones que hemos mostrado para los recursos rest, tendríamos que haber incluido los siguientes usuarios en el ApplicationRealm de Wildfly (todos ellos pertenecientes al grupo "usuario"):

  • login: vicente.casamayor, password: vicente, grupo: usuario

  • login: antonio.perez, password: antonio, grupo: usuario

  • login: anabel.garcia, password: anabel, grupo: usuario

4.2.5. Excepciones

Las excepciones de la capa REST serán de tipo RestException (por ejemplo). Resumimos las excepciones generadas desde el recurso libro y desde el recurso usuario (tened en cuenta que NO necesariamente tenéis que haber implementado "exactamente" todas ellas, se trata de un ejemplo orientativo):

  • RestException.USUARIO_NO_EXISTENTE

  • RestException.USUARIO_NO_ES_EL_LOGUEADO

  • RestException.LIBRO_NO_EXISTENTE

  • RestException.BUSQUEDA_POR_TITULO_Y_AUTOR_NO_IMPLEMENTADA

Las excepciones de la capa Servicio son de tipo BibliotecaException. Resumimos las excepciones de dicha capa:

  • BibliotecaException.USUARIO_NO_EXISTENTE

  • BibliotecaException.NO_HAY_EJEMPLARES_DISPONIBLES

  • BibliotecaException.EJEMPLAR_NO_EXISTENTE

  • BibliotecaException.EJEMPLAR_NO_DISPONIBLE

  • BibliotecaException.USUARIO_MOROSO

  • BibliotecaException.USUARIO_MULTADO

Implementaremos un mapeador de excepciones para capturar las excepciones tanto de la capa rest como de la capa de servicio, de forma que devolvamos la respuesta HTTP adecuada. Puedes crear una clase que encapsule el código de estado, y el mensaje, tal y como se ha implementado en el proyecto s5-tienda. Dicha clase se podrá serializar a formato Json con anotaciones JAXB.

El mapeado de excepciones se podrá hacer de la siguiente forma:

Para las excepciones de la capa rest:

  • Respuesta 404 Not found, cuando no exista el recurso

  • Respuesta 501 Not implemented, si no se ha implementado dicho servicio

  • Respuesta 401 Unauthorized, cuando el usuario que realiza la petición no es el mismo que se ha logueado en el sistema

  • Respuesta 500 Internal server error, en otro caso

Para las excepciones de la capa de servicio:

  • Respuesta 403 Forbiden, cuando no haya disponibilidad de ejemplares o el usuario sea moroso o esté multado

  • Respuesta 404 Not found, cuando no exista una entidad

  • Respuesta 500 Internal server error, en otro caso

4.3. Pruebas

Implementaremos algunos tests para probar los servicios. En este caso los implementaremos como "tests de integración", y se ejecutarán durante la fase "integration-test" de Maven. Ya hemos visto cómo hacer ésto en las sesiones de rest, aunque utilizando el plugin surefire (que está pensado para utilizarse con tests unitarios, en la fase test de Maven).

En este proyecto utilizaremos un nuevo plugin, el plugin surefire, que está pensado para trabajar con tests de integración (tests que requieren disponer del empaquetado con TODAS las "unidades" de código de nuestra aplicación, o al menos un subconjunto de ellas).

El plugin surefire está configurado para "reconocer" los tests de integración, que serán todas aquellas clases (con anotaciones JUnit) cuyo nombre siga uno de estos patrones: /IT*.java, /IT.java, y */*ITCase.java. Por lo tanto tendremos que llamar a nuestras clases de pruebas, por ejemplo, como "LibroTestsIT" y "UsuarioTestsIT". De esta forma la clase LibroTestsIT.java contendrá la implementación de los tests de integración con las pruebas sobre el recurso LibroResource, y la clase UsuarioTestsIT.java implementará los tests de integración sobre el recurso UsuarioResource.

¿Por qué necesitamos "diferenciar" nuestros tests en diferentes tipos? pues porque, además de que se ejecutan en instantes de tiempo diferentes (los tests de la capa de persistencia se ejecutan antes de los de la capa de servicio, y éstos antes que los de la capa rest), también se ejecutan en "entornos" diferentes (los tests de la capa de servicio, por ejemplo, se ejecutan DENTRO del contenedor correspondiente, en Wildfly, mientras que los tests rest NO se ejecutan en el contenedor, sino en una máquina virtual java separada). Por lo tanto, es lógico que haya diferentes plugins para encargarse de la ejecución de diferentes tipos de tests.

Ya hemos visto, por ejemplo, que no necesitamos utilizar Arquillian para ejecutar los tests rest (aunque podría hacerse utilizando ciertas anotaciones de Arquillian). Los test que habéis implementado hasta ahora se ejecutan con "mvn test" (podemos hacerlo porque Arquillian se encarga de realizar las pruebas en el contenedor). Ahora ejecutaremos los tests con "verify", y tendremos que incluir el plugin failsafe en nuestro pom.xml y configurarlo para ejecutar nuestros tests REST.

Configuración de los plugins surefire, failsafe y Wilfdfly.
<!--Añadimos dos propiedades: skipTests, y skipITs -->
<properties>
  ...
  <skipTests>false</skipTests>
  <skipITs>false</skipITs>
</properties>

<plugins>
  ...
  <!-- configuramos el plugin surefire. Por defecto
     se ejecutan los tests unitarios en la fase "test" -->
  <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <version>2.19</version>
    <configuration>
        <skipTests>${skipTests}</skipTests>
    </configuration>
  </plugin>

  <plugin>
    <groupId>org.wildfly.plugins</groupId>
    <artifactId>wildfly-maven-plugin</artifactId>
    <version>1.0.2.Final</version>
    <configuration>
      <hostname>localhost</hostname>
      <port>9990</port>
    </configuration>
    <!-- forzamos el despliegue después de empaquetar
            y antes de ejecutar los tests REST -->
    <executions>
      <execution>
        <id>wildfly-deploy</id>
        <phase>pre-integration-test</phase>
        <goals>
           <goal>deploy</goal>
        </goals>
      </execution>
    </executions>
  </plugin>

  <!-- ejecución de la goal failsafe:test
       durante la fase integration-test -->
  <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-failsafe-plugin</artifactId>
      <version>2.19</version>
      <configuration>
          <skipITs>${skipITs}</skipITs>
          <skipTests>false</skipTests>
      </configuration>
      <executions>
          <execution>
              <goals>
                  <goal>integration-test</goal>
                  <goal>verify</goal>
              </goals>
          </execution>
      </executions>
  </plugin>
</plugins>

Con esta configuración:

  • si ejecutamos mvn test se ejecutarán todos los tests de las capas inferiores a rest.

  • si ejecutamos mvn verify se ejecutarán TODOS los tests

  • si queremos ejecutar solamente los tests rest, podemos hacerlo con el comando:

    mvn verify -DskipTests=true -DskipITs=false"

Por otro lado, para implementar los tests REST necesitaremos incluir las dependencias necesarias para trabajar con el API Json, para serializar/deserializar tipos java a Json, así como para utilizar el API cliente, y matchers Hamcrest para realizar aserciones sobre tipos Json.

Dependencias para implementar los tests REST
<!--Jaxrs Api cliente -->
<dependency>
    <groupId>org.jboss.resteasy</groupId>
    <artifactId>resteasy-client</artifactId>
    <version>3.0.13.Final</version>
    <scope>test</scope>
</dependency>

<!--Librerías para serializar/deserializar json -->
<dependency>
    <groupId>org.jboss.resteasy</groupId>
    <artifactId>resteasy-jackson-provider</artifactId>
    <version>3.0.13.Final</version>
    <scope>test</scope>
</dependency>

<!--Jaxrs API json -->
<dependency>
    <groupId>org.jboss.resteasy</groupId>
    <artifactId>resteasy-json-p-provider</artifactId>
    <version>3.0.13.Final</version>
    <scope>test</scope>
</dependency>

<!--Hamcrest Json -->
<dependency>
    <groupId>uk.co.datumedge</groupId>
    <artifactId>hamcrest-json</artifactId>
    <version>0.2</version>
    <scope>test</scope>
</dependency>

Mostramos a continuación un ejemplo de tests (todos nuestros tests estarán en el paquete org.expertojava.jbibrest.rest (en la carpeta /src/test).

Ejemplo de test que realiza una consulta de un usuario NO registrado
package org.expertojava.jbibrest.rest;
...

public class UsuarioTestsIT {
...

@Test
public void consultarUsuarioNORegistrado() throws Exception {
  inicializamos_BD_con_datos("/dbunit/dataset3-rest.xml");

  String login_esperado = "vicente.casamayor";
  String mail_esperado = null;

  JsonObject respuesta_esperada =
          Json.createObjectBuilder()
                .add("status", "Unauthorized")
                .add("code", 401)
                .add("message", "El usuario no es el logueado")
                .build();

  Response respuesta = this.client
      .target("http://localhost:8080/jbib-rest/api/usuarios/roberto.garcia")
      .request(MediaType.APPLICATION_JSON)
      .header("Authorization", "Basic dmljZW50ZS5jYXNhbWF5b3I6dmljZW50ZQ==")
      .get();

  String respuesta_real= respuesta.readEntity(JsonObject.class).toString();

  //Comprobamos que recibimos el mensaje de error correcto
  Assert.assertThat(respuesta_real,
            sameJSONAs(respuesta_esperada.toString())
                    .allowingExtraUnexpectedFields()
                    );
}
}

Indicamos algunos de los test que podéis implementar,

ejemplos de tests para el recurso libro:

  • obtener el listado de todos los libros

  • obtener el listado de los libros de un autor determinado

  • obtener el listado de los libros con un título determinado

  • obtener el listado de los libros por autor y título

  • obtener los detalles de un libro

  • obtener las recomendaciones de un libro (sin indicar número)

  • obtener las reocomendaciones de un libro (indicando un número)

  • obtener las reocomendaciones de un libro que devuelva una lista vacía

ejemplos de tests para el recurso usuario:

  • obtener la información detallada de un usuario (sin proporcionar sus credenciales en la cabecera), de un usuario previamente registrado

  • obtener la información detallada de un usuario (proporcionando sus credenciales en la cabecera), de un usuario previamente registrado

  • obtener la información detallada de un usuario registrado (proporcionando las credenciales de otro usuario registrado)

  • solicitar un préstamo de un libro por parte de un usuario registrado cuyo estado sea moroso

  • solicitar un préstamo de un libro por parte de un usuario registrado, del que no existan ejemplares

  • solicitar un préstamo de un libro por parte de un usuario registrado, cuyos ejemplares no estén disponibles

  • solicitar un préstamo de un libro por parte de un usuario registrado que se lleve a cabo con éxito

  • realizar una devolución de un libro que se ha prestado previamente

Para ejecutar los tests implementados desde el IDE, y por comodidad, podemos crear un perfil de ejecución, desde la ventana Maven Projects con botón derecho sobre la fase de Maven verify, de forma que se ejecute el comando:

mvn verify -DskipTests=true -DskipITs=false

esto hará que el proceso de construcción y despliegue sea más rápido, ya que nos "saltaremos" la ejecución de cualquier test de las capas inferiores a REST. Así nos centraremos únicamente en la implentación de nuestros tests REST, con independencia de que tengamos parciamente operativos los tests de las capas inferiores. Ojo! sólo evitamos que se "ejecuten" los test de capas inferiores. Podríamos habernos "saltado" también la compilación de los mismos, aunque no lo hemos hecho.

La siguiente figura muestra el configuración del perfil de ejecución mencionado, de forma que ejecutemos el comando maven anterior, simplemente seleccionando dicho perfil desde la ventana Maven Projects:

Finalmente, nos puede resultar útil utilizar el plugin "sql" para borrar los datos de las tablas y volver a inicializarla con datos, durante el proceso de implementación de los tests, o si por ejemplo, queremos hacer pruebas previas con postman. En este caso tendremos que añadir el siguiente plugin en el pom.xml:

Plugin sql. Utilizado para borrar e inicializar los datos de la BD
<!-- Plugin para inicializar la BD con comandos sql-->
<!-- Podemos crear un elemento de configuración en la ventana Maven Projects
     de la siguiente forma:
     - desde jbib-rest - Plugins - sql - sql:execute
     - seleccionar con botón derecho "Create 'jbib-rest...
     - editamos el perfil de configuración añadiendo en
       "Command" line el comando "sql:execute@init-database"
     Para ejecutarlo, se hace
     desde jbib-rest - Run Configurations - jbib-rest [sql:execute]
     Hay que cambiar las settings del proyecto para inidicar que la ruta de
     maven es: /usr/local/apache-maven-3.3.3
     (si no no funciona, ya que la versión de maven que incorpora
     IntelliJ es la 3.0.5 y tiene que ser superior a 3.1
      -->
<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>sql-maven-plugin</artifactId>
    <version>1.5</version>

    <dependencies>
        <!-- JDBC driver -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.33</version>
        </dependency>
    </dependencies>

    <!-- common configuration shared by all executions -->
    <configuration>
        <driver>com.mysql.jdbc.Driver</driver>
        <url>jdbc:mysql://localhost:3306/biblioteca</url>
        <username>root</username>
        <password>expertojava</password>
    </configuration>

    <executions>
        <execution>
            <id>init-database</id>
            <goals>
                <goal>execute</goal>
            </goals>
            <configuration>
                <srcFiles>
                    <srcFile>src/test/resources/sql/biblioteca.sql</srcFile>
                </srcFiles>
            </configuration>
        </execution>
    </executions>
</plugin>

El fichero sql contendrá, por ejemplo:

USE biblioteca;
DELETE FROM Recomendacion;
DELETE FROM Libro;
DELETE FROM Ejemplar;
DELETE FROM Prestamo;
DELETE FROM Usuario;

INSERT INTO Libro(id, titulo, autor,isbn, numPaginas, portadaURI)
VALUES ('1','Patterns Of Enterprise Application Architecture', 'Martin Fowler',
 '0321127420', '533','0321127420.jpg');
INSERT INTO Libro(id, titulo, autor,isbn, numPaginas, portadaURI)
VALUES ('2','Clean Code', 'Robert C. Martin',
 '0132350882', '288','0132350882.jpg');
INSERT INTO Libro(id, titulo, autor,isbn, numPaginas, portadaURI)
VALUES ('3','Test Driven Development', 'Kent Beck',
 '0321146530', '192','0321146530.jpg');

INSERT INTO Recomendacion(id, libro_id,libroRelacionado_id) VALUES ('1','2', '3');
INSERT INTO Recomendacion(id, libro_id,libroRelacionado_id) VALUES ('2','2', '1');
INSERT INTO Recomendacion(id, libro_id,libroRelacionado_id) VALUES ('3','3', '2');

INSERT INTO Ejemplar(id, codigoEjemplar, fechaAdquisicion, libroId)
VALUES ('1', '001', '2014-10-01', '1');
INSERT INTO Ejemplar(id, codigoEjemplar, fechaAdquisicion, libroId)
VALUES ('2', '002', '2014-10-01', '1');
INSERT INTO Ejemplar(id, codigoEjemplar, fechaAdquisicion, libroId)
VALUES ('3', '001', '2014-11-01', '2');
INSERT INTO Ejemplar(id, codigoEjemplar, fechaAdquisicion, libroId)
VALUES ('4', '001', '2014-11-21', '3');

INSERT INTO Usuario(id,tipo,login, nombre, apellido1, apellido2)
VALUES ('1', 'PROFESOR', 'vicente.casamayor', 'Vicente', 'Casamayor', 'Garcia');
INSERT INTO Usuario(id,tipo,login, nombre, apellido1, apellido2)
VALUES ('2', 'ALUMNO', 'antonio.perez', 'Antonio', 'Perez', 'Sierra');
INSERT INTO Usuario(id,tipo,login, nombre, apellido1, apellido2)
VALUES ('3', 'ALUMNO', 'anabel.garcia', 'Anabel', 'Garcia', 'Sierra');

INSERT INTO Prestamo(id,ejemplar_id,usuario_id,fecha,deberiaDevolverseEl)
VALUES ('2', '4', '2', '2014-12-01', '2014-12-05');
INSERT INTO Prestamo(id,ejemplar_id,usuario_id,fecha,deberiaDevolverseEl)
VALUES ('3', '2', '1', '2014-11-01', '2014-11-30');
INSERT INTO Prestamo(id,ejemplar_id,usuario_id,fecha,deberiaDevolverseEl)
VALUES ('4', '3', '1', '2014-11-01', '2014-11-30');

Igual que antes, podemos crear el siguiente perfil de configuración desde ventana Maven Projects. Así podremos "recrear" los datos de la base de datos en cualquier momento seleccionando dicho perfil:

Para poder ejecutar el nuevo perfil creado, tenemos que asegurarnos de que la versión de Maven que se ejecute desde el IDE es una versión superior a la 3.1. Esto podremos hacerlo desde la ventana "Settings":

A continuación mostramos el aspecto de nuestra ventana Maven Projects después de añadir los dos perfiles nuevos de ejecución anteriores:

4.4. Entrega

Como resumen del trabajo a realizar, mostramos una vista del contenido del paquete org.expertojava.jbib.rest, en donde se situarán las nuevas clases implementadas.

La vista de la carpeta src/ es la siguiente:

La vista de la carpeta test/ es la siguiente:

La fecha de entrega del proyecto será el jueves 28 de enero.

Debes dar premiso de lectura en el repositorio jbib-rest-expertojava al usuario entregas-expertojava y confirmar la entrega en Moodle.