[API][Admin] Add DELETE endpoint for product variant resource

This commit is contained in:
Grzegorz Sadowski 2023-10-16 11:19:17 +02:00
parent 1bbfc60b7d
commit 98a7e3cab2
No known key found for this signature in database
GPG key ID: EF87FF4E6E3BD364
9 changed files with 231 additions and 0 deletions

View file

@ -0,0 +1,49 @@
<?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\ProductVariantCannotBeRemoved;
use Sylius\Component\Core\Model\ProductVariantInterface;
/** @experimental */
final class ProductVariantDataPersister implements ContextAwareDataPersisterInterface
{
public function __construct(private ContextAwareDataPersisterInterface $decoratedDataPersister)
{
}
/** @param array<mixed> $context */
public function supports($data, array $context = []): bool
{
return $data instanceof ProductVariantInterface;
}
/** @param array<mixed> $context */
public function persist($data, array $context = [])
{
return $this->decoratedDataPersister->persist($data, $context);
}
/** @param array<mixed> $context */
public function remove($data, array $context = [])
{
try {
return $this->decoratedDataPersister->remove($data, $context);
} catch (ForeignKeyConstraintViolationException) {
throw new ProductVariantCannotBeRemoved();
}
}
}

View file

@ -0,0 +1,26 @@
<?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\Exception;
/** @experimental */
final class ProductVariantCannotBeRemoved extends \RuntimeException
{
public function __construct(
string $message = 'Cannot delete, the product variant is in use.',
int $code = 0,
\Throwable $previous = null,
) {
parent::__construct($message, $code, $previous);
}
}

View file

@ -45,6 +45,11 @@
<attribute name="groups">shop:product_variant:read</attribute>
</attribute>
</itemOperation>
<itemOperation name="admin_delete">
<attribute name="method">DELETE</attribute>
<attribute name="path">/admin/product-variants/{code}</attribute>
</itemOperation>
</itemOperations>
<collectionOperations>

View file

@ -54,6 +54,7 @@ api_platform:
Sylius\Bundle\ApiBundle\Exception\OrderNoLongerEligibleForPromotion: 422
Sylius\Bundle\ApiBundle\Exception\ProductAttributeCannotBeRemoved: 422
Sylius\Bundle\ApiBundle\Exception\ProductCannotBeRemoved: 422
Sylius\Bundle\ApiBundle\Exception\ProductVariantCannotBeRemoved: 422
Sylius\Bundle\ApiBundle\Exception\ProvinceCannotBeRemoved: 422
Sylius\Bundle\ApiBundle\Exception\ShippingMethodCannotBeRemoved: 422
Sylius\Bundle\ApiBundle\Exception\TaxonCannotBeRemoved: 422

View file

@ -64,6 +64,11 @@
<tag name="api_platform.data_persister" />
</service>
<service id="Sylius\Bundle\ApiBundle\DataPersister\ProductVariantDataPersister">
<argument type="service" id="api_platform.doctrine.orm.data_persister" />
<tag name="api_platform.data_persister" />
</service>
<service id="Sylius\Bundle\ApiBundle\DataPersister\ZoneDataPersister">
<argument type="service" id="api_platform.doctrine.orm.data_persister" />
<argument type="service" id="Sylius\Component\Addressing\Checker\ZoneDeletionCheckerInterface" />

View file

@ -0,0 +1,66 @@
<?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\ProductVariantCannotBeRemoved;
use Sylius\Component\Core\Model\ProductVariantInterface;
final class ProductVariantDataPersisterSpec 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_product_variant(ProductVariantInterface $productVariant): void
{
$this->supports(new \stdClass())->shouldReturn(false);
$this->supports($productVariant)->shouldReturn(true);
}
function it_uses_inner_persister_to_persist_product_variant(
ContextAwareDataPersisterInterface $dataPersister,
ProductVariantInterface $productVariant,
): void {
$dataPersister->persist($productVariant, [])->shouldBeCalled();
$this->persist($productVariant);
}
function it_throws_cannot_be_removed_exception_if_constraint_fails_on_removal(
ContextAwareDataPersisterInterface $dataPersister,
ProductVariantInterface $productVariant,
): void {
$dataPersister->remove($productVariant, [])->willThrow(ForeignKeyConstraintViolationException::class);
$this->shouldThrow(ProductVariantCannotBeRemoved::class)->during('remove', [$productVariant]);
}
function it_uses_inner_persister_to_remove_product_variant(
ContextAwareDataPersisterInterface $dataPersister,
ProductVariantInterface $productVariant,
): void {
$dataPersister->remove($productVariant, [])->shouldBeCalled();
$this->remove($productVariant);
}
}

View file

@ -395,4 +395,56 @@ final class ProductVariantsTest extends JsonApiTestCase
$this->assertResponseCode($this->client->getResponse(), Response::HTTP_UNPROCESSABLE_ENTITY);
}
/** @test */
public function it_deletes_the_product_variant(): void
{
$fixtures = $this->loadFixturesFromFiles([
'authentication/api_administrator.yaml',
'channel.yaml',
'tax_category.yaml',
'shipping_category.yaml',
'product/product_variant.yaml',
]);
$header = array_merge($this->logInAdminUser('api@example.com'), self::CONTENT_TYPE_HEADER);
/** @var ProductVariantInterface $productVariant */
$productVariant = $fixtures['product_variant_2'];
$this->client->request(
method: 'DELETE',
uri: sprintf('/api/v2/admin/product-variants/%s', $productVariant->getCode()),
server: $header,
);
$this->assertResponseCode($this->client->getResponse(), Response::HTTP_NO_CONTENT);
}
/** @test */
public function it_does_not_delete_the_product_variant_in_use(): void
{
$fixtures = $this->loadFixturesFromFiles([
'authentication/api_administrator.yaml',
'channel.yaml',
'tax_category.yaml',
'shipping_category.yaml',
'product/product_variant.yaml',
]);
$header = array_merge($this->logInAdminUser('api@example.com'), self::CONTENT_TYPE_HEADER);
/** @var ProductVariantInterface $productVariant */
$productVariant = $fixtures['product_variant'];
$this->client->request(
method: 'DELETE',
uri: sprintf('/api/v2/admin/product-variants/%s', $productVariant->getCode()),
server: $header,
);
$this->assertResponse(
$this->client->getResponse(),
'admin/product_variant/delete_product_variant_in_use_response',
Response::HTTP_UNPROCESSABLE_ENTITY,
);
}
}

View file

@ -95,3 +95,24 @@ Sylius\Component\Product\Model\ProductOptionValueTranslation:
locale: 'en_US'
value: 'Red'
translatable: '@product_option_value_color_red'
Sylius\Component\Core\Model\Customer:
customer:
firstName: 'John'
lastName: 'Doe'
email: 'john.doe@example.com'
emailCanonical: 'john.doe@example.com'
Sylius\Component\Core\Model\Order:
cart:
channel: '@channel_web'
items: ['@order_item']
currencyCode: 'USD'
localeCode: 'en_US'
customer: '@customer'
state: 'cart'
Sylius\Component\Core\Model\OrderItem:
order_item:
variant: '@product_variant'
order: '@cart'

View file

@ -0,0 +1,6 @@
{
"@context": "\/api\/v2\/contexts\/Error",
"@type": "hydra:Error",
"hydra:title": "An error occurred",
"hydra:description": "Cannot delete, the product variant is in use."
}