sábado, 14 de marzo de 2020

WSO2: Como hacer data services REST

Buenas, en este post vamos a ver de forma rápida y sencilla cómo realizar un servicio con operaciones sobre una base de datos, a través del Enterprise Integrator de WSO2. Tradicionalmente estos servicios, denominados data services, se realizan como operaciones SOAP. Hoy veremos cómo hacerlos un poco más actuales, y desarrollarlos como servicios REST. 

Para empezar deberemos tener un Enterprise Integrator arrancado y con un datasoure configurado. El data service lo podremos generar a través del asistente de la plataforma, pero yo os lo explicaré a través de ejemplos de código. 

A continuación mostraremos un data service sencillo. Donde indicaremos el data source a utilizar, el cual previamente ya se ha configurado en el EI (en el ejemplo, LIBRARY será el nombre de dicho data source). El cual además tendrá una query sencilla a modo de ejemplo. 

<data name="book_dataservices" transports="http https local">
    <config id="LIBRARY">
        <property name="carbon_datasource_name">LIBRARY</property>
    </config>
    <query id="getBook" useConfig="LIBRARY">
        <sql>SELECT NAME, AUTHOR FROM BOOK WHERE ID = :id</sql>
        <result element="Books" rowName="book" escapeNonPrintableChar="true">
            <element column="NAME" name="name" xsdType="STRING"/>
            <element column="AUTHOR" name="author" xsdType="STRING"/>
        </result>
        <param name="id" ordinal="1" sqlType="INTEGER"/>
    </query>
</data>

Con esto aún no seremos capaces de obtener ningún dato. El siguiente paso será definir una operación o recurso del data service. Y aquí será donde radica la principal diferencia. Si queremos un data service con acceso SOAP deberemos configurar un objeto operation y si queremos que sea accesible como una API REST, deberemos configurar un objeto resource.

A la hora de crear un recurso, debemos indicar 4 valores:
  • resourcePath: El contexto del servicio donde también podemos indicar los path params. 
  • resource method: El método HTTP con el que invocamos el recurso.
  • queryId: El identificador de la query asociado al recurso.
  • queryParams: la asociación entre los path/query params de la invocación al recurso y los de la propia query. 
A continuación mostraremos un ejemplo para la query anterior. En este ejemplo, pasaremos el identificador como un query param. Y para ello debemos indicarlo en el path. 

<resource method="GET" path="book/{id}">
   <call-query href="getBook">      <with-param name="id" query-param="id"/>
   </call-query>
</resource>

A continuación veremos como obtener  los datos de un objeto en base a su identificador. Una cosa muy importante en este tipo de data services es el uso de las cabeceras Accept y Content-Type.

curl -kX GET 'https://localhost:8243/services/book_dataservices/book/3' \
-H 'Accept: application/json' 

A continuación vamos a realizar un POST.

<query id="postBook" keyColumns="ID" returnGeneratedKeys="true" useConfig="LIBRARY">
   <sql>INSERT INTO BOOK (NAME, AUTHOR) VALUES (?, ?)</sql>
   <result element="return" rowName="" useColumnNumbers="true">
      <element column="1" name="ID" xsdType="integer"/>
   </result>
   <param name="name" ordinal="1" sqlType="STRING"/>
   <param name="author" ordinal="2" sqlType="STRING"/>
</query>
<resource method="POST" path="book">
   <call-query href="postBook">
      <with-param name="name" query-param="name"/>
      <with-param name="author" query-param="author"/>
   </call-query>
</resource>

La principal novedad a la hora de invocarlo, es el paso de la información hacia el data service. Debemos crear un objeto json cuyo nombre empieza con el guión bajo y el nombre del método HTTP y posteriormente el nombre del contexto (las barras se sustituyen por guiones bajos). Y cada parámetro de entrada de la query es un atributo del objeto JSON.

curl -kX POST 'https://localhost:8243/services/book_dataservices/book' \
-H 'Content-Type: application/json' -H 'Accept: application/json' \
--data-raw '{ "_postbook": { "name": "AT", "author": "AT" } }'

El método PUT es prácticamente igual

<query id="putbook" returnUpdatedRowCount="true" useConfig="LIBRARY">
   <sql>UPDATE BOOK SET NAME=?, AUTHOR=? WHERE ID = ?</sql>
   <result element="UpdatedRowCount" rowName="" useColumnNumbers="true">
      <element column="1" name="ID" xsdType="integer"/>
   </result>
   <param name="name" sqlType="STRING"/>
   <param name="author" sqlType="STRING"/>
   <param name="id" sqlType="INTEGER"/>
</query>
<resource method="PUT" path="book">
   <call-query href="putbook">
      <with-param name="name" query-param="name"/>
      <with-param name="author" query-param="author"/>
      <with-param name="id" query-param="id"/>
   </call-query>
</resource>

Y podemos invocarlo así

curl -kX PUT 'https://localhost:8243/services/book_dataservices/book' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
--data-raw '{"_putbook": {"name": "AT1","author": "AT1","id":3 } }'

Por último solo quedaría el DELETE: 

<query id="deleteBook" useConfig="LIBRARY">
   <sql>DELETE FROM BOOK WHERE ID = :id</sql>
   <result element="UpdatedRowCount" rowName="" useColumnNumbers="true">
      <element column="1" name="ID" xsdType="integer"/>
   </result>
   <param name="id" ordinal="1" sqlType="INTEGER"/>
</query>
<resource method="DELETE" path="book/{id}">
   <call-query href="deleteBook">
      <with-param name="id" query-param="id"/>
   </call-query>
</resource>

Y su invocación sería:

curl -kX DELETE 'https://localhost:8243/services/book_dataservices/book' \
-H 'Accept: application/json' -H 'Content-Type: application/json' \
--data-raw '{ "_deletebook": { "id": 3 } }'

No hay comentarios:

Publicar un comentario