Tutoriel sur OpenAPI
1. Objectif
Nous allons ici voir comment documenter une API en utilisant le standard
OpenAPI
. Nous allons spécifier l’API de notre (micro)service Movie
.
Notons que pour des raisons pédagogiques nous avons d’abord écrit le code de notre (micro)service avant d’écrire la spécification de notre API. Une bonne pratique serait de procéder dans le sens inverse, même si des corrections peuvent être apportées à la spécification/documentation après implémentation.
Note
|
Une page introductive à OpenAPI https://swagger.io/docs/specification/about/ |
Note
|
La spécification complète de OpenAPI https://swagger.io/specification/ |
Note
|
Il existe d’autres standards comme RAML mais OpenAPI est très
utilisé et utilise le format Yaml
|
Note
|
Vous pouvez vous créer un compte gratuit sur https://app.swaggerhub.com/home et éditer votre documentation comme je le ferai en
séance, mais vous pouvez également écrire votre fichier yaml sur votre
machine avec n’importe quel bon éditeur de fichiers texte ou de code
(Sublime, Atom, VSCode, Emacs, etc.)
|
2. Informations et tags
Nous pouvons en premier indiquer la version du standard que nous utilisons. Nous allons définir les serveurs comme ne contenant rien.
openapi: 3.0.0
servers: []
Puis nous pouvons remplir l’objet d’information de la documentation
info:
description: This is the API of the Movie service
version: "1.0.0"
title: Movie API
contact:
email: helene.coullon@imt-atlantique.fr
license:
name: GPL v3
url: 'https://www.gnu.org/licenses/gpl-3.0.en.html'
Note
|
Pour plus de détails voir la spécification et l’objet info :
https://swagger.io/specification/
|
Nous allons maintenant pouvoir ajouter des tags
permettant d’identifier des
catégories dans les chemins définis ensuite.
tags:
- name: admins
description: Secured Admin-only calls
- name: developers
description: Operations available to regular developers
Ici par exemple, nous définissons 2 tags pour les utilisateurs de l’API, mais un tag peut représenter n’importe quelle classification utile.
3. Chemins et composants
3.1. Chemin simple
Les chemins décrivent les point d’accès de l’API définis par le décorateur @
dans Flask. Un premier exemple est donné ci-dessous.
paths:
/:
get:
tags:
- developers
summary: home page of the service
operationId: home
description: |
Nothing to do
responses:
'200':
description: welcome message
content:
text/html:
schema:
type: string
example: "<h1 style='color:blue'>Welcome to the Movie service!</h1>"
-
Nous associons d’abord une requête de type
GET
à ce chemin. Notons que plusieurs types de requêtes peuvent être associés au même chemin, nous le verrons ensuite. -
Nous pouvons ensuite associer un tag à cette requête, ici le tag
developers
. -
Viennent ensuite des éléments associés à l’objet de requête comme une description, un id, etc. (voir la spécification pour le détail).
-
La réponse doit être spécifiée dans l’objet requête également, elle contient différents codes retour (ici uniquement 200) et décrit le contenu de la réponse ici une balise HTML.
Ce chemin correspond donc bien au code Python
suivant :
@app.route("/", methods=['GET'])
def home():
return make_response("<h1 style='color:blue'>Welcome to the Movie service!</h1>",200)
3.2. Chemin et JSON
Voyons une chemin pour lequel la requête GET
retourne un format JSON
maintenant.
/json:
get:
tags:
- developers
summary: get the full JSON database
operationId: get_json
description: |
Nothing to do
responses:
'200':
description: full JSON
content:
application/json:
schema:
$ref: '#/components/schemas/AllMovies'
Dans ce cas, nous pointons vers une référence à un autre objet Yaml
contenu
dans components/schemas/AllMovies
. Nous allons donc voir comment définir ce
schéma JSON
.
components:
schemas:
AllMovies:
type: object
required:
- movies
properties:
movies:
type: array
items:
type: object
$ref: '#/components/schemas/MovieItem'
Ici le schéma AllMovies
est décrit comme étant un objet constitué de
movies
uniquement, cet élément étant de type array
et contenant lui-même
des objet dont le schéma est décrit dans components/schemas/MovieItem
.
Voici ce nouveau schéma.
MovieItem:
type: object
required:
- title
- rating
- director
- id
properties:
title:
type: string
example: The Martian
rating:
type: integer
example: 7
director:
type: string
example: Paul McGuigan
id:
type: string
example: 39ab85e5-5e8e-4dc5-afea-65dc368bd7ab
MovieItem
est un objet constitué d’un titre de type string
, d’une note de
type integer
, d’un directeur et d’un ID de types string
.
3.3. Chemin et paramètres
Voici la spécification du chemin permettant de récupérer les informations d’un film à partir de son ID.
/movies/{movieid}:
get:
tags:
- developers
summary: get the movie by its id
operationId: get_movie_byid
description: |
By passing in the appropriate options, you can get info of a Movie
parameters:
- name: movieid
in: path
required: true
description: Movie ID.
schema:
type : string
minimum: 1
maximum: 1
responses:
'200':
description: Movie description
content:
application/json:
schema:
$ref: '#/components/schemas/MovieItem'
'400':
description: bad input parameter
Les différences notables ici avec les exemples précédents sont :
-
un paramètre est déclaré et intégré dans le chemin,
-
plus d’un code retour est possible pour la réponse.
Les paramètres inclus dans le chemin sont identifiés par des {}
, puis le
paramètre est spécifié en détail dans la liste des paramètres parameters
.
Le schéma du paramètre doit également être spécifié à l’image des schémas
déclarés dans les composants.
3.4. Requête de type POST
Les requêtes de type POST
sont spécifiées de façon similaire aux requêtes
GET
mais contiennent en plus un objet requestBody
. Ici le corps de la
requête est un format JSON qui est donné par le schéma
components/schemas/MovieItem
déjà étudié.
/movies/{movieid}:
get:
...
post:
tags:
- admins
summary: add a movie item
operationId: create_movie
description: Adds a movie to the system
parameters:
- name: movieid
in: path
required: true
description: Movie ID.
schema:
type : string
minimum: 1
maximum: 1
responses:
'200':
description: Movie created
content:
application/json:
schema:
$ref: '#/components/schemas/MovieItem'
'409':
description: an existing item already exists
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/MovieItem'
description: Inventory item to add
3.5. Paramètre dans la requête
Voici un exemple de documentation avec un paramètre dans la requête. Rien de
très différent hors mis la clé/valeur in: query
.
/titles:
get:
tags:
- developers
summary: get the movie by its title
operationId: get_movie_bytitle
description: |
By passing in the appropriate options, you can get Movie info
parameters:
- in: query
name: title
description: pass a title
required: true
schema:
type: string
minimum: 1
maximum: 1
responses:
'200':
description: Movie item
content:
application/json:
schema:
$ref: '#/components/schemas/MovieItem'
'400':
description: bad input parameter