No description
Find a file
Jérémy 70d38cccd1 Merge branch 'arcadia-api-integration' into 'main'
Arcadia Api Integration

Closes #1

See merge request ldc-web/projets/arcadia/arcadia-php-client!2
2026-09-18 16:52:11 +02:00
.intellipest refactor: First draw with minimal tests Closes #1 2026-04-16 09:48:49 +02:00
.vscode chore(config): update dependencies, CI, sonar and development configuration 2026-09-14 19:25:58 +02:00
documentation refactor: First draw with minimal tests Closes #1 2026-04-16 09:48:49 +02:00
src fix(inco): align IncoMapper and IncoApiRepository with simplified IncoListResponse 2026-09-18 12:11:51 +02:00
tests fix(inco): align IncoMapper and IncoApiRepository with simplified IncoListResponse 2026-09-18 12:11:51 +02:00
.editorconfig refactor: First draw with minimal tests Closes #1 2026-04-16 09:48:49 +02:00
.gitattributes chore(meta): add MIT LICENSE and .gitattributes export-ignore rules 2026-09-16 12:45:23 +02:00
.gitignore build(composer): require published kiota package, move Tests to autoload-dev and untrack composer.lock 2026-09-16 12:45:09 +02:00
.gitlab-ci.yml ci: fixes on pipeline 2026-09-16 17:08:25 +02:00
composer.json chore: change php minimal dependency to 8.3 2026-09-17 12:54:45 +02:00
LICENSE chore(meta): add MIT LICENSE and .gitattributes export-ignore rules 2026-09-16 12:45:23 +02:00
phpcs.xml refactor: First draw with minimal tests Closes #1 2026-04-16 09:48:49 +02:00
phpstan.neon.dist chore: update PHPStan level to 10 for stricter analysis 2026-09-02 15:31:58 +02:00
phpunit.xml refactor: First draw with minimal tests Closes #1 2026-04-16 09:48:49 +02:00
pint.json refactor: First draw with minimal tests Closes #1 2026-04-16 09:48:49 +02:00
README.md docs: update README with comprehensive quickstart, services, pagination and error handling guides 2026-09-16 12:45:51 +02:00
sonar-project.properties chore(config): update dependencies, CI, sonar and development configuration 2026-09-14 19:25:58 +02:00

Arcadia API PHP Client

PHP Version License: MIT Tests: Pest PHPStan: Level 10

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.

Sommaire


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 :

{
  "repositories": [
    {
      "type": "composer",
      "url": "https://lab.s-web.fr/api/v4/group/23/-/packages/composer/packages.json"
    }
  ]
}

Puis installez le client via Composer :

composer require uwil/arcadia-api-php-client

Assurez-vous d'avoir configuré vos identifiants d'accès GitLab pour Composer (via auth.json ou variable d'environnement CI) :

composer config gitlab-token.lab.s-web.fr <deploy-token-user> <deploy-token-password>

Initialisation

L'entrée principale de la bibliothèque est ArcadiaApiClient, configurée avec ApiConfig :

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 :

$isAlive = $client->apiService()->isAlive();

if ($isAlive) {
    echo "L'API Arcadia est joignable et opérationnelle.\n";
}

Gestion des Collaborateurs (employees)

Le service EmployeeService permet de consulter et rechercher les salariés.

Récupérer par identifiant

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)

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 :

foreach ($client->employees()->streamAll() as $employee) {
    echo "{$employee->matricule} - {$employee->lastname}\n";
}

Fiches INCO & Compositions (incoSheets)

Le service IncoService gère les fiches techniques produit (INCO) et leurs compositions nutritionnelles détaillées.

Consulter une fiche INCO

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 :

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

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 donne accès aux marques référencées :

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<T> contenant les éléments et les métadonnées de pagination :

$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<T> qui implémente \IteratorAggregate. Il effectue les requêtes de page suivante automatiquement à la demande :

$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 (ou ses sous-classes spécialisées) :

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.

# 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 pour plus d'informations.