Skip to content

Commit 840fc22

Browse files
committed
Merge branch '2.2'
2 parents 98a6407 + d976f4b commit 840fc22

26 files changed

+485
-50
lines changed

.php_cs.dist

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,14 @@ $finder = PhpCsFixer\Finder::create()
1717
return PhpCsFixer\Config::create()
1818
->setRiskyAllowed(true)
1919
->setRules([
20+
'@DoctrineAnnotation' => true,
21+
'doctrine_annotation_array_assignment' => [
22+
'operator' => '=',
23+
],
24+
'doctrine_annotation_spaces' => [
25+
'after_array_assignments_equals' => false,
26+
'before_array_assignments_equals' => false,
27+
],
2028
'@Symfony' => true,
2129
'@Symfony:risky' => true,
2230
'array_syntax' => [

features/main/relation.feature

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -512,7 +512,8 @@ Feature: Relations support
512512
}
513513
"""
514514

515-
Scenario: Issue #1547 Passing wrong argument to a relation
515+
@dropSchema
516+
Scenario: Passing an invalid IRI to a relation
516517
When I add "Content-Type" header equal to "application/ld+json"
517518
And I send a "POST" request to "/relation_embedders" with body:
518519
"""
@@ -523,8 +524,11 @@ Feature: Relations support
523524
Then the response status code should be 400
524525
And the response should be in JSON
525526
And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8"
526-
And the JSON node "hydra:description" should contain "Expected IRI or nested document"
527+
And the JSON node "hydra:description" should contain "Invalid value provided (invalid IRI?)."
527528

529+
@wip
530+
@dropSchema
531+
Scenario: Passing an invalid type to a relation
528532
When I add "Content-Type" header equal to "application/ld+json"
529533
And I send a "POST" request to "/relation_embedders" with body:
530534
"""
@@ -535,4 +539,4 @@ Feature: Relations support
535539
Then the response status code should be 400
536540
And the response should be in JSON
537541
And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8"
538-
And the JSON node "hydra:description" should contain "Expected IRI or nested document"
542+
And the JSON node "hydra:description" should contain "Invalid value provided (invalid IRI?)."

src/Bridge/Doctrine/Orm/Extension/FilterEagerLoadingExtension.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ private function getQueryBuilderWithNewAliases(QueryBuilder $queryBuilder, Query
138138
$alias = substr($joinString, 0, $pos);
139139
$association = substr($joinString, $pos + 1);
140140
$condition = str_replace($aliases, $replacements, $joinPart->getCondition());
141-
$newAlias = QueryBuilderHelper::addJoinOnce($queryBuilderClone, $queryNameGenerator, $alias, $association, $joinPart->getJoinType(), $joinPart->getConditionType(), $condition);
141+
$newAlias = QueryBuilderHelper::addJoinOnce($queryBuilderClone, $queryNameGenerator, $alias, $association, $joinPart->getJoinType(), $joinPart->getConditionType(), $condition, $originAlias);
142142
$aliases[] = "{$joinPart->getAlias()}.";
143143
$replacements[] = "$newAlias.";
144144
}

src/Bridge/Doctrine/Orm/Util/QueryBuilderHelper.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,9 @@ private function __construct()
3030
/**
3131
* Adds a join to the queryBuilder if none exists.
3232
*/
33-
public static function addJoinOnce(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $alias, string $association, string $joinType = null, string $conditionType = null, string $condition = null): string
33+
public static function addJoinOnce(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $alias, string $association, string $joinType = null, string $conditionType = null, string $condition = null, string $originAlias = null): string
3434
{
35-
$join = self::getExistingJoin($queryBuilder, $alias, $association);
35+
$join = self::getExistingJoin($queryBuilder, $alias, $association, $originAlias);
3636

3737
if (null !== $join) {
3838
return $join->getAlias();
@@ -55,10 +55,10 @@ public static function addJoinOnce(QueryBuilder $queryBuilder, QueryNameGenerato
5555
*
5656
* @return Join|null
5757
*/
58-
private static function getExistingJoin(QueryBuilder $queryBuilder, string $alias, string $association)
58+
private static function getExistingJoin(QueryBuilder $queryBuilder, string $alias, string $association, string $originAlias = null)
5959
{
6060
$parts = $queryBuilder->getDQLPart('join');
61-
$rootAlias = $queryBuilder->getRootAliases()[0];
61+
$rootAlias = $originAlias ?? $queryBuilder->getRootAliases()[0];
6262

6363
if (!isset($parts[$rootAlias])) {
6464
return null;

src/Bridge/Symfony/Bundle/Resources/config/swagger.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@
3131
<tag name="serializer.normalizer" priority="16" />
3232
</service>
3333

34+
<service id="api_platform.swagger.normalizer.api_gateway" class="ApiPlatform\Core\Swagger\Serializer\ApiGatewayNormalizer" public="false" decorates="api_platform.swagger.normalizer.documentation">
35+
<argument type="service" id="api_platform.swagger.normalizer.api_gateway.inner" />
36+
<tag name="serializer.normalizer" priority="17" />
37+
</service>
38+
3439
<service id="api_platform.swagger.command.swagger_command" class="ApiPlatform\Core\Bridge\Symfony\Bundle\Command\SwaggerCommand">
3540
<argument type="service" id="api_platform.swagger.normalizer.documentation" />
3641
<argument type="service" id="api_platform.metadata.resource.name_collection_factory" />

src/Documentation/Action/DocumentationAction.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,11 @@ public function __construct(ResourceNameCollectionFactoryInterface $resourceName
4242
public function __invoke(Request $request = null): Documentation
4343
{
4444
if (null !== $request) {
45-
$request->attributes->set('_api_normalization_context', $request->attributes->get('_api_normalization_context', []) + ['base_url' => $request->getBaseUrl()]);
45+
$context = ['base_url' => $request->getBaseUrl()];
46+
if ($request->query->getBoolean('api_gateway', false)) {
47+
$context['api_gateway'] = true;
48+
}
49+
$request->attributes->set('_api_normalization_context', $request->attributes->get('_api_normalization_context', []) + $context);
4650
}
4751

4852
return new Documentation($this->resourceNameCollectionFactory->create(), $this->title, $this->description, $this->version, $this->formats);

src/Serializer/AbstractItemNormalizer.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,10 @@ protected function getAllowedAttributes($classOrObject, array $context, $attribu
160160
*/
161161
protected function setAttributeValue($object, $attribute, $value, $format = null, array $context = [])
162162
{
163+
if (!\is_string($attribute)) {
164+
throw new InvalidArgumentException('Invalid value provided (invalid IRI?).');
165+
}
166+
163167
$propertyMetadata = $this->propertyMetadataFactory->create($context['resource_class'], $attribute, $this->getFactoryOptions($context));
164168
$type = $propertyMetadata->getType();
165169

@@ -299,7 +303,7 @@ protected function denormalizeRelation(string $attributeName, PropertyMetadata $
299303

300304
if (
301305
!$this->resourceClassResolver->isResourceClass($className) ||
302-
($propertyMetadata->isWritableLink() && \is_array($value))
306+
$propertyMetadata->isWritableLink()
303307
) {
304308
$context['resource_class'] = $className;
305309
$context['api_allow_update'] = true;
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the API Platform project.
5+
*
6+
* (c) Kévin Dunglas <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
declare(strict_types=1);
13+
14+
namespace ApiPlatform\Core\Swagger\Serializer;
15+
16+
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
17+
18+
/**
19+
* Removes features unsupported by Amazon API Gateway.
20+
*
21+
* @see https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-known-issues.html
22+
*
23+
* @internal
24+
*
25+
* @author Vincent Chalamon <[email protected]>
26+
*/
27+
final class ApiGatewayNormalizer implements NormalizerInterface
28+
{
29+
private $documentationNormalizer;
30+
31+
public function __construct(NormalizerInterface $documentationNormalizer)
32+
{
33+
$this->documentationNormalizer = $documentationNormalizer;
34+
}
35+
36+
/**
37+
* {@inheritdoc}
38+
*/
39+
public function normalize($object, $format = null, array $context = [])
40+
{
41+
$data = $this->documentationNormalizer->normalize($object, $format, $context);
42+
if (empty($data['basePath'])) {
43+
$data['basePath'] = '/';
44+
}
45+
46+
if (!($context['api_gateway'] ?? false)) {
47+
return $data;
48+
}
49+
50+
foreach ($data['paths'] as $path => $operations) {
51+
foreach ($operations as $operation => $options) {
52+
if (isset($options['parameters'])) {
53+
foreach ($options['parameters'] as $key => $parameter) {
54+
if (!preg_match('/^[a-zA-Z0-9._$-]+$/', $parameter['name'])) {
55+
unset($data['paths'][$path][$operation]['parameters'][$key]);
56+
}
57+
if (isset($parameter['schema']['$ref']) && !preg_match('/^#\/definitions\/[A-z]+$/', $parameter['schema']['$ref'])) {
58+
$data['paths'][$path][$operation]['parameters'][$key]['schema']['$ref'] = str_replace(['-', '_'], '', $parameter['schema']['$ref']);
59+
}
60+
}
61+
$data['paths'][$path][$operation]['parameters'] = array_values($data['paths'][$path][$operation]['parameters']);
62+
}
63+
if (isset($options['responses'])) {
64+
foreach ($options['responses'] as $statusCode => $response) {
65+
if (isset($response['schema']['items']['$ref']) && !preg_match('/^#\/definitions\/[A-z]+$/', $response['schema']['items']['$ref'])) {
66+
$data['paths'][$path][$operation]['responses'][$statusCode]['schema']['items']['$ref'] = str_replace(['-', '_'], '', $response['schema']['items']['$ref']);
67+
}
68+
if (isset($response['schema']['$ref']) && !preg_match('/^#\/definitions\/[A-z]+$/', $response['schema']['$ref'])) {
69+
$data['paths'][$path][$operation]['responses'][$statusCode]['schema']['$ref'] = str_replace(['-', '_'], '', $response['schema']['$ref']);
70+
}
71+
}
72+
}
73+
}
74+
}
75+
76+
foreach ($data['definitions'] as $definition => $options) {
77+
if (!isset($options['properties'])) {
78+
continue;
79+
}
80+
foreach ($options['properties'] as $property => $propertyOptions) {
81+
if (isset($propertyOptions['readOnly'])) {
82+
unset($data['definitions'][$definition]['properties'][$property]['readOnly']);
83+
}
84+
if (isset($propertyOptions['$ref']) && !preg_match('/^#\/definitions\/[A-z]+$/', $propertyOptions['$ref'])) {
85+
$data['definitions'][$definition]['properties'][$property]['$ref'] = str_replace(['-', '_'], '', $propertyOptions['$ref']);
86+
}
87+
if (isset($propertyOptions['items']['$ref']) && !preg_match('/^#\/definitions\/[A-z]+$/', $propertyOptions['items']['$ref'])) {
88+
$data['definitions'][$definition]['properties'][$property]['items']['$ref'] = str_replace(['-', '_'], '', $propertyOptions['items']['$ref']);
89+
}
90+
}
91+
}
92+
93+
// $data['definitions'] is an instance of \ArrayObject
94+
foreach (array_keys($data['definitions']->getArrayCopy()) as $definition) {
95+
if (!preg_match('/^[A-z]+$/', $definition)) {
96+
$data['definitions'][str_replace(['-', '_'], '', $definition)] = $data['definitions'][$definition];
97+
unset($data['definitions'][$definition]);
98+
}
99+
}
100+
101+
return $data;
102+
}
103+
104+
/**
105+
* {@inheritdoc}
106+
*/
107+
public function supportsNormalization($data, $format = null)
108+
{
109+
return $this->documentationNormalizer->supportsNormalization($data, $format);
110+
}
111+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the API Platform project.
5+
*
6+
* (c) Kévin Dunglas <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
declare(strict_types=1);
13+
14+
namespace ApiPlatform\Core\Tests\Bridge\Doctrine\Orm\Util;
15+
16+
use ApiPlatform\Core\Bridge\Doctrine\Orm\Util\QueryBuilderHelper;
17+
use ApiPlatform\Core\Bridge\Doctrine\Orm\Util\QueryNameGeneratorInterface;
18+
use Doctrine\ORM\EntityManagerInterface;
19+
use Doctrine\ORM\QueryBuilder;
20+
use PHPUnit\Framework\TestCase;
21+
22+
class QueryBuilderHelperTest extends TestCase
23+
{
24+
/**
25+
* @dataProvider provideAddJoinOnce
26+
*
27+
* @param string|null $originAlias
28+
*/
29+
public function testAddJoinOnce(string $originAliasForJoinOnce = null, string $expectedAlias)
30+
{
31+
$queryBuilder = new QueryBuilder($this->prophesize(EntityManagerInterface::class)->reveal());
32+
$queryBuilder->from('foo', 'f');
33+
$queryBuilder->from('foo', 'f2');
34+
$queryBuilder->join('f.bar', 'b');
35+
$queryBuilder->join('f2.bar', 'b2');
36+
37+
$queryNameGenerator = $this->prophesize(QueryNameGeneratorInterface::class);
38+
39+
QueryBuilderHelper::addJoinOnce(
40+
$queryBuilder,
41+
$queryNameGenerator->reveal(),
42+
$originAliasForJoinOnce ?? 'f',
43+
'bar',
44+
null,
45+
null,
46+
null,
47+
$originAliasForJoinOnce
48+
);
49+
50+
$this->assertSame($expectedAlias,
51+
$queryBuilder->getDQLPart('join')[$originAliasForJoinOnce ?? 'f'][0]->getAlias());
52+
}
53+
54+
public function provideAddJoinOnce(): array
55+
{
56+
return [
57+
[
58+
null,
59+
'b',
60+
],
61+
[
62+
'f2',
63+
'b2',
64+
],
65+
];
66+
}
67+
}

tests/Bridge/Symfony/Bundle/DependencyInjection/ApiPlatformExtensionTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -659,6 +659,7 @@ private function getBaseContainerBuilderProphecy()
659659
'api_platform.jsonld.context_builder',
660660
'api_platform.jsonld.normalizer.item',
661661
'api_platform.swagger.normalizer.documentation',
662+
'api_platform.swagger.normalizer.api_gateway',
662663
'api_platform.swagger.command.swagger_command',
663664
'api_platform.swagger.action.ui',
664665
'api_platform.swagger.listener.ui',
@@ -697,7 +698,6 @@ private function getBaseContainerBuilderProphecy()
697698
'api_platform.problem.normalizer.error',
698699
'api_platform.swagger.action.ui',
699700
'api_platform.swagger.command.swagger_command',
700-
'api_platform.swagger.normalizer.documentation',
701701
'api_platform.http_cache.listener.response.configure',
702702
'api_platform.http_cache.purger.varnish',
703703
'api_platform.http_cache.purger.varnish_client',

0 commit comments

Comments
 (0)