mirror of
https://github.com/appwrite/appwrite.git
synced 2026-05-26 13:51:13 +00:00
Merge branch '1.6.x' into databases-reads-writes
This commit is contained in:
@@ -43,6 +43,13 @@ class Auth
|
||||
public const USER_ROLE_APPS = 'apps';
|
||||
public const USER_ROLE_SYSTEM = 'system';
|
||||
|
||||
/**
|
||||
* Activity associated with user or the app.
|
||||
*/
|
||||
public const ACTIVITY_TYPE_APP = 'app';
|
||||
public const ACTIVITY_TYPE_USER = 'user';
|
||||
public const ACTIVITY_TYPE_GUEST = 'guest';
|
||||
|
||||
/**
|
||||
* Token Types.
|
||||
*/
|
||||
|
||||
@@ -43,7 +43,7 @@ class Amazon extends OAuth2
|
||||
*/
|
||||
public function parseState(string $state)
|
||||
{
|
||||
return \json_decode(\html_entity_decode($state), true);
|
||||
return \json_decode(\urldecode(\html_entity_decode($state)), true);
|
||||
}
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ class Amazon extends OAuth2
|
||||
'response_type' => 'code',
|
||||
'client_id' => $this->appID,
|
||||
'scope' => \implode(' ', $this->getScopes()),
|
||||
'state' => \json_encode($this->state),
|
||||
'state' => \urlencode(\json_encode($this->state)),
|
||||
'redirect_uri' => $this->callback
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
namespace Appwrite\Auth\Validator;
|
||||
|
||||
use libphonenumber\NumberParseException;
|
||||
use libphonenumber\PhoneNumberUtil;
|
||||
use Utopia\Validator;
|
||||
|
||||
/**
|
||||
@@ -12,10 +14,12 @@ use Utopia\Validator;
|
||||
class Phone extends Validator
|
||||
{
|
||||
protected bool $allowEmpty;
|
||||
protected PhoneNumberUtil $helper;
|
||||
|
||||
public function __construct(bool $allowEmpty = false)
|
||||
{
|
||||
$this->allowEmpty = $allowEmpty;
|
||||
$this->helper = PhoneNumberUtil::getInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -47,6 +51,12 @@ class Phone extends Validator
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
$this->helper->parse($value);
|
||||
} catch (NumberParseException $e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !!\preg_match('/^\+[1-9]\d{6,14}$/', $value);
|
||||
}
|
||||
|
||||
|
||||
@@ -98,27 +98,36 @@ class Schema
|
||||
foreach ($routes as $route) {
|
||||
/** @var Route $route */
|
||||
|
||||
$namespace = $route->getLabel('sdk.namespace', '');
|
||||
$method = $route->getLabel('sdk.method', '');
|
||||
$name = $namespace . \ucfirst($method);
|
||||
/** @var \Appwrite\SDK\Method $sdk */
|
||||
$sdk = $route->getLabel('sdk', false);
|
||||
|
||||
if (empty($name)) {
|
||||
if (empty($sdk)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (Mapper::route($utopia, $route, $complexity) as $field) {
|
||||
switch ($route->getMethod()) {
|
||||
case 'GET':
|
||||
$queries[$name] = $field;
|
||||
break;
|
||||
case 'POST':
|
||||
case 'PUT':
|
||||
case 'PATCH':
|
||||
case 'DELETE':
|
||||
$mutations[$name] = $field;
|
||||
break;
|
||||
default:
|
||||
throw new \Exception("Unsupported method: {$route->getMethod()}");
|
||||
if (!\is_array($sdk)) {
|
||||
$sdk = [$sdk];
|
||||
}
|
||||
|
||||
foreach ($sdk as $method) {
|
||||
$namespace = $method->getNamespace();
|
||||
$methodName = $method->getMethodName();
|
||||
$name = $namespace . \ucfirst($methodName);
|
||||
|
||||
foreach (Mapper::route($utopia, $route, $method, $complexity) as $field) {
|
||||
switch ($route->getMethod()) {
|
||||
case 'GET':
|
||||
$queries[$name] = $field;
|
||||
break;
|
||||
case 'POST':
|
||||
case 'PUT':
|
||||
case 'PATCH':
|
||||
case 'DELETE':
|
||||
$mutations[$name] = $field;
|
||||
break;
|
||||
default:
|
||||
throw new \Exception("Unsupported method: {$route->getMethod()}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace Appwrite\GraphQL\Types;
|
||||
|
||||
use Appwrite\GraphQL\Resolvers;
|
||||
use Appwrite\GraphQL\Types;
|
||||
use Appwrite\SDK\Method;
|
||||
use Exception;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use GraphQL\Type\Definition\Type;
|
||||
@@ -78,6 +79,7 @@ class Mapper
|
||||
public static function route(
|
||||
App $utopia,
|
||||
Route $route,
|
||||
Method $method,
|
||||
callable $complexity
|
||||
): iterable {
|
||||
foreach (self::$blacklist as $blacklist) {
|
||||
@@ -86,10 +88,27 @@ class Mapper
|
||||
}
|
||||
}
|
||||
|
||||
$names = $route->getLabel('sdk.response.model', 'none');
|
||||
$models = \is_array($names)
|
||||
? \array_map(static fn ($m) => static::$models[$m], $names)
|
||||
: [static::$models[$names]];
|
||||
$responses = $method->getResponses() ?? [];
|
||||
|
||||
// If responses is an array, map each response to its model
|
||||
if (\is_array($responses)) {
|
||||
$models = [];
|
||||
foreach ($responses as $response) {
|
||||
$modelName = $response->getModel();
|
||||
|
||||
if (\is_array($modelName)) {
|
||||
foreach ($modelName as $name) {
|
||||
$models[] = static::$models[$name];
|
||||
}
|
||||
} else {
|
||||
$models[] = static::$models[$modelName];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// If single response, get its model and wrap in array
|
||||
$modelName = $responses->getModel();
|
||||
$models = [static::$models[$modelName]];
|
||||
}
|
||||
|
||||
foreach ($models as $model) {
|
||||
$type = Mapper::model(\ucfirst($model->getType()));
|
||||
@@ -98,13 +117,25 @@ class Mapper
|
||||
$list = false;
|
||||
|
||||
foreach ($route->getParams() as $name => $parameter) {
|
||||
$methodParameters = $method->getParameters();
|
||||
|
||||
if (!empty($methodParameters)) {
|
||||
if (!array_key_exists($name, $methodParameters)) {
|
||||
continue;
|
||||
}
|
||||
$optional = $methodParameters[$name]['optional'];
|
||||
} else {
|
||||
$optional = $parameter['optional'];
|
||||
}
|
||||
|
||||
if ($name === 'queries') {
|
||||
$list = true;
|
||||
}
|
||||
|
||||
$parameterType = Mapper::param(
|
||||
$utopia,
|
||||
$parameter['validator'],
|
||||
!$parameter['optional'],
|
||||
!$optional,
|
||||
$parameter['injections']
|
||||
);
|
||||
$params[$name] = [
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace Appwrite\Platform\Tasks;
|
||||
|
||||
use Appwrite\SDK\AuthType;
|
||||
use Appwrite\Specification\Format\OpenAPI3;
|
||||
use Appwrite\Specification\Format\Swagger2;
|
||||
use Appwrite\Specification\Specification;
|
||||
@@ -182,56 +183,69 @@ class Specs extends Action
|
||||
|
||||
foreach ($appRoutes as $key => $method) {
|
||||
foreach ($method as $route) {
|
||||
$hide = $route->getLabel('sdk.hide', false);
|
||||
if ($hide === true || (\is_array($hide) && \in_array($platform, $hide))) {
|
||||
$sdks = $route->getLabel('sdk', false);
|
||||
|
||||
if (empty($sdks)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/** @var \Utopia\Route $route */
|
||||
$routeSecurity = $route->getLabel('sdk.auth', []);
|
||||
$sdkPlatforms = [];
|
||||
if (!\is_array($sdks)) {
|
||||
$sdks = [$sdks];
|
||||
}
|
||||
|
||||
foreach ($routeSecurity as $value) {
|
||||
switch ($value) {
|
||||
case APP_AUTH_TYPE_SESSION:
|
||||
$sdkPlatforms[] = APP_PLATFORM_CLIENT;
|
||||
break;
|
||||
case APP_AUTH_TYPE_JWT:
|
||||
case APP_AUTH_TYPE_KEY:
|
||||
$sdkPlatforms[] = APP_PLATFORM_SERVER;
|
||||
break;
|
||||
case APP_AUTH_TYPE_ADMIN:
|
||||
$sdkPlatforms[] = APP_PLATFORM_CONSOLE;
|
||||
break;
|
||||
foreach ($sdks as $sdk) {
|
||||
/** @var \Appwrite\SDK\Method $sdks */
|
||||
|
||||
$hide = $sdk->isHidden();
|
||||
if ($hide === true || (\is_array($hide) && \in_array($platform, $hide))) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($routeSecurity)) {
|
||||
$sdkPlatforms[] = APP_PLATFORM_SERVER;
|
||||
$sdkPlatforms[] = APP_PLATFORM_CLIENT;
|
||||
}
|
||||
$routeSecurity = $sdk->getAuth();
|
||||
$sdkPlatforms = [];
|
||||
|
||||
if (!$route->getLabel('docs', true)) {
|
||||
continue;
|
||||
}
|
||||
foreach ($routeSecurity as $value) {
|
||||
switch ($value) {
|
||||
case AuthType::SESSION:
|
||||
$sdkPlatforms[] = APP_PLATFORM_CLIENT;
|
||||
break;
|
||||
case AuthType::JWT:
|
||||
case AuthType::KEY:
|
||||
$sdkPlatforms[] = APP_PLATFORM_SERVER;
|
||||
break;
|
||||
case AuthType::ADMIN:
|
||||
$sdkPlatforms[] = APP_PLATFORM_CONSOLE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($route->getLabel('sdk.mock', false) && !$mocks) {
|
||||
continue;
|
||||
}
|
||||
if (empty($routeSecurity)) {
|
||||
$sdkPlatforms[] = APP_PLATFORM_SERVER;
|
||||
$sdkPlatforms[] = APP_PLATFORM_CLIENT;
|
||||
}
|
||||
|
||||
if (!$route->getLabel('sdk.mock', false) && $mocks) {
|
||||
continue;
|
||||
}
|
||||
if (!$route->getLabel('docs', true)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (empty($route->getLabel('sdk.namespace', null))) {
|
||||
continue;
|
||||
}
|
||||
if ($route->getLabel('mock', false) && !$mocks) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($platform !== APP_PLATFORM_CONSOLE && !\in_array($platforms[$platform], $sdkPlatforms)) {
|
||||
continue;
|
||||
}
|
||||
if (!$route->getLabel('mock', false) && $mocks) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$routes[] = $route;
|
||||
if (empty($sdk->getNamespace())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($platform !== APP_PLATFORM_CONSOLE && !\in_array($platforms[$platform], $sdkPlatforms)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$routes[] = $route;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace Appwrite\Platform\Workers;
|
||||
|
||||
use Appwrite\Auth\Auth;
|
||||
use Exception;
|
||||
use Throwable;
|
||||
use Utopia\Audit\Audit;
|
||||
@@ -60,6 +61,7 @@ class Audits extends Action
|
||||
|
||||
$userName = $user->getAttribute('name', '');
|
||||
$userEmail = $user->getAttribute('email', '');
|
||||
$userType = $user->getAttribute('type', Auth::ACTIVITY_TYPE_USER);
|
||||
|
||||
$audit = new Audit($dbForProject);
|
||||
$audit->log(
|
||||
@@ -74,6 +76,7 @@ class Audits extends Action
|
||||
'userId' => $user->getId(),
|
||||
'userName' => $userName,
|
||||
'userEmail' => $userEmail,
|
||||
'userType' => $userType,
|
||||
'mode' => $mode,
|
||||
'data' => $auditPayload,
|
||||
]
|
||||
|
||||
@@ -147,7 +147,8 @@ class Certificates extends Action
|
||||
|
||||
// If certificate exists already, double-check expiry date. Skip if job is forced
|
||||
if (!$certificates->isRenewRequired($domain->get(), $log)) {
|
||||
throw new Exception('Renew isn\'t required.');
|
||||
Console::info("Skipping, renew isn't required");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -535,6 +535,8 @@ class Databases extends Action
|
||||
$databaseId = $database->getId();
|
||||
$databaseInternalId = $database->getInternalId();
|
||||
|
||||
$dbForProject->deleteCollection('database_' . $databaseInternalId . '_collection_' . $collection->getInternalId());
|
||||
|
||||
/**
|
||||
* Related collections relating to current collection
|
||||
*/
|
||||
@@ -553,8 +555,6 @@ class Databases extends Action
|
||||
}
|
||||
);
|
||||
|
||||
$dbForProject->deleteCollection('database_' . $databaseInternalId . '_collection_' . $collection->getInternalId());
|
||||
|
||||
$this->deleteByGroup('attributes', [
|
||||
Query::equal('databaseInternalId', [$databaseInternalId]),
|
||||
Query::equal('collectionInternalId', [$collectionInternalId])
|
||||
|
||||
@@ -30,16 +30,13 @@ class Usage extends Action
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
|
||||
$this
|
||||
->desc('Usage worker')
|
||||
->inject('message')
|
||||
->inject('project')
|
||||
->inject('getProjectDB')
|
||||
->inject('queueForUsageDump')
|
||||
->callback(function (Message $message, Document $project, callable $getProjectDB, UsageDump $queueForUsageDump) {
|
||||
$this->action($message, $project, $getProjectDB, $queueForUsageDump);
|
||||
});
|
||||
->desc('Usage worker')
|
||||
->inject('message')
|
||||
->inject('project')
|
||||
->inject('getProjectDB')
|
||||
->inject('queueForUsageDump')
|
||||
->callback([$this, 'action']);
|
||||
|
||||
$this->aggregationInterval = (int) System::getEnv('_APP_USAGE_AGGREGATION_INTERVAL', '20');
|
||||
$this->lastTriggeredTime = time();
|
||||
@@ -61,7 +58,6 @@ class Usage extends Action
|
||||
throw new Exception('Missing payload');
|
||||
}
|
||||
|
||||
|
||||
if (empty($project->getAttribute('database'))) {
|
||||
var_dump($payload);
|
||||
return;
|
||||
|
||||
@@ -7,7 +7,7 @@ use Utopia\CLI\Console;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\DateTime;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Exception\Duplicate;
|
||||
use Utopia\Database\Exception\NotFound;
|
||||
use Utopia\Platform\Action;
|
||||
use Utopia\Queue\Message;
|
||||
use Utopia\System\System;
|
||||
@@ -38,9 +38,7 @@ class UsageDump extends Action
|
||||
$this
|
||||
->inject('message')
|
||||
->inject('getProjectDB')
|
||||
->callback(function (Message $message, callable $getProjectDB) {
|
||||
$this->action($message, $getProjectDB);
|
||||
});
|
||||
->callback([$this, 'action']);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -57,230 +55,247 @@ class UsageDump extends Action
|
||||
throw new Exception('Missing payload');
|
||||
}
|
||||
|
||||
try {
|
||||
foreach ($payload['stats'] ?? [] as $stats) {
|
||||
$project = new Document($stats['project'] ?? []);
|
||||
$numberOfKeys = !empty($stats['keys']) ? \count($stats['keys']) : 0;
|
||||
$receivedAt = $stats['receivedAt'] ?? 'NONE';
|
||||
if ($numberOfKeys === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($payload['stats'] ?? [] as $stats) {
|
||||
$project = new Document($stats['project'] ?? []);
|
||||
|
||||
/**
|
||||
* End temp bug fallback
|
||||
*/
|
||||
$numberOfKeys = !empty($stats['keys']) ? count($stats['keys']) : 0;
|
||||
$receivedAt = $stats['receivedAt'] ?? 'NONE';
|
||||
if ($numberOfKeys === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
console::log('['.DateTime::now().'] Id: '.$project->getId(). ' InternalId: '.$project->getInternalId(). ' Db: '.$project->getAttribute('database').' ReceivedAt: '.$receivedAt. ' Keys: '.$numberOfKeys);
|
||||
|
||||
try {
|
||||
$dbForProject = $getProjectDB($project);
|
||||
$projectDocuments = [];
|
||||
$databaseCache = [];
|
||||
$collectionSizeCache = [];
|
||||
|
||||
Console::log('['.DateTime::now().'] Id: '.$project->getId(). ' InternalId: '.$project->getInternalId(). ' Db: '.$project->getAttribute('database').' ReceivedAt: '.$receivedAt. ' Keys: '.$numberOfKeys . ' Started');
|
||||
$start = \microtime(true);
|
||||
|
||||
foreach ($stats['keys'] ?? [] as $key => $value) {
|
||||
if ($value == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (str_contains($key, METRIC_DATABASES_STORAGE)) {
|
||||
try {
|
||||
$this->handleDatabaseStorage($key, $dbForProject);
|
||||
} catch (\Exception $e) {
|
||||
console::error('[' . DateTime::now() . '] failed to calculate database storage for key [' . $key . '] ' . $e->getMessage());
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($this->periods as $period => $format) {
|
||||
$time = 'inf' === $period ? null : date($format, time());
|
||||
$time = 'inf' === $period ? null : \date($format, \time());
|
||||
$id = \md5("{$time}_{$period}_{$key}");
|
||||
|
||||
try {
|
||||
$dbForProject->createDocument('stats', new Document([
|
||||
'$id' => $id,
|
||||
'period' => $period,
|
||||
'time' => $time,
|
||||
'metric' => $key,
|
||||
'value' => $value,
|
||||
'region' => System::getEnv('_APP_REGION', 'default'),
|
||||
]));
|
||||
} catch (Duplicate $th) {
|
||||
if ($value < 0) {
|
||||
$dbForProject->decreaseDocumentAttribute(
|
||||
'stats',
|
||||
$id,
|
||||
'value',
|
||||
abs($value)
|
||||
);
|
||||
} else {
|
||||
$dbForProject->increaseDocumentAttribute(
|
||||
'stats',
|
||||
$id,
|
||||
'value',
|
||||
$value
|
||||
);
|
||||
}
|
||||
if (\str_contains($key, METRIC_DATABASES_STORAGE)) {
|
||||
$this->handleDatabaseStorage(
|
||||
$id,
|
||||
$key,
|
||||
$time,
|
||||
$period,
|
||||
$dbForProject,
|
||||
$projectDocuments,
|
||||
$databaseCache,
|
||||
$collectionSizeCache
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
$projectDocuments[] = new Document([
|
||||
'$id' => $id,
|
||||
'period' => $period,
|
||||
'time' => $time,
|
||||
'metric' => $key,
|
||||
'value' => $value,
|
||||
'region' => System::getEnv('_APP_REGION', 'default'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
console::error('[' . DateTime::now() . '] project [' . $project->getInternalId() . '] database [' . $project['database'] . '] ' . ' ' . $e->getMessage());
|
||||
|
||||
$dbForProject->createOrUpdateDocumentsWithIncrease(
|
||||
collection: 'stats',
|
||||
attribute: 'value',
|
||||
documents: $projectDocuments
|
||||
);
|
||||
|
||||
$end = \microtime(true);
|
||||
Console::log('['.DateTime::now().'] Id: '.$project->getId(). ' InternalId: '.$project->getInternalId(). ' Db: '.$project->getAttribute('database').' ReceivedAt: '.$receivedAt. ' Keys: '.$numberOfKeys. ' Time: '.($end - $start).'s');
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
Console::error('[' . DateTime::now() . '] Error processing stats: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private function handleDatabaseStorage(string $key, Database $dbForProject): void
|
||||
{
|
||||
$data = explode('.', $key);
|
||||
$start = microtime(true);
|
||||
private function handleDatabaseStorage(
|
||||
string $id,
|
||||
string $key,
|
||||
?string $time,
|
||||
string $period,
|
||||
Database $dbForProject,
|
||||
array &$projectDocuments,
|
||||
array &$databaseCache,
|
||||
array &$collectionSizeCache,
|
||||
): void {
|
||||
$data = \explode('.', $key);
|
||||
$value = 0;
|
||||
$previousValue = 0;
|
||||
|
||||
$updateMetric = function (Database $dbForProject, int $value, string $key, string $period, string|null $time) {
|
||||
$id = \md5("{$time}_{$period}_{$key}");
|
||||
try {
|
||||
$previousValue = $dbForProject
|
||||
->getDocument('stats', $id)
|
||||
->getAttribute('value', 0);
|
||||
} catch (\Exception) {
|
||||
// No previous value
|
||||
}
|
||||
|
||||
try {
|
||||
$dbForProject->createDocument('stats', new Document([
|
||||
'$id' => $id,
|
||||
'period' => $period,
|
||||
'time' => $time,
|
||||
'metric' => $key,
|
||||
'value' => $value,
|
||||
'region' => System::getEnv('_APP_REGION', 'default'),
|
||||
]));
|
||||
} catch (Duplicate $th) {
|
||||
if ($value < 0) {
|
||||
$dbForProject->decreaseDocumentAttribute(
|
||||
'stats',
|
||||
$id,
|
||||
'value',
|
||||
abs($value)
|
||||
);
|
||||
} else {
|
||||
$dbForProject->increaseDocumentAttribute(
|
||||
'stats',
|
||||
$id,
|
||||
'value',
|
||||
$value
|
||||
);
|
||||
switch (\count($data)) {
|
||||
case METRIC_COLLECTION_LEVEL_STORAGE:
|
||||
$databaseInternalId = $data[0];
|
||||
$collectionInternalId = $data[1];
|
||||
$collectionId = "database_{$databaseInternalId}_collection_{$collectionInternalId}";
|
||||
|
||||
if (!isset($collectionSizeCache[$collectionId])) {
|
||||
try {
|
||||
$collectionSizeCache[$collectionId] = $dbForProject->getSizeOfCollection($collectionId);
|
||||
} catch (\Exception $e) {
|
||||
if (!$e instanceof NotFound) {
|
||||
throw $e;
|
||||
}
|
||||
$collectionSizeCache[$collectionId] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
foreach ($this->periods as $period => $format) {
|
||||
$time = 'inf' === $period ? null : date($format, time());
|
||||
$id = \md5("{$time}_{$period}_{$key}");
|
||||
$value = $collectionSizeCache[$collectionId];
|
||||
|
||||
$value = 0;
|
||||
$previousValue = 0;
|
||||
try {
|
||||
$previousValue = ($dbForProject->getDocument('stats', $id))->getAttribute('value', 0);
|
||||
} catch (\Exception $e) {
|
||||
// No previous value
|
||||
}
|
||||
|
||||
switch (count($data)) {
|
||||
// Collection Level
|
||||
case METRIC_COLLECTION_LEVEL_STORAGE:
|
||||
Console::log('[' . DateTime::now() . '] Collection Level Storage Calculation [' . $key . ']');
|
||||
$databaseInternalId = $data[0];
|
||||
$collectionInternalId = $data[1];
|
||||
|
||||
try {
|
||||
$value = $dbForProject->getSizeOfCollection('database_' . $databaseInternalId . '_collection_' . $collectionInternalId);
|
||||
} catch (\Exception $e) {
|
||||
// Collection not found
|
||||
if ($e->getMessage() !== 'Collection not found') {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
// Compare with previous value
|
||||
$diff = $value - $previousValue;
|
||||
|
||||
if ($diff === 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Update Collection
|
||||
$updateMetric($dbForProject, $diff, $key, $period, $time);
|
||||
|
||||
// Update Database
|
||||
$databaseKey = str_replace(['{databaseInternalId}'], [$data[0]], METRIC_DATABASE_ID_STORAGE);
|
||||
$updateMetric($dbForProject, $diff, $databaseKey, $period, $time);
|
||||
|
||||
// Update Project
|
||||
$projectKey = METRIC_DATABASES_STORAGE;
|
||||
$updateMetric($dbForProject, $diff, $projectKey, $period, $time);
|
||||
$diff = $value - $previousValue;
|
||||
if ($diff === 0) {
|
||||
break;
|
||||
// Database Level
|
||||
case METRIC_DATABASE_LEVEL_STORAGE:
|
||||
Console::log('[' . DateTime::now() . '] Database Level Storage Calculation [' . $key . ']');
|
||||
$databaseInternalId = $data[0];
|
||||
}
|
||||
|
||||
$collections = [];
|
||||
$keys = [
|
||||
$key,
|
||||
\str_replace(['{databaseInternalId}'], [$data[0]], METRIC_DATABASE_ID_STORAGE),
|
||||
METRIC_DATABASES_STORAGE
|
||||
];
|
||||
|
||||
foreach ($keys as $metric) {
|
||||
$projectDocuments[] = $this->createStatsDocument($id, $period, $time, $metric, $diff);
|
||||
}
|
||||
break;
|
||||
|
||||
case METRIC_DATABASE_LEVEL_STORAGE:
|
||||
$databaseInternalId = $data[0];
|
||||
$databaseId = "database_{$databaseInternalId}";
|
||||
|
||||
if (!isset($databaseCache[$databaseId])) {
|
||||
try {
|
||||
$collections = $dbForProject->find('database_' . $databaseInternalId);
|
||||
$databaseCache[$databaseId] = $dbForProject->find($databaseId);
|
||||
} catch (\Exception $e) {
|
||||
// Database not found
|
||||
if ($e->getMessage() !== 'Collection not found') {
|
||||
if (!$e instanceof NotFound) {
|
||||
throw $e;
|
||||
}
|
||||
$databaseCache[$databaseId] = [];
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($collections as $collection) {
|
||||
foreach ($databaseCache[$databaseId] as $collection) {
|
||||
$collectionId = "{$databaseId}_collection_{$collection->getInternalId()}";
|
||||
|
||||
if (!isset($collectionSizeCache[$collectionId])) {
|
||||
try {
|
||||
$value += $dbForProject->getSizeOfCollection('database_' . $databaseInternalId . '_collection_' . $collection->getInternalId());
|
||||
$collectionSizeCache[$collectionId] = $dbForProject->getSizeOfCollection($collectionId);
|
||||
} catch (\Exception $e) {
|
||||
// Collection not found
|
||||
if ($e->getMessage() !== 'Collection not found') {
|
||||
if (!$e instanceof NotFound) {
|
||||
throw $e;
|
||||
}
|
||||
$collectionSizeCache[$collectionId] = 0;
|
||||
}
|
||||
}
|
||||
$value += $collectionSizeCache[$collectionId];
|
||||
}
|
||||
|
||||
$diff = $value - $previousValue;
|
||||
if ($diff === 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
$keys = [
|
||||
\str_replace(['{databaseInternalId}'], [$data[0]], METRIC_DATABASE_ID_STORAGE),
|
||||
METRIC_DATABASES_STORAGE
|
||||
];
|
||||
|
||||
foreach ($keys as $metric) {
|
||||
$projectDocuments[] = $this->createStatsDocument($id, $period, $time, $metric, $diff);
|
||||
}
|
||||
break;
|
||||
|
||||
case METRIC_PROJECT_LEVEL_STORAGE:
|
||||
if (!isset($databaseCache['*'])) {
|
||||
try {
|
||||
$databaseCache['*'] = $dbForProject->find('databases');
|
||||
} catch (\Exception $e) {
|
||||
if (!$e instanceof NotFound) {
|
||||
throw $e;
|
||||
}
|
||||
$databaseCache['*'] = [];
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($databaseCache['*'] as $database) {
|
||||
$databaseId = "database_{$database->getInternalId()}";
|
||||
if (!isset($databaseCache[$databaseId])) {
|
||||
try {
|
||||
$databaseCache[$databaseId] = $dbForProject->find($databaseId);
|
||||
} catch (\Exception $e) {
|
||||
if (!$e instanceof NotFound) {
|
||||
throw $e;
|
||||
}
|
||||
$databaseCache[$databaseId] = [];
|
||||
}
|
||||
}
|
||||
|
||||
$diff = $value - $previousValue;
|
||||
foreach ($databaseCache[$databaseId] as $collection) {
|
||||
$collectionId = "{$databaseId}_collection_{$collection->getInternalId()}";
|
||||
|
||||
if ($diff === 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Update Database
|
||||
$databaseKey = str_replace(['{databaseInternalId}'], [$data[0]], METRIC_DATABASE_ID_STORAGE);
|
||||
$updateMetric($dbForProject, $diff, $databaseKey, $period, $time);
|
||||
|
||||
// Update Project
|
||||
$projectKey = METRIC_DATABASES_STORAGE;
|
||||
$updateMetric($dbForProject, $diff, $projectKey, $period, $time);
|
||||
break;
|
||||
// Project Level
|
||||
case METRIC_PROJECT_LEVEL_STORAGE:
|
||||
Console::log('[' . DateTime::now() . '] Project Level Storage Calculation [' . $key . ']');
|
||||
// Get all project databases
|
||||
$databases = $dbForProject->find('database');
|
||||
|
||||
// Recalculate all databases
|
||||
foreach ($databases as $database) {
|
||||
$collections = $dbForProject->find('database_' . $database->getInternalId());
|
||||
|
||||
foreach ($collections as $collection) {
|
||||
if (!isset($collectionSizeCache[$collectionId])) {
|
||||
try {
|
||||
$value += $dbForProject->getSizeOfCollection('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId());
|
||||
$collectionSizeCache[$collectionId] = $dbForProject->getSizeOfCollection($collectionId);
|
||||
} catch (\Exception $e) {
|
||||
// Collection not found
|
||||
if ($e->getMessage() !== 'Collection not found') {
|
||||
if (!$e instanceof NotFound) {
|
||||
throw $e;
|
||||
}
|
||||
$collectionSizeCache[$collectionId] = 0;
|
||||
}
|
||||
}
|
||||
$value += $collectionSizeCache[$collectionId];
|
||||
}
|
||||
}
|
||||
|
||||
$diff = $value - $previousValue;
|
||||
|
||||
// Update Project
|
||||
$projectKey = METRIC_DATABASES_STORAGE;
|
||||
$updateMetric($dbForProject, $diff, $projectKey, $period, $time);
|
||||
$diff = $value - $previousValue;
|
||||
if ($diff === 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$keys = [
|
||||
METRIC_DATABASES_STORAGE
|
||||
];
|
||||
|
||||
foreach ($keys as $metric) {
|
||||
$projectDocuments[] = $this->createStatsDocument($id, $period, $time, $metric, $diff);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$end = microtime(true);
|
||||
|
||||
console::log('[' . DateTime::now() . '] DB Storage Calculation [' . $key . '] took ' . (($end - $start) * 1000) . ' milliseconds');
|
||||
private function createStatsDocument(
|
||||
string $id,
|
||||
string $period,
|
||||
?string $time,
|
||||
string $key,
|
||||
int $diff,
|
||||
): Document {
|
||||
return new Document([
|
||||
'$id' => $id,
|
||||
'period' => $period,
|
||||
'time' => $time,
|
||||
'metric' => $key,
|
||||
'value' => $diff,
|
||||
'region' => System::getEnv('_APP_REGION', 'default'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\SDK;
|
||||
|
||||
enum AuthType: string
|
||||
{
|
||||
case JWT = APP_AUTH_TYPE_JWT;
|
||||
case KEY = APP_AUTH_TYPE_KEY;
|
||||
case SESSION = APP_AUTH_TYPE_SESSION;
|
||||
case ADMIN = APP_AUTH_TYPE_ADMIN;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\SDK;
|
||||
|
||||
enum ContentType: string
|
||||
{
|
||||
case NONE = '';
|
||||
case JSON = 'application/json';
|
||||
case IMAGE = 'image/*';
|
||||
case IMAGE_PNG = 'image/png';
|
||||
case MULTIPART = 'multipart/form-data';
|
||||
case HTML = 'text/html';
|
||||
case TEXT = 'text/plain';
|
||||
case ANY = '*/*';
|
||||
}
|
||||
@@ -0,0 +1,286 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\SDK;
|
||||
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Swoole\Http\Response as HttpResponse;
|
||||
|
||||
class Method
|
||||
{
|
||||
public static array $processed = [];
|
||||
|
||||
public static array $errors = [];
|
||||
|
||||
/**
|
||||
* Initialise a new SDK method
|
||||
*
|
||||
* @param string $namespace
|
||||
* @param string $name
|
||||
* @param string $description
|
||||
* @param array<AuthType> $auth
|
||||
* @param array<SDKResponse> $responses
|
||||
* @param ContentType $responseType
|
||||
* @param MethodType|null $methodType
|
||||
* @param bool $deprecated
|
||||
* @param array|bool $hide
|
||||
* @param bool $packaging
|
||||
* @param string $requestType
|
||||
* @param array $parameters
|
||||
* @param array $additionalParameters
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function __construct(
|
||||
protected string $namespace,
|
||||
protected string $name,
|
||||
protected string $description,
|
||||
protected array $auth,
|
||||
protected array $responses,
|
||||
protected ContentType $contentType = ContentType::JSON,
|
||||
protected ?MethodType $type = null,
|
||||
protected bool $deprecated = false,
|
||||
protected array|bool $hide = false,
|
||||
protected bool $packaging = false,
|
||||
protected string $requestType = 'application/json',
|
||||
protected array $parameters = [],
|
||||
protected array $additionalParameters = []
|
||||
) {
|
||||
$this->validateMethod($name, $namespace);
|
||||
$this->validateAuthTypes($auth);
|
||||
$this->validateDesc($description);
|
||||
|
||||
foreach ($responses as $response) {
|
||||
/** @var SDKResponse $response */
|
||||
$this->validateResponseModel($response->getModel());
|
||||
$this->validateNoContent($response);
|
||||
}
|
||||
}
|
||||
|
||||
protected function getRouteName(): string
|
||||
{
|
||||
return $this->namespace . '.' . $this->name;
|
||||
}
|
||||
|
||||
protected function validateMethod(string $name, string $namespace): void
|
||||
{
|
||||
if (\in_array($this->getRouteName(), self::$processed)) {
|
||||
self::$errors[] = "Error with {$this->getRouteName()} method: Method already exists in namespace {$namespace}";
|
||||
}
|
||||
|
||||
self::$processed[] = $this->getRouteName();
|
||||
}
|
||||
|
||||
protected function validateAuthTypes(array $authTypes): void
|
||||
{
|
||||
foreach ($authTypes as $authType) {
|
||||
if (!($authType instanceof AuthType)) {
|
||||
self::$errors[] = "Error with {$this->getRouteName()} method: Invalid auth type";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function validateDesc(string $desc): void
|
||||
{
|
||||
if (empty($desc)) {
|
||||
self::$errors[] = "Error with {$this->getRouteName()} method: Description label is empty";
|
||||
return;
|
||||
}
|
||||
|
||||
$descPath = $this->getDescriptionFilePath();
|
||||
|
||||
if (empty($descPath)) {
|
||||
self::$errors[] = "Error with {$this->getRouteName()} method: Description file not found at {$desc}";
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
protected function validateResponseModel(string|array $responseModel): void
|
||||
{
|
||||
$response = new Response(new HttpResponse());
|
||||
|
||||
if (!\is_array($responseModel)) {
|
||||
$responseModel = [$responseModel];
|
||||
}
|
||||
|
||||
foreach ($responseModel as $model) {
|
||||
try {
|
||||
$response->getModel($model);
|
||||
} catch (\Exception $e) {
|
||||
self::$errors[] = "Error with {$this->getRouteName()} method: Invalid response model, make sure the model has been defined in Response.php";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function validateNoContent(SDKResponse $response): void
|
||||
{
|
||||
if ($response->getCode() === 204) {
|
||||
if ($response->getModel() !== Response::MODEL_NONE) {
|
||||
self::$errors[] = "Error with {$this->getRouteName()} method: Response code 204 must have response model 'none'";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getNamespace(): string
|
||||
{
|
||||
return $this->namespace;
|
||||
}
|
||||
|
||||
public function getMethodName(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function getDescription(): string
|
||||
{
|
||||
return $this->description;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns the absolute path to the description file returning null if the file does not exist.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getDescriptionFilePath(): ?string
|
||||
{
|
||||
return \realpath(__DIR__ . '/../../../' . $this->getDescription()) ?: null;
|
||||
}
|
||||
|
||||
public function getAuth(): array
|
||||
{
|
||||
return $this->auth;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<SDKResponse>
|
||||
*/
|
||||
public function getResponses(): array
|
||||
{
|
||||
return $this->responses;
|
||||
}
|
||||
|
||||
public function getContentType(): ContentType
|
||||
{
|
||||
return $this->contentType;
|
||||
}
|
||||
|
||||
public function getType(): ?MethodType
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
public function isDeprecated(): bool
|
||||
{
|
||||
return $this->deprecated;
|
||||
}
|
||||
|
||||
public function isHidden(): bool|array
|
||||
{
|
||||
return $this->hide ?? false;
|
||||
}
|
||||
|
||||
public function isPackaging(): bool
|
||||
{
|
||||
return $this->packaging;
|
||||
}
|
||||
|
||||
public function getRequestType(): string
|
||||
{
|
||||
return $this->requestType;
|
||||
}
|
||||
|
||||
public function getParameters(): array
|
||||
{
|
||||
return $this->parameters;
|
||||
}
|
||||
|
||||
public function getAdditionalParameters(): array
|
||||
{
|
||||
return $this->additionalParameters;
|
||||
}
|
||||
|
||||
public function setNamespace(string $namespace): self
|
||||
{
|
||||
$this->namespace = $namespace;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setMethodName(string $name): self
|
||||
{
|
||||
$this->name = $name;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setDescription(string $description): self
|
||||
{
|
||||
$this->description = $description;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setAuth(array $auth): self
|
||||
{
|
||||
$this->validateAuthTypes($auth);
|
||||
$this->auth = $auth;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<SDKResponse> $responses
|
||||
*/
|
||||
public function setResponses(array $responses): self
|
||||
{
|
||||
foreach ($responses as $response) {
|
||||
$this->validateResponseModel($response->getModel());
|
||||
$this->validateNoContent($response);
|
||||
}
|
||||
$this->responses = $responses;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setContentType(ContentType $contentType): self
|
||||
{
|
||||
$this->contentType = $contentType;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setType(?MethodType $type): self
|
||||
{
|
||||
$this->type = $type;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setDeprecated(bool $deprecated): self
|
||||
{
|
||||
$this->deprecated = $deprecated;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setHide(bool|array $hide): self
|
||||
{
|
||||
$this->hide = $hide;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setPackaging(bool $packaging): self
|
||||
{
|
||||
$this->packaging = $packaging;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setRequestType(string $requestType): self
|
||||
{
|
||||
$this->requestType = $requestType;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setParameters(array $parameters): self
|
||||
{
|
||||
$this->parameters = $parameters;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public static function getErrors(): array
|
||||
{
|
||||
return self::$errors;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\SDK;
|
||||
|
||||
enum MethodType: string
|
||||
{
|
||||
case WEBAUTH = 'webAuth';
|
||||
case LOCATION = 'location';
|
||||
case GRAPHQL = 'graphql';
|
||||
case UPLOAD = 'upload';
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\SDK;
|
||||
|
||||
class Response
|
||||
{
|
||||
/**
|
||||
* @param int $code
|
||||
* @param string|array $model
|
||||
* @param string $description
|
||||
*/
|
||||
public function __construct(
|
||||
private int $code,
|
||||
private string|array $model
|
||||
) {
|
||||
}
|
||||
|
||||
public function getCode(): int
|
||||
{
|
||||
return $this->code;
|
||||
}
|
||||
|
||||
public function getModel(): string|array
|
||||
{
|
||||
return $this->model;
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
namespace Appwrite\Specification\Format;
|
||||
|
||||
use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\MethodType;
|
||||
use Appwrite\Specification\Format;
|
||||
use Appwrite\Template\Template;
|
||||
use Appwrite\Utopia\Response\Model;
|
||||
@@ -120,28 +122,49 @@ class OpenAPI3 extends Format
|
||||
foreach ($this->routes as $route) {
|
||||
$url = \str_replace('/v1', '', $route->getPath());
|
||||
$scope = $route->getLabel('scope', '');
|
||||
$consumes = [$route->getLabel('sdk.request.type', 'application/json')];
|
||||
|
||||
$sdk = $route->getLabel('sdk', false);
|
||||
|
||||
$method = $route->getLabel('sdk.method', \uniqid());
|
||||
$desc = (!empty($route->getLabel('sdk.description', ''))) ? \realpath(__DIR__ . '/../../../../' . $route->getLabel('sdk.description', '')) : null;
|
||||
$produces = $route->getLabel('sdk.response.type', null);
|
||||
$model = $route->getLabel('sdk.response.model', 'none');
|
||||
$routeSecurity = $route->getLabel('sdk.auth', []);
|
||||
if (empty($sdk)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$additionalMethods = null;
|
||||
if (is_array($sdk)) {
|
||||
$mainSdk = array_shift($sdk);
|
||||
$additionalMethods = $sdk;
|
||||
|
||||
$sdk = $mainSdk;
|
||||
}
|
||||
|
||||
/**
|
||||
* @var \Appwrite\SDK\Method $sdk
|
||||
*/
|
||||
$consumes = [$sdk->getRequestType()];
|
||||
|
||||
$method = $sdk->getMethodName() ?? \uniqid();
|
||||
|
||||
if (!empty($method) && is_array($method)) {
|
||||
$method = array_keys($method)[0];
|
||||
}
|
||||
|
||||
$desc = $sdk->getDescriptionFilePath();
|
||||
$produces = ($sdk->getContentType())->value;
|
||||
$routeSecurity = $sdk->getAuth() ?? [];
|
||||
$sdkPlatforms = [];
|
||||
|
||||
foreach ($routeSecurity as $value) {
|
||||
switch ($value) {
|
||||
case APP_AUTH_TYPE_SESSION:
|
||||
case AuthType::SESSION:
|
||||
$sdkPlatforms[] = APP_PLATFORM_CLIENT;
|
||||
break;
|
||||
case APP_AUTH_TYPE_KEY:
|
||||
case AuthType::KEY:
|
||||
$sdkPlatforms[] = APP_PLATFORM_SERVER;
|
||||
break;
|
||||
case APP_AUTH_TYPE_JWT:
|
||||
case AuthType::JWT:
|
||||
$sdkPlatforms[] = APP_PLATFORM_SERVER;
|
||||
break;
|
||||
case APP_AUTH_TYPE_ADMIN:
|
||||
case AuthType::ADMIN:
|
||||
$sdkPlatforms[] = APP_PLATFORM_CONSOLE;
|
||||
break;
|
||||
}
|
||||
@@ -152,102 +175,140 @@ class OpenAPI3 extends Format
|
||||
$sdkPlatforms[] = APP_PLATFORM_CLIENT;
|
||||
}
|
||||
|
||||
$namespace = $sdk->getNamespace() ?? 'default';
|
||||
|
||||
$temp = [
|
||||
'summary' => $route->getDesc(),
|
||||
'operationId' => $route->getLabel('sdk.namespace', 'default') . ucfirst($method),
|
||||
'tags' => [$route->getLabel('sdk.namespace', 'default')],
|
||||
'operationId' => $namespace . ucfirst($method),
|
||||
'tags' => [$namespace],
|
||||
'description' => ($desc) ? \file_get_contents($desc) : '',
|
||||
'responses' => [],
|
||||
'x-appwrite' => [ // Appwrite related metadata
|
||||
'method' => $method,
|
||||
'weight' => $route->getOrder(),
|
||||
'cookies' => $route->getLabel('sdk.cookies', false),
|
||||
'type' => $route->getLabel('sdk.methodType', ''),
|
||||
'deprecated' => $route->getLabel('sdk.deprecated', false),
|
||||
'demo' => Template::fromCamelCaseToDash($route->getLabel('sdk.namespace', 'default')) . '/' . Template::fromCamelCaseToDash($method) . '.md',
|
||||
'edit' => 'https://github.com/appwrite/appwrite/edit/master' . $route->getLabel('sdk.description', ''),
|
||||
'type' => $sdk->getType()->value ?? '',
|
||||
'deprecated' => $sdk->isDeprecated(),
|
||||
'demo' => Template::fromCamelCaseToDash($namespace) . '/' . Template::fromCamelCaseToDash($method) . '.md',
|
||||
'edit' => 'https://github.com/appwrite/appwrite/edit/master' . $sdk->getDescription() ?? '',
|
||||
'rate-limit' => $route->getLabel('abuse-limit', 0),
|
||||
'rate-time' => $route->getLabel('abuse-time', 3600),
|
||||
'rate-key' => $route->getLabel('abuse-key', 'url:{url},ip:{ip}'),
|
||||
'scope' => $route->getLabel('scope', ''),
|
||||
'platforms' => $sdkPlatforms,
|
||||
'packaging' => $route->getLabel('sdk.packaging', false),
|
||||
'offline-model' => $route->getLabel('sdk.offline.model', ''),
|
||||
'offline-key' => $route->getLabel('sdk.offline.key', ''),
|
||||
'offline-response-key' => $route->getLabel('sdk.offline.response.key', '$id'),
|
||||
'packaging' => $sdk->isPackaging()
|
||||
],
|
||||
];
|
||||
|
||||
foreach ($this->models as $value) {
|
||||
if (\is_array($model)) {
|
||||
$model = \array_map(fn ($m) => $m === $value->getType() ? $value : $m, $model);
|
||||
} else {
|
||||
if ($value->getType() === $model) {
|
||||
$model = $value;
|
||||
break;
|
||||
|
||||
if (!empty($additionalMethods)) {
|
||||
$temp['x-appwrite']['additional-methods'] = [];
|
||||
foreach ($additionalMethods as $method) {
|
||||
/** @var \Appwrite\SDK\Method $method */
|
||||
$additionalMethod = [
|
||||
'name' => $method->getMethodName(),
|
||||
'parameters' => [],
|
||||
'required' => [],
|
||||
'responses' => []
|
||||
];
|
||||
|
||||
foreach ($method->getParameters() as $name => $param) {
|
||||
$additionalMethod['parameters'][] = $name;
|
||||
|
||||
if (!$param['optional']) {
|
||||
$additionalMethod['required'][] = $name;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($method->getResponses() as $response) {
|
||||
/** @var \Appwrite\SDK\Response $response */
|
||||
$additionalMethod['responses'][] = [
|
||||
'code' => $response->getCode(),
|
||||
'model' => '#/components/schemas/' . $response->getModel()
|
||||
];
|
||||
}
|
||||
|
||||
$temp['x-appwrite']['additional-methods'][] = $additionalMethod;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(\is_array($model)) && $model->isNone()) {
|
||||
$temp['responses'][(string)$route->getLabel('sdk.response.code', '500')] = [
|
||||
'description' => in_array($produces, [
|
||||
'image/*',
|
||||
'image/jpeg',
|
||||
'image/gif',
|
||||
'image/png',
|
||||
'image/webp',
|
||||
'image/svg-x',
|
||||
'image/x-icon',
|
||||
'image/bmp',
|
||||
]) ? 'Image' : 'File',
|
||||
];
|
||||
} else {
|
||||
if (\is_array($model)) {
|
||||
$modelDescription = \join(', or ', \array_map(fn ($m) => $m->getName(), $model));
|
||||
// Handle response models
|
||||
foreach ($sdk->getResponses() as $response) {
|
||||
/** @var \Appwrite\SDK\Response $response */
|
||||
$model = $response->getModel();
|
||||
|
||||
// model has multiple possible responses, we will use oneOf
|
||||
foreach ($model as $m) {
|
||||
$usedModels[] = $m->getType();
|
||||
foreach ($this->models as $value) {
|
||||
if (\is_array($model)) {
|
||||
$model = \array_map(fn ($m) => $m === $value->getType() ? $value : $m, $model);
|
||||
} else {
|
||||
if ($value->getType() === $model) {
|
||||
$model = $value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$temp['responses'][(string)$route->getLabel('sdk.response.code', '500')] = [
|
||||
'description' => $modelDescription,
|
||||
'content' => [
|
||||
$produces => [
|
||||
'schema' => [
|
||||
'oneOf' => \array_map(fn ($m) => ['$ref' => '#/components/schemas/' . $m->getType()], $model)
|
||||
],
|
||||
],
|
||||
],
|
||||
if (!(\is_array($model)) && $model->isNone()) {
|
||||
$temp['responses'][(string)$response->getCode() ?? '500'] = [
|
||||
'description' => in_array($produces, [
|
||||
'image/*',
|
||||
'image/jpeg',
|
||||
'image/gif',
|
||||
'image/png',
|
||||
'image/webp',
|
||||
'image/svg-x',
|
||||
'image/x-icon',
|
||||
'image/bmp',
|
||||
]) ? 'Image' : 'File',
|
||||
];
|
||||
} else {
|
||||
// Response definition using one type
|
||||
$usedModels[] = $model->getType();
|
||||
$temp['responses'][(string)$route->getLabel('sdk.response.code', '500')] = [
|
||||
'description' => $model->getName(),
|
||||
'content' => [
|
||||
$produces => [
|
||||
'schema' => [
|
||||
'$ref' => '#/components/schemas/' . $model->getType(),
|
||||
if (\is_array($model)) {
|
||||
$modelDescription = \join(', or ', \array_map(fn ($m) => $m->getName(), $model));
|
||||
|
||||
// model has multiple possible responses, we will use oneOf
|
||||
foreach ($model as $m) {
|
||||
$usedModels[] = $m->getType();
|
||||
}
|
||||
|
||||
$temp['responses'][(string)$response->getCode() ?? '500'] = [
|
||||
'description' => $modelDescription,
|
||||
'content' => [
|
||||
$produces => [
|
||||
'schema' => [
|
||||
'oneOf' => \array_map(fn ($m) => ['$ref' => '#/components/schemas/' . $m->getType()], $model)
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
];
|
||||
} else {
|
||||
// Response definition using one type
|
||||
$usedModels[] = $model->getType();
|
||||
$temp['responses'][(string)$response->getCode() ?? '500'] = [
|
||||
'description' => $model->getName(),
|
||||
'content' => [
|
||||
$produces => [
|
||||
'schema' => [
|
||||
'$ref' => '#/components/schemas/' . $model->getType(),
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($route->getLabel('sdk.response.code', 500) === 204) {
|
||||
$temp['responses'][(string)$route->getLabel('sdk.response.code', '500')]['description'] = 'No content';
|
||||
unset($temp['responses'][(string)$route->getLabel('sdk.response.code', '500')]['schema']);
|
||||
if (($response->getCode() ?? 500) === 204) {
|
||||
$temp['responses'][(string)$response->getCode() ?? '500']['description'] = 'No content';
|
||||
unset($temp['responses'][(string)$response->getCode() ?? '500']['schema']);
|
||||
}
|
||||
}
|
||||
|
||||
if ((!empty($scope))) { // && 'public' != $scope
|
||||
$securities = ['Project' => []];
|
||||
|
||||
foreach ($route->getLabel('sdk.auth', []) as $security) {
|
||||
if (array_key_exists($security, $this->keys)) {
|
||||
$securities[$security] = [];
|
||||
foreach ($sdk->getAuth() as $security) {
|
||||
/** @var \Appwrite\SDK\AuthType $security */
|
||||
if (array_key_exists($security->value, $this->keys)) {
|
||||
$securities[$security->value] = [];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -298,7 +359,7 @@ class OpenAPI3 extends Format
|
||||
$node['schema']['x-example'] = false;
|
||||
break;
|
||||
case 'Appwrite\Utopia\Database\Validator\CustomId':
|
||||
if ($route->getLabel('sdk.methodType', '') === 'upload') {
|
||||
if ($sdk->getType() === MethodType::UPLOAD) {
|
||||
$node['schema']['x-upload-id'] = true;
|
||||
}
|
||||
$node['schema']['type'] = $validator->getType();
|
||||
@@ -323,7 +384,6 @@ class OpenAPI3 extends Format
|
||||
case 'Utopia\Validator\JSON':
|
||||
case 'Utopia\Validator\Mock':
|
||||
case 'Utopia\Validator\Assoc':
|
||||
case 'Appwrite\Functions\Validator\Payload':
|
||||
$param['default'] = (empty($param['default'])) ? new \stdClass() : $param['default'];
|
||||
$node['schema']['type'] = 'object';
|
||||
$node['schema']['x-example'] = '{}';
|
||||
@@ -423,7 +483,7 @@ class OpenAPI3 extends Format
|
||||
$allowed = true;
|
||||
foreach ($this->enumBlacklist as $blacklist) {
|
||||
if (
|
||||
$blacklist['namespace'] == $route->getLabel('sdk.namespace', '')
|
||||
$blacklist['namespace'] == $sdk->getNamespace()
|
||||
&& $blacklist['method'] == $method
|
||||
&& $blacklist['parameter'] == $name
|
||||
) {
|
||||
@@ -434,8 +494,8 @@ class OpenAPI3 extends Format
|
||||
|
||||
if ($allowed) {
|
||||
$node['schema']['enum'] = $validator->getList();
|
||||
$node['schema']['x-enum-name'] = $this->getEnumName($route->getLabel('sdk.namespace', ''), $method, $name);
|
||||
$node['schema']['x-enum-keys'] = $this->getEnumKeys($route->getLabel('sdk.namespace', ''), $method, $name);
|
||||
$node['schema']['x-enum-name'] = $this->getEnumName($sdk->getNamespace() ?? '', $method, $name);
|
||||
$node['schema']['x-enum-keys'] = $this->getEnumKeys($sdk->getNamespace() ?? '', $method, $name);
|
||||
}
|
||||
if ($validator->getType() === 'integer') {
|
||||
$node['format'] = 'int32';
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
namespace Appwrite\Specification\Format;
|
||||
|
||||
use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\MethodType;
|
||||
use Appwrite\Specification\Format;
|
||||
use Appwrite\Template\Template;
|
||||
use Appwrite\Utopia\Response\Model;
|
||||
@@ -34,6 +36,7 @@ class Swagger2 extends Format
|
||||
foreach ($this->models as $m) {
|
||||
if ($m->getType() === $ruleType) {
|
||||
$this->getNestedModels($m, $usedModels);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -45,6 +48,7 @@ class Swagger2 extends Format
|
||||
foreach ($this->models as $m) {
|
||||
if ($m->getType() === $rule['type']) {
|
||||
$this->getNestedModels($m, $usedModels);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -116,27 +120,47 @@ class Swagger2 extends Format
|
||||
/** @var \Utopia\Route $route */
|
||||
$url = \str_replace('/v1', '', $route->getPath());
|
||||
$scope = $route->getLabel('scope', '');
|
||||
$consumes = [$route->getLabel('sdk.request.type', 'application/json')];
|
||||
|
||||
$method = $route->getLabel('sdk.method', \uniqid());
|
||||
$desc = (!empty($route->getLabel('sdk.description', ''))) ? \realpath(__DIR__ . '/../../../../' . $route->getLabel('sdk.description', '')) : null;
|
||||
$produces = $route->getLabel('sdk.response.type', null);
|
||||
$model = $route->getLabel('sdk.response.model', 'none');
|
||||
$routeSecurity = $route->getLabel('sdk.auth', []);
|
||||
/** @var \Appwrite\SDK\Method $sdk */
|
||||
$sdk = $route->getLabel('sdk', false);
|
||||
|
||||
if (empty($sdk)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$additionalMethods = null;
|
||||
if (is_array($sdk)) {
|
||||
$mainSdk = array_shift($sdk);
|
||||
$additionalMethods = $sdk;
|
||||
|
||||
$sdk = $mainSdk;
|
||||
}
|
||||
|
||||
$consumes = [$sdk->getRequestType()];
|
||||
|
||||
$method = $sdk->getMethodName() ?? \uniqid();
|
||||
|
||||
if (!empty($method) && is_array($method)) {
|
||||
$method = array_keys($method)[0];
|
||||
}
|
||||
|
||||
$desc = $sdk->getDescriptionFilePath();
|
||||
$produces = ($sdk->getContentType())->value;
|
||||
$routeSecurity = $sdk->getAuth() ?? [];
|
||||
$sdkPlatforms = [];
|
||||
|
||||
foreach ($routeSecurity as $value) {
|
||||
switch ($value) {
|
||||
case APP_AUTH_TYPE_SESSION:
|
||||
case AuthType::SESSION:
|
||||
$sdkPlatforms[] = APP_PLATFORM_CLIENT;
|
||||
break;
|
||||
case APP_AUTH_TYPE_KEY:
|
||||
case AuthType::KEY:
|
||||
$sdkPlatforms[] = APP_PLATFORM_SERVER;
|
||||
break;
|
||||
case APP_AUTH_TYPE_JWT:
|
||||
case AuthType::JWT:
|
||||
$sdkPlatforms[] = APP_PLATFORM_SERVER;
|
||||
break;
|
||||
case APP_AUTH_TYPE_ADMIN:
|
||||
case AuthType::ADMIN:
|
||||
$sdkPlatforms[] = APP_PLATFORM_CONSOLE;
|
||||
break;
|
||||
}
|
||||
@@ -147,31 +171,30 @@ class Swagger2 extends Format
|
||||
$sdkPlatforms[] = APP_PLATFORM_CLIENT;
|
||||
}
|
||||
|
||||
$namespace = $sdk->getNamespace() ?? 'default';
|
||||
|
||||
$temp = [
|
||||
'summary' => $route->getDesc(),
|
||||
'operationId' => $route->getLabel('sdk.namespace', 'default') . ucfirst($method),
|
||||
'operationId' => $namespace . ucfirst($method),
|
||||
'consumes' => [],
|
||||
'produces' => [],
|
||||
'tags' => [$route->getLabel('sdk.namespace', 'default')],
|
||||
'tags' => [$namespace],
|
||||
'description' => ($desc) ? \file_get_contents($desc) : '',
|
||||
'responses' => [],
|
||||
'x-appwrite' => [ // Appwrite related metadata
|
||||
'method' => $method,
|
||||
'weight' => $route->getOrder(),
|
||||
'cookies' => $route->getLabel('sdk.cookies', false),
|
||||
'type' => $route->getLabel('sdk.methodType', ''),
|
||||
'deprecated' => $route->getLabel('sdk.deprecated', false),
|
||||
'demo' => Template::fromCamelCaseToDash($route->getLabel('sdk.namespace', 'default')) . '/' . Template::fromCamelCaseToDash($method) . '.md',
|
||||
'edit' => 'https://github.com/appwrite/appwrite/edit/master' . $route->getLabel('sdk.description', ''),
|
||||
'type' => $sdk->getType()->value ?? '',
|
||||
'deprecated' => $sdk->isDeprecated(),
|
||||
'demo' => Template::fromCamelCaseToDash($namespace) . '/' . Template::fromCamelCaseToDash($method) . '.md',
|
||||
'edit' => 'https://github.com/appwrite/appwrite/edit/master' . $sdk->getDescription() ?? '',
|
||||
'rate-limit' => $route->getLabel('abuse-limit', 0),
|
||||
'rate-time' => $route->getLabel('abuse-time', 3600),
|
||||
'rate-key' => $route->getLabel('abuse-key', 'url:{url},ip:{ip}'),
|
||||
'scope' => $route->getLabel('scope', ''),
|
||||
'platforms' => $sdkPlatforms,
|
||||
'packaging' => $route->getLabel('sdk.packaging', false),
|
||||
'offline-model' => $route->getLabel('sdk.offline.model', ''),
|
||||
'offline-key' => $route->getLabel('sdk.offline.key', ''),
|
||||
'offline-response-key' => $route->getLabel('sdk.offline.response.key', '$id'),
|
||||
'packaging' => $sdk->isPackaging()
|
||||
],
|
||||
];
|
||||
|
||||
@@ -179,71 +202,111 @@ class Swagger2 extends Format
|
||||
$temp['produces'][] = $produces;
|
||||
}
|
||||
|
||||
foreach ($this->models as $value) {
|
||||
if (\is_array($model)) {
|
||||
$model = \array_map(fn ($m) => $m === $value->getType() ? $value : $m, $model);
|
||||
} else {
|
||||
if ($value->getType() === $model) {
|
||||
$model = $value;
|
||||
break;
|
||||
if (!empty($additionalMethods)) {
|
||||
$temp['x-appwrite']['additional-methods'] = [];
|
||||
foreach ($additionalMethods as $method) {
|
||||
/** @var \Appwrite\SDK\Method $method */
|
||||
$additionalMethod = [
|
||||
'name' => $method->getMethodName(),
|
||||
'parameters' => [],
|
||||
'required' => [],
|
||||
'responses' => [],
|
||||
'description' => $method->getDescription(),
|
||||
];
|
||||
|
||||
foreach ($method->getParameters() as $name => $param) {
|
||||
$additionalMethod['parameters'][] = $name;
|
||||
|
||||
if (!$param['optional']) {
|
||||
$additionalMethod['required'][] = $name;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($method->getResponses() as $response) {
|
||||
/** @var \Appwrite\SDK\Response $response */
|
||||
$additionalMethod['responses'][] = [
|
||||
'code' => $response->getCode(),
|
||||
'model' => '#/definitions/' . $response->getModel()
|
||||
];
|
||||
}
|
||||
|
||||
$temp['x-appwrite']['additional-methods'][] = $additionalMethod;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(\is_array($model)) && $model->isNone()) {
|
||||
$temp['responses'][(string)$route->getLabel('sdk.response.code', '500')] = [
|
||||
'description' => in_array($produces, [
|
||||
'image/*',
|
||||
'image/jpeg',
|
||||
'image/gif',
|
||||
'image/png',
|
||||
'image/webp',
|
||||
'image/svg-x',
|
||||
'image/x-icon',
|
||||
'image/bmp',
|
||||
]) ? 'Image' : 'File',
|
||||
'schema' => [
|
||||
'type' => 'file'
|
||||
],
|
||||
];
|
||||
} else {
|
||||
if (\is_array($model)) {
|
||||
$modelDescription = \join(', or ', \array_map(fn ($m) => $m->getName(), $model));
|
||||
// model has multiple possible responses, we will use oneOf
|
||||
foreach ($model as $m) {
|
||||
$usedModels[] = $m->getType();
|
||||
// Handle Responses
|
||||
|
||||
foreach ($sdk->getResponses() as $response) {
|
||||
/** @var \Appwrite\SDK\Response $response */
|
||||
$model = $response->getModel();
|
||||
|
||||
foreach ($this->models as $value) {
|
||||
if (\is_array($model)) {
|
||||
$model = \array_map(fn ($m) => $m === $value->getType() ? $value : $m, $model);
|
||||
} else {
|
||||
if ($value->getType() === $model) {
|
||||
$model = $value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
$temp['responses'][(string)$route->getLabel('sdk.response.code', '500')] = [
|
||||
'description' => $modelDescription,
|
||||
}
|
||||
|
||||
if (!(\is_array($model)) && $model->isNone()) {
|
||||
$temp['responses'][(string)$response->getCode() ?? '500'] = [
|
||||
'description' => in_array($produces, [
|
||||
'image/*',
|
||||
'image/jpeg',
|
||||
'image/gif',
|
||||
'image/png',
|
||||
'image/webp',
|
||||
'image/svg-x',
|
||||
'image/x-icon',
|
||||
'image/bmp',
|
||||
]) ? 'Image' : 'File',
|
||||
'schema' => [
|
||||
'x-oneOf' => \array_map(function ($m) {
|
||||
return ['$ref' => '#/definitions/' . $m->getType()];
|
||||
}, $model)
|
||||
'type' => 'file'
|
||||
],
|
||||
];
|
||||
} else {
|
||||
// Response definition using one type
|
||||
$usedModels[] = $model->getType();
|
||||
$temp['responses'][(string)$route->getLabel('sdk.response.code', '500')] = [
|
||||
'description' => $model->getName(),
|
||||
'schema' => [
|
||||
'$ref' => '#/definitions/' . $model->getType(),
|
||||
],
|
||||
];
|
||||
if (\is_array($model)) {
|
||||
$modelDescription = \join(', or ', \array_map(fn ($m) => $m->getName(), $model));
|
||||
// model has multiple possible responses, we will use oneOf
|
||||
foreach ($model as $m) {
|
||||
$usedModels[] = $m->getType();
|
||||
}
|
||||
$temp['responses'][(string)$response->getCode() ?? '500'] = [
|
||||
'description' => $modelDescription,
|
||||
'schema' => [
|
||||
'x-oneOf' => \array_map(function ($m) {
|
||||
return ['$ref' => '#/definitions/' . $m->getType()];
|
||||
}, $model)
|
||||
],
|
||||
];
|
||||
} else {
|
||||
// Response definition using one type
|
||||
$usedModels[] = $model->getType();
|
||||
$temp['responses'][(string)$response->getCode() ?? '500'] = [
|
||||
'description' => $model->getName(),
|
||||
'schema' => [
|
||||
'$ref' => '#/definitions/' . $model->getType(),
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (in_array($route->getLabel('sdk.response.code', 500), [204, 301, 302, 308], true)) {
|
||||
$temp['responses'][(string)$route->getLabel('sdk.response.code', '500')]['description'] = 'No content';
|
||||
unset($temp['responses'][(string)$route->getLabel('sdk.response.code', '500')]['schema']);
|
||||
if (in_array($response->getCode() ?? 500, [204, 301, 302, 308], true)) {
|
||||
$temp['responses'][(string)$response->getCode() ?? '500']['description'] = 'No content';
|
||||
unset($temp['responses'][(string)$response->getCode() ?? '500']['schema']);
|
||||
}
|
||||
}
|
||||
|
||||
if ((!empty($scope))) { // && 'public' != $scope
|
||||
$securities = ['Project' => []];
|
||||
|
||||
foreach ($route->getLabel('sdk.auth', []) as $security) {
|
||||
if (array_key_exists($security, $this->keys)) {
|
||||
$securities[$security] = [];
|
||||
foreach ($sdk->getAuth() as $security) {
|
||||
/** @var \Appwrite\SDK\AuthType $security */
|
||||
if (array_key_exists($security->value, $this->keys)) {
|
||||
$securities[$security->value] = [];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -264,7 +327,7 @@ class Swagger2 extends Format
|
||||
|
||||
$parameters = \array_merge(
|
||||
$route->getParams(),
|
||||
$route->getLabel('sdk.parameters', []),
|
||||
$sdk->getAdditionalParameters() ?? [],
|
||||
);
|
||||
|
||||
foreach ($parameters as $name => $param) { // Set params
|
||||
@@ -314,7 +377,7 @@ class Swagger2 extends Format
|
||||
$node['x-example'] = false;
|
||||
break;
|
||||
case 'Appwrite\Utopia\Database\Validator\CustomId':
|
||||
if ($route->getLabel('sdk.methodType', '') === 'upload') {
|
||||
if ($sdk->getType() === MethodType::UPLOAD) {
|
||||
$node['x-upload-id'] = true;
|
||||
}
|
||||
$node['type'] = $validator->getType();
|
||||
@@ -347,7 +410,6 @@ class Swagger2 extends Format
|
||||
case 'Utopia\Validator\JSON':
|
||||
case 'Utopia\Validator\Mock':
|
||||
case 'Utopia\Validator\Assoc':
|
||||
case 'Appwrite\Functions\Validator\Payload':
|
||||
$node['type'] = 'object';
|
||||
$node['default'] = (empty($param['default'])) ? new \stdClass() : $param['default'];
|
||||
$node['x-example'] = '{}';
|
||||
@@ -422,7 +484,7 @@ class Swagger2 extends Format
|
||||
//Iterate the blackList. If it matches with the current one, then it is blackListed
|
||||
$allowed = true;
|
||||
foreach ($this->enumBlacklist as $blacklist) {
|
||||
if ($blacklist['namespace'] == $route->getLabel('sdk.namespace', '') && $blacklist['method'] == $method && $blacklist['parameter'] == $name) {
|
||||
if ($blacklist['namespace'] == $namespace && $blacklist['method'] == $method && $blacklist['parameter'] == $name) {
|
||||
$allowed = false;
|
||||
break;
|
||||
}
|
||||
@@ -430,8 +492,8 @@ class Swagger2 extends Format
|
||||
|
||||
if ($allowed && $validator->getType() === 'string') {
|
||||
$node['enum'] = $validator->getList();
|
||||
$node['x-enum-name'] = $this->getEnumName($route->getLabel('sdk.namespace', ''), $method, $name);
|
||||
$node['x-enum-keys'] = $this->getEnumKeys($route->getLabel('sdk.namespace', ''), $method, $name);
|
||||
$node['x-enum-name'] = $this->getEnumName($namespace, $method, $name);
|
||||
$node['x-enum-keys'] = $this->getEnumKeys($namespace, $method, $name);
|
||||
}
|
||||
|
||||
if ($validator->getType() === 'integer') {
|
||||
|
||||
@@ -28,8 +28,30 @@ class Request extends UtopiaRequest
|
||||
$parameters = parent::getParams();
|
||||
|
||||
if ($this->hasFilters() && self::hasRoute()) {
|
||||
$method = self::getRoute()->getLabel('sdk.method', 'unknown');
|
||||
$endpointIdentifier = self::getRoute()->getLabel('sdk.namespace', 'unknown') . '.' . $method;
|
||||
$methods = self::getRoute()->getLabel('sdk', null);
|
||||
|
||||
if (!\is_array($methods)) {
|
||||
$methods = [$methods];
|
||||
}
|
||||
|
||||
$params = [];
|
||||
|
||||
foreach ($methods as $method) {
|
||||
/** @var \Appwrite\SDK\Method $method */
|
||||
if (empty($method)) {
|
||||
$endpointIdentifier = 'unknown.unknown';
|
||||
} else {
|
||||
$endpointIdentifier = $method->getNamespace() . '.' . $method->getMethodName();
|
||||
}
|
||||
|
||||
$params += $method->getParameters();
|
||||
}
|
||||
|
||||
if (!empty($params)) {
|
||||
$parameters = array_filter($parameters, function ($key) use ($params) {
|
||||
return array_key_exists($key, $params);
|
||||
}, \ARRAY_FILTER_USE_KEY);
|
||||
}
|
||||
|
||||
foreach ($this->getFilters() as $filter) {
|
||||
$parameters = $filter->parse($parameters, $endpointIdentifier);
|
||||
|
||||
Reference in New Issue
Block a user