diff --git a/adr/2021_07_05_api_providing_locales_available_in_active_channel.md b/adr/2021_07_05_api_providing_locales_available_in_active_channel.md new file mode 100644 index 0000000000..12887e4e10 --- /dev/null +++ b/adr/2021_07_05_api_providing_locales_available_in_active_channel.md @@ -0,0 +1,26 @@ +# Using data provider for getting available locales in active channel for shop user + +* Status: proposed +* Date: 2021-07-05 + +## Context and Problem Statement +Customer should have access only to locales available in their channel + +## Considered Options + +### Using Doctrine Collection extension + +* Good, because consistent with actual approach to modifying responses content +* Good, because works with rest of API extension like pagination +* Bad, because locales don't have relation to channel, so using Doctrine Collection extension is extremely hard. + +### Using Data Provider + +* Good, because we already have this approach for older resources +* Good, because easy to implement +* Bad, because using data providers omits extra Doctrine extensions like pagination + +## Decision Outcome + +Chosen option: Using Data Provider +Shops shouldn't have many locales for each channel, so lack of a pagination is smaller problem than creating overcomplicated query in Doctrine Collection extension diff --git a/features/locale/getting_available_locales_in_current_channel.feature b/features/locale/getting_available_locales_in_current_channel.feature index 0c0ab404a6..df117ffc23 100644 --- a/features/locale/getting_available_locales_in_current_channel.feature +++ b/features/locale/getting_available_locales_in_current_channel.feature @@ -1,5 +1,5 @@ @locales -Feature: Getting a available locales in current channel +Feature: Getting available locales in the current channel In order to use a shop in chosen locale As a Customer I want to be able to get available locales diff --git a/src/Sylius/Behat/Context/Api/Shop/LocaleContext.php b/src/Sylius/Behat/Context/Api/Shop/LocaleContext.php index 0485a3cd1d..93b76835c1 100644 --- a/src/Sylius/Behat/Context/Api/Shop/LocaleContext.php +++ b/src/Sylius/Behat/Context/Api/Shop/LocaleContext.php @@ -56,9 +56,11 @@ final class LocaleContext implements Context */ public function theLocaleWithCodeShouldBeAvailable(string $name, string $code): void { - Assert::true( - $this->isLocaleWithName($name, $this->responseChecker->getCollection($this->client->getLastResponse())) - ); + Assert::true($this->isLocaleWithNameAndCode( + $this->responseChecker->getCollection($this->client->getLastResponse()), + $name, + $code + )); } /** @@ -66,15 +68,17 @@ final class LocaleContext implements Context */ public function theLocaleWithCodeShouldNotBeAvailable(string $name, string $code): void { - Assert::false( - $this->isLocaleWithName($name, $this->responseChecker->getCollection($this->client->getLastResponse())) - ); + Assert::false($this->isLocaleWithNameAndCode( + $this->responseChecker->getCollection($this->client->getLastResponse()), + $name, + $code + )); } - private function isLocaleWithName(string $name, array $locales): bool + private function isLocaleWithNameAndCode(array $locales, string $name, string $code): bool { foreach ($locales as $locale) { - if ($locale['name'] === $name) { + if ($locale['name'] === $name && $locale['code'] === $code) { return true; } } diff --git a/src/Sylius/Bundle/ApiBundle/DataProvider/LocaleCollectionDataProvider.php b/src/Sylius/Bundle/ApiBundle/DataProvider/LocaleCollectionDataProvider.php index 83f33e9cc1..c0e847fb21 100644 --- a/src/Sylius/Bundle/ApiBundle/DataProvider/LocaleCollectionDataProvider.php +++ b/src/Sylius/Bundle/ApiBundle/DataProvider/LocaleCollectionDataProvider.php @@ -15,8 +15,10 @@ namespace Sylius\Bundle\ApiBundle\DataProvider; use ApiPlatform\Core\DataProvider\CollectionDataProviderInterface; use ApiPlatform\Core\DataProvider\RestrictedDataProviderInterface; +use Doctrine\Common\Collections\Collection; use Sylius\Bundle\ApiBundle\Context\UserContextInterface; use Sylius\Bundle\ApiBundle\Serializer\ContextKeys; +use Sylius\Component\Core\Model\AdminUserInterface; use Sylius\Component\Core\Model\ChannelInterface; use Sylius\Component\Locale\Model\LocaleInterface; use Sylius\Component\Resource\Repository\RepositoryInterface; @@ -31,8 +33,10 @@ final class LocaleCollectionDataProvider implements CollectionDataProviderInterf /** @var UserContextInterface */ private $userContext; - public function __construct(RepositoryInterface $localeRepository, UserContextInterface $userContext) - { + public function __construct( + RepositoryInterface $localeRepository, + UserContextInterface $userContext + ) { $this->localeRepository = $localeRepository; $this->userContext = $userContext; } @@ -44,12 +48,14 @@ final class LocaleCollectionDataProvider implements CollectionDataProviderInterf public function getCollection(string $resourceClass, string $operationName = null, array $context = []) { - Assert::keyExists($context, ContextKeys::CHANNEL); - $user = $this->userContext->getUser(); - if ($user !== null && in_array('ROLE_API_ACCESS', $user->getRoles())) { + + if ($user instanceof AdminUserInterface && in_array('ROLE_API_ACCESS', $user->getRoles())) { return $this->localeRepository->findAll(); } + + Assert::keyExists($context, ContextKeys::CHANNEL); + /** @var ChannelInterface $channel */ $channel = $context[ContextKeys::CHANNEL]; diff --git a/src/Sylius/Bundle/ApiBundle/spec/DataProvider/LocaleCollectionDataProviderSpec.php b/src/Sylius/Bundle/ApiBundle/spec/DataProvider/LocaleCollectionDataProviderSpec.php index 8948f60399..15d360bc2d 100644 --- a/src/Sylius/Bundle/ApiBundle/spec/DataProvider/LocaleCollectionDataProviderSpec.php +++ b/src/Sylius/Bundle/ApiBundle/spec/DataProvider/LocaleCollectionDataProviderSpec.php @@ -14,9 +14,11 @@ declare(strict_types=1); namespace spec\Sylius\Bundle\ApiBundle\DataProvider; use Doctrine\Common\Collections\ArrayCollection; +use Doctrine\Common\Collections\Collection; use PhpSpec\ObjectBehavior; use Sylius\Bundle\ApiBundle\Context\UserContextInterface; use Sylius\Bundle\ApiBundle\Serializer\ContextKeys; +use Sylius\Component\Core\Model\AdminUserInterface; use Sylius\Component\Core\Model\ChannelInterface; use Sylius\Component\Core\Model\ProductInterface; use Sylius\Component\Locale\Model\LocaleInterface; @@ -57,17 +59,17 @@ final class LocaleCollectionDataProviderSpec extends ObjectBehavior $channel->addLocale($firstLocale); $channel->addLocale($secondLocale); - $channel->getLocales()->willReturn([$firstLocale, $secondLocale]); + $channel->getLocales()->willReturn(new ArrayCollection([$firstLocale->getWrappedObject(), $secondLocale->getWrappedObject()])); $this ->getCollection(LocaleInterface::class, 'get', [ContextKeys::CHANNEL => $channel]) - ->shouldReturn(new ArrayCollection([$firstLocale, $secondLocale])) + ->shouldBeLike(new ArrayCollection([$firstLocale->getWrappedObject(), $secondLocale->getWrappedObject()])) ; } function it_provides_all_locales_if_user_is_admin( UserContextInterface $userContext, - UserInterface $user, + AdminUserInterface $user, ChannelInterface $channel, LocaleInterface $firstLocale, LocaleInterface $secondLocale,