Skip to content

Commit b43fc65

Browse files
committed
Merge remote-tracking branch 'dunglas/fix-null-classname' into feat-input-output
2 parents 44170be + b08731f commit b43fc65

File tree

18 files changed

+265
-38
lines changed

18 files changed

+265
-38
lines changed

.circleci/config.yml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -164,9 +164,6 @@ jobs:
164164
behat-coverage:
165165
docker:
166166
- image: circleci/php:7.2-node-browsers
167-
environment:
168-
SYMFONY_DEPRECATIONS_HELPER: weak_vendors
169-
APP_ENV: test
170167
parallelism: 2
171168
working_directory: ~/api-platform/core
172169
steps:

.travis.yml

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,6 @@ cache:
66
- $HOME/.composer/cache
77
- $HOME/.npm
88

9-
env:
10-
global:
11-
- SYMFONY_DEPRECATIONS_HELPER=weak_vendors
12-
- APP_ENV=test
13-
149
jobs:
1510
include:
1611
- php: '7.1'

appveyor.yml

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,6 @@ clone_folder: c:\projects\api-platform\core
55
cache:
66
- '%LOCALAPPDATA%\Composer\files'
77

8-
environment:
9-
SYMFONY_DEPRECATIONS_HELPER: weak_vendors
10-
APP_ENV: test
11-
128
install:
139
- ps: Set-Service wuauserv -StartupType Manual
1410
- cinst -y php composer

features/bootstrap/FeatureContext.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\FooDummy;
3636
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\FourthLevel;
3737
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\Greeting;
38+
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\MaxDepthDummy;
3839
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\Node;
3940
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\Person;
4041
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\PersonToPet;
@@ -1024,4 +1025,22 @@ public function thereIsAPersonWithAGreeting(string $name, string $message)
10241025
$this->manager->flush();
10251026
$this->manager->clear();
10261027
}
1028+
1029+
/**
1030+
* @Given there is a max depth dummy with :level level of descendants
1031+
*/
1032+
public function thereIsAMaxDepthDummyWithLevelOfDescendants(int $level)
1033+
{
1034+
$maxDepthDummy = new MaxDepthDummy();
1035+
$maxDepthDummy->name = "level $level";
1036+
$this->manager->persist($maxDepthDummy);
1037+
1038+
for ($i = 1; $i <= $level; ++$i) {
1039+
$maxDepthDummy = $maxDepthDummy->child = new MaxDepthDummy();
1040+
$maxDepthDummy->name = 'level '.($i + 1);
1041+
$this->manager->persist($maxDepthDummy);
1042+
}
1043+
1044+
$this->manager->flush();
1045+
}
10271046
}

features/doctrine/search_filter.feature

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,35 @@ Feature: Search filter on collections
374374
}
375375
"""
376376

377+
@sqlite
378+
Scenario: Search for entities with an existing collection route name
379+
When I send a "GET" request to "/dummies?relatedDummies=dummy_cars"
380+
Then the response status code should be 200
381+
And the response should be in JSON
382+
And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8"
383+
And the JSON should be valid according to this schema:
384+
"""
385+
{
386+
"type": "object",
387+
"properties": {
388+
"@context": {"pattern": "^/contexts/Dummy$"},
389+
"@id": {"pattern": "^/dummies$"},
390+
"@type": {"pattern": "^hydra:Collection$"},
391+
"hydra:member": {
392+
"type": "array",
393+
"maxItems": 0
394+
},
395+
"hydra:view": {
396+
"type": "object",
397+
"properties": {
398+
"@id": {"pattern": "^/dummies\\?relatedDummies=dummy_cars"},
399+
"@type": {"pattern": "^hydra:PartialCollectionView$"}
400+
}
401+
}
402+
}
403+
}
404+
"""
405+
377406
@createSchema
378407
Scenario: Search related collection by name
379408
Given there are 3 dummy objects having each 3 relatedDummies

features/hal/max_depth.feature

Lines changed: 52 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ Feature: Max depth handling
77
Scenario: Create a resource with 1 level of descendants
88
When I add "Accept" header equal to "application/hal+json"
99
And I add "Content-Type" header equal to "application/json"
10-
And I send a "POST" request to "/max_depth_dummies" with body:
10+
And I send a "POST" request to "/max_depth_eager_dummies" with body:
1111
"""
1212
{
1313
"name": "level 1",
@@ -24,17 +24,17 @@ Feature: Max depth handling
2424
{
2525
"_links": {
2626
"self": {
27-
"href": "\/max_depth_dummies\/1"
27+
"href": "\/max_depth_eager_dummies\/1"
2828
},
2929
"child": {
30-
"href": "\/max_depth_dummies\/2"
30+
"href": "\/max_depth_eager_dummies\/2"
3131
}
3232
},
3333
"_embedded": {
3434
"child": {
3535
"_links": {
3636
"self": {
37-
"href": "\/max_depth_dummies\/2"
37+
"href": "\/max_depth_eager_dummies\/2"
3838
}
3939
},
4040
"id": 2,
@@ -46,8 +46,54 @@ Feature: Max depth handling
4646
}
4747
"""
4848

49-
@dropSchema
5049
Scenario: Add a 2nd level of descendants
50+
When I add "Accept" header equal to "application/hal+json"
51+
And I add "Content-Type" header equal to "application/json"
52+
And I send a "PUT" request to "max_depth_eager_dummies/1" with body:
53+
"""
54+
{
55+
"id": "/max_depth_eager_dummies/1",
56+
"child": {
57+
"id": "/max_depth_eager_dummies/2",
58+
"child": {
59+
"name": "level 3"
60+
}
61+
}
62+
}
63+
"""
64+
And the response status code should be 200
65+
And the response should be in JSON
66+
And the header "Content-Type" should be equal to "application/hal+json; charset=utf-8"
67+
And the JSON should be equal to:
68+
"""
69+
{
70+
"_links": {
71+
"self": {
72+
"href": "\/max_depth_eager_dummies\/1"
73+
},
74+
"child": {
75+
"href": "\/max_depth_eager_dummies\/2"
76+
}
77+
},
78+
"_embedded": {
79+
"child": {
80+
"_links": {
81+
"self": {
82+
"href": "\/max_depth_eager_dummies\/2"
83+
}
84+
},
85+
"id": 2,
86+
"name": "level 2"
87+
}
88+
},
89+
"id": 1,
90+
"name": "level 1"
91+
}
92+
"""
93+
94+
@dropSchema
95+
Scenario: Add a 2nd level of descendants when eager fetching is disabled
96+
Given there is a max depth dummy with 1 level of descendants
5197
When I add "Accept" header equal to "application/hal+json"
5298
And I add "Content-Type" header equal to "application/json"
5399
And I send a "PUT" request to "max_depth_dummies/1" with body:
@@ -62,7 +108,7 @@ Feature: Max depth handling
62108
}
63109
}
64110
"""
65-
And the response status code should be 200
111+
Then the response status code should be 200
66112
And the response should be in JSON
67113
And the header "Content-Type" should be equal to "application/hal+json; charset=utf-8"
68114
And the JSON should be equal to:

features/jsonld/max_depth.feature

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ Feature: Max depth handling
66
@createSchema
77
Scenario: Create a resource with 1 level of descendants
88
When I add "Content-Type" header equal to "application/ld+json"
9-
And I send a "POST" request to "/max_depth_dummies" with body:
9+
And I send a "POST" request to "/max_depth_eager_dummies" with body:
1010
"""
1111
{
1212
"name": "level 1",
@@ -21,14 +21,14 @@ Feature: Max depth handling
2121
And the JSON should be equal to:
2222
"""
2323
{
24-
"@context": "/contexts/MaxDepthDummy",
25-
"@id": "/max_depth_dummies/1",
26-
"@type": "MaxDepthDummy",
24+
"@context": "/contexts/MaxDepthEagerDummy",
25+
"@id": "/max_depth_eager_dummies/1",
26+
"@type": "MaxDepthEagerDummy",
2727
"id": 1,
2828
"name": "level 1",
2929
"child": {
30-
"@id": "/max_depth_dummies/2",
31-
"@type": "MaxDepthDummy",
30+
"@id": "/max_depth_eager_dummies/2",
31+
"@type": "MaxDepthEagerDummy",
3232
"id": 2,
3333
"name": "level 2"
3434
}
@@ -38,12 +38,12 @@ Feature: Max depth handling
3838
@dropSchema
3939
Scenario: Add a 2nd level of descendants
4040
When I add "Content-Type" header equal to "application/ld+json"
41-
And I send a "PUT" request to "max_depth_dummies/1" with body:
41+
And I send a "PUT" request to "max_depth_eager_dummies/1" with body:
4242
"""
4343
{
44-
"@id": "/max_depth_dummies/1",
44+
"@id": "/max_depth_eager_dummies/1",
4545
"child": {
46-
"@id": "/max_depth_dummies/2",
46+
"@id": "/max_depth_eager_dummies/2",
4747
"child": {
4848
"name": "level 3"
4949
}
@@ -56,14 +56,14 @@ Feature: Max depth handling
5656
And the JSON should be equal to:
5757
"""
5858
{
59-
"@context": "/contexts/MaxDepthDummy",
60-
"@id": "/max_depth_dummies/1",
61-
"@type": "MaxDepthDummy",
59+
"@context": "/contexts/MaxDepthEagerDummy",
60+
"@id": "/max_depth_eager_dummies/1",
61+
"@type": "MaxDepthEagerDummy",
6262
"id": 1,
6363
"name": "level 1",
6464
"child": {
65-
"@id": "/max_depth_dummies/2",
66-
"@type": "MaxDepthDummy",
65+
"@id": "/max_depth_eager_dummies/2",
66+
"@type": "MaxDepthEagerDummy",
6767
"id": 2,
6868
"name": "level 2"
6969
}

phpunit.xml.dist

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
<php>
1111
<ini name="error_reporting" value="-1" />
1212
<ini name="memory_limit" value="-1" />
13+
<env name="SYMFONY_DEPRECATIONS_HELPER" value="weak_vendors" />
1314
<server name="KERNEL_DIR" value="tests/Fixtures/app/" />
1415
<server name="KERNEL_CLASS" value="AppKernel" />
1516
<server name="APP_ENV" value="test" />

src/Bridge/Doctrine/Common/DataPersister.php

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
use ApiPlatform\Core\Util\ClassInfoTrait;
1818
use Doctrine\Common\Persistence\ManagerRegistry;
1919
use Doctrine\Common\Persistence\ObjectManager as DoctrineObjectManager;
20+
use Doctrine\ORM\Mapping\ClassMetadataInfo;
2021

2122
/**
2223
* Data persister for Doctrine.
@@ -51,7 +52,7 @@ public function persist($data)
5152
return $data;
5253
}
5354

54-
if (!$manager->contains($data)) {
55+
if (!$manager->contains($data) || $this->isDeferredExplicit($manager, $data)) {
5556
$manager->persist($data);
5657
}
5758

@@ -84,4 +85,19 @@ private function getManager($data)
8485
{
8586
return \is_object($data) ? $this->managerRegistry->getManagerForClass($this->getObjectClass($data)) : null;
8687
}
88+
89+
/**
90+
* Checks if doctrine does not manage data automatically.
91+
*
92+
* @return bool
93+
*/
94+
private function isDeferredExplicit(DoctrineObjectManager $manager, $data)
95+
{
96+
$classMetadata = $manager->getClassMetadata($this->getObjectClass($data));
97+
if ($classMetadata instanceof ClassMetadataInfo && \method_exists($classMetadata, 'isChangeTrackingDeferredExplicit')) {
98+
return $classMetadata->isChangeTrackingDeferredExplicit();
99+
}
100+
101+
return false;
102+
}
87103
}

src/Bridge/Symfony/Routing/IriConverter.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,10 @@ public function getItemFromIri(string $iri, array $context = [])
7979
throw new InvalidArgumentException(sprintf('No resource associated to "%s".', $iri));
8080
}
8181

82+
if (isset($parameters['_api_collection_operation_name'])) {
83+
throw new InvalidArgumentException(sprintf('The iri "%s" references a collection not an item.', $iri));
84+
}
85+
8286
$attributes = AttributesExtractor::extractAttributes($parameters);
8387

8488
try {

src/DataPersister/ChainDataPersister.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ public function remove($data)
6363
{
6464
foreach ($this->persisters as $persister) {
6565
if ($persister->supports($data)) {
66-
$persister->remove($data);
66+
return $persister->remove($data);
6767
}
6868
}
6969
}

src/GraphQl/Type/SchemaBuilder.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,10 @@ private function convertType(Type $type, bool $input = false, string $mutationNa
366366
}
367367

368368
$resourceClass = $this->isCollection($type) ? $type->getCollectionValueType()->getClassName() : $type->getClassName();
369+
if (null === $resourceClass) {
370+
return null;
371+
}
372+
369373
try {
370374
$resourceMetadata = $this->resourceMetadataFactory->create($resourceClass);
371375
if ([] === $resourceMetadata->getGraphql() ?? []) {

src/Hal/Serializer/ItemNormalizer.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use ApiPlatform\Core\Exception\RuntimeException;
1717
use ApiPlatform\Core\Serializer\AbstractItemNormalizer;
1818
use ApiPlatform\Core\Serializer\ContextTrait;
19+
use ApiPlatform\Core\Util\ClassInfoTrait;
1920
use Symfony\Component\Serializer\Mapping\AttributeMetadataInterface;
2021

2122
/**
@@ -26,6 +27,7 @@
2627
final class ItemNormalizer extends AbstractItemNormalizer
2728
{
2829
use ContextTrait;
30+
use ClassInfoTrait;
2931

3032
const FORMAT = 'jsonhal';
3133

@@ -102,7 +104,7 @@ protected function getAttributes($object, $format = null, array $context)
102104
*/
103105
private function getComponents($object, string $format = null, array $context)
104106
{
105-
$cacheKey = \get_class($object).'-'.$context['cache_key'];
107+
$cacheKey = $this->getObjectClass($object).'-'.$context['cache_key'];
106108

107109
if (isset($this->componentsCache[$cacheKey])) {
108110
return $this->componentsCache[$cacheKey];
@@ -160,7 +162,7 @@ private function getComponents($object, string $format = null, array $context)
160162
*/
161163
private function populateRelation(array $data, $object, string $format = null, array $context, array $components, string $type): array
162164
{
163-
$class = \get_class($object);
165+
$class = $this->getObjectClass($object);
164166

165167
$attributesMetadata = \array_key_exists($class, $this->attributesMetadataCache) ?
166168
$this->attributesMetadataCache[$class] :

0 commit comments

Comments
 (0)