View on GitHub

Bienvenido al sitio de GitHub de documentación de GLA.

Portal principal para acceder a las guias de diseño, estándares, y prácticas de desarrollo en GLA.

Guía para el diseño de APIs privadas.

Todas las aplicaciones de GLA deberían permitir interactuar con el mundo exterior mediante APIs. En el caso de que la construcción sea in-house, ésta guía pretende dar un estilo de diseño común para todas ellas. La forma propuesta en la guía es la más adaptada a los estándares de la industria y en lugar de ser una propuesta novedosa es más bien un documento de desambiguación para que aquellos que tengan que diseñar APIs REST no tengan duda y en esta forma facilitar su trabajo.

Estructura de una URL

{protocolo}://{dominio o hostname}[:puerto (opcional)]/{ruta del recurso}?{query}
http://icsdesadcsrv02:19732/v8/ui/sucursales
http://localhost:8731/v8/ui/ordenesDeRetiro?sincronizar=true

Forma correcta de representar la ruta del recurso

http://itgcore/v8/{colleción}/{id}/{colleción2}/{id2}

donde {colleción} debe SIEMPRE ser un sustantivo en plural. Ejemplo:http://localhost:8731/v8/api/sucursales/13/ordenesDeEnvio/T10000000662805

Reglas básicas para ponerle nombre a la URI de un recurso

Verbos

GET - Se usa para OBTENER o LISTAR recursos

GET /v1/api/operaciones/sucursales/3 HTTP/1.1
Host: ec2.qa.andreani.com.ar
Accept: application/xml
Cache-Control: no-cache

Devuleve una representación de la sucursal con id #3.

GET /v1/api/operaciones/sucursales HTTP/1.1
Host: ec2.qa.andreani.com.ar
Accept: application/xml
Cache-Control: no-cache

Devuelve una lista con todas las sucursales.

GET /v8/tax/paises/1/provincias/1/ciudades/ HTTP/1.1
Host: icsdesadcsrv02:19732
Authorization: Bearer wCCPzKOKUkOBfxGjkmgHzg==

Devuleve una lista con las todas las ciudades de la provincia #1 del pais #1

POST - Crear un recurso

POST /v8/api/sucursales HTTP/1.1
Host: icsdesadcsr v02.andreani.com.ar:19732
Accept: application/json
Authorization: Bearer rLecYd2DQUS8Z9IgVXHWVA==

{"nomenclatura":"STD","descripcion": "Santo Domingo"}

Crea la sucursal Santo Domingo.

Si es satisfactoria la operacion puede devolver 201-Created o 202-Accepted. En ambos casos debe devolver en el header Location el url que se puede invocar para obtener una representacion del recurso creado.

Location: "http://icsdesadcsrv02.andreani.com.ar:19732/v8/api/sucursales/3"

En situaciones normales los POST no deberían devolver resultados que no sean en la forma de headers.

PUT - Reemplazar un recurso

Se usa para reemplazar un recurso con uno nuevo pero manteniendo el id. Como la acción es de reemplazar, el contenido del body tiene que ser completo como si fuese un POST.

PUT /v8/api/sucursales/3 HTTP/1.1
Host: icsdesadcsr v02.andreani.com.ar:19732
Accept: application/json
Authorization: Bearer rLecYd2DQUS8Z9IgVXHWVA==

{"nomenclatura":"STDbis","descripcion":"Santo Domingo bis"}

También se puede usar para poner un recurso ya existente en una colección.

PUT /v8/api/sucursales/3/ordenesDeEnvio/W000007653 HTTP/1.1
Host: icsdesadcsr v02.andreani.com.ar:19732
Accept: application/json
Authorization: Bearer rLecYd2DQUS8Z9IgVXHWVA==

Pone la ordenDeEnvio W000007653 en la coleccion de OrdenesDeEnvio de la sucursal 3.

PATCH - Actualizar un recurso

Se usa para actualizar un recurso sin enviarle todos los datos, solo se envian aquellos que cambian.

PATCH /v8/api/sucursales/3 HTTP/1.1
Host: icsdesadcsr v02.andreani.com.ar:19732
Accept: application/json
Authorization: Bearer rLecYd2DQUS8Z9IgVXHWVA==

{"nomenclatura":"STD2"}

DELETE - Borrar un recurso

Se usa para borrar un recurso. Puede ser tambien para desactivar, dependerá el caso. Nunca es legal realizarlo sobre colecciones. Si es satisfactoria la operación devuelve 202-NoContent.

DELETE /v8/tax/paises/1/provincias/1/ciudades/124/codigosPostales/1712 HTTP/1.1
Host: icsdesadcsrv02:19732
Authorization: Bearer wCCPzKOKUkOBfxGjkmgHzg==

Borra o desactiva el codigo postal 1712, de tal forma que un GET a /v8/tax/paises/1/provincias/1/ciudades/124/codigosPostales/1712 da 404-NotFound.

El siguiente request es ilegal porque no se puede hacer un DELETE sobre una colección.

DELETE /v8/tax/paises/1/provincias/1/ciudades/124/codigosPostales HTTP/1.1
Host: icsdesadcsrv02:19732
Authorization: Bearer wCCPzKOKUkOBfxGjkmgHzg==

Se usa para verificar si cierto recurso existe. En en caso afirmativo se devuelve 302-Found y en caso contrario 404-NotFound

HEAD /v1/api/operaciones/sucursales/3 HTTP/1.1
Host: ec2.qa.andreani.com.ar
Accept: application/xml
Cache-Control: no-cache

Devuleve 302-Found si la sucursal 3 existe.

Filtros, paginado y propiedades expandidas

Filtros y paginado

Si bien hay muchas formas de especificar valores para que sirvan de filtros en los GETs, la recomendación es usar el que sigue:

GET /v8/api/prepiezas?numeroDeEnvio=W000007653&activo=true
Host: V8QADCSRV01:19433
Authorization: Bearer nWBQecoElUO7mpoF8MHVuw==
Accept: application/json

Esa es la forma correcta: especificar en el queryString las tuplas propiedad=valor. Es conveniente que las operaciones que listen devuelvan en el response un header con el nombre x-total-count donde informen la cantidad total de items de la colección. Eso le sirve a los controles paginadores de las UI.

Sorting

La clave sort del queryString indica sobre qué campo y en qué sentido se debe hacer el ordenamiento.

GET /v8/api/prepiezas/?numeroDeEnvio=W000007653&activo=true&sort=-fechaDeEntrada
Host: V8QADCSRV01:19433
Authorization: Bearer nWBQecoElUO7mpoF8MHVuw==
Accept: application/json

Un signo menos (-) indica que se debe ordenar de forma descendente.

Paginación

GET /v8/api/prepiezas?numeroDeEnvio=W000007653&activo=true&sort=-fechaDeEntrada&offest=2&limit=50
Host: V8QADCSRV01:19433
Authorization: Bearer nWBQecoElUO7mpoF8MHVuw==
Accept: application/json

Donde offest indica el número de página que hay que mostrar y limit la cantidad de items.

Propiedades expandidas

En el ejemplo anterior hay una clave del query string que se llaman expand, ¿a qué viene esto?: en general una entidad tiene relaciones con otras, por ejemplo, una ordenDeEnvio estará relacionada con una cuenta corriente o un contrato y entonces cuando devolvemos la información de la orden de envío, ¿debemos devolver también la representación de sus asociaciones?. La primera respuesta es no, y lo que sí se debe devolver es el url que el cliente tiene que invocar en el caso de que requiera el valor de esa relación:

[
    {
        "componentes": {
            "href": "http://icstestdcsrv02.andreani.com.ar:19732/v8/api/prePiezas/{cabecera:676474,numero:1}/componentes"
         },
        "contrato": {
            "esCorporativo": false,
            "esRecanalizable": false,
            "esUrgente": false,
            "estaActivo": false,
            "href": "http://icstestdcsrv02.andreani.com.ar:19732/v8/api/contratos/32050/iditg"
        },
        "cpDestinoValidado": {
            "ciudad": "589",
            "codigo": 1834,
            "href": "http://icstestdcsrv02.andreani.com.ar:19732/v8/api/direcciones/paises/1/provincias/1/ciudades/589/codigosPostales/1834",
            "idCiudad": 589,
            "idPais": 1,
            "idProvincia": 1
        },
        "destinatario": {
            "apellido": null,
            "documento": "DNI 23402292",
            "email": "timmothy_breitenberg@powlowskigusikowski.co.uk",
            "nombre": "Stefan",
            "nombreCompleto": "Stefan   DNI 23402292",
            "telefonos": [],
            "tipoDocumento": null
        },
        "destinoInformado": {
            "calle": "santo domingo",
            "ciudad": "TEMPERLEY",
            "codigoPostal": "1834",
            "numero": "40",
            "pais": "Argentina",
            "provincia": "AR"
        },
        "fechaDeEntrada": "2017-11-14T12:13:11.0000000",
        "lote": "201711141213111422",
        "numero": 1,
        "numeroDeEnvio": "W00000000375650",
        "remitente": {
            "apellido": null,
            "documento": null,
            "email": "annamae@goldner.ca",
            "nombre": "Reuben",
            "nombreCompleto": "Reuben   ",
            "telefonos": [],
            "tipoDocumento": null
        },

        "subLote": "1",
        "sucursalDeDistribucion": 9,
        "sucursalDeImposicion": 0,
        "valorACobrar": 0,
        "valorDeclarado": 0
    }
]

Notar en este ejemplo el valor de la propiedad contrato. Tiene un miembro de nombre href que indica el url de dónde obtener ese contrato. Lo mismo pasa con la propiedad cpDestinoValidado. Sin embargo, pueden haber casos en que si sea conveniente obtener en el mismo request las asociaciones del recurso y ahí entran las propiedades expandidas. La clave expand especifica en su valor el nombre de la relación que se quiere incluir en la respuesta:

GET /v8/api/prepiezas?expand=contrato HTTP/1.1
[
    {
        "componentes": {
            "href": "http://icstestdcsrv02.andreani.com.ar:19732/v8/api/prePiezas/{cabecera:676474, numero:1 }/componentes"
        },
        "contrato": {

    "cicloDeDistribucion": {
        "href": "http://icstestdcsrv02.andreani.com.ar:19732/v8/api/contratos/ciclos/746/fases",
        "permiteEntregaEnMostrador": null,
        "permiteReenvio": false
    },
    "cicloDeReenvio": {
        "href": "http://icstestdcsrv02.andreani.com.ar:19732/v8/api/contratos/ciclos/746/fases",
        "permiteEntregaEnMostrador": null,
        "permiteReenvio": false
    },

    "cicloDeRendicionParaEntrega": {
        "href": "http://icstestdcsrv02.andreani.com.ar:19732/v8/api/contratos/ciclos/227/fases",
        "permiteEntregaEnMostrador": null,
        "permiteReenvio": false
    },
    "cicloDeRendicionParaNoEntrega": {
        "href": "http://icstestdcsrv02.andreani.com.ar:19732/v8/api/contratos/ciclos/227/fases",
        "permiteEntregaEnMostrador": null,
        "permiteReenvio": false
    },
    "cliente": {
        "href": "http://icstestdcsrv02.andreani.com.ar:19732/v8/api/clientes/36184/iditg"
    },
    "codigoDeContratoInterno": "400006611",
    "descripcion": "45440-26024-ENT-AO",
    "direccionPostal": {
        "href": "http://icstestdcsrv02.andreani.com.ar:19732/v8/api/direcciones/32818598?esDeConfiguracion=True",
        "idLocalidad": null,
        "idPais": null,
        "idProvincia": null,
        "iditg": null
    },
    "esCorporativo": true,
    "esRecanalizable": false,
    "esUrgente": false,
    "estaActivo": true,
    "iditg": "32050",
    "tipoDeServicio": "Encomienda eCommerce"

        },
        "cpDestinoValidado": {
            "ciudad": "589",
            "codigo": 1834,
            "href": "http://icstestdcsrv02.andreani.com.ar:19732/v8/api/direcciones/paises/1/provincias/1/ciudades/589/codigosPostales/1834",
            "idCiudad": 589,
            "idPais": 1,
            "idProvincia": 1
            },
        "destinatario": {
            "apellido": null,
            "documento":"DNI 23402292",
            "email": "timmothy_breitenberg@powlowskigusikowski.co.uk",
            "nombre": "Stefan",
            "nombreCompleto":"Stefan   DNI 23402292",
            "telefonos": [],
            "tipoDocumento": null
        },
        "destinoInformado": {
            "calle": "santo domingo",
            "ciudad": "TEMPERLEY",
            "codigoPostal": "1834",
            "numero": "40",
            "pais": "Argentina",
            "provincia": "AR-B"
        },
        "fechaDeEntrada": "2017-11-14T12:13:11.0000000",
        "lote": "201711141213111422",
        "numero": 1,
        "numeroDeEnvio": "W00000000375650",
        "remitente": {
            "apellido": null,
            "documento": null,
            "email": "annamae@goldner.ca",
            "nombre": "Reuben",
            "nombreCompleto": "Reuben   ",
            "telefonos": [],
            "tipoDocumento": null
        },
        "subLote": "1",
        "sucursalDeDistribucion": 9,
        "sucursalDeImposicion": 0,
        "valorACobrar": 0,
        "valorDeclarado": 0
    }
]

La respuesta incluye la información del contrato. Es legal solicitar más de una asociación en el expand, por ejemplo

GET /v8/api/prepiezas?expand=contrato,cpDestinoValidado

es totalmente correcto y el API deberá devolver ambas asociaciones inicializadas.

El uso de las propiedades expandidas no es obligatorio y es decisión del diseñador del API si es plausible de implementar o no. En general lo será si implica un costo mucho menor del que hacer otro GET separado.

Headers

Accept : Todas las apis de Andreani deben hacer caso de este header y devolver el mime-type que corresponda.

Cache-Control : expiry:

etag if-match

Supongamos el siguiente request:

GET /v1/api/operaciones/sucursales/3 HTTP/1.1
Host: ec2.qa.andreani.com.ar
Accept: application/xml
Cache-Control: no-cache
Etag: 1234

En etag viene un hash que representa la versión del recurso. La forma del hash es dependiente de la implementación. Si luego un cliente quiere actualizar o borrar ese recurso debería incluir en el request un header if-match:

PUT /v8/api/sucursales/3 HTTP/1.1
Host: icsdesadcsr v02.andreani.com.ar:19732
Accept: application/json
Authorization: Bearer rLecYd2DQUS8Z9IgVXHWVA==
If-Match: 1234
{"nomenclatura":"STDbis","descripcion":"Santo Domingo bis" }

Si el if-match coincide con el etag, entonces el recurso se puede actualizar. Si no es así la implementación debería devolver 412 Precondition Fails