sgcOpenAPI 2026.9.0 : la refonte de l'analyseur

· Versions
sgcOpenAPI 2026.9.0, l'analyseur et générateur de code OpenAPI pour Delphi

sgcOpenAPI 2026.9.0 est la plus grande version que le produit ait connue. La version précédente gérait la forme de spécification que montrent la plupart des tutoriels, et se dégradait discrètement sur tout le reste. Celle-ci a repris l'analyseur fonctionnalité par fonctionnalité face aux spécifications OpenAPI 3.0, 3.1 et 3.2 et face à de vrais documents publiés, et le résultat est de 9 nouvelles fonctionnalités, 26 bugs corrigés et 5 changements de rupture assumés.

En résumé : le client généré est désormais correct pour des spécifications qui produisaient auparavant du code qui ne compilait pas, ou pire, du code qui compilait et appelait la mauvaise URL.

L'analyseur vous dit maintenant ce qu'il n'a pas pu faire

L'ancien analyseur avait une seule façon de signaler un problème, lever une exception, et une seule façon de gérer tout le reste, continuer en silence. Une opération qu'il ne pouvait pas générer n'était tout simplement pas dans la sortie, et vous le découvriez en allant chercher une méthode qui n'était pas là.

Chaque document revient désormais avec une liste Warnings. Un membre openapi ou info manquant, un membre avec le mauvais type JSON, une opération qui n'a pas pu être générée, une référence d'élément de chemin non résolue et un mot-clé JSON Schema qui est lu mais pas encore honoré y sont tous consignés. La liste est vidée à chaque lecture, donc ce que vous obtenez appartient au document que vous venez d'analyser.

uses
  sgcOpenAPI_Classes, sgcOpenAPI_Parser_Client_Pascal;

var
  oParser: TsgcOpenAPI_Parser_Client_Pascal;
  i: Integer;
begin
  oParser := TsgcOpenAPI_Parser_Client_Pascal.Create;
  Try
    oParser.OpenAPIClassName := 'TPetStoreClient';
    oParser.OpenAPINamespace := 'PetStore';
    oParser.OutputFileName := 'PetStoreClient.pas';

    oParser.ReadFromFile('petstore.json');

    for i := 0 to oParser.Warnings.Count - 1 do
      Memo1.Lines.Add('warning: ' + oParser.Warnings[i]);

    oParser.SaveToFile('PetStoreClient.pas');
  Finally
    oParser.Free;
  End;
end;

Définissez OutputFileName avant d'enregistrer. Une unité Pascal ne compile que lorsque son nom déclaré correspond au nom de base de son fichier, et le générateur nommait auparavant l'unité d'après le document d'entrée, si bien que générer MyClient.pas à partir de petstore.json produisait une unité appelée petstore qui ne compilait pas. Le nom de sortie l'emporte désormais.

Il sait quelle version il est en train de lire

OpenAPI 3.0 et 3.1 ne s'accordent pas sur des mots-clés qui partagent un nom, et l'ancien analyseur traitait chaque document comme du 3.0. Le cas le plus clair est exclusiveMinimum, qui est un modificateur booléen sur minimum en 3.0 et un nombre à part entière en 3.1. Lire l'un comme l'autre donne une borne erronée.

La version est maintenant analysée sous forme de dialecte, exposé par Dialect, DialectMajor et DialectMinor, et chaque mot-clé qui diffère est lu comme sa propre version l'exige.

oParser.ReadFromFile('api.yaml');

case oParser.Dialect of
  oapiDialect30: ShowMessage('OpenAPI 3.0');
  oapiDialect31: ShowMessage('OpenAPI 3.1');
  oapiDialect32: ShowMessage('OpenAPI 3.2');
end;

À cela s'ajoute que la 3.1 apporte webhooks, jsonSchemaDialect et components.pathItems, l'identifier de la licence, le schéma de sécurité mutualTLS, un type déclaré comme un tableau tel que ["string","null"], et un schéma déclaré comme un simple booléen. Tous sont pris en charge. Les mots-clés JSON Schema 2020-12 sur lesquels le générateur de code n'agit pas encore sont lus dans le modèle et signalés via Warnings, de sorte que le manque est visible au lieu d'être invisible.

À partir de la 3.2, l'analyseur prend en charge l'opération query et la table additionalOperations. Un chemin qui déclare l'une ou l'autre génère désormais les méthodes correspondantes, envoyées en POST avec un en-tête X-HTTP-Method-Override.

Les paramètres au niveau du chemin

C'est le correctif que la plupart des utilisateurs ressentiront. La spécification vous laisse déclarer un paramètre une seule fois sur l'élément de chemin au lieu de le répéter dans chaque opération en dessous, et c'est le style que la spécification recommande et celui qu'utilisent la plupart des documents publics. L'ancien analyseur lisait ces paramètres puis les abandonnait.

La méthode générée ne prenait aucun argument, et la requête partait avec le paramètre substituable toujours présent dans l'URL, littéralement /pets/{petId}. Cela ressemblait à un client qui fonctionne jusqu'à ce que le premier appel revienne avec un 404.

Des schémas qui se composent

La composition était la partie la plus faible de l'ancien analyseur, et chacune de ses branches était fausse d'une manière différente. allOf ne conservait que le dernier de plusieurs schémas de base et jetait les membres des autres. oneOf fusionnait toutes les branches en une seule classe, ce qui produisait des champs en double. anyOf n'était pas géré du tout et se résolvait en une chaîne. Un schéma déclarant à la fois properties et additionalProperties perdait toutes ses propriétés.

Les quatre génèrent maintenant ce que le document décrit. Un schéma d'objet en ligne obtient lui aussi sa propre classe au lieu de se dégrader en chaîne, et items est lu comme un schéma complet, si bien qu'un tableau d'objets en ligne, un tableau d'énumérations et un tableau imbriqué génèrent chacun le bon type.

Des énumérations qui portent la valeur attendue par le serveur

Les tables d'énumération générées contenaient auparavant l'identifiant Pascal nettoyé au lieu de la valeur réellement transmise, si bien que allow-all partait en allowall et json-file en jsonfile. Chaque requête construite à partir d'une de ces énumérations était rejetée.

Les tables portent désormais la valeur réelle, l'ordre de déclaration de la spécification est préservé, les énumérations d'entiers ont elles aussi leur table, et un membre Unknown supplémentaire est généré pour qu'une valeur ajoutée plus tard par le serveur ne soit pas décodée en silence comme le premier membre de la liste.

Les noms de propriétés reçoivent le même traitement dans l'autre sens. Une propriété de schéma nommée d'après un mot réservé de Delphi tel que property, class, string ou function, ou deux propriétés ne différant que par la casse telles que Name et name, produisaient auparavant une unité qui ne compilait pas. La propriété est maintenant renommée et le nom transmis est préservé par un attribut JSONName, de sorte que la sérialisation correspond toujours au document.

Les réponses, y compris celles que vous ne déclarez qu'une seule fois

La réponse default et les réponses par plage 2XX, 4XX et 5XX étaient abandonnées en silence. Une API qui déclare ses erreurs uniquement au travers de default, ce qui est courant, générait un client sans aucune erreur typée. Elles sont lues maintenant. Lorsque plusieurs réponses de succès sont déclarées, la plus basse est utilisée, et application/json est préféré quand une opération propose plusieurs types de média.

Les paramètres tels qu'ils sont transmis

Le client généré prend maintenant en charge les paramètres de cookie et l'ensemble des règles de sérialisation des paramètres OpenAPI : matrix, label, simple, form, spaceDelimited, pipeDelimited et deepObject, chacune avec explode et allowReserved. De nouvelles méthodes AddArray et AddObject construisent les valeurs structurées à la main lorsque vous en avez besoin.

// query parameters built with the serialization style the document declares
oRequest.AddArray('tags', ['dog', 'cat'], True);   // explode
oRequest.AddObject('filter', ['color', 'red', 'size', 'M']);

Les références externes

Une spécification répartie sur plusieurs fichiers fonctionnait à peine. Une référence portant un fragment JSON Pointer tel que ./common.yaml#/components/schemas/Error ne pouvait pas être résolue. Deux fichiers qui se référencent mutuellement faisaient planter l'analyseur. Une référence relative à l'intérieur d'un sous-document était résolue par rapport au document racine plutôt que par rapport à son propre fichier. Deux fichiers externes portant le même nom de base s'écrasaient l'un l'autre, et pouvaient remplacer un schéma appartenant au document principal. Et une chaîne de références n'était suivie que sur un seul niveau.

Tout cela est corrigé, et un point est délibérément resserré : une référence externe pouvait auparavant lire n'importe quel fichier de la machine, ../../../credentials.json compris, et copier son contenu dans l'unité générée. Les références externes sont maintenant confinées au répertoire du document principal. Lorsqu'une organisation des fichiers a réellement besoin d'en sortir, le confinement est levé explicitement.

uses
  sgcOpenAPI_Bundle;

begin
  // off by default: references may not leave the folder of the main document
  sgcOpenAPIAllowRefsOutsideRoot := True;
end;

Des fichiers qui ne sont pas tout à fait en UTF-8

La RFC 8259 dit qu'un document JSON est en UTF-8, et bon nombre de spécifications publiées ne le sont pas. Un fichier portant une marque d'ordre des octets était auparavant rejeté avec une erreur UTF-8 dès qu'il contenait un caractère hors ASCII, et le texte chinois ou japonais était remplacé en silence par des points d'interrogation.

Un document qui n'est pas de l'UTF-8 valide est désormais lu comme du Windows-1252 avec un avertissement consigné, au lieu d'échouer. Un fichier UTF-16 portant une marque d'ordre des octets est lu correctement. Le fichier généré est écrit avec un encodage explicite, et un caractère que l'encodage cible ne peut pas représenter est signalé au lieu de devenir discrètement un point d'interrogation.

Une ligne de commande que vous pouvez mettre dans un script de compilation

La ligne de commande définit maintenant un code de sortie : 0 en cas de succès, et 1 à 7 pour les différents échecs, de sorte qu'une étape de compilation peut savoir si la génération a fonctionné. Les messages d'erreur vont toujours vers la sortie d'erreur standard, et le commutateur -l ne sert plus qu'à la journalisation de la progression.

sgcOpenAPI -i petstore.json -o PetStoreClient.pas -c TPetStoreClient
if errorlevel 1 (
  echo OpenAPI generation failed with exit code %errorlevel%
  exit /b %errorlevel%
)

Trois bugs de ligne de commande sont partis avec lui. Le commutateur documenté -output écrivait l'unité dans un fichier appelé utput dans le répertoire courant, et comme les messages étaient supprimés, l'exécution avait quand même l'air d'avoir réussi. Rien ne se passait du tout lorsque l'outil s'exécutait sans console attachée, ce qui est exactement le cas sur une tâche planifiée ou un agent de compilation, et une redirection de sortie existante était ignorée. Et -h affichait une erreur de licence plutôt que le texte d'aide sur une machine qui n'était pas activée, une valeur invalide pour -m ou -a était acceptée en silence, et un commutateur inconnu était ignoré.

Nouveau dans cette version, -r (ou -remote) convertit un document YAML ou Swagger 2.0 au travers du convertisseur public de converter.swagger.io. Il est désactivé par défaut, parce qu'il envoie votre document à un tiers, c'est donc quelque chose que vous activez en connaissance de cause.

La conversion Swagger 2.0 elle-même était cassée de deux façons qui méritent d'être nommées. Chaque nombre devenait une chaîne, si bien qu'une valeur par défaut numérique produisait une unité qui ne compilait pas et que le document converti n'était pas un OpenAPI 3.0 valide. Et un discriminator Swagger 2.0, qui y est une simple chaîne, faisait avorter toute l'analyse avec un transtypage invalide.

Changements de rupture

Cinq changements demandent une décision de votre part plutôt qu'une simple mise à jour.

Les clients générés vérifient désormais le certificat du serveur. Ce n'était pas le cas avant, ce qui veut dire qu'ils acceptaient n'importe quel certificat, y compris celui présenté par un intermédiaire malveillant. Pour atteindre un point de terminaison auto-signé ou de test, désactivez la vérification délibérément.

oClient.TLSOptions.VerifyCertificate := False;   // test endpoints only
// certificates are trusted through the OpenSSL default paths, so a machine
// with no certificate store configured needs an explicit root
oClient.TLSOptions.RootCertFile := 'cacert.pem';

Le corps de la requête est en UTF-8. Comme l'exige la RFC 8259. Une classe sérialise maintenant une chaîne vide en "field": "" au lieu de l'omettre. Les valeurs nulles se contrôlent séparément.

oClient.JSONIgnoreEmptyStrings := True;  // previous output
oClient.JSONIgnoreNullValues := True;    // default

Une réponse ne libère plus un ResponseStream que vous avez fourni. Mettez OwnsResponseStream à True pour retrouver l'ancien comportement. Libérer le client depuis son propre gestionnaire OnResponse, OnError ou OnCancel lève maintenant une erreur claire au lieu de rester bloqué.

La valeur d'un commutateur de ligne de commande doit s'écrire -name value ou -name:value. La forme accolée sans séparateur, telle que -x"GET /pets", n'est plus acceptée. C'est aussi cette forme qui faisait que -x correspondait à d'autres commutateurs commençant par x, tels que -xml.

Un paramètre déclaré comme un tableau est généré comme un tableau. Il était auparavant généré comme une chaîne, la signature de la méthode générée change donc pour ces opérations.

Tout le reste

Les correctifs restants sont de ceux que l'on ne remarque que lorsqu'ils mordent. Un membre avec un type JSON inattendu, par exemple "properties": [], faisait avorter l'analyse avec un transtypage invalide au lieu d'être ignoré. Un schéma de type entier sans valeur par défaut se voyait attribuer une valeur par défaut de 0, et une énumération à valeur unique était traitée comme une constante, ce qui retirait complètement le paramètre de la méthode générée. Lire deux fois le même document dupliquait chaque chemin, tag, serveur et schéma. Une extension de spécification telle que x-tagGroups placée parmi les chemins était lue comme s'il s'agissait d'un chemin. enum, required et tags étaient analysés avec un utilitaire de texte séparé par des virgules, si bien qu'une valeur contenant une virgule ou un échappement JSON était scindée ou corrompue. Une exigence de sécurité listant plusieurs schémas n'en gardait que le premier, perdant l'exigence qu'ils soient tous satisfaits. info.contact et info.license n'étaient jamais lus du tout, à cause d'un test qui ne pouvait jamais être vrai. Une URL de serveur comportant plusieurs variables substituait la mauvaise valeur et pouvait déclencher une erreur d'index de liste. Le regroupement d'une spécification écrasait le fichier d'entrée sans sauvegarde et sans message, et supprimait du document toutes les apostrophes typographiques. Et une spécification stockée sous un chemin contenant un espace, tel que C:\My Specs\, ne pouvait pas résoudre ses références externes.

Pour l'obtenir

sgcOpenAPI 2026.9.0 est disponible dès maintenant, avec le code source complet et un an de mises à jour. Il prend en charge Delphi 7 jusqu'à Delphi 13 Florence et les versions correspondantes de C++ Builder.

Page du produit · Télécharger la version d'essai · Journal des modifications

Des questions ou des remarques ? Contactez-nous, vous recevrez une réponse des personnes qui ont écrit le code.