1
0
Fork 0
mirror of synced 2024-06-28 19:20:25 +12:00

Fix custom entity id properties

This commit is contained in:
Jake Barnby 2022-07-07 19:39:42 +12:00
parent 8762bcba90
commit b5e8273839
4 changed files with 129 additions and 94 deletions

View file

@ -55,68 +55,14 @@ App::post('/v1/graphql')
->param('query', '', new Text(1024), 'The query to execute. Max 1024 chars.', true)
->param('operationName', null, new Text(256), 'Name of the operation to execute', true)
->param('variables', [], new JSON(), 'Variables to use in the operation', true)
->param('operations', '', new Text(1024), 'Variables to use in the operation', true)
->param('map', '', new Text(1024), 'Variables to use in the operation', true)
->inject('request')
->inject('response')
->inject('promiseAdapter')
->inject('gqlSchema')
->action(Closure::fromCallable('graphqlRequest'));
App::post('/v1/graphql')
->desc('GraphQL Endpoint')
->groups(['grapgql'])
->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', 'upload')
->label('sdk.description', '/docs/references/graphql/upload.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)
->param('operations', '', new Text(1024), 'Query and variables for operation', true)
->param('map', '', new Text(1024), 'Map of form data keys to file replacement dot paths within the operations JSON. For example: "variables.code"', true)
->inject('request')
->inject('response')
->inject('promiseAdapter')
->inject('gqlSchema')
->action(Closure::fromCallable('graphqlUpload'));
/**
* @throws Exception
*/
function graphqlUpload(
?string $operations,
?string $map,
Appwrite\Utopia\Request $request,
Appwrite\Utopia\Response $response,
CoroutinePromiseAdapter $promiseAdapter,
Type\Schema $gqlSchema
): void {
$contentType = $request->getHeader('content-type');
if (!\str_starts_with($contentType, 'multipart/form-data')) {
throw new Exception('Invalid content type', 400);
}
$map = \json_decode($map, true);
$operations = \json_decode($operations, 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])) {
$items[$key] = [];
}
$items = &$items[$key];
}
$items = $request->getFiles($fileKey);
}
}
$query = $operations['query'];
$variables = $operations['variables'];
graphqlRequest($query, null, $variables, $request, $response, $promiseAdapter, $gqlSchema);
}
/**
* @throws Exception
* @throws \Exception
@ -125,15 +71,38 @@ function graphqlRequest(
string $query,
?string $operationName,
?array $variables,
?string $operations,
?string $map,
Appwrite\Utopia\Request $request,
Appwrite\Utopia\Response $response,
CoroutinePromiseAdapter $promiseAdapter,
Type\Schema $gqlSchema
): void {
$contentType = $request->getHeader('content-type');
if ($contentType === 'application/graphql') {
$query = $request->getSwoole()->rawContent();
}
if (\str_starts_with($contentType, 'multipart/form-data')) {
$map = \json_decode($map, true);
$operations = \json_decode($operations, 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])) {
$items[$key] = [];
}
$items = &$items[$key];
}
$items = $request->getFiles($fileKey);
}
}
$query = $operations['query'];
$variables = $operations['variables'];
}
if (empty($query)) {
throw new Exception('No query supplied.', 400, Exception::GRAPHQL_NO_QUERY);
}

View file

@ -16,7 +16,6 @@ use Utopia\CLI\Console;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Validator\Authorization;
use Utopia\Registry\Registry;
use Utopia\Route;
use Utopia\Validator;
@ -497,7 +496,7 @@ class Builder
foreach ($collections as $collectionId => $attributes) {
$objectType = new ObjectType([
'name' => $collectionId,
'fields' => $attributes
'fields' => \array_merge(["_id" => ['type' => Type::string()]], $attributes),
]);
$attributes = \array_merge(
@ -695,7 +694,6 @@ class Builder
}
$gqlResponse = $response;
$request = new Request($swoole);
$apiResponse = new Response($response->getSwoole());
$apiResponse->setContentType(Response::CONTENT_TYPE_NULL);
@ -710,31 +708,42 @@ class Builder
$utopia->execute($route, $request);
} catch (\Throwable $e) {
$gqlResponse->setStatusCode($apiResponse->getStatusCode());
self::reassign($gqlResponse, $apiResponse);
$reject($e);
return;
}
self::reassign($gqlResponse, $apiResponse);
$result = $apiResponse->getPayload();
$gqlResponse->setContentType($apiResponse->getContentType());
$gqlResponse->setStatusCode($apiResponse->getStatusCode());
if ($result['$id']) {
$result['_id'] = $result['$id'];
}
if ($apiResponse->getStatusCode() < 200 || $apiResponse->getStatusCode() >= 400) {
$reject(new GQLException($result['message'], $apiResponse->getStatusCode()));
return;
}
// Add headers and cookies from inner to outer response
// TODO: Add setters to response to allow setting entire array at once
$resolve($result);
}
/**
* @param Response $gqlResponse
* @param Response $apiResponse
* @return void
* @throws \Utopia\Exception
*/
private static function reassign(Response $gqlResponse, Response $apiResponse): void
{
$gqlResponse->setContentType($apiResponse->getContentType());
$gqlResponse->setStatusCode($apiResponse->getStatusCode());
foreach ($apiResponse->getHeaders() as $key => $value) {
$gqlResponse->addHeader($key, $value);
}
foreach ($apiResponse->getCookies() as $name => $cookie) {
$gqlResponse->addCookie($name, $cookie['value'], $cookie['expire'], $cookie['path'], $cookie['domain'], $cookie['secure'], $cookie['httponly']);
}
$resolve($result);
}
}

View file

@ -35,12 +35,18 @@ trait GraphQLBase
public static string $DELETE_INDEX = 'delete_index';
// Documents
public static string $CREATE_DOCUMENT = 'create_document_rest';
public static string $CREATE_CUSTOM_ENTITY = 'create_document_hooks';
public static string $GET_DOCUMENTS = 'list_documents';
public static string $GET_DOCUMENT = 'get_document';
public static string $UPDATE_DOCUMENT = 'update_document';
public static string $DELETE_DOCUMENT = 'delete_document';
// Custom Entities
public static string $CREATE_CUSTOM_ENTITY = 'create_custom_entity';
public static string $GET_CUSTOM_ENTITIES = 'get_custom_entities';
public static string $GET_CUSTOM_ENTITY = 'get_custom_entity';
public static string $UPDATE_CUSTOM_ENTITY = 'update_custom_entity';
public static string $DELETE_CUSTOM_ENTITY = 'delete_custom_entity';
// Localization
public static string $GET_LOCALE = 'get_locale';
public static string $LIST_COUNTRIES = 'list_countries';
@ -414,6 +420,7 @@ trait GraphQLBase
case self::$CREATE_CUSTOM_ENTITY:
return 'mutation createActor($name: String!, $age: Int!, $alive: Boolean!, $salary: Float, $email: String!, $role: String!, $ip: String, $url: String){
actorsCreate(name: $name, age: $age, alive: $alive, salary: $salary, email: $email, role: $role, ip: $ip, url: $url) {
_id
name
age
alive
@ -422,6 +429,52 @@ trait GraphQLBase
role
}
}';
case self::$GET_CUSTOM_ENTITIES:
return 'query getCustomEntities($name: String!){
actorsList(name: $name) {
total
actors {
name
age
alive
salary
email
role
ip
url
}
}
}';
case self::$GET_CUSTOM_ENTITY:
return 'query getCustomEntity($id: String!){
actorsGet(id: $id) {
name
age
alive
salary
email
role
ip
url
}
}';
case self::$UPDATE_CUSTOM_ENTITY:
return 'mutation updateCustomEntity($id: String!, $name: String!, $age: Int!, $alive: Boolean!, $salary: Float, $email: String!, $role: String!, $ip: String, $url: String){
actorsUpdate(id: $id, name: $name, age: $age, alive: $alive, salary: $salary, email: $email, role: $role, ip: $ip, url: $url) {
name
age
alive
salary
email
role
ip
url
}
}';
case self::$DELETE_CUSTOM_ENTITY:
return 'mutation deleteCustomEntity($id: String!){
actorsDelete(id: $id)
}';
case self::$UPDATE_DOCUMENT:
return 'mutation updateDocument($databaseId: String!, $collectionId: String!, $documentId: String!, $data: Json!, $read: [String!], $write: [String!]){
databasesUpdateDocument(databaseId: $databaseId, collectionId: $collectionId, documentId: $documentId, data: $data, read: $read, write: $write) {

View file

@ -426,7 +426,7 @@ class GraphQLDatabaseServerTest extends Scope
* @depends testCreateEnumAttribute
* @throws Exception
*/
public function testCreateCustomEntity(): void
public function testCreateCustomEntity(): array
{
$projectId = $this->getProject()['$id'];
$query = $this->getQuery(self::$CREATE_CUSTOM_ENTITY);
@ -447,9 +447,13 @@ class GraphQLDatabaseServerTest extends Scope
'x-appwrite-project' => $projectId,
], $this->getHeaders()), $gqlPayload);
$this->assertArrayNotHasKey('errors', $actor['body']);
$this->assertIsArray($actor['body']['data']);
$this->assertIsArray($actor['body']['data']['actorsCreate']);
$actor = $actor['body']['data']['actorsCreate'];
$this->assertIsArray($actor);
return $actor;
}
public function testGetDatabases(): void
@ -706,30 +710,30 @@ class GraphQLDatabaseServerTest extends Scope
$this->assertIsArray($document['body']['data']['databasesGetDocument']);
}
// /**
// * @depends testCreateCustomEntity
// * @throws Exception
// */
// public function testGetCustomEntity($data)
// {
// $projectId = $this->getProject()['$id'];
// $query = $this->getQuery(self::$GET_CUSTOM_ENTITY);
// $gqlPayload = [
// 'query' => $query,
// 'variables' => [
// 'id' => $data['entity']['_id'],
// ]
// ];
//
// $entity = $this->client->call(Client::METHOD_POST, '/graphql', array_merge([
// 'content-type' => 'application/json',
// 'x-appwrite-project' => $projectId,
// ], $this->getHeaders()), $gqlPayload);
//
// $this->assertArrayNotHasKey('errors', $entity['body']);
// $this->assertIsArray($entity['body']['data']);
// $this->assertIsArray($entity['body']['data']['actorGet']);
// }
/**
* @depends testCreateCustomEntity
* @throws Exception
*/
public function testGetCustomEntity($data)
{
$projectId = $this->getProject()['$id'];
$query = $this->getQuery(self::$GET_CUSTOM_ENTITY);
$gqlPayload = [
'query' => $query,
'variables' => [
'id' => $data['_id'],
]
];
$entity = $this->client->call(Client::METHOD_POST, '/graphql', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $projectId,
], $this->getHeaders()), $gqlPayload);
$this->assertArrayNotHasKey('errors', $entity['body']);
$this->assertIsArray($entity['body']['data']);
$this->assertIsArray($entity['body']['data']['actorsGet']);
}
/**
* @depends testCreateDatabase