Skip to content

Commit 8f9087f

Browse files
Merge branch '2.5' into patch/dto-output-class-same-as-original
2 parents 7724bde + 3137ed0 commit 8f9087f

File tree

9 files changed

+73
-174
lines changed

9 files changed

+73
-174
lines changed

features/bootstrap/DoctrineContext.php

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,6 @@
7373
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\CompositeLabel;
7474
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\CompositePrimitiveItem;
7575
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\CompositeRelation;
76-
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\Container;
7776
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\ConvertedBoolean;
7877
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\ConvertedDate;
7978
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\ConvertedInteger;
@@ -111,7 +110,6 @@
111110
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\Greeting;
112111
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\InternalUser;
113112
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\MaxDepthDummy;
114-
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\Node;
115113
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\Order;
116114
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\Person;
117115
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\PersonToPet;
@@ -1055,25 +1053,6 @@ public function thereIsAnAnswerToTheQuestion(string $a, string $q)
10551053
$this->manager->clear();
10561054
}
10571055

1058-
/**
1059-
* @Given there are :nb nodes in a container :uuid
1060-
*/
1061-
public function thereAreNodesInAContainer(int $nb, string $uuid)
1062-
{
1063-
$container = new Container();
1064-
$container->setId($uuid);
1065-
$this->manager->persist($container);
1066-
1067-
for ($i = 0; $i < $nb; ++$i) {
1068-
$node = new Node();
1069-
$node->setContainer($container);
1070-
$node->setSerial($i);
1071-
$this->manager->persist($node);
1072-
}
1073-
1074-
$this->manager->flush();
1075-
}
1076-
10771056
/**
10781057
* @Then the password :password for user :user should be hashed
10791058
*/

features/graphql/introspection.feature

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -426,7 +426,7 @@ Feature: GraphQL introspection support
426426
And the JSON node "data.typeCreatePayloadData.fields[3].type.name" should be equal to "createDummyGroupNestedPayload"
427427
And the JSON node "data.typeCreateNestedPayload.fields[0].name" should be equal to "id"
428428

429-
Scenario: Retrieve an item through a GraphQL query
429+
Scenario: Retrieve a type name through a GraphQL query
430430
Given there are 4 dummy objects with relatedDummy
431431
When I send the following GraphQL request:
432432
"""
@@ -447,3 +447,28 @@ Feature: GraphQL introspection support
447447
And the JSON node "data.dummy.name" should be equal to "Dummy #3"
448448
And the JSON node "data.dummy.relatedDummy.name" should be equal to "RelatedDummy #3"
449449
And the JSON node "data.dummy.relatedDummy.__typename" should be equal to "RelatedDummy"
450+
451+
Scenario: Introspect a type available only through relations
452+
When I send the following GraphQL request:
453+
"""
454+
{
455+
typeNotAvailable: __type(name: "VoDummyInspectionConnection") {
456+
description
457+
}
458+
typeOwner: __type(name: "VoDummyCar") {
459+
description,
460+
fields {
461+
name
462+
type {
463+
name
464+
}
465+
}
466+
}
467+
}
468+
"""
469+
Then the response status code should be 200
470+
And the response should be in JSON
471+
And the header "Content-Type" should be equal to "application/json"
472+
And the JSON node "errors[0].debugMessage" should be equal to 'Type with id "VoDummyInspectionConnection" is not present in the types container'
473+
And the JSON node "data.typeNotAvailable" should be null
474+
And the JSON node "data.typeOwner.fields[3].type.name" should be equal to "VoDummyInspectionConnection"

src/Bridge/Doctrine/Orm/Filter/SearchFilter.php

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -92,11 +92,12 @@ protected function filterProperty(string $property, $value, QueryBuilder $queryB
9292
$caseSensitive = true;
9393
$metadata = $this->getNestedMetadata($resourceClass, $associations);
9494

95-
$doctrineTypeField = $this->getDoctrineFieldType($property, $resourceClass);
96-
$values = array_map([$this, 'getIdFromValue'], $values);
97-
9895
if ($metadata->hasField($field)) {
99-
if (!$this->hasValidValues($values, $doctrineTypeField)) {
96+
if ('id' === $field) {
97+
$values = array_map([$this, 'getIdFromValue'], $values);
98+
}
99+
100+
if (!$this->hasValidValues($values, $this->getDoctrineFieldType($property, $resourceClass))) {
100101
$this->logger->notice('Invalid filter ignored', [
101102
'exception' => new InvalidArgumentException(sprintf('Values for field "%s" are not valid according to the doctrine type.', $field)),
102103
]);
@@ -113,7 +114,7 @@ protected function filterProperty(string $property, $value, QueryBuilder $queryB
113114
}
114115

115116
if (1 === \count($values)) {
116-
$this->addWhereByStrategy($strategy, $queryBuilder, $queryNameGenerator, $alias, $field, $doctrineTypeField, $values[0], $caseSensitive);
117+
$this->addWhereByStrategy($strategy, $queryBuilder, $queryNameGenerator, $alias, $field, $values[0], $caseSensitive);
117118

118119
return;
119120
}
@@ -139,7 +140,9 @@ protected function filterProperty(string $property, $value, QueryBuilder $queryB
139140
return;
140141
}
141142

143+
$values = array_map([$this, 'getIdFromValue'], $values);
142144
$associationFieldIdentifier = 'id';
145+
$doctrineTypeField = $this->getDoctrineFieldType($property, $resourceClass);
143146

144147
if (null !== $this->identifiersExtractor) {
145148
$associationResourceClass = $metadata->getAssociationTargetClass($field);
@@ -168,11 +171,11 @@ protected function filterProperty(string $property, $value, QueryBuilder $queryB
168171
if (1 === \count($values)) {
169172
$queryBuilder
170173
->andWhere(sprintf('%s.%s = :%s', $associationAlias, $associationField, $valueParameter))
171-
->setParameter($valueParameter, $values[0], $doctrineTypeField);
174+
->setParameter($valueParameter, $values[0]);
172175
} else {
173176
$queryBuilder
174177
->andWhere(sprintf('%s.%s IN (:%s)', $associationAlias, $associationField, $valueParameter))
175-
->setParameter($valueParameter, $values, $doctrineTypeField);
178+
->setParameter($valueParameter, $values);
176179
}
177180
}
178181

@@ -181,7 +184,7 @@ protected function filterProperty(string $property, $value, QueryBuilder $queryB
181184
*
182185
* @throws InvalidArgumentException If strategy does not exist
183186
*/
184-
protected function addWhereByStrategy(string $strategy, QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $alias, string $field, $fieldType, $value, bool $caseSensitive)
187+
protected function addWhereByStrategy(string $strategy, QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $alias, string $field, $value, bool $caseSensitive)
185188
{
186189
$wrapCase = $this->createWrapCase($caseSensitive);
187190
$valueParameter = $queryNameGenerator->generateParameterName($field);
@@ -191,27 +194,27 @@ protected function addWhereByStrategy(string $strategy, QueryBuilder $queryBuild
191194
case self::STRATEGY_EXACT:
192195
$queryBuilder
193196
->andWhere(sprintf($wrapCase('%s.%s').' = '.$wrapCase(':%s'), $alias, $field, $valueParameter))
194-
->setParameter($valueParameter, $value, $fieldType);
197+
->setParameter($valueParameter, $value);
195198
break;
196199
case self::STRATEGY_PARTIAL:
197200
$queryBuilder
198201
->andWhere(sprintf($wrapCase('%s.%s').' LIKE '.$wrapCase('CONCAT(\'%%\', :%s, \'%%\')'), $alias, $field, $valueParameter))
199-
->setParameter($valueParameter, $value, $fieldType);
202+
->setParameter($valueParameter, $value);
200203
break;
201204
case self::STRATEGY_START:
202205
$queryBuilder
203206
->andWhere(sprintf($wrapCase('%s.%s').' LIKE '.$wrapCase('CONCAT(:%s, \'%%\')'), $alias, $field, $valueParameter))
204-
->setParameter($valueParameter, $value, $fieldType);
207+
->setParameter($valueParameter, $value);
205208
break;
206209
case self::STRATEGY_END:
207210
$queryBuilder
208211
->andWhere(sprintf($wrapCase('%s.%s').' LIKE '.$wrapCase('CONCAT(\'%%\', :%s)'), $alias, $field, $valueParameter))
209-
->setParameter($valueParameter, $value, $fieldType);
212+
->setParameter($valueParameter, $value);
210213
break;
211214
case self::STRATEGY_WORD_START:
212215
$queryBuilder
213216
->andWhere(sprintf($wrapCase('%1$s.%2$s').' LIKE '.$wrapCase('CONCAT(:%3$s, \'%%\')').' OR '.$wrapCase('%1$s.%2$s').' LIKE '.$wrapCase('CONCAT(\'%% \', :%3$s, \'%%\')'), $alias, $field, $valueParameter))
214-
->setParameter($valueParameter, $value, $fieldType);
217+
->setParameter($valueParameter, $value);
215218
break;
216219
default:
217220
throw new InvalidArgumentException(sprintf('strategy %s does not exist.', $strategy));

src/GraphQl/Type/TypeConverter.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,9 +105,12 @@ private function getResourceType(Type $type, bool $input, ?string $queryName, ?s
105105

106106
try {
107107
$resourceMetadata = $this->resourceMetadataFactory->create($resourceClass);
108-
if ([] === ($resourceMetadata->getGraphql() ?? [])) {
108+
if (null === $resourceMetadata->getGraphql()) {
109109
return null;
110110
}
111+
if ('Node' === $resourceMetadata->getShortName()) {
112+
throw new \UnexpectedValueException('A "Node" resource cannot be used with GraphQL because the type is already used by the Relay specification.');
113+
}
111114
} catch (ResourceClassNotFoundException $e) {
112115
// Skip objects that are not resources for now
113116
return null;

tests/Fixtures/TestBundle/Document/VoDummyInspection.php

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,13 @@
1919
use Symfony\Component\Serializer\Annotation\Groups;
2020

2121
/**
22-
* @ApiResource(attributes={
23-
* "normalization_context"={"groups"={"inspection_read"}},
24-
* "denormalization_context"={"groups"={"inspection_write"}}
25-
* })
22+
* @ApiResource(
23+
* attributes={
24+
* "normalization_context"={"groups"={"inspection_read"}},
25+
* "denormalization_context"={"groups"={"inspection_write"}}
26+
* },
27+
* graphql={}
28+
* )
2629
* @ODM\Document
2730
*/
2831
class VoDummyInspection

tests/Fixtures/TestBundle/Entity/Container.php

Lines changed: 0 additions & 68 deletions
This file was deleted.

tests/Fixtures/TestBundle/Entity/Node.php

Lines changed: 0 additions & 62 deletions
This file was deleted.

tests/Fixtures/TestBundle/Entity/VoDummyInspection.php

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,13 @@
1919
use Symfony\Component\Serializer\Annotation\Groups;
2020

2121
/**
22-
* @ApiResource(attributes={
23-
* "normalization_context"={"groups"={"inspection_read"}},
24-
* "denormalization_context"={"groups"={"inspection_write"}}
25-
* })
22+
* @ApiResource(
23+
* attributes={
24+
* "normalization_context"={"groups"={"inspection_read"}},
25+
* "denormalization_context"={"groups"={"inspection_write"}}
26+
* },
27+
* graphql={}
28+
* )
2629
* @ORM\Entity
2730
*/
2831
class VoDummyInspection

tests/GraphQl/Type/TypeConverterTest.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,19 @@ public function testConvertTypeNoGraphQlResourceMetadata(): void
9696
$this->assertNull($graphqlType);
9797
}
9898

99+
public function testConvertTypeNodeResource(): void
100+
{
101+
$type = new Type(Type::BUILTIN_TYPE_OBJECT, false, 'node');
102+
103+
$this->typeBuilderProphecy->isCollection($type)->shouldBeCalled()->willReturn(false);
104+
$this->resourceMetadataFactoryProphecy->create('node')->shouldBeCalled()->willReturn((new ResourceMetadata('Node'))->withGraphql(['test']));
105+
106+
$this->expectException(\UnexpectedValueException::class);
107+
$this->expectExceptionMessage('A "Node" resource cannot be used with GraphQL because the type is already used by the Relay specification.');
108+
109+
$this->typeConverter->convertType($type, false, null, null, 'resourceClass', 'rootClass', null, 0);
110+
}
111+
99112
public function testConvertTypeResourceClassNotFound(): void
100113
{
101114
$type = new Type(Type::BUILTIN_TYPE_OBJECT, false, 'dummy');

0 commit comments

Comments
 (0)