OpenAPI 3.0 – Partie 3 
Finalisation des 3 services

02/06/2024 - 11:29:55

13'30"

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 !

Objectif de cet article

Nous allons finaliser la toute première version de notre API, une API de données économiques exposant 3 services. Nous allons voir que c'est excessivment simple.

Ensuite, une fois cette étape atteinte, dans un prochain article, nous irons crescendo dans la difficulté en ajoutant un quatrième service qui, cette fois, et ce sera là toute la nouveauté, nécessitera le passage de paramètre(s).

L'ajout des deux services manquants

Finalement, cet ajout est une simple affaire de copier-coller, puis de modifications mineures. Voici l'état de notre YAML :

openapi: 3.1.0
info:
  version: 0.0.1
  title: TRQL Radio economics-related services
  description: First version of the TRQLEconomics API featuring 3 services
  contact:
    email: pb@latosensu.be
  license:
      name: CC BY-SA 4.0 DEED Attribution-ShareAlike 4.0 International
      url: https://creativecommons.org/licenses/by-sa/4.0/
  termsOfService: https://www.trql.fm/tos/

servers:
  - url: http://www.trql.fm/vaesoli!
    description: Production Server
paths:
  /?bankruptcies:
    get:
      responses:
        '200':
          description: List of bankruptcies per country

Voici ce qu'il devient :

openapi: 3.1.0
info:
  version: 0.0.1
  title: TRQL Radio economics-related services
  description: First version of the TRQLEconomics API featuring 3 services
  contact:
    email: pb@latosensu.be
  license:
      name: CC BY-SA 4.0 DEED Attribution-ShareAlike 4.0 International
      url: https://creativecommons.org/licenses/by-sa/4.0/
  termsOfService: https://www.trql.fm/tos/

servers:
  - url: http://www.trql.fm/vaesoli!
    description: Production Server
paths:
  /?bankruptcies:
    get:
      responses:
        '200':
          description: List of bank ruptcies per country
  /?corruption:
    get:
      responses:
        '200':
          description: List of corruption index per country
  /?currencies:
    get:
      responses:
        '200':
        description: 9 conversion rates of currencies compared to EURO

Et voici ce que Swaggerhub nous propose en partie UI :

Fig. 1 - Swaggerhub: Nos 3 services sont renseignés ET nous pouvons les tester !

Ne boudons pas notre plaisir ! Avec ce genre de fonctionnalité, nous disposons d'une console à services ! Il suffirait de faire le pont entre notre catalogue de services et ce YAML pour disposer d'une solution quasi complète. Et … vous vous en doutez maintenant, c'est EXACTEMENT la ligne sur laquelle je vous conduit car, au surplus, nous disposons là aussi de notre embryon d'automatisation de notre pipeline de déploiement cher à DevOps ! Je vais arrêter de m'emballer. Pied sur le frein !

Petit mot pour le Product Owner, petit mot pour le Scrum Master, petit mot pour le Project Manager , et petit mot pour le Testeur

Cette série d'articles sur l'écriture des services et microservices vous est destinée. Les gens qui sont dans la technique connaissent cela par cœur.

Si je dis que c'est fait pour vous, c'est parce que la compréhension du Comment ça marche vous apportera un éclairage sur la nature du développement moderne, du moins ses principes. Cela vous permettra de juger de la pertinence des solutions techniques, surtout si vous consultez le ou les architecte(s) et autre(s) leader(s) technique(s), mais aussi de juger de l'adéquation entre ce qui est demandé et les estimations qui vous sont soumises. Pour le testeur, voilà de quoi commencer à tester les services, et même de corriger/participer à la définition des services, de telle sorte que les spécifications soient, elles aussi, de grande qualité. J'ai la faiblesse de penser que cela fait de vous des personnes mieux éclairées, si tant est que ce soit possible, pour le bien des organisations pour lesquelles vous exercez votre métier.

Développons plus avant les 3 services dont nous disposons

Commençons par la réponse. Comme vous vous en souvenez peut-être, toutes les réponses de nos services sont composées d'un prolog (meta data concernant le service) et d'un payload (le corps principal de la réponse). Dans notre cas, c'est invariable de telle sorte que nous pouvons définir le même type de retour pour chaque service, un retour qui se fait est au format JSON.

Le retour est donc un objet JSON composé de … 2 objets, le prolog et le payload. Voyons comment mentionner cela pour le service bankruptcies sans entrer dans le detail, ni du prolog, ni du payload :

schema object

Je vais vous montrer directement comment renseigner cette structure du retour :

paths:
  /?bankruptcies:

    get:
      responses:
        '200':
          description: List of bankruptcies per country
          content:
            application/json:              # Le retour est formaté en JSON
              schema:                      # À détailler
                type: object               # L'objet de retour englobant le prolog et le payload
                properties:
                  prolog:                  # L'objet prolog qu'on devra détailler plus avant
                    type: object
                  payload:                 # L'objet payload qu'on devra détailler plus avant
                    type: object

L'objet schema peut être consulté en ligne.

Trois propriétés de cet objet nous intéressent au premier chef :

  • type : type du schéma, dans notre cas, un objet (le retour JSON est un objet qui en inclut 2 autres)
  • description : Description du retour
  • properties : des propriétés imbriquées définies elles-mêmes comme des schémas

Ajoutons une description au schéma de retour :

paths:
  /?bankruptcies:

    get:
      responses:
        '200':
          description: List of bankruptcies per country
          content:
            application/json:             # Retour en JSON
              schema:                     # À détailler ultérieurement
                type: object              # L'objet de retour englobant le prolog et le payload
                description: Le retour est un objet composé de deux objets, prolog et payload.
                properties:
                  prolog:                 # L'objet prolog qu'on devra détailler plus avant
                    type: object
                  payload:                # L'objet payload  qu'on devra détailler plus avant
                    type: object
Fig. 2 - Swaggerhub: le schéma de réponse est mentionné pour bankruptcies et nous en avons fourni une brève description

C'est ici que les Romains s'empoignèrent : précisions à présent les propriétés ! Afin de montrer comment cela fonctionne et de minimiser le code YAML à considérer, je ne vais m'employer qu'à définir le payload car il est bien plus simple que le prolog.

paths:
  /?bankruptcies:

    get:
      responses:
        '200':
          description: List of bankruptcies per country
          content:
            application/json:             # Retour en JSON
              schema:                     # À détailler ultérieurement
                type: object              # L'objet de retour englobant le prolog et le payload
                description: Le retour est un objet composé de deux objets, prolog et payload.
                properties:
                  prolog:                 # L'objet prolog qu'on devra détailler plus avant
                    type: object
                  payload:                # L'objet payload  qu'on devra détailler plus avant
                    type: object
                    properties:
                      entries:            # Seule propriété de l'objet payload
                        type: array       # entries est un tableau d'entrées
Fig. 3 - Swaggerhub: vous constatez dès lors que le retiur est documenté plus avant

On pourrait — et nous le ferons plus tard — détailler le tableau entries.

Pourquoi vous disais-je que c'était ici que les Romains se mettaient à s'empoigner ? Tout simplement parce que l'objet prolog est beaucoup plus détaillé et donc, infiniment plus long à documenter. Faisons-le pour le service bankruptcies seul !

paths:
  /?bankruptcies:

    get:
      responses:
        '200':
          description: List of bankruptcies per country
          content:
            application/json:             # Retour en JSON
              schema:                     # À détailler ultérieurement
                type: object              # L'objet de retour englobant le prolog et le payload
                description: Le retour est un objet composé de deux objets, prolog et payload
                properties:
                  prolog:
                    type: object
                    description: metadata of the service
                    properties:
                      service:
                        type: string
                      domain:
                        type: string
                      line:
                        type: integer
                      performance:
                        type: number
                      error:
                        type: integer
                      messages:
                        type: array
                      datetime:
                        type: string
                      cached:
                        type: boolean
                      cacheDateTime:
                        type: string
                      ttl:
                        type: integer
                      callDateTime:
                        type: integer
                      copyright:
                        type: string
                      disclaimer:
                    type: string
                  payload:
                    type: object
                    description: véritable charge utile du retour (la cœur de la réponse)
                    properties:
                      entries:
                        type: array       # entries est un tableau d'entrées

Si on devait répercuter cela sur tous nos services, on alourdirait considérablement la taille du YAML ce qui peut rapidement devenir peu commode. De plus, si nous devions modifier la définition du prolog par exemple, on risque de faire des fautes de frappe, oublier l'un ou l'autre service, etc. Quand on n'a que 3 services comme dans notre cas, passe encore, mais lorsque vous en avez 63 comme dans le cas de TRQL Radio (services liés à des données économiques), cela devient carrément rédhibitoire !

Heureusement, OpenAPI a prévu le coup ! Nous allons passer par une définition générale et nous ferons référence à cette définition générale dans chaque service; nous allons utiliser … les composants !

Définir un composant

Les composants de OpenAPI sont une manière de standardiser ce que vos services utiliseront et ré-éutiliseront. Cela réduit considérablement le travail tout en améliorant la qualité de nos spécifications et c'est notamment pour cette raison que je m'adresse aux Product Owners, aux Scrum Masters, aux Project Managers et aux Testeurs parce que tous peuvent contribuer à réduire le travail (parfois en y prenant part), à comprendre ce qui se passe en arrière-plan, et à améliorer la qualité finale du tout, notamment par une révision approfondie des spécifications fournies pour chaque service. Tout profit pour vos projets/produits et pour l'entreprise dans laquelle vous œuvrez.

La doc des composants est disponible sur le GitHub de OpenAPI.

Voyons tout de suite comment définir des composants qui sont des schémas puisque c'est, au demeurant, ce qui nous intéresse au premier chef. Commençons par notre fameux prolog que nous allons inclure dans notre YAML au même niveau que l'objet paths:

components:
  schemas:
    prolog:
      type: object
      description: metadonnées du service
      properties:
        service:
          type: string
        domain:
          type: string
        line:
          type: integer
        performance:
          type: number
        error:
          type: integer
        messages:
          type: array
        datetime:
          type: string
        cached:
          type: integer
        cacheDateTime:
          type: string
        ttl:
          type: integer
        callDateTime:
          type: integer
        copyright:
          type: string
        disclaimer:
        type: string

Maintenant que nous avons défini notre schéma "prolog", voyons comment y faire référence dans le service bankruptcies

paths:
  /?bankruptcies:

    get:
      responses:
        '200':
          description: List of bankruptcies per country
          content:
            application/json:             # Retour en JSON
              schema:                     # À détailler ultérieurement
                type: object              # L'objet de retour englobant le prolog et le payload
                description: Le retour est un objet composé de deux objets, prolog et payload
                properties:
                  prolog:
                    $ref: "#/components/schemas/prolog"
                  payload:
                    type: object
                    description: véritable charge utile du retour (la cœur de la réponse)
                    properties:
                      entries:
                        type: array       # entries est un tableau d'entrées

Et le tour est joué ! On a une définition globale à laquelle on peut faire référence dans chaque service, on ne perd pas de temps à définir et redéfinir la même chose, on peut modifier la définition et voir nos modifications se répercuter sur tous les services où nous y faisons appel et tout le monde dans le projet peut partager la même vision commune du fonctionnement des services.

Puisque tel est le cas, faisons la même chose pour le payload :

components:
  schemas:
    prolog:
      type: object
      description: metadonnées du service
      properties:
        service:
          type: string
        domain:
          type: string
        line:
          type: integer
        performance:
          type: number
        error:
          type: integer
        messages:
          type: array
        datetime:
          type: string
        cached:
          type: integer
        cacheDateTime:
          type: string
        ttl:
          type: integer
        callDateTime:
          type: integer
        copyright:
          type: string
        disclaimer:
          type: string
    payload:
      type: object
      description: véritable charge utile du retour (la cœur de la réponse)
      properties:
        entries:
        type: array       # entries est un tableau d'entrées

Fig. 4 - Swaggerhub : 2 composants définis; utilisation dans le service bankruptcies

Généralisation aux 3 services et YAML final (pour cet article)

openapi: 3.1.0
info:
  version: 0.0.1
  title: TRQL Radio economics-related services
  description: First version of the TRQLEconomics API featuring 3 services
  contact:
    email: pb@latosensu.be
  license:
      name: CC BY-SA 4.0 DEED Attribution-ShareAlike 4.0 International
      url: https://creativecommons.org/licenses/by-sa/4.0/
  termsOfService: https://www.trql.fm/tos/

servers:
  - url: http://www.trql.fm/vaesoli!
    description: Production Server

components:
  schemas:
    prolog:
      type: object
      description: metadonnées du service
      properties:
        service:
          type: string
        domain:
          type: string
        line:
          type: integer
        performance:
          type: number
        error:
          type: integer
        messages:
          type: array
        datetime:
          type: string
        cached:
          type: integer
        cacheDateTime:
          type: string
        ttl:
          type: integer
        callDateTime:
          type: integer
        copyright:
          type: string
        disclaimer:
          type: string
    payload:
      type: object
      description: véritable charge utile du retour (la cœur de la réponse)
      properties:
        entries:
          type: array       # entries est un tableau d'entrées

paths:
  /?bankruptcies:

    get:
      responses:
        '200':
          description: List of bankruptcies per country
          content:
            application/json:             # Retour en JSON
              schema:
                type: object              # L'objet de retour englobant le prolog et le payload
                description: Le retour est un objet composé de deux objets, prolog et payload
                properties:
                  prolog:
                    $ref: "#/components/schemas/prolog"
                  payload:
                    $ref: "#/components/schemas/payload"
  /?corruption:
    get:
      responses:
        '200':
          description: List of corruption index per country
          content:
            application/json:             # Retour en JSON
              schema:
                type: object              # L'objet de retour englobant le prolog et le payload
                description: Le retour est un objet composé de deux objets, prolog et payload
                properties:
                  prolog:
                    $ref: "#/components/schemas/prolog"
                  payload:
                $ref: "#/components/schemas/payload"
  
  /?currencies:
    get:
      responses:
        '200':
          description: 9 conversion rates of currencies compared to EURO
          content:
            application/json:             # Retour en JSON
              schema:
                type: object              # L'objet de retour englobant le prolog et le payload
                description: Le retour est un objet composé de deux objets, prolog et payload
                properties:
                properties:
                  prolog:
                    $ref: "#/components/schemas/prolog"
                  payload:
                $ref: "#/components/schemas/payload"

Conclusion

Nos 3 services partagent la même structure; ils sont tous définis correctement et sont utilisables. Le prochain article s'attachera à définir des paramètres et pour illustrer leur utilisation nous allons ajouter un quatrième service, mercator, qui nous donnera une série d'informations de géolocalisation pour une latitude/longitude données (paramètres en question).

À très bientôt et, surtout, pas de complication !

Telegram icon