From 6d531aa5591d484aa5d92882aaf4d05461ecdf52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Munsch?= Date: Wed, 16 Sep 2026 12:45:51 +0200 Subject: [PATCH] docs: update README with comprehensive quickstart, services, pagination and error handling guides --- README.md | 343 +++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 313 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index 1970700..854476f 100644 --- a/README.md +++ b/README.md @@ -1,42 +1,325 @@ -# Arcadia Api PHP Client +# Arcadia API PHP Client -## Mappers +[![PHP Version](https://img.shields.io/badge/php-^8.5-blue.svg)](https://www.php.net/) +[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE) +[![Tests: Pest](https://img.shields.io/badge/tests-Pest%20PHP-purple.svg)](https://pestphp.com/) +[![PHPStan: Level 10](https://img.shields.io/badge/phpstan-level%2010-brightgreen.svg)](https://phpstan.org/) -### Employee +Client PHP haut niveau, fortement typé et orienté domaine pour l'API Arcadia. +Il s'appuie sur le client OpenAPI généré par Kiota (`uwil/arcadia-kiota-open-api-client`) et fournit : +- Une couche de services métier (`EmployeeService`, `IncoService`, `BrandService`, `ApiService`). +- Des Value Objects stricts (`ArcadiaEmployeeId`, `IncoId`, `IncoCompoId`, `BrandCode`, `SiteId`, etc.). +- Des DTOs riches (`Employee`, `Inco`, `IncoCompo`, `IncoShort`, `Brand`) avec typage complet et conformité `JsonSerializable`. +- Une gestion unifiée de la pagination (`PageOf`, `Pagination`) et du parcours continu par flux (`StreamOf`). +- L'authentification automatique avec signature HMAC (`HMAC-SHA256`) et gestion des nonces. +- Le support de la journalisation (PSR-3 `LoggerInterface`) et de l'observabilité (OpenTelemetry). +- Une gestion explicite des erreurs via la hiérarchie `ApiError`. -#### Props +--- -```php -public string $matricule; -public string $lastname; -public string $firstname; -public string $start_date; -public string|null $end_date; -public string|null $planned_end_date; -public string $establishment; -public string $site; -public string $service; +## Sommaire + +- [Prérequis](#prérequis) +- [Installation](#installation) +- [Initialisation](#initialisation) +- [Services et Utilisation](#services-et-utilisation) + - [Vérification de l'API (`apiService`)](#vérification-de-lapi-apiservice) + - [Gestion des Collaborateurs (`employees`)](#gestion-des-collaborateurs-employees) + - [Fiches INCO & Compositions (`incoSheets`)](#fiches-inco--compositions-incosheets) + - [Gestion des Marques (`brands`)](#gestion-des-marques-brands) +- [Pagination et Streaming](#pagination-et-streaming) +- [Gestion des Erreurs](#gestion-des-erreurs) +- [Tests et Qualité](#tests-et-qualité) +- [Licence](#licence) + +--- + +## Prérequis + +- **PHP 8.5+** +- Extensions PHP : `ext-curl`, `ext-mbstring` +- Accès au registre GitLab Composer de LDC Digital + +--- + +## Installation + +Comme cette bibliothèque et sa sous-dépendance Kiota sont hébergées sur le registre privé GitLab de LDC, configurez le dépôt Composer dans le `composer.json` de votre projet : + +```json +{ + "repositories": [ + { + "type": "composer", + "url": "https://lab.s-web.fr/api/v4/group/23/-/packages/composer/packages.json" + } + ] +} ``` -#### Usage +Puis installez le client via Composer : -```php - +```bash +composer require uwil/arcadia-api-php-client ``` -### INCO +Assurez-vous d'avoir configuré vos identifiants d'accès GitLab pour Composer (via `auth.json` ou variable d'environnement CI) : -TODO +```bash +composer config gitlab-token.lab.s-web.fr +``` -## TESTS +--- -[coverage](./coverage/index.html) +## Initialisation + +L'entrée principale de la bibliothèque est [`ArcadiaApiClient`](src/ArcadiaApiClient.php), configurée avec [`ApiConfig`](src/Lib/ApiConfig.php) : + +```php +use GuzzleHttp\Psr7\Utils; +use Uwil\ArcadiaApiPhpClient\ArcadiaApiClient; +use Uwil\ArcadiaApiPhpClient\Auth\ClientIdentifier; +use Uwil\ArcadiaApiPhpClient\Auth\ClientSecret; +use Uwil\ArcadiaApiPhpClient\Lib\ApiConfig; + +$config = new ApiConfig( + endpoint: Utils::uriFor('https://arcadia-api.domain.tld/api'), + apiKey: new ClientIdentifier('VOTRE_API_KEY'), + secret: new ClientSecret('VOTRE_SECRET_KEY'), + curlTimeOut: 20.0, // secondes (optionnel, défaut: 20.0) + maxRetries: 3, // tentatives en cas d'erreur réseau (optionnel, défaut: 0) + retryDelaySeconds: 1, // délai entre les retries (optionnel, défaut: 1) + ignoreSelfSignedCert: false, // pour environnements locaux de test (défaut: false) + logger: null, // ?Psr\Log\LoggerInterface + tracer: null, // ?OpenTelemetry\API\Trace\TracerInterface +); + +$client = new ArcadiaApiClient($config); +``` + +--- + +## Services et Utilisation + +### Vérification de l'API (`apiService`) + +Permet de vérifier la disponibilité et la santé de l'API Arcadia : + +```php +$isAlive = $client->apiService()->isAlive(); + +if ($isAlive) { + echo "L'API Arcadia est joignable et opérationnelle.\n"; +} +``` + +--- + +### Gestion des Collaborateurs (`employees`) + +Le service [`EmployeeService`](src/Service/EmployeeService.php) permet de consulter et rechercher les salariés. + +#### Récupérer par identifiant +```php +use Uwil\ArcadiaApiPhpClient\Lib\ApiValueObject\ArcadiaEmployeeId; +use Uwil\ArcadiaApiPhpClient\Error\ApiError; + +$employee = $client->employees()->findBy(new ArcadiaEmployeeId('0115590')); + +if ($employee instanceof ApiError) { + echo "Erreur API: " . $employee->getMessage(); +} elseif ($employee === null) { + echo "Collaborateur non trouvé (404).\n"; +} else { + echo "Salarié: {$employee->firstname} {$employee->lastname} (Site: {$employee->site})\n"; +} +``` + +#### Récupérer par site(s) +```php +use Uwil\ArcadiaApiPhpClient\Lib\ApiValueObject\SiteId; +use Uwil\ArcadiaApiPhpClient\Lib\ApiSorter\EmployeeSorter; +use Uwil\ArcadiaApiPhpClient\Lib\ApiSorter\EmployeeSortColumn; + +// Pour un site unique avec tri par matricule +$page = $client->employees()->getAllOfSites( + new SiteId('01'), + new EmployeeSorter(EmployeeSortColumn::MATRICULE, 'asc') +); + +// Pour plusieurs sites modifiés depuis une date +$page = $client->employees()->getAllOfSitesSince( + [new SiteId('01'), new SiteId('02')], + new DateTimeImmutable('2026-01-01') +); +``` + +#### Streaming de tous les collaborateurs +Le streaming parcourt automatiquement toutes les pages de façon transparente grâce à un générateur PHP : + +```php +foreach ($client->employees()->streamAll() as $employee) { + echo "{$employee->matricule} - {$employee->lastname}\n"; +} +``` + +--- + +### Fiches INCO & Compositions (`incoSheets`) + +Le service [`IncoService`](src/Service/IncoService.php) gère les fiches techniques produit (INCO) et leurs compositions nutritionnelles détaillées. + +#### Consulter une fiche INCO +```php +use Uwil\ArcadiaApiPhpClient\Lib\ApiValueObject\IncoId; + +$inco = $client->incoSheets()->findBy(new IncoId('12345')); + +if ($inco !== null && !($inco instanceof ApiError)) { + echo "Fiche: {$inco->commercialLabel}\n"; + echo "Nutri-Score: {$inco->nutriscore?->grade}\n"; +} +``` + +#### Consulter la composition nutritionnelle +Vous pouvez interroger la composition par son `IncoCompoId` ou directement par l'`IncoId` : + +```php +use Uwil\ArcadiaApiPhpClient\Lib\ApiValueObject\IncoCompoId; + +// Directement via IncoCompoId +$compo = $client->incoSheets()->findCompo(new IncoCompoId('98765')); + +// Ou via IncoId +$compo = $client->incoSheets()->findCompo(new IncoId('12345')); + +if ($compo !== null && !($compo instanceof ApiError)) { + echo "Énergie (kJ): {$compo->energy?->kJ?->value}\n"; + echo "Acides gras saturés: {$compo->fattyAcids?->saturatedFattyAcids?->value}\n"; + echo "Points Nutri-Score: {$compo->nutriScorePoints?->totalPoints}\n"; +} +``` + +#### Recherche et listage par marque +```php +use Uwil\ArcadiaApiPhpClient\Lib\ApiValueObject\BrandCode; + +// Liste courte (IncoShort) pour optimiser les performances d'affichage +$shortsPage = $client->incoSheets()->getAllShortOfBrands(new BrandCode('GAULOIS')); + +// Recherche par motif d'identifiant +$results = $client->incoSheets()->findByIncoIdLike('POULET%', new BrandCode('GAULOIS')); + +// Streaming de toutes les fiches d'une marque +foreach ($client->incoSheets()->streamAllOfBrands(new BrandCode('LEGAULOIS')) as $sheet) { + echo "Fiche #{$sheet->id->value}: {$sheet->commercialLabel}\n"; +} +``` + +--- + +### Gestion des Marques (`brands`) + +Le service [`BrandService`](src/Service/BrandService.php) donne accès aux marques référencées : + +```php +use Uwil\ArcadiaApiPhpClient\Lib\ApiValueObject\BrandCode; + +// Récupérer une marque +$brand = $client->brands()->findBy(new BrandCode('GAULOIS')); + +// Parcourir toutes les marques +foreach ($client->brands()->streamAll() as $brand) { + echo "Marque [{$brand->code->value}] : {$brand->label}\n"; +} +``` + +--- + +## Pagination et Streaming + +### Objet `PageOf` + +Toutes les méthodes de liste paginées retournent un objet [`PageOf`](src/Lib/ApiDto/Dto/PageOf.php) contenant les éléments et les métadonnées de pagination : + +```php +$page = $client->employees()->getAll(); + +// Éléments de la page courante +$employees = $page->items; + +// Métadonnées +echo "Page actuelle : " . $page->pagination->pageNumber->value . "\n"; +echo "Taille de page : " . $page->pagination->pageSize->value . "\n"; +echo "Total éléments : " . $page->pagination->totalItems . "\n"; +echo "Total pages : " . $page->pagination->totalPages . "\n"; +``` + +### Objet `StreamOf` + +Toutes les méthodes `stream*()` retournent un [`StreamOf`](src/Lib/Pagination/StreamOf.php) qui implémente `\IteratorAggregate`. Il effectue les requêtes de page suivante automatiquement à la demande : + +```php +$stream = $client->employees()->streamAll(); + +foreach ($stream as $employee) { + // Les pages suivantes sont chargées en arrière-plan au fur et à mesure de l'itération +} +``` + +--- + +## Gestion des Erreurs + +Les méthodes du client ne lancent pas d'exceptions inattendues pour les erreurs HTTP ou de communication ; elles renvoient une instance de [`ApiError`](src/Error/ApiError.php) (ou ses sous-classes spécialisées) : + +```php +use Uwil\ArcadiaApiPhpClient\Error\AuthenticationError; +use Uwil\ArcadiaApiPhpClient\Error\ValidationError; +use Uwil\ArcadiaApiPhpClient\Error\NetworkError; +use Uwil\ArcadiaApiPhpClient\Error\RateLimitError; +use Uwil\ArcadiaApiPhpClient\Error\ServerError; +use Uwil\ArcadiaApiPhpClient\Error\ApiError; + +$result = $client->employees()->findBy(new ArcadiaEmployeeId('0115590')); + +match (true) { + $result instanceof AuthenticationError => logAuthFailure($result), + $result instanceof ValidationError => logValidationDetails($result->getViolations()), + $result instanceof NetworkError => retryLater($result), + $result instanceof RateLimitError => handleRateLimit($result), + $result instanceof ServerError => notifyAdmin($result), + $result instanceof ApiError => logGenericError($result), + $result === null => handleNotFound(), + default => handleEmployee($result), +}; +``` + +--- + +## Tests et Qualité + +Le projet est rigoureusement testé avec une suite complète de tests unitaires et d'architecture. + +```bash +# Lancer les tests unitaires +composer test + +# Vérifier la couverture de code (seuil minimal 85% configuré) +composer test:coverage + +# Analyse statique PHPStan (niveau 10) +composer lint:phpstan + +# Vérifier le style du code (Laravel Pint) +composer lint:pint + +# Formater le code automatiquement +composer format +``` + +--- + +## Licence + +Ce projet est sous licence MIT. Consultez le fichier [LICENSE](LICENSE) pour plus d'informations.