Skip to content

Commit 81d276c

Browse files
committed
Merge branch '2.5'
2 parents 3366849 + a771df3 commit 81d276c

37 files changed

+811
-84
lines changed

CHANGELOG.md

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# Changelog
22

3+
## 2.5.0 beta 2
4+
5+
* GraphQL: Add support for multipart request so user can create custom file upload mutations (#3041)
6+
* GraphQL: Add support for name converter (#2765)
7+
38
## 2.5.0 beta 1
49

510
* Add an HTTP client dedicated to functional API testing (#2608)
@@ -22,15 +27,15 @@
2227
* Order filter now documents `asc`/`desc` as enum (#2971)
2328
* GraphQL: **BC Break** Separate `query` resource operation attribute into `item_query` and `collection_query` operations so user can use different security and serialization groups for them (#2944, #3015)
2429
* GraphQL: Add support for custom queries and mutations (#2447)
25-
* GraphQL: Add support for custom types
30+
* GraphQL: Add support for custom types (#2492)
2631
* GraphQL: Better pagination support (backwards pagination) (#2142)
27-
* GraphQL: Support the pagination per resource
32+
* GraphQL: Support the pagination per resource (#3035)
2833
* GraphQL: Add the concept of *stages* in the workflow of the resolvers and add the possibility to disable them with operation attributes (#2959)
29-
* GraphQL: Add GraphQL Playground besides GraphiQL and add the possibility to change the default IDE (or to disable it) for the GraphQL endpoint
34+
* GraphQL: Add GraphQL Playground besides GraphiQL and add the possibility to change the default IDE (or to disable it) for the GraphQL endpoint (#2956, #2961)
3035
* GraphQL: Add a command to print the schema in SDL `api:graphql:export > schema.graphql` (#2600)
31-
* GraphQL: Improve serialization performance by avoiding calls to the `serialize` PHP function
32-
* GraphQL: Allow to use a search and an exist filter on the same resource
33-
* GraphQL: Refactor the architecture of the whole system to allow the decoration of useful services (`TypeConverter` to manage custom types, `SerializerContextBuilder` to modify the (de)serialization context dynamically, etc.)
36+
* GraphQL: Improve serialization performance by avoiding calls to the `serialize` PHP function (#2576)
37+
* GraphQL: Allow to use a search and an exist filter on the same resource (#2243)
38+
* GraphQL: Refactor the architecture of the whole system to allow the decoration of useful services (`TypeConverter` to manage custom types, `SerializerContextBuilder` to modify the (de)serialization context dynamically, etc.) (#2772)
3439

3540
Notes:
3641

behat.yml.dist

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ default:
3030
bootstrap: 'tests/Fixtures/app/bootstrap.php'
3131
'Behat\MinkExtension':
3232
base_url: 'http://example.com/'
33+
files_path: 'features/files'
3334
sessions:
3435
default:
3536
symfony2: ~

features/bootstrap/DoctrineContext.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,7 @@ public function thereAreDummyObjectsWithRelatedDummy(int $nb)
420420
$dummy = $this->buildDummy();
421421
$dummy->setName('Dummy #'.$i);
422422
$dummy->setAlias('Alias #'.($nb - $i));
423+
$dummy->nameConverted = "Converted $i";
423424
$dummy->setRelatedDummy($relatedDummy);
424425

425426
$this->manager->persist($relatedDummy);

features/bootstrap/GraphqlContext.php

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use Behat\Behat\Context\Environment\InitializedContextEnvironment;
1616
use Behat\Behat\Hook\Scope\BeforeScenarioScope;
1717
use Behat\Gherkin\Node\PyStringNode;
18+
use Behat\Gherkin\Node\TableNode;
1819
use Behatch\Context\RestContext;
1920
use Behatch\HttpCall\Request;
2021
use GraphQL\Type\Introspection;
@@ -97,6 +98,45 @@ public function ISendTheGraphqlRequestWithOperation(string $operation)
9798
$this->sendGraphqlRequest();
9899
}
99100

101+
/**
102+
* @Given I have the following file(s) for a GraphQL request:
103+
*/
104+
public function iHaveTheFollowingFilesForAGraphqlRequest(TableNode $table)
105+
{
106+
$files = [];
107+
108+
foreach ($table->getHash() as $row) {
109+
if (!isset($row['name'], $row['file'])) {
110+
throw new \InvalidArgumentException('You must provide a "name" and "file" column in your table node.');
111+
}
112+
113+
$files[$row['name']] = $this->restContext->getMinkParameter('files_path').DIRECTORY_SEPARATOR.$row['file'];
114+
}
115+
116+
$this->graphqlRequest['files'] = $files;
117+
}
118+
119+
/**
120+
* @Given I have the following GraphQL multipart request map:
121+
*/
122+
public function iHaveTheFollowingGraphqlMultipartRequestMap(PyStringNode $string)
123+
{
124+
$this->graphqlRequest['map'] = $string->getRaw();
125+
}
126+
127+
/**
128+
* @When I send the following GraphQL multipart request operations:
129+
*/
130+
public function iSendTheFollowingGraphqlMultipartRequestOperations(PyStringNode $string)
131+
{
132+
$params = [];
133+
$params['operations'] = $string->getRaw();
134+
$params['map'] = $this->graphqlRequest['map'];
135+
136+
$this->request->setHttpHeader('Content-type', 'multipart/form-data');
137+
$this->request->send('POST', '/graphql', $params, $this->graphqlRequest['files']);
138+
}
139+
100140
/**
101141
* @When I send the query to introspect the schema
102142
*/

features/files/test.gif

35 Bytes
Loading

features/graphql/collection.feature

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -636,3 +636,23 @@ Feature: GraphQL collection support
636636
And the response should be in JSON
637637
And the header "Content-Type" should be equal to "application/json"
638638
And the JSON node "data.compositeRelation.value" should be equal to "somefoobardummy"
639+
640+
@createSchema
641+
Scenario: Retrieve a collection using name converter
642+
Given there are 4 dummy objects
643+
When I send the following GraphQL request:
644+
"""
645+
{
646+
dummies {
647+
edges {
648+
node {
649+
name_converted
650+
}
651+
}
652+
}
653+
}
654+
"""
655+
Then the response status code should be 200
656+
And the response should be in JSON
657+
And the header "Content-Type" should be equal to "application/json"
658+
And the JSON node "data.dummies.edges[1].node.name_converted" should be equal to "Converted 2"

features/graphql/filters.feature

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,51 @@ Feature: Collections filtering
8484
Then the JSON node "data.dummies.edges" should have 1 element
8585
And the JSON node "data.dummies.edges[0].node.id" should be equal to "/dummies/2"
8686

87+
@createSchema
88+
Scenario: Retrieve a collection filtered using the search filter and a name converter
89+
Given there are 10 dummy objects
90+
When I send the following GraphQL request:
91+
"""
92+
{
93+
dummies(name_converted: "Converted 2") {
94+
edges {
95+
node {
96+
id
97+
name
98+
name_converted
99+
}
100+
}
101+
}
102+
}
103+
"""
104+
Then the JSON node "data.dummies.edges" should have 1 element
105+
And the JSON node "data.dummies.edges[0].node.id" should be equal to "/dummies/2"
106+
And the JSON node "data.dummies.edges[0].node.name_converted" should be equal to "Converted 2"
107+
108+
@createSchema
109+
Scenario: Retrieve a collection filtered using the search filter and a name converter
110+
Given there are 20 convertedOwner objects with convertedRelated
111+
When I send the following GraphQL request:
112+
"""
113+
{
114+
convertedOwners(name_converted__name_converted: "Converted 2") {
115+
edges {
116+
node {
117+
id
118+
name_converted {
119+
name_converted
120+
}
121+
}
122+
}
123+
}
124+
}
125+
"""
126+
Then the JSON node "data.convertedOwners.edges" should have 2 element
127+
And the JSON node "data.convertedOwners.edges[0].node.id" should be equal to "/converted_owners/2"
128+
And the JSON node "data.convertedOwners.edges[0].node.name_converted.name_converted" should be equal to "Converted 2"
129+
And the JSON node "data.convertedOwners.edges[1].node.id" should be equal to "/converted_owners/20"
130+
And the JSON node "data.convertedOwners.edges[1].node.name_converted.name_converted" should be equal to "Converted 20"
131+
87132
@createSchema
88133
Scenario: Retrieve a collection filtered using the search filter
89134
Given there are 3 dummy objects having each 3 relatedDummies
@@ -141,7 +186,7 @@ Feature: Collections filtering
141186
When I send the following GraphQL request:
142187
"""
143188
{
144-
dummies(relatedDummies_name: "RelatedDummy31") {
189+
dummies(relatedDummies__name: "RelatedDummy31") {
145190
edges {
146191
node {
147192
id
@@ -159,7 +204,7 @@ Feature: Collections filtering
159204
When I send the following GraphQL request:
160205
"""
161206
{
162-
dummies(order: {relatedDummy_name: "DESC"}) {
207+
dummies(order: {relatedDummy__name: "DESC"}) {
163208
edges {
164209
node {
165210
name
@@ -183,7 +228,7 @@ Feature: Collections filtering
183228
When I send the following GraphQL request:
184229
"""
185230
{
186-
dummies(relatedDummy_name_list: ["RelatedDummy #1", "RelatedDummy #2"]) {
231+
dummies(relatedDummy__name_list: ["RelatedDummy #1", "RelatedDummy #2"]) {
187232
edges {
188233
node {
189234
id

features/graphql/introspection.feature

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ Feature: GraphQL introspection support
66
Then the response status code should be 400
77
And the response should be in JSON
88
And the header "Content-Type" should be equal to "application/json"
9-
And the JSON node "errors[0].message" should be equal to "GraphQL query is not valid"
9+
And the JSON node "errors[0].message" should be equal to "GraphQL query is not valid."
1010

1111
Scenario: Introspect the GraphQL schema
1212
When I send the query to introspect the schema

features/graphql/mutation.feature

Lines changed: 65 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
Feature: GraphQL mutation support
2+
23
@createSchema
34
Scenario: Introspect types
45
When I send the following GraphQL request:
@@ -30,14 +31,14 @@ Feature: GraphQL mutation support
3031
Then the response status code should be 200
3132
And the response should be in JSON
3233
And the header "Content-Type" should be equal to "application/json"
33-
And the JSON node "data.__type.fields[0].name" should contain "delete"
34-
And the JSON node "data.__type.fields[0].description" should match '/^Deletes a [A-z0-9]+.$/'
35-
And the JSON node "data.__type.fields[0].type.name" should match "/^delete[A-z0-9]+Payload$/"
36-
And the JSON node "data.__type.fields[0].type.kind" should be equal to "OBJECT"
37-
And the JSON node "data.__type.fields[0].args[0].name" should be equal to "input"
38-
And the JSON node "data.__type.fields[0].args[0].type.kind" should be equal to "NON_NULL"
39-
And the JSON node "data.__type.fields[0].args[0].type.ofType.name" should match "/^delete[A-z0-9]+Input$/"
40-
And the JSON node "data.__type.fields[0].args[0].type.ofType.kind" should be equal to "INPUT_OBJECT"
34+
And the JSON node "data.__type.fields[2].name" should contain "delete"
35+
And the JSON node "data.__type.fields[2].description" should match '/^Deletes a [A-z0-9]+.$/'
36+
And the JSON node "data.__type.fields[2].type.name" should match "/^delete[A-z0-9]+Payload$/"
37+
And the JSON node "data.__type.fields[2].type.kind" should be equal to "OBJECT"
38+
And the JSON node "data.__type.fields[2].args[0].name" should be equal to "input"
39+
And the JSON node "data.__type.fields[2].args[0].type.kind" should be equal to "NON_NULL"
40+
And the JSON node "data.__type.fields[2].args[0].type.ofType.name" should match "/^delete[A-z0-9]+Input$/"
41+
And the JSON node "data.__type.fields[2].args[0].type.ofType.kind" should be equal to "INPUT_OBJECT"
4142

4243
Scenario: Create an item
4344
When I send the following GraphQL request:
@@ -90,7 +91,7 @@ Feature: GraphQL mutation support
9091
When I send the following GraphQL request:
9192
"""
9293
mutation {
93-
createDummy(input: {name: "A dummy", foo: [], relatedDummy: "/related_dummies/1", clientMutationId: "myId"}) {
94+
createDummy(input: {name: "A dummy", foo: [], relatedDummy: "/related_dummies/1", name_converted: "Converted" clientMutationId: "myId"}) {
9495
dummy {
9596
id
9697
name
@@ -99,6 +100,7 @@ Feature: GraphQL mutation support
99100
name
100101
__typename
101102
}
103+
name_converted
102104
}
103105
clientMutationId
104106
}
@@ -112,6 +114,7 @@ Feature: GraphQL mutation support
112114
And the JSON node "data.createDummy.dummy.foo" should have 0 elements
113115
And the JSON node "data.createDummy.dummy.relatedDummy.name" should be equal to "RelatedDummy #1"
114116
And the JSON node "data.createDummy.dummy.relatedDummy.__typename" should be equal to "RelatedDummyItem"
117+
And the JSON node "data.createDummy.dummy.name_converted" should be equal to "Converted"
115118
And the JSON node "data.createDummy.clientMutationId" should be equal to "myId"
116119

117120
Scenario: Create an item with an iterable field
@@ -420,3 +423,56 @@ Feature: GraphQL mutation support
420423
And the header "Content-Type" should be equal to "application/json"
421424
And the JSON node "data.testCustomArgumentsDummyCustomMutation.dummyCustomMutation.result" should be equal to "18"
422425
And the JSON node "data.testCustomArgumentsDummyCustomMutation.clientMutationId" should be equal to "myId"
426+
427+
Scenario: Uploading a file with a custom mutation
428+
Given I have the following file for a GraphQL request:
429+
| name | file |
430+
| file | test.gif |
431+
And I have the following GraphQL multipart request map:
432+
"""
433+
{
434+
"file": ["variables.file"]
435+
}
436+
"""
437+
When I send the following GraphQL multipart request operations:
438+
"""
439+
{
440+
"query": "mutation($file: Upload!) { uploadMediaObject(input: {file: $file}) { mediaObject { id contentUrl } } }",
441+
"variables": {
442+
"file": null
443+
}
444+
}
445+
"""
446+
Then the response status code should be 200
447+
And the response should be in JSON
448+
And the JSON node "data.uploadMediaObject.mediaObject.contentUrl" should be equal to "test.gif"
449+
450+
Scenario: Uploading multiple files with a custom mutation
451+
Given I have the following files for a GraphQL request:
452+
| name | file |
453+
| 0 | test.gif |
454+
| 1 | test.gif |
455+
| 2 | test.gif |
456+
And I have the following GraphQL multipart request map:
457+
"""
458+
{
459+
"0": ["variables.files.0"],
460+
"1": ["variables.files.1"],
461+
"2": ["variables.files.2"]
462+
}
463+
"""
464+
When I send the following GraphQL multipart request operations:
465+
"""
466+
{
467+
"query": "mutation($files: [Upload!]!) { uploadMultipleMediaObject(input: {files: $files}) { mediaObject { id contentUrl } } }",
468+
"variables": {
469+
"files": [
470+
null,
471+
null,
472+
null
473+
]
474+
}
475+
}
476+
"""
477+
Then the response status code should be 200
478+
And the JSON node "data.uploadMultipleMediaObject.mediaObject.contentUrl" should be equal to "test.gif"

features/graphql/query.feature

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ Feature: GraphQL query support
88
dummy(id: "/dummies/1") {
99
id
1010
name
11+
name_converted
1112
}
1213
}
1314
"""
@@ -16,6 +17,7 @@ Feature: GraphQL query support
1617
And the header "Content-Type" should be equal to "application/json"
1718
And the JSON node "data.dummy.id" should be equal to "/dummies/1"
1819
And the JSON node "data.dummy.name" should be equal to "Dummy #1"
20+
And the JSON node "data.dummy.name_converted" should be equal to "Converted 1"
1921

2022
Scenario: Retrieve a Relay Node
2123
When I send the following GraphQL request:

0 commit comments

Comments
 (0)