Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NEXT-39768 - Added a listener to stop the Shop registration process if the Shop URL is not reachable #51

Merged
Show file tree
Hide file tree
Changes from 3 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
7 changes: 6 additions & 1 deletion src/DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@

final class Configuration implements ConfigurationInterface
{
public const EXTENSION_ALIAS = 'shopware_app';

public function getConfigTreeBuilder(): TreeBuilder
{
$treeBuilder = new TreeBuilder('shopware_app');
$treeBuilder = new TreeBuilder(self::EXTENSION_ALIAS);

/** @var ArrayNodeDefinition $rootNode */
$rootNode = $treeBuilder->getRootNode();
Expand Down Expand Up @@ -46,6 +48,9 @@ public function getConfigTreeBuilder(): TreeBuilder
->end()
->scalarNode('secret')
->defaultValue('TestSecret')
->end()
->booleanNode('check_if_shop_url_is_reachable')
->defaultFalse()
->end();

return $treeBuilder;
Expand Down
5 changes: 5 additions & 0 deletions src/DependencyInjection/ShopwareAppExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,5 +57,10 @@ public function load(array $configs, ContainerBuilder $container): void
->replaceArgument(0, $config['name'])
->replaceArgument(1, $config['secret'])
->replaceArgument(2, $config['confirmation_url']);

$container->setParameter(
sprintf('%s.check_if_shop_url_is_reachable', Configuration::EXTENSION_ALIAS),
$config['check_if_shop_url_is_reachable']
);
}
}
44 changes: 44 additions & 0 deletions src/EventListener/BeforeRegistrationStartsListener.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php

declare(strict_types=1);

namespace Shopware\AppBundle\EventListener;

use Shopware\App\SDK\Event\BeforeRegistrationStartsEvent;
use Shopware\AppBundle\Exception\ShopURLIsNotReachableException;
use Symfony\Component\EventDispatcher\Attribute\AsEventListener;
use Symfony\Contracts\HttpClient\HttpClientInterface;

#[AsEventListener]
class BeforeRegistrationStartsListener
{
private HttpClientInterface $httpClient;

private bool $checkShopURLIsReachable;

public function __construct(
HttpClientInterface $httpClient,
bool $checkShopURLIsReachable
) {
$this->httpClient = $httpClient;
$this->checkShopURLIsReachable = $checkShopURLIsReachable;
}

public function __invoke(BeforeRegistrationStartsEvent $event): void

Check failure on line 27 in src/EventListener/BeforeRegistrationStartsListener.php

View workflow job for this annotation

GitHub Actions / static-analyse

Parameter $event of method Shopware\AppBundle\EventListener\BeforeRegistrationStartsListener::__invoke() has invalid type Shopware\App\SDK\Event\BeforeRegistrationStartsEvent.
{
if ($this->checkShopURLIsReachable === false) {
return;
}

$shop = $event->getShop();

Check failure on line 33 in src/EventListener/BeforeRegistrationStartsListener.php

View workflow job for this annotation

GitHub Actions / static-analyse

Call to method getShop() on an unknown class Shopware\App\SDK\Event\BeforeRegistrationStartsEvent.

try {
$this->httpClient->request('HEAD', $shop->getShopUrl(), [
nsaliu marked this conversation as resolved.
Show resolved Hide resolved
'timeout' => 10,
'max_redirects' => 0,
]);
} catch (\Throwable $e) {
throw new ShopURLIsNotReachableException($shop->getShopUrl(), $e);
}
}
}
20 changes: 20 additions & 0 deletions src/Exception/ShopURLIsNotReachableException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

declare(strict_types=1);

namespace Shopware\AppBundle\Exception;

class ShopURLIsNotReachableException extends \RuntimeException
{
public function __construct(string $shopUrl, ?\Throwable $previous = null)
{
parent::__construct(
sprintf(
'Shop URL "%s" is not reachable from the application server.',
$shopUrl
),
0,
$previous
);
}
}
4 changes: 4 additions & 0 deletions src/Resources/config/services.xml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@

<service id="Shopware\AppBundle\ArgumentValueResolver\ContextArgumentResolver"/>
<service id="Shopware\AppBundle\EventListener\ResponseSignerListener"/>
<service id="Shopware\AppBundle\EventListener\BeforeRegistrationStartsListener">
<argument type="service" id="Symfony\Contracts\HttpClient\HttpClientInterface"/>
<argument>%shopware_app.check_if_shop_url_is_reachable%</argument>
</service>

<!-- PSR Integration -->
<service id="Symfony\Bridge\PsrHttpMessage\HttpFoundationFactoryInterface" class="Symfony\Bridge\PsrHttpMessage\Factory\HttpFoundationFactory"/>
Expand Down
29 changes: 16 additions & 13 deletions tests/DependencyInjection/ConfigurationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,27 +35,30 @@ public static function getComparableTreeBuilder(): TreeBuilder
->defaultValue('auto')
->end()
->arrayNode('doctrine')
->children()
->scalarNode('shop_class')
->defaultValue(AbstractShop::class)
->end()
->end()
->children()
->scalarNode('shop_class')
->defaultValue(AbstractShop::class)
->end()
->end()
->end()
->arrayNode('dynamodb')
->children()
->scalarNode('table_name')
->defaultValue('shops')
->end()
->end()
->children()
->scalarNode('table_name')
->defaultValue('shops')
->end()
->end()
->end()
->scalarNode('confirmation_url')
->defaultValue('shopware_app_lifecycle_confirm')
->defaultValue('shopware_app_lifecycle_confirm')
->end()
->scalarNode('name')
->defaultValue('TestApp')
->defaultValue('TestApp')
->end()
->scalarNode('secret')
->defaultValue('TestSecret')
->defaultValue('TestSecret')
->end()
->booleanNode('check_if_shop_url_is_reachable')
->defaultFalse()
->end();

return $treeBuilder;
Expand Down
2 changes: 2 additions & 0 deletions tests/DependencyInjection/ShopwareAppExtensionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ public function testDefaultConfigAuto(): void
static::assertTrue($container->hasDefinition(ShopRepositoryInterface::class));

static::assertSame(DynamoDBRepository::class, $container->getDefinition(ShopRepositoryInterface::class)->getClass());

static::assertTrue($container->hasParameter('shopware_app.check_if_shop_url_is_reachable'));
}

public function testDefaultInMemory(): void
Expand Down
112 changes: 112 additions & 0 deletions tests/EventListener/BeforeRegistrationStartsListenerTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
<?php

declare(strict_types=1);

namespace EventListener;

use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Psr\Http\Message\RequestInterface;
use Shopware\App\SDK\Event\BeforeRegistrationStartsEvent;
use Shopware\App\SDK\Shop\ShopInterface;
use Shopware\AppBundle\EventListener\BeforeRegistrationStartsListener;
use Shopware\AppBundle\Exception\ShopURLIsNotReachableException;
use Symfony\Contracts\HttpClient\HttpClientInterface;

#[CoversClass(BeforeRegistrationStartsListener::class)]
final class BeforeRegistrationStartsListenerTest extends TestCase
{
private HttpClientInterface&MockObject $httpClient;

protected function setUp(): void
{
$this->httpClient = $this->createMock(HttpClientInterface::class);
}

public function testListenerMustReturnBecauseTheCheckIsSetToFalseInBundleConfiguration(): void
{
$shop = $this->createMock(ShopInterface::class);
$shop
->expects(self::never())
->method('getShopUrl');

$this->httpClient
->expects(self::never())
->method('request');

$listener = new BeforeRegistrationStartsListener(
$this->httpClient,
false
);

$listener->__invoke(
new BeforeRegistrationStartsEvent(
$this->createMock(RequestInterface::class),
$shop
)
);
}

public function testListenerMustBeExecutedWithoutErrorsIfTheCheckIsSetToTrueInConfiguration(): void
{
$shop = $this->createMock(ShopInterface::class);
$shop
->expects(self::once())
->method('getShopUrl')
->willReturn('https://shop-url.com');

$this->httpClient
->expects(self::once())
->method('request')
->with('HEAD', 'https://shop-url.com', [
'timeout' => 10,
'max_redirects' => 0,
]);

$listener = new BeforeRegistrationStartsListener(
$this->httpClient,
true
);

$listener->__invoke(
new BeforeRegistrationStartsEvent(
$this->createMock(RequestInterface::class),
$shop
)
);
}

public function testListenerMustThrowExceptionBecauseTheShopURLIsNotReachable(): void
{
$this->expectException(ShopURLIsNotReachableException::class);
$this->expectExceptionMessage('Shop URL "https://shop-url.com" is not reachable from the application server.');

$shop = $this->createMock(ShopInterface::class);
$shop
->expects(self::exactly(2))
->method('getShopUrl')
->willReturn('https://shop-url.com');

$this->httpClient
->expects(self::once())
->method('request')
->with('HEAD', 'https://shop-url.com', [
'timeout' => 10,
'max_redirects' => 0,
])
->willThrowException(new \Exception('Shop url is not reachable'));

$listener = new BeforeRegistrationStartsListener(
$this->httpClient,
true
);

$listener->__invoke(
new BeforeRegistrationStartsEvent(
$this->createMock(RequestInterface::class),
$shop
)
);
}
}
28 changes: 28 additions & 0 deletions tests/Exception/ShopURLIsNotReachableExceptionTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

declare(strict_types=1);

namespace Exception;

use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\TestCase;
use Shopware\AppBundle\Exception\ShopURLIsNotReachableException;

#[CoversClass(ShopURLIsNotReachableException::class)]
class ShopURLIsNotReachableExceptionTest extends TestCase
{
public function testExceptionMessage(): void
{
$shopUrl = 'http://example.com';

$exception = new ShopURLIsNotReachableException($shopUrl);

static::assertSame(
sprintf(
'Shop URL "%s" is not reachable from the application server.',
$shopUrl
),
$exception->getMessage()
);
}
}
Loading