appwrite/src/Appwrite/GraphQL/Resolvers.php

327 lines
10 KiB
PHP
Raw Normal View History

2022-07-13 09:34:56 +00:00
<?php
namespace Appwrite\GraphQL;
2022-10-12 07:55:04 +00:00
use Appwrite\GraphQL\Exception as GQLException;
2022-10-12 01:04:11 +00:00
use Appwrite\Promises\Swoole;
2022-07-13 09:34:56 +00:00
use Appwrite\Utopia\Request;
use Appwrite\Utopia\Response;
2024-06-10 16:56:29 +00:00
use Utopia\DI\Container;
2022-07-13 09:34:56 +00:00
use Utopia\Exception;
2024-03-08 12:57:20 +00:00
use Utopia\Http\Http;
2024-06-10 19:14:06 +00:00
use Utopia\Http\Request as UtopiaHttpRequest;
use Utopia\Http\Response as UtopiaHttpResponse;
2024-03-07 14:29:42 +00:00
use Utopia\Http\Route;
2024-04-01 11:02:47 +00:00
use Utopia\System\System;
2022-07-13 09:34:56 +00:00
class Resolvers
{
/**
2022-10-12 01:04:11 +00:00
* Create a resolver for a given API {@see Route}.
2022-07-13 09:34:56 +00:00
*
2024-06-10 16:56:29 +00:00
* @param Http $http
2022-07-13 09:34:56 +00:00
* @param ?Route $route
* @return callable
*/
2024-06-10 19:06:42 +00:00
public function api(
2024-06-10 16:56:29 +00:00
Http $http,
2022-07-13 09:34:56 +00:00
?Route $route,
2024-06-10 16:56:29 +00:00
UtopiaHttpRequest $request,
UtopiaHttpResponse $response,
Container $container,
2022-07-13 09:34:56 +00:00
): callable {
2024-06-11 22:08:40 +00:00
return fn ($type, $args, $context, $info) => new Swoole(
function (callable $resolve, callable $reject) use ($http, $route, $args, $context, $container, $info, $request, $response) {
$path = $route->getPath();
foreach ($args as $key => $value) {
2022-07-18 09:38:13 +00:00
if (\str_contains($path, '/:' . $key)) {
$path = \str_replace(':' . $key, $value, $path);
}
}
2022-10-12 07:55:04 +00:00
$request->setMethod($route->getMethod());
$request->setURI($path);
2022-07-13 09:34:56 +00:00
switch ($route->getMethod()) {
case 'GET':
2024-04-22 15:58:40 +00:00
$request->setQuery($args);
2022-07-13 09:34:56 +00:00
break;
default:
2022-10-19 23:22:46 +00:00
$request->setPayload($args);
2022-07-13 09:34:56 +00:00
break;
}
Resolvers::resolve($http, $request, $response, $container, $resolve, $reject);
2022-07-13 09:34:56 +00:00
}
);
}
2022-09-22 08:59:47 +00:00
/**
2022-10-12 01:04:11 +00:00
* Create a resolver for a document in a specified database and collection with a specific method type.
2022-09-22 08:59:47 +00:00
*
2024-06-10 16:56:29 +00:00
* @param Http $http
2022-09-22 08:59:47 +00:00
* @param string $databaseId
* @param string $collectionId
2022-10-12 01:04:11 +00:00
* @param string $methodType
2022-09-22 08:59:47 +00:00
* @return callable
*/
2024-06-10 19:06:42 +00:00
public function document(
2024-06-10 16:56:29 +00:00
Http $http,
2022-09-22 08:59:47 +00:00
string $databaseId,
string $collectionId,
string $methodType,
): callable {
2022-10-12 01:04:11 +00:00
return [self::class, 'document' . \ucfirst($methodType)](
2024-06-10 16:56:29 +00:00
$http,
2022-09-22 08:59:47 +00:00
$databaseId,
$collectionId
);
}
2022-07-13 09:34:56 +00:00
/**
* Create a resolver for getting a document in a specified database and collection.
*
2024-06-10 16:56:29 +00:00
* @param Http $http
2022-07-13 09:34:56 +00:00
* @param string $databaseId
* @param string $collectionId
2022-10-17 00:27:15 +00:00
* @param callable $url
2022-07-13 09:34:56 +00:00
* @return callable
*/
2024-06-10 19:06:42 +00:00
public function documentGet(
2024-06-10 16:56:29 +00:00
Http $http,
2022-07-13 09:34:56 +00:00
string $databaseId,
string $collectionId,
2022-10-17 00:27:15 +00:00
callable $url,
2024-06-10 16:56:29 +00:00
UtopiaHttpRequest $request,
UtopiaHttpResponse $response,
Container $container,
2022-07-13 09:34:56 +00:00
): callable {
2024-06-10 19:14:06 +00:00
$resolver = $this;
2024-06-11 22:08:40 +00:00
return fn ($type, $args, $context, $info) => new Swoole(
2024-06-10 19:06:42 +00:00
function (callable $resolve, callable $reject) use ($http, $databaseId, $collectionId, $url, $type, $args, $container, $request, $response, $resolver) {
2022-10-12 07:55:04 +00:00
$request->setMethod('GET');
2022-10-17 00:27:15 +00:00
$request->setURI($url($databaseId, $collectionId, $args));
2022-07-13 09:34:56 +00:00
Resolvers::resolve($http, $request, $response, $container, $resolve, $reject);
2022-07-13 09:34:56 +00:00
}
);
}
/**
* Create a resolver for listing documents in a specified database and collection.
*
2024-06-10 16:56:29 +00:00
* @param Http $http
2022-07-13 09:34:56 +00:00
* @param string $databaseId
* @param string $collectionId
2022-10-17 00:27:15 +00:00
* @param callable $url
2022-12-08 03:08:57 +00:00
* @param callable $params
2022-07-13 09:34:56 +00:00
* @return callable
*/
2024-06-10 19:06:42 +00:00
public function documentList(
2024-06-10 16:56:29 +00:00
Http $http,
2022-07-13 09:34:56 +00:00
string $databaseId,
string $collectionId,
2022-10-17 00:27:15 +00:00
callable $url,
callable $params,
2024-06-10 16:56:29 +00:00
UtopiaHttpRequest $request,
UtopiaHttpResponse $response,
Container $container,
2022-07-13 09:34:56 +00:00
): callable {
2024-06-10 19:14:06 +00:00
$resolver = $this;
2024-06-11 22:08:40 +00:00
return fn ($type, $args, $context, $info) => new Swoole(
2024-06-10 19:06:42 +00:00
function (callable $resolve, callable $reject) use ($http, $databaseId, $collectionId, $url, $params, $type, $args, $container, $request, $response, $resolver) {
2022-10-12 07:55:04 +00:00
$request->setMethod('GET');
2022-10-17 00:27:15 +00:00
$request->setURI($url($databaseId, $collectionId, $args));
2024-04-22 15:58:40 +00:00
$request->setQuery($params($databaseId, $collectionId, $args));
2022-07-13 09:34:56 +00:00
2022-09-22 10:48:22 +00:00
$beforeResolve = function ($payload) {
return $payload['documents'];
};
Resolvers::resolve($http, $request, $response, $container, $resolve, $reject, $beforeResolve);
2022-07-13 09:34:56 +00:00
}
);
}
2022-09-22 08:59:47 +00:00
/**
* Create a resolver for creating a document in a specified database and collection.
*
2024-06-10 16:56:29 +00:00
* @param Http $http
2022-09-22 08:59:47 +00:00
* @param string $databaseId
* @param string $collectionId
2022-10-17 00:27:15 +00:00
* @param callable $url
2022-12-08 03:08:57 +00:00
* @param callable $params
2022-09-22 08:59:47 +00:00
* @return callable
*/
2024-06-10 19:06:42 +00:00
public function documentCreate(
2024-06-10 16:56:29 +00:00
Http $http,
2022-09-22 08:59:47 +00:00
string $databaseId,
string $collectionId,
2022-10-17 00:27:15 +00:00
callable $url,
callable $params,
2024-06-10 16:56:29 +00:00
UtopiaHttpRequest $request,
UtopiaHttpResponse $response,
Container $container,
2022-09-22 08:59:47 +00:00
): callable {
2024-06-10 19:14:06 +00:00
$resolver = $this;
2024-06-11 22:08:40 +00:00
return fn ($type, $args, $context, $info) => new Swoole(
2024-06-10 19:06:42 +00:00
function (callable $resolve, callable $reject) use ($http, $databaseId, $collectionId, $url, $params, $type, $args, $container, $request, $response, $resolver) {
2022-10-12 07:55:04 +00:00
$request->setMethod('POST');
2022-10-17 00:27:15 +00:00
$request->setURI($url($databaseId, $collectionId, $args));
2022-10-19 23:22:46 +00:00
$request->setPayload($params($databaseId, $collectionId, $args));
2022-09-23 01:49:36 +00:00
Resolvers::resolve($http, $request, $response, $container, $resolve, $reject);
2022-09-23 01:49:36 +00:00
}
);
2022-09-22 08:59:47 +00:00
}
/**
* Create a resolver for updating a document in a specified database and collection.
*
2024-06-10 16:56:29 +00:00
* @param Http $http
2022-09-22 08:59:47 +00:00
* @param string $databaseId
* @param string $collectionId
2022-10-17 00:27:15 +00:00
* @param callable $url
2022-12-08 03:08:57 +00:00
* @param callable $params
2022-09-22 08:59:47 +00:00
* @return callable
*/
2024-06-10 19:06:42 +00:00
public function documentUpdate(
2024-06-10 16:56:29 +00:00
Http $http,
2022-09-22 08:59:47 +00:00
string $databaseId,
string $collectionId,
2022-10-17 00:27:15 +00:00
callable $url,
callable $params,
2024-06-10 16:56:29 +00:00
UtopiaHttpRequest $request,
UtopiaHttpResponse $response,
Container $container,
2022-07-13 09:34:56 +00:00
): callable {
2024-06-10 19:14:06 +00:00
$resolver = $this;
2024-06-11 22:08:40 +00:00
return fn ($type, $args, $context, $info) => new Swoole(
2024-06-10 19:06:42 +00:00
function (callable $resolve, callable $reject) use ($http, $databaseId, $collectionId, $url, $params, $type, $args, $container, $request, $response, $resolver) {
2022-10-12 07:55:04 +00:00
$request->setMethod('PATCH');
2022-10-17 00:27:15 +00:00
$request->setURI($url($databaseId, $collectionId, $args));
2022-10-19 23:22:46 +00:00
$request->setPayload($params($databaseId, $collectionId, $args));
2022-07-13 09:34:56 +00:00
Resolvers::resolve($http, $request, $response, $container, $resolve, $reject);
2022-07-13 09:34:56 +00:00
}
);
}
/**
* Create a resolver for deleting a document in a specified database and collection.
*
2024-06-10 16:56:29 +00:00
* @param Http $http
2022-07-13 09:34:56 +00:00
* @param string $databaseId
* @param string $collectionId
2022-10-17 00:27:15 +00:00
* @param callable $url
2022-07-13 09:34:56 +00:00
* @return callable
*/
2024-06-10 19:06:42 +00:00
public function documentDelete(
2024-06-10 16:56:29 +00:00
Http $http,
2022-07-13 09:34:56 +00:00
string $databaseId,
string $collectionId,
2022-10-17 00:27:15 +00:00
callable $url,
2024-06-10 16:56:29 +00:00
UtopiaHttpRequest $request,
UtopiaHttpResponse $response,
Container $container,
2022-07-13 09:34:56 +00:00
): callable {
2024-06-10 19:14:06 +00:00
$resolver = $this;
2024-06-11 22:08:40 +00:00
return fn ($type, $args, $context, $info) => new Swoole(
2024-06-10 19:06:42 +00:00
function (callable $resolve, callable $reject) use ($http, $databaseId, $collectionId, $url, $type, $args, $container, $request, $response, $resolver) {
2022-10-12 07:55:04 +00:00
$request->setMethod('DELETE');
2022-10-17 00:27:15 +00:00
$request->setURI($url($databaseId, $collectionId, $args));
2022-07-13 09:34:56 +00:00
Resolvers::resolve($http, $request, $response, $container, $resolve, $reject);
2022-07-13 09:34:56 +00:00
}
);
}
/**
2024-06-10 16:56:29 +00:00
* @param Http $http
2022-07-13 09:34:56 +00:00
* @param Request $request
* @param Response $response
* @param callable $resolve
* @param callable $reject
2022-10-12 01:04:11 +00:00
* @param callable|null $beforeResolve
* @param callable|null $beforeReject
2022-07-13 09:34:56 +00:00
* @return void
* @throws Exception
*/
private static function resolve(
2024-06-10 16:56:29 +00:00
Http $http,
2022-07-13 09:34:56 +00:00
Request $request,
Response $response,
2024-06-10 16:56:29 +00:00
Container $context,
2022-07-13 09:34:56 +00:00
callable $resolve,
callable $reject,
2022-09-22 10:48:22 +00:00
?callable $beforeResolve = null,
?callable $beforeReject = null,
2022-07-13 09:34:56 +00:00
): void {
// Drop json content type so post args are used directly
if (\str_starts_with($request->getHeader('content-type'), 'application/json')) {
2022-10-12 07:55:04 +00:00
$request->removeHeader('content-type');
2022-07-13 09:34:56 +00:00
}
$response->setContentType(Response::CONTENT_TYPE_NULL);
try {
$context
->refresh('cache')
->refresh('dbForProject')
->refresh('dbForConsole')
->refresh('getProjectDb');
2022-07-13 09:34:56 +00:00
2024-06-20 20:51:30 +00:00
$http->run(clone $context);
2022-07-13 09:34:56 +00:00
} catch (\Throwable $e) {
2022-09-22 10:48:22 +00:00
if ($beforeReject) {
$e = $beforeReject($e);
}
2022-07-13 09:34:56 +00:00
$reject($e);
return;
}
$payload = $response->getPayload();
if ($response->getStatusCode() < 200 || $response->getStatusCode() >= 400) {
2022-09-22 10:48:22 +00:00
if ($beforeReject) {
$payload = $beforeReject($payload);
}
2024-06-10 16:56:29 +00:00
$reject(
new GQLException(
message: $payload['message'],
code: $response->getStatusCode()
)
);
2022-07-13 09:34:56 +00:00
return;
}
2022-12-27 19:15:22 +00:00
$payload = self::escapePayload($payload, 1);
if ($beforeResolve) {
$payload = $beforeResolve($payload);
}
$resolve($payload);
}
2022-12-27 19:33:16 +00:00
private static function escapePayload(array $payload, int $depth)
{
2024-04-01 11:02:47 +00:00
if ($depth > System::getEnv('_APP_GRAPHQL_MAX_DEPTH', 3)) {
2022-12-27 19:15:22 +00:00
return;
}
2022-09-21 06:24:47 +00:00
foreach ($payload as $key => $value) {
if (\str_starts_with($key, '$')) {
2022-12-08 03:08:57 +00:00
$escapedKey = \str_replace('$', '_', $key);
2022-09-21 06:24:47 +00:00
$payload[$escapedKey] = $value;
unset($payload[$key]);
}
2022-07-13 09:34:56 +00:00
2022-12-27 19:33:16 +00:00
if (\is_array($value)) {
2022-12-27 19:15:22 +00:00
$payload[$key] = self::escapePayload($value, $depth + 1);
}
2022-09-22 10:48:22 +00:00
}
2022-12-27 19:15:22 +00:00
return $payload;
2022-07-13 09:34:56 +00:00
}
}