mirror of
https://github.com/appwrite/appwrite.git
synced 2026-05-26 13:51:13 +00:00
Merge pull request #8323 from appwrite/feat-opr-v4-support
OPR v4 support
This commit is contained in:
@@ -405,9 +405,7 @@ class Functions extends Action
|
||||
'search' => implode(' ', [$functionId, $executionId]),
|
||||
]);
|
||||
|
||||
if ($function->getAttribute('logging')) {
|
||||
$execution = $dbForProject->createDocument('executions', $execution);
|
||||
}
|
||||
$execution = $dbForProject->createDocument('executions', $execution);
|
||||
|
||||
// TODO: @Meldiron Trigger executions.create event here
|
||||
|
||||
@@ -419,9 +417,7 @@ class Functions extends Action
|
||||
if ($execution->getAttribute('status') !== 'processing') {
|
||||
$execution->setAttribute('status', 'processing');
|
||||
|
||||
if ($function->getAttribute('logging')) {
|
||||
$execution = $dbForProject->updateDocument('executions', $executionId, $execution);
|
||||
}
|
||||
$execution = $dbForProject->updateDocument('executions', $executionId, $execution);
|
||||
}
|
||||
|
||||
$durationStart = \microtime(true);
|
||||
@@ -490,7 +486,8 @@ class Functions extends Action
|
||||
path: $path,
|
||||
method: $method,
|
||||
headers: $headers,
|
||||
runtimeEntrypoint: $command
|
||||
runtimeEntrypoint: $command,
|
||||
logging: $function->getAttribute('logging', true),
|
||||
);
|
||||
|
||||
$status = $executionResponse['statusCode'] >= 400 ? 'failed' : 'completed';
|
||||
@@ -532,9 +529,9 @@ class Functions extends Action
|
||||
;
|
||||
}
|
||||
|
||||
if ($function->getAttribute('logging')) {
|
||||
$execution = $dbForProject->updateDocument('executions', $executionId, $execution);
|
||||
}
|
||||
|
||||
$execution = $dbForProject->updateDocument('executions', $executionId, $execution);
|
||||
|
||||
/** Trigger Webhook */
|
||||
$executionModel = new Execution();
|
||||
$queueForEvents
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Utopia\Fetch;
|
||||
|
||||
class BodyMultipart
|
||||
{
|
||||
/**
|
||||
* @var array<string, mixed> $parts
|
||||
*/
|
||||
private array $parts = [];
|
||||
private string $boundary = "";
|
||||
|
||||
public function __construct(string $boundary = null)
|
||||
{
|
||||
if (is_null($boundary)) {
|
||||
$this->boundary = self::generateBoundary();
|
||||
} else {
|
||||
$this->boundary = $boundary;
|
||||
}
|
||||
}
|
||||
|
||||
public static function generateBoundary(): string
|
||||
{
|
||||
return '-----------------------------' . \uniqid();
|
||||
}
|
||||
|
||||
public function load(string $body): self
|
||||
{
|
||||
$eol = "\r\n";
|
||||
|
||||
$sections = \explode('--' . $this->boundary, $body);
|
||||
|
||||
foreach ($sections as $section) {
|
||||
if (empty($section)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strpos($section, $eol) === 0) {
|
||||
$section = substr($section, \strlen($eol));
|
||||
}
|
||||
|
||||
if (substr($section, -2) === $eol) {
|
||||
$section = substr($section, 0, -1 * \strlen($eol));
|
||||
}
|
||||
|
||||
if ($section == '--') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$partChunks = \explode($eol . $eol, $section, 2);
|
||||
|
||||
if (\count($partChunks) < 2) {
|
||||
continue; // Broken part
|
||||
}
|
||||
|
||||
[ $partHeaders, $partBody ] = $partChunks;
|
||||
$partHeaders = \explode($eol, $partHeaders);
|
||||
|
||||
$partName = "";
|
||||
foreach ($partHeaders as $partHeader) {
|
||||
if (!empty($partName)) {
|
||||
break;
|
||||
}
|
||||
|
||||
$partHeaderArray = \explode(':', $partHeader, 2);
|
||||
|
||||
$partHeaderName = \strtolower($partHeaderArray[0] ?? '');
|
||||
$partHeaderValue = $partHeaderArray[1] ?? '';
|
||||
if ($partHeaderName == "content-disposition") {
|
||||
$dispositionChunks = \explode("; ", $partHeaderValue);
|
||||
foreach ($dispositionChunks as $dispositionChunk) {
|
||||
$dispositionChunkValues = \explode("=", $dispositionChunk, 2);
|
||||
if (\count($dispositionChunkValues) >= 2) {
|
||||
if ($dispositionChunkValues[0] === "name") {
|
||||
$partName = \trim($dispositionChunkValues[1], "\"");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($partName)) {
|
||||
$this->parts[$partName] = $partBody;
|
||||
}
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function getParts(): array
|
||||
{
|
||||
return $this->parts ?? [];
|
||||
}
|
||||
|
||||
public function getPart(string $key, mixed $default = ''): mixed
|
||||
{
|
||||
return $this->parts[$key] ?? $default;
|
||||
}
|
||||
|
||||
public function setPart(string $key, mixed $value): self
|
||||
{
|
||||
$this->parts[$key] = $value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getBoundary(): string
|
||||
{
|
||||
return $this->boundary;
|
||||
}
|
||||
|
||||
public function setBoundary(string $boundary): self
|
||||
{
|
||||
$this->boundary = $boundary;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function exportHeader(): string
|
||||
{
|
||||
return 'multipart/form-data; boundary=' . $this->boundary;
|
||||
}
|
||||
|
||||
public function exportBody(): string
|
||||
{
|
||||
$eol = "\r\n";
|
||||
$query = '--' . $this->boundary;
|
||||
|
||||
foreach ($this->parts as $key => $value) {
|
||||
$query .= $eol . 'Content-Disposition: form-data; name="' . $key . '"';
|
||||
|
||||
if (\is_array($value)) {
|
||||
$query .= $eol . 'Content-Type: application/json';
|
||||
$value = \json_encode($value);
|
||||
}
|
||||
|
||||
$query .= $eol . $eol;
|
||||
$query .= $value . $eol;
|
||||
$query .= '--' . $this->boundary;
|
||||
}
|
||||
|
||||
$query .= "--" . $eol;
|
||||
|
||||
return $query;
|
||||
}
|
||||
}
|
||||
@@ -119,7 +119,7 @@ class Func extends Model
|
||||
->addRule('version', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'Version of Open Runtimes used for the function.',
|
||||
'default' => 'v3',
|
||||
'default' => 'v4',
|
||||
'example' => 'v2',
|
||||
])
|
||||
->addRule('installationId', [
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace Executor;
|
||||
|
||||
use Appwrite\Extend\Exception as AppwriteException;
|
||||
use Appwrite\Utopia\Fetch\BodyMultipart;
|
||||
use Exception;
|
||||
use Utopia\System\System;
|
||||
|
||||
@@ -178,6 +179,7 @@ class Executor
|
||||
string $method,
|
||||
array $headers,
|
||||
string $runtimeEntrypoint = null,
|
||||
bool $logging,
|
||||
int $requestTimeout = null
|
||||
) {
|
||||
if (empty($headers['host'])) {
|
||||
@@ -189,7 +191,6 @@ class Executor
|
||||
$params = [
|
||||
'runtimeId' => $runtimeId,
|
||||
'variables' => $variables,
|
||||
'body' => $body,
|
||||
'timeout' => $timeout,
|
||||
'path' => $path,
|
||||
'method' => $method,
|
||||
@@ -201,15 +202,20 @@ class Executor
|
||||
'memory' => $this->memory,
|
||||
'version' => $version,
|
||||
'runtimeEntrypoint' => $runtimeEntrypoint,
|
||||
'logging' => $logging,
|
||||
];
|
||||
|
||||
if(!empty($body)) {
|
||||
$params['body'] = $body;
|
||||
}
|
||||
|
||||
// Safety timeout. Executor has timeout, and open runtime has soft timeout.
|
||||
// This one shouldn't really happen, but prevents from unexpected networking behaviours.
|
||||
if ($requestTimeout == null) {
|
||||
$requestTimeout = $timeout + 15;
|
||||
}
|
||||
|
||||
$response = $this->call(self::METHOD_POST, $route, [ 'x-opr-runtime-id' => $runtimeId ], $params, true, $requestTimeout);
|
||||
$response = $this->call(self::METHOD_POST, $route, [ 'x-opr-runtime-id' => $runtimeId, 'content-type' => 'multipart/form-data', 'accept' => 'multipart/form-data' ], $params, true, $requestTimeout);
|
||||
|
||||
$status = $response['headers']['status-code'];
|
||||
if ($status >= 400) {
|
||||
@@ -217,6 +223,11 @@ class Executor
|
||||
throw new \Exception($message, $status);
|
||||
}
|
||||
|
||||
$response['body']['headers'] = \json_decode($response['body']['headers'] ?? '{}', true);
|
||||
$response['body']['statusCode'] = \intval($response['body']['statusCode'] ?? 500);
|
||||
$response['body']['duration'] = \intval($response['body']['duration'] ?? 0);
|
||||
$response['body']['startTime'] = \intval($response['body']['startTime'] ?? \microtime(true));
|
||||
|
||||
return $response['body'];
|
||||
}
|
||||
|
||||
@@ -248,7 +259,13 @@ class Executor
|
||||
break;
|
||||
|
||||
case 'multipart/form-data':
|
||||
$query = $this->flatten($params);
|
||||
$multipart = new BodyMultipart();
|
||||
foreach ($params as $key => $value) {
|
||||
$multipart->setPart($key, $value);
|
||||
}
|
||||
|
||||
$headers['content-type'] = $multipart->exportHeader();
|
||||
$query = $multipart->exportBody();
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -315,7 +332,16 @@ class Executor
|
||||
$curlErrorMessage = curl_error($ch);
|
||||
|
||||
if ($decode) {
|
||||
switch (substr($responseType, 0, strpos($responseType, ';'))) {
|
||||
$strpos = strpos($responseType, ';');
|
||||
$strpos = \is_bool($strpos) ? \strlen($responseType) : $strpos;
|
||||
switch (substr($responseType, 0, $strpos)) {
|
||||
case 'multipart/form-data':
|
||||
$boundary = \explode('boundary=', $responseHeaders['content-type'] ?? '')[1] ?? '';
|
||||
$multipartResponse = new BodyMultipart($boundary);
|
||||
$multipartResponse->load(\is_bool($responseBody) ? '' : $responseBody);
|
||||
|
||||
$responseBody = $multipartResponse->getParts();
|
||||
break;
|
||||
case 'application/json':
|
||||
$json = json_decode($responseBody, true);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user