2022-07-13 07:02:55 +00:00
< ? php
2022-07-14 02:04:31 +00:00
2022-11-14 10:01:41 +00:00
namespace Appwrite\Platform\Tasks ;
2022-07-13 07:02:55 +00:00
2025-01-17 04:31:39 +00:00
use Appwrite\SDK\AuthType ;
2022-07-13 07:02:55 +00:00
use Appwrite\Specification\Format\OpenAPI3 ;
use Appwrite\Specification\Format\Swagger2 ;
use Appwrite\Specification\Specification ;
2024-10-08 07:54:40 +00:00
use Appwrite\Utopia\Request as AppwriteRequest ;
use Appwrite\Utopia\Response as AppwriteResponse ;
2022-11-13 08:26:50 +00:00
use Exception ;
2024-10-08 07:54:40 +00:00
use Swoole\Http\Request as SwooleRequest ;
use Swoole\Http\Response as SwooleResponse ;
use Utopia\App ;
2022-10-15 22:44:41 +00:00
use Utopia\Cache\Adapter\None ;
use Utopia\Cache\Cache ;
2022-07-13 07:02:55 +00:00
use Utopia\CLI\Console ;
use Utopia\Config\Config ;
2022-10-15 22:44:41 +00:00
use Utopia\Database\Adapter\MySQL ;
use Utopia\Database\Database ;
2024-03-08 12:57:20 +00:00
use Utopia\Platform\Action ;
2024-10-08 07:54:40 +00:00
use Utopia\Registry\Registry ;
use Utopia\Request as UtopiaRequest ;
use Utopia\Response as UtopiaResponse ;
2024-04-01 11:08:46 +00:00
use Utopia\System\System ;
2024-10-08 07:54:40 +00:00
use Utopia\Validator\Text ;
use Utopia\Validator\WhiteList ;
2022-07-13 07:02:55 +00:00
2022-07-14 02:04:31 +00:00
class Specs extends Action
{
2022-08-02 01:58:36 +00:00
public static function getName () : string
{
return 'specs' ;
}
2022-07-14 02:04:31 +00:00
2024-10-08 07:54:40 +00:00
public function getRequest () : UtopiaRequest
{
return new AppwriteRequest ( new SwooleRequest ());
}
public function getResponse () : UtopiaResponse
{
return new AppwriteResponse ( new SwooleResponse ());
}
2022-07-13 07:02:55 +00:00
public function __construct ()
{
$this
2022-08-02 01:58:36 +00:00
-> desc ( 'Generate Appwrite API specifications' )
2022-09-01 12:00:13 +00:00
-> param ( 'version' , 'latest' , new Text ( 16 ), 'Spec version' , true )
2022-07-13 07:02:55 +00:00
-> param ( 'mode' , 'normal' , new WhiteList ([ 'normal' , 'mocks' ]), 'Spec Mode' , true )
2024-10-08 07:54:40 +00:00
-> inject ( 'register' )
-> callback ( fn ( string $version , string $mode , Registry $register ) => $this -> action ( $version , $mode , $register ));
2022-07-13 07:02:55 +00:00
}
2024-10-08 07:54:40 +00:00
public function action ( string $version , string $mode , Registry $register ) : void
2022-07-13 07:02:55 +00:00
{
2024-10-08 07:54:40 +00:00
$appRoutes = App :: getRoutes ();
$response = $this -> getResponse ();
2022-07-13 07:02:55 +00:00
$mocks = ( $mode === 'mocks' );
2022-10-15 22:44:41 +00:00
// Mock dependencies
2024-10-08 07:54:40 +00:00
App :: setResource ( 'request' , fn () => $this -> getRequest ());
App :: setResource ( 'response' , fn () => $response );
2024-12-12 10:30:26 +00:00
App :: setResource ( 'dbForPlatform' , fn () => new Database ( new MySQL ( '' ), new Cache ( new None ())));
2024-10-08 07:54:40 +00:00
App :: setResource ( 'dbForProject' , fn () => new Database ( new MySQL ( '' ), new Cache ( new None ())));
2022-07-13 07:02:55 +00:00
$platforms = [
'client' => APP_PLATFORM_CLIENT ,
'server' => APP_PLATFORM_SERVER ,
'console' => APP_PLATFORM_CONSOLE ,
];
$authCounts = [
'client' => 1 ,
'server' => 2 ,
'console' => 1 ,
];
$keys = [
APP_PLATFORM_CLIENT => [
'Project' => [
'type' => 'apiKey' ,
'name' => 'X-Appwrite-Project' ,
'description' => 'Your project ID' ,
'in' => 'header' ,
],
'JWT' => [
'type' => 'apiKey' ,
'name' => 'X-Appwrite-JWT' ,
'description' => 'Your secret JSON Web Token' ,
'in' => 'header' ,
],
'Locale' => [
'type' => 'apiKey' ,
'name' => 'X-Appwrite-Locale' ,
'description' => '' ,
'in' => 'header' ,
],
2023-12-12 15:39:24 +00:00
'Session' => [
'type' => 'apiKey' ,
'name' => 'X-Appwrite-Session' ,
'description' => 'The user session to authenticate with' ,
'in' => 'header' ,
]
2022-07-13 07:02:55 +00:00
],
APP_PLATFORM_SERVER => [
'Project' => [
'type' => 'apiKey' ,
'name' => 'X-Appwrite-Project' ,
'description' => 'Your project ID' ,
'in' => 'header' ,
],
'Key' => [
'type' => 'apiKey' ,
'name' => 'X-Appwrite-Key' ,
'description' => 'Your secret API key' ,
'in' => 'header' ,
],
'JWT' => [
'type' => 'apiKey' ,
'name' => 'X-Appwrite-JWT' ,
'description' => 'Your secret JSON Web Token' ,
'in' => 'header' ,
],
'Locale' => [
'type' => 'apiKey' ,
'name' => 'X-Appwrite-Locale' ,
'description' => '' ,
'in' => 'header' ,
2023-12-12 15:39:24 +00:00
],
'Session' => [
'type' => 'apiKey' ,
'name' => 'X-Appwrite-Session' ,
'description' => 'The user session to authenticate with' ,
'in' => 'header' ,
],
'ForwardedUserAgent' => [
'type' => 'apiKey' ,
'name' => 'X-Forwarded-User-Agent' ,
'description' => 'The user agent string of the client that made the request' ,
'in' => 'header' ,
2022-07-13 07:02:55 +00:00
],
],
APP_PLATFORM_CONSOLE => [
'Project' => [
'type' => 'apiKey' ,
'name' => 'X-Appwrite-Project' ,
'description' => 'Your project ID' ,
'in' => 'header' ,
],
'Key' => [
'type' => 'apiKey' ,
'name' => 'X-Appwrite-Key' ,
'description' => 'Your secret API key' ,
'in' => 'header' ,
],
'JWT' => [
'type' => 'apiKey' ,
'name' => 'X-Appwrite-JWT' ,
'description' => 'Your secret JSON Web Token' ,
'in' => 'header' ,
],
'Locale' => [
'type' => 'apiKey' ,
'name' => 'X-Appwrite-Locale' ,
'description' => '' ,
'in' => 'header' ,
],
'Mode' => [
'type' => 'apiKey' ,
'name' => 'X-Appwrite-Mode' ,
'description' => '' ,
'in' => 'header' ,
],
],
];
foreach ( $platforms as $platform ) {
$routes = [];
$models = [];
$services = [];
foreach ( $appRoutes as $key => $method ) {
foreach ( $method as $route ) {
2025-01-17 04:31:39 +00:00
$sdks = $route -> getLabel ( 'sdk' , false );
if ( empty ( $sdks )) {
2024-02-24 14:38:09 +00:00
continue ;
}
2025-01-17 04:31:39 +00:00
if ( ! \is_array ( $sdks )) {
$sdks = [ $sdks ];
2022-07-13 07:02:55 +00:00
}
2025-01-17 04:31:39 +00:00
foreach ( $sdks as $sdk ) {
/** @var \Appwrite\SDK\Method $sdks */
2022-07-13 07:02:55 +00:00
2025-01-17 04:31:39 +00:00
$hide = $sdk -> isHidden ();
if ( $hide === true || ( \is_array ( $hide ) && \in_array ( $platform , $hide ))) {
continue ;
}
2022-07-13 07:02:55 +00:00
2025-01-17 04:31:39 +00:00
$routeSecurity = $sdk -> getAuth ();
$sdkPlatforms = [];
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 ;
}
}
2022-07-13 07:02:55 +00:00
2025-01-17 04:31:39 +00:00
if ( empty ( $routeSecurity )) {
$sdkPlatforms [] = APP_PLATFORM_SERVER ;
$sdkPlatforms [] = APP_PLATFORM_CLIENT ;
}
2022-07-13 07:02:55 +00:00
2025-01-17 04:31:39 +00:00
if ( ! $route -> getLabel ( 'docs' , true )) {
continue ;
}
2022-07-13 07:02:55 +00:00
2025-01-17 04:31:39 +00:00
if ( $route -> getLabel ( 'mock' , false ) && ! $mocks ) {
continue ;
}
if ( ! $route -> getLabel ( 'mock' , false ) && $mocks ) {
continue ;
}
if ( empty ( $sdk -> getNamespace ())) {
continue ;
}
2022-07-13 07:02:55 +00:00
2025-01-17 04:31:39 +00:00
if ( $platform !== APP_PLATFORM_CONSOLE && ! \in_array ( $platforms [ $platform ], $sdkPlatforms )) {
continue ;
}
$routes [] = $route ;
}
2022-07-13 07:02:55 +00:00
}
}
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 ;
}
$services [] = [
'name' => $service [ 'key' ] ? ? '' ,
'description' => $service [ 'subtitle' ] ? ? '' ,
'x-globalAttributes' => $service [ 'globalAttributes' ] ? ? [],
];
}
2024-10-08 07:54:40 +00:00
$models = $response -> getModels ();
2022-07-13 07:02:55 +00:00
foreach ( $models as $key => $value ) {
if ( $platform !== APP_PLATFORM_CONSOLE && ! $value -> isPublic ()) {
unset ( $models [ $key ]);
}
}
2022-10-28 14:45:16 +00:00
2024-10-08 07:54:40 +00:00
$arguments = [ new App ( 'UTC' ), $services , $routes , $models , $keys [ $platform ], $authCounts [ $platform ] ? ? 0 ];
2022-07-13 07:02:55 +00:00
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 );
2024-04-01 11:02:47 +00:00
$endpoint = System :: getEnv ( '_APP_HOME' , '[HOSTNAME]' );
2024-08-02 12:59:47 +00:00
$email = System :: getEnv ( '_APP_SYSTEM_TEAM_EMAIL' , APP_EMAIL_TEAM );
2022-07-13 07:02:55 +00:00
$formatInstance
-> setParam ( 'name' , APP_NAME )
-> setParam ( 'description' , 'Appwrite backend as a service cuts up to 70% of the time and costs required for building a modern application. We abstract and simplify common development tasks behind a REST APIs, to help you develop your app in a fast and secure way. For full API documentation and tutorials go to [https://appwrite.io/docs](https://appwrite.io/docs)' )
2024-02-12 01:21:28 +00:00
-> setParam ( 'endpoint' , 'https://cloud.appwrite.io/v1' )
2022-07-13 07:02:55 +00:00
-> setParam ( 'version' , APP_VERSION_STABLE )
-> setParam ( 'terms' , $endpoint . '/policy/terms' )
-> setParam ( 'support.email' , $email )
-> setParam ( 'support.url' , $endpoint . '/support' )
-> setParam ( 'contact.name' , APP_NAME . ' Team' )
-> setParam ( 'contact.email' , $email )
-> setParam ( 'contact.url' , $endpoint . '/support' )
-> setParam ( 'license.name' , 'BSD-3-Clause' )
-> setParam ( 'license.url' , 'https://raw.githubusercontent.com/appwrite/appwrite/master/LICENSE' )
-> setParam ( 'docs.description' , 'Full API docs, specs and tutorials' )
-> setParam ( 'docs.url' , $endpoint . '/docs' );
if ( $mocks ) {
2023-09-07 23:25:42 +00:00
$path = __DIR__ . '/../../../../app/config/specs/' . $format . '-mocks-' . $platform . '.json' ;
2022-07-13 07:02:55 +00:00
2024-09-08 07:24:17 +00:00
if ( ! file_put_contents ( $path , json_encode ( $specs -> parse (), JSON_PRETTY_PRINT ))) {
2022-07-13 07:02:55 +00:00
throw new Exception ( 'Failed to save mocks spec file: ' . $path );
}
Console :: success ( 'Saved mocks spec file: ' . realpath ( $path ));
continue ;
}
2023-02-15 11:55:01 +00:00
$path = __DIR__ . '/../../../../app/config/specs/' . $format . '-' . $version . '-' . $platform . '.json' ;
2022-07-13 07:02:55 +00:00
2024-09-08 07:24:17 +00:00
if ( ! file_put_contents ( $path , json_encode ( $specs -> parse (), JSON_PRETTY_PRINT ))) {
2022-07-13 07:02:55 +00:00
throw new Exception ( 'Failed to save spec file: ' . $path );
}
Console :: success ( 'Saved spec file: ' . realpath ( $path ));
}
}
}
2025-01-17 04:39:16 +00:00
}