mirror of
https://github.com/Sylius/Sylius.git
synced 2026-07-05 20:57:12 +00:00
Refactor checking whether the channel can be deleted
This commit is contained in:
parent
14a94c043b
commit
8d4214d18c
17 changed files with 310 additions and 171 deletions
|
|
@ -24,5 +24,7 @@
|
|||
|
||||
<server name="IS_DOCTRINE_ORM_SUPPORTED" value="true" />
|
||||
<server name="ESCAPE_JSON" value="true" />
|
||||
|
||||
<server name="KERNEL_CLASS" value="App\Kernel" />
|
||||
</php>
|
||||
</phpunit>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,56 @@
|
|||
<?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 Sylius\Bundle\ApiBundle\Exception\ChannelCannotBeRemoved;
|
||||
use Sylius\Component\Channel\Checker\ChannelDeletionCheckerInterface;
|
||||
use Sylius\Component\Core\Model\ChannelInterface;
|
||||
|
||||
final class ChannelDataPersister implements ContextAwareDataPersisterInterface
|
||||
{
|
||||
public function __construct (
|
||||
private ContextAwareDataPersisterInterface $decoratedDataPersister,
|
||||
private ChannelDeletionCheckerInterface $channelDeletionChecker,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $context
|
||||
*/
|
||||
public function supports($data, array $context = []): bool
|
||||
{
|
||||
return $data instanceof ChannelInterface;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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
|
||||
{
|
||||
if (!$this->channelDeletionChecker->isDeletable($data)) {
|
||||
throw new ChannelCannotBeRemoved('The channel cannot be deleted. At least one enabled channel is required.');
|
||||
}
|
||||
|
||||
$this->decoratedDataPersister->remove($data, $context);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,47 +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\EventListener;
|
||||
|
||||
use Sylius\Bundle\ApiBundle\Exception\ChannelCannotBeRemoved;
|
||||
use Sylius\Component\Channel\Model\ChannelInterface;
|
||||
use Sylius\Component\Channel\Repository\ChannelRepositoryInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpKernel\Event\ViewEvent;
|
||||
|
||||
/** @experimental */
|
||||
final class ChannelDeletionEventListener
|
||||
{
|
||||
/**
|
||||
* @param ChannelRepositoryInterface<ChannelInterface> $channelRepository
|
||||
*/
|
||||
public function __construct(private ChannelRepositoryInterface $channelRepository)
|
||||
{
|
||||
}
|
||||
|
||||
public function protectFromRemovalTheOnlyChannelInStore(ViewEvent $event): void
|
||||
{
|
||||
$channel = $event->getControllerResult();
|
||||
$method = $event->getRequest()->getMethod();
|
||||
|
||||
if (!$channel instanceof ChannelInterface || $method !== Request::METHOD_DELETE) {
|
||||
return;
|
||||
}
|
||||
|
||||
$results = $this->channelRepository->findBy(['enabled' => true]);
|
||||
|
||||
if (!$results || (count($results) === 1 && current($results) === $channel)) {
|
||||
throw new ChannelCannotBeRemoved('The channel cannot be deleted. At least one enabled channel is required.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -177,11 +177,6 @@
|
|||
<tag name="kernel.event_listener" event="lexik_jwt_authentication.on_authentication_success" method="onAuthenticationSuccessResponse" />
|
||||
</service>
|
||||
|
||||
<service id="Sylius\Bundle\ApiBundle\EventListener\ChannelDeletionEventListener">
|
||||
<argument type="service" id="sylius.repository.channel"/>
|
||||
<tag name="kernel.event_listener" event="kernel.view" method="protectFromRemovalTheOnlyChannelInStore" priority="33"/>
|
||||
</service>
|
||||
|
||||
<service id="Sylius\Bundle\ApiBundle\Validator\ResourceInputDataPropertiesValidatorInterface" class="Sylius\Bundle\ApiBundle\Validator\ResourceApiInputDataPropertiesValidator">
|
||||
<argument type="service" id="validator" />
|
||||
</service>
|
||||
|
|
|
|||
|
|
@ -31,6 +31,12 @@
|
|||
<tag name="api_platform.data_persister" />
|
||||
</service>
|
||||
|
||||
<service id="Sylius\Bundle\ApiBundle\DataPersister\ChannelDataPersister">
|
||||
<argument type="service" id="api_platform.doctrine.orm.data_persister" />
|
||||
<argument type="service" id="sylius.checker.channel_deletion" />
|
||||
<tag name="api_platform.data_persister" />
|
||||
</service>
|
||||
|
||||
<service id="Sylius\Bundle\ApiBundle\DataPersister\MessengerDataPersister">
|
||||
<argument type="service" id="api_platform.messenger.data_persister" />
|
||||
<tag name="api_platform.data_persister" />
|
||||
|
|
|
|||
|
|
@ -0,0 +1,68 @@
|
|||
<?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 PhpSpec\ObjectBehavior;
|
||||
use Sylius\Bundle\ApiBundle\Exception\ChannelCannotBeRemoved;
|
||||
use Sylius\Component\Channel\Checker\ChannelDeletionCheckerInterface;
|
||||
use Sylius\Component\Core\Model\ChannelInterface;
|
||||
|
||||
final class ChannelDataPersisterSpec extends ObjectBehavior
|
||||
{
|
||||
function let(
|
||||
ContextAwareDataPersisterInterface $decoratedDataPersister,
|
||||
ChannelDeletionCheckerInterface $channelDeletionChecker,
|
||||
): void {
|
||||
$this->beConstructedWith($decoratedDataPersister, $channelDeletionChecker);
|
||||
}
|
||||
|
||||
function it_supports_only_channel_entity(ChannelInterface $channel, \stdClass $object): void
|
||||
{
|
||||
$this->supports($channel)->shouldReturn(true);
|
||||
$this->supports($object)->shouldReturn(false);
|
||||
}
|
||||
|
||||
function it_persists_channel(
|
||||
ContextAwareDataPersisterInterface $decoratedDataPersister,
|
||||
ChannelInterface $channel
|
||||
): void {
|
||||
$decoratedDataPersister->persist($channel, [])->willReturn($channel);
|
||||
|
||||
$this->persist($channel, [])->shouldReturn($channel);
|
||||
}
|
||||
|
||||
function it_throws_an_exception_if_channel_is_not_deletable(
|
||||
ChannelDeletionCheckerInterface $channelDeletionChecker,
|
||||
ChannelInterface $channel
|
||||
): void {
|
||||
$channelDeletionChecker->isDeletable($channel)->willReturn(false);
|
||||
|
||||
$this
|
||||
->shouldThrow(ChannelCannotBeRemoved::class)
|
||||
->during('remove', [$channel])
|
||||
;
|
||||
}
|
||||
|
||||
function it_removes_channel(
|
||||
ContextAwareDataPersisterInterface $decoratedDataPersister,
|
||||
ChannelDeletionCheckerInterface $channelDeletionChecker,
|
||||
ChannelInterface $channel
|
||||
): void {
|
||||
$channelDeletionChecker->isDeletable($channel)->willReturn(true);
|
||||
$decoratedDataPersister->remove($channel, [])->willReturn(null);
|
||||
|
||||
$this->remove($channel, [])->shouldReturn(null);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,112 +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\EventListener;
|
||||
|
||||
use PhpSpec\ObjectBehavior;
|
||||
use Sylius\Bundle\ApiBundle\Exception\ChannelCannotBeRemoved;
|
||||
use Sylius\Component\Channel\Repository\ChannelRepositoryInterface;
|
||||
use Sylius\Component\Core\Model\ChannelInterface;
|
||||
use Sylius\Component\Core\Model\OrderInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpKernel\Event\ViewEvent;
|
||||
use Symfony\Component\HttpKernel\HttpKernelInterface;
|
||||
|
||||
final class ChannelDeletionEventListenerSpec extends ObjectBehavior
|
||||
{
|
||||
function let(ChannelRepositoryInterface $channelRepository)
|
||||
{
|
||||
$this->beConstructedWith($channelRepository);
|
||||
}
|
||||
|
||||
function it_throws_exception_when_trying_to_delete_the_only_enabled_channel(
|
||||
Request $request,
|
||||
ChannelInterface $channel,
|
||||
ChannelRepositoryInterface $channelRepository,
|
||||
HttpKernelInterface $kernel,
|
||||
): void {
|
||||
$request->getMethod()->willReturn(Request::METHOD_DELETE);
|
||||
|
||||
$event = new ViewEvent(
|
||||
$kernel->getWrappedObject(),
|
||||
$request->getWrappedObject(),
|
||||
HttpKernelInterface::MAIN_REQUEST,
|
||||
$channel->getWrappedObject(),
|
||||
);
|
||||
|
||||
$channelRepository->findBy(['enabled' => true])->willReturn([$channel]);
|
||||
|
||||
$this->shouldThrow(ChannelCannotBeRemoved::class)->during('protectFromRemovalTheOnlyChannelInStore', [$event]);
|
||||
}
|
||||
|
||||
function it_does_nothing_when_there_are_more_than_one_enabled_channels(
|
||||
Request $request,
|
||||
ChannelInterface $channel,
|
||||
ChannelInterface $channel2,
|
||||
ChannelRepositoryInterface $channelRepository,
|
||||
HttpKernelInterface $kernel,
|
||||
): void {
|
||||
$request->getMethod()->willReturn(Request::METHOD_DELETE);
|
||||
|
||||
$event = new ViewEvent(
|
||||
$kernel->getWrappedObject(),
|
||||
$request->getWrappedObject(),
|
||||
HttpKernelInterface::MAIN_REQUEST,
|
||||
$channel->getWrappedObject(),
|
||||
);
|
||||
|
||||
$channelRepository->findBy(['enabled' => true])->willReturn([$channel, $channel2]);
|
||||
|
||||
$this->shouldNotThrow(ChannelCannotBeRemoved::class)->during('protectFromRemovalTheOnlyChannelInStore', [$event]);
|
||||
}
|
||||
|
||||
function it_does_nothing_when_request_method_is_not_delete(
|
||||
Request $request,
|
||||
ChannelInterface $channel,
|
||||
ChannelRepositoryInterface $channelRepository,
|
||||
HttpKernelInterface $kernel,
|
||||
): void {
|
||||
$request->getMethod()->willReturn(Request::METHOD_GET);
|
||||
|
||||
$event = new ViewEvent(
|
||||
$kernel->getWrappedObject(),
|
||||
$request->getWrappedObject(),
|
||||
HttpKernelInterface::MAIN_REQUEST,
|
||||
$channel->getWrappedObject(),
|
||||
);
|
||||
|
||||
$channelRepository->findBy(['enabled' => true])->willReturn([$channel]);
|
||||
|
||||
$this->shouldNotThrow(ChannelCannotBeRemoved::class)->during('protectFromRemovalTheOnlyChannelInStore', [$event]);
|
||||
}
|
||||
|
||||
function it_does_nothing_when_event_controller_result_is_not_channel(
|
||||
Request $request,
|
||||
ChannelRepositoryInterface $channelRepository,
|
||||
HttpKernelInterface $kernel,
|
||||
OrderInterface $order,
|
||||
): void {
|
||||
$request->getMethod()->willReturn(Request::METHOD_DELETE);
|
||||
|
||||
$event = new ViewEvent(
|
||||
$kernel->getWrappedObject(),
|
||||
$request->getWrappedObject(),
|
||||
HttpKernelInterface::MAIN_REQUEST,
|
||||
$order->getWrappedObject(),
|
||||
);
|
||||
|
||||
$channelRepository->findBy(['enabled' => true])->willReturn([]);
|
||||
|
||||
$this->shouldNotThrow(ChannelCannotBeRemoved::class)->during('protectFromRemovalTheOnlyChannelInStore', [$event]);
|
||||
}
|
||||
}
|
||||
|
|
@ -56,6 +56,17 @@ class ChannelRepository extends EntityRepository implements ChannelRepositoryInt
|
|||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ChannelInterface[]
|
||||
*/
|
||||
public function findEnabled(): iterable
|
||||
{
|
||||
/** @var ChannelInterface[] $enabledChannels */
|
||||
$enabledChannels = $this->findBy(['enabled' => true]);
|
||||
|
||||
return $enabledChannels;
|
||||
}
|
||||
|
||||
public function countAll(): int
|
||||
{
|
||||
return $this->count([]);
|
||||
|
|
|
|||
|
|
@ -69,5 +69,10 @@
|
|||
<argument>false</argument>
|
||||
<tag name="data_collector" template="@SyliusChannel/Collector/channel.html.twig" id="sylius.channel_collector" />
|
||||
</service>
|
||||
|
||||
<service id="sylius.checker.channel_deletion" class="Sylius\Component\Channel\Checker\ChannelDeletionChecker">
|
||||
<argument type="service" id="sylius.repository.channel" />
|
||||
</service>
|
||||
<service id="Sylius\Component\Channel\Checker\ChannelDeletionCheckerInterface" alias="sylius.checker.channel_deletion" />
|
||||
</services>
|
||||
</container>
|
||||
|
|
|
|||
|
|
@ -14,13 +14,13 @@ declare(strict_types=1);
|
|||
namespace Sylius\Bundle\CoreBundle\EventListener;
|
||||
|
||||
use Sylius\Bundle\ResourceBundle\Event\ResourceControllerEvent;
|
||||
use Sylius\Component\Channel\Checker\ChannelDeletionCheckerInterface;
|
||||
use Sylius\Component\Channel\Model\ChannelInterface;
|
||||
use Sylius\Component\Channel\Repository\ChannelRepositoryInterface;
|
||||
use Sylius\Component\Resource\Exception\UnexpectedTypeException;
|
||||
|
||||
final class ChannelDeletionListener
|
||||
{
|
||||
public function __construct(private ChannelRepositoryInterface $channelRepository)
|
||||
public function __construct(private ChannelDeletionCheckerInterface $channelDeletionChecker)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -38,9 +38,7 @@ final class ChannelDeletionListener
|
|||
);
|
||||
}
|
||||
|
||||
$results = $this->channelRepository->findBy(['enabled' => true]);
|
||||
|
||||
if (!$results || (count($results) === 1 && current($results) === $channel)) {
|
||||
if (!$this->channelDeletionChecker->isDeletable($channel)) {
|
||||
$event->stop('sylius.channel.delete_error');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
<defaults public="true" />
|
||||
|
||||
<service id="sylius.listener.channel" class="Sylius\Bundle\CoreBundle\EventListener\ChannelDeletionListener">
|
||||
<argument type="service" id="sylius.repository.channel" />
|
||||
<argument type="service" id="sylius.checker.channel_deletion" />
|
||||
<tag name="kernel.event_listener" event="sylius.channel.pre_delete" method="onChannelPreDelete" />
|
||||
</service>
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,38 @@
|
|||
<?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\Component\Channel\Checker;
|
||||
|
||||
use Sylius\Component\Channel\Model\ChannelInterface;
|
||||
use Sylius\Component\Channel\Repository\ChannelRepositoryInterface;
|
||||
|
||||
final class ChannelDeletionChecker implements ChannelDeletionCheckerInterface
|
||||
{
|
||||
/**
|
||||
* @param ChannelRepositoryInterface<ChannelInterface> $channelRepository
|
||||
*/
|
||||
public function __construct(private ChannelRepositoryInterface $channelRepository)
|
||||
{
|
||||
}
|
||||
|
||||
public function isDeletable(ChannelInterface $channel): bool
|
||||
{
|
||||
if (!$channel->isEnabled()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$enabledChannels = $this->channelRepository->findEnabled();
|
||||
|
||||
return count($enabledChannels) > 1;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
<?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\Component\Channel\Checker;
|
||||
|
||||
use Sylius\Component\Channel\Model\ChannelInterface;
|
||||
|
||||
interface ChannelDeletionCheckerInterface
|
||||
{
|
||||
public function isDeletable(ChannelInterface $channel): bool;
|
||||
}
|
||||
|
|
@ -35,5 +35,10 @@ interface ChannelRepositoryInterface extends RepositoryInterface
|
|||
|
||||
public function findAllWithBasicData(): iterable;
|
||||
|
||||
/**
|
||||
* @return ChannelInterface[]
|
||||
*/
|
||||
public function findEnabled(): iterable;
|
||||
|
||||
public function countAll(): int;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,64 @@
|
|||
<?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\Component\Channel\Checker;
|
||||
|
||||
use PhpSpec\ObjectBehavior;
|
||||
use Sylius\Component\Channel\Model\ChannelInterface;
|
||||
use Sylius\Component\Channel\Repository\ChannelRepositoryInterface;
|
||||
|
||||
final class ChannelDeletionCheckerSpec extends ObjectBehavior
|
||||
{
|
||||
function let(ChannelRepositoryInterface $channelRepository): void
|
||||
{
|
||||
$this->beConstructedWith($channelRepository);
|
||||
}
|
||||
|
||||
function it_returns_a_channel_can_be_deleted_when_is_disabled(ChannelInterface $channel): void
|
||||
{
|
||||
$channel->isEnabled()->willReturn(false);
|
||||
|
||||
$this->isDeletable($channel)->shouldReturn(true);
|
||||
}
|
||||
|
||||
function it_returns_a_channel_can_be_deleted_when_at_least_two_channels_are_enabled(
|
||||
ChannelRepositoryInterface $channelRepository,
|
||||
ChannelInterface $channel,
|
||||
ChannelInterface $anotherChannel
|
||||
): void {
|
||||
$channel->isEnabled()->willReturn(true);
|
||||
$channelRepository->findEnabled()->willReturn([$channel, $anotherChannel]);
|
||||
|
||||
$this->isDeletable($channel)->shouldReturn(true);
|
||||
}
|
||||
|
||||
function it_returns_a_channel_cannot_be_deleted_when_only_one_channel_is_enabled(
|
||||
ChannelRepositoryInterface $channelRepository,
|
||||
ChannelInterface $channel
|
||||
): void {
|
||||
$channel->isEnabled()->willReturn(true);
|
||||
$channelRepository->findEnabled()->willReturn([$channel]);
|
||||
|
||||
$this->isDeletable($channel)->shouldReturn(false);
|
||||
}
|
||||
|
||||
function it_returns_a_channel_cannot_be_deleted_when_no_channel_is_enabled(
|
||||
ChannelRepositoryInterface $channelRepository,
|
||||
ChannelInterface $channel
|
||||
): void {
|
||||
$channel->isEnabled()->willReturn(true);
|
||||
$channelRepository->findEnabled()->willReturn([]);
|
||||
|
||||
$this->isDeletable($channel)->shouldReturn(false);
|
||||
}
|
||||
}
|
||||
|
|
@ -14,7 +14,6 @@ declare(strict_types=1);
|
|||
namespace Sylius\Tests\Api\Admin;
|
||||
|
||||
use Sylius\Component\Core\Model\ChannelInterface;
|
||||
use Sylius\Component\Core\Model\ShopBillingData;
|
||||
use Sylius\Tests\Api\JsonApiTestCase;
|
||||
use Sylius\Tests\Api\Utils\AdminUserLoginTrait;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
|
@ -76,6 +75,30 @@ final class ChannelsTest extends JsonApiTestCase
|
|||
$this->assertResponseCode($this->client->getResponse(), Response::HTTP_NOT_FOUND);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function it_prevents_deleting_the_only_channel(): void
|
||||
{
|
||||
$this->loadFixturesFromFiles(['authentication/api_administrator.yaml', 'channel.yaml']);
|
||||
$header = array_merge($this->logInAdminUser('api@example.com'), self::CONTENT_TYPE_HEADER);
|
||||
|
||||
$this->client->request(
|
||||
method: 'DELETE',
|
||||
uri: '/api/v2/admin/channels/MOBILE',
|
||||
server: $header,
|
||||
);
|
||||
$this->client->request(
|
||||
method: 'DELETE',
|
||||
uri: '/api/v2/admin/channels/WEB',
|
||||
server: $header,
|
||||
);
|
||||
|
||||
$this->assertResponse(
|
||||
$this->client->getResponse(),
|
||||
'admin/channel/delete_channel_that_cannot_be_deleted',
|
||||
Response::HTTP_UNPROCESSABLE_ENTITY
|
||||
);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function it_gets_channels(): void
|
||||
{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"@context": "\/api\/v2\/contexts\/Error",
|
||||
"@type": "hydra:Error",
|
||||
"hydra:title": "An error occurred",
|
||||
"hydra:description": "The channel cannot be deleted. At least one enabled channel is required."
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue