1
0
Fork 0
mirror of synced 2024-09-28 15:31:43 +12:00

Update dynamic response method

This commit is contained in:
Khushboo Verma 2024-08-07 18:45:53 +05:30
parent 22853b7b59
commit dcf1684c8c
4 changed files with 79 additions and 58 deletions

View file

@ -18,7 +18,6 @@ use Appwrite\Utopia\Database\Validator\CustomId;
use Appwrite\Utopia\Database\Validator\Queries\Deployments; use Appwrite\Utopia\Database\Validator\Queries\Deployments;
use Appwrite\Utopia\Database\Validator\Queries\Executions; use Appwrite\Utopia\Database\Validator\Queries\Executions;
use Appwrite\Utopia\Database\Validator\Queries\Functions; use Appwrite\Utopia\Database\Validator\Queries\Functions;
use Appwrite\Utopia\Fetch\BodyMultipart;
use Appwrite\Utopia\Response; use Appwrite\Utopia\Response;
use Appwrite\Utopia\Response\Model\Rule; use Appwrite\Utopia\Response\Model\Rule;
use Executor\Executor; use Executor\Executor;
@ -1641,20 +1640,6 @@ App::post('/v1/functions/:functionId/executions')
} }
} }
$datetimeParams = ['scheduledAt'];
foreach ($datetimeParams as $datetimeParam) {
if (!empty($$datetimeParam)) {
$$datetimeParam = new DateTime($$datetimeParam);
}
}
$validator = new DatetimeValidator(requireDateInFuture: true);
foreach ($datetimeParams as $datetimeParam) {
if (!empty($$datetimeParam) && !$validator->isValid($$datetimeParam)) {
throw new Exception($validator->getDescription(), 400);
}
}
// 'headers' validator // 'headers' validator
$validator = new Headers(); $validator = new Headers();
if (!$validator->isValid($headers)) { if (!$validator->isValid($headers)) {
@ -1967,9 +1952,9 @@ App::post('/v1/functions/:functionId/executions')
$execution->setAttribute('responseBody', $executionResponse['body'] ?? ''); $execution->setAttribute('responseBody', $executionResponse['body'] ?? '');
$execution->setAttribute('responseHeaders', $headers); $execution->setAttribute('responseHeaders', $headers);
$acceptTypes = \explode(', ', $request->getHeader('accept', 'application/json'));
$isJson = false; $isJson = false;
$acceptTypes = \explode(', ', $request->getHeader('accept', 'application/json'));
foreach ($acceptTypes as $acceptType) { foreach ($acceptTypes as $acceptType) {
if (\str_starts_with($acceptType, 'application/json') || \str_starts_with($acceptType, 'application/*')) { if (\str_starts_with($acceptType, 'application/json') || \str_starts_with($acceptType, 'application/*')) {
$isJson = true; $isJson = true;
@ -1977,28 +1962,10 @@ App::post('/v1/functions/:functionId/executions')
} }
} }
if ($isJson) { $response
$executionString = \json_encode($execution, JSON_UNESCAPED_UNICODE); ->setStatusCode(Response::STATUS_CODE_CREATED)
if (!$executionString) { ->setContentType($isJson ? Response::CONTENT_TYPE_JSON : Response::CONTENT_TYPE_MULTIPART)
throw new Exception('Execution resulted in binary response, but JSON response does not allow binaries. Use "Accept: multipart/form-data" header to support binaries.', 400); ->dynamic($execution, Response::MODEL_EXECUTION);
}
$response
->setStatusCode(Response::STATUS_CODE_OK)
->addHeader('content-type', 'application/json')
->send($executionString);
} else {
// Multipart form data response
$multipart = new BodyMultipart();
foreach ($execution as $key => $value) {
$multipart->setPart($key, $value);
}
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->addHeader('content-type', $multipart->exportHeader())
->send($multipart->exportBody());
}
}); });
App::get('/v1/functions/:functionId/executions') App::get('/v1/functions/:functionId/executions')

30
composer.lock generated
View file

@ -2990,16 +2990,16 @@
"packages-dev": [ "packages-dev": [
{ {
"name": "appwrite/sdk-generator", "name": "appwrite/sdk-generator",
"version": "0.39.4", "version": "0.39.5",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/appwrite/sdk-generator.git", "url": "https://github.com/appwrite/sdk-generator.git",
"reference": "501b92d73ae55e0f880ed00f57bc64a54d0ce137" "reference": "40d0f66f2f85be74049ad710b46203aa151f53fd"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/501b92d73ae55e0f880ed00f57bc64a54d0ce137", "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/40d0f66f2f85be74049ad710b46203aa151f53fd",
"reference": "501b92d73ae55e0f880ed00f57bc64a54d0ce137", "reference": "40d0f66f2f85be74049ad710b46203aa151f53fd",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -3035,9 +3035,9 @@
"description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms", "description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms",
"support": { "support": {
"issues": "https://github.com/appwrite/sdk-generator/issues", "issues": "https://github.com/appwrite/sdk-generator/issues",
"source": "https://github.com/appwrite/sdk-generator/tree/0.39.4" "source": "https://github.com/appwrite/sdk-generator/tree/0.39.5"
}, },
"time": "2024-07-26T22:34:10+00:00" "time": "2024-08-06T00:51:40+00:00"
}, },
{ {
"name": "doctrine/deprecations", "name": "doctrine/deprecations",
@ -3158,16 +3158,16 @@
}, },
{ {
"name": "laravel/pint", "name": "laravel/pint",
"version": "v1.17.1", "version": "v1.17.2",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/laravel/pint.git", "url": "https://github.com/laravel/pint.git",
"reference": "b5b6f716db298671c1dfea5b1082ec2c0ae7064f" "reference": "e8a88130a25e3f9d4d5785e6a1afca98268ab110"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/laravel/pint/zipball/b5b6f716db298671c1dfea5b1082ec2c0ae7064f", "url": "https://api.github.com/repos/laravel/pint/zipball/e8a88130a25e3f9d4d5785e6a1afca98268ab110",
"reference": "b5b6f716db298671c1dfea5b1082ec2c0ae7064f", "reference": "e8a88130a25e3f9d4d5785e6a1afca98268ab110",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -3178,13 +3178,13 @@
"php": "^8.1.0" "php": "^8.1.0"
}, },
"require-dev": { "require-dev": {
"friendsofphp/php-cs-fixer": "^3.59.3", "friendsofphp/php-cs-fixer": "^3.61.1",
"illuminate/view": "^10.48.12", "illuminate/view": "^10.48.18",
"larastan/larastan": "^2.9.7", "larastan/larastan": "^2.9.8",
"laravel-zero/framework": "^10.4.0", "laravel-zero/framework": "^10.4.0",
"mockery/mockery": "^1.6.12", "mockery/mockery": "^1.6.12",
"nunomaduro/termwind": "^1.15.1", "nunomaduro/termwind": "^1.15.1",
"pestphp/pest": "^2.34.8" "pestphp/pest": "^2.35.0"
}, },
"bin": [ "bin": [
"builds/pint" "builds/pint"
@ -3220,7 +3220,7 @@
"issues": "https://github.com/laravel/pint/issues", "issues": "https://github.com/laravel/pint/issues",
"source": "https://github.com/laravel/pint" "source": "https://github.com/laravel/pint"
}, },
"time": "2024-08-01T09:06:33+00:00" "time": "2024-08-06T15:11:54+00:00"
}, },
{ {
"name": "matthiasmullie/minify", "name": "matthiasmullie/minify",

View file

@ -2,6 +2,7 @@
namespace Appwrite\Utopia; namespace Appwrite\Utopia;
use Appwrite\Utopia\Fetch\BodyMultipart;
use Appwrite\Utopia\Response\Filter; use Appwrite\Utopia\Response\Filter;
use Appwrite\Utopia\Response\Model; use Appwrite\Utopia\Response\Model;
use Appwrite\Utopia\Response\Model\Account; use Appwrite\Utopia\Response\Model\Account;
@ -107,6 +108,7 @@ use Appwrite\Utopia\Response\Model\Variable;
use Appwrite\Utopia\Response\Model\VcsContent; use Appwrite\Utopia\Response\Model\VcsContent;
use Appwrite\Utopia\Response\Model\Webhook; use Appwrite\Utopia\Response\Model\Webhook;
use Exception; use Exception;
use JsonException;
use Swoole\Http\Response as SwooleHTTPResponse; use Swoole\Http\Response as SwooleHTTPResponse;
// Keep last // Keep last
use Utopia\Database\Document; use Utopia\Database\Document;
@ -486,6 +488,7 @@ class Response extends SwooleResponse
*/ */
public const CONTENT_TYPE_YAML = 'application/x-yaml'; public const CONTENT_TYPE_YAML = 'application/x-yaml';
public const CONTENT_TYPE_NULL = 'null'; public const CONTENT_TYPE_NULL = 'null';
public const CONTENT_TYPE_MULTIPART = 'multipart/form-data';
/** /**
* List of defined output objects * List of defined output objects
@ -556,7 +559,11 @@ class Response extends SwooleResponse
switch ($this->getContentType()) { switch ($this->getContentType()) {
case self::CONTENT_TYPE_JSON: case self::CONTENT_TYPE_JSON:
$this->json(!empty($output) ? $output : new \stdClass()); try {
$this->json(!empty($output) ? $output : new \stdClass());
} catch (JsonException $e) {
throw new Exception('Failed to parse binary response: ' . $e->getMessage(), 400);
}
break; break;
case self::CONTENT_TYPE_YAML: case self::CONTENT_TYPE_YAML:
@ -566,6 +573,10 @@ class Response extends SwooleResponse
case self::CONTENT_TYPE_NULL: case self::CONTENT_TYPE_NULL:
break; break;
case self::CONTENT_TYPE_MULTIPART:
$this->multipart(!empty($output) ? $output : new \stdClass());
break;
default: default:
if ($model === self::MODEL_NONE) { if ($model === self::MODEL_NONE) {
$this->noContent(); $this->noContent();
@ -697,6 +708,49 @@ class Response extends SwooleResponse
->send(\yaml_emit($data, YAML_UTF8_ENCODING)); ->send(\yaml_emit($data, YAML_UTF8_ENCODING));
} }
/**
* Multipart
*
* This helper is for sending multipart/form-data HTTP response.
* It sets relevant content type header ('multipart/form-data') and convert a PHP array ($data) to valid Multipart using BodyMultipart
*
* @param array $data
*
* @return void
*/
public function multipart(array $data): void
{
$multipart = new BodyMultipart();
foreach ($data as $key => $value) {
$multipart->setPart($key, $value);
}
$this
->send($multipart->exportBody());
}
/**
* JSON
*
* This helper is for sending JSON HTTP response.
* It sets relevant content type header ('application/json') and convert a PHP array ($data) to valid JSON using native json_encode
*
* @see http://en.wikipedia.org/wiki/JSON
*
* @param mixed $data
* @return void
*/
public function json($data): void
{
if (!is_array($data) && !$data instanceof \stdClass) {
throw new \Exception('Invalid JSON input var');
}
$this
->setContentType(Response::CONTENT_TYPE_JSON, self::CHARSET_UTF8)
->send(\json_encode($data, JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR));
}
/** /**
* @return array * @return array
*/ */

View file

@ -1403,8 +1403,8 @@ class FunctionsCustomServerTest extends Scope
'body' => null, 'body' => null,
]); ]);
$this->assertEquals(500, $execution['headers']['status-code']); $this->assertEquals(400, $execution['headers']['status-code']);
$this->assertStringContainsString('Execution resulted in binary response, but JSON response does not allow binaries.', $execution['body']['type']); $this->assertStringContainsString('Failed to parse binary response', $execution['body']['message']);
// Cleanup : Delete function // Cleanup : Delete function
$response = $this->client->call(Client::METHOD_DELETE, '/functions/' . $functionId, [ $response = $this->client->call(Client::METHOD_DELETE, '/functions/' . $functionId, [
@ -1499,9 +1499,9 @@ class FunctionsCustomServerTest extends Scope
$executionBody = json_decode($execution['body'], true); $executionBody = json_decode($execution['body'], true);
$this->assertEquals(200, $execution['headers']['status-code']); $this->assertEquals(201, $execution['headers']['status-code']);
$this->assertEquals(\md5($bytes), $executionBody['responseBody']); $this->assertEquals(\md5($bytes), $executionBody['responseBody']);
$this->assertEquals($execution['headers']['content-type'], 'application/json'); $this->assertStringStartsWith('application/json', $execution['headers']['content-type']);
/** /**
* Test for FAILURE * Test for FAILURE