1
0
Fork 0
mirror of synced 2024-06-27 18:50:47 +12:00

feat: error handling for graphQL

This commit is contained in:
Christy Jacob 2021-03-05 00:10:52 +05:30
parent b3a0df734e
commit 42d6194ed9
12 changed files with 160 additions and 72 deletions

0
.swiftlint.yml Normal file
View file

View file

@ -7,6 +7,7 @@ $member = [
'home',
'console',
'account',
'graphql',
'teams.read',
'teams.write',
'documents.read',
@ -22,6 +23,7 @@ $member = [
];
$admins = [
'graphql',
'teams.read',
'teams.write',
'documents.read',
@ -56,6 +58,7 @@ return [
'public',
'home',
'console',
'graphql',
'documents.read',
'files.read',
'locale.read',
@ -82,6 +85,6 @@ return [
],
Auth::USER_ROLE_APP => [
'label' => 'Application',
'scopes' => ['health.read'],
'scopes' => ['health.read', 'graphql'],
],
];

View file

@ -1,13 +1,19 @@
<?php
use Appwrite\Auth\Auth;
use Appwrite\Database\Database;
use GraphQL\GraphQL;
use GraphQL\Type\Schema;
use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\Type;
use Appwrite\Utopia\Response;
use Appwrite\Utopia\Response\Model;
use GraphQL\Error\ClientAware;
use GraphQL\Error\DebugFlag;
use GraphQL\Language\Parser;
use GraphQL\Type\Definition\NonNull;
use Utopia\App;
use Utopia\Config\Config;
use Utopia\Validator;
/**
@ -59,13 +65,13 @@ function createTypeMapping(Model $model, Response $response) {
// If map already contains this complex type, then simply return
if (isset($typeMapping[$model->getType()])) return;
$rules = $model->getRules();
$name = $model->getType();
$fields = [];
$type = null;
foreach ($rules as $key => $props) {
// Replace this with php regex
$key = str_replace('$', '', $key);
$keyWithoutSpecialChars = str_replace('$', '', $key);
if (isset( $typeMapping[$props['type']])) {
$type = $typeMapping[$props['type']];
} else {
@ -82,10 +88,30 @@ function createTypeMapping(Model $model, Response $response) {
$type = Type::listOf($type);
}
$fields[$key] = [
$fields[$keyWithoutSpecialChars] = [
'type' => $type,
'description' => $props['description'],
'resolve' => function ($type, $args, $context, $info) use ($key) {
var_dump("************* RESOLVING FIELD {$info->fieldName} *************");
// var_dump("isCompositeType : ", Type::isCompositeType($type));
// var_dump("isLeafType : ", Type::isLeafType($type));
if ( $type[$key] instanceof stdClass ) {
// var_dump("STD Class");
return json_encode($type[$key]);
} else {
// var_dump("not stdclass");
return $type[$key];
}
}
];
// if ($keyWithoutSpecialChars !== $key) {
// $fields[$keyWithoutSpecialChars]['resolve'] = function ($value, $args, $context, $info) use ($key) {
// var_dump("************* RESOLVING FIELD {$info->fieldName} *************");
// var_dump($value);
// return $value[$key];
// };
// }
}
$objectType = [
@ -168,6 +194,29 @@ function getArgs(array $params, $utopia) {
return $args;
}
function isModel($response, Model $model) {
foreach ($model->getRules() as $key => $rule) {
if (!isset($response[$key])) {
return false;
}
}
return true;
}
class MySafeException extends \Exception implements ClientAware
{
public function isClientSafe()
{
return true;
}
public function getCategory()
{
return 'businessLogic';
}
}
function buildSchema($utopia, $response) {
$start = microtime(true);
@ -178,33 +227,48 @@ function buildSchema($utopia, $response) {
foreach($routes as $route) {
$namespace = $route->getLabel('sdk.namespace', '');
if ($namespace == 'users') {
if ($namespace == 'database' || true) {
$methodName = $namespace.'_'.$route->getLabel('sdk.method', '');
$responseModelName = $route->getLabel('sdk.response.model', "");
var_dump("******************************************");
var_dump("Processing route : ${method} : {$route->getURL()}");
var_dump("Model Name : ${responseModelName}");
if ( $responseModelName !== Response::MODEL_NONE && $responseModelName !== Response::MODEL_ANY ) {
// var_dump("******************************************");
// var_dump("Processing route : ${method} : {$route->getURL()}");
// var_dump("Model Name : ${responseModelName}");
if ( $responseModelName !== "" && $responseModelName !== Response::MODEL_NONE && $responseModelName !== Response::MODEL_ANY ) {
$responseModel = $response->getModel($responseModelName);
createTypeMapping($responseModel, $response);
$type = $typeMapping[$responseModel->getType()];
var_dump("Type Created : ${type}");
// var_dump("Type Created : ${type}");
$args = getArgs($route->getParams(), $utopia);
var_dump("Args Generated : ${args}");
// var_dump("Args Generated :");
// var_dump($args);
$field = [
'type' => $type,
'description' => $route->getDesc(),
'args' => $args,
'resolve' => function ($args) use (&$utopia, $route, $response) {
var_dump("************* REACHED RESOLVE *****************");
var_dump($route);
$utopia->execute($route, $args);
'resolve' => function ($type, $args, $context, $info) use (&$utopia, $route, $response) {
var_dump("************* REACHED RESOLVE FOR {$info->fieldName} *****************");
// var_dump($route);
// var_dump("************* CONTEXT *****************");
// var_dump($context);
var_dump("********************** ARGS *******************");
var_dump($args);
$utopia->setRoute($route);
$utopia->execute($route, $args);
var_dump("**************** OUTPUT ************");
var_dump($response->getPayload());
return $response->getPayload();
// var_dump($response->getPayload());
$result = $response->getPayload();
if (isModel($result, $response->getModel(Response::MODEL_ERROR)) || isModel($result, $response->getModel(Response::MODEL_ERROR_DEV))) {
throw new MySafeException($result['message'], $result['code']);
}
return $result;
}
];
@ -214,9 +278,9 @@ function buildSchema($utopia, $response) {
$mutationFields[$methodName] = $field;
}
var_dump("Processed route : ${method} : {$route->getURL()}");
// var_dump("Processed route : ${method} : {$route->getURL()}");
} else {
var_dump("Skipping route : {$route->getURL()}");
// var_dump("Skipping route : {$route->getURL()}");
}
}
}
@ -252,16 +316,19 @@ function buildSchema($utopia, $response) {
App::post('/v1/graphql')
->desc('GraphQL Endpoint')
->groups(['api', 'graphql'])
->label('scope', 'public')
->label('scope', 'graphql')
->inject('request')
->inject('response')
->inject('utopia')
->action(function ($request, $response, $utopia) {
->inject('user')
->inject('project')
->middleware(true)
->action(function ($request, $response, $utopia, $user, $project) {
// Generate the Schema of the server on startup.
// Use the routes from utopia and get the params then construct the queries and mutations.
$schema = buildSchema($utopia, $response);
$schema = buildSchema($utopia, $response, $request);
$query = $request->getPayload('query', '');
$variables = $request->getPayload('variables', null);
$response->setContentType(Response::CONTENT_TYPE_NULL);
@ -270,8 +337,8 @@ App::post('/v1/graphql')
$rootValue = [];
$result = GraphQL::executeQuery($schema, $query, $rootValue, null, $variables);
$output = $result->toArray();
var_dump("********** OUTPUT *********");
var_dump($output);
// var_dump("********** OUTPUT *********");
// var_dump($output);
} catch (\Exception $error) {
$output = [
'errors' => [
@ -286,6 +353,5 @@ App::post('/v1/graphql')
];
}
$response->json($output);
echo "\n"; //TODO REMOVE THIS
}
);
);

View file

@ -98,6 +98,8 @@ App::get('/v1/users')
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
var_dump("Running execute method for list users");
$results = $projectDB->getCollection([
'limit' => $limit,
'offset' => $offset,
@ -217,7 +219,7 @@ App::get('/v1/users/:userId/sessions')
'sum' => count($sessions),
'sessions' => $sessions
]), Response::MODEL_SESSION_LIST);
}, ['response', 'projectDB', 'locale']);
});
App::get('/v1/users/:userId/logs')
->desc('Get User Logs')

View file

@ -33,6 +33,8 @@ App::init(function ($utopia, $request, $response, $console, $project, $user, $lo
/** @var bool $mode */
/** @var array $clients */
var_dump("*********** In general.php init *************");
$localeParam = (string)$request->getParam('locale', $request->getHeader('x-appwrite-locale', ''));
if (\in_array($localeParam, Config::getParam('locale-codes'))) {
@ -210,6 +212,10 @@ App::init(function ($utopia, $request, $response, $console, $project, $user, $lo
// TDOO Check if user is god
var_dump("*********** Allowed Scopes *********");
var_dump($scopes);
var_dump($scope);
if (!\in_array($scope, $scopes)) {
if (empty($project->getId()) || Database::SYSTEM_COLLECTION_PROJECTS !== $project->getCollection()) { // Check if permission is denied because project is missing
throw new Exception('Project not found', 404);
@ -252,6 +258,8 @@ App::error(function ($error, $utopia, $request, $response, $layout, $project) {
/** @var Utopia\View $layout */
/** @var Appwrite\Database\Document $project */
var_dump("*********** In general.php error *************");
$route = $utopia->match($request);
$template = ($route) ? $route->getLabel('error', null) : null;

View file

@ -22,6 +22,8 @@ App::init(function ($utopia, $request, $response, $project, $user, $register, $e
/** @var Appwrite\Event\Event $deletes */
/** @var Appwrite\Event\Event $functions */
var_dump("*********** In api.php init *************");
Storage::setDevice('files', new Local(APP_STORAGE_UPLOADS.'/app-'.$project->getId()));
Storage::setDevice('functions', new Local(APP_STORAGE_FUNCTIONS.'/app-'.$project->getId()));
@ -50,9 +52,9 @@ App::init(function ($utopia, $request, $response, $project, $user, $register, $e
// var_dump($request->getParams());
foreach ($request->getParams() as $key => $value) { // Set request params as potential abuse keys
$timeLimit->setParam('{param-'.$key.'}', (\is_array($value)) ? \json_encode($value) : $value);
}
// foreach ($request->getParams() as $key => $value) { // Set request params as potential abuse keys
// $timeLimit->setParam('{param-'.$key.'}', (\is_array($value)) ? \json_encode($value) : $value);
// }
$abuse = new Abuse($timeLimit);
@ -125,6 +127,8 @@ App::shutdown(function ($utopia, $request, $response, $project, $events, $audits
/** @var Appwrite\Event\Event $functions */
/** @var bool $mode */
var_dump("*********** In api.php shutdown *************");
if (!empty($events->getParam('event'))) {
if(empty($events->getParam('payload'))) {
$events->setParam('payload', $response->getPayload());

View file

@ -102,6 +102,7 @@ $http->on('request', function (SwooleRequest $swooleRequest, SwooleResponse $swo
$app->run($request, $response);
} catch (\Throwable $th) {
var_dump("*********** In http.php catching error *************");
Console::error('[Error] Type: '.get_class($th));
Console::error('[Error] Message: '.$th->getMessage());
Console::error('[Error] File: '.$th->getFile());

View file

@ -212,10 +212,6 @@ $register->set('geodb', function () {
return new Reader(__DIR__.'/db/DBIP/dbip-country-lite-2021-02.mmdb');
});
$register->set('schema', function () {
});
/*
* Localization
*/
@ -289,14 +285,14 @@ Locale::setLanguage('zh-tw', include __DIR__.'/config/locale/translations/zh-tw.
// Runtime Execution
App::setResource('routeToScopeMapping', function($register) {
return new Event(Event::MAILS_QUEUE_NAME, Event::MAILS_CLASS_NAME);
}, ['register']);
App::setResource('register', function() use ($register) {
return $register;
});
App::setResource('schema', function() use ($register) {
return $register->get('schema');
});
App::setResource('layout', function($locale) {
$layout = new View(__DIR__.'/views/layouts/default.phtml');
$layout->setParam('locale', $locale);

View file

@ -34,7 +34,7 @@
"appwrite/php-clamav": "1.0.*",
"utopia-php/framework": "0.10.0",
"utopia-php/framework": "0.12.0",
"utopia-php/abuse": "0.3.*",
"utopia-php/analytics": "0.1.*",
"utopia-php/audit": "0.5.*",
@ -47,7 +47,7 @@
"utopia-php/domains": "0.2.*",
"utopia-php/swoole": "0.2.*",
"utopia-php/system": "0.4.*",
"utopia-php/storage": "0.2.*",
"utopia-php/storage": "0.4.*",
"webonyx/graphql-php": "14.4.0",
"resque/php-resque": "1.3.6",

54
composer.lock generated
View file

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "aed281e2643524e5f1320533abb612eb",
"content-hash": "d1b9fa5657767534eefd141136bb3cfd",
"packages": [
{
"name": "adhocore/jwt",
@ -349,7 +349,7 @@
"issues": "https://github.com/domnikl/statsd-php/issues",
"source": "https://github.com/domnikl/statsd-php/tree/master"
},
"abandoned": true,
"abandoned": "slickdeals/statsd",
"time": "2020-01-03T14:24:58+00:00"
},
{
@ -574,12 +574,12 @@
"source": {
"type": "git",
"url": "https://github.com/guzzle/psr7.git",
"reference": "f47ece9e6e8ce74e3be04bef47f46061dc18c095"
"reference": "2f3e4f6cf8fd4aad7624c90a94f0ab38fde25976"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/psr7/zipball/f47ece9e6e8ce74e3be04bef47f46061dc18c095",
"reference": "f47ece9e6e8ce74e3be04bef47f46061dc18c095",
"url": "https://api.github.com/repos/guzzle/psr7/zipball/2f3e4f6cf8fd4aad7624c90a94f0ab38fde25976",
"reference": "2f3e4f6cf8fd4aad7624c90a94f0ab38fde25976",
"shasum": ""
},
"require": {
@ -641,7 +641,7 @@
"issues": "https://github.com/guzzle/psr7/issues",
"source": "https://github.com/guzzle/psr7/tree/1.x"
},
"time": "2020-12-08T11:45:39+00:00"
"time": "2021-03-02T18:57:24+00:00"
},
{
"name": "influxdb/influxdb-php",
@ -1004,12 +1004,12 @@
"source": {
"type": "git",
"url": "https://github.com/php-fig/log.git",
"reference": "dd738d0b4491f32725492cf345f6b501f5922fec"
"reference": "a18c1e692e02b84abbafe4856c3cd7cc6903908c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/log/zipball/dd738d0b4491f32725492cf345f6b501f5922fec",
"reference": "dd738d0b4491f32725492cf345f6b501f5922fec",
"url": "https://api.github.com/repos/php-fig/log/zipball/a18c1e692e02b84abbafe4856c3cd7cc6903908c",
"reference": "a18c1e692e02b84abbafe4856c3cd7cc6903908c",
"shasum": ""
},
"require": {
@ -1047,7 +1047,7 @@
"support": {
"source": "https://github.com/php-fig/log/tree/master"
},
"time": "2020-09-18T06:44:51+00:00"
"time": "2021-03-02T15:02:34+00:00"
},
{
"name": "ralouphie/getallheaders",
@ -1548,16 +1548,16 @@
},
{
"name": "utopia-php/framework",
"version": "0.10.0",
"version": "0.12.0",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/framework.git",
"reference": "65909bdb24ef6b6c6751abfdea90caf96bbc6c50"
"reference": "bfdb236f91f4393f6db7faccd2d67550a1e73101"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/framework/zipball/65909bdb24ef6b6c6751abfdea90caf96bbc6c50",
"reference": "65909bdb24ef6b6c6751abfdea90caf96bbc6c50",
"url": "https://api.github.com/repos/utopia-php/framework/zipball/bfdb236f91f4393f6db7faccd2d67550a1e73101",
"reference": "bfdb236f91f4393f6db7faccd2d67550a1e73101",
"shasum": ""
},
"require": {
@ -1591,9 +1591,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/framework/issues",
"source": "https://github.com/utopia-php/framework/tree/0.10.0"
"source": "https://github.com/utopia-php/framework/tree/0.12.0"
},
"time": "2020-12-26T12:02:39+00:00"
"time": "2021-03-04T17:14:23+00:00"
},
{
"name": "utopia-php/locale",
@ -1753,21 +1753,21 @@
},
{
"name": "utopia-php/storage",
"version": "0.2.0",
"version": "0.4.3",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/storage.git",
"reference": "27bfd663c9b2a17ac0911522a87f42bee834df95"
"reference": "9db3ab713a6d392c3c2c799aeea751f6c8dc2ff7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/storage/zipball/27bfd663c9b2a17ac0911522a87f42bee834df95",
"reference": "27bfd663c9b2a17ac0911522a87f42bee834df95",
"url": "https://api.github.com/repos/utopia-php/storage/zipball/9db3ab713a6d392c3c2c799aeea751f6c8dc2ff7",
"reference": "9db3ab713a6d392c3c2c799aeea751f6c8dc2ff7",
"shasum": ""
},
"require": {
"php": ">=7.4",
"utopia-php/framework": "0.10.0"
"utopia-php/framework": "0.*.*"
},
"require-dev": {
"phpunit/phpunit": "^9.3",
@ -1799,9 +1799,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/storage/issues",
"source": "https://github.com/utopia-php/storage/tree/0.2.0"
"source": "https://github.com/utopia-php/storage/tree/0.4.3"
},
"time": "2021-01-27T12:21:27+00:00"
"time": "2021-03-02T20:25:02+00:00"
},
{
"name": "utopia-php/swoole",
@ -5792,12 +5792,12 @@
"source": {
"type": "git",
"url": "https://github.com/webmozarts/assert.git",
"reference": "9c89b265ccc4092d58e66d72af5d343ee77a41ae"
"reference": "4631e2c7d2d7132adac9fd84d4c1a98c10a6e049"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/webmozarts/assert/zipball/9c89b265ccc4092d58e66d72af5d343ee77a41ae",
"reference": "9c89b265ccc4092d58e66d72af5d343ee77a41ae",
"url": "https://api.github.com/repos/webmozarts/assert/zipball/4631e2c7d2d7132adac9fd84d4c1a98c10a6e049",
"reference": "4631e2c7d2d7132adac9fd84d4c1a98c10a6e049",
"shasum": ""
},
"require": {
@ -5843,7 +5843,7 @@
"issues": "https://github.com/webmozarts/assert/issues",
"source": "https://github.com/webmozarts/assert/tree/master"
},
"time": "2021-01-18T12:52:36+00:00"
"time": "2021-02-28T20:01:57+00:00"
},
{
"name": "webmozart/path-util",

View file

@ -63,7 +63,7 @@ services:
- ./psalm.xml:/usr/src/code/psalm.xml
- ./tests:/usr/src/code/tests
- ./app:/usr/src/code/app
# - ./vendor:/usr/src/code/vendor
- ./vendor:/usr/src/code/vendor
- ./docs:/usr/src/code/docs
- ./public:/usr/src/code/public
- ./src:/usr/src/code/src

View file

@ -117,7 +117,7 @@ class Response extends SwooleResponse
const MODEL_DOMAIN_LIST = 'domainList';
// Content type
const CONTENT_TYPE_NULL = null;
const CONTENT_TYPE_NULL = 'null';
/**
* @var Filter
@ -276,7 +276,7 @@ class Response extends SwooleResponse
break;
default :
throw new Exception("No Response format set.");
$this->json(!empty($output) ? $output : new stdClass());
break;
}
}
@ -330,6 +330,14 @@ class Response extends SwooleResponse
$this->payload = $output;
var_dump("********************** PAYLOAD SET *********************");
var_dump("Message : {$output['message']}");
var_dump("Code : {$output['code']}");
var_dump("Version : {$output['version']}");
var_dump("File : {$output['file']}");
var_dump("Line : {$output['line']}");
var_dump("Trace : ");
return $this->payload;
}