fix: add nested models to specifications
This commit is contained in:
parent
08704e8c93
commit
056e0d79ed
5 changed files with 173 additions and 244 deletions
|
@ -123,105 +123,94 @@ $cli
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
foreach (['swagger2', 'open-api3'] as $format) {
|
foreach ($platforms as $platform) {
|
||||||
foreach ($platforms as $platform) {
|
$routes = [];
|
||||||
$routes = [];
|
$models = [];
|
||||||
$models = [];
|
$services = [];
|
||||||
$services = [];
|
|
||||||
|
|
||||||
foreach ($appRoutes as $key => $method) {
|
foreach ($appRoutes as $key => $method) {
|
||||||
foreach ($method as $route) {
|
foreach ($method as $route) {
|
||||||
/** @var \Utopia\Route $route */
|
/** @var \Utopia\Route $route */
|
||||||
$routeSecurity = $route->getLabel('sdk.auth', []);
|
$routeSecurity = $route->getLabel('sdk.auth', []);
|
||||||
$sdkPlatofrms = [];
|
$sdkPlatofrms = [];
|
||||||
|
|
||||||
foreach ($routeSecurity as $value) {
|
foreach ($routeSecurity as $value) {
|
||||||
switch ($value) {
|
switch ($value) {
|
||||||
case APP_AUTH_TYPE_SESSION:
|
case APP_AUTH_TYPE_SESSION:
|
||||||
$sdkPlatofrms[] = APP_PLATFORM_CLIENT;
|
$sdkPlatofrms[] = APP_PLATFORM_CLIENT;
|
||||||
break;
|
break;
|
||||||
case APP_AUTH_TYPE_KEY:
|
case APP_AUTH_TYPE_KEY:
|
||||||
$sdkPlatofrms[] = APP_PLATFORM_SERVER;
|
$sdkPlatofrms[] = APP_PLATFORM_SERVER;
|
||||||
break;
|
break;
|
||||||
case APP_AUTH_TYPE_JWT:
|
case APP_AUTH_TYPE_JWT:
|
||||||
$sdkPlatofrms[] = APP_PLATFORM_SERVER;
|
$sdkPlatofrms[] = APP_PLATFORM_SERVER;
|
||||||
break;
|
break;
|
||||||
case APP_AUTH_TYPE_ADMIN:
|
case APP_AUTH_TYPE_ADMIN:
|
||||||
$sdkPlatofrms[] = APP_PLATFORM_CONSOLE;
|
$sdkPlatofrms[] = APP_PLATFORM_CONSOLE;
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (empty($routeSecurity)) {
|
|
||||||
$sdkPlatofrms[] = APP_PLATFORM_CLIENT;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$route->getLabel('docs', true)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($route->getLabel('sdk.mock', false) && !$mocks) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$route->getLabel('sdk.mock', false) && $mocks) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (empty($route->getLabel('sdk.namespace', null))) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($platform !== APP_PLATFORM_CONSOLE && !\in_array($platforms[$platform], $sdkPlatofrms)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$routes[] = $route;
|
|
||||||
$modelLabel = $route->getLabel('sdk.response.model', 'none');
|
|
||||||
\is_array($modelLabel) ? \array_map(function ($m) use ($response) {
|
|
||||||
return $response->getModel($m);
|
|
||||||
}, $modelLabel) : $response->getModel($modelLabel);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
foreach (Config::getParam('services', []) as $service) {
|
if (empty($routeSecurity)) {
|
||||||
if (
|
$sdkPlatofrms[] = APP_PLATFORM_CLIENT;
|
||||||
!isset($service['docs']) // Skip service if not part of the public API
|
}
|
||||||
|| !isset($service['sdk'])
|
|
||||||
|| !$service['docs']
|
if (!$route->getLabel('docs', true)) {
|
||||||
|| !$service['sdk']
|
|
||||||
) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$services[] = [
|
if ($route->getLabel('sdk.mock', false) && !$mocks) {
|
||||||
'name' => $service['key'] ?? '',
|
continue;
|
||||||
'description' => $service['subtitle'] ?? '',
|
|
||||||
'x-globalAttributes' => $service['globalAttributes'] ?? [],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
$models = $response->getModels();
|
|
||||||
|
|
||||||
foreach ($models as $key => $value) {
|
|
||||||
if ($platform !== APP_PLATFORM_CONSOLE && !$value->isPublic()) {
|
|
||||||
unset($models[$key]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!$route->getLabel('sdk.mock', false) && $mocks) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($route->getLabel('sdk.namespace', null))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($platform !== APP_PLATFORM_CONSOLE && !\in_array($platforms[$platform], $sdkPlatofrms)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$routes[] = $route;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (Config::getParam('services', []) as $service) {
|
||||||
|
if (
|
||||||
|
!isset($service['docs']) // Skip service if not part of the public API
|
||||||
|
|| !isset($service['sdk'])
|
||||||
|
|| !$service['docs']
|
||||||
|
|| !$service['sdk']
|
||||||
|
) {
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch ($format) {
|
$services[] = [
|
||||||
case 'swagger2':
|
'name' => $service['key'] ?? '',
|
||||||
$formatInstance = new Swagger2(new App('UTC'), $services, $routes, $models, $keys[$platform], $authCounts[$platform] ?? 0);
|
'description' => $service['subtitle'] ?? '',
|
||||||
break;
|
'x-globalAttributes' => $service['globalAttributes'] ?? [],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
case 'open-api3':
|
$models = $response->getModels();
|
||||||
$formatInstance = new OpenAPI3(new App('UTC'), $services, $routes, $models, $keys[$platform], $authCounts[$platform] ?? 0);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
foreach ($models as $key => $value) {
|
||||||
throw new Exception('Format not found: ' . $format);
|
if ($platform !== APP_PLATFORM_CONSOLE && !$value->isPublic()) {
|
||||||
break;
|
unset($models[$key]);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
// var_dump($models);
|
||||||
|
$arguments = [new App('UTC'), $services, $routes, $models, $keys[$platform], $authCounts[$platform] ?? 0];
|
||||||
|
foreach (['swagger2', 'open-api3'] as $format) {
|
||||||
|
$formatInstance = match($format) {
|
||||||
|
'swagger2' => new Swagger2(...$arguments),
|
||||||
|
'open-api3' => new OpenAPI3(...$arguments),
|
||||||
|
default => throw new Exception('Format not found: ' . $format)
|
||||||
|
};
|
||||||
|
|
||||||
$specs = new Specification($formatInstance);
|
$specs = new Specification($formatInstance);
|
||||||
$endpoint = App::getEnv('_APP_HOME', '[HOSTNAME]');
|
$endpoint = App::getEnv('_APP_HOME', '[HOSTNAME]');
|
||||||
|
|
|
@ -8,40 +8,22 @@ use Appwrite\Utopia\Response\Model;
|
||||||
|
|
||||||
abstract class Format
|
abstract class Format
|
||||||
{
|
{
|
||||||
/**
|
protected App $app;
|
||||||
* @var App
|
|
||||||
*/
|
|
||||||
protected $app;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $services;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var Route[]
|
* @var Route[]
|
||||||
*/
|
*/
|
||||||
protected $routes;
|
protected array $routes;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var Model[]
|
* @var Model[]
|
||||||
*/
|
*/
|
||||||
protected $models;
|
protected array $models;
|
||||||
|
|
||||||
/**
|
protected array $services;
|
||||||
* @var array
|
protected array $keys;
|
||||||
*/
|
protected int $authCount;
|
||||||
protected $keys;
|
protected array $params = [
|
||||||
|
|
||||||
/**
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
protected $authCount;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $params = [
|
|
||||||
'name' => '',
|
'name' => '',
|
||||||
'description' => '',
|
'description' => '',
|
||||||
'endpoint' => 'https://localhost',
|
'endpoint' => 'https://localhost',
|
||||||
|
@ -56,14 +38,6 @@ abstract class Format
|
||||||
'license.url' => '',
|
'license.url' => '',
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
|
||||||
* @param App $app
|
|
||||||
* @param array $services
|
|
||||||
* @param Route[] $routes
|
|
||||||
* @param Model[] $models
|
|
||||||
* @param array $keys
|
|
||||||
* @param int $authCount
|
|
||||||
*/
|
|
||||||
public function __construct(App $app, array $services, array $routes, array $models, array $keys, int $authCount)
|
public function __construct(App $app, array $services, array $routes, array $models, array $keys, int $authCount)
|
||||||
{
|
{
|
||||||
$this->app = $app;
|
$this->app = $app;
|
||||||
|
@ -121,10 +95,6 @@ abstract class Format
|
||||||
*/
|
*/
|
||||||
public function getParam(string $key, string $default = ''): string
|
public function getParam(string $key, string $default = ''): string
|
||||||
{
|
{
|
||||||
if (!isset($this->params[$key])) {
|
return $this->params[$key] ?? $default;
|
||||||
return $default;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->params[$key];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,35 +4,40 @@ namespace Appwrite\Specification\Format;
|
||||||
|
|
||||||
use Appwrite\Specification\Format;
|
use Appwrite\Specification\Format;
|
||||||
use Appwrite\Template\Template;
|
use Appwrite\Template\Template;
|
||||||
|
use Appwrite\Utopia\Response\Model;
|
||||||
use Utopia\Validator;
|
use Utopia\Validator;
|
||||||
|
|
||||||
class OpenAPI3 extends Format
|
class OpenAPI3 extends Format
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* Get Name.
|
|
||||||
*
|
|
||||||
* Get format name
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getName(): string
|
public function getName(): string
|
||||||
{
|
{
|
||||||
return 'Open API 3';
|
return 'Open API 3';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
protected function getNestedModels(Model $model, array &$usedModels): void
|
||||||
* Parse
|
{
|
||||||
*
|
foreach ($model->getRules() as $rule) {
|
||||||
* Parses Appwrite App to given format
|
if (
|
||||||
*
|
in_array($model->getType(), $usedModels)
|
||||||
* @return array
|
&& !in_array($rule['type'], ['string', 'integer', 'boolean', 'json', 'float', 'double'])
|
||||||
*/
|
) {
|
||||||
|
$usedModels[] = $rule['type'];
|
||||||
|
foreach ($this->models as $m) {
|
||||||
|
if ($m->getType() === $rule['type']) {
|
||||||
|
$this->getNestedModels($m, $usedModels);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function parse(): array
|
public function parse(): array
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Specifications (v3.0.0):
|
* Specifications (v3.0.0):
|
||||||
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md
|
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md
|
||||||
*/
|
*/
|
||||||
$output = [
|
$output = [
|
||||||
'openapi' => '3.0.0',
|
'openapi' => '3.0.0',
|
||||||
'info' => [
|
'info' => [
|
||||||
|
@ -89,7 +94,7 @@ class OpenAPI3 extends Format
|
||||||
|
|
||||||
$usedModels = [];
|
$usedModels = [];
|
||||||
|
|
||||||
foreach ($this->routes as $route) { /** @var \Utopia\Route $route */
|
foreach ($this->routes as $route) {
|
||||||
$url = \str_replace('/v1', '', $route->getPath());
|
$url = \str_replace('/v1', '', $route->getPath());
|
||||||
$scope = $route->getLabel('scope', '');
|
$scope = $route->getLabel('scope', '');
|
||||||
$hide = $route->getLabel('sdk.hide', false);
|
$hide = $route->getLabel('sdk.hide', false);
|
||||||
|
@ -104,34 +109,32 @@ class OpenAPI3 extends Format
|
||||||
$produces = $route->getLabel('sdk.response.type', null);
|
$produces = $route->getLabel('sdk.response.type', null);
|
||||||
$model = $route->getLabel('sdk.response.model', 'none');
|
$model = $route->getLabel('sdk.response.model', 'none');
|
||||||
$routeSecurity = $route->getLabel('sdk.auth', []);
|
$routeSecurity = $route->getLabel('sdk.auth', []);
|
||||||
$sdkPlatofrms = [];
|
$sdkPlatforms = [];
|
||||||
|
|
||||||
foreach ($routeSecurity as $value) {
|
foreach ($routeSecurity as $value) {
|
||||||
switch ($value) {
|
switch ($value) {
|
||||||
case APP_AUTH_TYPE_SESSION:
|
case APP_AUTH_TYPE_SESSION:
|
||||||
$sdkPlatofrms[] = APP_PLATFORM_CLIENT;
|
$sdkPlatforms[] = APP_PLATFORM_CLIENT;
|
||||||
break;
|
break;
|
||||||
case APP_AUTH_TYPE_KEY:
|
case APP_AUTH_TYPE_KEY:
|
||||||
$sdkPlatofrms[] = APP_PLATFORM_SERVER;
|
$sdkPlatforms[] = APP_PLATFORM_SERVER;
|
||||||
break;
|
break;
|
||||||
case APP_AUTH_TYPE_JWT:
|
case APP_AUTH_TYPE_JWT:
|
||||||
$sdkPlatofrms[] = APP_PLATFORM_SERVER;
|
$sdkPlatforms[] = APP_PLATFORM_SERVER;
|
||||||
break;
|
break;
|
||||||
case APP_AUTH_TYPE_ADMIN:
|
case APP_AUTH_TYPE_ADMIN:
|
||||||
$sdkPlatofrms[] = APP_PLATFORM_CONSOLE;
|
$sdkPlatforms[] = APP_PLATFORM_CONSOLE;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (empty($routeSecurity)) {
|
if (empty($routeSecurity)) {
|
||||||
$sdkPlatofrms[] = APP_PLATFORM_CLIENT;
|
$sdkPlatforms[] = APP_PLATFORM_CLIENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
$temp = [
|
$temp = [
|
||||||
'summary' => $route->getDesc(),
|
'summary' => $route->getDesc(),
|
||||||
'operationId' => $route->getLabel('sdk.namespace', 'default') . ucfirst($id),
|
'operationId' => $route->getLabel('sdk.namespace', 'default') . ucfirst($id),
|
||||||
// 'consumes' => [],
|
|
||||||
// 'produces' => [$produces],
|
|
||||||
'tags' => [$route->getLabel('sdk.namespace', 'default')],
|
'tags' => [$route->getLabel('sdk.namespace', 'default')],
|
||||||
'description' => ($desc) ? \file_get_contents($desc) : '',
|
'description' => ($desc) ? \file_get_contents($desc) : '',
|
||||||
'responses' => [],
|
'responses' => [],
|
||||||
|
@ -146,20 +149,14 @@ class OpenAPI3 extends Format
|
||||||
'rate-time' => $route->getLabel('abuse-time', 3600),
|
'rate-time' => $route->getLabel('abuse-time', 3600),
|
||||||
'rate-key' => $route->getLabel('abuse-key', 'url:{url},ip:{ip}'),
|
'rate-key' => $route->getLabel('abuse-key', 'url:{url},ip:{ip}'),
|
||||||
'scope' => $route->getLabel('scope', ''),
|
'scope' => $route->getLabel('scope', ''),
|
||||||
'platforms' => $sdkPlatofrms,
|
'platforms' => $sdkPlatforms,
|
||||||
'packaging' => $route->getLabel('sdk.packaging', false),
|
'packaging' => $route->getLabel('sdk.packaging', false),
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
foreach ($this->models as $key => $value) {
|
foreach ($this->models as $value) {
|
||||||
if (\is_array($model)) {
|
if (\is_array($model)) {
|
||||||
$model = \array_map(function ($m) use ($value) {
|
$model = \array_map(fn ($m) => $m === $value->getType() ? $value : $m, $model);
|
||||||
if ($m === $value->getType()) {
|
|
||||||
return $value;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $m;
|
|
||||||
}, $model);
|
|
||||||
} else {
|
} else {
|
||||||
if ($value->getType() === $model) {
|
if ($value->getType() === $model) {
|
||||||
$model = $value;
|
$model = $value;
|
||||||
|
@ -168,9 +165,9 @@ class OpenAPI3 extends Format
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(\is_array($model)) && $model->isNone()) {
|
if (!(\is_array($model)) && $model->isNone()) {
|
||||||
$temp['responses'][(string)$route->getLabel('sdk.response.code', '500')] = [
|
$temp['responses'][(string)$route->getLabel('sdk.response.code', '500')] = [
|
||||||
'description' => (in_array($produces, [
|
'description' => in_array($produces, [
|
||||||
'image/*',
|
'image/*',
|
||||||
'image/jpeg',
|
'image/jpeg',
|
||||||
'image/gif',
|
'image/gif',
|
||||||
|
@ -179,16 +176,11 @@ class OpenAPI3 extends Format
|
||||||
'image/svg-x',
|
'image/svg-x',
|
||||||
'image/x-icon',
|
'image/x-icon',
|
||||||
'image/bmp',
|
'image/bmp',
|
||||||
])) ? 'Image' : 'File',
|
]) ? 'Image' : 'File',
|
||||||
// 'schema' => [
|
|
||||||
// 'type' => 'file'
|
|
||||||
// ],
|
|
||||||
];
|
];
|
||||||
} else {
|
} else {
|
||||||
if (\is_array($model)) {
|
if (\is_array($model)) {
|
||||||
$modelDescription = \join(', or ', \array_map(function ($m) {
|
$modelDescription = \join(', or ', \array_map(fn ($m) => $m->getName(), $model));
|
||||||
return $m->getName();
|
|
||||||
}, $model));
|
|
||||||
|
|
||||||
// model has multiple possible responses, we will use oneOf
|
// model has multiple possible responses, we will use oneOf
|
||||||
foreach ($model as $m) {
|
foreach ($model as $m) {
|
||||||
|
@ -200,9 +192,7 @@ class OpenAPI3 extends Format
|
||||||
'content' => [
|
'content' => [
|
||||||
$produces => [
|
$produces => [
|
||||||
'schema' => [
|
'schema' => [
|
||||||
'oneOf' => \array_map(function ($m) {
|
'oneOf' => \array_map(fn ($m) => ['$ref' => '#/components/schemas/' . $m->getType()], $model)
|
||||||
return ['$ref' => '#/components/schemas/' . $m->getType()];
|
|
||||||
}, $model)
|
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
@ -255,7 +245,10 @@ class OpenAPI3 extends Format
|
||||||
$bodyRequired = [];
|
$bodyRequired = [];
|
||||||
|
|
||||||
foreach ($route->getParams() as $name => $param) { // Set params
|
foreach ($route->getParams() as $name => $param) { // Set params
|
||||||
$validator = (\is_callable($param['validator'])) ? call_user_func_array($param['validator'], $this->app->getResources($param['injections'])) : $param['validator']; /* @var $validator \Utopia\Validator */
|
/**
|
||||||
|
* @var \Utopia\Validator $validator
|
||||||
|
*/
|
||||||
|
$validator = (\is_callable($param['validator'])) ? call_user_func_array($param['validator'], $this->app->getResources($param['injections'])) : $param['validator'];
|
||||||
|
|
||||||
$node = [
|
$node = [
|
||||||
'name' => $name,
|
'name' => $name,
|
||||||
|
@ -329,7 +322,8 @@ class OpenAPI3 extends Format
|
||||||
$node['schema']['format'] = 'password';
|
$node['schema']['format'] = 'password';
|
||||||
$node['schema']['x-example'] = 'password';
|
$node['schema']['x-example'] = 'password';
|
||||||
break;
|
break;
|
||||||
case 'Utopia\Validator\Range': /** @var \Utopia\Validator\Range $validator */
|
case 'Utopia\Validator\Range':
|
||||||
|
/** @var \Utopia\Validator\Range $validator */
|
||||||
$node['schema']['type'] = $validator->getType() === Validator::TYPE_FLOAT ? 'number' : $validator->getType();
|
$node['schema']['type'] = $validator->getType() === Validator::TYPE_FLOAT ? 'number' : $validator->getType();
|
||||||
$node['schema']['format'] = $validator->getType() == Validator::TYPE_INTEGER ? 'int32' : 'float';
|
$node['schema']['format'] = $validator->getType() == Validator::TYPE_INTEGER ? 'int32' : 'float';
|
||||||
$node['schema']['x-example'] = $validator->getMin();
|
$node['schema']['x-example'] = $validator->getMin();
|
||||||
|
@ -351,7 +345,8 @@ class OpenAPI3 extends Format
|
||||||
$node['schema']['format'] = 'url';
|
$node['schema']['format'] = 'url';
|
||||||
$node['schema']['x-example'] = 'https://example.com';
|
$node['schema']['x-example'] = 'https://example.com';
|
||||||
break;
|
break;
|
||||||
case 'Utopia\Validator\WhiteList': /** @var \Utopia\Validator\WhiteList $validator */
|
case 'Utopia\Validator\WhiteList':
|
||||||
|
/** @var \Utopia\Validator\WhiteList $validator */
|
||||||
$node['schema']['type'] = $validator->getType();
|
$node['schema']['type'] = $validator->getType();
|
||||||
$node['schema']['x-example'] = $validator->getList()[0];
|
$node['schema']['x-example'] = $validator->getList()[0];
|
||||||
|
|
||||||
|
@ -413,20 +408,11 @@ class OpenAPI3 extends Format
|
||||||
$temp['requestBody'] = $body;
|
$temp['requestBody'] = $body;
|
||||||
}
|
}
|
||||||
|
|
||||||
//$temp['consumes'] = $consumes;
|
|
||||||
|
|
||||||
$output['paths'][$url][\strtolower($route->getMethod())] = $temp;
|
$output['paths'][$url][\strtolower($route->getMethod())] = $temp;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($this->models as $model) {
|
foreach ($this->models as $model) {
|
||||||
foreach ($model->getRules() as $rule) {
|
$this->getNestedModels($model, $usedModels);
|
||||||
if (
|
|
||||||
in_array($model->getType(), $usedModels)
|
|
||||||
&& !in_array($rule['type'], ['string', 'integer', 'boolean', 'json', 'float'])
|
|
||||||
) {
|
|
||||||
$usedModels[] = $rule['type'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($this->models as $model) {
|
foreach ($this->models as $model) {
|
||||||
|
|
|
@ -4,34 +4,39 @@ namespace Appwrite\Specification\Format;
|
||||||
|
|
||||||
use Appwrite\Specification\Format;
|
use Appwrite\Specification\Format;
|
||||||
use Appwrite\Template\Template;
|
use Appwrite\Template\Template;
|
||||||
|
use Appwrite\Utopia\Response\Model;
|
||||||
use Utopia\Validator;
|
use Utopia\Validator;
|
||||||
|
|
||||||
class Swagger2 extends Format
|
class Swagger2 extends Format
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* Get Name.
|
|
||||||
*
|
|
||||||
* Get format name
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getName(): string
|
public function getName(): string
|
||||||
{
|
{
|
||||||
return 'Swagger 2';
|
return 'Swagger 2';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
protected function getNestedModels(Model $model, array &$usedModels): void
|
||||||
* Parse
|
{
|
||||||
*
|
foreach ($model->getRules() as $rule) {
|
||||||
* Parses Appwrite App to given format
|
if (
|
||||||
*
|
in_array($model->getType(), $usedModels)
|
||||||
* @return array
|
&& !in_array($rule['type'], ['string', 'integer', 'boolean', 'json', 'float', 'double'])
|
||||||
*/
|
) {
|
||||||
|
$usedModels[] = $rule['type'];
|
||||||
|
foreach ($this->models as $m) {
|
||||||
|
if ($m->getType() === $rule['type']) {
|
||||||
|
$this->getNestedModels($m, $usedModels);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function parse(): array
|
public function parse(): array
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* Specifications (v3.0.0):
|
* Specifications (v2.0):
|
||||||
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md
|
* https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md
|
||||||
*/
|
*/
|
||||||
$output = [
|
$output = [
|
||||||
'swagger' => '2.0',
|
'swagger' => '2.0',
|
||||||
|
@ -87,7 +92,8 @@ class Swagger2 extends Format
|
||||||
|
|
||||||
$usedModels = [];
|
$usedModels = [];
|
||||||
|
|
||||||
foreach ($this->routes as $route) { /** @var \Utopia\Route $route */
|
foreach ($this->routes as $route) {
|
||||||
|
/** @var \Utopia\Route $route */
|
||||||
$url = \str_replace('/v1', '', $route->getPath());
|
$url = \str_replace('/v1', '', $route->getPath());
|
||||||
$scope = $route->getLabel('scope', '');
|
$scope = $route->getLabel('scope', '');
|
||||||
$hide = $route->getLabel('sdk.hide', false);
|
$hide = $route->getLabel('sdk.hide', false);
|
||||||
|
@ -102,27 +108,27 @@ class Swagger2 extends Format
|
||||||
$produces = $route->getLabel('sdk.response.type', null);
|
$produces = $route->getLabel('sdk.response.type', null);
|
||||||
$model = $route->getLabel('sdk.response.model', 'none');
|
$model = $route->getLabel('sdk.response.model', 'none');
|
||||||
$routeSecurity = $route->getLabel('sdk.auth', []);
|
$routeSecurity = $route->getLabel('sdk.auth', []);
|
||||||
$sdkPlatofrms = [];
|
$sdkPlatforms = [];
|
||||||
|
|
||||||
foreach ($routeSecurity as $value) {
|
foreach ($routeSecurity as $value) {
|
||||||
switch ($value) {
|
switch ($value) {
|
||||||
case APP_AUTH_TYPE_SESSION:
|
case APP_AUTH_TYPE_SESSION:
|
||||||
$sdkPlatofrms[] = APP_PLATFORM_CLIENT;
|
$sdkPlatforms[] = APP_PLATFORM_CLIENT;
|
||||||
break;
|
break;
|
||||||
case APP_AUTH_TYPE_KEY:
|
case APP_AUTH_TYPE_KEY:
|
||||||
$sdkPlatofrms[] = APP_PLATFORM_SERVER;
|
$sdkPlatforms[] = APP_PLATFORM_SERVER;
|
||||||
break;
|
break;
|
||||||
case APP_AUTH_TYPE_JWT:
|
case APP_AUTH_TYPE_JWT:
|
||||||
$sdkPlatofrms[] = APP_PLATFORM_SERVER;
|
$sdkPlatforms[] = APP_PLATFORM_SERVER;
|
||||||
break;
|
break;
|
||||||
case APP_AUTH_TYPE_ADMIN:
|
case APP_AUTH_TYPE_ADMIN:
|
||||||
$sdkPlatofrms[] = APP_PLATFORM_CONSOLE;
|
$sdkPlatforms[] = APP_PLATFORM_CONSOLE;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (empty($routeSecurity)) {
|
if (empty($routeSecurity)) {
|
||||||
$sdkPlatofrms[] = APP_PLATFORM_CLIENT;
|
$sdkPlatforms[] = APP_PLATFORM_CLIENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
$temp = [
|
$temp = [
|
||||||
|
@ -144,7 +150,7 @@ class Swagger2 extends Format
|
||||||
'rate-time' => $route->getLabel('abuse-time', 3600),
|
'rate-time' => $route->getLabel('abuse-time', 3600),
|
||||||
'rate-key' => $route->getLabel('abuse-key', 'url:{url},ip:{ip}'),
|
'rate-key' => $route->getLabel('abuse-key', 'url:{url},ip:{ip}'),
|
||||||
'scope' => $route->getLabel('scope', ''),
|
'scope' => $route->getLabel('scope', ''),
|
||||||
'platforms' => $sdkPlatofrms,
|
'platforms' => $sdkPlatforms,
|
||||||
'packaging' => $route->getLabel('sdk.packaging', false),
|
'packaging' => $route->getLabel('sdk.packaging', false),
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
@ -153,14 +159,9 @@ class Swagger2 extends Format
|
||||||
$temp['produces'][] = $produces;
|
$temp['produces'][] = $produces;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($this->models as $key => $value) {
|
foreach ($this->models as $value) {
|
||||||
if (\is_array($model)) {
|
if (\is_array($model)) {
|
||||||
$model = \array_map(function ($m) use ($value) {
|
$model = \array_map(fn ($m) => $m === $value->getType() ? $value : $m, $model);
|
||||||
if ($m === $value->getType()) {
|
|
||||||
return $value;
|
|
||||||
}
|
|
||||||
return $m;
|
|
||||||
}, $model);
|
|
||||||
} else {
|
} else {
|
||||||
if ($value->getType() === $model) {
|
if ($value->getType() === $model) {
|
||||||
$model = $value;
|
$model = $value;
|
||||||
|
@ -171,7 +172,7 @@ class Swagger2 extends Format
|
||||||
|
|
||||||
if (!(\is_array($model)) && $model->isNone()) {
|
if (!(\is_array($model)) && $model->isNone()) {
|
||||||
$temp['responses'][(string)$route->getLabel('sdk.response.code', '500')] = [
|
$temp['responses'][(string)$route->getLabel('sdk.response.code', '500')] = [
|
||||||
'description' => (in_array($produces, [
|
'description' => in_array($produces, [
|
||||||
'image/*',
|
'image/*',
|
||||||
'image/jpeg',
|
'image/jpeg',
|
||||||
'image/gif',
|
'image/gif',
|
||||||
|
@ -180,16 +181,14 @@ class Swagger2 extends Format
|
||||||
'image/svg-x',
|
'image/svg-x',
|
||||||
'image/x-icon',
|
'image/x-icon',
|
||||||
'image/bmp',
|
'image/bmp',
|
||||||
])) ? 'Image' : 'File',
|
]) ? 'Image' : 'File',
|
||||||
'schema' => [
|
'schema' => [
|
||||||
'type' => 'file'
|
'type' => 'file'
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
} else {
|
} else {
|
||||||
if (\is_array($model)) {
|
if (\is_array($model)) {
|
||||||
$modelDescription = \join(', or ', \array_map(function ($m) {
|
$modelDescription = \join(', or ', \array_map(fn ($m) => $m->getName(), $model));
|
||||||
return $m->getName();
|
|
||||||
}, $model));
|
|
||||||
// model has multiple possible responses, we will use oneOf
|
// model has multiple possible responses, we will use oneOf
|
||||||
foreach ($model as $m) {
|
foreach ($model as $m) {
|
||||||
$usedModels[] = $m->getType();
|
$usedModels[] = $m->getType();
|
||||||
|
@ -244,7 +243,8 @@ class Swagger2 extends Format
|
||||||
$bodyRequired = [];
|
$bodyRequired = [];
|
||||||
|
|
||||||
foreach ($route->getParams() as $name => $param) { // Set params
|
foreach ($route->getParams() as $name => $param) { // Set params
|
||||||
$validator = (\is_callable($param['validator'])) ? call_user_func_array($param['validator'], $this->app->getResources($param['injections'])) : $param['validator']; /** @var \Utopia\Validator $validator */
|
/** @var \Utopia\Validator $validator */
|
||||||
|
$validator = (\is_callable($param['validator'])) ? call_user_func_array($param['validator'], $this->app->getResources($param['injections'])) : $param['validator'];
|
||||||
|
|
||||||
$node = [
|
$node = [
|
||||||
'name' => $name,
|
'name' => $name,
|
||||||
|
@ -294,7 +294,6 @@ class Swagger2 extends Format
|
||||||
$node['type'] = 'object';
|
$node['type'] = 'object';
|
||||||
$node['default'] = (empty($param['default'])) ? new \stdClass() : $param['default'];
|
$node['default'] = (empty($param['default'])) ? new \stdClass() : $param['default'];
|
||||||
$node['x-example'] = '{}';
|
$node['x-example'] = '{}';
|
||||||
//$node['format'] = 'json';
|
|
||||||
break;
|
break;
|
||||||
case 'Utopia\Storage\Validator\File':
|
case 'Utopia\Storage\Validator\File':
|
||||||
$consumes = ['multipart/form-data'];
|
$consumes = ['multipart/form-data'];
|
||||||
|
@ -320,7 +319,8 @@ class Swagger2 extends Format
|
||||||
$node['format'] = 'password';
|
$node['format'] = 'password';
|
||||||
$node['x-example'] = 'password';
|
$node['x-example'] = 'password';
|
||||||
break;
|
break;
|
||||||
case 'Utopia\Validator\Range': /** @var \Utopia\Validator\Range $validator */
|
case 'Utopia\Validator\Range':
|
||||||
|
/** @var \Utopia\Validator\Range $validator */
|
||||||
$node['type'] = $validator->getType() === Validator::TYPE_FLOAT ? 'number' : $validator->getType();
|
$node['type'] = $validator->getType() === Validator::TYPE_FLOAT ? 'number' : $validator->getType();
|
||||||
$node['format'] = $validator->getType() == Validator::TYPE_INTEGER ? 'int32' : 'float';
|
$node['format'] = $validator->getType() == Validator::TYPE_INTEGER ? 'int32' : 'float';
|
||||||
$node['x-example'] = $validator->getMin();
|
$node['x-example'] = $validator->getMin();
|
||||||
|
@ -342,7 +342,8 @@ class Swagger2 extends Format
|
||||||
$node['format'] = 'url';
|
$node['format'] = 'url';
|
||||||
$node['x-example'] = 'https://example.com';
|
$node['x-example'] = 'https://example.com';
|
||||||
break;
|
break;
|
||||||
case 'Utopia\Validator\WhiteList': /** @var \Utopia\Validator\WhiteList $validator */
|
case 'Utopia\Validator\WhiteList':
|
||||||
|
/** @var \Utopia\Validator\WhiteList $validator */
|
||||||
$node['type'] = $validator->getType();
|
$node['type'] = $validator->getType();
|
||||||
$node['x-example'] = $validator->getList()[0];
|
$node['x-example'] = $validator->getList()[0];
|
||||||
|
|
||||||
|
@ -410,14 +411,7 @@ class Swagger2 extends Format
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($this->models as $model) {
|
foreach ($this->models as $model) {
|
||||||
foreach ($model->getRules() as $rule) {
|
$this->getNestedModels($model, $usedModels);
|
||||||
if (
|
|
||||||
in_array($model->getType(), $usedModels)
|
|
||||||
&& !in_array($rule['type'], ['string', 'integer', 'boolean', 'json', 'float'])
|
|
||||||
) {
|
|
||||||
$usedModels[] = $rule['type'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($this->models as $model) {
|
foreach ($this->models as $model) {
|
||||||
|
@ -485,15 +479,11 @@ class Swagger2 extends Format
|
||||||
if (\is_array($rule['type'])) {
|
if (\is_array($rule['type'])) {
|
||||||
if ($rule['array']) {
|
if ($rule['array']) {
|
||||||
$items = [
|
$items = [
|
||||||
'x-anyOf' => \array_map(function ($type) {
|
'x-anyOf' => \array_map(fn ($type) => ['$ref' => '#/definitions/' . $type], $rule['type'])
|
||||||
return ['$ref' => '#/definitions/' . $type];
|
|
||||||
}, $rule['type'])
|
|
||||||
];
|
];
|
||||||
} else {
|
} else {
|
||||||
$items = [
|
$items = [
|
||||||
'x-oneOf' => \array_map(function ($type) {
|
'x-oneOf' => \array_map(fn ($type) => ['$ref' => '#/definitions/' . $type], $rule['type'])
|
||||||
return ['$ref' => '#/definitions/' . $type];
|
|
||||||
}, $rule['type'])
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -4,14 +4,8 @@ namespace Appwrite\Specification;
|
||||||
|
|
||||||
class Specification
|
class Specification
|
||||||
{
|
{
|
||||||
/**
|
protected Format $format;
|
||||||
* @var Format
|
|
||||||
*/
|
|
||||||
protected $format;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param Format $format
|
|
||||||
*/
|
|
||||||
public function __construct(Format $format)
|
public function __construct(Format $format)
|
||||||
{
|
{
|
||||||
$this->format = $format;
|
$this->format = $format;
|
||||||
|
|
Loading…
Reference in a new issue