feature #11372 [API] Zones (oallain, GSadee)

This PR was merged into the 1.8-dev branch.

Discussion
----------

| Q               | A
| --------------- | -----
| Branch?         | master
| Bug fix?        | no
| New feature?    | yes
| BC breaks?      | no
| Deprecations?   | no
| Related tickets | partially #11250
| License         | MIT

Cover `features/addressing/managing_zones/`

- [x] adding_zone.feature  
- [x] browsing_zone.feature  
- [x] deleting_multiple_zones.feature  
- [x] deleting_zone.feature  
- [x] editing_zone.feature
- [x] zone_unique_code_validation.feature  
- [x] zone_validation.feature





Commits
-------

6a8324bd0e adding zone
3ddbcbf90b browsing zone
e2c514f4be deleting zone
28ae5d484b deleting multiple zones
88cf3236ee editing zone
ba3b88f960 zone unique code validation
e757ce8f62 zone validation
dc69492017 editing zone
faa9da1200 rebase and update after merge #11397
d19fca5cba [API][Zone] Minor fixes after review
b9df4a5f60 [API][Zone] Revert implementation of seeing disable type field scenarios in api context
788c2ea47b [Zone] Move zone validator from ApiBundle to AddressingBundle
This commit is contained in:
Łukasz Chruściel 2020-05-12 12:35:50 +02:00 committed by GitHub
commit c5e319511d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
27 changed files with 902 additions and 79 deletions

View file

@ -11,7 +11,7 @@ Feature: Adding a new zone with country type members
And the store has a zone "North America" with code "NA"
And I am logged in as an administrator
@ui @javascript
@ui @javascript @api
Scenario: Adding a zone with a country type member
When I want to create a new zone consisting of country
And I name it "European Union"
@ -21,7 +21,7 @@ Feature: Adding a new zone with country type members
Then I should be notified that it has been successfully created
And the zone named "European Union" with the "France" country member should appear in the registry
@ui @javascript
@ui @javascript @api
Scenario: Adding a zone with province type member
When I want to create a new zone consisting of province
And I name it "United States"
@ -31,7 +31,7 @@ Feature: Adding a new zone with country type members
Then I should be notified that it has been successfully created
And the zone named "United States" with the "Alabama" province member should appear in the registry
@ui @javascript
@ui @javascript @api
Scenario: Adding a zone with zone type member
When I want to create a new zone consisting of zone
And I name it "America"
@ -41,7 +41,7 @@ Feature: Adding a new zone with country type members
Then I should be notified that it has been successfully created
And the zone named "America" with the "North America" zone member should appear in the registry
@ui @javascript
@ui @javascript @api
Scenario: Adding a zone with a country type member and a shipping scope
When I want to create a new zone consisting of country
And I name it "European Union"

View file

@ -9,7 +9,7 @@ Feature: Browsing zones
And the store also has a zone "South America" with code "SA"
And I am logged in as an administrator
@ui
@ui @api
Scenario: Browsing zones in store
When I want to see all zones in store
Then I should see 2 zones in the list

View file

@ -8,7 +8,7 @@ Feature: Deleting multiple zones
Given the store has zones "North America", "South America" and "Europe"
And I am logged in as an administrator
@ui
@ui @api
Scenario: Deleting multiple zones at once
When I browse zones
And I check the "North America" zone
@ -16,4 +16,4 @@ Feature: Deleting multiple zones
And I delete them
Then I should be notified that they have been successfully deleted
And I should see a single zone in the list
And I should see the zone "Europe" in the list
And I should see the zone named "Europe" in the list

View file

@ -11,13 +11,13 @@ Feature: Deleting a zone
And the store has "Sales Tax" tax rate of 20% for "Sports gear" within the "SA" zone
And I am logged in as an administrator
@ui
@ui @api
Scenario: Deleted zone should disappear from the registry
When I delete zone named "North America"
Then I should be notified that it has been successfully deleted
And the zone named "North America" should no longer exist in the registry
@ui
@ui @api
Scenario: Deleting zone with associated tax rates should not be possible
When I delete zone named "South America"
Then I should be notified that this zone cannot be deleted

View file

@ -13,7 +13,7 @@ Feature: Editing a zone
And the store has a zone "South America" with code "SA"
And I am logged in as an administrator
@ui @javascript
@ui @javascript @api
Scenario: Removing a country from a zone
Given the store has a zone "European Union" with code "EU"
And it has the "France" country member
@ -24,7 +24,7 @@ Feature: Editing a zone
Then I should be notified that it has been successfully edited
And this zone should have only the "France" country member
@ui @javascript
@ui @javascript @api
Scenario: Removing a province from a zone
Given the store has a zone "United States" with code "USA"
And it has the "Alabama" province member
@ -35,7 +35,7 @@ Feature: Editing a zone
Then I should be notified that it has been successfully edited
And this zone should have only the "Alabama" province member
@ui @javascript
@ui @javascript @api
Scenario: Removing a zone from a zone
Given the store has a zone "America" with code "AM"
And it has the zone named "North America"
@ -46,7 +46,7 @@ Feature: Editing a zone
Then I should be notified that it has been successfully edited
And this zone should have only the "South America" zone member
@ui
@ui @api
Scenario: Renaming a zone
Given the store has a zone "European Union" with code "EU"
And it has the "France" country member
@ -55,15 +55,3 @@ Feature: Editing a zone
And I save my changes
Then I should be notified that it has been successfully edited
And this zone name should be "EU"
@ui
Scenario: Seeing a disabled code field when editing a zone
Given the store has a zone "European Union" with code "EU"
When I want to modify the zone named "European Union"
Then the code field should be disabled
@ui
Scenario: Not seeing zone itself in member select when editing a zone of type zone
Given the store has a zone "European Union" with code "EU"
When I want to modify the zone named "European Union"
Then I can not add a zone "European Union"

View file

@ -10,7 +10,7 @@ Feature: Zone unique code validation
And this zone has the "United States" country member
And I am logged in as an administrator
@ui
@ui @api
Scenario: Trying to add zone with taken code
When I want to create a new zone consisting of country
And I specify its code as "US"

View file

@ -5,19 +5,20 @@ Feature: Zone validation
I want to be prevented from adding it without specifying required fields
Background:
Given the store has country "United States"
Given the store operates in "France" and "Germany"
And the store has country "United States"
And I am logged in as an administrator
@ui
@ui @api
Scenario: Trying to add a zone without specifying its code
When I want to create a new zone consisting of country
And I name it "European Union"
But I do not specify its code
And I try to add it
Then I should be notified that code is required
And zone with code "European Union" should not be added
And zone with name "European Union" should not be added
@ui
@ui @api
Scenario: Trying to add a zone without specifying its name
When I want to create a new zone consisting of country
And I specify its code as "EU"
@ -26,7 +27,7 @@ Feature: Zone validation
Then I should be notified that name is required
And zone with code "EU" should not be added
@ui
@ui @api
Scenario: Trying to add a zone without any countries
When I want to create a new zone consisting of country
And I name it "European Union"
@ -36,14 +37,27 @@ Feature: Zone validation
Then I should be notified that at least one zone member is required
And zone with name "European Union" should not be added
@ui @api
Scenario: Being unable to edit code of an existing zone
Given the store has a zone "European Union" with code "EU"
And it has the "France" country member
When I want to modify the zone named "European Union"
Then I should not be able to edit its code
@ui @api
Scenario: Being unable to add itself to members during editing an existing zone
Given the store has a zone "European Union" with code "EU"
When I want to modify the zone named "European Union"
Then I can not add a zone "European Union"
@ui
Scenario: Seeing a disabled type field when adding country type zone
When I want to create a new zone consisting of country
Then the type field should be disabled
Then I should not be able to edit its type
And it should be of country type
@ui
Scenario: Seeing a disabled type field when adding province type zone
When I want to create a new zone consisting of province
Then the type field should be disabled
Then I should not be able to edit its type
And it should be of province type

View file

@ -0,0 +1,468 @@
<?php
/*
* This file is part of the Sylius package.
*
* (c) Paweł Jędrzejewski
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Sylius\Behat\Context\Api\Admin;
use ApiPlatform\Core\Api\IriConverterInterface;
use Behat\Behat\Context\Context;
use Sylius\Behat\Client\ApiClientInterface;
use Sylius\Behat\Client\ResponseCheckerInterface;
use Sylius\Behat\Service\SharedStorageInterface;
use Sylius\Component\Addressing\Model\CountryInterface;
use Sylius\Component\Addressing\Model\ProvinceInterface;
use Sylius\Component\Addressing\Model\ZoneInterface;
use Sylius\Component\Addressing\Model\ZoneMember;
use Sylius\Component\Addressing\Model\ZoneMemberInterface;
use Webmozart\Assert\Assert;
final class ManagingZonesContext implements Context
{
/** @var ApiClientInterface */
private $client;
/** @var ResponseCheckerInterface */
private $responseChecker;
/** @var SharedStorageInterface */
private $sharedStorage;
/** @var IriConverterInterface */
private $iriConverter;
public function __construct(
ApiClientInterface $client,
ResponseCheckerInterface $responseChecker,
SharedStorageInterface $sharedStorage,
IriConverterInterface $iriConverter
) {
$this->client = $client;
$this->responseChecker = $responseChecker;
$this->sharedStorage = $sharedStorage;
$this->iriConverter = $iriConverter;
}
/**
* @When I want to create a new zone consisting of :memberType
*/
public function iWantToCreateANewZoneConsistingOfCountry(string $memberType): void
{
$this->client->buildCreateRequest();
$this->client->addRequestData('type', $memberType);
}
/**
* @When I name it :name
* @When I rename it to :name
*/
public function iNameIt(string $name): void
{
$this->client->addRequestData('name', $name);
}
/**
* @When I specify its code as :code
*/
public function iSpecifyItsCodeAs(string $code): void
{
$this->client->addRequestData('code', $code);
}
/**
* @When I do not specify its :type
* @When I do not add a country member
*/
public function iDoNotSpecifyItsField(): void
{
// Intentionally left blank
}
/**
* @When I add a country :country
*/
public function iAddACountry(CountryInterface $country): void
{
$this->client->addSubResourceData('members', [
'code' => $country->getCode(),
]);
}
/**
* @When I add a province :province
*/
public function iAddAProvince(ProvinceInterface $province): void
{
$this->client->addSubResourceData('members', [
'code' => $province->getCode(),
]);
}
/**
* @When I add a zone :zone
*/
public function iAddAZone(ZoneInterface $zone): void
{
$this->client->addSubResourceData('members', [
'code' => $zone->getCode(),
]);
}
/**
* @When I select its scope as :scope
*/
public function iSelectItsScopeAs(string $scope): void
{
$this->client->addRequestData('scope', $scope);
}
/**
* @When I add it
* @When I try to add it
*/
public function iAddIt(): void
{
$this->client->create();
}
/**
* @When I want to see all zones in store
* @When I browse zones
*/
public function iWantToSeeAllZonesInStore(): void
{
$this->client->index();
}
/**
* @When I delete zone named :zone
*/
public function iDeleteZoneNamed(ZoneInterface $zone): void
{
$this->client->delete($zone->getCode());
}
/**
* @When I check the :zone zone
* @When I check also the :zone zone
*/
public function iCheckTheZone(ZoneInterface $zone): void
{
$ZoneToDelete = [];
if ($this->sharedStorage->has('zone_to_delete')) {
$ZoneToDelete = $this->sharedStorage->get('zone_to_delete');
}
$ZoneToDelete[] = $zone->getCode();
$this->sharedStorage->set('zone_to_delete', $ZoneToDelete);
}
/**
* @When I delete them
*/
public function iDeleteThem(): void
{
foreach ($this->sharedStorage->get('zone_to_delete') as $code) {
$this->client->delete($code);
}
}
/**
* @When I want to modify the zone named :zone
*/
public function iWantToModifyTheZoneNamed(ZoneInterface $zone): void
{
$this->client->buildUpdateRequest($zone->getCode());
}
/**
* @When I remove the :country country member
*/
public function iRemoveTheCountryMember(CountryInterface $country): void
{
$this->removeZoneMember($country);
}
/**
* @When I remove the :province province member
*/
public function iRemoveTheProvinceMember(ProvinceInterface $province): void
{
$this->removeZoneMember($province);
}
/**
* @When I remove the :zone zone member
*/
public function iRemoveTheZoneMember(ZoneInterface $zone): void
{
$this->removeZoneMember($zone);
}
/**
* @When I save my changes
*/
public function iSaveMyChanges(): void
{
$this->client->update();
}
/**
* @Then the zone named :zone with the :country country member should appear in the registry
*/
public function theZoneNamedWithTheCountryMemberShouldAppearInTheRegistry(
ZoneInterface $zone,
CountryInterface $country
): void {
Assert::true($this->responseChecker->hasItemWithValue(
$this->client->subResourceIndex('members', $zone->getCode()),
'code',
$country->getCode()
));
}
/**
* @Then I should not be able to edit its code
*/
public function iShouldNotBeAbleToEditItsCode(): void
{
$this->client->addRequestData('code', 'NEW_CODE');
Assert::false(
$this->responseChecker->hasValue($this->client->update(), 'code', 'NEW_CODE'),
'The code field with value NEW_CODE exists'
);
}
/**
* @Then I can not add a zone :zone
*/
public function iCanNotAddAZone(ZoneInterface $zone): void
{
$this->client->addSubResourceData('members', [
'code' => $zone->getCode(),
]);
Assert::contains(
$this->responseChecker->getError($this->client->update()),
'members: Zone member cannot be the same as a zone.'
);
}
/**
* @Then the zone named :zone with the :province province member should appear in the registry
*/
public function theZoneNamedWithTheProvinceMemberShouldAppearInTheRegistry(
ZoneInterface $zone,
ProvinceInterface $province
): void {
Assert::true($this->responseChecker->hasItemWithValue(
$this->client->subResourceIndex('members', $zone->getCode()),
'code',
$province->getCode()
));
}
/**
* @Then the zone named :zone with the :otherZone zone member should appear in the registry
*/
public function theZoneNamedWithTheZoneMemberShouldAppearInTheRegistry(
ZoneInterface $zone,
ZoneInterface $otherZone
): void {
Assert::true($this->responseChecker->hasItemWithValue(
$this->client->subResourceIndex('members', $zone->getCode()),
'code',
$otherZone->getCode()
));
}
/**
* @Then its scope should be :scope
*/
public function itsScopeShouldBe(string $scope): void
{
Assert::true(
$this->responseChecker->hasValue($this->client->show('EU'), 'scope', $scope),
sprintf('Its Zone does not have %s scope', $scope)
);
}
/**
* @Then I should see :count zones in the list
* @Then I should see a single zone in the list
*/
public function iShouldSeeZonesInTheList(int $count = 1): void
{
Assert::same($this->responseChecker->countCollectionItems($this->client->index()), $count);
}
/**
* @Then I should see the zone named :name in the list
* @Then I should still see the zone named :name in the list
*/
public function iShouldSeeTheZoneNamedInTheList(string $name): void
{
Assert::true(
$this->responseChecker->hasItemWithValue($this->client->index(), 'name', $name),
sprintf('There is no zone with name "%s"', $name)
);
}
/**
* @Then there should still be only one zone with code :code
*/
public function thereShouldStillBeOnlyOneZoneWithCode(string $code): void
{
Assert::count(
$this->responseChecker->getCollectionItemsWithValue($this->client->index(), 'code', $code),
1,
sprintf('There should be only one zone with code "%s"', $code)
);
}
/**
* @Then the zone named :name should no longer exist in the registry
*/
public function theZoneNamedShouldNoLongerExistInTheRegistry(string $name): void
{
Assert::false(
$this->responseChecker->hasItemWithValue($this->client->index(), 'name', $name),
sprintf('Zone with name %s exists', $name)
);
}
/**
* @Then /^zone with (code|name) "([^"]*)" should not be added$/
*/
public function zoneShouldNotBeAdded(string $field, string $value): void
{
Assert::false(
$this->responseChecker->hasItemWithValue($this->client->index(), $field, $value),
sprintf('Zone with %s %s exists', $field, $value)
);
}
/**
* @Then /^(this zone) should have only (the "([^"]*)" (?:country|province|zone) member)$/
*/
public function thisZoneShouldHaveOnlyTheProvinceMember(ZoneInterface $zone, ZoneMemberInterface $zoneMember): void
{
Assert::true($this->responseChecker->hasItemWithValue(
$this->client->subResourceIndex('members', $zone->getCode()),
'code',
$zoneMember->getCode()
));
Assert::same(
$this->responseChecker->countCollectionItems($this->client->subResourceIndex('members', $zone->getCode())),
1
);
}
/**
* @Then /^(this zone) name should be "([^"]*)"$/
*/
public function thisZoneNameShouldBe(ZoneInterface $zone, string $name): void
{
Assert::true(
$this->responseChecker->hasValue($this->client->show($zone->getCode()), 'name', $name),
sprintf('Its Zone does not have name %s.', $name)
);
}
/**
* @Then I should be notified that it has been successfully created
*/
public function iShouldBeNotifiedThatItHasBeenSuccessfullyCreated(): void
{
Assert::true(
$this->responseChecker->isCreationSuccessful($this->client->getLastResponse()),
'Zone could not be created'
);
}
/**
* @Then I should be notified that it has been successfully edited
*/
public function iShouldBeNotifiedThatItHasBeenSuccessfullyEdited(): void
{
Assert::true(
$this->responseChecker->isUpdateSuccessful($this->client->getLastResponse()),
'Zone could not be edited'
);
}
/**
* @Then I should be notified that it has been successfully deleted
* @Then I should be notified that they have been successfully deleted
*/
public function iShouldBeNotifiedThatItHasBeenSuccessfullyDeleted(): void
{
Assert::true($this->responseChecker->isDeletionSuccessful(
$this->client->getLastResponse()),
'Zone could not be deleted'
);
}
/**
* @Then I should be notified that this zone cannot be deleted
*/
public function iShouldBeNotifiedThatThisZoneCannotBeDeleted(): void
{
Assert::false(
$this->responseChecker->isDeletionSuccessful($this->client->getLastResponse()),
'Zone can be deleted, but it should not'
);
}
/**
* @Then I should be notified that zone with this code already exists
*/
public function iShouldBeNotifiedThatZoneWithThisCodeAlreadyExists(): void
{
Assert::contains(
$this->responseChecker->getError($this->client->getLastResponse()),
'code: Zone code must be unique.'
);
}
/**
* @Then /^I should be notified that (code|name) is required$/
*/
public function iShouldBeNotifiedThatIsRequired(string $element): void
{
Assert::contains(
$this->responseChecker->getError($this->client->getLastResponse()),
sprintf('Please enter zone %s.', $element)
);
}
/**
* @Then I should be notified that at least one zone member is required
*/
public function iShouldBeNotifiedThatAtLeastOneZoneMemberIsRequired(): void
{
Assert::contains(
$this->responseChecker->getError($this->client->getLastResponse()),
'members: Please add at least 1 zone member.'
);
}
/**
* @param CountryInterface|ZoneInterface|ProvinceInterface $objectToRemove
*/
private function removeZoneMember($objectToRemove): void
{
$iri = $this->iriConverter->getItemIriFromResourceClass(ZoneMember::class, ['code' => $objectToRemove->getCode()]);
$this->client->removeSubResource('members', $iri);
}
}

View file

@ -31,19 +31,24 @@ final class ZoneContext implements Context
/**
* @Transform /^"([^"]+)" zone$/
* @Transform /^zone "([^"]+)"$/
* @Transform :zone
*/
public function getZoneByCode(string $code): ZoneInterface
{
return $this->getZoneBy(['code' => $code]);
}
/**
* @Transform /^zone named "([^"]+)"$/
* @Transform :zone
* @Transform :otherZone
*/
public function getZoneByName(string $name): ZoneInterface
public function getZone(string $codeOrName): ZoneInterface
{
return $this->getZoneBy(['name' => $name]);
$zone = $this->zoneRepository->findOneBy(['code' => $codeOrName]);
if (null !== $zone) {
return $zone;
}
$zone = $this->zoneRepository->findOneBy(['name' => $codeOrName]);
Assert::notNull(
$zone,
'Zone does not exist.'
);
return $zone;
}
/**
@ -59,15 +64,4 @@ final class ZoneContext implements Context
return $zone;
}
private function getZoneBy(array $parameters): ZoneInterface
{
$existingZone = $this->zoneRepository->findOneBy($parameters);
Assert::notNull(
$existingZone,
'Zone does not exist.'
);
return $existingZone;
}
}

View file

@ -231,9 +231,9 @@ final class ManagingZonesContext implements Context
}
/**
* @Then /^the code field should be disabled$/
* @Then I should not be able to edit its code
*/
public function theCodeFieldShouldBeDisabled()
public function iShouldNotBeAbleToEditItsCode(): void
{
Assert::true($this->updatePage->isCodeDisabled());
}
@ -289,9 +289,9 @@ final class ManagingZonesContext implements Context
}
/**
* @Then the type field should be disabled
* @Then I should not be able to edit its type
*/
public function theTypeFieldShouldBeDisabled()
public function iShouldNotBeAbleToEditItsType(): void
{
Assert::true($this->createPage->isTypeFieldDisabled());
}
@ -329,14 +329,6 @@ final class ManagingZonesContext implements Context
Assert::true($this->indexPage->isSingleResourceOnPage(['code' => $zone->getCode(), 'name' => $zone->getName()]));
}
/**
* @Then I should see the zone :zoneName in the list
*/
public function iShouldSeeTheZoneInTheList(string $zoneName): void
{
Assert::true($this->indexPage->isSingleResourceOnPage(['name' => $zoneName]));
}
/**
* @Then I should be notified that this zone cannot be deleted
*/

View file

@ -150,7 +150,7 @@ final class CheckoutAddressingContext implements Context
/**
* @When I specify shipping country province as :provinceName
*/
public function iSpecifyShippingCountryProvinceAs($provinceName)
public function iSpecifyShippingCountryProvinceAs(string $provinceName): void
{
$this->addressPage->selectShippingAddressProvince($provinceName);
}
@ -158,7 +158,7 @@ final class CheckoutAddressingContext implements Context
/**
* @When I specify billing country province as :provinceName
*/
public function iSpecifyBillingCountryProvinceAs($provinceName)
public function iSpecifyBillingCountryProvinceAs(string $provinceName): void
{
$this->addressPage->selectBillingAddressProvince($provinceName);
}

View file

@ -139,6 +139,12 @@
<argument>taxons</argument>
</service>
<service id="sylius.behat.api_platform_client.zone" class="Sylius\Behat\Client\ApiPlatformClient">
<argument type="service" id="test.client" />
<argument type="service" id="sylius.behat.shared_storage" />
<argument>zones</argument>
</service>
<service id="Sylius\Behat\Client\ResponseCheckerInterface" class="Sylius\Behat\Client\ResponseChecker">
<argument type="service" id="test.client" />
</service>

View file

@ -140,5 +140,12 @@
<argument type="service" id="api_platform.iri_converter" />
<argument type="service" id="sylius.behat.shared_storage" />
</service>
<service id="sylius.behat.context.api.admin.managing_zones" class="Sylius\Behat\Context\Api\Admin\ManagingZonesContext">
<argument type="service" id="sylius.behat.api_platform_client.zone" />
<argument type="service" id="Sylius\Behat\Client\ResponseCheckerInterface" />
<argument type="service" id="sylius.behat.shared_storage" />
<argument type="service" id="api_platform.iri_converter" />
</service>
</services>
</container>

View file

@ -5,6 +5,7 @@ imports:
- suites/api/addressing/managing_countries.yml
- suites/api/account/customer_registration.yml
- suites/api/account/login.yml
- suites/api/addressing/managing_zones.yml
- suites/api/admin/login.yml
- suites/api/channel/managing_channels.yml
- suites/api/currency/managing_currencies.yml

View file

@ -0,0 +1,25 @@
# This file is part of the Sylius package.
# (c) Paweł Jędrzejewski
default:
suites:
api_managing_zones:
contexts:
- sylius.behat.context.hook.doctrine_orm
- sylius.behat.context.transform.country
- sylius.behat.context.transform.province
- sylius.behat.context.transform.shared_storage
- sylius.behat.context.transform.zone_member
- sylius.behat.context.transform.zone
- sylius.behat.context.setup.admin_api_security
- sylius.behat.context.setup.geographical
- sylius.behat.context.setup.taxation
- sylius.behat.context.setup.zone
- sylius.behat.context.api.admin.managing_zones
filters:
tags: "@managing_zones && @api"
javascript: false

View file

@ -56,5 +56,9 @@
<argument type="service" id="sylius.repository.province" />
<tag name="validator.constraint_validator" alias="sylius_province_address_validator" />
</service>
<service id="sylius.validator.zone_cannot_contain_itself" class="Sylius\Bundle\AddressingBundle\Validator\Constraints\ZoneCannotContainItselfValidator">
<tag name="validator.constraint_validator" alias="sylius_zone_cannot_contain_itself_validator" />
</service>
</services>
</container>

View file

@ -53,6 +53,12 @@
<option name="message">sylius.zone_member.unique</option>
<option name="groups">sylius</option>
</constraint>
<constraint name="Sylius\Bundle\AddressingBundle\Validator\Constraints\ZoneCannotContainItself">
<option name="message">sylius.zone_member.cannot_be_the_same_as_zone</option>
<option name="groups">
<value>sylius</value>
</option>
</constraint>
<constraint name="Valid" />
<constraint name="Count">
<option name="min">1</option>

View file

@ -56,6 +56,7 @@ sylius:
min_length: Zone name must be at least {{ limit }} characters long|Zone name must be at least {{ limit }} characters long.
not_blank: Please enter zone name.
zone_member:
cannot_be_the_same_as_zone: Zone member cannot be the same as a zone.
code:
not_blank: Please choose a zone member.
regex: Zone member code can only be comprised of letters, numbers, dashes and underscores.

View file

@ -0,0 +1,27 @@
<?php
/*
* This file is part of the Sylius package.
*
* (c) Paweł Jędrzejewski
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Sylius\Bundle\AddressingBundle\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
final class ZoneCannotContainItself extends Constraint
{
/** @var string */
public $message = 'sylius.zone_member.cannot_be_the_same_as_zone';
public function validatedBy(): string
{
return 'sylius_zone_cannot_contain_itself_validator';
}
}

View file

@ -0,0 +1,39 @@
<?php
/*
* This file is part of the Sylius package.
*
* (c) Paweł Jędrzejewski
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Sylius\Bundle\AddressingBundle\Validator\Constraints;
use Sylius\Component\Addressing\Model\ZoneMemberInterface;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Webmozart\Assert\Assert;
final class ZoneCannotContainItselfValidator extends ConstraintValidator
{
public function validate($value, Constraint $constraint): void
{
if ($value === null) {
return;
}
/** @var ZoneCannotContainItself $constraint */
Assert::isInstanceOf($constraint, ZoneCannotContainItself::class);
/** @var ZoneMemberInterface $zoneMember */
foreach ($value as $zoneMember) {
if ($zoneMember->getCode() === $zoneMember->getBelongsTo()->getCode()) {
$this->context->addViolation($constraint->message);
}
}
}
}

View file

@ -0,0 +1,80 @@
<?php
/*
* This file is part of the Sylius package.
*
* (c) Paweł Jędrzejewski
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare(strict_types=1);
namespace spec\Sylius\Bundle\AddressingBundle\Validator\Constraints;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
use Sylius\Bundle\AddressingBundle\Validator\Constraints\ZoneCannotContainItself;
use Sylius\Component\Addressing\Model\ZoneInterface;
use Sylius\Component\Addressing\Model\ZoneMemberInterface;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidatorInterface;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
final class ZoneCannotContainItselfValidatorSpec extends ObjectBehavior
{
function let(ExecutionContextInterface $executionContext): void
{
$this->beConstructedWith();
$this->initialize($executionContext);
}
function it_is_a_constraint_validator(): void
{
$this->shouldImplement(ConstraintValidatorInterface::class);
}
function it_does_nothing_if_value_is_null(ExecutionContextInterface $executionContext): void
{
$executionContext->addViolation(Argument::cetera())->shouldNotBeCalled();
$this->validate(null, new ZoneCannotContainItself());
}
function it_throws_an_exception_if_constraint_is_not_of_expected_type(): void
{
$this
->shouldThrow(\InvalidArgumentException::class)
->during('validate', ['', new class() extends Constraint {}])
;
}
function it_does_not_add_violation_if_zone_does_not_contain_itself_in_members(
ExecutionContextInterface $executionContext,
ZoneInterface $zone,
ZoneMemberInterface $zoneMember
): void {
$zone->getCode()->willReturn('WORLD');
$zoneMember->getCode()->willReturn('EU');
$zoneMember->getBelongsTo()->willReturn($zone);
$executionContext->addViolation(Argument::cetera())->shouldNotBeCalled();
$this->validate([$zoneMember], new ZoneCannotContainItself());
}
function it_adds_violation_if_zone_contains_itself_in_members(
ExecutionContextInterface $executionContext,
ZoneInterface $zone,
ZoneMemberInterface $zoneMember
): void {
$zone->getCode()->willReturn('EU');
$zoneMember->getCode()->willReturn('EU');
$zoneMember->getBelongsTo()->willReturn($zone);
$executionContext->addViolation(Argument::cetera())->shouldBeCalled();
$this->validate([$zoneMember], new ZoneCannotContainItself());
}
}

View file

@ -22,18 +22,44 @@
</attribute>
</attribute>
<collectionOperations />
<attribute name="validation_groups">sylius</attribute>
<collectionOperations>
<collectionOperation name="get">
<attribute name="security">is_granted('ROLE_API_ACCESS')</attribute>
</collectionOperation>
<collectionOperation name="post">
<attribute name="security">is_granted('ROLE_API_ACCESS')</attribute>
<attribute name="denormalization_context">
<attribute name="groups">zone:create</attribute>
</attribute>
</collectionOperation>
</collectionOperations>
<itemOperations>
<itemOperation name="get">
<attribute name="security">is_granted('ROLE_API_ACCESS')</attribute>
</itemOperation>
<itemOperation name="put">
<attribute name="security">is_granted('ROLE_API_ACCESS')</attribute>
<attribute name="denormalization_context">
<attribute name="groups">zone:update</attribute>
</attribute>
</itemOperation>
<itemOperation name="delete">
<attribute name="security">is_granted('ROLE_API_ACCESS')</attribute>
</itemOperation>
</itemOperations>
<property name="id" identifier="false" writable="false" />
<property name="createdAt" writable="false" />
<property name="updatedAt" writable="false" />
<property name="code" identifier="true" required="true" />
<property name="name" writable="false" />
<property name="name" required="true" />
<property name="scope" readable="true" writable="true" />
<property name="type" readable="true" writable="true" />
<property name="members" readable="true" writable="true">
<subresource resourceClass="Sylius\Component\Addressing\Model\ZoneMember" collection="true" />
</property>
</resource>
</resources>

View file

@ -0,0 +1,45 @@
<?xml version="1.0" ?>
<!--
This file is part of the Sylius package.
(c) Paweł Jędrzejewski
For the full copyright and license information, please view the LICENSE
file that was distributed with this source code.
-->
<resources xmlns="https://api-platform.com/schema/metadata"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://api-platform.com/schema/metadata https://api-platform.com/schema/metadata/metadata-2.0.xsd"
>
<resource class="%sylius.model.zone_member.class%" shortName="ZoneMember">
<attribute name="normalization_context">
<attribute name="groups">
<attribute>zone_member:read</attribute>
</attribute>
</attribute>
<attribute name="denormalization_context">
<attribute name="groups">
<attribute>zone_member:write</attribute>
</attribute>
</attribute>
<attribute name="validation_groups">sylius</attribute>
<collectionOperations />
<itemOperations>
<itemOperation name="get">
<attribute name="security">is_granted('ROLE_API_ACCESS')</attribute>
</itemOperation>
</itemOperations>
<property name="id" identifier="false" writable="false" />
<property name="code" identifier="true" required="true" />
<property name="belongsTo" readable="true" writable="true" />
</resource>
</resources>

View file

@ -0,0 +1,51 @@
<?xml version="1.0" ?>
<!--
This file is part of the Sylius package.
(c) Paweł Jędrzejewski
For the full copyright and license information, please view the LICENSE
file that was distributed with this source code.
-->
<serializer xmlns="http://symfony.com/schema/dic/serializer-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/serializer-mapping https://symfony.com/schema/dic/serializer-mapping/serializer-mapping-1.0.xsd"
>
<class name="Sylius\Component\Addressing\Model\Zone">
<attribute name="id">
<group>zone:read</group>
</attribute>
<attribute name="code">
<group>zone:read</group>
<group>zone:create</group>
</attribute>
<attribute name="name">
<group>zone:read</group>
<group>zone:create</group>
<group>zone:update</group>
</attribute>
<attribute name="type">
<group>zone:read</group>
<group>zone:create</group>
</attribute>
<attribute name="scope">
<group>zone:read</group>
<group>zone:create</group>
</attribute>
<attribute name="createdAt">
<group>zone:read</group>
</attribute>
<attribute name="updatedAt">
<group>zone:read</group>
</attribute>
<attribute name="members">
<group>zone:read</group>
<group>zone:create</group>
<group>zone:update</group>
</attribute>
</class>
</serializer>

View file

@ -0,0 +1,29 @@
<?xml version="1.0" ?>
<!--
This file is part of the Sylius package.
(c) Paweł Jędrzejewski
For the full copyright and license information, please view the LICENSE
file that was distributed with this source code.
-->
<serializer xmlns="http://symfony.com/schema/dic/serializer-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/serializer-mapping https://symfony.com/schema/dic/serializer-mapping/serializer-mapping-1.0.xsd"
>
<class name="Sylius\Component\Addressing\Model\ZoneMember">
<attribute name="id">
<group>zone_member:read</group>
</attribute>
<attribute name="code">
<group>zone:create</group>
<group>zone:update</group>
<group>zone_member:read</group>
<group>zone_member:write</group>
</attribute>
</class>
</serializer>

View file

@ -21,9 +21,10 @@
<import resource="services/context_builders.xml" />
<import resource="services/data_persisters.xml" />
<import resource="services/data_providers.xml" />
<import resource="services/event_subscribers.xml"/>
<import resource="services/filters.xml"/>
<import resource="services/providers.xml"/>
<import resource="services/event_subscribers.xml" />
<import resource="services/filters.xml" />
<import resource="services/providers.xml" />
<import resource="services/validator.xml" />
</imports>
<services>
@ -60,11 +61,5 @@
>
<argument type="service" id="sylius.api.swagger_shop_authentication_documentation_normalizer.inner" />
</service>
<service id="sylius.validator.unique_shop_user_email" class="Sylius\Bundle\ApiBundle\Validator\Constraints\UniqueShopUserEmailValidator">
<argument type="service" id="sylius.canonicalizer" />
<argument type="service" id="sylius.repository.shop_user" />
<tag name="validator.constraint_validator" alias="sylius.validator.unique_shop_user_email" />
</service>
</services>
</container>

View file

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
This file is part of the Sylius package.
(c) Paweł Jędrzejewski
For the full copyright and license information, please view the LICENSE
file that was distributed with this source code.
-->
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"
>
<services>
<service id="sylius.validator.unique_shop_user_email" class="Sylius\Bundle\ApiBundle\Validator\Constraints\UniqueShopUserEmailValidator">
<argument type="service" id="sylius.canonicalizer" />
<argument type="service" id="sylius.repository.shop_user" />
<tag name="validator.constraint_validator" alias="sylius.validator.unique_shop_user_email" />
</service>
</services>
</container>