Table des matières

Swagger

Qu'est-ce que Swagger ?

Swagger est une infrastructure logicielle open-source reposant sur un vaste écosystème d'outils qui aide les développeurs à concevoir, créer, documenter et utiliser des services Web RESTful . Alors que la plupart des utilisateurs identifient Swagger à l'aide de l'outil d'interface utilisateur Swagger, le jeu d'outils Swagger comprend la prise en charge de la documentation automatisée, de la génération de code et de la génération de cas de test.

Parrainé par SmartBear Software , Swagger est un fervent partisan des logiciels à code source ouvert et a été largement adopté.

Histoire

Le projet API Swagger a été créé en 2011 par Tony Tam, cofondateur technique du site de dictionnaire Wordnik. Au cours du développement des produits Wordnik, la nécessité d'automatiser la documentation de l'API et la génération de SDK client est devenue une source majeure de frustration. Tam a conçu une représentation JSON simple de l'API, exploitant la flexibilité du style d'architecture REST et utilisant de nombreuses fonctionnalités d'outils conçues pour le protocole SOAP. Le concept d'interface utilisateur a été proposé par Ayush Gupta, qui a suggéré qu'une interface utilisateur interactive serait avantageuse pour les utilisateurs finaux souhaitant “essayer” et se développer par rapport à l'API. Ramesh Pidikiti a dirigé l'implémentation du générateur de code initial et le concepteur/développeur Zeke Sikilianos a inventé le nom Swagger. Le projet d'API Swagger est devenu open source en septembre 2011. Peu après sa publication, de nouveaux composants ont été ajoutés au projet, notamment un validateur autonome, la prise en charge de Node.js et Ruby on Rails.

Au début de Swagger, les petites entreprises et les développeurs indépendants ont eu une influence modeste. Les API RESTful n'avaient généralement pas de mécanisme de description lisible par machine, et Swagger fournissait un moyen simple et découvrable de le faire. Tam a été invité à une réunion avec certains des leaders d'opinion de l'industrie des API, notamment John Musser (ProgrammableWeb), Marsh Gardiner (Apigee , désormais produit de Google), Marco Palladino (Kong) et Kin Lane (API Evangelist) pour discuter d'un effort de normalisation autour des descriptions des API. Bien que la réunion n'ait pas abouti à un plan concret, elle a permis à Swagger de figurer sur la carte en tant qu'innovation cruciale dans le domaine des API.

Grâce à l'utilisation de la licence open source Apache 2.0, un certain nombre de produits et de services en ligne ont commencé à inclure Swagger dans leurs offres, qui s'est rapidement accélérée après son adoption par Apigee, Intuit, Microsoft, IBM et d'autres qui ont commencé à approuver publiquement le projet Swagger.

Peu de temps après la création de Swagger, des structures alternatives pour décrire les API RESTful ont été introduites, les plus populaires étant API Blueprint en avril 2013 et RAML en septembre 2013. Bien que ces produits concurrents aient un support financier supérieur à celui de Swagger, ils se sont initialement concentrés sur différents cas d'utilisation de Swagger, et à la mi-2014, l'intérêt de Swagger augmentait plus rapidement que la combinaison des deux autres [source: Google Trends].

En novembre 2015, SmartBear Software , la société qui maintenait Swagger, a annoncé qu'elle participait à la création d'une nouvelle organisation, sous le parrainage de la Linux Foundation, appelée OpenAPI Initiative. Diverses sociétés, notamment Google, IBM et Microsoft, sont des membres fondateurs.

Le 1er janvier 2016, la spécification Swagger a été renommée Spécification OpenAPI et déplacée vers un nouveau référentiel dans GitHub. Bien que la spécification elle-même n'ait pas été modifiée, ce changement de nom a signifié la scission du format de description de l'API et de l'outillage open source.

Depuis juillet 2017, les outils Swagger sont téléchargés plus de 100 000 fois par jour, selon les référentiels d'hébergement Sonatype et npm .

Description d’un document OpenAPI

Lorsque vous utilisez la spécification OpenAPI pour designer votre API, vous pouvez entre autres : générer une documentation interactive, générer le code de la documentation, client et serveur. Cela est possible à travers de nombreux projets open source développés autour de la spécification OpenAPI.

Les langages supportés pour designer une API sont JSON et YAML. La spécification définit plusieurs étiquettes qui vont permettre entre autres de :

Note:

vous pouvez consulter le site suivant pour explorer les différentes étiquettes de la spécification OpenAPI

Une fois que vous maitrisez le langage JSON ou YAML et que vous comprenez la structure d’un document OpenAPI, avec un simple éditeur de texte, vous êtes en mesure de designer votre API.

Ci-dessous la structure d’un document YAML OpenAPI :

swagger: '2.0' 
info: 
  version: Version de votre API 
  title: Nom 
  description: Description de l'API 
  termsOfService: Termes d'utilisation 
  contact: 
    name: Nom personne de contact 
    url: Site Web  
    email: Adesse mail 
  license: 
    name: Nom de la licence 
    url: Site Web ou tenir les informations sur la licence  
basePath: "/" 
paths: 
  "Chemin de la l'API": 
    get: 
      tags: 
      - Nom du Tag 
      summary: Resumé de la méthode qui est exposée 
      description: Description de la méthode exposée 
      operationId: Nom de la méthode exposée 
      consumes: [] 
      produces: 
      - application/json 
      responses: 
        '200': 
          description: Description de la réponse 
          schema: 
            "$ref": "#/definitions/ExempleDefinition" 
definitions: 
  ExempleDefinition: 
    type: string

Swagger Editor

Écrire un document OpenAPI en utilisant un simple éditeur peut être assez fastidieux. Toutefois, il existe un éditeur Swagger en ligne gratuit. Ce dernier offre de nombreuses fonctionnalités intéressantes, dont l’autocompletion.
Swagger Editor

Ici, nous allons le télécharger et l'utiliser sur notre machine en locale, nous allons voir :

Installer Swagger Editor

Télécharger la dernière version de Swagger Editor depuis Github.

La dernière version en date est la v3.6.36.

[root@sandbox ~]# wget https://github.com/swagger-api/swagger-editor/archive/v3.6.36.tar.gz
--2019-09-29 14:21:26--  https://github.com/swagger-api/swagger-editor/archive/v3.6.36.tar.gz
Résolution de github.com (github.com)… 140.82.118.3
Connexion à github.com (github.com)|140.82.118.3|:443… connecté.
requête HTTP transmise, en attente de la réponse… 302 Found
Emplacement : https://codeload.github.com/swagger-api/swagger-editor/tar.gz/v3.6.36 [suivant]
--2019-09-29 14:21:27--  https://codeload.github.com/swagger-api/swagger-editor/tar.gz/v3.6.36
Résolution de codeload.github.com (codeload.github.com)… 192.30.253.121
Connexion à codeload.github.com (codeload.github.com)|192.30.253.121|:443… connecté.
requête HTTP transmise, en attente de la réponse… 200 OK
Taille : non indiqué [application/x-gzip]
Sauvegarde en : « v3.6.36.tar.gz »
 
v3.6.36.tar.gz                    [                                    <=>                      ]  10,33M   889KB/s    ds 12s     
 
2019-09-29 14:21:40 (868 KB/s) - « v3.6.36.tar.gz » sauvegardé [10831742]

Décompresser le contenu de l'archive :

[root@sandbox ~]# tar -zxf v3.6.36.tar.gz

Un dossier swagger-editor-X.X.XX a été créer, à l'intérieur se trouve les sources de Swagger Editor.
Vous avez plusieurs possibilités pour utiliser ces sources.
Vous pouvez :

Ici, nous allons juste ouvrir le fichier “index.html” dans un navigateur, vous devriez obtenir ceci :

Comme vous pouvez le constater, vous obtiendrez le même affichage que la version de l’éditeur en ligne, il s'agit de la même version.

Définition de notre API

Nous avons deux possibilités :

Afin d'avoir un exemple concret et simple à la fois, nous allons juste nous contenter de modifier légèrement l'API.

La définition des chemins de l'API commence à “path”, ce qui se trouve au dessus rassemble des informations sur l'API, une description, la Licence etc…

Note:

La petite flèche sur le côté permet de condenser des blocs de code ensemble.
Chaque bloc contient plusieurs propriétés définissant le chemin concernés, celui-ci pouvant contenir d'autres propriétés lié a chaque action définit dans le chemin.

Par exemple le bloc suivant (l'ensemble des propriétés pour chaque actions ont été retirés) :

  /pet/{petId}:                   ---->>>> Définit un des chemins de notre API
    get:                          ---->>>> Définit l'action "GET"
      tags:                       ---->>>> Définit un tag, ici "pet"
      - "pet"
      summary: "Find pet by ID"   ---->>>> Description de l'action définit
 
...

    post:                         ---->>>> Définit l'action "POST"
      tags:
      - "pet"
      summary: "Updates a pet in the store with form data"
 
...

    delete:                         ---->>>> Définit l'action "DELETE"
      tags:
      - "pet"
      summary: "Deletes a pet"
 
...

Comme vous pouvez le voir, notre chemin définis trois actions :

Chacune de ces actions ayant des propriétés qui lui sont propres.

Dans notre exemple, nous allons retirer toutes les actions de tous les chemins qui permettent une modification/suppression. Ce qui veux donc dire que nous allons garder uniquement ce qui concerne les actions “GET”.

Pour les chemins qui ne contiennent aucune action “GET”, retirés le chemin complet.
Retirer également tous les chemins en rapport avec la gestion des utilisateurs (“user”).

Une fois fait, si vous regardez un peu plus bas, dans la partie des définitions, vous verrez “” en regard de certaines définitions.

Cela signifie que votre code contient des “warnings” qu'il faut traiter. En l’occurrence ici, si vous mettez le pointeur de votre souris dessus, vous aurez le message suivant : “Definition was declared but never used in document”, ce qui signifie que Swagger trouve une définition qui n'est pas utilisé dans les chemins de votre API.

Vous pouvez donc les supprimer sans crainte.

A la fin, vous devriez obtenir le code YAML de la définition de votre API tel que:

swagger: "2.0"
info:
  description: "This is a sample server Petstore server.  You can find out more about     Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/).      For this sample, you can use the api key `special-key` to test the authorization     filters."
  version: "1.0.0"
  title: "Swagger Petstore"
  termsOfService: "http://swagger.io/terms/"
  contact:
    email: "apiteam@swagger.io"
  license:
    name: "Apache 2.0"
    url: "http://www.apache.org/licenses/LICENSE-2.0.html"
host: "petstore.swagger.io"
basePath: "/v2"
tags:
- name: "pet"
  description: "Everything about your Pets"
  externalDocs:
    description: "Find out more"
    url: "http://swagger.io"
- name: "store"
  description: "Access to Petstore orders"
- name: "user"
  description: "Operations about user"
  externalDocs:
    description: "Find out more about our store"
    url: "http://swagger.io"
schemes:
- "https"
- "http"
paths:
  /pet/findByStatus:
    get:
      tags:
      - "pet"
      summary: "Finds Pets by status"
      description: "Multiple status values can be provided with comma separated strings"
      operationId: "findPetsByStatus"
      produces:
      - "application/xml"
      - "application/json"
      parameters:
      - name: "status"
        in: "query"
        description: "Status values that need to be considered for filter"
        required: true
        type: "array"
        items:
          type: "string"
          enum:
          - "available"
          - "pending"
          - "sold"
          default: "available"
        collectionFormat: "multi"
      responses:
        200:
          description: "successful operation"
          schema:
            type: "array"
            items:
              $ref: "#/definitions/Pet"
        400:
          description: "Invalid status value"
      security:
      - petstore_auth:
        - "write:pets"
        - "read:pets"
  /pet/findByTags:
    get:
      tags:
      - "pet"
      summary: "Finds Pets by tags"
      description: "Muliple tags can be provided with comma separated strings. Use         tag1, tag2, tag3 for testing."
      operationId: "findPetsByTags"
      produces:
      - "application/xml"
      - "application/json"
      parameters:
      - name: "tags"
        in: "query"
        description: "Tags to filter by"
        required: true
        type: "array"
        items:
          type: "string"
        collectionFormat: "multi"
      responses:
        200:
          description: "successful operation"
          schema:
            type: "array"
            items:
              $ref: "#/definitions/Pet"
        400:
          description: "Invalid tag value"
      security:
      - petstore_auth:
        - "write:pets"
        - "read:pets"
      deprecated: true
  /pet/{petId}:
    get:
      tags:
      - "pet"
      summary: "Find pet by ID"
      description: "Returns a single pet"
      operationId: "getPetById"
      produces:
      - "application/xml"
      - "application/json"
      parameters:
      - name: "petId"
        in: "path"
        description: "ID of pet to return"
        required: true
        type: "integer"
        format: "int64"
      responses:
        200:
          description: "successful operation"
          schema:
            $ref: "#/definitions/Pet"
        400:
          description: "Invalid ID supplied"
        404:
          description: "Pet not found"
      security:
      - api_key: []
  /store/inventory:
    get:
      tags:
      - "store"
      summary: "Returns pet inventories by status"
      description: "Returns a map of status codes to quantities"
      operationId: "getInventory"
      produces:
      - "application/json"
      parameters: []
      responses:
        200:
          description: "successful operation"
          schema:
            type: "object"
            additionalProperties:
              type: "integer"
              format: "int32"
      security:
      - api_key: []
  /store/order/{orderId}:
    get:
      tags:
      - "store"
      summary: "Find purchase order by ID"
      description: "For valid response try integer IDs with value >= 1 and <= 10.         Other values will generated exceptions"
      operationId: "getOrderById"
      produces:
      - "application/xml"
      - "application/json"
      parameters:
      - name: "orderId"
        in: "path"
        description: "ID of pet that needs to be fetched"
        required: true
        type: "integer"
        maximum: 10.0
        minimum: 1.0
        format: "int64"
      responses:
        200:
          description: "successful operation"
          schema:
            $ref: "#/definitions/Order"
        400:
          description: "Invalid ID supplied"
        404:
          description: "Order not found"
securityDefinitions:
  petstore_auth:
    type: "oauth2"
    authorizationUrl: "http://petstore.swagger.io/oauth/dialog"
    flow: "implicit"
    scopes:
      write:pets: "modify pets in your account"
      read:pets: "read your pets"
  api_key:
    type: "apiKey"
    name: "api_key"
    in: "header"
definitions:
  Order:
    type: "object"
    properties:
      id:
        type: "integer"
        format: "int64"
      petId:
        type: "integer"
        format: "int64"
      quantity:
        type: "integer"
        format: "int32"
      shipDate:
        type: "string"
        format: "date-time"
      status:
        type: "string"
        description: "Order Status"
        enum:
        - "placed"
        - "approved"
        - "delivered"
      complete:
        type: "boolean"
        default: false
    xml:
      name: "Order"
  Category:
    type: "object"
    properties:
      id:
        type: "integer"
        format: "int64"
      name:
        type: "string"
    xml:
      name: "Category"
  Tag:
    type: "object"
    properties:
      id:
        type: "integer"
        format: "int64"
      name:
        type: "string"
    xml:
      name: "Tag"
  Pet:
    type: "object"
    required:
    - "name"
    - "photoUrls"
    properties:
      id:
        type: "integer"
        format: "int64"
      category:
        $ref: "#/definitions/Category"
      name:
        type: "string"
        example: "doggie"
      photoUrls:
        type: "array"
        xml:
          name: "photoUrl"
          wrapped: true
        items:
          type: "string"
      tags:
        type: "array"
        xml:
          name: "tag"
          wrapped: true
        items:
          $ref: "#/definitions/Tag"
      status:
        type: "string"
        description: "pet status in the store"
        enum:
        - "available"
        - "pending"
        - "sold"
    xml:
      name: "Pet"
externalDocs:
  description: "Find out more about Swagger"
  url: "http://swagger.io"

A partir de là, vous avez l'intégralité d'une définition API que vous pouvez réutilisez avec votre langage de programmation préférée. Pour cela, vous pouvez :

Vous pouvez également généré le code serveur qui utilisera votre définition où même le code client qui fera appel a l'API que vous venez de définir.

Pour continuer dans notre test, nous allons voir comment générer notre code serveur et comment l'utiliser.

Générer le code serveur de notre API

Pour générer le code serveur de notre API, rien de plus simple, cliquez sur le menu “Generate Server” et sélectionnez le langage désiré. Comme vous pouvez le voir, plusieurs langages sont disponibles…

Dans notre cas, nous prendrons “Python-Flask”. Vous pouvez vous reportez a la documentation Flask pour en savoir plus.

Une fois sélectionnez, vous devriez obtenir le fichier “python-flask-server-generated.zip”.
Placer le sur une machine disposant de Python et sur laquelle vous pouvez créer des environnements virtuelles (Venv).
Vous pouvez vous reportez a la documentation Virtualenv pour en savoir plus.

Décompresser le fichier, puis créer un Virtualenv :

[root@sandbox ~]# unzip python-flask-server-generated.zip 
Archive:  python-flask-server-generated.zip
  inflating: python-flask-server/test-requirements.txt  
  inflating: python-flask-server/requirements.txt  
  inflating: python-flask-server/README.md  
  inflating: python-flask-server/.swagger-codegen-ignore  
  inflating: python-flask-server/.swagger-codegen/VERSION  
  inflating: python-flask-server/.travis.yml  
  inflating: python-flask-server/tox.ini  
  inflating: python-flask-server/swagger_server/__main__.py  
  inflating: python-flask-server/swagger_server/models/category.py  
  inflating: python-flask-server/swagger_server/models/pet.py  
  inflating: python-flask-server/swagger_server/models/base_model_.py  
  inflating: python-flask-server/swagger_server/models/__init__.py  
  inflating: python-flask-server/swagger_server/models/tag.py  
  inflating: python-flask-server/swagger_server/models/order.py  
  inflating: python-flask-server/swagger_server/util.py  
  inflating: python-flask-server/swagger_server/controllers/pet_controller.py  
  inflating: python-flask-server/swagger_server/controllers/store_controller.py  
  inflating: python-flask-server/swagger_server/controllers/__init__.py  
  inflating: python-flask-server/swagger_server/encoder.py  
  inflating: python-flask-server/swagger_server/__init__.py  
  inflating: python-flask-server/swagger_server/test/__init__.py  
  inflating: python-flask-server/swagger_server/test/test_store_controller.py  
  inflating: python-flask-server/swagger_server/test/test_pet_controller.py  
  inflating: python-flask-server/swagger_server/swagger/swagger.yaml  
  inflating: python-flask-server/git_push.sh  
  inflating: python-flask-server/.gitignore  
  inflating: python-flask-server/.dockerignore  
  inflating: python-flask-server/setup.py  
  inflating: python-flask-server/Dockerfile  
[root@sandbox ~]# cd python-flask-server/
[root@sandbox python-flask-server]# python3 -m venv venv

Activez votre Virtualenv :

[root@sandbox python-flask-server]# . venv/bin/activate
(venv) [root@sandbox python-flask-server]# 

Assurez-vous que “pip” soit à jour :

(venv) [root@sandbox python-flask-server]# pip install --upgrade pip
Collecting pip
  Using cached https://files.pythonhosted.org/packages/30/db/9e38760b32e3e7f40cce46dd5fb107b8c73840df38f0046d8e6514e675a1/pip-19.2.3-py2.py3-none-any.whl
Installing collected packages: pip
  Found existing installation: pip 19.0.3
    Uninstalling pip-19.0.3:
      Successfully uninstalled pip-19.0.3
Successfully installed pip-19.2.3

Pour exécuter le code, des prérequis sont nécessaires, ceux-ci sont définis dans le fichier “requirments.txt”.

(venv) [root@sandbox python-flask-server]# cat requirements.txt
connexion == 1.1.15
python_dateutil == 2.6.0
setuptools >= 21.0.0

Lancer la commande suivante afin d'installer les prérequis :

(venv) [root@sandbox python-flask-server]# pip install -r requirements.txt
Collecting connexion==1.1.15 (from -r requirements.txt (line 1))
  Using cached https://files.pythonhosted.org/packages/06/45/e87be44f33982b2ee7b4fd5b1fe8bb616a22517f751f8fe2ee0b5567d231/connexion-1.1.15-py2.py3-none-any.whl
Collecting python_dateutil==2.6.0 (from -r requirements.txt (line 2))
  Using cached https://files.pythonhosted.org/packages/40/8b/275015d7a9ec293cf1bbf55433258fbc9d0711890a7f6dc538bac7b86bce/python_dateutil-2.6.0-py2.py3-none-any.whl
 
...
 
Collecting MarkupSafe>=0.23 (from Jinja2>=2.10.1->flask>=0.10.1->connexion==1.1.15->-r requirements.txt (line 1))
  Using cached https://files.pythonhosted.org/packages/ce/c6/f000f1af136ef74e4a95e33785921c73595c5390403f102e9b231b065b7a/MarkupSafe-1.1.1-cp37-cp37m-macosx_10_6_intel.whl
Installing collected packages: PyYAML, idna, chardet, urllib3, certifi, requests, inflection, six, attrs, pyrsistent, jsonschema, swagger-spec-validator, Werkzeug, MarkupSafe, Jinja2, click, itsdangerous, flask, clickclick, connexion, python-dateutil
  Running setup.py install for PyYAML ... done
  Running setup.py install for inflection ... done
  Running setup.py install for pyrsistent ... done
Successfully installed Jinja2-2.10.1 MarkupSafe-1.1.1 PyYAML-5.1.2 Werkzeug-0.16.0 attrs-19.1.0 certifi-2019.9.11 chardet-3.0.4 click-7.0 clickclick-1.2.2 connexion-1.1.15 flask-1.1.1 idna-2.8 inflection-0.3.1 itsdangerous-1.1.0 jsonschema-3.0.2 pyrsistent-0.15.4 python-dateutil-2.6.0 requests-2.22.0 six-1.12.0 swagger-spec-validator-2.4.3 urllib3-1.25.6

Nous sommes désormais prêt à lancer le code serveur de notre API, pour cela exécuter la commande suivante :

(venv) [root@sandbox python-flask-server]# python -m swagger_server
... OAuth2 token info URL missing. **IGNORING SECURITY REQUIREMENTS**
... OAuth2 token info URL missing. **IGNORING SECURITY REQUIREMENTS**
 * Serving Flask app "__main__" (lazy loading)
 * Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
 * Debug mode: off
 * Running on http://0.0.0.0:8080/ (Press CTRL+C to quit)

Votre API est désormais disponible sur http://localhost:8080/.
Pour quitter le serveur, faites juste CTRL+C.

Utilisation de l'API

Comment appeler l'API

Implémentation de l'API

Annexe

Swagger

Spécification OpenAPI