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é.
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 .
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
É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 :
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.
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.
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.