- Extract shared ID generation + timestamps into CuidEntityTrait used by all entities - Create AbstractAuditSubscriber to deduplicate audit logic across 7 subscribers - Merge per-entity history controllers into single EntityHistoryController - Delete redundant ComposantHistory/MachineHistory/PieceHistory/ProductHistoryController - Add OpenApiDecorator for API documentation customization - Disable failOnDeprecation in PHPUnit (vendor API Platform deprecation) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
521 lines
24 KiB
PHP
521 lines
24 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\OpenApi;
|
|
|
|
use ApiPlatform\OpenApi\Factory\OpenApiFactoryInterface;
|
|
use ApiPlatform\OpenApi\Model;
|
|
use ApiPlatform\OpenApi\OpenApi;
|
|
use ArrayObject;
|
|
|
|
final class OpenApiDecorator implements OpenApiFactoryInterface
|
|
{
|
|
public function __construct(
|
|
private readonly OpenApiFactoryInterface $decorated,
|
|
) {}
|
|
|
|
public function __invoke(array $context = []): OpenApi
|
|
{
|
|
$openApi = ($this->decorated)($context);
|
|
|
|
$this->addHealthCheck($openApi);
|
|
$this->addSessionRoutes($openApi);
|
|
$this->addAdminProfileRoutes($openApi);
|
|
$this->addActivityLogs($openApi);
|
|
$this->addCommentRoutes($openApi);
|
|
$this->addCustomFieldValueRoutes($openApi);
|
|
$this->addDocumentQueryRoutes($openApi);
|
|
$this->addDocumentServeRoutes($openApi);
|
|
$this->addEntityHistoryRoutes($openApi);
|
|
$this->addMachineStructureRoutes($openApi);
|
|
$this->addMachineCustomFieldsRoutes($openApi);
|
|
$this->addModelTypeConversionRoutes($openApi);
|
|
|
|
return $this->addTagDescriptions($openApi);
|
|
}
|
|
|
|
private function addHealthCheck(OpenApi $openApi): void
|
|
{
|
|
$openApi->getPaths()->addPath('/api/health', new Model\PathItem(
|
|
get: new Model\Operation(
|
|
operationId: 'getHealthCheck',
|
|
tags: ['Monitoring'],
|
|
summary: 'Vérification de santé du système',
|
|
description: 'Retourne le statut du système, la version, la latence BDD et la mémoire utilisée.',
|
|
responses: [
|
|
'200' => $this->jsonResponse('Système opérationnel.'),
|
|
'503' => $this->jsonResponse('Système dégradé.'),
|
|
],
|
|
),
|
|
));
|
|
}
|
|
|
|
private function addSessionRoutes(OpenApi $openApi): void
|
|
{
|
|
$openApi->getPaths()->addPath('/api/session/profiles', new Model\PathItem(
|
|
get: new Model\Operation(
|
|
operationId: 'getSessionProfiles',
|
|
tags: ['Session'],
|
|
summary: 'Lister les profils disponibles',
|
|
description: 'Retourne les profils actifs (id, prénom, nom, hasPassword). Aucune authentification requise.',
|
|
responses: ['200' => $this->jsonResponse('Liste des profils.')],
|
|
),
|
|
));
|
|
|
|
$openApi->getPaths()->addPath('/api/session/profile', new Model\PathItem(
|
|
get: new Model\Operation(
|
|
operationId: 'getSessionProfile',
|
|
tags: ['Session'],
|
|
summary: 'Profil actif de la session',
|
|
description: 'Retourne le profil actuellement connecté via la session.',
|
|
responses: [
|
|
'200' => $this->jsonResponse('Profil actif.'),
|
|
'401' => $this->jsonResponse('Aucun profil actif.'),
|
|
],
|
|
),
|
|
post: new Model\Operation(
|
|
operationId: 'loginSessionProfile',
|
|
tags: ['Session'],
|
|
summary: 'Connexion — activer un profil',
|
|
description: 'Active un profil dans la session. Requiert profileId et password.',
|
|
requestBody: $this->jsonRequestBody('Identifiants de connexion.'),
|
|
responses: [
|
|
'200' => $this->jsonResponse('Connexion réussie.'),
|
|
'401' => $this->jsonResponse('Mot de passe incorrect.'),
|
|
'404' => $this->jsonResponse('Profil introuvable.'),
|
|
],
|
|
),
|
|
delete: new Model\Operation(
|
|
operationId: 'logoutSessionProfile',
|
|
tags: ['Session'],
|
|
summary: 'Déconnexion — invalider la session',
|
|
responses: ['200' => $this->jsonResponse('Session invalidée.')],
|
|
),
|
|
));
|
|
}
|
|
|
|
private function addAdminProfileRoutes(OpenApi $openApi): void
|
|
{
|
|
$openApi->getPaths()->addPath('/api/admin/profiles', new Model\PathItem(
|
|
get: new Model\Operation(
|
|
operationId: 'adminListProfiles',
|
|
tags: ['Admin — Profils'],
|
|
summary: 'Lister tous les profils',
|
|
description: 'Liste complète des profils triés par prénom. Requiert ROLE_ADMIN.',
|
|
responses: ['200' => $this->jsonResponse('Liste des profils.')],
|
|
),
|
|
post: new Model\Operation(
|
|
operationId: 'adminCreateProfile',
|
|
tags: ['Admin — Profils'],
|
|
summary: 'Créer un profil',
|
|
description: 'Crée un nouveau profil avec rôle. Requiert ROLE_ADMIN.',
|
|
requestBody: $this->jsonRequestBody('Données du profil (firstName, lastName, email, password, role).'),
|
|
responses: [
|
|
'201' => $this->jsonResponse('Profil créé.'),
|
|
'400' => $this->jsonResponse('Données invalides.'),
|
|
'409' => $this->jsonResponse('Email déjà utilisé.'),
|
|
],
|
|
),
|
|
));
|
|
|
|
$idParam = $this->pathParam('id', 'Identifiant du profil');
|
|
|
|
$openApi->getPaths()->addPath('/api/admin/profiles/{id}/role', new Model\PathItem(
|
|
put: new Model\Operation(
|
|
operationId: 'adminUpdateProfileRole',
|
|
tags: ['Admin — Profils'],
|
|
summary: 'Modifier le rôle d\'un profil',
|
|
description: 'Change le rôle d\'un profil. Empêche la suppression du dernier admin. Requiert ROLE_ADMIN.',
|
|
parameters: [$idParam],
|
|
requestBody: $this->jsonRequestBody('Nouveau rôle.'),
|
|
responses: [
|
|
'200' => $this->jsonResponse('Rôle mis à jour.'),
|
|
'400' => $this->jsonResponse('Rôle invalide ou dernier admin.'),
|
|
],
|
|
),
|
|
));
|
|
|
|
$openApi->getPaths()->addPath('/api/admin/profiles/{id}/password', new Model\PathItem(
|
|
put: new Model\Operation(
|
|
operationId: 'adminUpdateProfilePassword',
|
|
tags: ['Admin — Profils'],
|
|
summary: 'Modifier le mot de passe d\'un profil',
|
|
description: 'Requiert ROLE_ADMIN.',
|
|
parameters: [$idParam],
|
|
requestBody: $this->jsonRequestBody('Nouveau mot de passe.'),
|
|
responses: ['200' => $this->jsonResponse('Mot de passe mis à jour.')],
|
|
),
|
|
));
|
|
|
|
$openApi->getPaths()->addPath('/api/admin/profiles/{id}/deactivate', new Model\PathItem(
|
|
put: new Model\Operation(
|
|
operationId: 'adminDeactivateProfile',
|
|
tags: ['Admin — Profils'],
|
|
summary: 'Désactiver un profil',
|
|
description: 'Désactive un profil. Empêche la désactivation du dernier admin. Requiert ROLE_ADMIN.',
|
|
parameters: [$idParam],
|
|
responses: [
|
|
'200' => $this->jsonResponse('Profil désactivé.'),
|
|
'400' => $this->jsonResponse('Dernier admin, impossible de désactiver.'),
|
|
],
|
|
),
|
|
));
|
|
}
|
|
|
|
private function addActivityLogs(OpenApi $openApi): void
|
|
{
|
|
$openApi->getPaths()->addPath('/api/activity-logs', new Model\PathItem(
|
|
get: new Model\Operation(
|
|
operationId: 'getActivityLogs',
|
|
tags: ['Audit'],
|
|
summary: 'Journal d\'activité paginé',
|
|
description: 'Retourne les logs d\'audit avec filtrage optionnel par type d\'entité et action. Requiert ROLE_VIEWER.',
|
|
parameters: [
|
|
$this->queryParam('page', 'Numéro de page'),
|
|
$this->queryParam('itemsPerPage', 'Éléments par page'),
|
|
$this->queryParam('entityType', 'Filtrer par type d\'entité'),
|
|
$this->queryParam('action', 'Filtrer par action'),
|
|
],
|
|
responses: ['200' => $this->jsonResponse('Logs paginés.')],
|
|
),
|
|
));
|
|
}
|
|
|
|
private function addCommentRoutes(OpenApi $openApi): void
|
|
{
|
|
$openApi->getPaths()->addPath('/api/comments', new Model\PathItem(
|
|
post: new Model\Operation(
|
|
operationId: 'createComment',
|
|
tags: ['Commentaires'],
|
|
summary: 'Créer un commentaire',
|
|
description: 'Ajoute un commentaire à une entité (machine, pièce, composant, produit, catégorie, squelette). Requiert ROLE_VIEWER.',
|
|
requestBody: $this->jsonRequestBody('Données du commentaire (content, entityType, entityId, entityName).'),
|
|
responses: [
|
|
'201' => $this->jsonResponse('Commentaire créé.'),
|
|
'400' => $this->jsonResponse('Données invalides.'),
|
|
],
|
|
),
|
|
));
|
|
|
|
$openApi->getPaths()->addPath('/api/comments/{id}/resolve', new Model\PathItem(
|
|
patch: new Model\Operation(
|
|
operationId: 'resolveComment',
|
|
tags: ['Commentaires'],
|
|
summary: 'Résoudre un commentaire',
|
|
description: 'Marque un commentaire comme résolu. Requiert ROLE_GESTIONNAIRE.',
|
|
parameters: [$this->pathParam('id', 'Identifiant du commentaire')],
|
|
responses: [
|
|
'200' => $this->jsonResponse('Commentaire résolu.'),
|
|
'404' => $this->jsonResponse('Commentaire introuvable.'),
|
|
],
|
|
),
|
|
));
|
|
|
|
$openApi->getPaths()->addPath('/api/comments/stats/unresolved-count', new Model\PathItem(
|
|
get: new Model\Operation(
|
|
operationId: 'getUnresolvedCommentCount',
|
|
tags: ['Commentaires'],
|
|
summary: 'Nombre de commentaires non résolus',
|
|
description: 'Requiert ROLE_VIEWER.',
|
|
responses: ['200' => $this->jsonResponse('Compteur.')],
|
|
),
|
|
));
|
|
}
|
|
|
|
private function addCustomFieldValueRoutes(OpenApi $openApi): void
|
|
{
|
|
$openApi->getPaths()->addPath('/api/custom-fields/values', new Model\PathItem(
|
|
post: new Model\Operation(
|
|
operationId: 'createCustomFieldValue',
|
|
tags: ['Champs personnalisés'],
|
|
summary: 'Créer une valeur de champ personnalisé',
|
|
description: 'Crée une valeur pour un champ personnalisé sur une entité. Auto-crée le champ si nécessaire. Requiert ROLE_GESTIONNAIRE.',
|
|
requestBody: $this->jsonRequestBody('Données (customFieldId/customFieldName, value, entité cible).'),
|
|
responses: [
|
|
'201' => $this->jsonResponse('Valeur créée.'),
|
|
'400' => $this->jsonResponse('Données invalides.'),
|
|
],
|
|
),
|
|
));
|
|
|
|
$openApi->getPaths()->addPath('/api/custom-fields/values/upsert', new Model\PathItem(
|
|
post: new Model\Operation(
|
|
operationId: 'upsertCustomFieldValue',
|
|
tags: ['Champs personnalisés'],
|
|
summary: 'Créer ou mettre à jour une valeur de champ personnalisé',
|
|
description: 'Requiert ROLE_GESTIONNAIRE.',
|
|
requestBody: $this->jsonRequestBody('Données du champ.'),
|
|
responses: ['200' => $this->jsonResponse('Valeur créée ou mise à jour.')],
|
|
),
|
|
));
|
|
|
|
$openApi->getPaths()->addPath('/api/custom-fields/values/{entityType}/{entityId}', new Model\PathItem(
|
|
get: new Model\Operation(
|
|
operationId: 'listCustomFieldValues',
|
|
tags: ['Champs personnalisés'],
|
|
summary: 'Lister les valeurs de champs personnalisés d\'une entité',
|
|
description: 'Requiert ROLE_VIEWER.',
|
|
parameters: [
|
|
$this->pathParam('entityType', 'Type d\'entité (machine, composant, piece, product)'),
|
|
$this->pathParam('entityId', 'Identifiant de l\'entité'),
|
|
],
|
|
responses: ['200' => $this->jsonResponse('Liste des valeurs.')],
|
|
),
|
|
));
|
|
|
|
$idParam = $this->pathParam('id', 'Identifiant de la valeur');
|
|
|
|
$openApi->getPaths()->addPath('/api/custom-fields/values/{id}', new Model\PathItem(
|
|
patch: new Model\Operation(
|
|
operationId: 'updateCustomFieldValue',
|
|
tags: ['Champs personnalisés'],
|
|
summary: 'Modifier une valeur de champ personnalisé',
|
|
description: 'Requiert ROLE_GESTIONNAIRE.',
|
|
parameters: [$idParam],
|
|
requestBody: $this->jsonRequestBody('Nouvelle valeur.'),
|
|
responses: ['200' => $this->jsonResponse('Valeur mise à jour.')],
|
|
),
|
|
delete: new Model\Operation(
|
|
operationId: 'deleteCustomFieldValue',
|
|
tags: ['Champs personnalisés'],
|
|
summary: 'Supprimer une valeur de champ personnalisé',
|
|
description: 'Requiert ROLE_GESTIONNAIRE.',
|
|
parameters: [$idParam],
|
|
responses: ['204' => new Model\Response(description: 'Valeur supprimée.')],
|
|
),
|
|
));
|
|
}
|
|
|
|
private function addDocumentQueryRoutes(OpenApi $openApi): void
|
|
{
|
|
$entities = [
|
|
'site' => 'un site',
|
|
'machine' => 'une machine',
|
|
'composant' => 'un composant',
|
|
'piece' => 'une pièce',
|
|
'product' => 'un produit',
|
|
];
|
|
|
|
foreach ($entities as $entity => $label) {
|
|
$openApi->getPaths()->addPath("/api/documents/{$entity}/{id}", new Model\PathItem(
|
|
get: new Model\Operation(
|
|
operationId: 'getDocumentsBy'.ucfirst($entity),
|
|
tags: ['Documents'],
|
|
summary: "Documents rattachés à {$label}",
|
|
description: 'Requiert ROLE_VIEWER.',
|
|
parameters: [$this->pathParam('id', "Identifiant de l'entité")],
|
|
responses: ['200' => $this->jsonResponse('Liste des documents.')],
|
|
),
|
|
));
|
|
}
|
|
}
|
|
|
|
private function addDocumentServeRoutes(OpenApi $openApi): void
|
|
{
|
|
$idParam = $this->pathParam('id', 'Identifiant du document');
|
|
|
|
$openApi->getPaths()->addPath('/api/documents/{id}/file', new Model\PathItem(
|
|
get: new Model\Operation(
|
|
operationId: 'serveDocumentFile',
|
|
tags: ['Documents'],
|
|
summary: 'Afficher un fichier document (inline)',
|
|
description: 'Sert le fichier pour affichage dans le navigateur. Requiert ROLE_VIEWER.',
|
|
parameters: [$idParam],
|
|
responses: ['200' => new Model\Response(description: 'Contenu du fichier.')],
|
|
),
|
|
));
|
|
|
|
$openApi->getPaths()->addPath('/api/documents/{id}/download', new Model\PathItem(
|
|
get: new Model\Operation(
|
|
operationId: 'downloadDocumentFile',
|
|
tags: ['Documents'],
|
|
summary: 'Télécharger un fichier document',
|
|
description: 'Sert le fichier en téléchargement (attachment). Requiert ROLE_VIEWER.',
|
|
parameters: [$idParam],
|
|
responses: ['200' => new Model\Response(description: 'Fichier en téléchargement.')],
|
|
),
|
|
));
|
|
}
|
|
|
|
private function addEntityHistoryRoutes(OpenApi $openApi): void
|
|
{
|
|
$entities = [
|
|
'machines' => 'une machine',
|
|
'pieces' => 'une pièce',
|
|
'composants' => 'un composant',
|
|
'products' => 'un produit',
|
|
];
|
|
|
|
foreach ($entities as $plural => $label) {
|
|
$openApi->getPaths()->addPath("/api/{$plural}/{id}/history", new Model\PathItem(
|
|
get: new Model\Operation(
|
|
operationId: 'get'.ucfirst(rtrim($plural, 's')).'History',
|
|
tags: ['Audit'],
|
|
summary: "Historique d'audit de {$label}",
|
|
description: "Retourne les 200 derniers événements d'audit. Requiert ROLE_VIEWER.",
|
|
parameters: [$this->pathParam('id', "Identifiant de l'entité")],
|
|
responses: ['200' => $this->jsonResponse('Historique paginé.')],
|
|
),
|
|
));
|
|
}
|
|
}
|
|
|
|
private function addMachineStructureRoutes(OpenApi $openApi): void
|
|
{
|
|
$idParam = $this->pathParam('id', 'Identifiant de la machine');
|
|
|
|
$openApi->getPaths()->addPath('/api/machines/{id}/structure', new Model\PathItem(
|
|
get: new Model\Operation(
|
|
operationId: 'getMachineStructure',
|
|
tags: ['Machines — Structure'],
|
|
summary: 'Structure complète d\'une machine',
|
|
description: 'Retourne les composants, pièces et produits avec hiérarchie. Requiert ROLE_VIEWER.',
|
|
parameters: [$idParam],
|
|
responses: ['200' => $this->jsonResponse('Structure de la machine.')],
|
|
),
|
|
patch: new Model\Operation(
|
|
operationId: 'updateMachineStructure',
|
|
tags: ['Machines — Structure'],
|
|
summary: 'Modifier la structure d\'une machine',
|
|
description: 'Crée, met à jour ou supprime les liens composants/pièces/produits. Requiert ROLE_GESTIONNAIRE.',
|
|
parameters: [$idParam],
|
|
requestBody: $this->jsonRequestBody('Liens à créer/modifier/supprimer.'),
|
|
responses: ['200' => $this->jsonResponse('Structure mise à jour.')],
|
|
),
|
|
));
|
|
|
|
$openApi->getPaths()->addPath('/api/machines/{id}/clone', new Model\PathItem(
|
|
post: new Model\Operation(
|
|
operationId: 'cloneMachine',
|
|
tags: ['Machines — Structure'],
|
|
summary: 'Cloner une machine',
|
|
description: 'Clone une machine avec tous ses composants, pièces, produits, champs personnalisés et constructeurs. Requiert ROLE_GESTIONNAIRE.',
|
|
parameters: [$idParam],
|
|
requestBody: $this->jsonRequestBody('Données de la copie (name, siteId, reference).'),
|
|
responses: [
|
|
'201' => $this->jsonResponse('Machine clonée.'),
|
|
'404' => $this->jsonResponse('Machine source introuvable.'),
|
|
],
|
|
),
|
|
));
|
|
}
|
|
|
|
private function addMachineCustomFieldsRoutes(OpenApi $openApi): void
|
|
{
|
|
$openApi->getPaths()->addPath('/api/machines/{id}/add-custom-fields', new Model\PathItem(
|
|
post: new Model\Operation(
|
|
operationId: 'addMachineCustomFields',
|
|
tags: ['Machines — Structure'],
|
|
summary: 'Initialiser les champs personnalisés manquants',
|
|
description: 'Crée les entrées de valeur manquantes pour les champs personnalisés définis. Requiert ROLE_GESTIONNAIRE.',
|
|
parameters: [$this->pathParam('id', 'Identifiant de la machine')],
|
|
responses: ['200' => $this->jsonResponse('Champs ajoutés.')],
|
|
),
|
|
));
|
|
}
|
|
|
|
private function addModelTypeConversionRoutes(OpenApi $openApi): void
|
|
{
|
|
$idParam = $this->pathParam('id', 'Identifiant du ModelType');
|
|
|
|
$openApi->getPaths()->addPath('/api/model_types/{id}/conversion-check', new Model\PathItem(
|
|
get: new Model\Operation(
|
|
operationId: 'checkModelTypeConversion',
|
|
tags: ['ModelType'],
|
|
summary: 'Vérifier la convertibilité d\'un ModelType',
|
|
description: 'Vérifie si la catégorie du ModelType peut être convertie. Requiert ROLE_VIEWER.',
|
|
parameters: [$idParam],
|
|
responses: ['200' => $this->jsonResponse('Résultat de la vérification.')],
|
|
),
|
|
));
|
|
|
|
$openApi->getPaths()->addPath('/api/model_types/{id}/convert', new Model\PathItem(
|
|
post: new Model\Operation(
|
|
operationId: 'convertModelType',
|
|
tags: ['ModelType'],
|
|
summary: 'Convertir la catégorie d\'un ModelType',
|
|
description: 'Convertit la catégorie. Retourne 409 en cas de conflit. Requiert ROLE_GESTIONNAIRE.',
|
|
parameters: [$idParam],
|
|
responses: [
|
|
'200' => $this->jsonResponse('Conversion effectuée.'),
|
|
'409' => $this->jsonResponse('Conflit — conversion impossible.'),
|
|
],
|
|
),
|
|
));
|
|
}
|
|
|
|
private function addTagDescriptions(OpenApi $openApi): OpenApi
|
|
{
|
|
$customTags = [
|
|
'Monitoring' => 'Supervision et vérification de l\'état de santé du système (statut, version, latence BDD, mémoire).',
|
|
'Session' => 'Authentification par session. Permet de lister les profils disponibles, se connecter (activer un profil) et se déconnecter.',
|
|
'Admin — Profils' => 'Administration des profils utilisateurs. Création, modification des rôles et mots de passe, désactivation. Réservé aux administrateurs.',
|
|
'Audit' => 'Journal d\'activité et historique d\'audit. Consultation des modifications apportées aux entités avec détail des changements (diff et snapshot).',
|
|
'Commentaires' => 'Système de commentaires et annotations sur les entités. Permet de créer des commentaires, les résoudre et suivre le nombre de commentaires ouverts.',
|
|
'Champs personnalisés' => 'Gestion des valeurs de champs personnalisés. Permet d\'ajouter, modifier et supprimer des données dynamiques sur les machines, pièces, composants et produits.',
|
|
'Documents' => 'Gestion des fichiers joints. Consultation des documents par entité, affichage inline et téléchargement.',
|
|
'Machines — Structure' => 'Structure hiérarchique des machines. Consultation et modification des liaisons composants/pièces/produits, clonage de machines et initialisation des champs personnalisés.',
|
|
'ModelType' => 'Conversion de catégories de types. Vérification de compatibilité et conversion effective des catégories de ModelType.',
|
|
];
|
|
|
|
$existingTags = $openApi->getTags();
|
|
$existingNames = array_map(static fn (Model\Tag $tag) => $tag->getName(), $existingTags);
|
|
|
|
foreach ($customTags as $name => $description) {
|
|
if (!in_array($name, $existingNames, true)) {
|
|
$existingTags[] = new Model\Tag(name: $name, description: $description);
|
|
}
|
|
}
|
|
|
|
return $openApi->withTags($existingTags);
|
|
}
|
|
|
|
private function jsonResponse(string $description): Model\Response
|
|
{
|
|
return new Model\Response(
|
|
description: $description,
|
|
content: new ArrayObject([
|
|
'application/json' => new Model\MediaType(
|
|
schema: new ArrayObject(['type' => 'object']),
|
|
),
|
|
]),
|
|
);
|
|
}
|
|
|
|
private function jsonRequestBody(string $description): Model\RequestBody
|
|
{
|
|
return new Model\RequestBody(
|
|
description: $description,
|
|
content: new ArrayObject([
|
|
'application/json' => new Model\MediaType(
|
|
schema: new ArrayObject(['type' => 'object']),
|
|
),
|
|
]),
|
|
required: true,
|
|
);
|
|
}
|
|
|
|
private function pathParam(string $name, string $description): Model\Parameter
|
|
{
|
|
return new Model\Parameter(
|
|
name: $name,
|
|
in: 'path',
|
|
description: $description,
|
|
required: true,
|
|
schema: ['type' => 'string'],
|
|
);
|
|
}
|
|
|
|
private function queryParam(string $name, string $description): Model\Parameter
|
|
{
|
|
return new Model\Parameter(
|
|
name: $name,
|
|
in: 'query',
|
|
description: $description,
|
|
required: false,
|
|
schema: ['type' => 'string'],
|
|
);
|
|
}
|
|
}
|