Update dynamic response method
This commit is contained in:
parent
22853b7b59
commit
dcf1684c8c
4 changed files with 79 additions and 58 deletions
|
@ -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
30
composer.lock
generated
|
@ -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",
|
||||||
|
|
|
@ -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
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue