Add resolvers for get, list, create, update, delete for user collections
This commit is contained in:
parent
69e7c2fed9
commit
48ba76f365
9 changed files with 279 additions and 116 deletions
|
@ -1,6 +1,5 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
use Appwrite\GraphQL\GraphQLPromiseAdapter;
|
|
||||||
use Appwrite\Utopia\Response;
|
use Appwrite\Utopia\Response;
|
||||||
use GraphQL\Error\DebugFlag;
|
use GraphQL\Error\DebugFlag;
|
||||||
use GraphQL\Executor\ExecutionResult;
|
use GraphQL\Executor\ExecutionResult;
|
||||||
|
@ -29,9 +28,15 @@ App::post('/v1/graphql')
|
||||||
|
|
||||||
$query = $request->getPayload('query', '');
|
$query = $request->getPayload('query', '');
|
||||||
$variables = $request->getPayload('variables');
|
$variables = $request->getPayload('variables');
|
||||||
|
|
||||||
$response->setContentType(Response::CONTENT_TYPE_NULL);
|
$response->setContentType(Response::CONTENT_TYPE_NULL);
|
||||||
|
|
||||||
|
$register->set('__app', function () use ($utopia) {
|
||||||
|
return $utopia;
|
||||||
|
});
|
||||||
|
$register->set('__response', function () use ($response) {
|
||||||
|
return $response;
|
||||||
|
});
|
||||||
|
|
||||||
$isDevelopment = App::isDevelopment();
|
$isDevelopment = App::isDevelopment();
|
||||||
|
|
||||||
$debugFlags = $isDevelopment
|
$debugFlags = $isDevelopment
|
||||||
|
|
14
app/init.php
14
app/init.php
|
@ -789,6 +789,8 @@ App::setResource('dbForProject', function($db, $cache, $project) {
|
||||||
$database->setDefaultDatabase(App::getEnv('_APP_DB_SCHEMA', 'appwrite'));
|
$database->setDefaultDatabase(App::getEnv('_APP_DB_SCHEMA', 'appwrite'));
|
||||||
$database->setNamespace("_{$project->getId()}");
|
$database->setNamespace("_{$project->getId()}");
|
||||||
|
|
||||||
|
Console::info("Getting dbForProject with ID: {$project->getId()}");
|
||||||
|
|
||||||
return $database;
|
return $database;
|
||||||
}, ['db', 'cache', 'project']);
|
}, ['db', 'cache', 'project']);
|
||||||
|
|
||||||
|
@ -858,16 +860,14 @@ App::setResource('geodb', function($register) {
|
||||||
|
|
||||||
App::setResource('schema', function($utopia, $response, $request, $register, $dbForProject) {
|
App::setResource('schema', function($utopia, $response, $request, $register, $dbForProject) {
|
||||||
try {
|
try {
|
||||||
// Try to get the schema from the register.
|
Console::log('Getting GraphQL schema from register...');
|
||||||
// If there is no base schema catch the exception and generate it.
|
|
||||||
// If the base schema exists, extend it with the current project schema.
|
|
||||||
Console::log('Getting Schema from register...');
|
|
||||||
$schema = $register->get('_schema');
|
$schema = $register->get('_schema');
|
||||||
$schema = Builder::appendSchema($schema, $dbForProject);
|
$schema = Builder::appendSchema($schema, $dbForProject);
|
||||||
} catch (Exception $e) {
|
} catch (\Exception $e) {
|
||||||
Console::error('Schema not present. Generating Schema...');
|
Console::error('Base GraphQL schema not present. Generating...');
|
||||||
$schema = Builder::buildSchema($utopia, $response, $register, $dbForProject);
|
$schema = Builder::buildSchema($utopia, $response, $register, $dbForProject);
|
||||||
$register->set('_schema', function () use ($schema){
|
Console::error('Built GraphQL schema: ' . \json_encode($schema));
|
||||||
|
$register->set('_schema', function () use ($schema) {
|
||||||
return $schema;
|
return $schema;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,7 @@ use Utopia\WebSocket\Adapter;
|
||||||
|
|
||||||
require_once __DIR__ . '/init.php';
|
require_once __DIR__ . '/init.php';
|
||||||
|
|
||||||
Runtime::enableCoroutine(SWOOLE_HOOK_ALL);
|
Runtime::enableCoroutine(true,SWOOLE_HOOK_ALL);
|
||||||
|
|
||||||
$realtime = new Realtime();
|
$realtime = new Realtime();
|
||||||
|
|
||||||
|
|
24
composer.lock
generated
24
composer.lock
generated
|
@ -1583,16 +1583,16 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/deprecation-contracts",
|
"name": "symfony/deprecation-contracts",
|
||||||
"version": "v3.0.0",
|
"version": "v3.0.1",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/deprecation-contracts.git",
|
"url": "https://github.com/symfony/deprecation-contracts.git",
|
||||||
"reference": "c726b64c1ccfe2896cb7df2e1331c357ad1c8ced"
|
"reference": "26954b3d62a6c5fd0ea8a2a00c0353a14978d05c"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/c726b64c1ccfe2896cb7df2e1331c357ad1c8ced",
|
"url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/26954b3d62a6c5fd0ea8a2a00c0353a14978d05c",
|
||||||
"reference": "c726b64c1ccfe2896cb7df2e1331c357ad1c8ced",
|
"reference": "26954b3d62a6c5fd0ea8a2a00c0353a14978d05c",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -1630,7 +1630,7 @@
|
||||||
"description": "A generic function and convention to trigger deprecation notices",
|
"description": "A generic function and convention to trigger deprecation notices",
|
||||||
"homepage": "https://symfony.com",
|
"homepage": "https://symfony.com",
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/deprecation-contracts/tree/v3.0.0"
|
"source": "https://github.com/symfony/deprecation-contracts/tree/v3.0.1"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
|
@ -1646,7 +1646,7 @@
|
||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2021-11-01T23:48:49+00:00"
|
"time": "2022-01-02T09:55:41+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/polyfill-ctype",
|
"name": "symfony/polyfill-ctype",
|
||||||
|
@ -6127,16 +6127,16 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/service-contracts",
|
"name": "symfony/service-contracts",
|
||||||
"version": "v3.0.0",
|
"version": "v3.0.1",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/service-contracts.git",
|
"url": "https://github.com/symfony/service-contracts.git",
|
||||||
"reference": "36715ebf9fb9db73db0cb24263c79077c6fe8603"
|
"reference": "e517458f278c2131ca9f262f8fbaf01410f2c65c"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/service-contracts/zipball/36715ebf9fb9db73db0cb24263c79077c6fe8603",
|
"url": "https://api.github.com/repos/symfony/service-contracts/zipball/e517458f278c2131ca9f262f8fbaf01410f2c65c",
|
||||||
"reference": "36715ebf9fb9db73db0cb24263c79077c6fe8603",
|
"reference": "e517458f278c2131ca9f262f8fbaf01410f2c65c",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -6189,7 +6189,7 @@
|
||||||
"standards"
|
"standards"
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/service-contracts/tree/v3.0.0"
|
"source": "https://github.com/symfony/service-contracts/tree/v3.0.1"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
|
@ -6205,7 +6205,7 @@
|
||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2021-11-04T17:53:12+00:00"
|
"time": "2022-03-13T20:10:05+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/string",
|
"name": "symfony/string",
|
||||||
|
|
|
@ -727,13 +727,13 @@ services:
|
||||||
# - '3001:80'
|
# - '3001:80'
|
||||||
|
|
||||||
graphql-explorer:
|
graphql-explorer:
|
||||||
container_name: graphql-explorer
|
container_name: appwrite-graphql-explorer
|
||||||
image: appwrite/altair:0.1.0
|
image: appwrite/altair:0.1.0
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
networks:
|
networks:
|
||||||
- appwrite
|
- appwrite
|
||||||
ports:
|
ports:
|
||||||
- 9509:3000
|
- "9509:3000"
|
||||||
environment:
|
environment:
|
||||||
- SERVER_URL=http://localhost/v1/graphql
|
- SERVER_URL=http://localhost/v1/graphql
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,10 @@ use GraphQL\Type\Definition\ObjectType;
|
||||||
use GraphQL\Type\Definition\Type;
|
use GraphQL\Type\Definition\Type;
|
||||||
use GraphQL\Type\Schema;
|
use GraphQL\Type\Schema;
|
||||||
use Utopia\CLI\Console;
|
use Utopia\CLI\Console;
|
||||||
|
use Utopia\Database\Database;
|
||||||
|
use Utopia\Database\Document;
|
||||||
|
use Utopia\Database\Validator\Authorization;
|
||||||
|
use Utopia\Registry\Registry;
|
||||||
|
|
||||||
class Builder
|
class Builder
|
||||||
{
|
{
|
||||||
|
@ -171,7 +175,7 @@ class Builder
|
||||||
Console::log("[INFO] Appending GraphQL Database Schema...");
|
Console::log("[INFO] Appending GraphQL Database Schema...");
|
||||||
$start = microtime(true);
|
$start = microtime(true);
|
||||||
|
|
||||||
$db = self::buildDatabaseSchema($dbForProject);
|
$db = self::buildCollectionsSchema($dbForProject);
|
||||||
|
|
||||||
$queryFields = $schema->getQueryType()?->getFields() ?? [];
|
$queryFields = $schema->getQueryType()?->getFields() ?? [];
|
||||||
$mutationFields = $schema->getMutationType()?->getFields() ?? [];
|
$mutationFields = $schema->getMutationType()?->getFields() ?? [];
|
||||||
|
@ -201,10 +205,13 @@ class Builder
|
||||||
return $schema;
|
return $schema;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
public static function buildSchema($utopia, $response, $register, $dbForProject): Schema
|
public static function buildSchema($utopia, $response, $register, $dbForProject): Schema
|
||||||
{
|
{
|
||||||
$db = self::buildDatabaseSchema($dbForProject);
|
$db = self::buildCollectionsSchema($dbForProject, $register);
|
||||||
$api = self::buildAPISchema($utopia, $response, $register, $dbForProject);
|
$api = self::buildAPISchema($utopia, $response, $register);
|
||||||
|
|
||||||
$queryFields = \array_merge($api['query'], $db['query']);
|
$queryFields = \array_merge($api['query'], $db['query']);
|
||||||
$mutationFields = \array_merge($api['mutation'], $db['mutation']);
|
$mutationFields = \array_merge($api['mutation'], $db['mutation']);
|
||||||
|
@ -215,12 +222,12 @@ class Builder
|
||||||
return new Schema([
|
return new Schema([
|
||||||
'query' => new ObjectType([
|
'query' => new ObjectType([
|
||||||
'name' => 'Query',
|
'name' => 'Query',
|
||||||
'description' => 'The root of all your queries',
|
'description' => 'The root of all queries',
|
||||||
'fields' => $queryFields
|
'fields' => $queryFields
|
||||||
]),
|
]),
|
||||||
'mutation' => new ObjectType([
|
'mutation' => new ObjectType([
|
||||||
'name' => 'Mutation',
|
'name' => 'Mutation',
|
||||||
'description' => 'The root of all your mutations',
|
'description' => 'The root of all mutations',
|
||||||
'fields' => $mutationFields
|
'fields' => $mutationFields
|
||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
|
@ -230,77 +237,120 @@ class Builder
|
||||||
* This function goes through all the project attributes and builds a
|
* This function goes through all the project attributes and builds a
|
||||||
* GraphQL schema for all the collections they make up.
|
* GraphQL schema for all the collections they make up.
|
||||||
*
|
*
|
||||||
* @param $dbForProject
|
* @param Database $dbForProject
|
||||||
* @return array
|
* @return array
|
||||||
|
* @throws \Exception
|
||||||
*/
|
*/
|
||||||
public static function buildDatabaseSchema($dbForProject): array
|
public static function buildCollectionsSchema(Database $dbForProject, Registry &$register): array
|
||||||
{
|
{
|
||||||
Console::log("[INFO] Building GraphQL Database Schema...");
|
Console::log("[INFO] Building GraphQL Database Schema...");
|
||||||
$start = microtime(true);
|
$start = microtime(true);
|
||||||
|
|
||||||
$attrs = $dbForProject->getCollection('attributes');
|
$collections = [];
|
||||||
|
|
||||||
$queryFields = [];
|
$queryFields = [];
|
||||||
$mutationFields = [];
|
$mutationFields = [];
|
||||||
$collections = [];
|
$offset = 0;
|
||||||
|
|
||||||
foreach ($attrs as $attr) {
|
Authorization::skip(function () use ($mutationFields, $queryFields, $collections, $register, $offset, $dbForProject) {
|
||||||
$collectionId = $attr->getAttribute('collectionId');
|
while (!empty($attrs = $dbForProject->find(
|
||||||
|
'attributes',
|
||||||
|
limit: $dbForProject->getAttributeLimit(),
|
||||||
|
offset: $offset
|
||||||
|
))) {
|
||||||
|
go(function ($attrs, $dbForProject, $register, $collections, $queryFields, $mutationFields) {
|
||||||
|
foreach ($attrs as $attr) {
|
||||||
|
go(function ($attr, &$collections) {
|
||||||
|
/** @var Document $attr */
|
||||||
|
|
||||||
if (isset(self::$typeMapping[$collectionId])) {
|
$collectionId = $attr->getAttribute('collectionId');
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$key = $attr->getAttribute('key');
|
if (isset(self::$typeMapping[$collectionId])) {
|
||||||
$type = $attr->getAttribute('type');
|
return;
|
||||||
$keyWithoutSpecialChars = str_replace('$', '_', $key);
|
}
|
||||||
|
if ($attr->getAttribute('status') !== 'available') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
$collections[$collectionId][$keyWithoutSpecialChars] = [
|
$key = $attr->getAttribute('key');
|
||||||
'type' => $type,
|
$type = $attr->getAttribute('type');
|
||||||
'resolve' => function ($object, $args, $context, $info) use ($key) {
|
|
||||||
return $object->getAttribute($key);
|
|
||||||
}
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
$args = [];
|
$escapedKey = str_replace('$', '_', $key);
|
||||||
|
|
||||||
foreach ($collections as $id => $fields) {
|
$collections[$collectionId][$escapedKey] = [
|
||||||
$objectType = new ObjectType([
|
'type' => $type,
|
||||||
'name' => $id,
|
'resolve' => function ($object, $args, $context, $info) use ($key) {
|
||||||
'fields' => $fields
|
return $object->getAttribute($key);
|
||||||
]);
|
}
|
||||||
|
];
|
||||||
|
|
||||||
self::$typeMapping[$id] = $objectType;
|
}, $attr, $collections);
|
||||||
|
|
||||||
foreach ($fields as $field => $fieldInfo) {
|
|
||||||
$args[$field] = [
|
|
||||||
'type' => $fieldInfo['type']
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
$resolve = function ($type, $args, $context, $info) use (&$register, $dbForProject) {
|
|
||||||
return SwoolePromise::create(function (callable $resolve, callable $reject) use ($type, $args, $dbForProject) {
|
|
||||||
try {
|
|
||||||
$resolve($dbForProject->getCollection($type));
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
$reject($e);
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
$field = [
|
foreach ($collections as $collectionId => $attributes) {
|
||||||
'type' => $type,
|
go(function ($collectionId, $attributes, $dbForProject, $register, &$queryFields, &$mutationFields) {
|
||||||
'args' => $args,
|
if (isset(self::$typeMapping[$collectionId])) {
|
||||||
'resolve' => $resolve
|
return;
|
||||||
];
|
}
|
||||||
|
|
||||||
$queryFields[$id] = $field;
|
$objectType = new ObjectType([
|
||||||
$mutationFields[$id] = $field;
|
'name' => \ucfirst($collectionId),
|
||||||
}
|
'fields' => $attributes
|
||||||
|
]);
|
||||||
|
|
||||||
|
self::$typeMapping[$collectionId] = $objectType;
|
||||||
|
|
||||||
|
$mutateArgs = [];
|
||||||
|
|
||||||
|
foreach ($attributes as $name => $attribute) {
|
||||||
|
$mutateArgs[$name] = [
|
||||||
|
'type' => $attribute['type']
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$idArgs = [
|
||||||
|
'id' => [
|
||||||
|
'type' => Type::string()
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
$listArgs = [
|
||||||
|
'limit' => [
|
||||||
|
'type' => Type::int()
|
||||||
|
],
|
||||||
|
'offset' => [
|
||||||
|
'type' => Type::int()
|
||||||
|
],
|
||||||
|
'cursor' => [
|
||||||
|
'type' => Type::string()
|
||||||
|
],
|
||||||
|
'orderAttributes' => [
|
||||||
|
'type' => Type::listOf(Type::string())
|
||||||
|
],
|
||||||
|
'orderType' => [
|
||||||
|
'types' => Type::listOf(Type::string())
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
self::createCollectionGetQuery($collectionId, $register, $dbForProject, $idArgs, $queryFields);
|
||||||
|
self::createCollectionListQuery($collectionId, $register, $dbForProject, $listArgs, $queryFields);
|
||||||
|
self::createCollectionCreateMutation($collectionId, $register, $dbForProject, $mutateArgs, $mutationFields);
|
||||||
|
self::createCollectionUpdateMutation($collectionId, $register, $dbForProject, $mutateArgs, $mutationFields);
|
||||||
|
self::createCollectionDeleteMutation($collectionId, $register, $dbForProject, $idArgs, $mutationFields);
|
||||||
|
|
||||||
|
}, $collectionId, $attributes, $dbForProject, $register, $queryFields, $mutationFields);
|
||||||
|
}
|
||||||
|
}, $attrs, $dbForProject, $register, $collections, $queryFields, $mutationFields);
|
||||||
|
|
||||||
|
$offset += $dbForProject->getAttributeLimit();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
$time_elapsed_secs = microtime(true) - $start;
|
$time_elapsed_secs = microtime(true) - $start;
|
||||||
Console::log("[INFO] Time Taken To Build Database Schema : ${time_elapsed_secs}s");
|
Console::log("[INFO] Time Taken To Build Database Schema : ${time_elapsed_secs}s");
|
||||||
|
Console::info('[INFO] Schema : ' . json_encode([
|
||||||
|
'query' => $queryFields,
|
||||||
|
'mutation' => $mutationFields
|
||||||
|
]));
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'query' => $queryFields,
|
'query' => $queryFields,
|
||||||
|
@ -308,6 +358,104 @@ class Builder
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static function createCollectionGetQuery($collectionId, $register, $dbForProject, $args, &$queryFields)
|
||||||
|
{
|
||||||
|
$resolve = function ($type, $args, $context, $info) use ($collectionId, &$register, $dbForProject) {
|
||||||
|
return SwoolePromise::create(function (callable $resolve, callable $reject) use ($collectionId, $type, $args, $dbForProject) {
|
||||||
|
try {
|
||||||
|
$resolve($dbForProject->getDocument($collectionId, $args['id']));
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
$reject($e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
$get = [
|
||||||
|
'type' => \ucfirst($collectionId),
|
||||||
|
'args' => $args,
|
||||||
|
'resolve' => $resolve
|
||||||
|
];
|
||||||
|
$queryFields['get' . \ucfirst($collectionId)] = $get;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function createCollectionListQuery($collectionId, $register, $dbForProject, $args, &$queryFields)
|
||||||
|
{
|
||||||
|
$resolve = function ($type, $args, $context, $info) use ($collectionId, &$register, $dbForProject) {
|
||||||
|
return SwoolePromise::create(function (callable $resolve, callable $reject) use ($collectionId, $type, $args, $dbForProject) {
|
||||||
|
try {
|
||||||
|
$resolve($dbForProject->getCollection($collectionId));
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
$reject($e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
$list = [
|
||||||
|
'type' => \ucfirst($collectionId),
|
||||||
|
'args' => $args,
|
||||||
|
'resolve' => $resolve
|
||||||
|
];
|
||||||
|
$queryFields['list' . \ucfirst($collectionId)] = $list;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function createCollectionCreateMutation($collectionId, $register, $dbForProject, $args, &$mutationFields)
|
||||||
|
{
|
||||||
|
$resolve = function ($type, $args, $context, $info) use ($collectionId, &$register, $dbForProject) {
|
||||||
|
return SwoolePromise::create(function (callable $resolve, callable $reject) use ($collectionId, $type, $args, $dbForProject) {
|
||||||
|
try {
|
||||||
|
$resolve($dbForProject->createDocument($collectionId, new Document($args)));
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
$reject($e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
$create = [
|
||||||
|
'type' => \ucfirst($collectionId),
|
||||||
|
'args' => $args,
|
||||||
|
'resolve' => $resolve
|
||||||
|
];
|
||||||
|
$mutationFields['create' . \ucfirst($collectionId)] = $create;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function createCollectionUpdateMutation($collectionId, $register, $dbForProject, $args, &$mutationFields)
|
||||||
|
{
|
||||||
|
$resolve = function ($type, $args, $context, $info) use ($collectionId, &$register, $dbForProject) {
|
||||||
|
return SwoolePromise::create(function (callable $resolve, callable $reject) use ($collectionId, $type, $args, $dbForProject) {
|
||||||
|
try {
|
||||||
|
$resolve($dbForProject->updateDocument($collectionId, $args['id'], new Document($args)));
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
$reject($e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
$update = [
|
||||||
|
'type' => \ucfirst($collectionId),
|
||||||
|
'args' => $args,
|
||||||
|
'resolve' => $resolve
|
||||||
|
];
|
||||||
|
|
||||||
|
$mutationFields['update' . \ucfirst($collectionId)] = $update;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static function createCollectionDeleteMutation($collectionId, $register, $dbForProject, $args, &$mutationFields)
|
||||||
|
{
|
||||||
|
$resolve = function ($type, $args, $context, $info) use ($collectionId, &$register, $dbForProject) {
|
||||||
|
return SwoolePromise::create(function (callable $resolve, callable $reject) use ($collectionId, $type, $args, $dbForProject) {
|
||||||
|
try {
|
||||||
|
$resolve($dbForProject->deleteDocument($collectionId, $args['id']));
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
$reject($e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
$delete = [
|
||||||
|
'type' => \ucfirst($collectionId),
|
||||||
|
'args' => $args,
|
||||||
|
'resolve' => $resolve
|
||||||
|
];
|
||||||
|
$mutationFields['delete' . \ucfirst($collectionId)] = $delete;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function goes through all the REST endpoints in the API and builds a
|
* This function goes through all the REST endpoints in the API and builds a
|
||||||
* GraphQL schema for all those routes whose response model is neither empty nor NONE
|
* GraphQL schema for all those routes whose response model is neither empty nor NONE
|
||||||
|
@ -315,10 +463,9 @@ class Builder
|
||||||
* @param $utopia
|
* @param $utopia
|
||||||
* @param $response
|
* @param $response
|
||||||
* @param $register
|
* @param $register
|
||||||
* @param $dbForProject
|
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
public static function buildAPISchema($utopia, $response, $register, $dbForProject): array
|
public static function buildAPISchema($utopia, $response, $register): array
|
||||||
{
|
{
|
||||||
Console::log("[INFO] Building GraphQL API Schema...");
|
Console::log("[INFO] Building GraphQL API Schema...");
|
||||||
$start = microtime(true);
|
$start = microtime(true);
|
||||||
|
@ -329,12 +476,17 @@ class Builder
|
||||||
|
|
||||||
foreach ($utopia->getRoutes() as $method => $routes) {
|
foreach ($utopia->getRoutes() as $method => $routes) {
|
||||||
foreach ($routes as $route) {
|
foreach ($routes as $route) {
|
||||||
|
|
||||||
$namespace = $route->getLabel('sdk.namespace', '');
|
$namespace = $route->getLabel('sdk.namespace', '');
|
||||||
$methodName = $namespace . '_' . $route->getLabel('sdk.method', '');
|
$methodName = $namespace . \ucfirst($route->getLabel('sdk.method', ''));
|
||||||
$responseModelName = $route->getLabel('sdk.response.model', "");
|
$responseModelName = $route->getLabel('sdk.response.model', "none");
|
||||||
|
|
||||||
if ($responseModelName !== "") {
|
Console::info("Namespace: $namespace");
|
||||||
|
Console::info("Method: $methodName");
|
||||||
|
Console::info("Response Model: $responseModelName");
|
||||||
|
Console::info("Raw routes: " . \json_encode($routes));
|
||||||
|
Console::info("Raw route: " . \json_encode($route));
|
||||||
|
|
||||||
|
if ($responseModelName !== "none") {
|
||||||
$responseModel = $response->getModel($responseModelName);
|
$responseModel = $response->getModel($responseModelName);
|
||||||
|
|
||||||
/* Create a GraphQL type for the current response model */
|
/* Create a GraphQL type for the current response model */
|
||||||
|
@ -351,8 +503,8 @@ class Builder
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
/* Define a resolve function that defines how to fetch data for this type */
|
/* Define a resolve function that defines how to fetch data for this type */
|
||||||
$resolve = function ($type, $args, $context, $info) use (&$register, $route, $dbForProject) {
|
$resolve = function ($type, $args, $context, $info) use (&$register, $route) {
|
||||||
return SwoolePromise::create(function (callable $resolve, callable $reject) use (&$register, $route, $dbForProject, $args) {
|
return SwoolePromise::create(function (callable $resolve, callable $reject) use (&$register, $route, $args) {
|
||||||
$utopia = $register->get('__app');
|
$utopia = $register->get('__app');
|
||||||
$utopia->setRoute($route)->execute($route, $args);
|
$utopia->setRoute($route)->execute($route, $args);
|
||||||
|
|
||||||
|
@ -403,12 +555,12 @@ class Builder
|
||||||
* @param string $version
|
* @param string $version
|
||||||
* @return callable
|
* @return callable
|
||||||
*/
|
*/
|
||||||
public
|
public static function getErrorFormatter(bool $isDevelopment, string $version): callable
|
||||||
static function getErrorFormatter(bool $isDevelopment, string $version): callable
|
|
||||||
{
|
{
|
||||||
$errorFormatter = function (Error $error) use ($isDevelopment, $version) {
|
return function (Error $error) use ($isDevelopment, $version) {
|
||||||
$formattedError = FormattedError::createFromException($error);
|
$formattedError = FormattedError::createFromException($error);
|
||||||
/** Previous error represents the actual error thrown by Appwrite server */
|
|
||||||
|
// Previous error represents the actual error thrown by Appwrite server
|
||||||
$previousError = $error->getPrevious() ?? $error;
|
$previousError = $error->getPrevious() ?? $error;
|
||||||
$formattedError['code'] = $previousError->getCode();
|
$formattedError['code'] = $previousError->getCode();
|
||||||
$formattedError['version'] = $version;
|
$formattedError['version'] = $version;
|
||||||
|
@ -418,7 +570,5 @@ class Builder
|
||||||
}
|
}
|
||||||
return $formattedError;
|
return $formattedError;
|
||||||
};
|
};
|
||||||
|
|
||||||
return $errorFormatter;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
|
|
||||||
namespace Appwrite\GraphQL;
|
namespace Appwrite\GraphQL;
|
||||||
|
|
||||||
use Swoole\Coroutine;
|
|
||||||
use Swoole\Coroutine\Channel;
|
use Swoole\Coroutine\Channel;
|
||||||
|
use function Co\go;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class SwoolePromise
|
* Class SwoolePromise
|
||||||
|
@ -40,7 +40,8 @@ class SwoolePromise
|
||||||
$this->setState(self::STATE_REJECTED);
|
$this->setState(self::STATE_REJECTED);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Coroutine::create(function (callable $executor, callable $resolve, callable $reject) {
|
|
||||||
|
go(function (callable $executor, callable $resolve, callable $reject) {
|
||||||
try {
|
try {
|
||||||
$executor($resolve, $reject);
|
$executor($resolve, $reject);
|
||||||
} catch (\Throwable $exception) {
|
} catch (\Throwable $exception) {
|
||||||
|
|
|
@ -6,17 +6,19 @@ use GraphQL\Error\InvariantViolation;
|
||||||
use GraphQL\Executor\Promise\Promise;
|
use GraphQL\Executor\Promise\Promise;
|
||||||
use GraphQL\Executor\Promise\PromiseAdapter;
|
use GraphQL\Executor\Promise\PromiseAdapter;
|
||||||
use GraphQL\Utils\Utils;
|
use GraphQL\Utils\Utils;
|
||||||
|
use function Co\go;
|
||||||
|
use function Co\run;
|
||||||
|
|
||||||
class GraphQLPromiseAdapter implements PromiseAdapter
|
class SwoolePromiseAdapter implements PromiseAdapter
|
||||||
{
|
{
|
||||||
public function isThenable($value): bool
|
public function isThenable($value): bool
|
||||||
{
|
{
|
||||||
return $value instanceof SwoolePromise;
|
return $value instanceof Promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function convertThenable($thenable): Promise
|
public function convertThenable($thenable): Promise
|
||||||
{
|
{
|
||||||
if (!$thenable instanceof SwoolePromise) {
|
if (!$thenable instanceof Promise) {
|
||||||
throw new InvariantViolation('Expected instance of SwoolePromise, got ' . Utils::printSafe($thenable));
|
throw new InvariantViolation('Expected instance of SwoolePromise, got ' . Utils::printSafe($thenable));
|
||||||
}
|
}
|
||||||
return new Promise($thenable, $this);
|
return new Promise($thenable, $this);
|
||||||
|
@ -66,25 +68,30 @@ class GraphQLPromiseAdapter implements PromiseAdapter
|
||||||
$count = 0;
|
$count = 0;
|
||||||
$result = [];
|
$result = [];
|
||||||
|
|
||||||
foreach ($promisesOrValues as $index => $promiseOrValue) {
|
run(function ($promisesOrValues, $all, $total, &$count, $result) {
|
||||||
if ($promiseOrValue instanceof Promise) {
|
foreach ($promisesOrValues as $index => $promiseOrValue) {
|
||||||
$result[$index] = null;
|
go(function ($index, $promiseOrValue, $all, $total, &$count, $result) {
|
||||||
$promiseOrValue->then(
|
if (!($promiseOrValue instanceof SwoolePromise)) {
|
||||||
static function ($value) use ($index, &$count, $total, &$result, $all): void {
|
$result[$index] = $promiseOrValue;
|
||||||
$result[$index] = $value;
|
|
||||||
$count++;
|
$count++;
|
||||||
if ($count < $total) {
|
return;
|
||||||
return;
|
}
|
||||||
}
|
$result[$index] = null;
|
||||||
$all->resolve($result);
|
$promiseOrValue->then(
|
||||||
},
|
static function ($value) use ($index, &$count, $total, &$result, $all): void {
|
||||||
[$all, 'reject']
|
$result[$index] = $value;
|
||||||
);
|
$count++;
|
||||||
} else {
|
if ($count < $total) {
|
||||||
$result[$index] = $promiseOrValue;
|
return;
|
||||||
$count++;
|
}
|
||||||
|
$all->resolve($result);
|
||||||
|
},
|
||||||
|
[$all, 'reject']
|
||||||
|
);
|
||||||
|
}, $index, $promiseOrValue, $all, $total, $count, $result);
|
||||||
}
|
}
|
||||||
}
|
}, $promisesOrValues, $all, $total, $count, $result);
|
||||||
|
|
||||||
if ($count === $total) {
|
if ($count === $total) {
|
||||||
$all->resolve($result);
|
$all->resolve($result);
|
||||||
}
|
}
|
|
@ -89,7 +89,7 @@ abstract class Migration
|
||||||
*/
|
*/
|
||||||
public function forEachDocument(callable $callback): void
|
public function forEachDocument(callable $callback): void
|
||||||
{
|
{
|
||||||
Runtime::enableCoroutine(SWOOLE_HOOK_ALL);
|
Runtime::enableCoroutine(true, SWOOLE_HOOK_ALL);
|
||||||
|
|
||||||
foreach ($this->collections as $collection) {
|
foreach ($this->collections as $collection) {
|
||||||
$sum = 0;
|
$sum = 0;
|
||||||
|
|
Loading…
Reference in a new issue