Skip to content

Commit

Permalink
Merge pull request #63 from overblog/add-compatible-react-relay-netwo…
Browse files Browse the repository at this point in the history
…rk-layer-batching

Add compatible react-relay-network-layer batching
  • Loading branch information
mcg-web authored Sep 16, 2016
2 parents 197e8f8 + 0e91253 commit ece07fe
Show file tree
Hide file tree
Showing 6 changed files with 66 additions and 39 deletions.
31 changes: 19 additions & 12 deletions Controller/GraphController.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,25 +19,32 @@ class GraphController extends Controller
{
public function endpointAction(Request $request)
{
if ($request->query->has('batch')) {
$data = $this->processBatchQuery($request);
} else {
$data = $this->processNormalQuery($request);
}
$payload = $this->processNormalQuery($request);

return new JsonResponse($data, 200);
return new JsonResponse($payload, 200);
}

private function processBatchQuery(Request $request)
public function batchEndpointAction(Request $request)
{
$params = $this->get('overblog_graphql.request_batch_parser')->parse($request);
$data = [];
$payloads = $this->processBatchQuery($request);

return new JsonResponse($payloads, 200);
}

foreach ($params as $i => $entry) {
$data[$i] = $this->get('overblog_graphql.request_executor')->execute($entry)->toArray();
private function processBatchQuery(Request $request)
{
$queries = $this->get('overblog_graphql.request_batch_parser')->parse($request);
$payloads = [];

foreach ($queries as $query) {
$payloadResult = $this->get('overblog_graphql.request_executor')->execute([
'query' => $query['query'],
'variables' => $query['variables'],
]);
$payloads[] = ['id' => $query['id'], 'payload' => $payloadResult->toArray()];
}

return $data;
return $payloads;
}

private function processNormalQuery(Request $request)
Expand Down
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
OverblogGraphQLBundle
======================

This Bundle provide integration [GraphQL](https://facebook.github.io/graphql/) using [webonyx/graphql-php](https://github.com/webonyx/graphql-php)
This Symfony 2 / 3 bundle provide integration [GraphQL](https://facebook.github.io/graphql/) using [webonyx/graphql-php](https://github.com/webonyx/graphql-php)
and [GraphQL Relay](https://facebook.github.io/relay/docs/graphql-relay-specification.html).
It also supports batching using libs like [ReactRelayNetworkLayer](https://github.com/nodkz/react-relay-network-layer).

[![Build Status](https://travis-ci.org/overblog/GraphQLBundle.svg?branch=master)](https://travis-ci.org/overblog/GraphQLBundle)
[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/overblog/GraphQLBundle/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/overblog/GraphQLBundle/?branch=master)
Expand Down Expand Up @@ -877,6 +878,12 @@ Expression | Description | Scope

**Tips**: the expression language service can be custom using bundle configuration.

Batching
---------

Batching can help decrease io between server and client.
The default route of batching is `/batch`.

Contribute
----------

Expand Down
29 changes: 17 additions & 12 deletions Request/BatchParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,14 @@

class BatchParser implements ParserInterface
{
const PARAM_ID = 'id';

private static $queriesDefaultValue = [
self::PARAM_ID => null,
self::PARAM_QUERY => null,
self::PARAM_VARIABLES => null,
];

/**
* @param Request $request
*
Expand All @@ -24,24 +32,21 @@ class BatchParser implements ParserInterface
public function parse(Request $request)
{
// Extracts the GraphQL request parameters
$data = $this->getParsedBody($request);
$queries = $this->getParsedBody($request);

if (empty($data)) {
if (empty($queries)) {
throw new BadRequestHttpException('Must provide at least one valid query.');
}

foreach ($data as $i => &$entry) {
if (empty($entry[static::PARAM_QUERY]) || !is_string($entry[static::PARAM_QUERY])) {
throw new BadRequestHttpException(sprintf('No valid query found in node "%s"', $i));
}
foreach ($queries as $i => &$query) {
$query = $query + self::$queriesDefaultValue;

$entry = $entry + [
static::PARAM_VARIABLES => null,
static::PARAM_OPERATION_NAME => null,
];
if (!is_string($query[static::PARAM_QUERY])) {
throw new BadRequestHttpException(sprintf('%s is not a valid query', json_encode($query[static::PARAM_QUERY])));
}
}

return $data;
return $queries;
}

/**
Expand All @@ -57,7 +62,7 @@ private function getParsedBody(Request $request)

// JSON object
if ($type !== static::CONTENT_TYPE_JSON) {
throw new BadRequestHttpException(sprintf('Only request with content type "%" is accepted.', static::CONTENT_TYPE_JSON));
throw new BadRequestHttpException(sprintf('Only request with content type "%s" is accepted.', static::CONTENT_TYPE_JSON));
}

$parsedBody = json_decode($request->getContent(), true);
Expand Down
6 changes: 3 additions & 3 deletions Request/Executor.php
Original file line number Diff line number Diff line change
Expand Up @@ -80,11 +80,11 @@ public function execute(array $data, array $context = [])

$executionResult = GraphQL::executeAndReturnResult(
$this->schema,
isset($data['query']) ? $data['query'] : null,
isset($data[ParserInterface::PARAM_QUERY]) ? $data[ParserInterface::PARAM_QUERY] : null,
$context,
$context,
$data['variables'],
$data['operationName']
$data[ParserInterface::PARAM_VARIABLES],
isset($data[ParserInterface::PARAM_OPERATION_NAME]) ? $data[ParserInterface::PARAM_OPERATION_NAME] : null
);

if (null !== $this->errorHandler) {
Expand Down
6 changes: 6 additions & 0 deletions Resources/config/routing/graphql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,9 @@ overblog_graphql_endpoint:
defaults:
_controller: OverblogGraphQLBundle:Graph:endpoint
_format: "json"

overblog_graphql_batch_endpoint:
path: /batch
defaults:
_controller: OverblogGraphQLBundle:Graph:batchEndpoint
_format: "json"
24 changes: 13 additions & 11 deletions Tests/Functional/Controller/GraphControllerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -154,20 +154,22 @@ public function testBatchEndpointAction()
$client = static::createClient(['test_case' => 'connection']);

$data = [
'friends' => [
[
'id' => 'friends',
'query' => $this->friendsQuery,
],
'friendsTotalCount' => [
[
'id' => 'friendsTotalCount',
'query' => $this->friendsTotalCountQuery,
],
];

$client->request('POST', '/?batch', [], [], ['CONTENT_TYPE' => 'application/json'], json_encode($data));
$client->request('POST', '/batch', [], [], ['CONTENT_TYPE' => 'application/json'], json_encode($data));
$result = $client->getResponse()->getContent();

$expected = [
'friends' => ['data' => $this->expectedData],
'friendsTotalCount' => ['data' => ['user' => ['friends' => ['totalCount' => 4]]]],
['id' => 'friends', 'payload' => ['data' => $this->expectedData]],
['id' => 'friendsTotalCount', 'payload' => ['data' => ['user' => ['friends' => ['totalCount' => 4]]]]],
];
$this->assertEquals($expected, json_decode($result, true), $result);
}
Expand All @@ -179,18 +181,18 @@ public function testBatchEndpointAction()
public function testBatchEndpointWithEmptyQuery()
{
$client = static::createClient();
$client->request('GET', '/?batch', [], [], ['CONTENT_TYPE' => 'application/json'], '{}');
$client->request('GET', '/batch', [], [], ['CONTENT_TYPE' => 'application/json'], '{}');
$client->getResponse()->getContent();
}

/**
* @expectedException \Symfony\Component\HttpKernel\Exception\BadRequestHttpException
* @expectedExceptionMessage Only request with content type " is accepted.
* @expectedExceptionMessage Only request with content type "application/json" is accepted.
*/
public function testBatchEndpointWrongContentType()
{
$client = static::createClient();
$client->request('GET', '/?batch');
$client->request('GET', '/batch');
$client->getResponse()->getContent();
}

Expand All @@ -201,18 +203,18 @@ public function testBatchEndpointWrongContentType()
public function testBatchEndpointWithInvalidJson()
{
$client = static::createClient();
$client->request('GET', '/?batch', [], [], ['CONTENT_TYPE' => 'application/json'], '{');
$client->request('GET', '/batch', [], [], ['CONTENT_TYPE' => 'application/json'], '{');
$client->getResponse()->getContent();
}

/**
* @expectedException \Symfony\Component\HttpKernel\Exception\BadRequestHttpException
* @expectedExceptionMessage No valid query found in node "test"
* @expectedExceptionMessage 1 is not a valid query
*/
public function testBatchEndpointWithInvalidQuery()
{
$client = static::createClient();
$client->request('GET', '/?batch', [], [], ['CONTENT_TYPE' => 'application/json'], '{"test" : {"query": 1}}');
$client->request('GET', '/batch', [], [], ['CONTENT_TYPE' => 'application/json'], '{"test" : {"query": 1}}');
$client->getResponse()->getContent();
}
}

0 comments on commit ece07fe

Please sign in to comment.