diff --git a/src/Appwrite/Platform/Tasks/Specs.php b/src/Appwrite/Platform/Tasks/Specs.php index 0953610a69..4e7480ebe0 100644 --- a/src/Appwrite/Platform/Tasks/Specs.php +++ b/src/Appwrite/Platform/Tasks/Specs.php @@ -21,7 +21,6 @@ use Utopia\Database\Adapter\MySQL; use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\DI\Container; -use Utopia\Http\Adapter\FPM\Server as FPMServer; use Utopia\Http\Http; use Utopia\Http\Request as UtopiaRequest; use Utopia\Http\Response as UtopiaResponse; @@ -60,6 +59,22 @@ class Specs extends Action return new AppwriteResponse(new SwooleResponse()); } + protected function getSpecsContainer(UtopiaResponse $response): Container + { + $container = new Container(); + $container->set('request', fn () => $this->getRequest()); + $container->set('response', fn () => $response); + $container->set('dbForPlatform', fn () => new Database(new MySQL(''), new Cache(new None()))); + $container->set('dbForProject', fn () => new Database(new MySQL(''), new Cache(new None()))); + $container->set('redirectValidator', fn () => new Redirect([], [])); + $container->set('project', fn () => new Document([])); + $container->set('passwordsDictionary', fn () => []); + $container->set('localeCodes', fn () => \array_map(fn ($locale) => $locale['code'], Config::getParam('locale-codes', []))); + $container->set('plan', fn () => []); + + return $container; + } + protected function getFormatInstance(string $format, array $arguments) { return match ($format) { @@ -340,17 +355,8 @@ class Specs extends Action $mocks = ($mode === 'mocks'); - // Mock dependencies needed by param validator injections in route definitions - $specsContainer = new Container(); - $specsContainer->set('request', fn () => $this->getRequest()); - $specsContainer->set('response', fn () => $response); - $specsContainer->set('dbForPlatform', fn () => new Database(new MySQL(''), new Cache(new None()))); - $specsContainer->set('dbForProject', fn () => new Database(new MySQL(''), new Cache(new None()))); - $specsContainer->set('redirectValidator', fn () => new Redirect([], [])); - $specsContainer->set('project', fn () => new Document([])); - $specsContainer->set('passwordsDictionary', fn () => []); - $specsContainer->set('localeCodes', fn () => \array_map(fn ($locale) => $locale['code'], Config::getParam('locale-codes', []))); - $specsContainer->set('plan', fn () => []); + // Specs generation only needs route validator dependencies, not an HTTP server runtime. + $specsContainer = $this->getSpecsContainer($response); $platforms = static::getPlatforms(); $authCounts = $this->getAuthCounts(); @@ -448,7 +454,7 @@ class Specs extends Action } $arguments = [ - new Http(new FPMServer($specsContainer), 'UTC'), + $specsContainer, $services, $routes, $models, diff --git a/src/Appwrite/SDK/Specification/Format.php b/src/Appwrite/SDK/Specification/Format.php index 628f0f2f8f..02fac12a7a 100644 --- a/src/Appwrite/SDK/Specification/Format.php +++ b/src/Appwrite/SDK/Specification/Format.php @@ -4,12 +4,12 @@ namespace Appwrite\SDK\Specification; use Appwrite\Utopia\Response\Model; use Utopia\Config\Config; -use Utopia\Http\Http; +use Utopia\DI\Container; use Utopia\Http\Route; abstract class Format { - protected Http $app; + protected Container $container; /** * @var array @@ -80,9 +80,9 @@ abstract class Format protected array $enumBlacklist = []; - public function __construct(Http $app, array $services, array $routes, array $models, array $keys, int $authCount, string $platform) + public function __construct(Container $container, array $services, array $routes, array $models, array $keys, int $authCount, string $platform) { - $this->app = $app; + $this->container = $container; $this->services = $services; $this->routes = $routes; $this->models = $models; @@ -210,6 +210,28 @@ abstract class Format return $this->services; } + /** + * @param list $injections + * @return array + */ + protected function getResources(array $injections): array + { + $resources = []; + + foreach ($injections as $name) { + $resources[$name] = $this->container->get($name); + } + + return $resources; + } + + protected function getValidator(array $param): mixed + { + return \is_callable($param['validator']) + ? ($param['validator'])(...$this->getResources($param['injections'] ?? [])) + : $param['validator']; + } + protected function getDescriptionContents(?string $description): string { if ($description === null || $description === '') { diff --git a/src/Appwrite/SDK/Specification/Format/OpenAPI3.php b/src/Appwrite/SDK/Specification/Format/OpenAPI3.php index 94e0f831b7..7da48fc2ca 100644 --- a/src/Appwrite/SDK/Specification/Format/OpenAPI3.php +++ b/src/Appwrite/SDK/Specification/Format/OpenAPI3.php @@ -379,7 +379,7 @@ class OpenAPI3 extends Format /** * @var \Utopia\Validator $validator */ - $validator = (\is_callable($param['validator'])) ? call_user_func_array($param['validator'], $this->app->getResources($param['injections'])) : $param['validator']; + $validator = $this->getValidator($param); $node = [ 'name' => $name, diff --git a/src/Appwrite/SDK/Specification/Format/Swagger2.php b/src/Appwrite/SDK/Specification/Format/Swagger2.php index 14a18eea2e..d95f99bb70 100644 --- a/src/Appwrite/SDK/Specification/Format/Swagger2.php +++ b/src/Appwrite/SDK/Specification/Format/Swagger2.php @@ -381,9 +381,7 @@ class Swagger2 extends Format } /** @var Validator $validator */ - $validator = (\is_callable($param['validator'])) - ? ($param['validator'])(...$this->app->getResources($param['injections'])) - : $param['validator']; + $validator = $this->getValidator($param); $node = [ 'name' => $name, diff --git a/tests/unit/SDK/Specification/FormatTest.php b/tests/unit/SDK/Specification/FormatTest.php new file mode 100644 index 0000000000..e72df52719 --- /dev/null +++ b/tests/unit/SDK/Specification/FormatTest.php @@ -0,0 +1,46 @@ +set('first', fn () => 'alpha'); + $container->set('second', fn () => 'beta'); + + $format = new class($container, [], [], [], [], 0, APP_SDK_PLATFORM_SERVER) extends Format { + public function getName(): string + { + return 'stub'; + } + + public function parse(): array + { + return []; + } + + public function resolveValidatorForTest(array $param): mixed + { + return $this->getValidator($param); + } + }; + + $validator = $format->resolveValidatorForTest([ + 'validator' => fn (string $first, string $second) => new Text( + ($first === 'alpha' && $second === 'beta') ? 9 : 1 + ), + 'injections' => ['second', 'first'], + ]); + + $this->assertInstanceOf(Text::class, $validator); + $this->assertTrue($validator->isValid('123456789')); + $this->assertFalse($validator->isValid('1234567890')); + } +}