Specifica OpenAPI

OpenAPI è un progetto nato nel 2015 per volere della società SmartBear Software che fino ad allora aveva sviluppato la specifica Swagger. Il progetto nacque con la sponsorizzazione della Linux Foundation ed annovera tra i suoi membri vendor come: Google, IBM, Microsoft, PayPal, etc. Lo scopo del progetto è quello di promuovere uno standard per la descrizione, produzione, consumo e visualizzazione delle informazioni relative ad un RESTful API Web Service, che sia allo stesso tempo human e machine readable.

La specifica quindi nasce come progetto Swagger ed è probabilmente più nota con questo brand name, per questo motivo c’è ancora molta confusione su quale sia la differenza tra OpenAPI e Swagger, quando utilizzare un nome piuttosto che l’altro e soprattutto su quale sia la differenza tra i due. Semplificando al massimo tale differenza può essere così riassunta:

  • OpenAPI: è il nome ufficiale  della specifica;
  • Swagger: identifica un insieme di tool che implementano ed utilizzano la specifica.

Poiché i tool Swagger sono stati sviluppati dal team coinvolto nella creazione della specifica  originale, gli strumenti sono spesso considerati sinonimo della specifiche. Ma gli strumenti Swagger non sono gli unici disponibili per l’implementazione della specifica OpenAPI. Esistono numerose soluzioni per la progettazione, documentazione, test, gestione e monitoraggio di API che supportano la versione 2.0 delle specifiche e che stanno lavorando attivamente per aggiungere il supporto 3.0. Quest’ultima infatti è la prima rilasciata sotto il cappello OpenAPI ed è comunemente conosciuta con l’acronimo OAS 3.0 (OpenAPI Specification 3.0).

Approcci Possibili

La specifica si presta ad essere utilizzata in due modalità diametralmente opposte:

  • Approccio top-down o design-first: la specifica è utilizzata per progettare le API prima di aver scritto qualsiasi riga di codice.
  • Approccio bottom-up o code-first: è stato già scritto il codice per l’implementazione delle API e si vuole generare la documentare a corredo.

L’approccio code-first è sicuramente quello storicamente più utilizzato, in quanto più semplice perché si possono apportare modifiche ed aggiustamenti mentre si procede nel coding e si adatta perfettamente a un processo di delivery agile. Ma poiché la progettazione è carente potrebbe portare ad una API difficile da capire (per l’utilizzatore) e documentare.

La spinta verso una documentazione chiara e di facile lettura ha reso popolare l’approccio design-first. Non solo più persone possono contribuire nella definizione della documentazione, ma inoltre si traduce spesso in un codice più pulito, perchè si è costretti a pensare in modo più semplice, conciso e facile da seguire.

Struttura di Base

Una definizione OpenAPI può essere espressa attraverso un file YAML o JSON. In questo articolo utilizziamo YAML, ma JSON funzionerebbe ugualmente bene. Il contenuto e la struttura di un file di descrizione OpenAPI è rappresentato nella figura seguente, in cui è anche mostrata la differenza che l’attuale versione 3.0 ha rispetto alla precedente versione 2.0:

Info

In questa sezione sono presenti i metadata associati al contratto dell’API. Le parti obbligatorie di tale sezione sono il titolo, la versione e la descrizione della API. Altri campi ammessi sono le informazioni di contatto, della licenza e una URL che ad un pagina in cui sono espressi i termini e condizioni di utilizzo del servizio. In sostanza, l’oggetto Info dovrebbe fornire agli utilizzatori ed agli sviluppatori una panoramica di alto livello su ciò che fa la API.

La prima riga del file deve contenere quindi la versione della specifica da applicare e può assumere il valore swagger: "2.0" o openapi: 3.0.n (ad esempio openapi: 3.0.0).

Servers

La sezione servers fornisce agli utilizzatori delle API le informazioni su come contattare il server che le ospita, attraverso la definizione della URL. A differenza della versione 2.0 della specifica, che consentiva di definire una sola URL, la versione OAS 3.0 supporta più server URL. Ciò è utile poiché nel mondo reale le API esistono in più ambienti (environment) e la logica di business del contratto può cambiare in base all’ambiente. Si pensi ad esempio alla distinzione tra server di test, preproduzione e produzione.

Security

Nel mondo reale tutte le API hanno bisogno di un certo livello di sicurezza che impedisca ai clienti non autorizzati di accedervi. A tale scopo è necessario descrive i metodi di autenticazione  ed autorizzazione supportati dalla API ed in particolare OpenAPI supporta diversi schemi:

  • HTTP authentication schemes (Basic, Bearer, etc.);
  • API keys (in headers, cookies, o query strings);
  • OAuth2;
  • OpenID Connect Discovery.

La specifica del livello di sicurezza nella progettazione delle API si ottiene attraverso due step. Il primo consiste nel definire le implementazioni di sicurezza adottate, che sono descritte nella sezione components/securitySchemes. Il secondo step consiste nell’applicare tali schema globalmente a tutte le API o singolarmente alle diverse operazioni, semplicemente inserendo la sezione security a livello globale o di metodo.

Nella sezione securitySchemes quindi definiamo gli shema applicabili fornendo un nome (qualsiasi) allo schema ed indicandone il tipo tra: httapiKey, oauth2 e openIdConnect. In base al tipo possono poi essere necessarie ulteriori informazioni, ad esempio:

http scheme Indica il nome dell’HTTP Authorization scheme utilizzato (RFC7235).
bearerFormat Indica il formato del token. Si noti che si tratta di una informazione finalizzata solamente alla documentazione del metodo perché essendo il token generato dal server al client non serve conoscerne il formato.
apiKey in Indica dove è trasportata la chiave: query, header o cookie.
name Indica il nome del parametro nell’header, nella query o nei cookie che trasporta la chiave.
openIdConnect openIdConnectUrl OpenId Connect URL per scoprire i valori di configurazione di OAuth2. Deve essere una URL.
oauth2 flows Un oggetto complesso contenente informazioni di configurazione per i tipi di flusso oAuth2 supportati.

Infine la sezione security indica quale schema di sicurezza applicare tra quelli descritti referenziandoli utilizzando il nome datogli nella sezione securitySchemes. Nel caso poi di OAuth 2 e OpenID Connect è anche possibile indicare gli scope, per controllare le autorizzazioni per varie risorse utente. Nel caso di OAuth 2, gli scope devono essere dichiarati nel securitySchemes, mentre per OpenID Connect, i possibili scope sono elencati nel discovery endpoint specificato dal parametro openIdConnectUrl. Gli altri schemi non usano gli scope, quindi troviamo un array vuoto [] a fianco alle loro voci.

Paths

In questa sezione sono dichiarati i vari end-point esposti dalla API ed i corrispondenti metodi HTTP supportati. A ciascun metodo è associato una sottosezione che inizia con una delle seguenti parole chiavi: get, put, post, delete, options, head, patch e trace.

Ciascun metodo è caratterizzato da una descrizione breve, summary, ed una estesa, description, ma è anche possibile indicare una risorsa esterna attraverso il tag externalDocs. Inoltre attraverso la definizione di uno o più tag è possibile raggruppare i metodo al fine di uniformare la documentazione prodotta. Ciascuna operazione è poi univocamente identificata da un operationId che dovrà quindi essere univoca in tutto il documento. Infine è anche possibile sovrascrivere le impostazioni globali per quanto riguarda i server da contattare ed i security schema applicabili.

All’interno di ciascuna definizione di metodo viene poi dettagliato l’effettivo ciclo di request e response, attraverso la definizione degli oggetti parameters, requestBody e responses.

Parameters

In tale sezione sono descritti i parametri che caratterizzano l’operazione. Ciascun parametro è definito attraverso la dichiarazione obbligatoria del parametro name ed in. Quest’ultimo identifica la location in cui trovare il parametro e può assumere uno dei quattro seguenti valori: path, query, header e cookie. Altri parametri non obbligatori sono: description, required, deprecatedallowEmptyValue.

A tali informazioni va poi aggiunto uno schema che definisce il tipo di dato che caratterizza il parametro.

Request Body

Il request body viene generalmente utilizzato con le operazioni di creazione e aggiornamento (POST, PUT, PATCH). Ad esempio, quando si crea una risorsa, utilizzando i metodi POST o PUT, o la si aggiorna, utilizzando il metodo PATCH, il request body di solito contiene la rappresentazione della risorsa coinvolta. La sua definizione è abbastanza flessibile in quanto consente di utilizzare diversi tipi di Media Type, come JSON, XML, form data, plain text ed altro, e utilizzare schemi diversi per tipi di media diversi.

La sezione requestBody è caratterizzata da un contenuto, una descrizione opzionale e da un flag che indica la sua obbligatorietà (che è false per impostazione predefinita). Il contenuto elenca i tipi di media utilizzati dall’operazione (come application/json) e specifica lo schema per ciascun tipo di media. Tale schema può essere definito all’interno della stessa definizione del requestBody o globalmente nella sezione components.

Responses

Per ogni operazione, è possibile definire diversi codici di stato, ad esempio 200 OK o 404 NOT FOUND, e per ognuno di essi fornire:

  • una descrizione dell’esito;
  • il contenuto del body con il relativo schema;
  • una mappa di eventuali header da aggiungere alla response, indicandone il nome e lo schema associato.

Gli shema utilizzati possono essere definiti sia in linea che referenziandoli tramite $ref dopo averli definiti nella sezione components.

Components

Nel processo di progettazione delle API all’aumentare del numero di operazioni e di risorse coinvolte corrisponde un aumento delle dimensioni del file che le definisce. Può dover essere necessario ripetere molte informazioni, come i parametri o la definizione delle request e delle response che, in molti casi, possono essere simili benchè associate a metodi differenti. Riscriverli ogni volta oltre ad impiegare tempo può portare ad errori o peggio a definizioni incoerenti.

La sezione components, che per altro abbiamo già introdotto in modo indiretto, consente di mitigare questi problemi in quanto destinata alla definizione di oggetti che possono essere referenziati in altri parti del file di specifica. Esempi di oggetti riutilizzabili sono: gli schema, i parametri, le response, etc.

naturalmente gli oggetti definiti nella sezione components non avranno alcun effetto sulla definizione dell’API a meno che non siano esplicitamente referenziati da proprietà esterne alla sezione.

Generazione della Documentazione

Esistono diversi editor utili sia a produrre il file di specifica che a generare la documentazione associata. Sicuramente quello più famoso e completo è lo swagger editor, che di fatto abbiamo utilizzato per produrre l’esempio descritto nell’articolo e di cui riportiamo uno screenshot nell’immagine seguente.

La particolarità di tale editor è che consente la generazione sia del codice server che client per diverse tecnologie: java, python, php, c#, etc. Inoltre è possibile anche generare la documentazione, in formato html, della nostra API da poter distribuire agli utilizzatori.

Il Codice Sorgente

Il codice sorgente del progetto è scaricabile qui user-openapi.yaml

How useful was this post?

Click on a star to rate it!

Average rating / 5. Vote count:

No votes so far! Be the first to rate this post.

As you found this post useful...

Follow us on social media!

Lascia un commento

Il tuo indirizzo email non sarà pubblicato.

*