From 9909493bfca23fbf2fb9fd272eea0b49ef9d3185 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Wed, 13 Jul 2022 15:49:59 +1200 Subject: [PATCH] Support query batching --- app/controllers/api/graphql.php | 70 ++++++++++++---------- composer.json | 9 +-- composer.lock | 101 +++++++++++++++++++------------- 3 files changed, 99 insertions(+), 81 deletions(-) diff --git a/app/controllers/api/graphql.php b/app/controllers/api/graphql.php index 8951bdf669..6b0525cd78 100644 --- a/app/controllers/api/graphql.php +++ b/app/controllers/api/graphql.php @@ -27,9 +27,7 @@ App::get('/v1/graphql') ->label('sdk.response.model', Response::MODEL_ANY) ->label('abuse-limit', 60) ->label('abuse-time', 60) - ->param('query', '', new Text(2048), 'The query to execute. Max 2048 chars') - ->param('operationName', null, new Text(512), 'Name of the operation to execute. Required if multiple operations are provided', true) - ->param('variables', [], new JSON(), 'Map of variables to use as replacement values in the query', true) + ->param('query', '', new JSON(), 'The query or queries to execute.', body: true) ->inject('request') ->inject('response') ->inject('utopia') @@ -52,11 +50,7 @@ App::post('/v1/graphql') ->label('sdk.response.model', Response::MODEL_ANY) ->label('abuse-limit', 60) ->label('abuse-time', 60) - ->param('query', '', new Text(2048), 'The query to execute. Max 1024 chars. Required if the request content type is "application/json"', true) - ->param('operationName', null, new Text(512), 'Name of the operation to execute. Required if multiple operations are provided', true) - ->param('variables', [], new JSON(), 'Map of variables to use as replacement values in the query', true) - ->param('operations', '', new Text(4096), 'JSON encoded query and variables with nulled file values. Required if the request content type "multipart/form-data"', true) - ->param('map', '', new Text(1024), 'Map of form-data filenames to the operations dot-path to inject the file to. For example: "variables.file"', true) + ->param('query', '', new JSON(), 'The query or queries to execute.', body: true) ->inject('request') ->inject('response') ->inject('promiseAdapter') @@ -68,11 +62,7 @@ App::post('/v1/graphql') * @throws \Exception */ function graphqlRequest( - string $query, - ?string $operationName, - ?array $variables, - ?string $operations, - ?string $map, + array $query, Appwrite\Utopia\Request $request, Appwrite\Utopia\Response $response, CoroutinePromiseAdapter $promiseAdapter, @@ -81,17 +71,23 @@ function graphqlRequest( $contentType = $request->getHeader('content-type'); if ($contentType === 'application/graphql') { - $query = $request->getSwoole()->rawContent(); + $query = [ 'query' => $request->getSwoole()->rawContent() ]; + } + + $batch = true; + if (!isset($query[0])) { + $batch = false; + $query = [$query]; } if (\str_starts_with($contentType, 'multipart/form-data')) { - $operations = \json_decode($operations, true); - $map = \json_decode($map, true); + $operations = \json_decode($query[0]['operations'], true); + $map = \json_decode($query[0]['map'], true); foreach ($map as $fileKey => $locations) { foreach ($locations as $location) { $items = &$operations; - foreach (explode('.', $location) as $key) { - if (!isset($items[$key]) || !is_array($items[$key])) { + foreach (\explode('.', $location) as $key) { + if (!isset($items[$key]) || !\is_array($items[$key])) { $items[$key] = []; } $items = &$items[$key]; @@ -99,14 +95,14 @@ function graphqlRequest( $items = $request->getFiles($fileKey); } } - $query = $operations['query']; - $variables = $operations['variables']; + $query[0]['query'] = $operations['query']; + $query[0]['variables'] = $operations['variables']; } if (empty($query)) { throw new Exception('No query supplied.', 400, Exception::GRAPHQL_NO_QUERY); } - + $maxComplexity = App::getEnv('_APP_GRAPHQL_MAX_QUERY_COMPLEXITY', 200); $maxDepth = App::getEnv('_APP_GRAPHQL_MAX_QUERY_DEPTH', 3); @@ -120,22 +116,32 @@ function graphqlRequest( } else { $debugFlags = DebugFlag::NONE; } - - $promise = GraphQL::promiseToExecute( - $promiseAdapter, - $gqlSchema, - $query, - variableValues: $variables, - operationName: $operationName, - validationRules: $validations - ); + + $promises = []; + foreach($query as $indexed) { + $promises[] = GraphQL::promiseToExecute( + $promiseAdapter, + $gqlSchema, + $indexed['query'], + variableValues: $indexed['variables'], + operationName: $indexed['operationName'], + validationRules: $validations + ); + } $output = []; $wg = new WaitGroup(); $wg->add(); - $promise->then( + $promiseAdapter->all($promises)->then( function ($result) use ($response, &$output, $wg, $debugFlags) { - $output = $result->toArray($debugFlags); + foreach ($result as $queryResponse) { + $output[] = $queryResponse->toArray($debugFlags); + } + if (isset($output[1])) { + $output = \array_merge_recursive(...$output); + } else { + $output = $output[0]; + } $wg->done(); }, function ($error) use ($response, &$output, $wg) { diff --git a/composer.json b/composer.json index 28d2daa63b..420d37dd9f 100644 --- a/composer.json +++ b/composer.json @@ -42,7 +42,7 @@ "ext-sockets": "*", "appwrite/php-clamav": "1.1.*", "appwrite/php-runtimes": "0.10.*", - "abnegate/framework": "dev-feat-graphql-helpers", + "utopia-php/framework": "dev-feat-graphql-helpers as 0.19.2", "utopia-php/logger": "0.3.*", "utopia-php/abuse": "0.7.*", "utopia-php/analytics": "0.2.*", @@ -70,17 +70,10 @@ "slickdeals/statsd": "3.1.0", "webonyx/graphql-php": "14.1.1" }, - "replace": { - "utopia-php/framework": "*" - }, "repositories": [ { "url": "https://github.com/appwrite/runtimes.git", "type": "git" - }, - { - "url": "https://github.com/abnegate/framework.git", - "type": "git" } ], "require-dev": { diff --git a/composer.lock b/composer.lock index d05062cfc9..9fafd3f4c1 100644 --- a/composer.lock +++ b/composer.lock @@ -4,46 +4,8 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "e47b45e70996f8046494cc0db6aeb761", + "content-hash": "b395a62241bc19cb1b7d845e3287808b", "packages": [ - { - "name": "abnegate/framework", - "version": "dev-feat-graphql-helpers", - "source": { - "type": "git", - "url": "https://github.com/abnegate/framework.git", - "reference": "a3bf64536f2d86728a06097717a5cc351d0a88d0" - }, - "require": { - "php": ">=8.0.0" - }, - "require-dev": { - "phpunit/phpunit": "^9.5.10", - "vimeo/psalm": "4.13.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\": "src/" - } - }, - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - } - ], - "description": "A simple, light and advanced PHP framework", - "keywords": [ - "framework", - "php", - "upf" - ], - "time": "2022-07-12T00:06:19+00:00" - }, { "name": "adhocore/jwt", "version": "1.1.2", @@ -2205,6 +2167,56 @@ }, "time": "2020-02-23T07:40:02+00:00" }, + { + "name": "utopia-php/framework", + "version": "dev-feat-graphql-helpers", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/framework.git", + "reference": "ab785d1605cf1a01cfb9d38f87a15235cdcdf2ca" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/framework/zipball/ab785d1605cf1a01cfb9d38f87a15235cdcdf2ca", + "reference": "ab785d1605cf1a01cfb9d38f87a15235cdcdf2ca", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "require-dev": { + "ext-curl": "*", + "phpunit/phpunit": "^9.5.10", + "vimeo/psalm": "4.13.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + } + ], + "description": "A simple, light and advanced PHP framework", + "keywords": [ + "framework", + "php", + "upf" + ], + "support": { + "issues": "https://github.com/utopia-php/framework/issues", + "source": "https://github.com/utopia-php/framework/tree/feat-graphql-helpers" + }, + "time": "2022-07-13T03:41:04+00:00" + }, { "name": "utopia-php/image", "version": "0.5.4", @@ -5401,10 +5413,17 @@ "time": "2022-05-17T05:48:52+00:00" } ], - "aliases": [], + "aliases": [ + { + "package": "utopia-php/framework", + "version": "dev-feat-graphql-helpers", + "alias": "0.19.2", + "alias_normalized": "0.19.2.0" + } + ], "minimum-stability": "stable", "stability-flags": { - "abnegate/framework": 20, + "utopia-php/framework": 20, "utopia-php/registry": 20 }, "prefer-stable": false,