2020-01-04 10:16:26 +13:00
< ? php
2022-04-26 19:49:36 +12:00
use Appwrite\Extend\Exception ;
2022-06-22 16:44:58 +12:00
use Appwrite\GraphQL\CoroutinePromiseAdapter ;
2021-02-26 09:43:21 +13:00
use Appwrite\Utopia\Response ;
2022-06-22 16:44:58 +12:00
use GraphQL\Error\DebugFlag ;
2022-04-06 01:48:51 +12:00
use GraphQL\GraphQL ;
use GraphQL\Type ;
2022-04-20 22:30:48 +12:00
use GraphQL\Validator\Rules\DisableIntrospection ;
2022-04-07 18:40:28 +12:00
use GraphQL\Validator\Rules\QueryComplexity ;
use GraphQL\Validator\Rules\QueryDepth ;
use Swoole\Coroutine\WaitGroup ;
2020-06-29 05:31:21 +12:00
use Utopia\App ;
2022-04-07 18:40:28 +12:00
use Utopia\Validator\JSON ;
use Utopia\Validator\Text ;
2020-01-04 10:16:26 +13:00
2022-04-11 23:08:57 +12:00
App :: get ( '/v1/graphql' )
2021-02-26 09:43:21 +13:00
-> desc ( 'GraphQL Endpoint' )
2022-07-06 15:27:00 +12:00
-> groups ([ 'grapgql' ])
2021-03-05 07:40:52 +13:00
-> label ( 'scope' , 'graphql' )
2022-04-11 23:08:57 +12:00
-> label ( 'sdk.auth' , [ APP_AUTH_TYPE_KEY , APP_AUTH_TYPE_SESSION , APP_AUTH_TYPE_JWT ])
2022-04-07 18:39:33 +12:00
-> label ( 'sdk.namespace' , 'graphql' )
2022-04-11 23:08:57 +12:00
-> label ( 'sdk.method' , 'query' )
-> label ( 'sdk.description' , '/docs/references/graphql/query.md' )
2022-04-07 18:39:33 +12:00
-> label ( 'sdk.response.code' , Response :: STATUS_CODE_OK )
-> label ( 'sdk.response.type' , Response :: CONTENT_TYPE_JSON )
-> label ( 'sdk.response.model' , Response :: MODEL_ANY )
-> label ( 'abuse-limit' , 60 )
2022-04-07 18:39:42 +12:00
-> label ( 'abuse-time' , 60 )
2022-07-12 17:06:11 +12:00
-> 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 )
2021-02-26 09:43:21 +13:00
-> inject ( 'request' )
-> inject ( 'response' )
2021-03-11 04:22:19 +13:00
-> inject ( 'utopia' )
-> inject ( 'register' )
2022-04-06 01:48:51 +12:00
-> inject ( 'dbForProject' )
-> inject ( 'promiseAdapter' )
2022-04-22 17:47:27 +12:00
-> inject ( 'gqlSchema' )
2022-04-11 23:08:57 +12:00
-> action ( Closure :: fromCallable ( 'graphqlRequest' ));
App :: post ( '/v1/graphql' )
-> desc ( 'GraphQL Endpoint' )
2022-07-06 15:27:00 +12:00
-> groups ([ 'grapgql' ])
2022-04-11 23:08:57 +12:00
-> label ( 'scope' , 'graphql' )
-> label ( 'sdk.auth' , [ APP_AUTH_TYPE_KEY , APP_AUTH_TYPE_SESSION , APP_AUTH_TYPE_JWT ])
-> label ( 'sdk.namespace' , 'graphql' )
-> label ( 'sdk.method' , 'mutate' )
-> label ( 'sdk.description' , '/docs/references/graphql/mutate.md' )
-> label ( 'sdk.response.code' , Response :: STATUS_CODE_OK )
-> label ( 'sdk.response.type' , Response :: CONTENT_TYPE_JSON )
-> label ( 'sdk.response.model' , Response :: MODEL_ANY )
-> label ( 'abuse-limit' , 60 )
-> label ( 'abuse-time' , 60 )
2022-07-12 17:06:11 +12:00
-> 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 )
2022-04-11 23:08:57 +12:00
-> inject ( 'request' )
-> inject ( 'response' )
-> inject ( 'promiseAdapter' )
2022-04-22 17:47:27 +12:00
-> inject ( 'gqlSchema' )
2022-04-11 23:08:57 +12:00
-> action ( Closure :: fromCallable ( 'graphqlRequest' ));
2021-11-25 21:04:39 +13:00
2022-07-07 18:13:12 +12:00
/**
* @ throws Exception
* @ throws \Exception
*/
function graphqlRequest (
string $query ,
? string $operationName ,
? array $variables ,
2022-07-07 19:39:42 +12:00
? string $operations ,
? string $map ,
2022-07-07 18:13:12 +12:00
Appwrite\Utopia\Request $request ,
Appwrite\Utopia\Response $response ,
CoroutinePromiseAdapter $promiseAdapter ,
Type\Schema $gqlSchema
) : void {
$contentType = $request -> getHeader ( 'content-type' );
2022-07-07 19:39:42 +12:00
2022-07-07 18:13:12 +12:00
if ( $contentType === 'application/graphql' ) {
$query = $request -> getSwoole () -> rawContent ();
}
2022-07-07 19:39:42 +12:00
if ( \str_starts_with ( $contentType , 'multipart/form-data' )) {
$operations = \json_decode ( $operations , true );
2022-07-12 17:06:11 +12:00
$map = \json_decode ( $map , true );
2022-07-07 19:39:42 +12:00
foreach ( $map as $fileKey => $locations ) {
foreach ( $locations as $location ) {
$items = & $operations ;
foreach ( explode ( '.' , $location ) as $key ) {
if ( ! isset ( $items [ $key ]) || ! is_array ( $items [ $key ])) {
$items [ $key ] = [];
}
$items = & $items [ $key ];
}
$items = $request -> getFiles ( $fileKey );
}
}
$query = $operations [ 'query' ];
$variables = $operations [ 'variables' ];
}
2022-04-11 23:08:57 +12:00
if ( empty ( $query )) {
2022-05-02 20:21:40 +12:00
throw new Exception ( 'No query supplied.' , 400 , Exception :: GRAPHQL_NO_QUERY );
2022-04-11 23:08:57 +12:00
}
2021-03-11 02:51:03 +13:00
2022-04-26 19:49:36 +12:00
$maxComplexity = App :: getEnv ( '_APP_GRAPHQL_MAX_QUERY_COMPLEXITY' , 200 );
$maxDepth = App :: getEnv ( '_APP_GRAPHQL_MAX_QUERY_DEPTH' , 3 );
$validations = GraphQL :: getStandardValidationRules ();
$validations [] = new QueryComplexity ( $maxComplexity );
$validations [] = new QueryDepth ( $maxDepth );
2022-06-22 16:44:58 +12:00
if ( App :: isProduction ()) {
$validations [] = new DisableIntrospection ();
$debugFlags = DebugFlag :: INCLUDE_DEBUG_MESSAGE | DebugFlag :: INCLUDE_TRACE ;
} else {
$debugFlags = DebugFlag :: NONE ;
}
2022-04-20 22:30:48 +12:00
2022-04-11 23:08:57 +12:00
$promise = GraphQL :: promiseToExecute (
$promiseAdapter ,
2022-04-22 17:47:27 +12:00
$gqlSchema ,
2022-04-11 23:08:57 +12:00
$query ,
variableValues : $variables ,
operationName : $operationName ,
validationRules : $validations
);
2022-04-07 18:40:28 +12:00
2022-05-02 20:21:40 +12:00
$output = [];
2022-04-11 23:08:57 +12:00
$wg = new WaitGroup ();
$wg -> add ();
2022-04-26 19:49:36 +12:00
$promise -> then (
2022-06-22 16:44:58 +12:00
function ( $result ) use ( $response , & $output , $wg , $debugFlags ) {
$output = $result -> toArray ( $debugFlags );
2022-04-26 19:49:36 +12:00
$wg -> done ();
},
2022-05-02 20:21:40 +12:00
function ( $error ) use ( $response , & $output , $wg ) {
$output = [ 'errors' => [ $error ]];
2022-04-11 23:08:57 +12:00
$wg -> done ();
}
2022-04-26 19:49:36 +12:00
);
2022-04-11 23:08:57 +12:00
$wg -> wait ();
2022-05-02 20:21:40 +12:00
$response -> json ( $output );
2022-04-11 23:08:57 +12:00
}