La validation des contrats
d’interfaces au format OpenAPI
Meetup - 16 Mai 2017
Marseille PHP User Group - AFUP
Nicolas Macherey – Fondateur WakeOnWeb
OpenAPI/Swagger: Qu’est-ce que c’est ?
• Un format simple, compréhensible et collaboratif
• Complet, très répandu
• YAML ou JSON
• Structuré et respectant l’HTTP
• Facile à intégrer au SCM (Git ou autre)
• Permet de sortir une doc simple et compréhensible
• Des outils dédiés
• Swagger Editor: Editeur web avancé avec système de validation
• Swagger UI: Générateur de documentation à partir de la spec
• Swagger CodeGen: Générateur de code client/serveur
Swagger UI permet aussi de tester les APIs manuellement en
faisant des appels depuis l’interface exposant la documentation.
http://swagger.io/
swagger: '2.0’
info:
version: "1.0.0"
title: Basic Auth Example
description: |
An example for how to use Basic Auth with Swagger.
**User Name and Password**
* User Name: `user`
* Password: `pass`
host: basic-auth-server.herokuapp.com
schemes:
- http
- https
securityDefinitions:
basicAuth:
type: basic
description: HTTP Basic Authentication….
paths:
/:
get:
security:
- basicAuth: []
responses:
200:
description: Will send `Authenticated`…
Méthodologies: Contract First/Last mais pourquoi ?
Contract Last
Les spécifications d’API sont extraites du code comme un
constat de ce qui a été fait…
• Agile/Plus rapide
• Moins collaboratif Très difficile de paralléliser le travail
• Correspond toujours à la réalité de l’API
Contract First
Les spécifications sont le cœur central du développement
elles sont faites au moment des phases de spécifications
• Facilite le travail parallèle (Gain de productivité)
• Moins agile, nécessite la mise en place de
communications dédiées
• Peut contenir des différences à l’implémentation
(Recette)
• Permet la génération automatique de code serveur
Les enjeux architecturaux récents…
• Propriétés
• Chaque service peut être (et est) conçu, développé, testé et déployé
indépendamment.
• De ce fait, il peut évoluer a son rythme.
• Bénéficie d’une forte cohésion interne à l’application.
• Est parfaitement adapté aux technologies de type docker, permettant
de déployer chaque service dans un conteneur dédié.
• Avantages
• Réduction du Time To Market, notamment en parallélisant les
développements
• Flexibilité technologique et indépendance pour chaque service
• Extensibilité / Evolutivité largement facilité
• Résilience augmentée
• Réduction des coûts de maintenance et d’évolution
• Réduction des risques
Mais il y a tout de même des règles à respecter pour que cela se
passe bien…
API GATEWAY
L’importance de valider que les nouvelles évolutions
respectent les contrats d’interface dans une suite distribuée
(entrée/sortie)
Intégration aux tests automatisés
Intégration continue
Non-Régression
Objectif n°1: Mieux gérer ses évolutions
Identifier les erreurs
Sécuriser ses API
Faciliter ses traitements métiers
Intégration aux frameworks
Intégration en production
Objectif n°2: Faciliter le traitement des requêtes HTTP
WakeOnWeb/swagger: Présentation
▪ Répondre à un besoin (d’abord perso...)
▪ L’existant n’était pas satisfaisant
▪ Faciliter nos phases d’intégrations front/back ou dans nos suites de services
▪ Une première ouverture vers l’OpenSource/Contribution communautaire
▪ Un composant utilisable au sein de tous les frameworks PHP (Silex, Laravel, Symfony…)
▪ Extensible et rapide
▪ Compatible avec différents validateurs de données JSON (JustinRainbow/Jval…)
WakeOnWeb/swagger: Fonctionnalités
▪ Ajout de validateurs personnalisés
▪ Compatibilité PSR-7 (MessageInterface)
▪ Compatibilité PSR-6 (Cache)
▪ Respect de la spécification OpenAPI au format Swagger 2.0 (3.0 à venir)
▪ Gestion des extensions vendor spécifiques
▪ Intégration PHPUnit/Behat
▪ Intégration Symfony
WakeOnWeb/swagger: Installation
> composer require wakeonweb/swagger
> composer require justinrainbow/json-schema
Le composant intègre 1 bridge et permet d’en intégrer
d’autres
// File system for cache storage
$filesystem = new Filesystem(new Local('path/to/wakeonweb/swagger'));
$factory = new SwaggerFactory(new FilesystemCachePool($filesystem));
// Add loader Yaml and JSON supported
$factory->addLoader(new YamlLoader());
// Parse file and store to cache directory and/or read the file content
$swagger = $factory->buildFrom('path/to/swagger.yml');
$this->swaggerValidator = new SwaggerValidator($swagger);
// Register the appropriate JSON Schema Validator for Content validation
$this->swaggerValidator->registerContentValidator(new JustinRainbowJsonSchemaValidator());
// Converts the response to a PRS-7 compliant format.
$response = (new DiactorosFactory())->createResponse($response);
// Validate the response for the given path/method/status
$this->swaggerValidator->validateResponseFor(
$response,
PathItem::METHOD_GET, '/api/users/{id}/orders', 200
);
WakeOnWeb/swagger: Utilisation
WakeOnWeb/swagger: Exemple d’intégration a Behat
class OpenApiValidationContext implements Context, SnippetAcceptingContext
{
use KernelDictionary;
private $swaggerValidator;
/**
* @BeforeScenario
*/
public function loadSwaggerValidator()
{
$kernel = $this->getContainer()->get('kernel');
$filesystem = new Filesystem(new Local($kernel->getCacheDir() . '/wakeonweb/swagger'));
$factory = new SwaggerFactory(new FilesystemCachePool($filesystem));
$factory->addLoader(new YamlLoader());
$swagger = $factory->buildFrom($kernel->getRootDir() . '/../docs/api/swagger.yml');
$this->swaggerValidator = new SwaggerValidator($swagger);
$this->swaggerValidator->registerContentValidator(new JustinRainbowJsonSchemaValidator());
}
/**
* @Then The response should validate :path open api specification with :method method and :statusCode status
*/
public function validateOpenApiSpecification($path, $method = PathItem::METHOD_GET, $statusCode = 200)
{
$this->swaggerValidator->validateResponseFor(
(new DiactorosFactory())->createResponse($this->response), $method, $path, $statusCode);
}
}
WakeOnWeb/swagger: Roadmap
▪ 7 Issues fonctionnelles planifiées pour faire évoluer le système (Juin 2016)
▪ Un travail pour permettre la validation des requêtes en production
▪ Analyse de performances
▪ Optimisation des traitements
▪ Evolutions des validateurs/JSON Schema
▪ Amélioration du processus de mise en cache
▪ Une intégration sous forme de bundle Symfony (Sept 2016) suivant 2 modes:
▪ Listener de requêtes
▪ Annotation
▪ Objectif: Profiter des fonctionnalités du Framework pour matcher les Routes avec les paths swagger
directement dans les Controllers, Intégration au système de gestion du cache de Symfony.
▪ Intégration avec Nelmio/ApiDocBunde (Compatible avec le format OpenAPI)