Skip to content

Commit

Permalink
NEXT-2128 - Add test for storefront api proxy
Browse files Browse the repository at this point in the history
  • Loading branch information
janbuecker committed Mar 13, 2019
1 parent 9204616 commit d221411
Show file tree
Hide file tree
Showing 4 changed files with 301 additions and 1 deletion.
35 changes: 35 additions & 0 deletions Framework/Api/EventListener/ResponseHeaderListener.php
@@ -0,0 +1,35 @@
<?php declare(strict_types=1);

namespace Shopware\Core\Framework\Api\EventListener;

use Shopware\Core\PlatformRequest;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;

class ResponseHeaderListener implements EventSubscriberInterface
{
private const HEADERS = [
PlatformRequest::HEADER_VERSION_ID,
PlatformRequest::HEADER_LANGUAGE_ID,
PlatformRequest::HEADER_CONTEXT_TOKEN,
];

public static function getSubscribedEvents(): array
{
return [
KernelEvents::RESPONSE => 'onResponse',
];
}

public function onResponse(FilterResponseEvent $event): void
{
foreach (self::HEADERS as $header) {
$event->getResponse()->headers->set(
$header,
$event->getRequest()->headers->get($header),
false
);
}
}
}
4 changes: 4 additions & 0 deletions Framework/DependencyInjection/api.xml
Expand Up @@ -17,6 +17,10 @@
<argument>%kernel.debug%</argument>
</service>

<service id="Shopware\Core\Framework\Api\EventListener\ResponseHeaderListener">
<tag name="kernel.event_subscriber"/>
</service>

<service id="Shopware\Core\Framework\Api\Context\ContextValueResolver">
<tag name="controller.argument_value_resolver" priority="1000"/>
</service>
Expand Down
2 changes: 1 addition & 1 deletion Framework/Routing/SalesChannelRequestContextResolver.php
Expand Up @@ -64,7 +64,7 @@ public function handleCheckoutContext(Request $request, Request $master, string
if ($master->attributes->has(PlatformRequest::ATTRIBUTE_STOREFRONT_CONTEXT_OBJECT)) {
$context = $master->attributes->get(PlatformRequest::ATTRIBUTE_STOREFRONT_CONTEXT_OBJECT);
} else {
$context = $this->contextService->get($salesChannelId, $contextToken, null);
$context = $this->contextService->get($salesChannelId, $contextToken, $request->headers->get(PlatformRequest::HEADER_LANGUAGE_ID));
}

$request->attributes->set(PlatformRequest::ATTRIBUTE_CONTEXT_OBJECT, $context->getContext());
Expand Down
261 changes: 261 additions & 0 deletions Framework/Test/Api/Controller/StorefrontProxyControllerTest.php
@@ -0,0 +1,261 @@
<?php declare(strict_types=1);

namespace Shopware\Core\Framework\Test\Api\Controller;

use PHPUnit\Framework\TestCase;
use Shopware\Core\Defaults;
use Shopware\Core\Framework\Api\Util\AccessKeyHelper;
use Shopware\Core\Framework\Context;
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface;
use Shopware\Core\Framework\Struct\Uuid;
use Shopware\Core\Framework\Test\TestCaseBase\AdminFunctionalTestBehaviour;
use Shopware\Core\Framework\Test\TestCaseBase\AssertArraySubsetBehaviour;
use Shopware\Core\PlatformRequest;
use Symfony\Component\HttpFoundation\Response;

class StorefrontProxyControllerTest extends TestCase
{
use AdminFunctionalTestBehaviour,
AssertArraySubsetBehaviour;

/**
* @var EntityRepositoryInterface
*/
private $salesChannelRepository;

protected function setUp(): void
{
$this->salesChannelRepository = $this->getContainer()->get('sales_channel.repository');
}

public function testProxyWithInvalidSalesChannelId(): void
{
$this->getClient()->request('GET', $this->getUrl(Uuid::uuid4()->getHex(), '/product'));

$response = $this->getClient()->getResponse()->getContent();
$response = json_decode($response, true);

static::assertArrayHasKey('errors', $response);
static::assertEquals('INVALID-SALES-CHANNEL-ID', $response['errors'][0]['code'] ?? null);
}

public function testProxyCallToStorefrontApi(): void
{
$salesChannel = $this->createSalesChannel();

$this->getClient()->request('GET', $this->getUrl($salesChannel['id'], '/product'));

$response = $this->getClient()->getResponse()->getContent();
$response = json_decode($response, true);

static::assertArrayNotHasKey('errors', $response);
}

public function testHeadersAreCopied(): void
{
$salesChannel = $this->createSalesChannel();
$uuid = Uuid::uuid4()->getHex();

$this->getClient()->request(
'GET',
$this->getUrl($salesChannel['id'], '/product'),
[],
[],
[
'HTTP_X_SW_CONTEXT_TOKEN' => $uuid,
'HTTP_X_SW_LANGUAGE_ID' => $uuid,
'HTTP_X_SW_VERSION_ID' => $uuid,
]
);

static::assertEquals($uuid, $this->getClient()->getRequest()->headers->get('x-sw-context-token'));
static::assertEquals($uuid, $this->getClient()->getRequest()->headers->get('x-sw-language-id'));
static::assertEquals($uuid, $this->getClient()->getRequest()->headers->get('x-sw-version-id'));
static::assertEquals($uuid, $this->getClient()->getResponse()->headers->get('x-sw-context-token'));
static::assertEquals($uuid, $this->getClient()->getResponse()->headers->get('x-sw-language-id'));
static::assertEquals($uuid, $this->getClient()->getResponse()->headers->get('x-sw-version-id'));
}

public function testOnlyDefinedHeadersAreCopied(): void
{
$salesChannel = $this->createSalesChannel();

$this->getClient()->request(
'GET',
$this->getUrl($salesChannel['id'], '/product'),
[],
[],
[
'HTTP_X_SW_CUSTOM_HEADER' => 'foo',
]
);

static::assertEquals('foo', $this->getClient()->getRequest()->headers->get('x-sw-custom-header'));
static::assertArrayNotHasKey('x-sw-custom-header', $this->getClient()->getResponse()->headers->all());
}

public function testDifferentLanguage(): void
{
$langId = Uuid::uuid4()->getHex();
$salesChannel = $this->createSalesChannel();
$this->createLanguage($langId, $salesChannel['id']);

$this->assertTranslation(
['name' => 'not translated', 'viewData' => ['name' => 'not translated']],
[
'translations' => [
Defaults::LANGUAGE_SYSTEM => ['name' => 'not translated'],
$langId => ['name' => 'translated'],
],
],
$salesChannel['id'],
Defaults::LANGUAGE_SYSTEM
);

$this->assertTranslation(
['name' => 'translated', 'viewData' => ['name' => 'translated']],
[
'translations' => [
Defaults::LANGUAGE_SYSTEM => ['name' => 'not translated'],
$langId => ['name' => 'translated'],
],
],
$salesChannel['id'],
$langId
);

$this->assertTranslation(
['name' => 'translated', 'metaTitle' => null, 'viewData' => ['name' => 'translated', 'metaTitle' => 'foo']],
[
'translations' => [
Defaults::LANGUAGE_SYSTEM => ['name' => 'not translated', 'metaTitle' => 'foo'],
$langId => ['name' => 'translated'],
],
],
$salesChannel['id'],
$langId
);
}

private function getLangHeaderName(): string
{
return 'HTTP_' . strtoupper(str_replace('-', '_', PlatformRequest::HEADER_LANGUAGE_ID));
}

private function assertTranslation(array $expectedTranslations, array $data, string $salesChannelId, $langOverride = null): void
{
$baseResource = '/api/v' . PlatformRequest::API_VERSION . '/category';

$categoryData = $data;
if (!isset($categoryData['id'])) {
$categoryData['id'] = Uuid::uuid4()->getHex();
}

$this->getClient()->request('POST', $baseResource, $categoryData);
$response = $this->getClient()->getResponse();

static::assertEquals(204, $response->getStatusCode(), $response->getContent());

$this->assertEntityExists($this->getClient(), 'category', $categoryData['id']);

$headers = ['HTTP_ACCEPT' => 'application/json'];
if ($langOverride) {
$headers[$this->getLangHeaderName()] = $langOverride;
}

$this->getClient()->request('GET', $this->getUrl($salesChannelId, '/category/' . $categoryData['id']), [], [], $headers);

$response = $this->getClient()->getResponse();
static::assertSame(Response::HTTP_OK, $response->getStatusCode(), $response->getContent());
$responseData = json_decode($response->getContent(), true);

static::assertArrayHasKey('data', $responseData, $response->getContent());

$this->silentAssertArraySubset($expectedTranslations, $responseData['data']);
}

private function createLanguage($langId, string $salesChannelId, $fallbackId = null): void
{
$baseUrl = '/api/v' . PlatformRequest::API_VERSION;

if ($fallbackId) {
$fallbackLocaleId = Uuid::uuid4()->getHex();
$parentLanguageData = [
'id' => $fallbackId,
'name' => 'test language ' . $fallbackId,
'locale' => [
'id' => $fallbackLocaleId,
'code' => 'x-tst_' . $fallbackLocaleId,
'name' => 'Test locale ' . $fallbackLocaleId,
'territory' => 'Test territory ' . $fallbackLocaleId,
],
'translationCodeId' => $fallbackLocaleId,
];
$this->getClient()->request('POST', $baseUrl . '/language', $parentLanguageData);
static::assertEquals(204, $this->getClient()->getResponse()->getStatusCode());
}

$localeId = Uuid::uuid4()->getHex();
$languageData = [
'id' => $langId,
'name' => 'test language ' . $langId,
'parentId' => $fallbackId,
'locale' => [
'id' => $localeId,
'code' => 'x-tst_' . $localeId,
'name' => 'Test locale ' . $localeId,
'territory' => 'Test territory ' . $localeId,
],
'translationCodeId' => $localeId,
'salesChannels' => [
['id' => $salesChannelId],
],
];

$this->getClient()->request('POST', $baseUrl . '/language', $languageData);
static::assertEquals(204, $this->getClient()->getResponse()->getStatusCode(), $this->getClient()->getResponse()->getContent());

$this->getClient()->request('GET', $baseUrl . '/language/' . $langId);
}

private function getUrl(string $salesChannelId, string $url): string
{
return sprintf(
'/api/v%d/_proxy/storefront-api/%s/v%1$d/%s',
PlatformRequest::API_VERSION,
$salesChannelId,
ltrim($url, '/')
);
}

private function createSalesChannel(array $salesChannel = []): array
{
$defaults = [
'id' => Uuid::uuid4()->getHex(),
'name' => 'unit test channel',
'typeId' => Defaults::SALES_CHANNEL_STOREFRONT,
'accessKey' => AccessKeyHelper::generateAccessKey('sales-channel'),
'languageId' => Defaults::LANGUAGE_SYSTEM,
'snippetSetId' => Defaults::SNIPPET_BASE_SET_EN,
'currencyId' => Defaults::CURRENCY,
'currencyVersionId' => Defaults::LIVE_VERSION,
'paymentMethodId' => Defaults::PAYMENT_METHOD_DEBIT,
'paymentMethodVersionId' => Defaults::LIVE_VERSION,
'shippingMethodId' => Defaults::SHIPPING_METHOD,
'shippingMethodVersionId' => Defaults::LIVE_VERSION,
'countryId' => Defaults::COUNTRY,
'countryVersionId' => Defaults::LIVE_VERSION,
'currencies' => [['id' => Defaults::CURRENCY]],
'languages' => [['id' => Defaults::LANGUAGE_SYSTEM]],
'paymentMethods' => [['id' => Defaults::PAYMENT_METHOD_DEBIT]],
'shippingMethods' => [['id' => Defaults::SHIPPING_METHOD]],
'countries' => [['id' => Defaults::COUNTRY]],
];

$salesChannel = array_merge_recursive($defaults, $salesChannel);

$this->salesChannelRepository->create([$salesChannel], Context::createDefaultContext());

return $salesChannel;
}
}

0 comments on commit d221411

Please sign in to comment.