• Non ci sono risultati.

7. Sviluppo di un microservizio backend

7.1.2 Scrittura delle API

Le api vengono definite come file swagger e sono basate sullo standard open api:

- swagger è un linguaggio di descrizione dell'interfaccia, utilizzato per

descrivere le API RESTful espresse utilizzando JSON, viene utilizzato insieme ad una serie di strumenti software open source per progettare, costruire, documentare ed utilizzare i servizi web basati su architetture REST;

- la specifica open api è uno standard utilizzato per definire API REST che permette, mediante l’utilizzo di strumenti e software, di generare

automaticamente la documentazione di metodi, parametri e modelli a partire dall’interfaccia, permettendo quindi di comprendere da essa le funzionalità di un servizio che ne implementa uno o più metodi.

56

Il file di specifica open api nel progetto auto-generato su gitlab per la definizione dell’api sarà già sotto forma di template così da aiutare lo sviluppatore nel

popolamento, ne vedremo le varie sezioni nel resto del sottoparagrafo corrente.

Per ogni file di specifica bisogna limitare il numero di operation definiti, in quanto avere troppi endpoint in una singola API implica un cattivo design se l’obiettivo e quello di avere un architettura a microservizi, e non una SOA.

Di seguito, un’analisi delle varie sezioni del template del file di specifica open api.

Definizione di path o collection

Definire sotto la sezione paths le operation contemplate dalla api che si andrà a sviluppare: per path si intende una collection sulla quale è poi possibile effettuare delle operazioni REST (GET, POST, PUT, DELETE, PATCH, ...).

La struttura di un path è definita nel seguente modo:

57 Definizione delle operations

Una operation dovrà definire il verbo HTTP, i parametri e le risposte (comprensive di errori) seguendo il seguente template:

58

Per ogni operation è necessario definire i seguenti tag nel seguente ordine:

- Nome gruppo: verrà tradotto nel nome dell’interfaccia che sarà implementata dai controller spring o dai clienti axios:

- Deve essere lo stesso per tutte le operation di una specifica;

- Non deve presentare spazi;

- Può aver senso utilizzare il nome dell’api stessa.

- Visibilità: a scelta tra private o public, pilota il framework di sicurezza.

La risposta ottenuta in caso di successo, quindi associata al codice 200, deve essere un oggetto che estende una classe astratta, mai una lista.

La presenza del parametro x-pagination, da settare a livello di operation, ha come effetto che il codice auto-generato avrà in automatico tre parametri utili per la gestione della paginazione del risultato:

- page: numero di pagina richiesta;

- size: dimensione della pagina;

- order: opzionale, indica il campo dell’elemento sulla base del quale ordinare gli elementi tornati.

Questo meccanismo di paginazione si traduce in modi diversi a seconda dell’ambito:

se l’API è utilizzata in ambito backend, l’operation generata presenterà come ultimo parametro un oggetto Pageable contenente le informazione da passare eventualemnte al repository sottostante; se l’API è utilizzata come interfaccia Client-Feign,

l’operation generata presenterà i parametri page, size ed order.

59 Definizione delle risposte

Le risposte seguono tutte la medesima struttura, se ne riporta di seguito il template:

Affinché il framework funzioni correttamente è necessario definire una risposta che estenda una ApiResponse:

- Utilizzando il costrutto allOf di open api per riferire il tipo corretto;

- Inserendo l’informazione “x-api-response: true” per indicare che questo modello rappresenta una risposta applicativa;

- Facendo in modo che l’api response abbia una ed una sola property definita come data che riferisce la risposta utile.

Vediamo di seguito due esempi di risposta: singola e con lista.

Nel caso di una risposta singola, il template sarà quello riportato di seguito:

IstanzaResponse: allOf: - $ref: 'includes/apiresponse.yaml#/ApiResponse' <--estensione x-api-response: true <--decorazione

type: object

properties: data: <--dato utile

$ref: '#/components/schemas/IstanzaNumeroRosso'

60

Nel caso di una risposta con lista, il template sarà il seguente:

Per le operation per cui non è prevista risposta (ad esempio le POST), è stata definita una risposta apposita definita come VoidApiResponse da utilizzarsi nel seguente modo:

Definizione di Messaggi di errore e warning:

I messaggi di errore e warning sono definiti all’interno dei rispettivi file nella cartella includes/messages. Si dovrà rispettare il seguente formato (il template ha già la struttura predisposta):

IstanzeListResponse: allOf: - $ref: 'includes/apiresponse.yaml#/ApiResponse' <--estensione

x-api-response: true <--decorazione type: object

properties: data: <--dato utile type: array

items: $ref: '#/components/schemas/IstanzaNumeroRosso'

responses:

‘200’:

description: Stato lettura della risposta aggiornato content:

application/hal+json schema:

$ref: ‘includes/apiresponse.yaml#/VoidResponse’

61

Per quanto riguarda la scelta dei codici c'è questa semplice convenzione:

- errori: devono iniziare per E seguiti da un codice alfanumerico (es. E001) - warning: devono iniziare per W seguiti da un codice alfanumerico (es. W001) NB. per non appesantire eccessivamente lo sviluppo i messaggi di info si possono definire all'interno del microservizio, tuttavia anche qui si applicherà una

convenzione:

- info: devono iniziare per I seguiti da un codice alfanumerico (es. W001).

Aggiunta esempio di response

Gli esempi di risposta si inseriranno all'interno della definizione della risposta mediante la sezione x-examples, in questo modo:

62 Template della Specifica Openapi

Di seguito una copia completa del file di specifica openapi:

openapi: 3.0.2 info:

title: <nome api>

description: <descrizione dell'api>

termsOfService: 'http://www.websitename.it' contact:

name: <struttura di progetto che ha richiesto l'API>

email: <mail della struttura di progetto>

version: <versione dell'artefatto software dell'API, nella forma x.x.x sarà usato per generare il jar>

servers:

- url: '{protocol}://{server}:{port}/{basePath}' variables:

protocol:

default: http enum:

- http - https server:

default: localhost enum:

- localhost

- dev-websitename.it - test-websitename.it - prod-websitename.it port:

default: '8080' basePath:

default: <base path dell api, per convenzione usare 'vx' dove x è un intero che identifica la major version, ad esempio 1.0.0 --> v1 >

security:

- OHS: []

- OSB: []

externalDocs:

description: Linee guida OpenAPI 3.0

url: 'http://spec.openapis.org/oas/v3.0.3' tags:

- name: public

description: le api identificate come pubbliche saranno disponibili senza autenticazione

- name: private

description: le api identificate come private saranno protette da sistemi di autenticazione

paths:

/<base path dell'API, identifica la collection remota es. /accounts>:

parameters:

- $ref: 'includes/apiheaders.yaml#/X-Client-ID' - $ref: 'includes/apiheaders.yaml#/X-External-JWT' - $ref: 'includes/apiheaders.yaml#/X-Correlation-ID' get:

summary: <descrizione breve>

description: <descrizione estesa>

operationId: <nome del metodo java auto-generato in camel case, es. getAccounts>

63

tags:

- <tag identificativo gruppo risorse, diventerà il nome del controller axios/java si suggerisce di ripetere il base path al singolare, es. account>

- <tag visibilità, public/private a seconda della visibilità richiesta, andrà a far parte del base path dell'API>

parameters:

#Aggiungere il seguente parametro se si vuole supportare le risposte parziali con hal.

#- $ref: 'includes/apiheaders.yaml#/X-FULL'

#lista parametri secondo la specifica open api, di seguito un esempio

- name: <nome parametro>

in: <tipo parametro, ammessi soltanto query o path>

description: <descrizione parametro>

required: <true o false se obbligatorio o meno>

schema:

type: <tipo parametro secondo specifica openapi>

format: <formato parametro, opzionale>

responses:

'200':

description: <descrizione>

content:

application/hal+json:

schema:

$ref: '#/components/schemas/<componente risposta definito in basso che estende ApiResponse>'

#sezione opzionale links con dettaglio dei link restituiti in caso di risposta paziale.

#ATTENZIONE: solo documentazione: i link non sono generati automaticamente

links:

<nome link>:

description: <descrizione>

operationId: <operationId dell'api a cui si fa riferimento>

parameters:

<nome parametro link destinazione>:

'$response.data#/<nome parametro risposta>'

#sezione esempi di risposta, da notare il prefisso "x" che la rende custom del framework

x-examples:

- summary: '<Titolo esempio>'

description: '<Descrizione dell esempio>'

value: #valorizzazione: sotto "value" è possibile inserire la risposta in formato yaml: questa sarà trasformata in json negli

esempi

- idIstanza: "S0000001813000000470"

tipoIstanza : "C"

dataCreazione : "2018-07-16T11:56:58+02:00"

dataRichiesta : "2018-07-16T11:56:58+02:00"

'500':

description: Errore - Internal Server Error content:

application/problem+json:

schema:

$ref: 'includes/apiresponse.yaml#/Problem'

#lista altri errori supportati, ammessi solo 400 e 404, usare sempre lo schema ref problem+json

64

post:

summary: <descrizione breve>

description: <descrizione estesa>

operationId: <nome del metodo java auto-generato in camel case, es. getAccounts>

tags:

- <tag identificativo gruppo risorse, diventerà il nome del controller axios/java si suggerisce di ripetere il base path, es.

accounts>

- <tag visibilità, public/private a seconda della visibilità richiesta, andrà a far parte del base path dell'API>

requestBody:

description: <descrizione body>

required: true content:

application/json:

schema:

$ref: '#/components/requestBodies/<oggetto richiesta, definito in basso>'

responses:

'200':

description: <descrizione>

content:

application/hal+json:

schema: #Usare il riferimento a VoidResponse per operazioni che non restituiscono risultato

$ref: 'includes/apiresponse.yaml#/VoidResponse' '500':

description: Errore - Internal Server Error content:

application/problem+json:

schema:

$ref: 'includes/apiresponse.yaml#/Problem'

#lista altri errori supportati, ammessi solo 400 e 404, usare sempre lo schema ref problem+json

/<base path dell'API che referenzia una risorsa della collection tramite id, es /account/{accountId}>:

parameters:

- $ref: 'includes/apiheaders.yaml#/X-Client-ID' - $ref: 'includes/apiheaders.yaml#/X-External-JWT' - $ref: 'includes/apiheaders.yaml#/X-Correlation-ID' get:

summary: <descrizione breve>

description: <descrizione estesa>

operationId: <nome del metodo java auto-generato in camel case, es. getAccounts>

tags:

- <tag identificativo gruppo risorse, diventerà il nome del controller axios/java si suggerisce di ripetere il base path, es.

accounts>

- <tag visibilità, public/private a seconda della visibilità richiesta, andrà a far parte del base path dell'API>

parameters:

- name: <id della collection indicato nel path in alto, es accountId>

in: path

description: <descrizione>

required: true schema:

type: integer format: int64

#lista altri parametri secondo la specifica open api

65

format: int64

#lista altri parametri secondo la specifica open api responses:

'200':

description: <descrizione>

content:

application/hal+json:

schema:

$ref: '#/components/schemas/<componente risposta definito in basso che estende ApiResponse>'

'500':

description: Errore - Internal Server Error content:

application/problem+json:

schema:

$ref: 'includes/apiresponse.yaml#/Problem'

#lista altri errori supportati, ammessi solo 400 e 404, usare sempre lo schema ref problem+json

put:

summary: <descrizione breve>

description: <descrizione estesa>

operationId: <nome del metodo java auto-generato in camel case, es. getAccounts>

tags:

- <tag identificativo gruppo risorse, diventerà il nome del controller axios/java si suggerisce di ripetere il base path, es.

accounts>

- <tag visibilità, public/private a seconda della visibilità richiesta, andrà a far parte del base path dell'API>

parameters:

- name: <id della collection indicato nel path in alto, es accountId>

in: path

description: <descrizione>

required: true schema:

type: integer format: int64 requestBody:

description: <descrizione body>

required: true content:

application/json:

schema:

$ref: '#/components/requestBodies/<oggetto richiesta, definito in basso>'

responses:

'200':

description: <descrizione>

content:

application/hal+json:

schema:

$ref: 'includes/apiresponse.yaml#/VoidResponse' '500':

description: Errore - Internal Server Error content:

application/problem+json:

schema:

$ref: 'includes/apiresponse.yaml#/Problem'

#lista altri errori supportati, ammessi solo 400 e 404, usare sempre lo schema ref problem+json

patch:

summary: <descrizione breve>

66

patch:

summary: <descrizione breve>

description: <descrizione estesa>

operationId: <nome del metodo java auto-generato in camel case, es. getAccounts>

tags:

- <tag identificativo gruppo risorse, diventerà il nome del controller axios/java si suggerisce di ripetere il base path, es.

accounts>

- <tag visibilità, public/private a seconda della visibilità richiesta, andrà a far parte del base path dell'API>

parameters:

- name: <id della collection indicato nel path in alto, es accountId>

in: path

description: <descrizione>

required: true schema:

type: integer format: int64

#lista altri parametri secondo la specifica open api requestBody:

content:

application/json:

schema:

type: object properties:

#proprietà oggetto parziale di seguito esempi <nome property>:

description: <descrzione>

#type oppure ref a un oggetto in basso responses:

'200':

description: <descrizione>

content:

application/hal+json:

schema:

$ref: 'includes/apiresponse.yaml#/VoidResponse' '500':

description: Errore - Internal Server Error content:

application/problem+json:

schema:

$ref: 'includes/apiresponse.yaml#/Problem'

#lista altri errori supportati, ammessi solo 400 e 404, usare sempre lo schema ref problem+json

delete:

summary: <descrizione breve>

description: <descrizione estesa>

operationId: <nome del metodo java auto-generato in camel case, es. getAccounts>

tags:

- <tag identificativo gruppo risorse, diventerà il nome del controller axios/java si suggerisce di ripetere il base path, es.

accounts>

- <tag visibilità, public/private a seconda della visibilità richiesta, andrà a far parte del base path dell'API>

parameters:

- name: <id della collection indicato nel path in alto, es accountId>

67

in: path

description: <descrizione>

required: true schema:

type: integer format: int64

#lista altri parametri secondo la specifica open api responses:

'200':

description: <descrizione>

content:

application/hal+json:

schema:

$ref: 'includes/apiresponse.yaml#/VoidResponse' '500':

description: Errore - Internal Server Error content:

application/problem+json:

schema:

$ref: 'includes/apiresponse.yaml#/Problem'

#lista altri errori supportati, ammessi solo 400 e 404, usare sempre lo schema ref problem+json

components:

#sezione risposte/oggetti

schemas:

#riferimenti ai file errori e warnings Errors:

$ref: "includes/messages/errors.yaml#/Errors"

Warnings:

$ref: "includes/messages/warnings.yaml#/Warnings"

#esempio oggetto di request body <nome oggetto richiesta>:

type: object properties:

prop1: string

example: #opzionalmente si può aggiungere un esempio di body prop1: 'test'

#esempio risposta che restituisce una lista <nome oggetto lista>Response:

allOf:

- $ref: 'includes/apiresponse.yaml#/ApiResponse' x-api-response: true

type: object properties:

data: #unica property chiamata data, all'interno ci sarà il riferimento al DTO di risposta

type: array items:

$ref: '#/components/schemas/<DTO risposta>'

#esempio risposta che restituisce un singolo risultato <nome oggetto semplice>Response:

allOf:

- $ref: 'includes/apiresponse.yaml#/ApiResponse' x-api-response: true

type: object

68

properties:

data: #unica property chiamata data, all'interno ci sarà il riferimento al DT= di risposta

$ref: '#/components/schemas/<DTO risposta>'

#definiamo il DTO risposta qui e lo referenziamo nella risp singola o array

<DTO risposta>:

type: <oggetto oppure array o tipo primitivo>

#eventuali property links: {}

callbacks: {}

securitySchemes:

OHS:

type: apiKey in: header

name: OAM_REMOTE_USER OSB:

type: apiKey in: header

name: X-EXTERNAL-REMOTE-USER

description: Errore - Internal Server Error

69

Documenti correlati