[API] Promotion Retrieval and Removal (#16613)

| Q               | A
|-----------------|-----
| Branch?         | api-platform-3 <!-- see the comment below -->
| Bug fix?        | no
| New feature?    | yes
| BC breaks?      | no
| Deprecations? | no<!-- don't forget to update the UPGRADE-*.md file
-->
| License         | MIT

<!--
 - Bug fixes must be submitted against the 1.13 branch
 - Features and deprecations must be submitted against the 1.14 branch
- Features, removing deprecations and BC breaks must be submitted
against the 2.0 branch
 - Make sure that the correct base branch is set

To be sure you are not breaking any Backward Compatibilities, check the
documentation:

https://docs.sylius.com/en/latest/book/organization/backward-compatibility-promise.html
-->

Current endpoints state:
<img width="629" alt="image"
src="https://github.com/user-attachments/assets/c26c5b91-eabc-4996-b33c-f105b691bfc5">

<img width="1401" alt="image"
src="https://github.com/user-attachments/assets/3a9ed70f-f9e4-4796-9a6c-42ddf4efb927">

<img width="1224" alt="image"
src="https://github.com/user-attachments/assets/877aac05-a8b1-43bd-b58f-f0ec649cd724">
This commit is contained in:
Karol 2024-07-24 08:11:56 +02:00 committed by GitHub
commit cebe951f68
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
22 changed files with 320 additions and 369 deletions

View file

@ -35,28 +35,6 @@ final class PromotionsTest extends JsonApiTestCase
{
use AdminUserLoginTrait;
/** @test */
public function it_gets_a_promotion(): void
{
$fixtures = $this->loadFixturesFromFiles(['authentication/api_administrator.yaml', 'channel.yaml', 'promotion/promotion.yaml']);
$header = array_merge($this->logInAdminUser('api@example.com'), self::CONTENT_TYPE_HEADER);
/** @var PromotionInterface $promotion */
$promotion = $fixtures['promotion_50_off'];
$this->client->request(
method: 'GET',
uri: sprintf('/api/v2/admin/promotions/%s', $promotion->getCode()),
server: $header,
);
$this->assertResponse(
$this->client->getResponse(),
'admin/promotion/get_promotion_response',
Response::HTTP_OK,
);
}
/** @test */
public function it_gets_promotions(): void
{
@ -625,44 +603,4 @@ final class PromotionsTest extends JsonApiTestCase
Response::HTTP_OK,
);
}
/** @test */
public function it_deletes_a_promotion(): void
{
$fixtures = $this->loadFixturesFromFiles(['authentication/api_administrator.yaml', 'channel.yaml', 'promotion/promotion.yaml']);
$header = array_merge($this->logInAdminUser('api@example.com'), self::CONTENT_TYPE_HEADER);
/** @var PromotionInterface $promotion */
$promotion = $fixtures['promotion_50_off'];
$this->client->request(
method: 'DELETE',
uri: sprintf('/api/v2/admin/promotions/%s', $promotion->getCode()),
server: $header,
);
$this->assertResponseCode($this->client->getResponse(), Response::HTTP_NO_CONTENT);
}
/** @test */
public function it_does_not_delete_the_promotion_in_use(): void
{
$fixtures = $this->loadFixturesFromFiles([
'authentication/api_administrator.yaml',
'channel.yaml',
'promotion/promotion.yaml',
'promotion/promotion_order.yaml',
]);
$header = array_merge($this->logInAdminUser('api@example.com'), self::CONTENT_TYPE_HEADER);
/** @var PromotionInterface $promotion */
$promotion = $fixtures['promotion_50_off'];
$this->client->request(
method: 'DELETE',
uri: sprintf('/api/v2/admin/promotions/%s', $promotion->getCode()),
server: $header,
);
$this->assertResponseCode($this->client->getResponse(), Response::HTTP_UNPROCESSABLE_ENTITY);
}
}

View file

@ -860,21 +860,6 @@ parameters:
count: 1
path: src/Sylius/Bundle/ApiBundle/DataPersister/PromotionCouponDataPersister.php
-
message: "#^Method Sylius\\\\Bundle\\\\ApiBundle\\\\DataPersister\\\\PromotionDataPersister\\:\\:persist\\(\\) has parameter \\$data with no type specified\\.$#"
count: 1
path: src/Sylius/Bundle/ApiBundle/DataPersister/PromotionDataPersister.php
-
message: "#^Method Sylius\\\\Bundle\\\\ApiBundle\\\\DataPersister\\\\PromotionDataPersister\\:\\:remove\\(\\) has parameter \\$data with no type specified\\.$#"
count: 1
path: src/Sylius/Bundle/ApiBundle/DataPersister/PromotionDataPersister.php
-
message: "#^Method Sylius\\\\Bundle\\\\ApiBundle\\\\DataPersister\\\\PromotionDataPersister\\:\\:supports\\(\\) has parameter \\$data with no type specified\\.$#"
count: 1
path: src/Sylius/Bundle/ApiBundle/DataPersister/PromotionDataPersister.php
-
message: "#^Method Sylius\\\\Bundle\\\\ApiBundle\\\\DataPersister\\\\TranslatableDataPersister\\:\\:supports\\(\\) has parameter \\$data with no type specified\\.$#"
count: 1

View file

@ -1,48 +0,0 @@
<?php
/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* 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\ApiBundle\DataPersister;
use ApiPlatform\Core\DataPersister\ContextAwareDataPersisterInterface;
use Doctrine\DBAL\Exception\ForeignKeyConstraintViolationException;
use Sylius\Bundle\ApiBundle\Exception\PromotionCannotBeRemoved;
use Sylius\Component\Core\Model\PromotionInterface;
final class PromotionDataPersister implements ContextAwareDataPersisterInterface
{
public function __construct(private ContextAwareDataPersisterInterface $decoratedDataPersister)
{
}
/** @param array<string, mixed> $context */
public function supports($data, array $context = []): bool
{
return $data instanceof PromotionInterface;
}
/** @param array<string, mixed> $context */
public function persist($data, array $context = [])
{
return $this->decoratedDataPersister->persist($data, $context);
}
/** @param array<string, mixed> $context */
public function remove($data, array $context = []): void
{
try {
$this->decoratedDataPersister->remove($data, $context);
} catch (ForeignKeyConstraintViolationException) {
throw new PromotionCannotBeRemoved();
}
}
}

View file

@ -29,7 +29,7 @@ final readonly class RestrictingFilterEagerLoadingExtension implements QueryColl
*/
public function __construct(
private QueryCollectionExtensionInterface $decoratedExtension,
private array $restrictedResources
private array $restrictedResources,
) {
}

View file

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
This file is part of the Sylius package.
(c) Sylius Sp. z o.o.
For the full copyright and license information, please view the LICENSE
file that was distributed with this source code.
-->
<properties xmlns="https://api-platform.com/schema/metadata/properties-3.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://api-platform.com/schema/metadata/properties-3.0 https://api-platform.com/schema/metadata/properties-3.0.xsd"
>
<property resource="Sylius\Component\Core\Model\Promotion" name="id" identifier="false" />
<property resource="Sylius\Component\Core\Model\Promotion" name="code" identifier="true" />
</properties>

View file

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
This file is part of the Sylius package.
(c) Sylius Sp. z o.o.
For the full copyright and license information, please view the LICENSE
file that was distributed with this source code.
-->
<properties xmlns="https://api-platform.com/schema/metadata/properties-3.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://api-platform.com/schema/metadata/properties-3.0 https://api-platform.com/schema/metadata/properties-3.0.xsd"
>
<property resource="Sylius\Component\Promotion\Model\PromotionTranslation" name="id" identifier="false" />
<property resource="Sylius\Component\Promotion\Model\PromotionTranslation" name="locale" identifier="true" />
</properties>

View file

@ -28,7 +28,6 @@
</validationContext>
<operations>
<operation class="ApiPlatform\Metadata\Get" uriTemplate="/admin/product-variant-translations/{id}">
<normalizationContext>
<values>
@ -41,8 +40,6 @@
</values>
</normalizationContext>
</operation>
</operations>
</resource>
</resources>

View file

@ -0,0 +1,40 @@
<?xml version="1.0" ?>
<!--
This file is part of the Sylius package.
(c) Sylius Sp. z o.o.
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/resources-3.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://api-platform.com/schema/metadata/resources-3.0 https://api-platform.com/schema/metadata/resources-3.0.xsd"
>
<resource class="Sylius\Component\Core\Model\Promotion">
<operations>
<operation class="ApiPlatform\Metadata\Get" uriTemplate="/admin/promotions/{code}">
<normalizationContext>
<values>
<value name="groups">
<values>
<value>sylius:admin:promotion:show</value>
</values>
</value>
</values>
</normalizationContext>
</operation>
<operation
class="ApiPlatform\Metadata\Delete"
uriTemplate="/admin/promotions/{code}"
processor="sylius_api.state_processor.admin.promotion.remove"
/>
</operations>
</resource>
</resources>

View file

@ -0,0 +1,29 @@
<?xml version="1.0" ?>
<!--
This file is part of the Sylius package.
(c) Sylius Sp. z o.o.
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/resources-3.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://api-platform.com/schema/metadata/resources-3.0 https://api-platform.com/schema/metadata/resources-3.0.xsd"
>
<resource class="Sylius\Component\Promotion\Model\PromotionTranslation">
<operations>
<operation class="ApiPlatform\Metadata\NotExposed" uriTemplate="/admin/promotions/{code}/translations/{localeCode}">
<uriVariables>
<uriVariable parameterName="code" fromClass="Sylius\Component\Core\Model\Promotion" fromProperty="translations"/>
<uriVariable parameterName="localeCode" fromClass="Sylius\Component\Promotion\Model\PromotionTranslation"/>
</uriVariables>
</operation>
</operations>
</resource>
</resources>

View file

@ -95,18 +95,6 @@ Example configuration for `order_fixed_discount` action type:
</collectionOperations>
<itemOperations>
<itemOperation name="admin_get">
<attribute name="method">GET</attribute>
<attribute name="normalization_context">
<attribute name="groups">
<attribute>admin:promotion:show</attribute>
<attribute>sylius:admin:promotion:show</attribute>
</attribute>
<!-- It auto-turned on when adding PATCH itemOperations: https://github.com/api-platform/core/issues/3600 -->
<attribute name="skip_null_values">false</attribute>
</attribute>
</itemOperation>
<itemOperation name="admin_put">
<attribute name="method">PUT</attribute>
<attribute name="denormalization_context">
@ -185,10 +173,6 @@ Example configuration for `order_fixed_discount` action type:
</attribute>
</attribute>
</itemOperation>
<itemOperation name="admin_delete">
<attribute name="method">DELETE</attribute>
</itemOperation>
</itemOperations>
<subresourceOperations>

View file

@ -1,34 +0,0 @@
<?xml version="1.0" ?>
<!--
This file is part of the Sylius package.
(c) Sylius Sp. z o.o.
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.promotion_translation.class%" shortName="PromotionTranslation">
<attribute name="validation_groups">sylius</attribute>
<collectionOperations />
<itemOperations>
<itemOperation name="admin_get">
<attribute name="method">GET</attribute>
<attribute name="path">/admin/promotion-translations/{id}</attribute>
</itemOperation>
</itemOperations>
<property name="id" identifier="true" writable="false" />
<property name="label" identifier="false" />
<property name="locale" identifier="false" required="true" />
</resource>
</resources>

View file

@ -13,195 +13,103 @@
>
<class name="Sylius\Component\Core\Model\Promotion">
<attribute name="id">
<group>admin:promotion:index</group>
<group>sylius:admin:promotion:index</group>
<group>admin:promotion:show</group>
<group>sylius:admin:promotion:show</group>
</attribute>
<attribute name="createdAt">
<group>admin:promotion:index</group>
<group>sylius:admin:promotion:index</group>
<group>admin:promotion:show</group>
<group>sylius:admin:promotion:show</group>
</attribute>
<attribute name="updatedAt">
<group>admin:promotion:index</group>
<group>sylius:admin:promotion:index</group>
<group>admin:promotion:show</group>
<group>sylius:admin:promotion:show</group>
</attribute>
<attribute name="name">
<group>admin:promotion:index</group>
<group>sylius:admin:promotion:index</group>
<group>admin:promotion:show</group>
<group>sylius:admin:promotion:show</group>
<group>admin:promotion:create</group>
<group>sylius:admin:promotion:create</group>
<group>admin:promotion:update</group>
<group>sylius:admin:promotion:update</group>
</attribute>
<attribute name="code">
<group>admin:promotion:index</group>
<group>sylius:admin:promotion:index</group>
<group>admin:promotion:show</group>
<group>sylius:admin:promotion:show</group>
<group>admin:promotion:create</group>
<group>sylius:admin:promotion:create</group>
</attribute>
<attribute name="description">
<group>admin:promotion:index</group>
<group>sylius:admin:promotion:index</group>
<group>admin:promotion:show</group>
<group>sylius:admin:promotion:show</group>
<group>admin:promotion:create</group>
<group>sylius:admin:promotion:create</group>
<group>admin:promotion:update</group>
<group>sylius:admin:promotion:update</group>
</attribute>
<attribute name="priority">
<group>admin:promotion:index</group>
<group>sylius:admin:promotion:index</group>
<group>admin:promotion:show</group>
<group>sylius:admin:promotion:show</group>
<group>admin:promotion:create</group>
<group>sylius:admin:promotion:create</group>
<group>admin:promotion:update</group>
<group>sylius:admin:promotion:update</group>
</attribute>
<attribute name="exclusive">
<group>admin:promotion:index</group>
<group>sylius:admin:promotion:index</group>
<group>admin:promotion:show</group>
<group>sylius:admin:promotion:show</group>
<group>admin:promotion:create</group>
<group>sylius:admin:promotion:create</group>
<group>admin:promotion:update</group>
<group>sylius:admin:promotion:update</group>
</attribute>
<attribute name="appliesToDiscounted">
<group>admin:promotion:index</group>
<group>sylius:admin:promotion:index</group>
<group>admin:promotion:show</group>
<group>sylius:admin:promotion:show</group>
<group>admin:promotion:create</group>
<group>sylius:admin:promotion:create</group>
<group>admin:promotion:update</group>
<group>sylius:admin:promotion:update</group>
</attribute>
<attribute name="usageLimit">
<group>admin:promotion:index</group>
<group>sylius:admin:promotion:index</group>
<group>admin:promotion:show</group>
<group>sylius:admin:promotion:show</group>
<group>admin:promotion:create</group>
<group>sylius:admin:promotion:create</group>
<group>admin:promotion:update</group>
<group>sylius:admin:promotion:update</group>
</attribute>
<attribute name="used">
<group>admin:promotion:index</group>
<group>sylius:admin:promotion:index</group>
<group>admin:promotion:show</group>
<group>sylius:admin:promotion:show</group>
</attribute>
<attribute name="startsAt">
<group>admin:promotion:index</group>
<group>sylius:admin:promotion:index</group>
<group>admin:promotion:show</group>
<group>sylius:admin:promotion:show</group>
<group>admin:promotion:create</group>
<group>sylius:admin:promotion:create</group>
<group>admin:promotion:update</group>
<group>sylius:admin:promotion:update</group>
</attribute>
<attribute name="endsAt">
<group>admin:promotion:index</group>
<group>sylius:admin:promotion:index</group>
<group>admin:promotion:show</group>
<group>sylius:admin:promotion:show</group>
<group>admin:promotion:create</group>
<group>sylius:admin:promotion:create</group>
<group>admin:promotion:update</group>
<group>sylius:admin:promotion:update</group>
</attribute>
<attribute name="couponBased">
<group>admin:promotion:index</group>
<group>sylius:admin:promotion:index</group>
<group>admin:promotion:show</group>
<group>sylius:admin:promotion:show</group>
<group>admin:promotion:create</group>
<group>sylius:admin:promotion:create</group>
<group>admin:promotion:update</group>
<group>sylius:admin:promotion:update</group>
</attribute>
<attribute name="coupons">
<group>admin:promotion:index</group>
<group>sylius:admin:promotion:index</group>
<group>admin:promotion:show</group>
<group>sylius:admin:promotion:show</group>
</attribute>
<attribute name="rules">
<group>admin:promotion:index</group>
<group>sylius:admin:promotion:index</group>
<group>admin:promotion:show</group>
<group>sylius:admin:promotion:show</group>
<group>admin:promotion:create</group>
<group>sylius:admin:promotion:create</group>
<group>admin:promotion:update</group>
<group>sylius:admin:promotion:update</group>
</attribute>
<attribute name="actions">
<group>admin:promotion:index</group>
<group>sylius:admin:promotion:index</group>
<group>admin:promotion:show</group>
<group>sylius:admin:promotion:show</group>
<group>admin:promotion:create</group>
<group>sylius:admin:promotion:create</group>
<group>admin:promotion:update</group>
<group>sylius:admin:promotion:update</group>
</attribute>
<attribute name="channels">
<group>admin:promotion:index</group>
<group>sylius:admin:promotion:index</group>
<group>admin:promotion:show</group>
<group>sylius:admin:promotion:show</group>
<group>admin:promotion:create</group>
<group>sylius:admin:promotion:create</group>
<group>admin:promotion:update</group>
<group>sylius:admin:promotion:update</group>
</attribute>
<attribute name="translations">
<group>admin:promotion:index</group>
<group>sylius:admin:promotion:index</group>
<group>admin:promotion:show</group>
<group>sylius:admin:promotion:show</group>
<group>admin:promotion:create</group>
<group>sylius:admin:promotion:create</group>
<group>admin:promotion:update</group>
<group>sylius:admin:promotion:update</group>
</attribute>
<attribute name="archivedAt">
<group>admin:promotion:read</group>
<group>sylius:admin:promotion:read</group>
<group>sylius:admin:promotion:index</group>
<group>sylius:admin:promotion:show</group>
</attribute>
</class>
</serializer>

View file

@ -16,29 +16,9 @@
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\Promotion\Model\PromotionTranslation">
<attribute name="id">
<group>admin:promotion:index</group>
<group>sylius:admin:promotion:index</group>
<group>admin:promotion:show</group>
<group>sylius:admin:promotion:show</group>
</attribute>
<attribute name="label">
<group>admin:promotion:index</group>
<group>sylius:admin:promotion:index</group>
<group>admin:promotion:show</group>
<group>sylius:admin:promotion:show</group>
<group>admin:promotion:create</group>
<group>sylius:admin:promotion:create</group>
<group>admin:promotion:update</group>
<group>sylius:admin:promotion:update</group>
</attribute>
<attribute name="locale">
<group>admin:promotion:create</group>
<group>sylius:admin:promotion:create</group>
<group>admin:promotion:update</group>
<group>sylius:admin:promotion:update</group>
</attribute>
</class>
</serializer>

View file

@ -71,11 +71,6 @@
<tag name="api_platform.data_persister" />
</service>
<service id="Sylius\Bundle\ApiBundle\DataPersister\PromotionDataPersister">
<argument type="service" id="api_platform.doctrine.orm.data_persister" />
<tag name="api_platform.data_persister" />
</service>
<service id="Sylius\Bundle\ApiBundle\DataPersister\CustomerDataPersister">
<argument type="service" id="api_platform.doctrine.orm.data_persister" />
<argument type="service" id="sylius.security.password_updater" />

View file

@ -63,6 +63,11 @@
<tag name="api_platform.state_processor" />
</service>
<service id="sylius_api.state_processor.admin.promotion.remove" class="Sylius\Bundle\ApiBundle\StateProcessor\Admin\Promotion\RemoveProcessor">
<argument type="service" id="api_platform.doctrine.orm.state.remove_processor" />
<tag name="api_platform.state_processor" />
</service>
<service id="sylius_api.state_processor.shop.address.persist" class="Sylius\Bundle\ApiBundle\StateProcessor\Shop\Address\PersistProcessor">
<argument type="service" id="api_platform.doctrine.orm.state.persist_processor" />
<argument type="service" id="Sylius\Bundle\ApiBundle\Context\UserContextInterface" />

View file

@ -93,8 +93,7 @@ final class ShippingMethodNormalizer implements NormalizerInterface, NormalizerA
$data instanceof ShippingMethodInterface &&
$this->sectionProvider->getSection() instanceof ShopApiSection &&
$operation instanceof HttpOperation &&
isset($operation->getUriVariables()['tokenValue']) &&
isset($operation->getUriVariables()['shipmentId'])
isset($operation->getUriVariables()['tokenValue'], $operation->getUriVariables()['shipmentId'])
;
}
}

View file

@ -0,0 +1,44 @@
<?php
/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* 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\ApiBundle\StateProcessor\Admin\Promotion;
use ApiPlatform\Metadata\DeleteOperationInterface;
use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProcessorInterface;
use Doctrine\DBAL\Exception\ForeignKeyConstraintViolationException;
use Sylius\Bundle\ApiBundle\Exception\PromotionCannotBeRemoved;
use Sylius\Component\Core\Model\PromotionInterface;
use Webmozart\Assert\Assert;
/**
* @implements ProcessorInterface<PromotionInterface>
*/
final readonly class RemoveProcessor implements ProcessorInterface
{
public function __construct(private ProcessorInterface $removeProcessor)
{
}
public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): void
{
Assert::isInstanceOf($data, PromotionInterface::class);
Assert::isInstanceOf($operation, DeleteOperationInterface::class);
try {
$this->removeProcessor->process($data, $operation, $uriVariables, $context);
} catch (ForeignKeyConstraintViolationException) {
throw new PromotionCannotBeRemoved();
}
}
}

View file

@ -1,66 +0,0 @@
<?php
/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* 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\ApiBundle\DataPersister;
use ApiPlatform\Core\DataPersister\ContextAwareDataPersisterInterface;
use Doctrine\DBAL\Exception\ForeignKeyConstraintViolationException;
use PhpSpec\ObjectBehavior;
use Sylius\Bundle\ApiBundle\Exception\PromotionCannotBeRemoved;
use Sylius\Component\Core\Model\PromotionInterface;
final class PromotionDataPersisterSpec extends ObjectBehavior
{
function let(ContextAwareDataPersisterInterface $dataPersister): void
{
$this->beConstructedWith($dataPersister);
}
function it_is_a_context_aware_persister(): void
{
$this->shouldImplement(ContextAwareDataPersisterInterface::class);
}
function it_supports_only_promotion(PromotionInterface $promotion): void
{
$this->supports(new \stdClass())->shouldReturn(false);
$this->supports($promotion)->shouldReturn(true);
}
function it_uses_inner_persister_to_persist_promotion(
ContextAwareDataPersisterInterface $dataPersister,
PromotionInterface $promotion,
): void {
$dataPersister->persist($promotion, [])->shouldBeCalled();
$this->persist($promotion);
}
function it_throws_cannot_be_removed_exception_if_constraint_fails_on_removal(
ContextAwareDataPersisterInterface $dataPersister,
PromotionInterface $promotion,
): void {
$dataPersister->remove($promotion, [])->willThrow(ForeignKeyConstraintViolationException::class);
$this->shouldThrow(PromotionCannotBeRemoved::class)->during('remove', [$promotion]);
}
function it_uses_inner_persister_to_remove_promotion(
ContextAwareDataPersisterInterface $dataPersister,
PromotionInterface $promotion,
): void {
$dataPersister->remove($promotion, [])->shouldBeCalled();
$this->remove($promotion);
}
}

View file

@ -0,0 +1,71 @@
<?php
/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* 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\ApiBundle\StateProcessor\Admin\Promotion;
use ApiPlatform\Metadata\DeleteOperationInterface;
use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProcessorInterface;
use Doctrine\DBAL\Exception\ForeignKeyConstraintViolationException;
use PhpSpec\ObjectBehavior;
use Sylius\Bundle\ApiBundle\Exception\PromotionCannotBeRemoved;
use Sylius\Component\Core\Model\PromotionInterface;
final class RemoveProcessorSpec extends ObjectBehavior
{
function let(ProcessorInterface $removeProcessor): void
{
$this->beConstructedWith($removeProcessor);
}
function it_is_a_processor_interface(): void
{
$this->shouldImplement(ProcessorInterface::class);
}
public function it_processes_remove_operation(
ProcessorInterface $removeProcessor,
Operation $operation,
PromotionInterface $Promotion,
) {
$operation->implement(DeleteOperationInterface::class);
$removeProcessor->process($Promotion, $operation, [], [])->willReturn(null);
$this->process($Promotion, $operation, [], [])->shouldReturn(null);
}
public function it_throws_exception_when_foreign_key_constraint_violation_occurs(
ProcessorInterface $removeProcessor,
Operation $operation,
PromotionInterface $Promotion,
) {
$operation->implement(DeleteOperationInterface::class);
$removeProcessor->process($Promotion, $operation, [], [])->willThrow(ForeignKeyConstraintViolationException::class);
$this->shouldThrow(PromotionCannotBeRemoved::class)->during('process', [$Promotion, $operation, [], []]);
}
public function it_throws_exception_if_operation_is_not_delete(
Operation $operation,
PromotionInterface $Promotion,
) {
$this->shouldThrow(\InvalidArgumentException::class)->during('process', [$Promotion, $operation, [], []]);
}
public function it_throws_exception_if_data_is_not_promotion_interface(
Operation $operation,
\stdClass $nonPromotion,
) {
$this->shouldThrow(\InvalidArgumentException::class)->during('process', [$nonPromotion, $operation, [], []]);
}
}

View file

@ -14,7 +14,6 @@ declare(strict_types=1);
namespace spec\Sylius\Bundle\ApiBundle\StateProvider\Common\Adjustment;
use ApiPlatform\Metadata\GetCollection;
use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProviderInterface;
use Doctrine\Common\Collections\ArrayCollection;
use PhpSpec\ObjectBehavior;

View file

@ -0,0 +1,86 @@
<?php
/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* 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\Tests\Api\Admin;
use Sylius\Component\Core\Model\PromotionInterface;
use Sylius\Tests\Api\JsonApiTestCase;
use Sylius\Tests\Api\Utils\AdminUserLoginTrait;
use Symfony\Component\HttpFoundation\Response;
final class PromotionsTest extends JsonApiTestCase
{
use AdminUserLoginTrait;
/** @test */
public function it_gets_a_promotion(): void
{
$fixtures = $this->loadFixturesFromFiles(['authentication/api_administrator.yaml', 'channel.yaml', 'promotion/promotion.yaml']);
$header = array_merge($this->logInAdminUser('api@example.com'), self::CONTENT_TYPE_HEADER);
/** @var PromotionInterface $promotion */
$promotion = $fixtures['promotion_50_off'];
$this->client->request(
method: 'GET',
uri: sprintf('/api/v2/admin/promotions/%s', $promotion->getCode()),
server: $header,
);
$this->assertResponse(
$this->client->getResponse(),
'admin/promotion/get_promotion_response',
Response::HTTP_OK,
);
}
/** @test */
public function it_deletes_a_promotion(): void
{
$fixtures = $this->loadFixturesFromFiles(['authentication/api_administrator.yaml', 'channel.yaml', 'promotion/promotion.yaml']);
$header = array_merge($this->logInAdminUser('api@example.com'), self::CONTENT_TYPE_HEADER);
/** @var PromotionInterface $promotion */
$promotion = $fixtures['promotion_50_off'];
$this->client->request(
method: 'DELETE',
uri: sprintf('/api/v2/admin/promotions/%s', $promotion->getCode()),
server: $header,
);
$this->assertResponseCode($this->client->getResponse(), Response::HTTP_NO_CONTENT);
}
/** @test */
public function it_does_not_delete_the_promotion_in_use(): void
{
$fixtures = $this->loadFixturesFromFiles([
'authentication/api_administrator.yaml',
'channel.yaml',
'promotion/promotion.yaml',
'promotion/promotion_order.yaml',
]);
$header = array_merge($this->logInAdminUser('api@example.com'), self::CONTENT_TYPE_HEADER);
/** @var PromotionInterface $promotion */
$promotion = $fixtures['promotion_50_off'];
$this->client->request(
method: 'DELETE',
uri: sprintf('/api/v2/admin/promotions/%s', $promotion->getCode()),
server: $header,
);
$this->assertResponseCode($this->client->getResponse(), Response::HTTP_UNPROCESSABLE_ENTITY);
}
}

View file

@ -25,9 +25,8 @@
"updatedAt": "@date@",
"translations": {
"en_US": {
"@id": "\/api\/v2\/admin\/promotion-translations\/@integer@",
"@id": "\/api\/v2\/admin\/promotions\/50_off\/translations\/en_US",
"@type": "PromotionTranslation",
"id": "@integer@",
"label": "-50% on first order!"
}
}