Skip to content

Commit 92d45aa

Browse files
authored
Allow to push relations to the client using HTTP/2 (#2450)
* Allow to push relations to the client using HTTP/2 * Simplify code * Fix CS * Minor fix * Update features/push_relations/push.feature Co-Authored-By: dunglas <[email protected]> * Fix the deps=low build
1 parent 78c9bdb commit 92d45aa

File tree

9 files changed

+53
-8
lines changed

9 files changed

+53
-8
lines changed

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545
"phpdocumentor/reflection-docblock": "^3.0 || ^4.0",
4646
"phpdocumentor/type-resolver": "^0.3 || ^0.4",
4747
"phpspec/prophecy": "^1.8",
48-
"phpunit/phpunit": "^7.5.1",
48+
"phpunit/phpunit": "^7.5.2",
4949
"psr/log": "^1.0",
5050
"ramsey/uuid": "^3.7",
5151
"ramsey/uuid-doctrine": "^1.4",

features/push_relations/push.feature

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
@sqlite
2+
Feature: Push relations using HTTP/2
3+
In order to have a fast API
4+
As an API software developer
5+
I need to push relations using HTTP/2
6+
7+
@createSchema
8+
Scenario: Push the relations of a collection of items
9+
Given there are 2 dummy objects with relatedDummy
10+
When I add "Content-Type" header equal to "application/ld+json"
11+
And I send a "GET" request to "/dummies"
12+
Then the header "Link" should be equal to '</related_dummies/1>; rel="preload",</related_dummies/2>; rel="preload",<http://example.com/docs.jsonld>; rel="http://www.w3.org/ns/hydra/core#apiDocumentation"'
13+
14+
Scenario: Push the relations of an item
15+
When I add "Content-Type" header equal to "application/ld+json"
16+
And I send a "GET" request to "/dummies/1"
17+
Then the header "Link" should be equal to '</related_dummies/1>; rel="preload",<http://example.com/docs.jsonld>; rel="http://www.w3.org/ns/hydra/core#apiDocumentation"'

src/Annotation/ApiProperty.php

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
* @Attribute("fetchEager", type="bool"),
2929
* @Attribute("openapiContext", type="array"),
3030
* @Attribute("jsonldContext", type="array"),
31+
* @Attribute("push", type="bool"),
3132
* @Attribute("swaggerContext", type="array")
3233
* )
3334
*/
@@ -108,14 +109,21 @@ final class ApiProperty
108109
*
109110
* @var array
110111
*/
111-
private $swaggerContext;
112+
private $openapiContext;
113+
114+
/**
115+
* @see https://github.com/Haehnchen/idea-php-annotation-plugin/issues/112
116+
*
117+
* @var bool
118+
*/
119+
private $push;
112120

113121
/**
114122
* @see https://github.com/Haehnchen/idea-php-annotation-plugin/issues/112
115123
*
116124
* @var array
117125
*/
118-
private $openapiContext;
126+
private $swaggerContext;
119127

120128
/**
121129
* @throws InvalidArgumentException

src/EventListener/DeserializeListener.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,8 @@ public function onKernelRequest(GetResponseEvent $event)
6464
$request = $event->getRequest();
6565
$method = $request->getMethod();
6666
if (
67-
$request->isMethodSafe(false)
68-
|| 'DELETE' === $method
67+
'DELETE' === $method
68+
|| $request->isMethodSafe(false)
6969
|| !($attributes = RequestAttributesExtractor::extractAttributes($request))
7070
|| false === ($attributes['input_class'] ?? null)
7171
|| !$attributes['receive']

src/EventListener/SerializeListener.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
use ApiPlatform\Core\Serializer\ResourceList;
1818
use ApiPlatform\Core\Serializer\SerializerContextBuilderInterface;
1919
use ApiPlatform\Core\Util\RequestAttributesExtractor;
20+
use Fig\Link\GenericLinkProvider;
21+
use Fig\Link\Link;
2022
use Symfony\Component\HttpFoundation\Request;
2123
use Symfony\Component\HttpFoundation\Response;
2224
use Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent;
@@ -77,11 +79,23 @@ public function onKernelView(GetResponseForControllerResultEvent $event)
7779
$resources = new ResourceList();
7880
$context['resources'] = &$resources;
7981

82+
$resourcesToPush = new ResourceList();
83+
$context['resources_to_push'] = &$resourcesToPush;
84+
8085
$request->attributes->set('_api_normalization_context', $context);
8186

8287
$event->setControllerResult($this->serializer->serialize($controllerResult, $request->getRequestFormat(), $context));
8388

8489
$request->attributes->set('_resources', $request->attributes->get('_resources', []) + (array) $resources);
90+
if (!\count($resourcesToPush)) {
91+
return;
92+
}
93+
94+
$linkProvider = $request->attributes->get('_links', new GenericLinkProvider());
95+
foreach ($resourcesToPush as $resourceToPush) {
96+
$linkProvider = $linkProvider->withLink(new Link('preload', $resourceToPush));
97+
}
98+
$request->attributes->set('_links', $linkProvider);
8599
}
86100

87101
/**

src/Serializer/AbstractItemNormalizer.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -531,7 +531,7 @@ protected function normalizeCollectionOfRelations(PropertyMetadata $propertyMeta
531531
*/
532532
protected function normalizeRelation(PropertyMetadata $propertyMetadata, $relatedObject, string $resourceClass, string $format = null, array $context)
533533
{
534-
if (null === $relatedObject || $propertyMetadata->isReadableLink() || !empty($context['attributes'])) {
534+
if (null === $relatedObject || !empty($context['attributes']) || $propertyMetadata->isReadableLink()) {
535535
if (null === $relatedObject) {
536536
unset($context['resource_class']);
537537
} else {
@@ -548,6 +548,9 @@ protected function normalizeRelation(PropertyMetadata $propertyMetadata, $relate
548548
if (isset($context['resources'])) {
549549
$context['resources'][$iri] = $iri;
550550
}
551+
if (isset($context['resources_to_push']) && $propertyMetadata->getAttribute('push', false)) {
552+
$context['resources_to_push'][$iri] = $iri;
553+
}
551554

552555
return $iri;
553556
}

tests/Annotation/ApiPropertyTest.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ public function testConstruct()
5454
'jsonldContext' => ['foo' => 'bar'],
5555
'swaggerContext' => ['foo' => 'baz'],
5656
'openapiContext' => ['foo' => 'baz'],
57+
'push' => true,
5758
'attributes' => ['unknown' => 'unknown', 'fetchable' => false],
5859
]);
5960
$this->assertEquals([
@@ -63,6 +64,7 @@ public function testConstruct()
6364
'jsonld_context' => ['foo' => 'bar'],
6465
'swagger_context' => ['foo' => 'baz'],
6566
'openapi_context' => ['foo' => 'baz'],
67+
'push' => true,
6668
'unknown' => 'unknown',
6769
], $property->attributes);
6870
}

tests/EventListener/SerializeListenerTest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ public function testSerializeCollectionOperation()
113113
'xml',
114114
Argument::allOf(
115115
Argument::that(function (array $context) {
116-
return $context['resources'] instanceof ResourceList;
116+
return $context['resources'] instanceof ResourceList && $context['resources_to_push'] instanceof ResourceList;
117117
}),
118118
Argument::withEntry('request_uri', ''),
119119
Argument::withEntry('resource_class', 'Foo'),
@@ -168,7 +168,7 @@ public function testSerializeItemOperation()
168168
'xml',
169169
Argument::allOf(
170170
Argument::that(function (array $context) {
171-
return $context['resources'] instanceof ResourceList;
171+
return $context['resources'] instanceof ResourceList && $context['resources_to_push'] instanceof ResourceList;
172172
}),
173173
Argument::withEntry('request_uri', ''),
174174
Argument::withEntry('resource_class', 'Foo'),

tests/Fixtures/TestBundle/Entity/Dummy.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ class Dummy
120120
* @var RelatedDummy A related dummy
121121
*
122122
* @ORM\ManyToOne(targetEntity="RelatedDummy")
123+
* @ApiProperty(push=true)
123124
*/
124125
public $relatedDummy;
125126

0 commit comments

Comments
 (0)