Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
Latest
------

### Changes
* [#27](https://github.com/cleverage/soap-process-bundle/issues/27) Give the ids of both services in the error on duplicate client codes: the clients are registered by a compiler pass of the bundle, `ClientRegistry::addClient()` gets an optional `$serviceId` argument. Update documentation, add tests.

v3.1
------

Expand Down
5 changes: 3 additions & 2 deletions docs/reference/client.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,8 +123,9 @@ class CountryInfoClient extends Client
Notes
-----

* Codes must be unique: registering two clients with the same code throws an `\UnexpectedValueException`
(`Client <code> is already defined`) when the registry is instantiated, i.e. when the first SOAP task or
* Codes must be unique: registering two clients with the same code throws an `\UnexpectedValueException` giving the
ids of both services (`Client <code> is already defined by service "<id>", cannot register service "<id>"`) when
the registry is instantiated, i.e. when the first SOAP task or
transformer service is built.
* Using a code that is not registered throws a `CleverAge\SoapProcessBundle\Exception\MissingClientException`
(`No Soap client with code : <code>`).
Expand Down
10 changes: 2 additions & 8 deletions src/CleverAgeSoapProcessBundle.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

namespace CleverAge\SoapProcessBundle;

use CleverAge\ProcessBundle\DependencyInjection\Compiler\RegistryCompilerPass;
use CleverAge\SoapProcessBundle\DependencyInjection\Compiler\RegisterClientsPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\Bundle\Bundle;

Expand All @@ -24,13 +24,7 @@ class CleverAgeSoapProcessBundle extends Bundle
*/
public function build(ContainerBuilder $container): void
{
$container->addCompilerPass(
new RegistryCompilerPass(
'cleverage_soap_process.registry.client',
'cleverage.soap.client',
'addClient'
)
);
$container->addCompilerPass(new RegisterClientsPass());
}

#[\Override]
Expand Down
36 changes: 36 additions & 0 deletions src/DependencyInjection/Compiler/RegisterClientsPass.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

declare(strict_types=1);

/*
* This file is part of the CleverAge/SoapProcessBundle package.
*
* Copyright (c) Clever-Age
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace CleverAge\SoapProcessBundle\DependencyInjection\Compiler;

use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;

/**
* Adds the tagged SOAP clients to the client registry, with their service id.
*/
class RegisterClientsPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container): void
{
if (!$container->has('cleverage_soap_process.registry.client')) {
return;
}

$definition = $container->findDefinition('cleverage_soap_process.registry.client');
foreach (array_keys($container->findTaggedServiceIds('cleverage.soap.client')) as $id) {
$definition->addMethodCall('addClient', [new Reference($id), $id]);
}
}
}
21 changes: 17 additions & 4 deletions src/Registry/ClientRegistry.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,25 @@ class ClientRegistry
/** @var ClientInterface[] */
private array $clients = [];

public function addClient(ClientInterface $client): void
/** @var array<string, string|null> Service ids of the clients, indexed by code */
private array $serviceIds = [];

/**
* @param string|null $serviceId Id of the client service, used to identify the clients with the same code
*/
public function addClient(ClientInterface $client, ?string $serviceId = null): void
{
if (\array_key_exists($client->getCode(), $this->getClients())) {
throw new \UnexpectedValueException("Client {$client->getCode()} is already defined");
$code = $client->getCode();
if (\array_key_exists($code, $this->getClients())) {
$message = "Client {$code} is already defined";
if (null !== $this->serviceIds[$code] && null !== $serviceId) {
$message .= " by service \"{$this->serviceIds[$code]}\", cannot register service \"{$serviceId}\"";
}

throw new \UnexpectedValueException($message);
}
$this->clients[$client->getCode()] = $client;
$this->clients[$code] = $client;
$this->serviceIds[$code] = $serviceId;
}

/**
Expand Down
76 changes: 76 additions & 0 deletions tests/DependencyInjection/Compiler/RegisterClientsPassTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<?php

declare(strict_types=1);

/*
* This file is part of the CleverAge/SoapProcessBundle package.
*
* Copyright (c) Clever-Age
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace CleverAge\SoapProcessBundle\Tests\DependencyInjection\Compiler;

use CleverAge\SoapProcessBundle\Client\Client;
use CleverAge\SoapProcessBundle\DependencyInjection\Compiler\RegisterClientsPass;
use CleverAge\SoapProcessBundle\Registry\ClientRegistry;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\UsesClass;
use PHPUnit\Framework\TestCase;
use Psr\Log\NullLogger;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;

#[CoversClass(RegisterClientsPass::class)]
#[UsesClass(ClientRegistry::class)]
#[UsesClass(Client::class)]
class RegisterClientsPassTest extends TestCase
{
public function testClientsAreRegistered(): void
{
$container = $this->createContainer(['app.client' => 'client', 'app.other' => 'other']);
$container->compile(true);

/** @var ClientRegistry $registry */
$registry = $container->get('cleverage_soap_process.registry.client');
self::assertSame('client', $registry->getClient('client')->getCode());
self::assertSame('other', $registry->getClient('other')->getCode());
}

public function testDuplicateCodeGivesTheServiceIds(): void
{
$container = $this->createContainer(['app.client' => 'client', 'app.client_duplicate' => 'client']);
$container->compile(true);

$this->expectException(\UnexpectedValueException::class);
$this->expectExceptionMessage('Client client is already defined by service "app.client", cannot register service "app.client_duplicate"');
$container->get('cleverage_soap_process.registry.client');
}

public function testWithoutRegistry(): void
{
$container = new ContainerBuilder();
(new RegisterClientsPass())->process($container);

self::assertFalse($container->has('cleverage_soap_process.registry.client'));
}

/**
* @param array<string, string> $clients Codes of the clients, indexed by service id
*/
private function createContainer(array $clients): ContainerBuilder
{
$container = new ContainerBuilder();
$container->addCompilerPass(new RegisterClientsPass());
$container->setDefinition('cleverage_soap_process.registry.client', new Definition(ClientRegistry::class))
->setPublic(true);
foreach ($clients as $id => $code) {
$container->setDefinition($id, new Definition(Client::class, [new Definition(NullLogger::class), $code, null]))
->addTag('cleverage.soap.client');
}

return $container;
}
}
75 changes: 75 additions & 0 deletions tests/Registry/ClientRegistryTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<?php

declare(strict_types=1);

/*
* This file is part of the CleverAge/SoapProcessBundle package.
*
* Copyright (c) Clever-Age
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace CleverAge\SoapProcessBundle\Tests\Registry;

use CleverAge\SoapProcessBundle\Client\ClientInterface;
use CleverAge\SoapProcessBundle\Exception\MissingClientException;
use CleverAge\SoapProcessBundle\Registry\ClientRegistry;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\UsesClass;
use PHPUnit\Framework\TestCase;

#[CoversClass(ClientRegistry::class)]
#[UsesClass(MissingClientException::class)]
class ClientRegistryTest extends TestCase
{
public function testGetClient(): void
{
$registry = new ClientRegistry();
$client = $this->createClient('client');
$other = $this->createClient('other');
$registry->addClient($client, 'app.client');
$registry->addClient($other);

self::assertSame($client, $registry->getClient('client'));
self::assertSame($other, $registry->getClient('other'));
self::assertTrue($registry->hasClient('client'));
self::assertSame(['client' => $client, 'other' => $other], $registry->getClients());
}

public function testMissingClient(): void
{
$this->expectException(MissingClientException::class);
$this->expectExceptionMessage('No Soap client with code : missing');
(new ClientRegistry())->getClient('missing');
}

public function testDuplicateCodeGivesTheServiceIds(): void
{
$registry = new ClientRegistry();
$registry->addClient($this->createClient('client'), 'app.client');

$this->expectException(\UnexpectedValueException::class);
$this->expectExceptionMessage('Client client is already defined by service "app.client", cannot register service "app.client_duplicate"');
$registry->addClient($this->createClient('client'), 'app.client_duplicate');
}

public function testDuplicateCodeWithoutServiceIds(): void
{
$registry = new ClientRegistry();
$registry->addClient($this->createClient('client'));

$this->expectException(\UnexpectedValueException::class);
$this->expectExceptionMessageMatches('/^Client client is already defined$/');
$registry->addClient($this->createClient('client'), 'app.client_duplicate');
}

private function createClient(string $code): ClientInterface
{
$client = $this->createStub(ClientInterface::class);
$client->method('getCode')->willReturn($code);

return $client;
}
}
Loading