OpenAPI 3.0 %--% Partie 1

01/06/2024 - 12:08:25

14'46"

Introduction

TRQL Radio est bourré de services : passage au prochain morceau, passage d'un jingle, saut "quantique" dans le catalogue de musique, passage d'une locomotive, passage d'une annonce de sponsoring, passage des tops horaire, liste d'artistes correspondant à des critères spécifiques, météo, recettes de cuisine, géolocalisation et points d'intérêt aux coordonnées courantes, liste des anniversaires de naissance et de décès d'artistes, today in history, nouvelles du jour, devises par rapport à l'euro, cotation des cryptos, faillites par pays, index de corruption, notation attribuée par les agences de notation par pays, PIB par pays, … Nous avons plus de 200 services qui sont définis pour faire tourner la radio. Les services sont utilisés partout !

Pour développer nos services, nous avons décidé, comme beaucoup de gens avant nous et beaucoup de gens après nous, de nous baser sur un système de spécifications standardisées, j'ai nommé OpenAPI, spécifiquement la version 3.0. Dès lors, je vous partage notre expérience en la matière.

Objectifs

Au terme de cet article, (1) vous savez à quoi ressemble une petite API de trois services (nous les puiserons dans nos propres définitions), (2) vous avez compris toutes les données/variables/scalaires/structures utilisés, (3) vous êtes capable de créer votre propre petite API en OpenAPI 3.0 et (4) vous disposez d'une liste de référence miniimale pour vous dirigez dans ce monde de OpenAPI.

OpenAPI 2.0 vs. OpenAPI 3.0

Alors, TRQL Radio est parti sur OpenAPI 3.0 en 2017. Avant cela, OpenAPI en était la version 2.0, une version que nous n'avons pas utilisée. Avec la version 3.0 d'OpenAPI (la version courante est la 3.1.0) une série de changements sont intervenus dans la manière de concevoir les objets de base que permettait le langage YAML et utilisés dans la description d'une API. Nous faisons l'impasse sur la spec 2.0, comme nous faisons aussi l'impasse sur Swagger (ancêtre de OpenAPI) pour directement décrire la version 3.0. Voici ces objets de base qui constituent les éléments fondamentaux pour décrire une API et avec lesquels il faut pouvoir jouer …

Objets de base de OpenAPI 3.0

Un document OpenAPI (OpenAPI object) tel que décrit par le langage YAML est un objet (une structure) qui en contient d'autres. Pour être valide, il doit contenir un ensemble de choses lesquelles sont identifiées par une petite étoile dans la liste ci-dessous.

L'objet OpenAPI est donc l'objet racine du document OpenAPI et il combine toutes les informations de l'API.

  • openapi : il s'agit d'une simple chaîne de caractères. Cette chaîne DOIT être le numéro de la version de OpenAPI utilisée.
  • Info Object : Fournit des métadonnées sur l'API, comme le titre, la version, etc.
  • Paths Object : Contient les chemins disponibles et les opérations associées à l'API.
  • Servers Object : Tableau de serveurs dont on spécifie les URL de base que l'API utilise pour les appels.
  • Components Object : Contient divers composants réutilisables pour l'API, tels que les schémas, les réponses, les paramètres, les exemples, les requêtes et les en- têtes.
  • Security Schemes Object : Tableau Définit les mécanismes de sécurité utilisés par l'API. Le nom utilisé pour chaque propriété DOIT correspondre à un schéma de sécurité déclaré dans les schémas de sécurité de l'objet Composants.
  • Tags Property : Définit les mécanismes de sécurité utilisés par l'API.
  • External Docs object : Documentation externe supplémentaire si nécessaire

Ces objets sont combinés pour créer une spécification complète qui décrit les aspects fonctionnels et techniques d'une API. Pour plus de détails, vous pouvez consulter la spécification OpenAPI v3.1.0, la dernière spec en cours, qui contient des informations complètes sur chaque objet et ses champs.

PAS DE PANIQUE : nous n'avons pas besoin de tous ces objets et propriétés pour démarrer !

Autres objets utiles de OpenAPI 3.0

  • Contact Object : Informations de contact.
  • License Object : Informations sur la licence de l'API exposée.
  • Operation Object : Décrit une opération API unique sur un path.
  • Server Object : Informations concernant le ou les serveurs qui seront suscités par les requêtes API.

L'eau à la bouche

Pour vous mettre l'eau à la bouche, et bien que cela ne soit pas notre manière de décrire une API, l'exemple qui suit a l'énorme avantage de vous faire comprendre vers quoi nous allons. Premier exemple de YAML qui décrirait une API (MAIS qui ne correspond pas du tout à OpenAPI 3.0!) :

YAML

microservices:
  - microservice:
      name: bankruptcies
      title: List of bankruptcies per country
      description: List of bankruptcies for 178 countries\:name, last numbers, previous numbers, last update

XML

Peut-être comprendrez-vous mieux la structure XML :

<microservices>
  <microservice>
    <name>bankruptcies</name>
    <title>List of bankruptcies per country</title>
    <description>List of bankruptcies for 178 countries\:name, last numbers, previous numbers, last update</description>
  </microservice>
</microservices>

C'est donc vers cela qu'on se dirige et dans notre cas on fera cela pour une API qu'on appellera TRQL Economics regroupant 3 services ! Sans que cela soit l'implémentation réelle, voici à quoi pourrait ressembler unne telle API :

XML

<microservices>
  <microservice>
    <name>bankruptcies</name>
    <title>Bankruptcies per country</title>
    <description>List of bankruptcies for 178 countries \:name, last numbers, previous numbers, last update</description>
  </microservice>

  <microservice>
    <name>corruption</name>
    <title>Corruption index per country</title>
    <description>Corruption index for a list of 178 countries \:name, last index, previous index, last update</description>
  </microservice>

  <microservice>
    <name>currencies</name>
    <title>EUR to currency list</title>
    <description>EUR to currency rate for a list of 9 countries \:name, last rate, previous rate, daily delta, daily percentage, weekly percentage, annual percentage</description>
  </microservice>
</microservices>

YAML correspondant

microservices:
  microservice:
    - name: bankruptcies
      title: Bankruptcies per country
      description: >-
        List of bankruptcies for 178 countries \:name, last numbers, previous
        numbers, last update
    - name: corruption
      title: Corruption index per country
      description: >-
        Corruption index for a list of 178 countries \:name, last index,
        previous index, last update
    - name: currencies
      title: EUR to currency list
      description: >-
        EUR to currency rate for a list of 9 countries \:name, last rate,
        previous rate, daily delta, daily percentage, weekly percentage, annual
        percentage

Structure minimale de description d'une API

Pour être valide, un document OpenAPI doit au minimum contenir 3 éléments : openapi, info, et paths.

openapi

C'est la fameuse chaîne de caractères qui spécifie quelle est version de OpenAPI qu'on utilise. Dans notre cas, bien que notre catalogue de services fait référence à OpenAPI 3.0.0, nous allons utiliser la dernière version, c'est-à-dire, 3.1.0 ce qui ne poretra pas à conséquence car tout ce que nous avons défini dans le passé est strictement compatible avec la dernière version

info object

Field Name Type Description
title string REQUIS. Le titre de l'appplication.
description string Une courte description de l'application. La syntaxe CommonMark peut être utilisée pour des représentations textuelles riches.
termsOfService string Une URL vers les conditions d'utilisation de l'API. Doit être au format d'une URL.
contact Contact Object Les informations de contact pour l'API exposée.
license License Object Les informations de licence pour l'API exposée.
version string REQUIS. La version du document OpenAPI (qui se distingue de la version de la spécification OpenAPI ou de la version de l'implémentation de l'API).

Comme vous pouvez le constater, peu d'infos sont absolument nécessaires. Pas de panique non plus !

paths object

Field Name Type Description
$ref string Permet de disposer d'une définition externe (utilisation du "$") pour ce path. La structure référencée doit l'être dans le format d'un objet Path. S'il y a conflit entre la définition référencée et ce qui est attendu, le comportement est indéfini.
summary string Un résumé facultatif, sous forme de chaîne, destiné à s'appliquer à toutes les opérations de ce path.
description string Une description facultative, sous forme de chaîne, destinée à s'appliquer à toutes les opérations ce path. La syntaxe CommonMark peut être utilisée pour des représentations textuelles riches.
get Operation Object Définition d'une opération GET pour ce path.
put Operation Object Définition d'une opération PUT pour ce path.
post Operation Object Définition d'une opération POST pour ce path.
delete Operation Object Définition d'une opération DELETE pour ce path.
options Operation Object Définition d'une opération OPTIONS pour ce path.
head Operation Object Définition d'une opération HEAD pour ce path.
patch Operation Object Définition d'une opération PATCH pour ce path.
trace Operation Object Définition d'une opération TRACE pour ce path.
servers Server Object Un tableau server alternatif pour servir toutes les opérations dans ce path
parameters Parameter Object | Reference Object Une liste de paramètres applicables pour toutes les opérations décrites pour ce path. Ces paramètres peuvent être redéfinis pour chaque opération, mais ne peuvent être éliminés. La liste ne PEUT PAS inclure de doublons. Un paramètre unique parameter est défini par une combinaison formée d'un nom (name) et d'une destination (location). La liste peut utiliser un Reference Object pour être couplée aux paramètres qui sont définis dans les composants et paramètres de l'objet OpenAPI.

Je répète le même argument : bien que ce tableau puisse être imposant vous n'utiliserez pas beaucoup de ses propriétés. Rappelez-vous que je vous ai dit que chez TRQL Radio, on utilisait essentiellement les GETs et c'est d'ailleurs 3 services de type REST, méthode GET, que nous allons documenter. Ne soyez pas interdit d'aller plus avant !

Première mise en musique

Alors, maintenant que vous savez tout cela, voyons comment on va créer un premier jet de document OpenAPI en YAML. Ce n'est pas très long et vous voyez donc que vous n'avez aucunement à être impressionné !

openapi: 3.1.0
info:
  version: 1.0
  title: "Economics"
  description: "Specifications for the TRQL Radio services related to economics data"
  termsOfService: https://www.trql.fm/tos/
  contact:
     name: Pat Boens
     email: pb@latosensu.be
     license:
       name: Creative Commons Attribution-ShareAlike 4.0 International
       url: https://creativecommons.org/licenses/by-sa/4.0/
paths: {}

Voici ce que cela donne en XML :

<openapi>3.1.0</openapi>
<info>
  <version>1</version>
  <title>Economics</title>
  <description>Specifications for the TRQL Radio services related to economics data</description>
  <termsOfService>https://www.trql.fm/tos/</termsOfService>
  <contact>
    <name>Pat Boens</name>
    <email>pb@latosensu.be</email>
    <license>
      <name>Creative Commons Attribution-ShareAlike 4.0 International</name>
      <url>https://creativecommons.org/licenses/by-sa/4.0/</url>
    </license>
  </contact>
</info>
<paths/>

Bien entendu, c'est la toute base ! Aucune opération n'est définie. On va s'en occuper par la suite et vous verrez que ce n'est pas bien sorcier non plus !

Ajoutons un (ou des) serveur(s)

Les informations concernant le serveur sur lequel les services sont disponibles ne sont pas obligatoires. Nous allons quand même en faire usage car elles simplifient la vie de la définition de nos services à venir.

Pour rappel, nous nous sommes fixés l'objectif de construire une API qui regroupe 3 services comme je l'avais précisé dans un YAML factice auquel, ici, j'ajoute la question de endpoint (couleur verte) justement pour être capable de faire le pont avec la question de serveur :

microservices:
  microservice:
    - name: bankruptcies
      title: Bankruptcies per country
      description: >-
        List of bankruptcies for 178 countries \:name, last numbers, previous
        numbers, last update
      endpoint: https://www.trql.fm/vaesoli!/?bankruptcies
    - name: corruption
      title: Corruption index per country
      description: >-
        Corruption index for a list of 178 countries \:name, last index,
        previous index, last update
      endpoint: https://www.trql.fm/vaesoli!/?corruption
    - name: currencies
      title: EUR to currency list
      description: >-
        EUR to currency rate for a list of 9 countries \:name, last rate,
        previous rate, daily delta, daily percentage, weekly percentage, annual
        percentage
      endpoint: https://www.trql.fm/vaesoli!/?currencies

L'objet server est relativement simple. En voici la définition :

Field Name Type Description
url string REQUIS. URL de l'hôte cible. Cette URL prend en charge les variables de serveur et PEUT être relative, pour indiquer que l'emplacement de l'hôte est relatif à l'emplacement où le document OpenAPI est servi. Des substitutions de variables seront effectuées lorsqu'une variable est nommée entre {accolades}.
description string Chaîne facultative décrivant l'hôte désigné par l'URL. La syntaxe CommonMark peut être utilisée pour des représentations textuelles riches.
variables Map string, Server Variable Object Une correspondance entre le nom d'une variable et sa valeur. La valeur est utilisée pour la substitution dans le modèle d'URL du serveur.

PAS DE PANIQUE : vous connaissez la rengaine. C'est pas bien compliqué et il y a très peu de choses à mentionner pour avoir une définition minimale et puisque mon mantra c'est Pas de complication ! … on va rester dans les choses sipmples !

Et donc … qu'est-ce que cela donne ? Et bien un truc très simple. Pour le coup, je vais décrire 3 serveurs : un serveur de production et un serveur de test, juste pour l'exemple car chez TRQL Radio nous n'avons qu'un seul et même serveur [1] .

Cela va être super simple ! Vous êtes prêts ?

servers:
  - url: https://dev.trql.fm/?vaesoli
    description: Development Server
  - url: https://acceptance.trql.fm/?vaesoli
    description: Acceptance Server
  - url: https://www.trql.fm/?vaesoli
    description: Production Server

Et oui … c'est vraiment tout ! Pfff … c'est trop simple !

Alors, à quoi ressemble notre document OpenAPI maintenant si nous lui ajoutons la partie "serveurs" ?

openapi: 3.1.0
info:
  version: 1.0
  title: "Economics"
  description: "Specifications for the TRQL Radio services related to economics data"
  termsOfService: https://www.trql.fm/tos/
  contact:
     name: Pat Boens
     email: pb@latosensu.be
     license:
       name: Creative Commons Attribution-ShareAlike 4.0 International
       url: https://creativecommons.org/licenses/by-sa/4.0/
servers:
  - url: https://dev.trql.fm/?vaesoli
    description: Development Server
  - url: https://acceptance.trql.fm/?vaesoli
    description: Acceptance Server
  - url: https://www.trql.fm/?vaesoli
    description: Production Server
paths: {}

Allons au %coeur% : ajout de paths

C'est ma partie préférée car c'est elle qui va me permettre de spécifier les appels et les retours de mes services. Bon, c'est vrai que je dois reconnaître que ma partie favorite reste … l'écriture du code même des services.

Bref rappel : les paths sont REQUIS ! On s'en est tiré jusqu'à présent en fournissant une donnée factice ({} — tableau vide). Cette fois, il s'agit d'être concret !

Vous vous souvenez de ce que nous utilisons que très peu de méthodes HTTP différentes ? Très souvent, il s'agit de méthodes GET. Cela va nous faciliter la tâche !

Voci ce que cela donne en YAML:

paths:
  ?bankruptcies:
    get:
       responses:
          '200':
            description: List of bankruptcies per country
  ?corruption:
     get:
       responses:
          '200':
            description: List of corruption index per country
  ?currencies:
     get:
       responses:
          '200':
            description: List of 8 rates of currencies vs. EURO

Ajoutons cette partie à notre YAML précédent :

openapi: 3.1.0
info:
  version: 1.0
  title: "Economics"
  description: "Specifications for the TRQL Radio services related to economics data"
  termsOfService: https://www.trql.fm/tos/
  contact:
     name: Pat Boens
     email: pb@latosensu.be
     license:
       name: Creative Commons Attribution-ShareAlike 4.0 International
       url: https://creativecommons.org/licenses/by-sa/4.0/
servers:
  - url: https://dev.trql.fm/?vaesoli
    description: Development Server
  - url: https://acceptance.trql.fm/?vaesoli
    description: Acceptance Server
  - url: https://www.trql.fm/?vaesoli
    description: Production Server
paths:
  ?bankruptcies:
    get:
       responses:
          '200':
            description: List of bankruptcies per country
  ?corruption:
     get:
       responses:
          '200':
            description: List of corruption index per country
  ?currencies:
     get:
       responses:
          '200':
        description: List of 8 rates of currencies vs. EURO

Vous le croirez ou pas, mais nous venons de définir notre API de trois services. Ça y est !

Liens externes utiles

Notes de bas de page

[1] … Quand nous développons un service, nous le développons avec un nom bidon, souvent généré aléatoirement. Ce service se trouve disponible directement production ! Je n'ose pas vous proposer la même approche, même si elle est particulièrement efficace. C'est la raison pour laquelle, ici, nous mentionnerons 3 serveurs, comme c'est un peu l'usage : un serveur de développement, un serveur d'acceptance (ou staging) et un serveur de production.

Telegram icon