diff --git a/features/main/operation.feature b/features/main/operation.feature
index b81805498ea..8c70d1c2b99 100644
--- a/features/main/operation.feature
+++ b/features/main/operation.feature
@@ -55,3 +55,11 @@ Feature: Operation support
}
}
"""
+
+ Scenario: Get the collection of a resource that have disabled item operation
+ When I send a "GET" request to "/disable_item_operations"
+ Then the response status code should be 200
+
+ Scenario: Get a 404 response for the disabled item operation
+ When I send a "GET" request to "/disable_item_operations/1"
+ Then the response status code should be 404
diff --git a/src/Action/NotFoundAction.php b/src/Action/NotFoundAction.php
new file mode 100644
index 00000000000..8dd73fb7090
--- /dev/null
+++ b/src/Action/NotFoundAction.php
@@ -0,0 +1,27 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace ApiPlatform\Core\Action;
+
+use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
+
+/**
+ * An action which always returns HTTP 404 Not Found. Useful for disabling an operation.
+ */
+final class NotFoundAction
+{
+ public function __invoke()
+ {
+ throw new NotFoundHttpException();
+ }
+}
diff --git a/src/Bridge/Symfony/Bundle/Resources/config/api.xml b/src/Bridge/Symfony/Bundle/Resources/config/api.xml
index 2508cc8ff92..f34847662f7 100644
--- a/src/Bridge/Symfony/Bundle/Resources/config/api.xml
+++ b/src/Bridge/Symfony/Bundle/Resources/config/api.xml
@@ -229,6 +229,8 @@
+
+
diff --git a/tests/Bridge/Symfony/Bundle/DependencyInjection/ApiPlatformExtensionTest.php b/tests/Bridge/Symfony/Bundle/DependencyInjection/ApiPlatformExtensionTest.php
index 620ef4ce6f1..11f23d41b3f 100644
--- a/tests/Bridge/Symfony/Bundle/DependencyInjection/ApiPlatformExtensionTest.php
+++ b/tests/Bridge/Symfony/Bundle/DependencyInjection/ApiPlatformExtensionTest.php
@@ -13,6 +13,7 @@
namespace ApiPlatform\Core\Tests\Bridge\Symfony\Bundle\DependencyInjection;
+use ApiPlatform\Core\Action\NotFoundAction;
use ApiPlatform\Core\Api\FilterInterface;
use ApiPlatform\Core\Api\IdentifiersExtractorInterface;
use ApiPlatform\Core\Api\IriConverterInterface;
@@ -843,6 +844,7 @@ private function getPartialContainerBuilderProphecy()
'api_platform.action.documentation',
'api_platform.action.entrypoint',
'api_platform.action.exception',
+ 'api_platform.action.not_found',
'api_platform.action.placeholder',
'api_platform.cache.identifiers_extractor',
'api_platform.cache.metadata.property',
@@ -938,23 +940,24 @@ private function getPartialContainerBuilderProphecy()
'api_platform.property_accessor' => 'property_accessor',
'api_platform.property_info' => 'property_info',
'api_platform.serializer' => 'serializer',
- Pagination::class => 'api_platform.pagination',
- IriConverterInterface::class => 'api_platform.iri_converter',
- UrlGeneratorInterface::class => 'api_platform.router',
- SerializerContextBuilderInterface::class => 'api_platform.serializer.context_builder',
CollectionDataProviderInterface::class => 'api_platform.collection_data_provider',
- ItemDataProviderInterface::class => 'api_platform.item_data_provider',
- SubresourceDataProviderInterface::class => 'api_platform.subresource_data_provider',
DataPersisterInterface::class => 'api_platform.data_persister',
- ResourceNameCollectionFactoryInterface::class => 'api_platform.metadata.resource.name_collection_factory',
- ResourceMetadataFactoryInterface::class => 'api_platform.metadata.resource.metadata_factory',
+ GroupFilter::class => 'api_platform.serializer.group_filter',
+ IdentifiersExtractorInterface::class => 'api_platform.identifiers_extractor.cached',
+ IriConverterInterface::class => 'api_platform.iri_converter',
+ ItemDataProviderInterface::class => 'api_platform.item_data_provider',
+ NotFoundAction::class => 'api_platform.action.not_found',
+ OperationAwareFormatsProviderInterface::class => 'api_platform.formats_provider',
+ Pagination::class => 'api_platform.pagination',
+ PropertyFilter::class => 'api_platform.serializer.property_filter',
PropertyNameCollectionFactoryInterface::class => 'api_platform.metadata.property.name_collection_factory',
PropertyMetadataFactoryInterface::class => 'api_platform.metadata.property.metadata_factory',
ResourceClassResolverInterface::class => 'api_platform.resource_class_resolver',
- PropertyFilter::class => 'api_platform.serializer.property_filter',
- GroupFilter::class => 'api_platform.serializer.group_filter',
- OperationAwareFormatsProviderInterface::class => 'api_platform.formats_provider',
- IdentifiersExtractorInterface::class => 'api_platform.identifiers_extractor.cached',
+ ResourceNameCollectionFactoryInterface::class => 'api_platform.metadata.resource.name_collection_factory',
+ ResourceMetadataFactoryInterface::class => 'api_platform.metadata.resource.metadata_factory',
+ SerializerContextBuilderInterface::class => 'api_platform.serializer.context_builder',
+ SubresourceDataProviderInterface::class => 'api_platform.subresource_data_provider',
+ UrlGeneratorInterface::class => 'api_platform.router',
];
foreach ($aliases as $alias => $service) {
diff --git a/tests/Fixtures/TestBundle/Document/DisableItemOperation.php b/tests/Fixtures/TestBundle/Document/DisableItemOperation.php
new file mode 100644
index 00000000000..6d84f2a8b7d
--- /dev/null
+++ b/tests/Fixtures/TestBundle/Document/DisableItemOperation.php
@@ -0,0 +1,53 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace ApiPlatform\Core\Tests\Fixtures\TestBundle\Document;
+
+use ApiPlatform\Core\Action\NotFoundAction;
+use ApiPlatform\Core\Annotation\ApiResource;
+use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM;
+
+/**
+ * @ApiResource(
+ * collectionOperations={
+ * "get",
+ * },
+ * itemOperations={
+ * "get"={
+ * "controller"=NotFoundAction::class,
+ * "read"=false,
+ * "output"=false,
+ * },
+ * },
+ * )
+ * @ODM\Document
+ */
+class DisableItemOperation
+{
+ /**
+ * @ODM\Id(strategy="INCREMENT", type="integer")
+ */
+ private $id;
+
+ /**
+ * @var string The dummy name
+ *
+ * @ODM\Field
+ */
+ public $name;
+
+ public function getId()
+ {
+ return $this->id;
+ }
+}
diff --git a/tests/Fixtures/TestBundle/Entity/DisableItemOperation.php b/tests/Fixtures/TestBundle/Entity/DisableItemOperation.php
new file mode 100644
index 00000000000..e5f85128843
--- /dev/null
+++ b/tests/Fixtures/TestBundle/Entity/DisableItemOperation.php
@@ -0,0 +1,57 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity;
+
+use ApiPlatform\Core\Action\NotFoundAction;
+use ApiPlatform\Core\Annotation\ApiResource;
+use Doctrine\ORM\Mapping as ORM;
+
+/**
+ * @ApiResource(
+ * collectionOperations={
+ * "get",
+ * },
+ * itemOperations={
+ * "get"={
+ * "controller"=NotFoundAction::class,
+ * "read"=false,
+ * "output"=false,
+ * },
+ * },
+ * )
+ * @ORM\Entity
+ */
+class DisableItemOperation
+{
+ /**
+ * @var int The id
+ *
+ * @ORM\Column(type="integer", nullable=true)
+ * @ORM\Id
+ * @ORM\GeneratedValue(strategy="AUTO")
+ */
+ private $id;
+
+ /**
+ * @var string The dummy name
+ *
+ * @ORM\Column
+ */
+ public $name;
+
+ public function getId()
+ {
+ return $this->id;
+ }
+}
diff --git a/tests/JsonSchema/Command/JsonSchemaGenerateCommandTest.php b/tests/JsonSchema/Command/JsonSchemaGenerateCommandTest.php
index f6e74c1bef4..86e234df469 100644
--- a/tests/JsonSchema/Command/JsonSchemaGenerateCommandTest.php
+++ b/tests/JsonSchema/Command/JsonSchemaGenerateCommandTest.php
@@ -68,7 +68,7 @@ public function testExecuteWithTooManyOptions()
{
$this->tester->run(['command' => 'api:json-schema:generate', 'resource' => $this->entityClass, '--collectionOperation' => 'get', '--itemOperation' => 'get', '--type' => 'output']);
- $this->assertStringStartsWith('[ERROR] You can only use one of "--itemOperation" and "--collectionOperation"', trim(str_replace(["\r", "\n"], '', $this->tester->getDisplay())));
+ $this->assertStringStartsWith('[ERROR] You can only use one of "--itemOperation" and "--collectionOperation" options at the same time.', trim(preg_replace('/\s+/', ' ', $this->tester->getDisplay())));
}
public function testExecuteWithJsonldFormatOption()