Skip to content

Commit 3d780a4

Browse files
committed
Merge remote-tracking branch 'upstream/2.4' into merge
2 parents 19c08e0 + 37c6152 commit 3d780a4

File tree

59 files changed

+710
-312
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

59 files changed

+710
-312
lines changed

features/doctrine/search_filter.feature

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -415,6 +415,17 @@ Feature: Search filter on collections
415415
And the JSON node "_embedded.item[1]._links.relatedDummies" should have 3 elements
416416
And the JSON node "_embedded.item[2]._links.relatedDummies" should have 3 elements
417417

418+
@createSchema
419+
Scenario: Search by related collection id
420+
Given there are 2 dummy objects having each 2 relatedDummies
421+
When I add "Accept" header equal to "application/hal+json"
422+
And I send a "GET" request to "/dummies?relatedDummies=3"
423+
Then the response status code should be 200
424+
And the response should be in JSON
425+
And the JSON node "totalItems" should be equal to "1"
426+
And the JSON node "_links.item" should have 1 element
427+
And the JSON node "_links.item[0].href" should be equal to "/dummies/2"
428+
418429
@createSchema
419430
Scenario: Get collection by id equals 9.99 which is not possible
420431
Given there are 30 dummy objects

features/graphql/introspection.feature

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -304,11 +304,10 @@ Feature: GraphQL introspection support
304304
And the JSON node "data.typeCreateInput.inputFields[0].name" should be equal to "bar"
305305
And the JSON node "data.typeCreateInput.inputFields[1].name" should be equal to "baz"
306306
And the JSON node "data.typeCreateInput.inputFields[2].name" should be equal to "clientMutationId"
307-
And the JSON node "data.typeCreatePayload.fields" should have 4 elements
307+
And the JSON node "data.typeCreatePayload.fields" should have 3 elements
308308
And the JSON node "data.typeCreatePayload.fields[0].name" should be equal to "id"
309309
And the JSON node "data.typeCreatePayload.fields[1].name" should be equal to "bar"
310-
And the JSON node "data.typeCreatePayload.fields[2].name" should be equal to "baz"
311-
And the JSON node "data.typeCreatePayload.fields[3].name" should be equal to "clientMutationId"
310+
And the JSON node "data.typeCreatePayload.fields[2].name" should be equal to "clientMutationId"
312311

313312
Scenario: Retrieve an item through a GraphQL query
314313
Given there are 4 dummy objects with relatedDummy

features/graphql/mutation.feature

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ Feature: GraphQL mutation support
4040
mutation {
4141
createFoo(input: {name: "A new one", bar: "new", clientMutationId: "myId"}) {
4242
id
43+
_id
4344
name
4445
bar
4546
clientMutationId
@@ -50,10 +51,29 @@ Feature: GraphQL mutation support
5051
And the response should be in JSON
5152
And the header "Content-Type" should be equal to "application/json"
5253
And the JSON node "data.createFoo.id" should be equal to "/foos/1"
54+
And the JSON node "data.createFoo._id" should be equal to 1
5355
And the JSON node "data.createFoo.name" should be equal to "A new one"
5456
And the JSON node "data.createFoo.bar" should be equal to "new"
5557
And the JSON node "data.createFoo.clientMutationId" should be equal to "myId"
5658

59+
Scenario: Create an item without a clientMutationId
60+
When I send the following GraphQL request:
61+
"""
62+
mutation {
63+
createFoo(input: {name: "Created without mutation id", bar: "works"}) {
64+
id
65+
name
66+
bar
67+
}
68+
}
69+
"""
70+
Then the response status code should be 200
71+
And the response should be in JSON
72+
And the header "Content-Type" should be equal to "application/json"
73+
And the JSON node "data.createFoo.id" should be equal to "/foos/2"
74+
And the JSON node "data.createFoo.name" should be equal to "Created without mutation id"
75+
And the JSON node "data.createFoo.bar" should be equal to "works"
76+
5777
Scenario: Create an item with a subresource
5878
Given there are 1 dummy objects with relatedDummy
5979
When I send the following GraphQL request:
@@ -254,7 +274,6 @@ Feature: GraphQL mutation support
254274
createDummyGroup(input: {bar: "Bar", baz: "Baz", clientMutationId: "myId"}) {
255275
id
256276
bar
257-
baz
258277
clientMutationId
259278
}
260279
}
@@ -264,7 +283,6 @@ Feature: GraphQL mutation support
264283
And the header "Content-Type" should be equal to "application/json"
265284
And the JSON node "data.createDummyGroup.id" should be equal to "/dummy_groups/2"
266285
And the JSON node "data.createDummyGroup.bar" should be equal to "Bar"
267-
And the JSON node "data.createDummyGroup.baz" should be null
268286
And the JSON node "data.createDummyGroup.clientMutationId" should be equal to "myId"
269287

270288
Scenario: Trigger a validation error

phpstan.neon

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ parameters:
2222
- '#Parameter \#1 \$function of function call_user_func expects callable, .+ given\.#'
2323
- '#Parameter \#1 \$classes of class ApiPlatform\\Core\\Metadata\\Resource\\ResourceNameCollection constructor expects array<string>, array<int, int\|string> given\.#'
2424
- '#Method ApiPlatform\\Core\\Util\\RequestParser::parseRequestParams\(\) should return array but returns array\|false\.#'
25-
- '#Parameter \#1 \$vars of class GraphQL\\Language\\AST\\(IntValue|ObjectField|ObjectValue|BooleanValue|ListValue|StringValue)Node constructor expects array<GraphQL\\Language\\AST\\Location\|GraphQL\\Language\\AST\\NameNode\|GraphQL\\Language\\AST\\NodeList\|GraphQL\\Language\\AST\\SelectionSetNode\|string\|null>, array<string, .+> given\.#'
25+
- '#Parameter \#1 \$vars of class GraphQL\\Language\\AST\\(IntValue|ObjectField|ObjectValue|BooleanValue|ListValue|StringValue)Node constructor expects array<bool\|float\|GraphQL\\Language\\AST\\Location\|GraphQL\\Language\\AST\\NameNode\|GraphQL\\Language\\AST\\NodeList\|GraphQL\\Language\\AST\\SelectionSetNode\|int\|string\|null>, array<string, .+> given\.#'
2626
- '#Parameter \#1 \$defaultContext of class Symfony\\Component\\Serializer\\Encoder\\Json(De|En)code constructor expects array, (int|true) given\.#'
2727
- '#Parameter \#(2|3) \$(resourceMetadataFactory|pagination) of class ApiPlatform\\Core\\Bridge\\Doctrine\\Orm\\Extension\\PaginationExtension constructor expects (ApiPlatform\\Core\\Metadata\\Resource\\Factory\\ResourceMetadataFactoryInterface\|Symfony\\Component\\HttpFoundation\\RequestStack|ApiPlatform\\Core\\DataProvider\\Pagination\|ApiPlatform\\Core\\Metadata\\Resource\\Factory\\ResourceMetadataFactoryInterface), stdClass given\.#'
2828
# Temporary fix while the PHPStan extension for Prophecy isn't compatible with 0.10

src/Annotation/ApiResource.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
* @Attribute("denormalizationContext", type="array"),
3232
* @Attribute("deprecationReason", type="string"),
3333
* @Attribute("description", type="string"),
34+
* @Attribute("elasticsearch", type="bool"),
3435
* @Attribute("fetchPartial", type="bool"),
3536
* @Attribute("forceEager", type="bool"),
3637
* @Attribute("formats", type="array"),
@@ -137,6 +138,13 @@ final class ApiResource
137138
*/
138139
private $deprecationReason;
139140

141+
/**
142+
* @see https://github.com/Haehnchen/idea-php-annotation-plugin/issues/112
143+
*
144+
* @var bool
145+
*/
146+
private $elasticsearch;
147+
140148
/**
141149
* @see https://github.com/Haehnchen/idea-php-annotation-plugin/issues/112
142150
*

src/Bridge/Doctrine/Common/DataPersister.php

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
namespace ApiPlatform\Core\Bridge\Doctrine\Common;
1515

16-
use ApiPlatform\Core\DataPersister\DataPersisterInterface;
16+
use ApiPlatform\Core\DataPersister\ContextAwareDataPersisterInterface;
1717
use ApiPlatform\Core\Util\ClassInfoTrait;
1818
use Doctrine\Common\Persistence\ManagerRegistry;
1919
use Doctrine\Common\Persistence\ObjectManager as DoctrineObjectManager;
@@ -24,7 +24,7 @@
2424
*
2525
* @author Baptiste Meyer <[email protected]>
2626
*/
27-
final class DataPersister implements DataPersisterInterface
27+
final class DataPersister implements ContextAwareDataPersisterInterface
2828
{
2929
use ClassInfoTrait;
3030

@@ -38,15 +38,15 @@ public function __construct(ManagerRegistry $managerRegistry)
3838
/**
3939
* {@inheritdoc}
4040
*/
41-
public function supports($data): bool
41+
public function supports($data, array $context = []): bool
4242
{
4343
return null !== $this->getManager($data);
4444
}
4545

4646
/**
4747
* {@inheritdoc}
4848
*/
49-
public function persist($data)
49+
public function persist($data, array $context = [])
5050
{
5151
if (!$manager = $this->getManager($data)) {
5252
return $data;
@@ -65,7 +65,7 @@ public function persist($data)
6565
/**
6666
* {@inheritdoc}
6767
*/
68-
public function remove($data)
68+
public function remove($data, array $context = [])
6969
{
7070
if (!$manager = $this->getManager($data)) {
7171
return;

src/Bridge/Doctrine/MongoDbOdm/Filter/RangeFilter.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ protected function addMatch(Builder $aggregationBuilder, string $field, string $
7878
return;
7979
}
8080

81-
$aggregationBuilder->match()->field($matchField)->lte($rangeValue[0])->gte($rangeValue[1]);
81+
$aggregationBuilder->match()->field($matchField)->gte($rangeValue[0])->lte($rangeValue[1]);
8282

8383
break;
8484
case self::PARAMETER_GREATER_THAN:

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ protected function filterProperty(string $property, $value, Builder $aggregation
135135

136136
$aggregationBuilder
137137
->match()
138-
->field("$matchField.\$id")
138+
->field($matchField)
139139
->in($values);
140140
}
141141

src/Bridge/Elasticsearch/DataProvider/CollectionDataProvider.php

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
use ApiPlatform\Core\DataProvider\ContextAwareCollectionDataProviderInterface;
2222
use ApiPlatform\Core\DataProvider\Pagination;
2323
use ApiPlatform\Core\DataProvider\RestrictedDataProviderInterface;
24+
use ApiPlatform\Core\Exception\ResourceClassNotFoundException;
25+
use ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface;
2426
use Elasticsearch\Client;
2527
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
2628

@@ -38,18 +40,20 @@ final class CollectionDataProvider implements ContextAwareCollectionDataProvider
3840
private $identifierExtractor;
3941
private $denormalizer;
4042
private $pagination;
43+
private $resourceMetadataFactory;
4144
private $collectionExtensions;
4245

4346
/**
4447
* @param RequestBodySearchCollectionExtensionInterface[] $collectionExtensions
4548
*/
46-
public function __construct(Client $client, DocumentMetadataFactoryInterface $documentMetadataFactory, IdentifierExtractorInterface $identifierExtractor, DenormalizerInterface $denormalizer, Pagination $pagination, iterable $collectionExtensions = [])
49+
public function __construct(Client $client, DocumentMetadataFactoryInterface $documentMetadataFactory, IdentifierExtractorInterface $identifierExtractor, DenormalizerInterface $denormalizer, Pagination $pagination, ResourceMetadataFactoryInterface $resourceMetadataFactory, iterable $collectionExtensions = [])
4750
{
4851
$this->client = $client;
4952
$this->documentMetadataFactory = $documentMetadataFactory;
5053
$this->identifierExtractor = $identifierExtractor;
5154
$this->denormalizer = $denormalizer;
5255
$this->pagination = $pagination;
56+
$this->resourceMetadataFactory = $resourceMetadataFactory;
5357
$this->collectionExtensions = $collectionExtensions;
5458
}
5559

@@ -58,6 +62,15 @@ public function __construct(Client $client, DocumentMetadataFactoryInterface $do
5862
*/
5963
public function supports(string $resourceClass, ?string $operationName = null, array $context = []): bool
6064
{
65+
try {
66+
$resourceMetadata = $this->resourceMetadataFactory->create($resourceClass);
67+
if (false === $resourceMetadata->getCollectionOperationAttribute($operationName, 'elasticsearch', true, true)) {
68+
return false;
69+
}
70+
} catch (ResourceClassNotFoundException $e) {
71+
return false;
72+
}
73+
6174
try {
6275
$this->documentMetadataFactory->create($resourceClass);
6376
} catch (IndexNotFoundException $e) {

src/Bridge/Elasticsearch/DataProvider/ItemDataProvider.php

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
use ApiPlatform\Core\Bridge\Elasticsearch\Serializer\ItemNormalizer;
2121
use ApiPlatform\Core\DataProvider\ItemDataProviderInterface;
2222
use ApiPlatform\Core\DataProvider\RestrictedDataProviderInterface;
23+
use ApiPlatform\Core\Exception\ResourceClassNotFoundException;
24+
use ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface;
2325
use Elasticsearch\Client;
2426
use Elasticsearch\Common\Exceptions\Missing404Exception;
2527
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
@@ -38,20 +40,31 @@ final class ItemDataProvider implements ItemDataProviderInterface, RestrictedDat
3840
private $documentMetadataFactory;
3941
private $identifierExtractor;
4042
private $denormalizer;
43+
private $resourceMetadataFactory;
4144

42-
public function __construct(Client $client, DocumentMetadataFactoryInterface $documentMetadataFactory, IdentifierExtractorInterface $identifierExtractor, DenormalizerInterface $denormalizer)
45+
public function __construct(Client $client, DocumentMetadataFactoryInterface $documentMetadataFactory, IdentifierExtractorInterface $identifierExtractor, DenormalizerInterface $denormalizer, ResourceMetadataFactoryInterface $resourceMetadataFactory)
4346
{
4447
$this->client = $client;
4548
$this->documentMetadataFactory = $documentMetadataFactory;
4649
$this->identifierExtractor = $identifierExtractor;
4750
$this->denormalizer = $denormalizer;
51+
$this->resourceMetadataFactory = $resourceMetadataFactory;
4852
}
4953

5054
/**
5155
* {@inheritdoc}
5256
*/
5357
public function supports(string $resourceClass, ?string $operationName = null, array $context = []): bool
5458
{
59+
try {
60+
$resourceMetadata = $this->resourceMetadataFactory->create($resourceClass);
61+
if (false === $resourceMetadata->getItemOperationAttribute($operationName, 'elasticsearch', true, true)) {
62+
return false;
63+
}
64+
} catch (ResourceClassNotFoundException $e) {
65+
return false;
66+
}
67+
5568
try {
5669
$this->documentMetadataFactory->create($resourceClass);
5770
} catch (IndexNotFoundException $e) {

0 commit comments

Comments
 (0)