diff --git a/CHANGELOG.md b/CHANGELOG.md index 37edc3d9d22..40c3f92020a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ * GraphQL: Add support for custom queries and mutations * GraphQL: Add support for custom types * GraphQL: Better pagination support (backwards pagination) +* GraphQL: Support the pagination per resource * GraphQL: Add the concept of *stages* in the workflow of the resolvers and add the possibility to disable them with operation attributes * GraphQL: Add GraphQL Playground besides GraphiQL and add the possibility to change the default IDE (or to disable it) for the GraphQL endpoint * GraphQL: Add a command to print the schema in SDL diff --git a/features/graphql/collection.feature b/features/graphql/collection.feature index 894347550f5..f26132582dd 100644 --- a/features/graphql/collection.feature +++ b/features/graphql/collection.feature @@ -482,6 +482,26 @@ Feature: GraphQL collection support And the header "Content-Type" should be equal to "application/json" And the JSON node "data.dummies.edges" should have 0 element + @createSchema + Scenario: Retrieve a collection with pagination disabled + Given there are 4 foo objects with fake names + When I send the following GraphQL request: + """ + { + foos { + id + name + bar + } + } + """ + Then the response status code should be 200 + And the response should be in JSON + And the header "Content-Type" should be equal to "application/json" + And the JSON node "data.foos[3].id" should be equal to "/foos/4" + And the JSON node "data.foos[3].name" should be equal to "Separativeness" + And the JSON node "data.foos[3].bar" should be equal to "Sit" + Scenario: Custom collection query Given there are 2 dummyCustomQuery objects When I send the following GraphQL request: diff --git a/src/Bridge/Doctrine/MongoDbOdm/Extension/PaginationExtension.php b/src/Bridge/Doctrine/MongoDbOdm/Extension/PaginationExtension.php index 4b0a9da8475..75215bbeb61 100644 --- a/src/Bridge/Doctrine/MongoDbOdm/Extension/PaginationExtension.php +++ b/src/Bridge/Doctrine/MongoDbOdm/Extension/PaginationExtension.php @@ -52,6 +52,10 @@ public function applyToCollection(Builder $aggregationBuilder, string $resourceC return; } + if (($context['graphql_operation_name'] ?? false) && !$this->pagination->isGraphQlEnabled($resourceClass, $operationName, $context)) { + return; + } + $context = $this->addCountToContext(clone $aggregationBuilder, $context); [, $offset, $limit] = $this->pagination->getPagination($resourceClass, $operationName, $context); @@ -90,6 +94,10 @@ public function applyToCollection(Builder $aggregationBuilder, string $resourceC */ public function supportsResult(string $resourceClass, string $operationName = null, array $context = []): bool { + if ($context['graphql_operation_name'] ?? false) { + return $this->pagination->isGraphQlEnabled($resourceClass, $operationName, $context); + } + return $this->pagination->isEnabled($resourceClass, $operationName, $context); } diff --git a/src/Bridge/Doctrine/Orm/Extension/PaginationExtension.php b/src/Bridge/Doctrine/Orm/Extension/PaginationExtension.php index 64e3f294fb2..d1e6a48e4ff 100644 --- a/src/Bridge/Doctrine/Orm/Extension/PaginationExtension.php +++ b/src/Bridge/Doctrine/Orm/Extension/PaginationExtension.php @@ -134,6 +134,10 @@ public function applyToCollection(QueryBuilder $queryBuilder, QueryNameGenerator */ public function supportsResult(string $resourceClass, string $operationName = null, array $context = []): bool { + if ($context['graphql_operation_name'] ?? false) { + return $this->pagination->isGraphQlEnabled($resourceClass, $operationName, $context); + } + if (null === $this->requestStack) { return $this->pagination->isEnabled($resourceClass, $operationName, $context); } @@ -193,6 +197,10 @@ private function getPagination(QueryBuilder $queryBuilder, string $resourceClass return null; } + if (($context['graphql_operation_name'] ?? false) && !$this->pagination->isGraphQlEnabled($resourceClass, $operationName, $context)) { + return null; + } + $context = $this->addCountToContext($queryBuilder, $context); return \array_slice($this->pagination->getPagination($resourceClass, $operationName, $context), 1); diff --git a/src/Bridge/Symfony/Bundle/DependencyInjection/ApiPlatformExtension.php b/src/Bridge/Symfony/Bundle/DependencyInjection/ApiPlatformExtension.php index ccbf91515a8..886628944df 100644 --- a/src/Bridge/Symfony/Bundle/DependencyInjection/ApiPlatformExtension.php +++ b/src/Bridge/Symfony/Bundle/DependencyInjection/ApiPlatformExtension.php @@ -378,6 +378,7 @@ private function registerGraphQlConfiguration(ContainerBuilder $container, array } $container->setParameter('api_platform.graphql.default_ide', $config['graphql']['default_ide']); + $container->setParameter('api_platform.graphql.collection.pagination', $config['graphql']['collection']['pagination']); $loader->load('graphql.xml'); diff --git a/src/Bridge/Symfony/Bundle/DependencyInjection/Configuration.php b/src/Bridge/Symfony/Bundle/DependencyInjection/Configuration.php index 7ab0c5da92e..40e43a3eee6 100644 --- a/src/Bridge/Symfony/Bundle/DependencyInjection/Configuration.php +++ b/src/Bridge/Symfony/Bundle/DependencyInjection/Configuration.php @@ -239,6 +239,14 @@ private function addGraphQlSection(ArrayNodeDefinition $rootNode): void ->arrayNode('graphql_playground') ->{class_exists(GraphQL::class) && class_exists(TwigBundle::class) ? 'canBeDisabled' : 'canBeEnabled'}() ->end() + ->arrayNode('collection') + ->addDefaultsIfNotSet() + ->children() + ->arrayNode('pagination') + ->canBeDisabled() + ->end() + ->end() + ->end() ->end() ->end() ->end(); diff --git a/src/Bridge/Symfony/Bundle/Resources/config/data_provider.xml b/src/Bridge/Symfony/Bundle/Resources/config/data_provider.xml index a64b76fe370..800cdaf4511 100644 --- a/src/Bridge/Symfony/Bundle/Resources/config/data_provider.xml +++ b/src/Bridge/Symfony/Bundle/Resources/config/data_provider.xml @@ -30,6 +30,7 @@ %api_platform.collection.pagination% + %api_platform.graphql.collection.pagination% diff --git a/src/Bridge/Symfony/Bundle/Resources/config/graphql.xml b/src/Bridge/Symfony/Bundle/Resources/config/graphql.xml index 5a3a78f8ce5..147b0af5cda 100644 --- a/src/Bridge/Symfony/Bundle/Resources/config/graphql.xml +++ b/src/Bridge/Symfony/Bundle/Resources/config/graphql.xml @@ -64,7 +64,7 @@ - %api_platform.collection.pagination.enabled% + @@ -135,7 +135,7 @@ - %api_platform.collection.pagination.enabled% + diff --git a/src/DataProvider/Pagination.php b/src/DataProvider/Pagination.php index 4b912e2133d..9326ad9fcb9 100644 --- a/src/DataProvider/Pagination.php +++ b/src/DataProvider/Pagination.php @@ -14,6 +14,7 @@ namespace ApiPlatform\Core\DataProvider; use ApiPlatform\Core\Exception\InvalidArgumentException; +use ApiPlatform\Core\Exception\ResourceClassNotFoundException; use ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface; /** @@ -24,9 +25,10 @@ final class Pagination { private $options; + private $graphQlOptions; private $resourceMetadataFactory; - public function __construct(ResourceMetadataFactoryInterface $resourceMetadataFactory, array $options = []) + public function __construct(ResourceMetadataFactoryInterface $resourceMetadataFactory, array $options = [], array $graphQlOptions = []) { $this->resourceMetadataFactory = $resourceMetadataFactory; $this->options = array_merge([ @@ -43,6 +45,9 @@ public function __construct(ResourceMetadataFactoryInterface $resourceMetadataFa 'client_partial' => false, 'partial_parameter_name' => 'partial', ], $options); + $this->graphQlOptions = array_merge([ + 'enabled' => true, + ], $graphQlOptions); } /** @@ -171,6 +176,14 @@ public function isEnabled(string $resourceClass = null, string $operationName = return $this->getEnabled($context, $resourceClass, $operationName); } + /** + * Is the pagination enabled for GraphQL? + */ + public function isGraphQlEnabled(?string $resourceClass = null, ?string $operationName = null, array $context = []): bool + { + return $this->getGraphQlEnabled($resourceClass, $operationName); + } + /** * Is the partial pagination enabled? */ @@ -198,6 +211,23 @@ private function getEnabled(array $context, string $resourceClass = null, string return filter_var($this->getParameterFromContext($context, $this->options[$partial ? 'partial_parameter_name' : 'enabled_parameter_name'], $enabled), FILTER_VALIDATE_BOOLEAN); } + return (bool) $enabled; + } + + private function getGraphQlEnabled(?string $resourceClass, ?string $operationName): bool + { + $enabled = $this->graphQlOptions['enabled']; + + if (null !== $resourceClass) { + try { + $resourceMetadata = $this->resourceMetadataFactory->create($resourceClass); + } catch (ResourceClassNotFoundException $e) { + return $enabled; + } + + return (bool) $resourceMetadata->getGraphqlAttribute($operationName, 'pagination_enabled', $enabled, true); + } + return $enabled; } diff --git a/src/GraphQl/Resolver/Factory/ItemResolverFactory.php b/src/GraphQl/Resolver/Factory/ItemResolverFactory.php index 5931747d712..ed00054e537 100644 --- a/src/GraphQl/Resolver/Factory/ItemResolverFactory.php +++ b/src/GraphQl/Resolver/Factory/ItemResolverFactory.php @@ -79,7 +79,7 @@ public function __invoke(?string $resourceClass = null, ?string $rootClass = nul if (null !== $queryResolverId) { /** @var QueryItemResolverInterface $queryResolver */ $queryResolver = $this->queryResolverLocator->get($queryResolverId); - $item = $queryResolver($item, ['source' => $source, 'args' => $args, 'info' => $info]); + $item = $queryResolver($item, $resolverContext); $resourceClass = $this->getResourceClass($item, $resourceClass, $info, sprintf('Custom query resolver "%s"', $queryResolverId).' has to return an item of class %s but returned an item of class %s.'); } diff --git a/src/GraphQl/Resolver/Stage/ReadStage.php b/src/GraphQl/Resolver/Stage/ReadStage.php index 522c46f9344..6a57d9dda30 100644 --- a/src/GraphQl/Resolver/Stage/ReadStage.php +++ b/src/GraphQl/Resolver/Stage/ReadStage.php @@ -92,7 +92,7 @@ public function __invoke(?string $resourceClass, ?string $rootClass, string $ope $source = $context['source']; if (isset($source[$rootProperty = $info->fieldName], $source[ItemNormalizer::ITEM_IDENTIFIERS_KEY])) { $rootResolvedFields = $source[ItemNormalizer::ITEM_IDENTIFIERS_KEY]; - $subresourceCollection = $this->getSubresource($rootClass, $rootResolvedFields, $rootProperty, $resourceClass, $normalizationContext); + $subresourceCollection = $this->getSubresource($rootClass, $rootResolvedFields, $rootProperty, $resourceClass, $normalizationContext, $operationName); if (!is_iterable($subresourceCollection)) { throw new \UnexpectedValueException('Expected subresource collection to be iterable'); } @@ -100,7 +100,7 @@ public function __invoke(?string $resourceClass, ?string $rootClass, string $ope return $subresourceCollection; } - return $this->collectionDataProvider->getCollection($resourceClass, null, $normalizationContext); + return $this->collectionDataProvider->getCollection($resourceClass, $operationName, $normalizationContext); } private function getIdentifier(array $context): ?string @@ -156,7 +156,7 @@ private function getNormalizedFilters(array $args): array /** * @return iterable|object|null */ - private function getSubresource(string $rootClass, array $rootResolvedFields, string $rootProperty, string $subresourceClass, array $normalizationContext) + private function getSubresource(string $rootClass, array $rootResolvedFields, string $rootProperty, string $subresourceClass, array $normalizationContext, string $operationName) { $resolvedIdentifiers = []; $rootIdentifiers = array_keys($rootResolvedFields); @@ -168,6 +168,6 @@ private function getSubresource(string $rootClass, array $rootResolvedFields, st 'property' => $rootProperty, 'identifiers' => $resolvedIdentifiers, 'collection' => true, - ]); + ], $operationName); } } diff --git a/src/GraphQl/Resolver/Stage/SerializeStage.php b/src/GraphQl/Resolver/Stage/SerializeStage.php index 57715d63fe2..8c1d6feff07 100644 --- a/src/GraphQl/Resolver/Stage/SerializeStage.php +++ b/src/GraphQl/Resolver/Stage/SerializeStage.php @@ -13,6 +13,7 @@ namespace ApiPlatform\Core\GraphQl\Resolver\Stage; +use ApiPlatform\Core\DataProvider\Pagination; use ApiPlatform\Core\DataProvider\PaginatorInterface; use ApiPlatform\Core\GraphQl\Serializer\ItemNormalizer; use ApiPlatform\Core\GraphQl\Serializer\SerializerContextBuilderInterface; @@ -33,14 +34,14 @@ final class SerializeStage implements SerializeStageInterface private $resourceMetadataFactory; private $normalizer; private $serializerContextBuilder; - private $paginationEnabled; + private $pagination; - public function __construct(ResourceMetadataFactoryInterface $resourceMetadataFactory, NormalizerInterface $normalizer, SerializerContextBuilderInterface $serializerContextBuilder, bool $paginationEnabled) + public function __construct(ResourceMetadataFactoryInterface $resourceMetadataFactory, NormalizerInterface $normalizer, SerializerContextBuilderInterface $serializerContextBuilder, Pagination $pagination) { $this->resourceMetadataFactory = $resourceMetadataFactory; $this->normalizer = $normalizer; $this->serializerContextBuilder = $serializerContextBuilder; - $this->paginationEnabled = $paginationEnabled; + $this->pagination = $pagination; } /** @@ -54,7 +55,7 @@ public function __invoke($itemOrCollection, string $resourceClass, string $opera $resourceMetadata = $this->resourceMetadataFactory->create($resourceClass); if (!$resourceMetadata->getGraphqlAttribute($operationName, 'serialize', true, true)) { if ($isCollection) { - if ($this->paginationEnabled) { + if ($this->pagination->isGraphQlEnabled($resourceClass, $operationName, $context)) { return $this->getDefaultPaginatedData(); } @@ -84,7 +85,7 @@ public function __invoke($itemOrCollection, string $resourceClass, string $opera } if ($isCollection && is_iterable($itemOrCollection)) { - if (!$this->paginationEnabled) { + if (!$this->pagination->isGraphQlEnabled($resourceClass, $operationName, $context)) { $data = []; foreach ($itemOrCollection as $index => $object) { $data[$index] = $this->normalizer->normalize($object, ItemNormalizer::FORMAT, $normalizationContext); diff --git a/src/GraphQl/Type/FieldsBuilder.php b/src/GraphQl/Type/FieldsBuilder.php index 87a295762fa..42c9c343798 100644 --- a/src/GraphQl/Type/FieldsBuilder.php +++ b/src/GraphQl/Type/FieldsBuilder.php @@ -13,6 +13,7 @@ namespace ApiPlatform\Core\GraphQl\Type; +use ApiPlatform\Core\DataProvider\Pagination; use ApiPlatform\Core\Exception\ResourceClassNotFoundException; use ApiPlatform\Core\GraphQl\Resolver\Factory\ResolverFactoryInterface; use ApiPlatform\Core\GraphQl\Type\Definition\TypeInterface; @@ -48,9 +49,9 @@ final class FieldsBuilder implements FieldsBuilderInterface private $collectionResolverFactory; private $itemMutationResolverFactory; private $filterLocator; - private $paginationEnabled; + private $pagination; - public function __construct(PropertyNameCollectionFactoryInterface $propertyNameCollectionFactory, PropertyMetadataFactoryInterface $propertyMetadataFactory, ResourceMetadataFactoryInterface $resourceMetadataFactory, TypesContainerInterface $typesContainer, TypeBuilderInterface $typeBuilder, TypeConverterInterface $typeConverter, ResolverFactoryInterface $itemResolverFactory, ResolverFactoryInterface $collectionResolverFactory, ResolverFactoryInterface $itemMutationResolverFactory, ContainerInterface $filterLocator, bool $paginationEnabled) + public function __construct(PropertyNameCollectionFactoryInterface $propertyNameCollectionFactory, PropertyMetadataFactoryInterface $propertyMetadataFactory, ResourceMetadataFactoryInterface $resourceMetadataFactory, TypesContainerInterface $typesContainer, TypeBuilderInterface $typeBuilder, TypeConverterInterface $typeConverter, ResolverFactoryInterface $itemResolverFactory, ResolverFactoryInterface $collectionResolverFactory, ResolverFactoryInterface $itemMutationResolverFactory, ContainerInterface $filterLocator, Pagination $pagination) { $this->propertyNameCollectionFactory = $propertyNameCollectionFactory; $this->propertyMetadataFactory = $propertyMetadataFactory; @@ -62,7 +63,7 @@ public function __construct(PropertyNameCollectionFactoryInterface $propertyName $this->collectionResolverFactory = $collectionResolverFactory; $this->itemMutationResolverFactory = $itemMutationResolverFactory; $this->filterLocator = $filterLocator; - $this->paginationEnabled = $paginationEnabled; + $this->pagination = $pagination; } /** @@ -247,7 +248,7 @@ private function getResourceFieldConfiguration(?string $property, ?string $field $args = []; if (!$input && null === $mutationName && !$isStandardGraphqlType && $this->typeBuilder->isCollection($type)) { - if ($this->paginationEnabled) { + if ($this->pagination->isGraphQlEnabled($resourceClass, $queryName)) { $args = [ 'first' => [ 'type' => GraphQLType::int(), @@ -409,7 +410,7 @@ private function convertType(Type $type, bool $input, ?string $queryName, ?strin } if ($this->typeBuilder->isCollection($type)) { - return $this->paginationEnabled && !$input ? $this->typeBuilder->getResourcePaginatedCollectionType($graphqlType) : GraphQLType::listOf($graphqlType); + return $this->pagination->isGraphQlEnabled($resourceClass, $queryName ?? $mutationName) && !$input ? $this->typeBuilder->getResourcePaginatedCollectionType($graphqlType) : GraphQLType::listOf($graphqlType); } return !$graphqlType instanceof NullableType || $type->isNullable() || (null !== $mutationName && 'update' === $mutationName) diff --git a/tests/Bridge/Doctrine/MongoDbOdm/Extension/PaginationExtensionTest.php b/tests/Bridge/Doctrine/MongoDbOdm/Extension/PaginationExtensionTest.php index bae73dd4244..a4c7e4837d9 100644 --- a/tests/Bridge/Doctrine/MongoDbOdm/Extension/PaginationExtensionTest.php +++ b/tests/Bridge/Doctrine/MongoDbOdm/Extension/PaginationExtensionTest.php @@ -290,6 +290,28 @@ public function testApplyToCollectionPaginationDisabled() $extension->applyToCollection($aggregationBuilderProphecy->reveal(), 'Foo', 'op', $context); } + public function testApplyToCollectionGraphQlPaginationDisabled() + { + $resourceMetadataFactoryProphecy = $this->prophesize(ResourceMetadataFactoryInterface::class); + $resourceMetadataFactoryProphecy->create('Foo')->willReturn(new ResourceMetadata(null, null, null, [], [])); + $resourceMetadataFactory = $resourceMetadataFactoryProphecy->reveal(); + + $pagination = new Pagination($resourceMetadataFactory, [], [ + 'enabled' => false, + ]); + + $aggregationBuilderProphecy = $this->prophesize(Builder::class); + $aggregationBuilderProphecy->facet()->shouldNotBeCalled(); + + $context = ['graphql_operation_name' => 'op']; + + $extension = new PaginationExtension( + $this->managerRegistryProphecy->reveal(), + $pagination + ); + $extension->applyToCollection($aggregationBuilderProphecy->reveal(), 'Foo', 'op', $context); + } + public function testApplyToCollectionWithMaximumItemsPerPage() { $resourceMetadataFactoryProphecy = $this->prophesize(ResourceMetadataFactoryInterface::class); @@ -368,6 +390,23 @@ public function testSupportsResultPaginationDisabled() $this->assertFalse($extension->supportsResult('Foo', 'op', ['filters' => ['enabled' => false]])); } + public function testSupportsResultGraphQlPaginationDisabled() + { + $resourceMetadataFactoryProphecy = $this->prophesize(ResourceMetadataFactoryInterface::class); + $resourceMetadataFactoryProphecy->create('Foo')->willReturn(new ResourceMetadata(null, null, null, [], [])); + $resourceMetadataFactory = $resourceMetadataFactoryProphecy->reveal(); + + $pagination = new Pagination($resourceMetadataFactory, [], [ + 'enabled' => false, + ]); + + $extension = new PaginationExtension( + $this->managerRegistryProphecy->reveal(), + $pagination + ); + $this->assertFalse($extension->supportsResult('Foo', 'op', ['filters' => ['enabled' => false], 'graphql_operation_name' => 'op'])); + } + public function testGetResult() { $resourceMetadataFactoryProphecy = $this->prophesize(ResourceMetadataFactoryInterface::class); diff --git a/tests/Bridge/Doctrine/Orm/Extension/PaginationExtensionTest.php b/tests/Bridge/Doctrine/Orm/Extension/PaginationExtensionTest.php index 64e56e4dc79..0b7e05949db 100644 --- a/tests/Bridge/Doctrine/Orm/Extension/PaginationExtensionTest.php +++ b/tests/Bridge/Doctrine/Orm/Extension/PaginationExtensionTest.php @@ -651,6 +651,29 @@ public function testLegacyApplyToCollectionPaginationDisabled() $extension->applyToCollection($queryBuilder, new QueryNameGenerator(), 'Foo', 'op'); } + public function testApplyToCollectionGraphQlPaginationDisabled() + { + $resourceMetadataFactoryProphecy = $this->prophesize(ResourceMetadataFactoryInterface::class); + $resourceMetadataFactoryProphecy->create('Foo')->willReturn(new ResourceMetadata(null, null, null, [], [])); + $resourceMetadataFactory = $resourceMetadataFactoryProphecy->reveal(); + + $pagination = new Pagination($resourceMetadataFactory, [], [ + 'enabled' => false, + ]); + + $queryBuilderProphecy = $this->prophesize(QueryBuilder::class); + $queryBuilderProphecy->setFirstResult(Argument::any())->shouldNotBeCalled(); + $queryBuilderProphecy->setMaxResults(Argument::any())->shouldNotBeCalled(); + $queryBuilder = $queryBuilderProphecy->reveal(); + + $extension = new PaginationExtension( + $this->prophesize(ManagerRegistry::class)->reveal(), + $resourceMetadataFactory, + $pagination + ); + $extension->applyToCollection($queryBuilder, new QueryNameGenerator(), 'Foo', 'op', ['graphql_operation_name' => 'op']); + } + public function testApplyToCollectionWithMaximumItemsPerPage() { $resourceMetadataFactoryProphecy = $this->prophesize(ResourceMetadataFactoryInterface::class); @@ -883,6 +906,24 @@ public function testLegacySupportsResultPaginationDisabled() $this->assertFalse($extension->supportsResult('Foo', 'op')); } + public function testSupportsResultGraphQlPaginationDisabled() + { + $resourceMetadataFactoryProphecy = $this->prophesize(ResourceMetadataFactoryInterface::class); + $resourceMetadataFactoryProphecy->create('Foo')->willReturn(new ResourceMetadata(null, null, null, [], [])); + $resourceMetadataFactory = $resourceMetadataFactoryProphecy->reveal(); + + $pagination = new Pagination($resourceMetadataFactory, [], [ + 'enabled' => false, + ]); + + $extension = new PaginationExtension( + $this->prophesize(ManagerRegistry::class)->reveal(), + $resourceMetadataFactory, + $pagination + ); + $this->assertFalse($extension->supportsResult('Foo', 'op', ['graphql_operation_name' => 'op'])); + } + public function testGetResult() { $dummyMetadata = new ClassMetadata(Dummy::class); diff --git a/tests/Bridge/Symfony/Bundle/DependencyInjection/ApiPlatformExtensionTest.php b/tests/Bridge/Symfony/Bundle/DependencyInjection/ApiPlatformExtensionTest.php index 87d3de7c536..bc68efb031a 100644 --- a/tests/Bridge/Symfony/Bundle/DependencyInjection/ApiPlatformExtensionTest.php +++ b/tests/Bridge/Symfony/Bundle/DependencyInjection/ApiPlatformExtensionTest.php @@ -366,6 +366,8 @@ public function testDisableGraphQl() $containerBuilderProphecy->setParameter('api_platform.graphql.enabled', false)->shouldBeCalled(); $containerBuilderProphecy->setParameter('api_platform.graphql.default_ide', 'graphiql')->shouldNotBeCalled(); $containerBuilderProphecy->setParameter('api_platform.graphql.default_ide', Argument::any())->shouldNotBeCalled(); + $containerBuilderProphecy->setParameter('api_platform.graphql.collection.pagination', ['enabled' => true])->shouldNotBeCalled(); + $containerBuilderProphecy->setParameter('api_platform.graphql.collection.pagination', Argument::any())->shouldNotBeCalled(); $containerBuilderProphecy->setParameter('api_platform.graphql.graphiql.enabled', true)->shouldNotBeCalled(); $containerBuilderProphecy->setParameter('api_platform.graphql.graphiql.enabled', false)->shouldBeCalled(); $containerBuilderProphecy->setParameter('api_platform.graphql.graphql_playground.enabled', true)->shouldNotBeCalled(); @@ -1060,6 +1062,7 @@ private function getBaseContainerBuilderProphecy(array $doctrineIntegrationsToLo 'api_platform.enable_re_doc' => true, 'api_platform.graphql.enabled' => true, 'api_platform.graphql.default_ide' => 'graphiql', + 'api_platform.graphql.collection.pagination' => ['enabled' => true], 'api_platform.graphql.graphiql.enabled' => true, 'api_platform.graphql.graphql_playground.enabled' => true, 'api_platform.resource_class_directories' => Argument::type('array'), diff --git a/tests/Bridge/Symfony/Bundle/DependencyInjection/ConfigurationTest.php b/tests/Bridge/Symfony/Bundle/DependencyInjection/ConfigurationTest.php index c631b7b1fe9..e7bff85edb5 100644 --- a/tests/Bridge/Symfony/Bundle/DependencyInjection/ConfigurationTest.php +++ b/tests/Bridge/Symfony/Bundle/DependencyInjection/ConfigurationTest.php @@ -124,6 +124,11 @@ private function runDefaultConfigTests(array $doctrineIntegrationsToLoad = ['orm 'graphiql' => [ 'enabled' => true, ], + 'collection' => [ + 'pagination' => [ + 'enabled' => true, + ], + ], ], 'elasticsearch' => [ 'enabled' => false, diff --git a/tests/Fixtures/TestBundle/Document/Foo.php b/tests/Fixtures/TestBundle/Document/Foo.php index 6bbeb6a544b..d159b9362bb 100644 --- a/tests/Fixtures/TestBundle/Document/Foo.php +++ b/tests/Fixtures/TestBundle/Document/Foo.php @@ -21,9 +21,17 @@ * * @author Vincent Chalamon * - * @ApiResource(attributes={ - * "order"={"bar", "name"="DESC"} - * }) + * @ApiResource( + * attributes={ + * "order"={"bar", "name"="DESC"} + * }, + * graphql={ + * "item_query", + * "collection_query"={"pagination_enabled"=false}, + * "create", + * "delete" + * } + * ) * @ODM\Document */ class Foo diff --git a/tests/Fixtures/TestBundle/Entity/Foo.php b/tests/Fixtures/TestBundle/Entity/Foo.php index 3b0115ec18c..dd970d4f639 100644 --- a/tests/Fixtures/TestBundle/Entity/Foo.php +++ b/tests/Fixtures/TestBundle/Entity/Foo.php @@ -21,9 +21,17 @@ * * @author Vincent Chalamon * - * @ApiResource(attributes={ - * "order"={"bar", "name"="DESC"} - * }) + * @ApiResource( + * attributes={ + * "order"={"bar", "name"="DESC"} + * }, + * graphql={ + * "item_query", + * "collection_query"={"pagination_enabled"=false}, + * "create", + * "delete" + * } + * ) * @ORM\Entity */ class Foo diff --git a/tests/GraphQl/Resolver/Stage/ReadStageTest.php b/tests/GraphQl/Resolver/Stage/ReadStageTest.php index 2430ef7f52a..754f75e9393 100644 --- a/tests/GraphQl/Resolver/Stage/ReadStageTest.php +++ b/tests/GraphQl/Resolver/Stage/ReadStageTest.php @@ -201,9 +201,9 @@ public function testApplyCollection(array $args, ?string $rootClass, ?array $sou $normalizationContext = ['normalization' => true]; $this->serializerContextBuilderProphecy->create($resourceClass, $operationName, $context, true)->shouldBeCalled()->willReturn($normalizationContext); - $this->subresourceDataProviderProphecy->getSubresource($resourceClass, ['id' => 3], $normalizationContext + ['filters' => $expectedFilters, 'property' => $fieldName, 'identifiers' => [['id', $resourceClass]], 'collection' => true])->willReturn(['subresource']); + $this->subresourceDataProviderProphecy->getSubresource($resourceClass, ['id' => 3], $normalizationContext + ['filters' => $expectedFilters, 'property' => $fieldName, 'identifiers' => [['id', $resourceClass]], 'collection' => true], $operationName)->willReturn(['subresource']); - $this->collectionDataProviderProphecy->getCollection($resourceClass, null, $normalizationContext + ['filters' => $expectedFilters])->willReturn($expectedResult); + $this->collectionDataProviderProphecy->getCollection($resourceClass, $operationName, $normalizationContext + ['filters' => $expectedFilters])->willReturn($expectedResult); $result = ($this->readStage)($resourceClass, $rootClass, $operationName, $context); diff --git a/tests/GraphQl/Resolver/Stage/SerializeStageTest.php b/tests/GraphQl/Resolver/Stage/SerializeStageTest.php index 7e9ce67cf92..640d15cfa90 100644 --- a/tests/GraphQl/Resolver/Stage/SerializeStageTest.php +++ b/tests/GraphQl/Resolver/Stage/SerializeStageTest.php @@ -14,6 +14,7 @@ namespace ApiPlatform\Core\Tests\GraphQl\Resolver\Stage; use ApiPlatform\Core\DataProvider\ArrayPaginator; +use ApiPlatform\Core\DataProvider\Pagination; use ApiPlatform\Core\GraphQl\Resolver\Stage\SerializeStage; use ApiPlatform\Core\GraphQl\Serializer\ItemNormalizer; use ApiPlatform\Core\GraphQl\Serializer\SerializerContextBuilderInterface; @@ -170,11 +171,13 @@ public function testApplyBadNormalizedData(): void private function createSerializeStage(bool $paginationEnabled): SerializeStage { + $pagination = new Pagination($this->resourceMetadataFactoryProphecy->reveal(), [], ['enabled' => $paginationEnabled]); + return new SerializeStage( $this->resourceMetadataFactoryProphecy->reveal(), $this->normalizerProphecy->reveal(), $this->serializerContextBuilderProphecy->reveal(), - $paginationEnabled + $pagination ); } } diff --git a/tests/GraphQl/Type/FieldsBuilderTest.php b/tests/GraphQl/Type/FieldsBuilderTest.php index c766967e87a..2deb9703417 100644 --- a/tests/GraphQl/Type/FieldsBuilderTest.php +++ b/tests/GraphQl/Type/FieldsBuilderTest.php @@ -14,6 +14,7 @@ namespace ApiPlatform\Core\Tests\GraphQl\Type; use ApiPlatform\Core\Api\FilterInterface; +use ApiPlatform\Core\DataProvider\Pagination; use ApiPlatform\Core\GraphQl\Resolver\Factory\ResolverFactoryInterface; use ApiPlatform\Core\GraphQl\Type\FieldsBuilder; use ApiPlatform\Core\GraphQl\Type\TypeBuilderInterface; @@ -90,7 +91,7 @@ protected function setUp(): void $this->collectionResolverFactoryProphecy = $this->prophesize(ResolverFactoryInterface::class); $this->itemMutationResolverFactoryProphecy = $this->prophesize(ResolverFactoryInterface::class); $this->filterLocatorProphecy = $this->prophesize(ContainerInterface::class); - $this->fieldsBuilder = new FieldsBuilder($this->propertyNameCollectionFactoryProphecy->reveal(), $this->propertyMetadataFactoryProphecy->reveal(), $this->resourceMetadataFactoryProphecy->reveal(), $this->typesContainerProphecy->reveal(), $this->typeBuilderProphecy->reveal(), $this->typeConverterProphecy->reveal(), $this->itemResolverFactoryProphecy->reveal(), $this->collectionResolverFactoryProphecy->reveal(), $this->itemMutationResolverFactoryProphecy->reveal(), $this->filterLocatorProphecy->reveal(), true); + $this->fieldsBuilder = new FieldsBuilder($this->propertyNameCollectionFactoryProphecy->reveal(), $this->propertyMetadataFactoryProphecy->reveal(), $this->resourceMetadataFactoryProphecy->reveal(), $this->typesContainerProphecy->reveal(), $this->typeBuilderProphecy->reveal(), $this->typeConverterProphecy->reveal(), $this->itemResolverFactoryProphecy->reveal(), $this->collectionResolverFactoryProphecy->reveal(), $this->itemMutationResolverFactoryProphecy->reveal(), $this->filterLocatorProphecy->reveal(), new Pagination($this->resourceMetadataFactoryProphecy->reveal())); } public function testGetNodeQueryFields(): void