2020-11-11 22:02:42 +00:00
< ? php
2025-02-04 11:08:56 +00:00
namespace Appwrite\SDK\Specification\Format ;
2020-11-11 22:02:42 +00:00
2025-12-13 16:06:44 +00:00
use Appwrite\Platform\Tasks\Specs ;
2025-01-17 04:31:39 +00:00
use Appwrite\SDK\AuthType ;
2025-03-27 08:30:10 +00:00
use Appwrite\SDK\Method ;
2025-01-17 04:31:39 +00:00
use Appwrite\SDK\MethodType ;
2025-03-27 08:30:10 +00:00
use Appwrite\SDK\Response ;
2025-02-04 11:08:56 +00:00
use Appwrite\SDK\Specification\Format ;
2020-11-11 22:02:42 +00:00
use Appwrite\Template\Template ;
2025-09-11 11:31:53 +00:00
use Appwrite\Utopia\Database\Validator\Operation ;
2022-06-29 23:41:49 +00:00
use Appwrite\Utopia\Response\Model ;
2025-09-23 05:17:39 +00:00
use Appwrite\Utopia\Response\Model\Any ;
2025-09-08 18:00:28 +00:00
use Utopia\Database\Database ;
2022-12-14 16:04:06 +00:00
use Utopia\Database\Helpers\Permission ;
use Utopia\Database\Helpers\Role ;
2025-09-08 18:00:28 +00:00
use Utopia\Database\Validator\Spatial ;
2024-10-08 07:54:40 +00:00
use Utopia\Validator ;
use Utopia\Validator\ArrayList ;
use Utopia\Validator\Nullable ;
use Utopia\Validator\Range ;
2020-11-11 22:02:42 +00:00
class OpenAPI3 extends Format
{
2022-05-23 14:54:50 +00:00
public function getName () : string
2020-11-11 22:02:42 +00:00
{
return 'Open API 3' ;
}
public function parse () : array
{
2022-01-04 10:42:23 +00:00
/**
2022-06-29 23:41:49 +00:00
* Specifications ( v3 . 0.0 ) :
* https :// github . com / OAI / OpenAPI - Specification / blob / master / versions / 3.0 . 0. md
*/
2020-11-11 22:02:42 +00:00
$output = [
'openapi' => '3.0.0' ,
'info' => [
'version' => $this -> getParam ( 'version' ),
'title' => $this -> getParam ( 'name' ),
'description' => $this -> getParam ( 'description' ),
'termsOfService' => $this -> getParam ( 'terms' ),
'contact' => [
'name' => $this -> getParam ( 'contact.name' ),
'url' => $this -> getParam ( 'contact.url' ),
'email' => $this -> getParam ( 'contact.email' ),
],
'license' => [
'name' => 'BSD-3-Clause' ,
'url' => 'https://raw.githubusercontent.com/appwrite/appwrite/master/LICENSE' ,
],
],
'servers' => [
[
'url' => $this -> getParam ( 'endpoint' , '' ),
],
2025-04-24 11:15:23 +00:00
[
'url' => $this -> getParam ( 'endpoint.docs' , '' ),
],
2020-11-11 22:02:42 +00:00
],
'paths' => [],
2021-01-26 11:54:06 +00:00
'tags' => $this -> services ,
2020-11-11 22:02:42 +00:00
'components' => [
'schemas' => [],
2020-11-14 11:52:38 +00:00
'securitySchemes' => $this -> keys ,
2020-11-11 22:02:42 +00:00
],
'externalDocs' => [
'description' => $this -> getParam ( 'docs.description' ),
'url' => $this -> getParam ( 'docs.url' ),
],
];
if ( isset ( $output [ 'components' ][ 'securitySchemes' ][ 'Project' ])) {
2024-05-07 18:55:58 +00:00
$output [ 'components' ][ 'securitySchemes' ][ 'Project' ][ 'x-appwrite' ] = [ 'demo' => '<YOUR_PROJECT_ID>' ];
2020-11-11 22:02:42 +00:00
}
2021-09-30 09:59:01 +00:00
2020-11-11 22:02:42 +00:00
if ( isset ( $output [ 'components' ][ 'securitySchemes' ][ 'Key' ])) {
2024-05-07 18:55:58 +00:00
$output [ 'components' ][ 'securitySchemes' ][ 'Key' ][ 'x-appwrite' ] = [ 'demo' => '<YOUR_API_KEY>' ];
2020-11-11 22:02:42 +00:00
}
2021-03-28 21:47:17 +00:00
2026-04-01 04:50:20 +00:00
if ( isset ( $output [ 'components' ][ 'securitySchemes' ][ 'JWT' ])) {
$output [ 'components' ][ 'securitySchemes' ][ 'JWT' ][ 'x-appwrite' ] = [ 'demo' => '<YOUR_JWT>' ];
2021-03-28 21:47:17 +00:00
}
2021-09-30 09:59:01 +00:00
2020-11-11 22:02:42 +00:00
if ( isset ( $output [ 'components' ][ 'securitySchemes' ][ 'Locale' ])) {
$output [ 'components' ][ 'securitySchemes' ][ 'Locale' ][ 'x-appwrite' ] = [ 'demo' => 'en' ];
}
if ( isset ( $output [ 'components' ][ 'securitySchemes' ][ 'Mode' ])) {
$output [ 'components' ][ 'securitySchemes' ][ 'Mode' ][ 'x-appwrite' ] = [ 'demo' => '' ];
}
2021-04-08 08:46:45 +00:00
$usedModels = [];
2022-06-29 23:41:49 +00:00
foreach ( $this -> routes as $route ) {
2021-08-19 05:09:55 +00:00
$url = \str_replace ( '/v1' , '' , $route -> getPath ());
2020-11-11 22:02:42 +00:00
$scope = $route -> getLabel ( 'scope' , '' );
2025-01-17 04:31:39 +00:00
$sdk = $route -> getLabel ( 'sdk' , false );
2020-11-11 22:02:42 +00:00
2026-04-01 04:50:20 +00:00
if ( $sdk === false ) {
2025-01-17 04:31:39 +00:00
continue ;
}
$additionalMethods = null ;
2025-03-27 08:30:10 +00:00
if ( \is_array ( $sdk )) {
2025-01-17 04:31:39 +00:00
$additionalMethods = $sdk ;
2025-01-24 07:19:43 +00:00
$sdk = $sdk [ 0 ];
2025-01-17 04:31:39 +00:00
}
/**
2025-03-27 08:30:10 +00:00
* @ var Method $sdk
2025-01-17 04:31:39 +00:00
*/
2025-03-27 08:30:10 +00:00
$consumes = [ $sdk -> getRequestType () -> value ];
2025-01-17 04:31:39 +00:00
2025-07-27 06:35:40 +00:00
$methodName = $sdk -> getMethodName () ? ? \uniqid ();
2025-01-17 04:31:39 +00:00
2025-04-16 10:45:58 +00:00
$desc = $sdk -> getDescriptionFilePath () ? : $sdk -> getDescription ();
2025-01-17 04:31:39 +00:00
$produces = ( $sdk -> getContentType ()) -> value ;
$routeSecurity = $sdk -> getAuth () ? ? [];
2021-05-19 15:29:06 +00:00
2025-12-13 16:06:44 +00:00
$specs = new Specs ();
$sdkPlatforms = $specs -> getSDKPlatformsForRouteSecurity ( $routeSecurity );
2021-09-30 09:59:01 +00:00
2025-01-17 04:31:39 +00:00
$namespace = $sdk -> getNamespace () ? ? 'default' ;
2026-04-05 14:07:29 +00:00
$descContents = $this -> getDescriptionContents ( $desc );
2025-02-05 11:27:20 +00:00
2020-11-11 22:02:42 +00:00
$temp = [
'summary' => $route -> getDesc (),
2025-07-27 06:35:40 +00:00
'operationId' => $namespace . ucfirst ( $methodName ),
2025-01-17 04:31:39 +00:00
'tags' => [ $namespace ],
2025-02-05 11:27:20 +00:00
'description' => $descContents ,
2020-11-14 11:52:38 +00:00
'responses' => [],
2025-07-02 07:53:00 +00:00
'deprecated' => $sdk -> isDeprecated (),
2020-11-11 22:02:42 +00:00
'x-appwrite' => [ // Appwrite related metadata
2025-07-27 06:35:40 +00:00
'method' => $methodName ,
2025-03-31 09:32:47 +00:00
'group' => $sdk -> getGroup (),
2023-08-30 16:36:47 +00:00
'weight' => $route -> getOrder (),
2020-11-11 22:02:42 +00:00
'cookies' => $route -> getLabel ( 'sdk.cookies' , false ),
2025-01-17 04:31:39 +00:00
'type' => $sdk -> getType () -> value ? ? '' ,
2025-08-26 13:09:34 +00:00
'demo' => \strtolower ( $namespace ) . '/' . Template :: fromCamelCaseToDash ( $methodName ) . '.md' ,
2020-11-11 22:02:42 +00:00
'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' , '' ),
2022-06-29 23:41:49 +00:00
'platforms' => $sdkPlatforms ,
2025-12-11 08:23:34 +00:00
'packaging' => $sdk -> isPackaging (),
'public' => $sdk -> isPublic (),
2020-11-11 22:02:42 +00:00
],
];
2025-12-15 16:11:17 +00:00
if ( $sdk -> getDescriptionFilePath () !== null ) {
$temp [ 'x-appwrite' ][ 'edit' ] = 'https://github.com/appwrite/appwrite/edit/master' . $sdk -> getDescription ();
}
2025-08-26 12:13:11 +00:00
if ( $sdk -> getDeprecated ()) {
2025-07-22 11:29:05 +00:00
$temp [ 'x-appwrite' ][ 'deprecated' ] = [
'since' => $sdk -> getDeprecated () -> getSince (),
'replaceWith' => $sdk -> getDeprecated () -> getReplaceWith (),
];
2025-07-02 07:53:00 +00:00
}
2026-04-01 05:45:59 +00:00
if ( \is_array ( $additionalMethods ) && \count ( $additionalMethods ) > 0 ) {
2025-01-24 07:19:43 +00:00
$temp [ 'x-appwrite' ][ 'methods' ] = [];
2025-07-27 06:35:40 +00:00
foreach ( $additionalMethods as $methodObj ) {
/** @var Method $methodObj */
$desc = $methodObj -> getDescriptionFilePath ();
2025-07-23 15:26:20 +00:00
2025-07-27 06:35:40 +00:00
$methodSecurities = $methodObj -> getAuth ();
2025-12-13 16:06:44 +00:00
$methodSdkPlatforms = $specs -> getSDKPlatformsForRouteSecurity ( $methodSecurities );
2025-07-24 04:37:46 +00:00
2025-12-13 16:06:44 +00:00
if ( ! \in_array ( $this -> platform , $methodSdkPlatforms )) {
2025-07-24 04:37:46 +00:00
continue ;
}
2025-07-23 15:26:20 +00:00
$methodSecurities = [ 'Project' => []];
2025-07-27 06:35:40 +00:00
foreach ( $methodObj -> getAuth () as $security ) {
2025-07-23 15:26:20 +00:00
if ( \array_key_exists ( $security -> value , $this -> keys )) {
$methodSecurities [ $security -> value ] = [];
}
}
2025-01-17 04:31:39 +00:00
$additionalMethod = [
2025-07-27 06:35:40 +00:00
'name' => $methodObj -> getMethodName (),
2025-07-27 08:17:44 +00:00
'namespace' => $methodObj -> getNamespace (),
2025-08-19 13:23:11 +00:00
'desc' => $methodObj -> getDesc () ? ? '' ,
2025-07-23 15:26:20 +00:00
'auth' => \array_slice ( $methodSecurities , 0 , $this -> authCount ),
2025-01-17 04:31:39 +00:00
'parameters' => [],
'required' => [],
2025-01-24 07:19:43 +00:00
'responses' => [],
2026-04-05 14:07:29 +00:00
'description' => $this -> getDescriptionContents ( $desc ),
2025-08-27 13:34:37 +00:00
'demo' => \strtolower ( $namespace ) . '/' . Template :: fromCamelCaseToDash ( $methodObj -> getMethodName ()) . '.md' ,
2025-12-11 08:23:34 +00:00
'public' => $methodObj -> isPublic (),
2025-01-17 04:31:39 +00:00
];
2025-07-27 13:49:14 +00:00
// add deprecation only if method has it!
2025-08-26 12:03:49 +00:00
if ( $methodObj -> getDeprecated ()) {
2025-07-27 13:49:14 +00:00
$additionalMethod [ 'deprecated' ] = [
'since' => $methodObj -> getDeprecated () -> getSince (),
'replaceWith' => $methodObj -> getDeprecated () -> getReplaceWith (),
];
}
2025-07-27 11:28:04 +00:00
// If additional method has no parameters, inherit from route
if ( empty ( $methodObj -> getParameters ())) {
foreach ( $route -> getParams () as $name => $param ) {
$additionalMethod [ 'parameters' ][] = $name ;
if ( ! $param [ 'optional' ]) {
$additionalMethod [ 'required' ][] = $name ;
}
}
} else {
// Use method's own parameters
foreach ( $methodObj -> getParameters () as $parameter ) {
$additionalMethod [ 'parameters' ][] = $parameter -> getName ();
if ( ! $parameter -> getOptional ()) {
$additionalMethod [ 'required' ][] = $parameter -> getName ();
}
2025-01-17 04:31:39 +00:00
}
}
2025-07-27 06:35:40 +00:00
foreach ( $methodObj -> getResponses () as $response ) {
2025-11-13 10:35:27 +00:00
/** @var Response|array $response */
$responseModel = $response -> getModel ();
if ( \is_array ( $responseModel )) {
foreach ( $responseModel as $modelName ) {
foreach ( $this -> models as $value ) {
if ( $value -> getType () === $modelName ) {
$usedModels [] = $modelName ;
break ;
}
}
}
2025-01-30 05:19:20 +00:00
$additionalMethod [ 'responses' ][] = [
'code' => $response -> getCode (),
2025-11-13 10:35:27 +00:00
'model' => \array_map ( fn ( $m ) => '#/components/schemas/' . $m , $responseModel )
2025-01-30 05:19:20 +00:00
];
} else {
2025-07-27 08:17:44 +00:00
$responseData = [
2025-01-30 05:19:20 +00:00
'code' => $response -> getCode (),
];
2025-07-27 08:17:44 +00:00
// lets not assume stuff here!
if ( $response -> getCode () !== 204 ) {
2025-11-13 10:35:27 +00:00
$responseData [ 'model' ] = '#/components/schemas/' . $responseModel ;
foreach ( $this -> models as $value ) {
if ( $value -> getType () === $responseModel ) {
$usedModels [] = $responseModel ;
break ;
}
}
2025-07-27 08:17:44 +00:00
}
$additionalMethod [ 'responses' ][] = $responseData ;
2025-01-30 05:19:20 +00:00
}
2021-09-30 09:59:01 +00:00
}
2025-01-17 04:31:39 +00:00
2025-01-24 07:19:43 +00:00
$temp [ 'x-appwrite' ][ 'methods' ][] = $additionalMethod ;
2020-11-14 11:52:38 +00:00
}
}
2025-01-17 04:31:39 +00:00
// Handle response models
foreach ( $sdk -> getResponses () as $response ) {
2025-03-27 08:30:10 +00:00
/** @var Response $response */
2025-01-17 04:31:39 +00:00
$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 ;
}
2021-09-30 09:59:01 +00:00
}
2025-01-17 04:31:39 +00:00
}
2021-09-30 09:59:01 +00:00
2026-04-08 05:11:43 +00:00
if ( \is_string ( $model )) {
throw new \RuntimeException ( " Unresolved response model ' { $model } ' for method ' { $sdk -> getNamespace () } . { $sdk -> getMethodName () } '. Ensure the model is registered. " );
}
if ( \is_array ( $model )) {
foreach ( $model as $m ) {
if ( \is_string ( $m )) {
throw new \RuntimeException ( " Unresolved response model ' { $m } ' for method ' { $sdk -> getNamespace () } . { $sdk -> getMethodName () } '. Ensure the model is registered. " );
}
}
}
2025-01-17 04:31:39 +00:00
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' ,
2021-09-30 09:59:01 +00:00
];
} else {
2025-01-17 04:31:39 +00:00
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 => [
2026-04-16 05:37:07 +00:00
'schema' => \array_filter ([
'oneOf' => \array_map ( fn ( $m ) => [ '$ref' => '#/components/schemas/' . $m -> getType ()], $model ),
2026-04-16 05:59:16 +00:00
'discriminator' => $this -> getDiscriminator ( $model , '#/components/schemas/' ),
2026-04-16 05:37:07 +00:00
]),
2021-09-30 09:59:01 +00:00
],
],
2025-01-17 04:31:39 +00:00
];
} 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 (),
],
],
],
];
}
2021-09-30 09:59:01 +00:00
}
2020-11-14 11:52:38 +00:00
2025-01-17 04:31:39 +00:00
if (( $response -> getCode () ? ? 500 ) === 204 ) {
$temp [ 'responses' ][( string ) $response -> getCode () ? ? '500' ][ 'description' ] = 'No content' ;
2026-04-01 04:50:20 +00:00
unset ( $temp [ 'responses' ][( string ) $response -> getCode () ? ? '500' ][ 'content' ]);
2025-01-17 04:31:39 +00:00
}
2020-11-14 11:52:38 +00:00
}
2025-07-23 15:43:59 +00:00
if ( ! empty ( $scope )) {
2021-03-28 21:22:12 +00:00
$securities = [ 'Project' => []];
2021-09-30 09:59:01 +00:00
2025-01-17 04:31:39 +00:00
foreach ( $sdk -> getAuth () as $security ) {
2025-03-27 08:30:10 +00:00
/** @var AuthType $security */
2025-01-17 04:31:39 +00:00
if ( array_key_exists ( $security -> value , $this -> keys )) {
$securities [ $security -> value ] = [];
2021-03-28 21:22:12 +00:00
}
}
2021-03-28 21:47:17 +00:00
2021-05-19 14:26:06 +00:00
$temp [ 'x-appwrite' ][ 'auth' ] = array_slice ( $securities , 0 , $this -> authCount );
2021-03-28 21:22:12 +00:00
$temp [ 'security' ][] = $securities ;
2020-11-11 22:02:42 +00:00
}
2020-11-14 11:52:38 +00:00
$body = [
'content' => [
$consumes [ 0 ] => [
'schema' => [
'type' => 'object' ,
'properties' => [],
],
],
],
];
$bodyRequired = [];
foreach ( $route -> getParams () as $name => $param ) { // Set params
2025-05-20 09:50:36 +00:00
if (( $param [ 'deprecated' ] ? ? false ) === true ) {
2025-05-18 20:07:52 +00:00
continue ;
}
2024-10-08 07:54:40 +00:00
/**
* @ var \Utopia\Validator $validator
*/
2026-04-09 05:11:04 +00:00
$validator = $this -> getValidator ( $param );
2020-11-11 22:02:42 +00:00
2026-04-10 04:12:17 +00:00
$isNullable = $validator instanceof Nullable ;
$parameter = $this -> getRequestParameterConfig (
$sdk -> getNamespace () ? ? '' ,
$methodName ,
$name ,
$param [ 'optional' ],
$isNullable ,
$param [ 'default' ],
);
2020-11-11 22:02:42 +00:00
$node = [
'name' => $name ,
'description' => $param [ 'description' ],
2026-04-10 04:12:17 +00:00
'required' => $parameter [ 'required' ],
2020-11-11 22:02:42 +00:00
];
2023-03-13 10:24:49 +00:00
if ( $isNullable ) {
/** @var Nullable $validator */
2023-03-10 10:06:10 +00:00
$validator = $validator -> getValidator ();
}
2026-04-01 04:50:20 +00:00
$class = $validator instanceof Validator
2025-09-11 11:31:53 +00:00
? \get_class ( $validator )
: '' ;
$base = ! empty ( $class )
? \get_parent_class ( $class )
: '' ;
switch ( $base ) {
2026-01-27 11:58:34 +00:00
case \Appwrite\Utopia\Database\Validator\Queries\Base :: class :
2025-09-11 11:31:53 +00:00
$class = $base ;
break ;
}
2026-01-27 11:58:34 +00:00
if ( $class === \Utopia\Validator\AnyOf :: class ) {
2025-09-11 11:31:53 +00:00
$validator = $param [ 'validator' ] -> getValidators ()[ 0 ];
$class = \get_class ( $validator );
}
$array = false ;
2026-01-27 11:58:34 +00:00
if ( $class === \Utopia\Validator\ArrayList :: class ) {
2025-09-11 11:31:53 +00:00
$array = true ;
$subclass = \get_class ( $validator -> getValidator ());
switch ( $subclass ) {
2026-01-27 11:58:34 +00:00
case \Appwrite\Utopia\Database\Validator\Operation :: class :
case \Utopia\Validator\WhiteList :: class :
2025-09-11 11:31:53 +00:00
$class = $subclass ;
break ;
}
}
switch ( $class ) {
2026-01-27 11:58:34 +00:00
case \Utopia\Database\Validator\UID :: class :
case \Utopia\Validator\Text :: class :
2021-05-21 07:08:53 +00:00
$node [ 'schema' ][ 'type' ] = $validator -> getType ();
2025-12-01 13:51:49 +00:00
$node [ 'schema' ][ 'x-example' ] = ( $param [ 'example' ] ? ? '' ) ? : '<' . \strtoupper ( Template :: fromCamelCaseToSnake ( $node [ 'name' ])) . '>' ;
2020-11-11 22:02:42 +00:00
break ;
2026-01-27 11:58:34 +00:00
case \Utopia\Validator\Boolean :: class :
2021-05-21 07:08:53 +00:00
$node [ 'schema' ][ 'type' ] = $validator -> getType ();
2025-12-01 13:51:49 +00:00
$node [ 'schema' ][ 'x-example' ] = ( $param [ 'example' ] ? ? '' ) ? : false ;
2020-11-11 22:02:42 +00:00
break ;
2026-01-27 11:58:34 +00:00
case \Appwrite\Utopia\Database\Validator\CustomId :: class :
2025-01-17 04:31:39 +00:00
if ( $sdk -> getType () === MethodType :: UPLOAD ) {
2022-02-11 00:30:08 +00:00
$node [ 'schema' ][ 'x-upload-id' ] = true ;
}
2021-05-21 07:08:53 +00:00
$node [ 'schema' ][ 'type' ] = $validator -> getType ();
2025-12-01 13:51:49 +00:00
$node [ 'schema' ][ 'x-example' ] = ( $param [ 'example' ] ? ? '' ) ? : '<' . \strtoupper ( Template :: fromCamelCaseToSnake ( $node [ 'name' ])) . '>' ;
2020-11-11 22:02:42 +00:00
break ;
2026-04-01 04:50:20 +00:00
case \Utopia\Database\Validator\Datetime :: class :
2022-07-28 12:38:54 +00:00
$node [ 'schema' ][ 'type' ] = $validator -> getType ();
$node [ 'schema' ][ 'format' ] = 'datetime' ;
2025-12-01 13:51:49 +00:00
$node [ 'schema' ][ 'x-example' ] = ( $param [ 'example' ] ? ? '' ) ? : Model :: TYPE_DATETIME_EXAMPLE ;
2022-07-28 12:38:54 +00:00
break ;
2026-01-27 11:58:34 +00:00
case \Utopia\Database\Validator\Spatial :: class :
2025-09-11 11:31:53 +00:00
/** @var Spatial $validator */
$node [ 'schema' ][ 'type' ] = 'array' ;
$node [ 'schema' ][ 'items' ] = [
'oneOf' => [
[ 'type' => 'array' ]
]
];
2025-12-01 13:51:49 +00:00
$node [ 'schema' ][ 'x-example' ] = ( $param [ 'example' ] ? ? '' ) ? : match ( $validator -> getSpatialType ()) {
2025-09-11 11:31:53 +00:00
Database :: VAR_POINT => '[1, 2]' ,
Database :: VAR_LINESTRING => '[[1, 2], [3, 4], [5, 6]]' ,
Database :: VAR_POLYGON => '[[[1, 2], [3, 4], [5, 6], [1, 2]]]' ,
};
break ;
2026-03-19 22:02:41 +00:00
case \Utopia\Emails\Validator\Email :: class :
2021-05-21 07:08:53 +00:00
$node [ 'schema' ][ 'type' ] = $validator -> getType ();
2020-11-14 11:52:38 +00:00
$node [ 'schema' ][ 'format' ] = 'email' ;
2025-12-01 13:51:49 +00:00
$node [ 'schema' ][ 'x-example' ] = ( $param [ 'example' ] ? ? '' ) ? : 'email@example.com' ;
2020-11-11 22:02:42 +00:00
break ;
2026-01-27 11:58:34 +00:00
case \Utopia\Validator\Host :: class :
case \Utopia\Validator\URL :: class :
case \Appwrite\Network\Validator\Redirect :: class :
2021-05-21 07:08:53 +00:00
$node [ 'schema' ][ 'type' ] = $validator -> getType ();
2020-11-14 11:52:38 +00:00
$node [ 'schema' ][ 'format' ] = 'url' ;
2025-12-01 13:51:49 +00:00
$node [ 'schema' ][ 'x-example' ] = ( $param [ 'example' ] ? ? '' ) ? : 'https://example.com' ;
2020-11-11 22:02:42 +00:00
break ;
2026-01-27 11:58:34 +00:00
case \Utopia\Validator\JSON :: class :
case \Utopia\Validator\Assoc :: class :
2021-10-05 15:39:39 +00:00
$param [ 'default' ] = ( empty ( $param [ 'default' ])) ? new \stdClass () : $param [ 'default' ];
2021-05-21 08:44:51 +00:00
$node [ 'schema' ][ 'type' ] = 'object' ;
2025-09-04 17:15:46 +00:00
$node [ 'schema' ][ 'x-example' ] = ( $param [ 'example' ] ? ? '' ) ? : '{}' ;
2020-11-11 22:02:42 +00:00
break ;
2026-01-27 11:58:34 +00:00
case \Utopia\Storage\Validator\File :: class :
2020-11-14 11:52:38 +00:00
$consumes = [ 'multipart/form-data' ];
2021-05-21 07:08:53 +00:00
$node [ 'schema' ][ 'type' ] = $validator -> getType ();
2020-11-14 11:52:38 +00:00
$node [ 'schema' ][ 'format' ] = 'binary' ;
2020-11-11 22:02:42 +00:00
break ;
2026-01-27 11:58:34 +00:00
case \Utopia\Validator\ArrayList :: class :
2024-02-02 11:33:48 +00:00
/** @var ArrayList $validator */
$node [ 'schema' ][ 'type' ] = 'array' ;
$node [ 'schema' ][ 'items' ] = [
'type' => $validator -> getValidator () -> getType (),
];
2025-12-01 13:51:49 +00:00
if ( ! empty ( $param [ 'example' ])) {
$node [ 'schema' ][ 'x-example' ] = $param [ 'example' ];
}
2024-02-02 11:33:48 +00:00
break ;
2026-01-27 11:58:34 +00:00
case \Appwrite\Utopia\Database\Validator\Queries\Base :: class :
case \Appwrite\Utopia\Database\Validator\Queries\Columns :: class :
case \Appwrite\Utopia\Database\Validator\Queries\Attributes :: class :
case \Appwrite\Utopia\Database\Validator\Queries\Buckets :: class :
case \Appwrite\Utopia\Database\Validator\Queries\Tables :: class :
case \Appwrite\Utopia\Database\Validator\Queries\Collections :: class :
case \Appwrite\Utopia\Database\Validator\Queries\Databases :: class :
case \Appwrite\Utopia\Database\Validator\Queries\Deployments :: class :
case \Appwrite\Utopia\Database\Validator\Queries\Executions :: class :
case \Appwrite\Utopia\Database\Validator\Queries\Files :: class :
case \Appwrite\Utopia\Database\Validator\Queries\Functions :: class :
case \Appwrite\Utopia\Database\Validator\Queries\Identities :: class :
case \Appwrite\Utopia\Database\Validator\Queries\Indexes :: class :
case \Appwrite\Utopia\Database\Validator\Queries\Installations :: class :
case \Appwrite\Utopia\Database\Validator\Queries\Memberships :: class :
case \Appwrite\Utopia\Database\Validator\Queries\Messages :: class :
case \Appwrite\Utopia\Database\Validator\Queries\Migrations :: class :
case \Appwrite\Utopia\Database\Validator\Queries\Projects :: class :
case \Appwrite\Utopia\Database\Validator\Queries\Providers :: class :
case \Appwrite\Utopia\Database\Validator\Queries\Rules :: class :
case \Appwrite\Utopia\Database\Validator\Queries\Subscribers :: class :
case \Appwrite\Utopia\Database\Validator\Queries\Targets :: class :
case \Appwrite\Utopia\Database\Validator\Queries\Teams :: class :
case \Appwrite\Utopia\Database\Validator\Queries\Topics :: class :
case \Appwrite\Utopia\Database\Validator\Queries\Users :: class :
case \Appwrite\Utopia\Database\Validator\Queries\Variables :: class :
case \Utopia\Database\Validator\Queries :: class :
case \Utopia\Database\Validator\Queries\Document :: class :
case \Utopia\Database\Validator\Queries\Documents :: class :
2021-05-21 08:44:51 +00:00
$node [ 'schema' ][ 'type' ] = 'array' ;
2020-11-14 11:52:38 +00:00
$node [ 'schema' ][ 'items' ] = [
2020-11-11 22:02:42 +00:00
'type' => 'string' ,
];
break ;
2026-01-27 11:58:34 +00:00
case \Utopia\Database\Validator\Permissions :: class :
2021-11-18 11:49:54 +00:00
$node [ 'schema' ][ 'type' ] = $validator -> getType ();
$node [ 'schema' ][ 'items' ] = [
'type' => 'string' ,
];
2025-12-01 13:51:49 +00:00
$node [ 'schema' ][ 'x-example' ] = ( $param [ 'example' ] ? ? '' ) ? : '["' . Permission :: read ( Role :: any ()) . '"]' ;
2022-08-22 02:27:17 +00:00
break ;
2026-01-27 11:58:34 +00:00
case \Utopia\Database\Validator\Roles :: class :
2022-08-22 02:27:17 +00:00
$node [ 'schema' ][ 'type' ] = $validator -> getType ();
$node [ 'schema' ][ 'items' ] = [
'type' => 'string' ,
];
2025-12-01 13:51:49 +00:00
$node [ 'schema' ][ 'x-example' ] = ( $param [ 'example' ] ? ? '' ) ? : '["' . Role :: any () -> toString () . '"]' ;
2021-11-18 11:49:54 +00:00
break ;
2026-01-27 11:58:34 +00:00
case \Appwrite\Auth\Validator\Password :: class :
2021-05-21 07:08:53 +00:00
$node [ 'schema' ][ 'type' ] = $validator -> getType ();
2021-05-21 08:44:51 +00:00
$node [ 'schema' ][ 'format' ] = 'password' ;
2025-12-01 13:51:49 +00:00
$node [ 'schema' ][ 'x-example' ] = ( $param [ 'example' ] ? ? '' ) ? : 'password' ;
2020-11-11 22:02:42 +00:00
break ;
2026-01-27 11:58:34 +00:00
case \Appwrite\Auth\Validator\Phone :: class :
2022-10-03 15:06:44 +00:00
$node [ 'schema' ][ 'type' ] = $validator -> getType ();
$node [ 'schema' ][ 'format' ] = 'phone' ;
2025-12-01 13:51:49 +00:00
$node [ 'schema' ][ 'x-example' ] = ( $param [ 'example' ] ? ? '' ) ? : '+12065550100' ; // In the US, 555 is reserved like example.com
2022-10-03 15:06:44 +00:00
break ;
2026-01-27 11:58:34 +00:00
case \Utopia\Validator\Range :: class :
2024-02-02 11:33:48 +00:00
/** @var Range $validator */
2022-05-23 14:54:50 +00:00
$node [ 'schema' ][ 'type' ] = $validator -> getType () === Validator :: TYPE_FLOAT ? 'number' : $validator -> getType ();
2021-05-21 07:08:53 +00:00
$node [ 'schema' ][ 'format' ] = $validator -> getType () == Validator :: TYPE_INTEGER ? 'int32' : 'float' ;
2025-12-01 13:51:49 +00:00
$node [ 'schema' ][ 'x-example' ] = ( $param [ 'example' ] ? ? '' ) ? : $validator -> getMin ();
2020-11-11 22:02:42 +00:00
break ;
2026-01-27 11:58:34 +00:00
case \Utopia\Validator\Integer :: class :
2021-05-21 07:08:53 +00:00
$node [ 'schema' ][ 'type' ] = $validator -> getType ();
2026-01-13 10:40:47 +00:00
$node [ 'schema' ][ 'format' ] = $validator -> getFormat ();
2025-12-01 13:51:49 +00:00
if ( ! empty ( $param [ 'example' ])) {
$node [ 'schema' ][ 'x-example' ] = $param [ 'example' ];
}
2020-11-11 22:02:42 +00:00
break ;
2026-01-27 11:58:34 +00:00
case \Utopia\Validator\Numeric :: class :
case \Utopia\Validator\FloatValidator :: class :
2022-01-31 10:42:33 +00:00
$node [ 'schema' ][ 'type' ] = 'number' ;
$node [ 'schema' ][ 'format' ] = 'float' ;
2025-12-01 13:51:49 +00:00
if ( ! empty ( $param [ 'example' ])) {
$node [ 'schema' ][ 'x-example' ] = $param [ 'example' ];
}
2022-01-31 10:42:33 +00:00
break ;
2026-01-27 11:58:34 +00:00
case \Utopia\Validator\WhiteList :: class :
2025-12-03 03:58:39 +00:00
if ( $array ) {
$validator = $validator -> getValidator ();
$node [ 'schema' ][ 'type' ] = 'array' ;
$node [ 'schema' ][ 'items' ] = [
'type' => $validator -> getType (),
];
if ( ! empty ( $param [ 'example' ])) {
$node [ 'schema' ][ 'x-example' ] = $param [ 'example' ];
}
// Iterate from the blackList. If it matches with the current one, then it is a blackList
// Do not add the enum
$allowed = true ;
2026-01-14 09:59:30 +00:00
$excludeKeys = null ;
2025-12-03 03:58:39 +00:00
foreach ( $this -> enumBlacklist as $blacklist ) {
if (
$blacklist [ 'namespace' ] == $sdk -> getNamespace ()
&& $blacklist [ 'method' ] == $methodName
&& $blacklist [ 'parameter' ] == $name
) {
2026-01-14 09:59:30 +00:00
// 'exclude' => true means full exclude
if ( isset ( $blacklist [ 'exclude' ]) && $blacklist [ 'exclude' ] === true ) {
$allowed = false ;
break ;
}
if ( isset ( $blacklist [ 'excludeKeys' ])) {
$excludeKeys = $blacklist [ 'excludeKeys' ];
}
2025-12-03 03:58:39 +00:00
break ;
}
}
if ( $allowed && $validator -> getType () === 'string' ) {
2026-01-14 11:45:33 +00:00
$allValues = \array_values ( $validator -> getList ());
$allKeys = $this -> getRequestEnumKeys ( $sdk -> getNamespace () ? ? '' , $methodName , $name );
2026-01-14 09:59:30 +00:00
if ( $excludeKeys !== null ) {
2026-01-14 11:45:33 +00:00
$keepIndices = [];
foreach ( $allValues as $index => $value ) {
if ( ! \in_array ( $value , $excludeKeys , true )) {
$keepIndices [] = $index ;
}
}
$enumKeys = \array_values ( \array_intersect_key ( $allKeys , \array_flip ( $keepIndices )));
$enumValues = \array_values ( \array_intersect_key ( $allValues , \array_flip ( $keepIndices )));
} else {
$enumKeys = $allKeys ;
$enumValues = $allValues ;
2026-01-14 09:59:30 +00:00
}
$node [ 'schema' ][ 'items' ][ 'enum' ] = $enumValues ;
2025-12-03 03:58:39 +00:00
$node [ 'schema' ][ 'items' ][ 'x-enum-name' ] = $this -> getRequestEnumName ( $sdk -> getNamespace () ? ? '' , $methodName , $name );
2026-01-14 11:12:30 +00:00
$node [ 'schema' ][ 'items' ][ 'x-enum-keys' ] = $enumKeys ;
2026-01-15 06:26:19 +00:00
if ( ! empty ( $excludeKeys )) {
$node [ 'description' ] = $this -> parseDescription ( $node [ 'description' ], $excludeKeys );
}
2025-12-03 03:58:39 +00:00
}
if ( $validator -> getType () === 'integer' ) {
2026-01-13 10:40:47 +00:00
$node [ 'schema' ][ 'items' ][ 'format' ] = $validator -> getFormat () ? ? 'int32' ;
2025-12-03 03:58:39 +00:00
}
} else {
$node [ 'schema' ][ 'type' ] = $validator -> getType ();
$node [ 'schema' ][ 'x-example' ] = ( $param [ 'example' ] ? ? '' ) ? : $validator -> getList ()[ 0 ];
// Iterate from the blackList. If it matches with the current one, then it is a blackList
// Do not add the enum
$allowed = true ;
2026-01-14 09:59:30 +00:00
$excludeKeys = null ;
2025-12-03 03:58:39 +00:00
foreach ( $this -> enumBlacklist as $blacklist ) {
if (
$blacklist [ 'namespace' ] == $sdk -> getNamespace ()
&& $blacklist [ 'method' ] == $methodName
&& $blacklist [ 'parameter' ] == $name
) {
2026-01-14 09:59:30 +00:00
// 'exclude' => true means full exclude
if ( isset ( $blacklist [ 'exclude' ]) && $blacklist [ 'exclude' ] === true ) {
$allowed = false ;
break ;
}
if ( isset ( $blacklist [ 'excludeKeys' ])) {
$excludeKeys = $blacklist [ 'excludeKeys' ];
}
2025-12-03 03:58:39 +00:00
break ;
}
}
if ( $allowed && $validator -> getType () === 'string' ) {
2026-01-14 11:45:33 +00:00
$allValues = \array_values ( $validator -> getList ());
$allKeys = $this -> getRequestEnumKeys ( $sdk -> getNamespace () ? ? '' , $methodName , $name );
2026-01-14 09:59:30 +00:00
if ( $excludeKeys !== null ) {
2026-01-14 11:45:33 +00:00
$keepIndices = [];
foreach ( $allValues as $index => $value ) {
if ( ! \in_array ( $value , $excludeKeys , true )) {
$keepIndices [] = $index ;
}
}
$enumKeys = \array_values ( \array_intersect_key ( $allKeys , \array_flip ( $keepIndices )));
$enumValues = \array_values ( \array_intersect_key ( $allValues , \array_flip ( $keepIndices )));
} else {
$enumKeys = $allKeys ;
$enumValues = $allValues ;
2026-01-14 09:59:30 +00:00
}
$node [ 'schema' ][ 'enum' ] = $enumValues ;
2025-12-03 03:58:39 +00:00
$node [ 'schema' ][ 'x-enum-name' ] = $this -> getRequestEnumName ( $sdk -> getNamespace () ? ? '' , $methodName , $name );
2026-01-14 11:12:30 +00:00
$node [ 'schema' ][ 'x-enum-keys' ] = $enumKeys ;
2026-01-15 06:26:19 +00:00
if ( ! empty ( $excludeKeys )) {
$node [ 'description' ] = $this -> parseDescription ( $node [ 'description' ], $excludeKeys );
}
2025-12-03 03:58:39 +00:00
}
if ( $validator -> getType () === 'integer' ) {
2026-01-13 10:40:47 +00:00
$node [ 'schema' ][ 'format' ] = $validator -> getFormat () ? ? 'int32' ;
2023-08-11 07:43:46 +00:00
}
2021-04-13 08:46:30 +00:00
}
2020-11-11 22:02:42 +00:00
break ;
2026-01-27 11:58:34 +00:00
case \Appwrite\Utopia\Database\Validator\CompoundUID :: class :
2024-02-20 07:15:08 +00:00
$node [ 'schema' ][ 'type' ] = $validator -> getType ();
2025-12-01 13:51:49 +00:00
$node [ 'schema' ][ 'x-example' ] = ( $param [ 'example' ] ? ? '' ) ? : '<ID1:ID2>' ;
2025-09-11 11:31:53 +00:00
break ;
2026-01-27 11:58:34 +00:00
case \Appwrite\Utopia\Database\Validator\Operation :: class :
2025-09-11 11:31:53 +00:00
if ( $array ) {
$validator = $validator -> getValidator ();
}
/** @var Operation $validator */
$collectionIdKey = $validator -> getCollectionIdKey ();
$documentIdKey = $validator -> getDocumentIdKey ();
if ( $array ) {
$node [ 'schema' ][ 'type' ] = 'array' ;
$node [ 'schema' ][ 'items' ] = [ 'type' => 'object' ];
} else {
$node [ 'schema' ][ 'type' ] = 'object' ;
}
2025-12-01 13:51:49 +00:00
if ( empty ( $param [ 'example' ])) {
$example = [
'action' => 'create' ,
'databaseId' => '<DATABASE_ID>' ,
$collectionIdKey => '<' . \strtoupper ( Template :: fromCamelCaseToSnake ( $collectionIdKey )) . '>' ,
$documentIdKey => '<' . \strtoupper ( Template :: fromCamelCaseToSnake ( $documentIdKey )) . '>' ,
'data' => [
'name' => 'Walter O\'Brien' ,
],
];
if ( $array ) {
$example = [ $example ];
}
$node [ 'schema' ][ 'x-example' ] = \str_replace ( " \n " , " \n \t " , \json_encode ( $example , JSON_PRETTY_PRINT ));
} else {
$node [ 'schema' ][ 'x-example' ] = $param [ 'example' ];
2025-09-11 11:31:53 +00:00
}
2024-02-20 07:15:08 +00:00
break ;
2020-11-11 22:02:42 +00:00
default :
2020-11-14 11:52:38 +00:00
$node [ 'schema' ][ 'type' ] = 'string' ;
2025-12-01 13:51:49 +00:00
if ( ! empty ( $param [ 'example' ])) {
$node [ 'schema' ][ 'x-example' ] = $param [ 'example' ];
}
2020-11-11 22:02:42 +00:00
break ;
}
2026-04-10 04:12:17 +00:00
if ( $parameter [ 'emitDefault' ]) { // Param has default value
2020-11-14 11:52:38 +00:00
$node [ 'schema' ][ 'default' ] = $param [ 'default' ];
2020-11-11 22:02:42 +00:00
}
2022-05-23 14:54:50 +00:00
if ( false !== \strpos ( $url , ':' . $name )) { // Param is in URL path
2020-11-11 22:02:42 +00:00
$node [ 'in' ] = 'path' ;
$temp [ 'parameters' ][] = $node ;
} elseif ( $route -> getMethod () == 'GET' ) { // Param is in query
$node [ 'in' ] = 'query' ;
$temp [ 'parameters' ][] = $node ;
} else { // Param is in payload
2026-04-10 04:07:01 +00:00
if ( $node [ 'required' ]) {
2020-11-14 11:52:38 +00:00
$bodyRequired [] = $name ;
}
2020-11-11 22:02:42 +00:00
2020-11-14 11:52:38 +00:00
$body [ 'content' ][ $consumes [ 0 ]][ 'schema' ][ 'properties' ][ $name ] = [
'type' => $node [ 'schema' ][ 'type' ],
'description' => $node [ 'description' ],
2022-02-20 09:22:42 +00:00
'x-example' => $node [ 'schema' ][ 'x-example' ] ? ? null
2020-11-14 11:52:38 +00:00
];
2026-01-13 10:40:47 +00:00
if ( isset ( $node [ 'schema' ][ 'format' ])) {
$body [ 'content' ][ $consumes [ 0 ]][ 'schema' ][ 'properties' ][ $name ][ 'format' ] = $node [ 'schema' ][ 'format' ];
}
2023-08-11 07:43:46 +00:00
if ( isset ( $node [ 'schema' ][ 'enum' ])) {
/// If the enum flag is Set, add the enum values to the body
$body [ 'content' ][ $consumes [ 0 ]][ 'schema' ][ 'properties' ][ $name ][ 'enum' ] = $node [ 'schema' ][ 'enum' ];
$body [ 'content' ][ $consumes [ 0 ]][ 'schema' ][ 'properties' ][ $name ][ 'x-enum-name' ] = $node [ 'schema' ][ 'x-enum-name' ] ? ? null ;
$body [ 'content' ][ $consumes [ 0 ]][ 'schema' ][ 'properties' ][ $name ][ 'x-enum-keys' ] = $node [ 'schema' ][ 'x-enum-keys' ] ? ? null ;
}
2022-05-23 14:54:50 +00:00
if ( $node [ 'schema' ][ 'x-upload-id' ] ? ? false ) {
2022-02-20 09:22:42 +00:00
$body [ 'content' ][ $consumes [ 0 ]][ 'schema' ][ 'properties' ][ $name ][ 'x-upload-id' ] = $node [ 'schema' ][ 'x-upload-id' ];
}
2022-05-23 14:54:50 +00:00
if ( isset ( $node [ 'default' ])) {
2020-11-14 13:05:18 +00:00
$body [ 'content' ][ $consumes [ 0 ]][ 'schema' ][ 'properties' ][ $name ][ 'default' ] = $node [ 'default' ];
}
2022-05-23 14:54:50 +00:00
if ( \array_key_exists ( 'items' , $node [ 'schema' ])) {
2020-11-14 11:52:38 +00:00
$body [ 'content' ][ $consumes [ 0 ]][ 'schema' ][ 'properties' ][ $name ][ 'items' ] = $node [ 'schema' ][ 'items' ];
2020-11-11 22:02:42 +00:00
}
2022-06-27 09:14:27 +00:00
if ( $node [ 'x-global' ] ? ? false ) {
$body [ 'content' ][ $consumes [ 0 ]][ 'schema' ][ 'properties' ][ $name ][ 'x-global' ] = true ;
}
2023-03-13 10:24:49 +00:00
2026-04-10 04:12:17 +00:00
if ( $parameter [ 'nullable' ]) {
2023-03-13 10:24:49 +00:00
$body [ 'content' ][ $consumes [ 0 ]][ 'schema' ][ 'properties' ][ $name ][ 'x-nullable' ] = true ;
}
2020-11-11 22:02:42 +00:00
}
2022-05-23 14:54:50 +00:00
$url = \str_replace ( ':' . $name , '{' . $name . '}' , $url );
2020-11-11 22:02:42 +00:00
}
2022-05-23 14:54:50 +00:00
if ( ! empty ( $bodyRequired )) {
2020-11-14 13:05:18 +00:00
$body [ 'content' ][ $consumes [ 0 ]][ 'schema' ][ 'required' ] = $bodyRequired ;
}
2020-11-14 11:52:38 +00:00
2022-05-23 14:54:50 +00:00
if ( ! empty ( $body [ 'content' ][ $consumes [ 0 ]][ 'schema' ][ 'properties' ])) {
2020-11-14 11:52:38 +00:00
$temp [ 'requestBody' ] = $body ;
}
2020-11-11 22:02:42 +00:00
$output [ 'paths' ][ $url ][ \strtolower ( $route -> getMethod ())] = $temp ;
}
2022-01-04 10:42:23 +00:00
2020-11-11 22:02:42 +00:00
foreach ( $this -> models as $model ) {
2022-06-29 23:41:49 +00:00
$this -> getNestedModels ( $model , $usedModels );
2021-04-08 09:24:41 +00:00
}
2022-01-04 10:42:23 +00:00
2020-11-11 22:02:42 +00:00
foreach ( $this -> models as $model ) {
2021-04-08 09:27:59 +00:00
if ( ! in_array ( $model -> getType (), $usedModels ) && $model -> getType () !== 'error' ) {
2021-04-08 08:46:45 +00:00
continue ;
}
2020-11-11 22:02:42 +00:00
$required = $model -> getRequired ();
$rules = $model -> getRules ();
2025-08-01 07:39:39 +00:00
$examples = [];
2020-11-11 22:02:42 +00:00
$output [ 'components' ][ 'schemas' ][ $model -> getType ()] = [
2020-11-14 11:52:38 +00:00
'description' => $model -> getName (),
2020-11-11 22:02:42 +00:00
'type' => 'object' ,
];
2022-05-23 14:54:50 +00:00
if ( ! empty ( $rules )) {
2020-11-14 11:52:38 +00:00
$output [ 'components' ][ 'schemas' ][ $model -> getType ()][ 'properties' ] = [];
}
2022-05-23 14:54:50 +00:00
if ( $model -> isAny ()) {
2020-11-11 22:02:42 +00:00
$output [ 'components' ][ 'schemas' ][ $model -> getType ()][ 'additionalProperties' ] = true ;
}
2021-09-30 09:59:01 +00:00
2022-05-23 14:54:50 +00:00
if ( ! empty ( $required )) {
2020-11-11 22:02:42 +00:00
$output [ 'components' ][ 'schemas' ][ $model -> getType ()][ 'required' ] = $required ;
}
2022-05-23 14:54:50 +00:00
foreach ( $model -> getRules () as $name => $rule ) {
2026-03-24 12:33:15 +00:00
if (( $rule [ 'hidden' ] ? ? false ) === true ) {
continue ;
}
2020-11-11 22:02:42 +00:00
$type = '' ;
$format = null ;
$items = null ;
2025-08-01 07:47:45 +00:00
$examples [ $name ] = $rule [ 'example' ] ? ? null ;
2020-11-11 22:02:42 +00:00
switch ( $rule [ 'type' ]) {
case 'string' :
2022-07-28 12:38:54 +00:00
case 'datetime' :
2024-09-26 11:59:41 +00:00
case 'payload' :
2020-11-11 22:02:42 +00:00
$type = 'string' ;
break ;
2021-09-30 09:59:01 +00:00
2026-02-25 07:44:44 +00:00
case 'id' :
2026-03-20 00:08:19 +00:00
$type = 'string' ;
2026-02-25 07:44:44 +00:00
break ;
2025-09-15 04:58:34 +00:00
case 'enum' :
$type = 'string' ;
break ;
2022-03-01 00:45:24 +00:00
case 'json' :
$type = 'object' ;
$output [ 'components' ][ 'schemas' ][ $model -> getType ()][ 'properties' ][ $name ][ 'additionalProperties' ] = true ;
break ;
2025-09-05 15:08:46 +00:00
2025-09-05 14:34:56 +00:00
case 'array' :
$type = 'array' ;
break ;
2025-09-05 15:08:46 +00:00
2020-11-11 22:02:42 +00:00
case 'integer' :
$type = 'integer' ;
2026-01-13 10:40:47 +00:00
$format = $rule [ 'format' ] ? ? 'int32' ;
2020-11-11 22:02:42 +00:00
break ;
case 'float' :
$type = 'number' ;
$format = 'float' ;
break ;
2021-09-21 12:25:41 +00:00
case 'double' :
$type = 'number' ;
$format = 'double' ;
break ;
2021-09-30 09:59:01 +00:00
2020-11-11 22:02:42 +00:00
case 'boolean' :
2020-11-14 11:52:38 +00:00
$type = 'boolean' ;
2020-11-11 22:02:42 +00:00
break ;
2021-09-30 09:59:01 +00:00
2020-11-11 22:02:42 +00:00
default :
$type = 'object' ;
$rule [ 'type' ] = ( $rule [ 'type' ]) ? $rule [ 'type' ] : 'none' ;
2022-05-23 14:54:50 +00:00
if ( \is_array ( $rule [ 'type' ])) {
2026-04-16 07:32:57 +00:00
$resolvedModels = \array_map ( function ( string $type ) {
foreach ( $this -> models as $model ) {
if ( $model -> getType () === $type ) {
return $model ;
}
}
throw new \RuntimeException ( " Unresolved model ' { $type } '. Ensure the model is registered. " );
}, $rule [ 'type' ]);
2022-05-23 14:54:50 +00:00
if ( $rule [ 'array' ]) {
2026-04-16 05:37:07 +00:00
$items = \array_filter ([
2022-05-23 14:54:50 +00:00
'anyOf' => \array_map ( function ( $type ) {
return [ '$ref' => '#/components/schemas/' . $type ];
2026-04-16 05:37:07 +00:00
}, $rule [ 'type' ]),
2026-04-16 07:32:57 +00:00
'discriminator' => $this -> getDiscriminator ( $resolvedModels , '#/components/schemas/' ),
2026-04-16 05:37:07 +00:00
]);
2021-09-23 11:12:50 +00:00
} else {
2026-04-16 05:37:07 +00:00
$items = \array_filter ([
2022-05-23 14:54:50 +00:00
'oneOf' => \array_map ( function ( $type ) {
return [ '$ref' => '#/components/schemas/' . $type ];
2026-04-16 05:37:07 +00:00
}, $rule [ 'type' ]),
2026-04-16 07:32:57 +00:00
'discriminator' => $this -> getDiscriminator ( $resolvedModels , '#/components/schemas/' ),
2026-04-16 05:37:07 +00:00
]);
2021-09-23 11:12:50 +00:00
}
2021-09-21 12:01:18 +00:00
} else {
$items = [
2022-05-23 14:54:50 +00:00
'$ref' => '#/components/schemas/' . $rule [ 'type' ],
2021-09-21 12:01:18 +00:00
];
}
2020-11-11 22:02:42 +00:00
break ;
}
2025-08-08 11:10:12 +00:00
$readOnly = $rule [ 'readOnly' ] ? ? false ;
2022-05-23 14:54:50 +00:00
if ( $rule [ 'array' ]) {
2020-11-11 22:02:42 +00:00
$output [ 'components' ][ 'schemas' ][ $model -> getType ()][ 'properties' ][ $name ] = [
'type' => 'array' ,
'description' => $rule [ 'description' ] ? ? '' ,
'items' => [
'type' => $type ,
2021-01-31 23:07:11 +00:00
],
'x-example' => $rule [ 'example' ] ? ? null ,
2020-11-11 22:02:42 +00:00
];
2022-05-23 14:54:50 +00:00
if ( $format ) {
2020-11-11 22:02:42 +00:00
$output [ 'components' ][ 'schemas' ][ $model -> getType ()][ 'properties' ][ $name ][ 'items' ][ 'format' ] = $format ;
}
2025-08-08 11:10:12 +00:00
if ( $readOnly ) {
$output [ 'components' ][ 'schemas' ][ $model -> getType ()][ 'properties' ][ $name ][ 'readOnly' ] = true ;
2025-08-08 07:23:58 +00:00
}
2020-11-11 22:02:42 +00:00
} else {
$output [ 'components' ][ 'schemas' ][ $model -> getType ()][ 'properties' ][ $name ] = [
'type' => $type ,
'description' => $rule [ 'description' ] ? ? '' ,
2020-11-14 11:52:38 +00:00
'x-example' => $rule [ 'example' ] ? ? null ,
2020-11-11 22:02:42 +00:00
];
2022-05-23 14:54:50 +00:00
if ( $format ) {
2020-11-11 22:02:42 +00:00
$output [ 'components' ][ 'schemas' ][ $model -> getType ()][ 'properties' ][ $name ][ 'format' ] = $format ;
}
2025-08-08 11:10:12 +00:00
if ( $readOnly ) {
$output [ 'components' ][ 'schemas' ][ $model -> getType ()][ 'properties' ][ $name ][ 'readOnly' ] = true ;
2025-08-08 07:23:58 +00:00
}
2022-03-01 00:45:24 +00:00
}
2022-05-23 14:54:50 +00:00
if ( $items ) {
2022-03-01 00:45:24 +00:00
$output [ 'components' ][ 'schemas' ][ $model -> getType ()][ 'properties' ][ $name ][ 'items' ] = $items ;
2020-11-11 22:02:42 +00:00
}
2025-09-15 04:58:34 +00:00
if ( $rule [ 'type' ] === 'enum' && ! empty ( $rule [ 'enum' ])) {
if ( $rule [ 'array' ]) {
2025-12-03 06:34:30 +00:00
$output [ 'components' ][ 'schemas' ][ $model -> getType ()][ 'properties' ][ $name ][ 'items' ][ 'enum' ] = \array_values ( $rule [ 'enum' ]);
2025-09-23 05:17:39 +00:00
$enumName = $this -> getResponseEnumName ( $model -> getType (), $name );
if ( $enumName ) {
$output [ 'components' ][ 'schemas' ][ $model -> getType ()][ 'properties' ][ $name ][ 'items' ][ 'x-enum-name' ] = $enumName ;
}
2025-09-15 04:58:34 +00:00
} else {
2025-12-03 06:34:30 +00:00
$output [ 'components' ][ 'schemas' ][ $model -> getType ()][ 'properties' ][ $name ][ 'enum' ] = \array_values ( $rule [ 'enum' ]);
2025-09-23 05:17:39 +00:00
$enumName = $this -> getResponseEnumName ( $model -> getType (), $name );
if ( $enumName ) {
$output [ 'components' ][ 'schemas' ][ $model -> getType ()][ 'properties' ][ $name ][ 'x-enum-name' ] = $enumName ;
}
2025-09-15 04:58:34 +00:00
}
}
2021-11-25 10:08:39 +00:00
if ( ! in_array ( $name , $required )) {
$output [ 'components' ][ 'schemas' ][ $model -> getType ()][ 'properties' ][ $name ][ 'nullable' ] = true ;
}
2020-11-11 22:02:42 +00:00
}
2025-08-01 07:39:39 +00:00
2025-09-23 05:17:39 +00:00
/** @var Any $model */
2025-08-01 09:56:34 +00:00
if ( $model -> isAny () && ! empty ( $model -> getSampleData ())) {
$examples = array_merge ( $examples , $model -> getSampleData ());
2025-08-01 07:39:39 +00:00
}
$output [ 'components' ][ 'schemas' ][ $model -> getType ()][ 'example' ] = $examples ;
2020-11-11 22:02:42 +00:00
}
\ksort ( $output [ 'paths' ]);
2026-01-14 09:59:30 +00:00
return $output ;
2020-11-11 22:02:42 +00:00
}
2025-01-17 04:39:16 +00:00
}