feat : ajout de l'auth et du guichet pour la récupération d'info bovin
This commit is contained in:
112
src/Api/BovinApi.php
Normal file
112
src/Api/BovinApi.php
Normal file
@@ -0,0 +1,112 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Malio\EdnotifBundle\Api;
|
||||
|
||||
use Malio\EdnotifBundle\Auth\TokenProvider;
|
||||
use Malio\EdnotifBundle\Dto\DossierAnimalDto;
|
||||
use Malio\EdnotifBundle\Exception\EdnotifException;
|
||||
use SoapClient;
|
||||
use SoapFault;
|
||||
|
||||
final class BovinApi implements BovinApiInterface
|
||||
{
|
||||
public function __construct(
|
||||
private TokenProvider $tokenProvider,
|
||||
private SoapClient $metierClient,
|
||||
) {
|
||||
}
|
||||
|
||||
public function getDossierAnimal(string $exploitationNumero, string $numeroNational, string $codePays = 'FR'): DossierAnimalDto
|
||||
{
|
||||
$token = $this->tokenProvider->getToken();
|
||||
|
||||
$payload = [[
|
||||
'JetonAuthentification' => $token,
|
||||
'Exploitation' => [
|
||||
'CodePays' => $codePays,
|
||||
'NumeroExploitation' => $exploitationNumero,
|
||||
],
|
||||
'Bovin' => [
|
||||
'CodePays' => $codePays,
|
||||
'NumeroNational' => $numeroNational,
|
||||
],
|
||||
]];
|
||||
|
||||
try {
|
||||
/** @var object $response */
|
||||
$response = $this->metierClient->__soapCall('IpBGetDossierAnimal', $payload);
|
||||
} catch (SoapFault $e) {
|
||||
// Si c’est un souci de jeton, tu peux invalider et retenter une fois (optionnel)
|
||||
throw new \RuntimeException('SOAP Fault lors de IpBGetDossierAnimal: ' . $e->getMessage(), 0, $e);
|
||||
}
|
||||
|
||||
$rs = $response->ReponseStandard ?? null;
|
||||
$ok = is_object($rs) && (($rs->Resultat ?? false) === true);
|
||||
|
||||
if (!$ok) {
|
||||
$anom = $rs->Anomalie ?? null;
|
||||
$code = (string)($anom->Code ?? 'UNKNOWN');
|
||||
$sev = (int)($anom->Severite ?? 1);
|
||||
$msg = (string)($anom->Message ?? 'Appel EDNOTIF refusé');
|
||||
throw new EdnotifException($code, $sev, $msg);
|
||||
}
|
||||
|
||||
$identite = [];
|
||||
$periodes = [];
|
||||
|
||||
$bovinNode = $response->ReponseSpecifique->Bovin ?? null;
|
||||
if (is_object($bovinNode)) {
|
||||
$identiteObj = $bovinNode->IdentiteBovin ?? null;
|
||||
if (is_object($identiteObj)) {
|
||||
$identite = $this->objectToArray($identiteObj);
|
||||
}
|
||||
|
||||
$pp = $bovinNode->PeriodesPresences->PeriodePresence ?? null;
|
||||
foreach ($this->normalizeList($pp) as $periode) {
|
||||
if (!is_object($periode)) {
|
||||
continue;
|
||||
}
|
||||
$entree = is_object($periode->Entree ?? null) ? $this->objectToArray($periode->Entree) : [];
|
||||
$sortie = is_object($periode->Sortie ?? null) ? $this->objectToArray($periode->Sortie) : null;
|
||||
|
||||
$row = ['entree' => $entree];
|
||||
if ($sortie !== null) {
|
||||
$row['sortie'] = $sortie;
|
||||
}
|
||||
$periodes[] = $row;
|
||||
}
|
||||
}
|
||||
|
||||
return new DossierAnimalDto(
|
||||
numeroNational: $numeroNational,
|
||||
identiteBovin: $identite,
|
||||
periodesPresence: $periodes,
|
||||
rawResponse: $response,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<mixed>
|
||||
*/
|
||||
private function normalizeList(mixed $value): array
|
||||
{
|
||||
if ($value === null) {
|
||||
return [];
|
||||
}
|
||||
if (is_array($value)) {
|
||||
return $value;
|
||||
}
|
||||
return [$value];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string,mixed>
|
||||
*/
|
||||
private function objectToArray(object $obj): array
|
||||
{
|
||||
// conversion simple (suffisante pour démarrer)
|
||||
return json_decode(json_encode($obj, JSON_THROW_ON_ERROR), true, 512, JSON_THROW_ON_ERROR);
|
||||
}
|
||||
}
|
||||
16
src/Api/BovinApiInterface.php
Normal file
16
src/Api/BovinApiInterface.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Malio\EdnotifBundle\Api;
|
||||
|
||||
use Malio\EdnotifBundle\Dto\DossierAnimalDto;
|
||||
|
||||
interface BovinApiInterface
|
||||
{
|
||||
public function getDossierAnimal(
|
||||
string $exploitationNumero,
|
||||
string $numeroNational,
|
||||
string $codePays = 'FR'
|
||||
): DossierAnimalDto;
|
||||
}
|
||||
98
src/Auth/TokenProvider.php
Normal file
98
src/Auth/TokenProvider.php
Normal file
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Malio\EdnotifBundle\Auth;
|
||||
|
||||
use Malio\EdnotifBundle\Exception\EdnotifException;
|
||||
use Psr\Cache\CacheItemPoolInterface;
|
||||
use SoapClient;
|
||||
use SoapFault;
|
||||
|
||||
final class TokenProvider
|
||||
{
|
||||
public function __construct(
|
||||
private SoapClient $guichetClient,
|
||||
private string $entreprise,
|
||||
private ?string $zone,
|
||||
private ?string $application,
|
||||
private string $login,
|
||||
private string $password,
|
||||
private int $tokenTtlSeconds,
|
||||
private CacheItemPoolInterface $cachePool,
|
||||
) {
|
||||
}
|
||||
|
||||
public function getToken(): string
|
||||
{
|
||||
$cacheKey = $this->getCacheKey();
|
||||
$item = $this->cachePool->getItem($cacheKey);
|
||||
|
||||
if ($item->isHit()) {
|
||||
$token = $item->get();
|
||||
if (is_string($token) && $token !== '') {
|
||||
return $token;
|
||||
}
|
||||
}
|
||||
|
||||
$token = $this->createToken();
|
||||
|
||||
$item->set($token);
|
||||
$item->expiresAfter($this->tokenTtlSeconds);
|
||||
$this->cachePool->save($item);
|
||||
|
||||
return $token;
|
||||
}
|
||||
|
||||
public function invalidateToken(): void
|
||||
{
|
||||
$this->cachePool->deleteItem($this->getCacheKey());
|
||||
}
|
||||
|
||||
private function createToken(): string
|
||||
{
|
||||
$profil = array_filter([
|
||||
'Entreprise' => $this->entreprise,
|
||||
'Zone' => $this->zone,
|
||||
'Application' => $this->application,
|
||||
], static fn ($v) => $v !== null && $v !== '');
|
||||
|
||||
$payload = [
|
||||
'Identification' => [
|
||||
'UserId' => $this->login,
|
||||
'Password' => $this->password,
|
||||
'Profil' => $profil,
|
||||
],
|
||||
];
|
||||
|
||||
try {
|
||||
/** @var object $response */
|
||||
$response = $this->guichetClient->__soapCall('tkCreateIdentification', [$payload]);
|
||||
} catch (SoapFault $e) {
|
||||
throw new \RuntimeException('SOAP Fault lors de tkCreateIdentification: ' . $e->getMessage(), 0, $e);
|
||||
}
|
||||
|
||||
$rs = $response->ReponseStandard ?? null;
|
||||
$ok = is_object($rs) && (($rs->Resultat ?? false) === true);
|
||||
|
||||
if (!$ok) {
|
||||
$anom = $rs->Anomalie ?? null;
|
||||
$code = (string)($anom->Code ?? 'UNKNOWN');
|
||||
$sev = (int)($anom->Severite ?? 1);
|
||||
$msg = (string)($anom->Message ?? 'Authentification refusée');
|
||||
throw new EdnotifException($code, $sev, $msg);
|
||||
}
|
||||
|
||||
$token = $response->Jeton ?? null;
|
||||
if (!is_string($token) || $token === '') {
|
||||
throw new \RuntimeException('Guichet: réponse OK mais Jeton absent.');
|
||||
}
|
||||
|
||||
return $token;
|
||||
}
|
||||
|
||||
private function getCacheKey(): string
|
||||
{
|
||||
return 'ednotif.token.' . hash('sha256', $this->entreprise . '|' . $this->login);
|
||||
}
|
||||
}
|
||||
45
src/DependencyInjection/Configuration.php
Normal file
45
src/DependencyInjection/Configuration.php
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Malio\EdnotifBundle\DependencyInjection;
|
||||
|
||||
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
|
||||
use Symfony\Component\Config\Definition\ConfigurationInterface;
|
||||
|
||||
final class Configuration implements ConfigurationInterface
|
||||
{
|
||||
public function getConfigTreeBuilder(): TreeBuilder
|
||||
{
|
||||
$treeBuilder = new TreeBuilder('ednotif');
|
||||
|
||||
$root = $treeBuilder->getRootNode();
|
||||
$root
|
||||
->children()
|
||||
->scalarNode('guichet_wsdl')->cannotBeEmpty()->isRequired()->end()
|
||||
->scalarNode('metier_wsdl')->cannotBeEmpty()->isRequired()->end()
|
||||
|
||||
->scalarNode('entreprise')->cannotBeEmpty()->isRequired()->end()
|
||||
->scalarNode('zone')->defaultNull()->end()
|
||||
->scalarNode('application')->defaultNull()->end()
|
||||
|
||||
->scalarNode('login')->cannotBeEmpty()->isRequired()->end()
|
||||
->scalarNode('password')->cannotBeEmpty()->isRequired()->end()
|
||||
|
||||
->integerNode('token_ttl_seconds')->min(30)->defaultValue(900)->end()
|
||||
|
||||
->arrayNode('soap_options')
|
||||
->addDefaultsIfNotSet()
|
||||
->children()
|
||||
->booleanNode('trace')->defaultFalse()->end()
|
||||
->booleanNode('exceptions')->defaultTrue()->end()
|
||||
->integerNode('connection_timeout')->min(1)->defaultValue(15)->end()
|
||||
->integerNode('cache_wsdl')->defaultValue(\WSDL_CACHE_BOTH)->end()
|
||||
->integerNode('features')->defaultValue(\SOAP_SINGLE_ELEMENT_ARRAYS)->end()
|
||||
->end()
|
||||
->end()
|
||||
->end();
|
||||
|
||||
return $treeBuilder;
|
||||
}
|
||||
}
|
||||
47
src/DependencyInjection/EdnotifExtension.php
Normal file
47
src/DependencyInjection/EdnotifExtension.php
Normal file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Malio\EdnotifBundle\DependencyInjection;
|
||||
|
||||
use Symfony\Component\Config\FileLocator;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Extension\Extension;
|
||||
use Symfony\Component\DependencyInjection\Loader\PhpFileLoader;
|
||||
|
||||
final class EdnotifExtension extends Extension
|
||||
{
|
||||
public function load(array $configs, ContainerBuilder $container): void
|
||||
{
|
||||
$configuration = new Configuration();
|
||||
/** @var array{
|
||||
* guichet_wsdl:string,
|
||||
* metier_wsdl:string,
|
||||
* entreprise:string,
|
||||
* zone:?string,
|
||||
* application:?string,
|
||||
* login:string,
|
||||
* password:string,
|
||||
* token_ttl_seconds:int,
|
||||
* soap_options:array<string,mixed>
|
||||
* } $config
|
||||
*/
|
||||
$config = $this->processConfiguration($configuration, $configs);
|
||||
|
||||
$container->setParameter('ednotif.guichet_wsdl', $config['guichet_wsdl']);
|
||||
$container->setParameter('ednotif.metier_wsdl', $config['metier_wsdl']);
|
||||
|
||||
$container->setParameter('ednotif.entreprise', $config['entreprise']);
|
||||
$container->setParameter('ednotif.zone', $config['zone']);
|
||||
$container->setParameter('ednotif.application', $config['application']);
|
||||
|
||||
$container->setParameter('ednotif.login', $config['login']);
|
||||
$container->setParameter('ednotif.password', $config['password']);
|
||||
|
||||
$container->setParameter('ednotif.token_ttl_seconds', $config['token_ttl_seconds']);
|
||||
$container->setParameter('ednotif.soap_options', $config['soap_options']);
|
||||
|
||||
$loader = new PhpFileLoader($container, new FileLocator(__DIR__ . '/../../config'));
|
||||
$loader->load('services.php');
|
||||
}
|
||||
}
|
||||
20
src/Dto/DossierAnimalDto.php
Normal file
20
src/Dto/DossierAnimalDto.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Malio\EdnotifBundle\Dto;
|
||||
|
||||
final readonly class DossierAnimalDto
|
||||
{
|
||||
/**
|
||||
* @param array<string,mixed> $identiteBovin
|
||||
* @param list<array{entree: array<string,mixed>, sortie?: array<string,mixed>}> $periodesPresence
|
||||
*/
|
||||
public function __construct(
|
||||
public string $numeroNational,
|
||||
public array $identiteBovin,
|
||||
public array $periodesPresence,
|
||||
public object $rawResponse,
|
||||
) {
|
||||
}
|
||||
}
|
||||
11
src/EdnotifBundle.php
Normal file
11
src/EdnotifBundle.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Malio\EdnotifBundle;
|
||||
|
||||
use Symfony\Component\HttpKernel\Bundle\Bundle;
|
||||
|
||||
final class EdnotifBundle extends Bundle
|
||||
{
|
||||
}
|
||||
18
src/Exception/EdnotifException.php
Normal file
18
src/Exception/EdnotifException.php
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Malio\EdnotifBundle\Exception;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
final class EdnotifException extends RuntimeException
|
||||
{
|
||||
public function __construct(
|
||||
public readonly string $codeAnomalie,
|
||||
public readonly int $severite,
|
||||
string $message
|
||||
) {
|
||||
parent::__construct($message);
|
||||
}
|
||||
}
|
||||
28
src/Soap/SoapClientFactory.php
Normal file
28
src/Soap/SoapClientFactory.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Malio\EdnotifBundle\Soap;
|
||||
|
||||
use SoapClient;
|
||||
|
||||
final class SoapClientFactory
|
||||
{
|
||||
/**
|
||||
* @param array<string,mixed> $soapOptions
|
||||
*/
|
||||
public function __construct(private array $soapOptions = [])
|
||||
{
|
||||
}
|
||||
|
||||
public function create(string $wsdl): SoapClient
|
||||
{
|
||||
$options = $this->soapOptions;
|
||||
|
||||
// Petites valeurs sûres par défaut
|
||||
$options['encoding'] ??= 'UTF-8';
|
||||
$options['exceptions'] ??= true;
|
||||
|
||||
return new SoapClient($wsdl, $options);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user