From 680cb04de792822fda78dc37f2c7d47258ae7a7f Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Thu, 16 Apr 2026 11:07:07 +0530 Subject: [PATCH 01/14] feat(specs): add discriminators for polymorphic responses --- app/init/models.php | 6 +- .../Repositories/Detections/Create.php | 1 + .../Http/Installations/Repositories/XList.php | 1 + src/Appwrite/SDK/Specification/Format.php | 111 ++++++++++++++++++ .../SDK/Specification/Format/OpenAPI3.php | 27 +++-- .../SDK/Specification/Format/Swagger2.php | 27 +++-- .../Utopia/Response/Model/Detection.php | 9 +- .../Response/Model/DetectionFramework.php | 6 +- .../Response/Model/DetectionRuntime.php | 6 +- .../Model/ProviderRepositoryFrameworkList.php | 30 +++++ .../Model/ProviderRepositoryRuntimeList.php | 30 +++++ 11 files changed, 231 insertions(+), 23 deletions(-) create mode 100644 src/Appwrite/Utopia/Response/Model/ProviderRepositoryFrameworkList.php create mode 100644 src/Appwrite/Utopia/Response/Model/ProviderRepositoryRuntimeList.php diff --git a/app/init/models.php b/app/init/models.php index dd97b03652..c92295ae33 100644 --- a/app/init/models.php +++ b/app/init/models.php @@ -117,7 +117,9 @@ use Appwrite\Utopia\Response\Model\Project; use Appwrite\Utopia\Response\Model\Provider; use Appwrite\Utopia\Response\Model\ProviderRepository; use Appwrite\Utopia\Response\Model\ProviderRepositoryFramework; +use Appwrite\Utopia\Response\Model\ProviderRepositoryFrameworkList; use Appwrite\Utopia\Response\Model\ProviderRepositoryRuntime; +use Appwrite\Utopia\Response\Model\ProviderRepositoryRuntimeList; use Appwrite\Utopia\Response\Model\ResourceToken; use Appwrite\Utopia\Response\Model\Row; use Appwrite\Utopia\Response\Model\Rule; @@ -190,8 +192,8 @@ Response::setModel(new BaseList('Site Templates List', Response::MODEL_TEMPLATE_ Response::setModel(new BaseList('Functions List', Response::MODEL_FUNCTION_LIST, 'functions', Response::MODEL_FUNCTION)); Response::setModel(new BaseList('Function Templates List', Response::MODEL_TEMPLATE_FUNCTION_LIST, 'templates', Response::MODEL_TEMPLATE_FUNCTION)); Response::setModel(new BaseList('Installations List', Response::MODEL_INSTALLATION_LIST, 'installations', Response::MODEL_INSTALLATION)); -Response::setModel(new BaseList('Framework Provider Repositories List', Response::MODEL_PROVIDER_REPOSITORY_FRAMEWORK_LIST, 'frameworkProviderRepositories', Response::MODEL_PROVIDER_REPOSITORY_FRAMEWORK)); -Response::setModel(new BaseList('Runtime Provider Repositories List', Response::MODEL_PROVIDER_REPOSITORY_RUNTIME_LIST, 'runtimeProviderRepositories', Response::MODEL_PROVIDER_REPOSITORY_RUNTIME)); +Response::setModel(new ProviderRepositoryFrameworkList()); +Response::setModel(new ProviderRepositoryRuntimeList()); Response::setModel(new BaseList('Branches List', Response::MODEL_BRANCH_LIST, 'branches', Response::MODEL_BRANCH)); Response::setModel(new BaseList('Frameworks List', Response::MODEL_FRAMEWORK_LIST, 'frameworks', Response::MODEL_FRAMEWORK)); Response::setModel(new BaseList('Runtimes List', Response::MODEL_RUNTIME_LIST, 'runtimes', Response::MODEL_RUNTIME)); diff --git a/src/Appwrite/Platform/Modules/VCS/Http/Installations/Repositories/Detections/Create.php b/src/Appwrite/Platform/Modules/VCS/Http/Installations/Repositories/Detections/Create.php index 5dd5c6dcfa..6295fcd03b 100644 --- a/src/Appwrite/Platform/Modules/VCS/Http/Installations/Repositories/Detections/Create.php +++ b/src/Appwrite/Platform/Modules/VCS/Http/Installations/Repositories/Detections/Create.php @@ -307,6 +307,7 @@ class Create extends Action ]; } + $output->setAttribute('type', $type); $output->setAttribute('variables', $variables); $response->dynamic($output, $type === 'framework' ? Response::MODEL_DETECTION_FRAMEWORK : Response::MODEL_DETECTION_RUNTIME); diff --git a/src/Appwrite/Platform/Modules/VCS/Http/Installations/Repositories/XList.php b/src/Appwrite/Platform/Modules/VCS/Http/Installations/Repositories/XList.php index d5b2b48175..b4172fabdf 100644 --- a/src/Appwrite/Platform/Modules/VCS/Http/Installations/Repositories/XList.php +++ b/src/Appwrite/Platform/Modules/VCS/Http/Installations/Repositories/XList.php @@ -313,6 +313,7 @@ class XList extends Action }, $repos); $response->dynamic(new Document([ + 'type' => $type, $type === 'framework' ? 'frameworkProviderRepositories' : 'runtimeProviderRepositories' => $repos, 'total' => $total, ]), ($type === 'framework') ? Response::MODEL_PROVIDER_REPOSITORY_FRAMEWORK_LIST : Response::MODEL_PROVIDER_REPOSITORY_RUNTIME_LIST); diff --git a/src/Appwrite/SDK/Specification/Format.php b/src/Appwrite/SDK/Specification/Format.php index 91b090a9f6..f762d2bbaa 100644 --- a/src/Appwrite/SDK/Specification/Format.php +++ b/src/Appwrite/SDK/Specification/Format.php @@ -263,6 +263,117 @@ abstract class Format return $contents; } + protected function getRegisteredModel(string $type): Model + { + foreach ($this->models as $model) { + if ($model->getType() === $type) { + return $model; + } + } + + throw new \RuntimeException("Unresolved model '{$type}'. Ensure the model is registered."); + } + + /** + * @param array $models + * @return array|null + */ + protected function getUnionDiscriminator(array $models, string $refPrefix): ?array + { + if (\count($models) < 2) { + return null; + } + + $candidateKeys = null; + + foreach ($models as $model) { + $keys = []; + + foreach ($model->conditions as $key => $condition) { + if ($this->isDiscriminatorConditionSupported($condition)) { + $keys[] = $key; + } + } + + $candidateKeys = $candidateKeys === null + ? $keys + : \array_values(\array_intersect($candidateKeys, $keys)); + } + + if (empty($candidateKeys)) { + return null; + } + + foreach ($candidateKeys as $key) { + $mapping = []; + $matchedModels = []; + + foreach ($models as $model) { + $rules = $model->getRules(); + + if (!isset($rules[$key]) || ($rules[$key]['required'] ?? false) !== true) { + continue 2; + } + + $condition = $model->conditions[$key]; + $values = \is_array($condition) ? $condition : [$condition]; + + if (isset($rules[$key]['enum']) && \is_array($rules[$key]['enum'])) { + $values = \array_values(\array_filter( + $values, + fn (mixed $value) => \in_array($value, $rules[$key]['enum'], true) + )); + } + + if ($values === []) { + continue 2; + } + + foreach ($values as $value) { + $mappingKey = \is_bool($value) ? ($value ? 'true' : 'false') : (string) $value; + + if (isset($mapping[$mappingKey]) && $mapping[$mappingKey] !== $refPrefix . $model->getType()) { + continue 2; + } + + $mapping[$mappingKey] = $refPrefix . $model->getType(); + } + + $matchedModels[$model->getType()] = true; + } + + if (\count($matchedModels) !== \count($models)) { + continue; + } + + return [ + 'propertyName' => $key, + 'mapping' => $mapping, + ]; + } + + return null; + } + + protected function isDiscriminatorConditionSupported(mixed $condition): bool + { + if (\is_scalar($condition) || \is_bool($condition)) { + return true; + } + + if (!\is_array($condition) || $condition === []) { + return false; + } + + foreach ($condition as $value) { + if (!(\is_scalar($value) || \is_bool($value))) { + return false; + } + } + + return true; + } + protected function getRequestEnumName(string $service, string $method, string $param): ?string { /* `$service` is `$namespace` */ diff --git a/src/Appwrite/SDK/Specification/Format/OpenAPI3.php b/src/Appwrite/SDK/Specification/Format/OpenAPI3.php index b611558826..c5af43f64d 100644 --- a/src/Appwrite/SDK/Specification/Format/OpenAPI3.php +++ b/src/Appwrite/SDK/Specification/Format/OpenAPI3.php @@ -316,9 +316,10 @@ class OpenAPI3 extends Format 'description' => $modelDescription, 'content' => [ $produces => [ - 'schema' => [ - 'oneOf' => \array_map(fn ($m) => ['$ref' => '#/components/schemas/' . $m->getType()], $model) - ], + 'schema' => \array_filter([ + 'oneOf' => \array_map(fn ($m) => ['$ref' => '#/components/schemas/' . $m->getType()], $model), + 'discriminator' => $this->getUnionDiscriminator($model, '#/components/schemas/'), + ]), ], ], ]; @@ -901,17 +902,25 @@ class OpenAPI3 extends Format if (\is_array($rule['type'])) { if ($rule['array']) { - $items = [ + $items = \array_filter([ 'anyOf' => \array_map(function ($type) { return ['$ref' => '#/components/schemas/' . $type]; - }, $rule['type']) - ]; + }, $rule['type']), + 'discriminator' => $this->getUnionDiscriminator( + \array_map(fn (string $type) => $this->getRegisteredModel($type), $rule['type']), + '#/components/schemas/' + ), + ]); } else { - $items = [ + $items = \array_filter([ 'oneOf' => \array_map(function ($type) { return ['$ref' => '#/components/schemas/' . $type]; - }, $rule['type']) - ]; + }, $rule['type']), + 'discriminator' => $this->getUnionDiscriminator( + \array_map(fn (string $type) => $this->getRegisteredModel($type), $rule['type']), + '#/components/schemas/' + ), + ]); } } else { $items = [ diff --git a/src/Appwrite/SDK/Specification/Format/Swagger2.php b/src/Appwrite/SDK/Specification/Format/Swagger2.php index 413239f000..8e9a39a3c1 100644 --- a/src/Appwrite/SDK/Specification/Format/Swagger2.php +++ b/src/Appwrite/SDK/Specification/Format/Swagger2.php @@ -322,11 +322,12 @@ class Swagger2 extends Format } $temp['responses'][(string)$response->getCode() ?? '500'] = [ 'description' => $modelDescription, - 'schema' => [ + 'schema' => \array_filter([ 'x-oneOf' => \array_map(function ($m) { return ['$ref' => '#/definitions/' . $m->getType()]; - }, $model) - ], + }, $model), + 'x-discriminator' => $this->getUnionDiscriminator($model, '#/definitions/'), + ]), ]; } else { // Response definition using one type @@ -881,13 +882,21 @@ class Swagger2 extends Format if (\is_array($rule['type'])) { if ($rule['array']) { - $items = [ - 'x-anyOf' => \array_map(fn ($type) => ['$ref' => '#/definitions/' . $type], $rule['type']) - ]; + $items = \array_filter([ + 'x-anyOf' => \array_map(fn ($type) => ['$ref' => '#/definitions/' . $type], $rule['type']), + 'x-discriminator' => $this->getUnionDiscriminator( + \array_map(fn (string $type) => $this->getRegisteredModel($type), $rule['type']), + '#/definitions/' + ), + ]); } else { - $items = [ - 'x-oneOf' => \array_map(fn ($type) => ['$ref' => '#/definitions/' . $type], $rule['type']) - ]; + $items = \array_filter([ + 'x-oneOf' => \array_map(fn ($type) => ['$ref' => '#/definitions/' . $type], $rule['type']), + 'x-discriminator' => $this->getUnionDiscriminator( + \array_map(fn (string $type) => $this->getRegisteredModel($type), $rule['type']), + '#/definitions/' + ), + ]); } } else { $items = [ diff --git a/src/Appwrite/Utopia/Response/Model/Detection.php b/src/Appwrite/Utopia/Response/Model/Detection.php index 007182d1e9..d57e4a27c0 100644 --- a/src/Appwrite/Utopia/Response/Model/Detection.php +++ b/src/Appwrite/Utopia/Response/Model/Detection.php @@ -7,9 +7,16 @@ use Appwrite\Utopia\Response\Model; abstract class Detection extends Model { - public function __construct() + public function __construct(string $type) { $this + ->addRule('type', [ + 'type' => self::TYPE_ENUM, + 'description' => 'Repository detection type.', + 'default' => $type, + 'example' => $type, + 'enum' => ['runtime', 'framework'], + ]) ->addRule('variables', [ 'type' => Response::MODEL_DETECTION_VARIABLE, 'description' => 'Environment variables found in .env files', diff --git a/src/Appwrite/Utopia/Response/Model/DetectionFramework.php b/src/Appwrite/Utopia/Response/Model/DetectionFramework.php index 4cdf37bbcf..00f318ba4a 100644 --- a/src/Appwrite/Utopia/Response/Model/DetectionFramework.php +++ b/src/Appwrite/Utopia/Response/Model/DetectionFramework.php @@ -8,7 +8,11 @@ class DetectionFramework extends Detection { public function __construct() { - parent::__construct(); + $this->conditions = [ + 'type' => 'framework', + ]; + + parent::__construct('framework'); $this ->addRule('framework', [ diff --git a/src/Appwrite/Utopia/Response/Model/DetectionRuntime.php b/src/Appwrite/Utopia/Response/Model/DetectionRuntime.php index 1e63929092..94368f890c 100644 --- a/src/Appwrite/Utopia/Response/Model/DetectionRuntime.php +++ b/src/Appwrite/Utopia/Response/Model/DetectionRuntime.php @@ -8,7 +8,11 @@ class DetectionRuntime extends Detection { public function __construct() { - parent::__construct(); + $this->conditions = [ + 'type' => 'runtime', + ]; + + parent::__construct('runtime'); $this ->addRule('runtime', [ diff --git a/src/Appwrite/Utopia/Response/Model/ProviderRepositoryFrameworkList.php b/src/Appwrite/Utopia/Response/Model/ProviderRepositoryFrameworkList.php new file mode 100644 index 0000000000..9816ce806e --- /dev/null +++ b/src/Appwrite/Utopia/Response/Model/ProviderRepositoryFrameworkList.php @@ -0,0 +1,30 @@ + 'framework', + ]; + + public function __construct() + { + parent::__construct( + 'Framework Provider Repositories List', + Response::MODEL_PROVIDER_REPOSITORY_FRAMEWORK_LIST, + 'frameworkProviderRepositories', + Response::MODEL_PROVIDER_REPOSITORY_FRAMEWORK + ); + + $this->addRule('type', [ + 'type' => self::TYPE_ENUM, + 'description' => 'Repository detection type.', + 'default' => 'framework', + 'example' => 'framework', + 'enum' => ['runtime', 'framework'], + ]); + } +} diff --git a/src/Appwrite/Utopia/Response/Model/ProviderRepositoryRuntimeList.php b/src/Appwrite/Utopia/Response/Model/ProviderRepositoryRuntimeList.php new file mode 100644 index 0000000000..a30fa4d3b5 --- /dev/null +++ b/src/Appwrite/Utopia/Response/Model/ProviderRepositoryRuntimeList.php @@ -0,0 +1,30 @@ + 'runtime', + ]; + + public function __construct() + { + parent::__construct( + 'Runtime Provider Repositories List', + Response::MODEL_PROVIDER_REPOSITORY_RUNTIME_LIST, + 'runtimeProviderRepositories', + Response::MODEL_PROVIDER_REPOSITORY_RUNTIME + ); + + $this->addRule('type', [ + 'type' => self::TYPE_ENUM, + 'description' => 'Repository detection type.', + 'default' => 'runtime', + 'example' => 'runtime', + 'enum' => ['runtime', 'framework'], + ]); + } +} From 6a7280e7dddb162b9cc994dec9aa462a57551fd8 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Thu, 16 Apr 2026 11:12:43 +0530 Subject: [PATCH 02/14] refactor(specs): inline discriminator condition checks --- src/Appwrite/SDK/Specification/Format.php | 47 ++++++++++------------- 1 file changed, 20 insertions(+), 27 deletions(-) diff --git a/src/Appwrite/SDK/Specification/Format.php b/src/Appwrite/SDK/Specification/Format.php index f762d2bbaa..76e8bc8678 100644 --- a/src/Appwrite/SDK/Specification/Format.php +++ b/src/Appwrite/SDK/Specification/Format.php @@ -287,13 +287,7 @@ abstract class Format $candidateKeys = null; foreach ($models as $model) { - $keys = []; - - foreach ($model->conditions as $key => $condition) { - if ($this->isDiscriminatorConditionSupported($condition)) { - $keys[] = $key; - } - } + $keys = \array_keys($model->conditions); $candidateKeys = $candidateKeys === null ? $keys @@ -316,7 +310,25 @@ abstract class Format } $condition = $model->conditions[$key]; - $values = \is_array($condition) ? $condition : [$condition]; + if (!\is_array($condition)) { + if (!\is_scalar($condition)) { + continue 2; + } + + $values = [$condition]; + } else { + if ($condition === []) { + continue 2; + } + + $values = $condition; + + foreach ($values as $value) { + if (!\is_scalar($value)) { + continue 3; + } + } + } if (isset($rules[$key]['enum']) && \is_array($rules[$key]['enum'])) { $values = \array_values(\array_filter( @@ -355,25 +367,6 @@ abstract class Format return null; } - protected function isDiscriminatorConditionSupported(mixed $condition): bool - { - if (\is_scalar($condition) || \is_bool($condition)) { - return true; - } - - if (!\is_array($condition) || $condition === []) { - return false; - } - - foreach ($condition as $value) { - if (!(\is_scalar($value) || \is_bool($value))) { - return false; - } - } - - return true; - } - protected function getRequestEnumName(string $service, string $method, string $param): ?string { /* `$service` is `$namespace` */ From a0db02386088d162ebacdd2f17e99a9fe1151696 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Thu, 16 Apr 2026 11:15:08 +0530 Subject: [PATCH 03/14] refactor(specs): simplify discriminator resolution --- src/Appwrite/SDK/Specification/Format.php | 52 ++++++++++++------- .../SDK/Specification/Format/OpenAPI3.php | 6 +-- .../SDK/Specification/Format/Swagger2.php | 6 +-- 3 files changed, 38 insertions(+), 26 deletions(-) diff --git a/src/Appwrite/SDK/Specification/Format.php b/src/Appwrite/SDK/Specification/Format.php index 76e8bc8678..47c683b358 100644 --- a/src/Appwrite/SDK/Specification/Format.php +++ b/src/Appwrite/SDK/Specification/Format.php @@ -278,20 +278,16 @@ abstract class Format * @param array $models * @return array|null */ - protected function getUnionDiscriminator(array $models, string $refPrefix): ?array + protected function getDisciminator(array $models, string $refPrefix): ?array { if (\count($models) < 2) { return null; } - $candidateKeys = null; + $candidateKeys = \array_keys($models[0]->conditions); - foreach ($models as $model) { - $keys = \array_keys($model->conditions); - - $candidateKeys = $candidateKeys === null - ? $keys - : \array_values(\array_intersect($candidateKeys, $keys)); + foreach (\array_slice($models, 1) as $model) { + $candidateKeys = \array_values(\array_intersect($candidateKeys, \array_keys($model->conditions))); } if (empty($candidateKeys)) { @@ -300,34 +296,44 @@ abstract class Format foreach ($candidateKeys as $key) { $mapping = []; - $matchedModels = []; + $isValid = true; foreach ($models as $model) { $rules = $model->getRules(); + $condition = $model->conditions[$key] ?? null; if (!isset($rules[$key]) || ($rules[$key]['required'] ?? false) !== true) { - continue 2; + $isValid = false; + break; } - $condition = $model->conditions[$key]; if (!\is_array($condition)) { if (!\is_scalar($condition)) { - continue 2; + $isValid = false; + break; } $values = [$condition]; } else { if ($condition === []) { - continue 2; + $isValid = false; + break; } $values = $condition; + $hasInvalidValue = false; foreach ($values as $value) { if (!\is_scalar($value)) { - continue 3; + $hasInvalidValue = true; + break; } } + + if ($hasInvalidValue) { + $isValid = false; + break; + } } if (isset($rules[$key]['enum']) && \is_array($rules[$key]['enum'])) { @@ -338,23 +344,29 @@ abstract class Format } if ($values === []) { - continue 2; + $isValid = false; + break; } + $ref = $refPrefix . $model->getType(); + foreach ($values as $value) { $mappingKey = \is_bool($value) ? ($value ? 'true' : 'false') : (string) $value; - if (isset($mapping[$mappingKey]) && $mapping[$mappingKey] !== $refPrefix . $model->getType()) { - continue 2; + if (isset($mapping[$mappingKey]) && $mapping[$mappingKey] !== $ref) { + $isValid = false; + break; } - $mapping[$mappingKey] = $refPrefix . $model->getType(); + $mapping[$mappingKey] = $ref; } - $matchedModels[$model->getType()] = true; + if (!$isValid) { + break; + } } - if (\count($matchedModels) !== \count($models)) { + if (!$isValid || $mapping === []) { continue; } diff --git a/src/Appwrite/SDK/Specification/Format/OpenAPI3.php b/src/Appwrite/SDK/Specification/Format/OpenAPI3.php index c5af43f64d..bb9451ff4d 100644 --- a/src/Appwrite/SDK/Specification/Format/OpenAPI3.php +++ b/src/Appwrite/SDK/Specification/Format/OpenAPI3.php @@ -318,7 +318,7 @@ class OpenAPI3 extends Format $produces => [ 'schema' => \array_filter([ 'oneOf' => \array_map(fn ($m) => ['$ref' => '#/components/schemas/' . $m->getType()], $model), - 'discriminator' => $this->getUnionDiscriminator($model, '#/components/schemas/'), + 'discriminator' => $this->getDisciminator($model, '#/components/schemas/'), ]), ], ], @@ -906,7 +906,7 @@ class OpenAPI3 extends Format 'anyOf' => \array_map(function ($type) { return ['$ref' => '#/components/schemas/' . $type]; }, $rule['type']), - 'discriminator' => $this->getUnionDiscriminator( + 'discriminator' => $this->getDisciminator( \array_map(fn (string $type) => $this->getRegisteredModel($type), $rule['type']), '#/components/schemas/' ), @@ -916,7 +916,7 @@ class OpenAPI3 extends Format 'oneOf' => \array_map(function ($type) { return ['$ref' => '#/components/schemas/' . $type]; }, $rule['type']), - 'discriminator' => $this->getUnionDiscriminator( + 'discriminator' => $this->getDisciminator( \array_map(fn (string $type) => $this->getRegisteredModel($type), $rule['type']), '#/components/schemas/' ), diff --git a/src/Appwrite/SDK/Specification/Format/Swagger2.php b/src/Appwrite/SDK/Specification/Format/Swagger2.php index 8e9a39a3c1..5258fc8b7c 100644 --- a/src/Appwrite/SDK/Specification/Format/Swagger2.php +++ b/src/Appwrite/SDK/Specification/Format/Swagger2.php @@ -326,7 +326,7 @@ class Swagger2 extends Format 'x-oneOf' => \array_map(function ($m) { return ['$ref' => '#/definitions/' . $m->getType()]; }, $model), - 'x-discriminator' => $this->getUnionDiscriminator($model, '#/definitions/'), + 'x-discriminator' => $this->getDisciminator($model, '#/definitions/'), ]), ]; } else { @@ -884,7 +884,7 @@ class Swagger2 extends Format if ($rule['array']) { $items = \array_filter([ 'x-anyOf' => \array_map(fn ($type) => ['$ref' => '#/definitions/' . $type], $rule['type']), - 'x-discriminator' => $this->getUnionDiscriminator( + 'x-discriminator' => $this->getDisciminator( \array_map(fn (string $type) => $this->getRegisteredModel($type), $rule['type']), '#/definitions/' ), @@ -892,7 +892,7 @@ class Swagger2 extends Format } else { $items = \array_filter([ 'x-oneOf' => \array_map(fn ($type) => ['$ref' => '#/definitions/' . $type], $rule['type']), - 'x-discriminator' => $this->getUnionDiscriminator( + 'x-discriminator' => $this->getDisciminator( \array_map(fn (string $type) => $this->getRegisteredModel($type), $rule['type']), '#/definitions/' ), From 945cdb3a99080fb8700e656ce4162c03b88c8fb4 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Thu, 16 Apr 2026 11:16:25 +0530 Subject: [PATCH 04/14] refactor(specs): inline model resolution --- src/Appwrite/SDK/Specification/Format.php | 11 ---------- .../SDK/Specification/Format/OpenAPI3.php | 20 +++++++++++++++++-- .../SDK/Specification/Format/Swagger2.php | 20 +++++++++++++++++-- 3 files changed, 36 insertions(+), 15 deletions(-) diff --git a/src/Appwrite/SDK/Specification/Format.php b/src/Appwrite/SDK/Specification/Format.php index 47c683b358..b60d16274f 100644 --- a/src/Appwrite/SDK/Specification/Format.php +++ b/src/Appwrite/SDK/Specification/Format.php @@ -263,17 +263,6 @@ abstract class Format return $contents; } - protected function getRegisteredModel(string $type): Model - { - foreach ($this->models as $model) { - if ($model->getType() === $type) { - return $model; - } - } - - throw new \RuntimeException("Unresolved model '{$type}'. Ensure the model is registered."); - } - /** * @param array $models * @return array|null diff --git a/src/Appwrite/SDK/Specification/Format/OpenAPI3.php b/src/Appwrite/SDK/Specification/Format/OpenAPI3.php index bb9451ff4d..72dac7064a 100644 --- a/src/Appwrite/SDK/Specification/Format/OpenAPI3.php +++ b/src/Appwrite/SDK/Specification/Format/OpenAPI3.php @@ -907,7 +907,15 @@ class OpenAPI3 extends Format return ['$ref' => '#/components/schemas/' . $type]; }, $rule['type']), 'discriminator' => $this->getDisciminator( - \array_map(fn (string $type) => $this->getRegisteredModel($type), $rule['type']), + \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']), '#/components/schemas/' ), ]); @@ -917,7 +925,15 @@ class OpenAPI3 extends Format return ['$ref' => '#/components/schemas/' . $type]; }, $rule['type']), 'discriminator' => $this->getDisciminator( - \array_map(fn (string $type) => $this->getRegisteredModel($type), $rule['type']), + \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']), '#/components/schemas/' ), ]); diff --git a/src/Appwrite/SDK/Specification/Format/Swagger2.php b/src/Appwrite/SDK/Specification/Format/Swagger2.php index 5258fc8b7c..46280152e4 100644 --- a/src/Appwrite/SDK/Specification/Format/Swagger2.php +++ b/src/Appwrite/SDK/Specification/Format/Swagger2.php @@ -885,7 +885,15 @@ class Swagger2 extends Format $items = \array_filter([ 'x-anyOf' => \array_map(fn ($type) => ['$ref' => '#/definitions/' . $type], $rule['type']), 'x-discriminator' => $this->getDisciminator( - \array_map(fn (string $type) => $this->getRegisteredModel($type), $rule['type']), + \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']), '#/definitions/' ), ]); @@ -893,7 +901,15 @@ class Swagger2 extends Format $items = \array_filter([ 'x-oneOf' => \array_map(fn ($type) => ['$ref' => '#/definitions/' . $type], $rule['type']), 'x-discriminator' => $this->getDisciminator( - \array_map(fn (string $type) => $this->getRegisteredModel($type), $rule['type']), + \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']), '#/definitions/' ), ]); From b71d42d226610cbbb0dd6857538aee764ad87d11 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Thu, 16 Apr 2026 11:29:16 +0530 Subject: [PATCH 05/14] fix(specs): rename getDisciminator typo and extract shared model resolution Fix misspelled method name (getDisciminator -> getDiscriminator) across Format, OpenAPI3, and Swagger2. Extract duplicated model-resolution lambda into Format::resolveModels(). Fix copy-pasted descriptions in ProviderRepository list models. --- src/Appwrite/SDK/Specification/Format.php | 19 +++++++++++++- .../SDK/Specification/Format/OpenAPI3.php | 26 ++++--------------- .../SDK/Specification/Format/Swagger2.php | 26 ++++--------------- .../Model/ProviderRepositoryFrameworkList.php | 2 +- .../Model/ProviderRepositoryRuntimeList.php | 2 +- 5 files changed, 30 insertions(+), 45 deletions(-) diff --git a/src/Appwrite/SDK/Specification/Format.php b/src/Appwrite/SDK/Specification/Format.php index b60d16274f..2cf2759054 100644 --- a/src/Appwrite/SDK/Specification/Format.php +++ b/src/Appwrite/SDK/Specification/Format.php @@ -263,11 +263,28 @@ abstract class Format return $contents; } + /** + * @param array $types + * @return array + */ + protected function resolveModels(array $types): array + { + return \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."); + }, $types); + } + /** * @param array $models * @return array|null */ - protected function getDisciminator(array $models, string $refPrefix): ?array + protected function getDiscriminator(array $models, string $refPrefix): ?array { if (\count($models) < 2) { return null; diff --git a/src/Appwrite/SDK/Specification/Format/OpenAPI3.php b/src/Appwrite/SDK/Specification/Format/OpenAPI3.php index 72dac7064a..76fcdf9a4d 100644 --- a/src/Appwrite/SDK/Specification/Format/OpenAPI3.php +++ b/src/Appwrite/SDK/Specification/Format/OpenAPI3.php @@ -318,7 +318,7 @@ class OpenAPI3 extends Format $produces => [ 'schema' => \array_filter([ 'oneOf' => \array_map(fn ($m) => ['$ref' => '#/components/schemas/' . $m->getType()], $model), - 'discriminator' => $this->getDisciminator($model, '#/components/schemas/'), + 'discriminator' => $this->getDiscriminator($model, '#/components/schemas/'), ]), ], ], @@ -906,16 +906,8 @@ class OpenAPI3 extends Format 'anyOf' => \array_map(function ($type) { return ['$ref' => '#/components/schemas/' . $type]; }, $rule['type']), - 'discriminator' => $this->getDisciminator( - \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']), + 'discriminator' => $this->getDiscriminator( + $this->resolveModels($rule['type']), '#/components/schemas/' ), ]); @@ -924,16 +916,8 @@ class OpenAPI3 extends Format 'oneOf' => \array_map(function ($type) { return ['$ref' => '#/components/schemas/' . $type]; }, $rule['type']), - 'discriminator' => $this->getDisciminator( - \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']), + 'discriminator' => $this->getDiscriminator( + $this->resolveModels($rule['type']), '#/components/schemas/' ), ]); diff --git a/src/Appwrite/SDK/Specification/Format/Swagger2.php b/src/Appwrite/SDK/Specification/Format/Swagger2.php index 46280152e4..2ca24dd921 100644 --- a/src/Appwrite/SDK/Specification/Format/Swagger2.php +++ b/src/Appwrite/SDK/Specification/Format/Swagger2.php @@ -326,7 +326,7 @@ class Swagger2 extends Format 'x-oneOf' => \array_map(function ($m) { return ['$ref' => '#/definitions/' . $m->getType()]; }, $model), - 'x-discriminator' => $this->getDisciminator($model, '#/definitions/'), + 'x-discriminator' => $this->getDiscriminator($model, '#/definitions/'), ]), ]; } else { @@ -884,32 +884,16 @@ class Swagger2 extends Format if ($rule['array']) { $items = \array_filter([ 'x-anyOf' => \array_map(fn ($type) => ['$ref' => '#/definitions/' . $type], $rule['type']), - 'x-discriminator' => $this->getDisciminator( - \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']), + 'x-discriminator' => $this->getDiscriminator( + $this->resolveModels($rule['type']), '#/definitions/' ), ]); } else { $items = \array_filter([ 'x-oneOf' => \array_map(fn ($type) => ['$ref' => '#/definitions/' . $type], $rule['type']), - 'x-discriminator' => $this->getDisciminator( - \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']), + 'x-discriminator' => $this->getDiscriminator( + $this->resolveModels($rule['type']), '#/definitions/' ), ]); diff --git a/src/Appwrite/Utopia/Response/Model/ProviderRepositoryFrameworkList.php b/src/Appwrite/Utopia/Response/Model/ProviderRepositoryFrameworkList.php index 9816ce806e..4562b175a4 100644 --- a/src/Appwrite/Utopia/Response/Model/ProviderRepositoryFrameworkList.php +++ b/src/Appwrite/Utopia/Response/Model/ProviderRepositoryFrameworkList.php @@ -21,7 +21,7 @@ class ProviderRepositoryFrameworkList extends BaseList $this->addRule('type', [ 'type' => self::TYPE_ENUM, - 'description' => 'Repository detection type.', + 'description' => 'Provider repository list type.', 'default' => 'framework', 'example' => 'framework', 'enum' => ['runtime', 'framework'], diff --git a/src/Appwrite/Utopia/Response/Model/ProviderRepositoryRuntimeList.php b/src/Appwrite/Utopia/Response/Model/ProviderRepositoryRuntimeList.php index a30fa4d3b5..f2617d46f6 100644 --- a/src/Appwrite/Utopia/Response/Model/ProviderRepositoryRuntimeList.php +++ b/src/Appwrite/Utopia/Response/Model/ProviderRepositoryRuntimeList.php @@ -21,7 +21,7 @@ class ProviderRepositoryRuntimeList extends BaseList $this->addRule('type', [ 'type' => self::TYPE_ENUM, - 'description' => 'Repository detection type.', + 'description' => 'Provider repository list type.', 'default' => 'runtime', 'example' => 'runtime', 'enum' => ['runtime', 'framework'], From 4545989c912f9debb000e1f3d43ad9021bba4648 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Thu, 16 Apr 2026 12:22:37 +0530 Subject: [PATCH 06/14] fix(specs): remove type rule from list models, keep only on specific models --- app/init/models.php | 6 ++-- .../Http/Installations/Repositories/XList.php | 1 - .../Model/ProviderRepositoryFrameworkList.php | 30 ------------------- .../Model/ProviderRepositoryRuntimeList.php | 30 ------------------- 4 files changed, 2 insertions(+), 65 deletions(-) delete mode 100644 src/Appwrite/Utopia/Response/Model/ProviderRepositoryFrameworkList.php delete mode 100644 src/Appwrite/Utopia/Response/Model/ProviderRepositoryRuntimeList.php diff --git a/app/init/models.php b/app/init/models.php index c92295ae33..dd97b03652 100644 --- a/app/init/models.php +++ b/app/init/models.php @@ -117,9 +117,7 @@ use Appwrite\Utopia\Response\Model\Project; use Appwrite\Utopia\Response\Model\Provider; use Appwrite\Utopia\Response\Model\ProviderRepository; use Appwrite\Utopia\Response\Model\ProviderRepositoryFramework; -use Appwrite\Utopia\Response\Model\ProviderRepositoryFrameworkList; use Appwrite\Utopia\Response\Model\ProviderRepositoryRuntime; -use Appwrite\Utopia\Response\Model\ProviderRepositoryRuntimeList; use Appwrite\Utopia\Response\Model\ResourceToken; use Appwrite\Utopia\Response\Model\Row; use Appwrite\Utopia\Response\Model\Rule; @@ -192,8 +190,8 @@ Response::setModel(new BaseList('Site Templates List', Response::MODEL_TEMPLATE_ Response::setModel(new BaseList('Functions List', Response::MODEL_FUNCTION_LIST, 'functions', Response::MODEL_FUNCTION)); Response::setModel(new BaseList('Function Templates List', Response::MODEL_TEMPLATE_FUNCTION_LIST, 'templates', Response::MODEL_TEMPLATE_FUNCTION)); Response::setModel(new BaseList('Installations List', Response::MODEL_INSTALLATION_LIST, 'installations', Response::MODEL_INSTALLATION)); -Response::setModel(new ProviderRepositoryFrameworkList()); -Response::setModel(new ProviderRepositoryRuntimeList()); +Response::setModel(new BaseList('Framework Provider Repositories List', Response::MODEL_PROVIDER_REPOSITORY_FRAMEWORK_LIST, 'frameworkProviderRepositories', Response::MODEL_PROVIDER_REPOSITORY_FRAMEWORK)); +Response::setModel(new BaseList('Runtime Provider Repositories List', Response::MODEL_PROVIDER_REPOSITORY_RUNTIME_LIST, 'runtimeProviderRepositories', Response::MODEL_PROVIDER_REPOSITORY_RUNTIME)); Response::setModel(new BaseList('Branches List', Response::MODEL_BRANCH_LIST, 'branches', Response::MODEL_BRANCH)); Response::setModel(new BaseList('Frameworks List', Response::MODEL_FRAMEWORK_LIST, 'frameworks', Response::MODEL_FRAMEWORK)); Response::setModel(new BaseList('Runtimes List', Response::MODEL_RUNTIME_LIST, 'runtimes', Response::MODEL_RUNTIME)); diff --git a/src/Appwrite/Platform/Modules/VCS/Http/Installations/Repositories/XList.php b/src/Appwrite/Platform/Modules/VCS/Http/Installations/Repositories/XList.php index b4172fabdf..d5b2b48175 100644 --- a/src/Appwrite/Platform/Modules/VCS/Http/Installations/Repositories/XList.php +++ b/src/Appwrite/Platform/Modules/VCS/Http/Installations/Repositories/XList.php @@ -313,7 +313,6 @@ class XList extends Action }, $repos); $response->dynamic(new Document([ - 'type' => $type, $type === 'framework' ? 'frameworkProviderRepositories' : 'runtimeProviderRepositories' => $repos, 'total' => $total, ]), ($type === 'framework') ? Response::MODEL_PROVIDER_REPOSITORY_FRAMEWORK_LIST : Response::MODEL_PROVIDER_REPOSITORY_RUNTIME_LIST); diff --git a/src/Appwrite/Utopia/Response/Model/ProviderRepositoryFrameworkList.php b/src/Appwrite/Utopia/Response/Model/ProviderRepositoryFrameworkList.php deleted file mode 100644 index 4562b175a4..0000000000 --- a/src/Appwrite/Utopia/Response/Model/ProviderRepositoryFrameworkList.php +++ /dev/null @@ -1,30 +0,0 @@ - 'framework', - ]; - - public function __construct() - { - parent::__construct( - 'Framework Provider Repositories List', - Response::MODEL_PROVIDER_REPOSITORY_FRAMEWORK_LIST, - 'frameworkProviderRepositories', - Response::MODEL_PROVIDER_REPOSITORY_FRAMEWORK - ); - - $this->addRule('type', [ - 'type' => self::TYPE_ENUM, - 'description' => 'Provider repository list type.', - 'default' => 'framework', - 'example' => 'framework', - 'enum' => ['runtime', 'framework'], - ]); - } -} diff --git a/src/Appwrite/Utopia/Response/Model/ProviderRepositoryRuntimeList.php b/src/Appwrite/Utopia/Response/Model/ProviderRepositoryRuntimeList.php deleted file mode 100644 index f2617d46f6..0000000000 --- a/src/Appwrite/Utopia/Response/Model/ProviderRepositoryRuntimeList.php +++ /dev/null @@ -1,30 +0,0 @@ - 'runtime', - ]; - - public function __construct() - { - parent::__construct( - 'Runtime Provider Repositories List', - Response::MODEL_PROVIDER_REPOSITORY_RUNTIME_LIST, - 'runtimeProviderRepositories', - Response::MODEL_PROVIDER_REPOSITORY_RUNTIME - ); - - $this->addRule('type', [ - 'type' => self::TYPE_ENUM, - 'description' => 'Provider repository list type.', - 'default' => 'runtime', - 'example' => 'runtime', - 'enum' => ['runtime', 'framework'], - ]); - } -} From 965836c8b4e7c5f5e9bea93ab3cb99944926b75c Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Thu, 16 Apr 2026 12:28:53 +0530 Subject: [PATCH 07/14] fix(specs): use swagger discriminator extension mapping --- src/Appwrite/SDK/Specification/Format.php | 17 ------- .../SDK/Specification/Format/OpenAPI3.php | 20 +++++++- .../SDK/Specification/Format/Swagger2.php | 51 ++++++++++++++----- 3 files changed, 55 insertions(+), 33 deletions(-) diff --git a/src/Appwrite/SDK/Specification/Format.php b/src/Appwrite/SDK/Specification/Format.php index 2cf2759054..8cdb3ee5c3 100644 --- a/src/Appwrite/SDK/Specification/Format.php +++ b/src/Appwrite/SDK/Specification/Format.php @@ -263,23 +263,6 @@ abstract class Format return $contents; } - /** - * @param array $types - * @return array - */ - protected function resolveModels(array $types): array - { - return \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."); - }, $types); - } - /** * @param array $models * @return array|null diff --git a/src/Appwrite/SDK/Specification/Format/OpenAPI3.php b/src/Appwrite/SDK/Specification/Format/OpenAPI3.php index 76fcdf9a4d..84d99fc2f6 100644 --- a/src/Appwrite/SDK/Specification/Format/OpenAPI3.php +++ b/src/Appwrite/SDK/Specification/Format/OpenAPI3.php @@ -907,7 +907,15 @@ class OpenAPI3 extends Format return ['$ref' => '#/components/schemas/' . $type]; }, $rule['type']), 'discriminator' => $this->getDiscriminator( - $this->resolveModels($rule['type']), + \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']), '#/components/schemas/' ), ]); @@ -917,7 +925,15 @@ class OpenAPI3 extends Format return ['$ref' => '#/components/schemas/' . $type]; }, $rule['type']), 'discriminator' => $this->getDiscriminator( - $this->resolveModels($rule['type']), + \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']), '#/components/schemas/' ), ]); diff --git a/src/Appwrite/SDK/Specification/Format/Swagger2.php b/src/Appwrite/SDK/Specification/Format/Swagger2.php index 2ca24dd921..eede2183f0 100644 --- a/src/Appwrite/SDK/Specification/Format/Swagger2.php +++ b/src/Appwrite/SDK/Specification/Format/Swagger2.php @@ -322,12 +322,17 @@ class Swagger2 extends Format } $temp['responses'][(string)$response->getCode() ?? '500'] = [ 'description' => $modelDescription, - 'schema' => \array_filter([ - 'x-oneOf' => \array_map(function ($m) { - return ['$ref' => '#/definitions/' . $m->getType()]; - }, $model), - 'x-discriminator' => $this->getDiscriminator($model, '#/definitions/'), - ]), + 'schema' => (function () use ($model) { + $discriminator = $this->getDiscriminator($model, '#/definitions/'); + + return \array_filter([ + 'x-oneOf' => \array_map(function ($m) { + return ['$ref' => '#/definitions/' . $m->getType()]; + }, $model), + 'discriminator' => $discriminator['propertyName'] ?? null, + 'x-discriminator-mapping' => $discriminator['mapping'] ?? null, + ]); + })(), ]; } else { // Response definition using one type @@ -882,20 +887,38 @@ class Swagger2 extends Format if (\is_array($rule['type'])) { if ($rule['array']) { + $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']); + $discriminator = $this->getDiscriminator($resolvedModels, '#/definitions/'); + $items = \array_filter([ 'x-anyOf' => \array_map(fn ($type) => ['$ref' => '#/definitions/' . $type], $rule['type']), - 'x-discriminator' => $this->getDiscriminator( - $this->resolveModels($rule['type']), - '#/definitions/' - ), + 'discriminator' => $discriminator['propertyName'] ?? null, + 'x-discriminator-mapping' => $discriminator['mapping'] ?? null, ]); } else { + $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']); + $discriminator = $this->getDiscriminator($resolvedModels, '#/definitions/'); + $items = \array_filter([ 'x-oneOf' => \array_map(fn ($type) => ['$ref' => '#/definitions/' . $type], $rule['type']), - 'x-discriminator' => $this->getDiscriminator( - $this->resolveModels($rule['type']), - '#/definitions/' - ), + 'discriminator' => $discriminator['propertyName'] ?? null, + 'x-discriminator-mapping' => $discriminator['mapping'] ?? null, ]); } } else { From 1493b7b8a6eb62e3852a183d75a628858c6262ea Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Thu, 16 Apr 2026 13:02:57 +0530 Subject: [PATCH 08/14] feat(specs): unified discriminator with compound support and algo conditions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Unify getDiscriminator to produce a single discriminator object for both single-key and compound cases. Single-key returns standard {propertyName, mapping}. Compound falls back to extending the object with x-propertyNames and x-mapping for multi-property discrimination. Simplify call sites: OpenAPI3 uses 'discriminator', Swagger2 uses 'x-discriminator' — no more split keys. Add conditions to all 7 Algo models (AlgoArgon2, AlgoBcrypt, AlgoMd5, AlgoPhpass, AlgoScrypt, AlgoScryptModified, AlgoSha) to enable discriminator generation for hashOptions unions. --- src/Appwrite/SDK/Specification/Format.php | 73 ++++++++++++++++++- .../SDK/Specification/Format/OpenAPI3.php | 36 +++------ .../SDK/Specification/Format/Swagger2.php | 52 +++++-------- .../Utopia/Response/Model/AlgoArgon2.php | 4 + .../Utopia/Response/Model/AlgoBcrypt.php | 4 + .../Utopia/Response/Model/AlgoMd5.php | 4 + .../Utopia/Response/Model/AlgoPhpass.php | 4 + .../Utopia/Response/Model/AlgoScrypt.php | 4 + .../Response/Model/AlgoScryptModified.php | 4 + .../Utopia/Response/Model/AlgoSha.php | 4 + 10 files changed, 129 insertions(+), 60 deletions(-) diff --git a/src/Appwrite/SDK/Specification/Format.php b/src/Appwrite/SDK/Specification/Format.php index 8cdb3ee5c3..ce1eb97203 100644 --- a/src/Appwrite/SDK/Specification/Format.php +++ b/src/Appwrite/SDK/Specification/Format.php @@ -365,7 +365,78 @@ abstract class Format ]; } - return null; + // Single-key failed — try compound discriminator + return $this->getCompoundDiscriminator($models, $refPrefix); + } + + /** + * @param array $models + * @return array|null + */ + private function getCompoundDiscriminator(array $models, string $refPrefix): ?array + { + $allKeys = []; + foreach ($models as $model) { + foreach (\array_keys($model->conditions) as $key) { + if (!\in_array($key, $allKeys, true)) { + $allKeys[] = $key; + } + } + } + + if (\count($allKeys) < 2) { + return null; + } + + $primaryKey = $allKeys[0]; + $primaryMapping = []; + $compoundMapping = []; + + foreach ($models as $model) { + $rules = $model->getRules(); + $conditions = []; + + foreach ($model->conditions as $key => $condition) { + if (!isset($rules[$key]) || ($rules[$key]['required'] ?? false) !== true) { + return null; + } + + if (!\is_scalar($condition)) { + return null; + } + + $conditions[$key] = \is_bool($condition) ? ($condition ? 'true' : 'false') : (string) $condition; + } + + if (empty($conditions)) { + return null; + } + + $ref = $refPrefix . $model->getType(); + $compoundMapping[$ref] = $conditions; + + // Best-effort single-key mapping — last model with this value wins (fallback) + if (isset($conditions[$primaryKey])) { + $primaryMapping[$conditions[$primaryKey]] = $ref; + } + } + + // Verify compound uniqueness + $seen = []; + foreach ($compoundMapping as $conditions) { + $sig = \json_encode($conditions, JSON_THROW_ON_ERROR); + if (isset($seen[$sig])) { + return null; + } + $seen[$sig] = true; + } + + return \array_filter([ + 'propertyName' => $primaryKey, + 'mapping' => !empty($primaryMapping) ? $primaryMapping : null, + 'x-propertyNames' => $allKeys, + 'x-mapping' => $compoundMapping, + ]); } protected function getRequestEnumName(string $service, string $method, string $param): ?string diff --git a/src/Appwrite/SDK/Specification/Format/OpenAPI3.php b/src/Appwrite/SDK/Specification/Format/OpenAPI3.php index 84d99fc2f6..fcff6ac2f4 100644 --- a/src/Appwrite/SDK/Specification/Format/OpenAPI3.php +++ b/src/Appwrite/SDK/Specification/Format/OpenAPI3.php @@ -901,41 +901,29 @@ class OpenAPI3 extends Format $rule['type'] = ($rule['type']) ? $rule['type'] : 'none'; if (\is_array($rule['type'])) { + $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']); + if ($rule['array']) { $items = \array_filter([ 'anyOf' => \array_map(function ($type) { return ['$ref' => '#/components/schemas/' . $type]; }, $rule['type']), - 'discriminator' => $this->getDiscriminator( - \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']), - '#/components/schemas/' - ), + 'discriminator' => $this->getDiscriminator($resolvedModels, '#/components/schemas/'), ]); } else { $items = \array_filter([ 'oneOf' => \array_map(function ($type) { return ['$ref' => '#/components/schemas/' . $type]; }, $rule['type']), - 'discriminator' => $this->getDiscriminator( - \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']), - '#/components/schemas/' - ), + 'discriminator' => $this->getDiscriminator($resolvedModels, '#/components/schemas/'), ]); } } else { diff --git a/src/Appwrite/SDK/Specification/Format/Swagger2.php b/src/Appwrite/SDK/Specification/Format/Swagger2.php index eede2183f0..8d47766117 100644 --- a/src/Appwrite/SDK/Specification/Format/Swagger2.php +++ b/src/Appwrite/SDK/Specification/Format/Swagger2.php @@ -322,17 +322,12 @@ class Swagger2 extends Format } $temp['responses'][(string)$response->getCode() ?? '500'] = [ 'description' => $modelDescription, - 'schema' => (function () use ($model) { - $discriminator = $this->getDiscriminator($model, '#/definitions/'); - - return \array_filter([ - 'x-oneOf' => \array_map(function ($m) { - return ['$ref' => '#/definitions/' . $m->getType()]; - }, $model), - 'discriminator' => $discriminator['propertyName'] ?? null, - 'x-discriminator-mapping' => $discriminator['mapping'] ?? null, - ]); - })(), + 'schema' => \array_filter([ + 'x-oneOf' => \array_map(function ($m) { + return ['$ref' => '#/definitions/' . $m->getType()]; + }, $model), + 'x-discriminator' => $this->getDiscriminator($model, '#/definitions/'), + ]), ]; } else { // Response definition using one type @@ -886,39 +881,26 @@ class Swagger2 extends Format $rule['type'] = ($rule['type']) ?: 'none'; if (\is_array($rule['type'])) { - if ($rule['array']) { - $resolvedModels = \array_map(function (string $type) { - foreach ($this->models as $model) { - if ($model->getType() === $type) { - return $model; - } + $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']); - $discriminator = $this->getDiscriminator($resolvedModels, '#/definitions/'); + throw new \RuntimeException("Unresolved model '{$type}'. Ensure the model is registered."); + }, $rule['type']); + $xDiscriminator = $this->getDiscriminator($resolvedModels, '#/definitions/'); + if ($rule['array']) { $items = \array_filter([ 'x-anyOf' => \array_map(fn ($type) => ['$ref' => '#/definitions/' . $type], $rule['type']), - 'discriminator' => $discriminator['propertyName'] ?? null, - 'x-discriminator-mapping' => $discriminator['mapping'] ?? null, + 'x-discriminator' => $xDiscriminator, ]); } else { - $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']); - $discriminator = $this->getDiscriminator($resolvedModels, '#/definitions/'); - $items = \array_filter([ 'x-oneOf' => \array_map(fn ($type) => ['$ref' => '#/definitions/' . $type], $rule['type']), - 'discriminator' => $discriminator['propertyName'] ?? null, - 'x-discriminator-mapping' => $discriminator['mapping'] ?? null, + 'x-discriminator' => $xDiscriminator, ]); } } else { diff --git a/src/Appwrite/Utopia/Response/Model/AlgoArgon2.php b/src/Appwrite/Utopia/Response/Model/AlgoArgon2.php index 3e162bb905..a721235f94 100644 --- a/src/Appwrite/Utopia/Response/Model/AlgoArgon2.php +++ b/src/Appwrite/Utopia/Response/Model/AlgoArgon2.php @@ -7,6 +7,10 @@ use Appwrite\Utopia\Response\Model; class AlgoArgon2 extends Model { + public array $conditions = [ + 'type' => 'argon2', + ]; + public function __construct() { // No options if imported. If hashed by Appwrite, following configuration is available: diff --git a/src/Appwrite/Utopia/Response/Model/AlgoBcrypt.php b/src/Appwrite/Utopia/Response/Model/AlgoBcrypt.php index 709dea1a41..ef15e5d50a 100644 --- a/src/Appwrite/Utopia/Response/Model/AlgoBcrypt.php +++ b/src/Appwrite/Utopia/Response/Model/AlgoBcrypt.php @@ -7,6 +7,10 @@ use Appwrite\Utopia\Response\Model; class AlgoBcrypt extends Model { + public array $conditions = [ + 'type' => 'bcrypt', + ]; + public function __construct() { // No options, because this can only be imported, and verifying doesnt require any configuration diff --git a/src/Appwrite/Utopia/Response/Model/AlgoMd5.php b/src/Appwrite/Utopia/Response/Model/AlgoMd5.php index 509ee70c31..26b2886330 100644 --- a/src/Appwrite/Utopia/Response/Model/AlgoMd5.php +++ b/src/Appwrite/Utopia/Response/Model/AlgoMd5.php @@ -7,6 +7,10 @@ use Appwrite\Utopia\Response\Model; class AlgoMd5 extends Model { + public array $conditions = [ + 'type' => 'md5', + ]; + public function __construct() { // No options, because this can only be imported, and verifying doesnt require any configuration diff --git a/src/Appwrite/Utopia/Response/Model/AlgoPhpass.php b/src/Appwrite/Utopia/Response/Model/AlgoPhpass.php index f16792086e..7d8400edec 100644 --- a/src/Appwrite/Utopia/Response/Model/AlgoPhpass.php +++ b/src/Appwrite/Utopia/Response/Model/AlgoPhpass.php @@ -7,6 +7,10 @@ use Appwrite\Utopia\Response\Model; class AlgoPhpass extends Model { + public array $conditions = [ + 'type' => 'phpass', + ]; + public function __construct() { // No options, because this can only be imported, and verifying doesnt require any configuration diff --git a/src/Appwrite/Utopia/Response/Model/AlgoScrypt.php b/src/Appwrite/Utopia/Response/Model/AlgoScrypt.php index 4dda297d71..043a27166d 100644 --- a/src/Appwrite/Utopia/Response/Model/AlgoScrypt.php +++ b/src/Appwrite/Utopia/Response/Model/AlgoScrypt.php @@ -7,6 +7,10 @@ use Appwrite\Utopia\Response\Model; class AlgoScrypt extends Model { + public array $conditions = [ + 'type' => 'scrypt', + ]; + public function __construct() { $this diff --git a/src/Appwrite/Utopia/Response/Model/AlgoScryptModified.php b/src/Appwrite/Utopia/Response/Model/AlgoScryptModified.php index 40b9df1dad..24dd41bb77 100644 --- a/src/Appwrite/Utopia/Response/Model/AlgoScryptModified.php +++ b/src/Appwrite/Utopia/Response/Model/AlgoScryptModified.php @@ -7,6 +7,10 @@ use Appwrite\Utopia\Response\Model; class AlgoScryptModified extends Model { + public array $conditions = [ + 'type' => 'scryptMod', + ]; + public function __construct() { $this diff --git a/src/Appwrite/Utopia/Response/Model/AlgoSha.php b/src/Appwrite/Utopia/Response/Model/AlgoSha.php index 2a0893adc4..52743ec26a 100644 --- a/src/Appwrite/Utopia/Response/Model/AlgoSha.php +++ b/src/Appwrite/Utopia/Response/Model/AlgoSha.php @@ -7,6 +7,10 @@ use Appwrite\Utopia\Response\Model; class AlgoSha extends Model { + public array $conditions = [ + 'type' => 'sha', + ]; + public function __construct() { // No options, because this can only be imported, and verifying doesnt require any configuration From 6dc17c91bce78310ab4f2bd58a8bb0fd537b220c Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Thu, 16 Apr 2026 13:08:14 +0530 Subject: [PATCH 09/14] trigger greptile From 98ec9e45c4e004ce5896e92003fd008a9fa8caf8 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Thu, 16 Apr 2026 13:16:13 +0530 Subject: [PATCH 10/14] fix(specs): narrow Detection type enum to each subclass's own value Each Detection subclass now declares only its own type value in the enum rather than sharing the full ['runtime', 'framework'] list. This prevents SDK validators from accepting invalid values on concrete models. --- src/Appwrite/Utopia/Response/Model/Detection.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Appwrite/Utopia/Response/Model/Detection.php b/src/Appwrite/Utopia/Response/Model/Detection.php index d57e4a27c0..9dfcc795d6 100644 --- a/src/Appwrite/Utopia/Response/Model/Detection.php +++ b/src/Appwrite/Utopia/Response/Model/Detection.php @@ -15,7 +15,7 @@ abstract class Detection extends Model 'description' => 'Repository detection type.', 'default' => $type, 'example' => $type, - 'enum' => ['runtime', 'framework'], + 'enum' => [$type], ]) ->addRule('variables', [ 'type' => Response::MODEL_DETECTION_VARIABLE, From 05d70f8826228502e4d919c256da610254a0d3ba Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Thu, 16 Apr 2026 13:32:05 +0530 Subject: [PATCH 11/14] refactor(specs): rename x-propertyNames/x-mapping to x-discriminator-properties/x-union-typemap --- src/Appwrite/SDK/Specification/Format.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Appwrite/SDK/Specification/Format.php b/src/Appwrite/SDK/Specification/Format.php index ce1eb97203..d01ba20ae3 100644 --- a/src/Appwrite/SDK/Specification/Format.php +++ b/src/Appwrite/SDK/Specification/Format.php @@ -434,8 +434,8 @@ abstract class Format return \array_filter([ 'propertyName' => $primaryKey, 'mapping' => !empty($primaryMapping) ? $primaryMapping : null, - 'x-propertyNames' => $allKeys, - 'x-mapping' => $compoundMapping, + 'x-discriminator-properties' => $allKeys, + 'x-union-typemap' => $compoundMapping, ]); } From e472d98fe39a8661be61ca80e57a3e0ed55993fe Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Thu, 16 Apr 2026 13:55:36 +0530 Subject: [PATCH 12/14] Revert "refactor(specs): rename x-propertyNames/x-mapping to x-discriminator-properties/x-union-typemap" This reverts commit 05d70f8826228502e4d919c256da610254a0d3ba. --- src/Appwrite/SDK/Specification/Format.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Appwrite/SDK/Specification/Format.php b/src/Appwrite/SDK/Specification/Format.php index d01ba20ae3..ce1eb97203 100644 --- a/src/Appwrite/SDK/Specification/Format.php +++ b/src/Appwrite/SDK/Specification/Format.php @@ -434,8 +434,8 @@ abstract class Format return \array_filter([ 'propertyName' => $primaryKey, 'mapping' => !empty($primaryMapping) ? $primaryMapping : null, - 'x-discriminator-properties' => $allKeys, - 'x-union-typemap' => $compoundMapping, + 'x-propertyNames' => $allKeys, + 'x-mapping' => $compoundMapping, ]); } From 807e8bec8be12af10e9e9af01d004d6cf68420df Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Thu, 16 Apr 2026 16:29:42 +0530 Subject: [PATCH 13/14] feat(specs): add discriminator for provider repository list response union Add ProviderRepositoryFrameworkList and ProviderRepositoryRuntimeList model classes with conditions and type field so the listRepositories endpoint's oneOf response gets a discriminator on the type property. --- app/init/models.php | 6 ++-- .../Http/Installations/Repositories/XList.php | 1 + .../Model/ProviderRepositoryFrameworkList.php | 29 +++++++++++++++++++ .../Model/ProviderRepositoryRuntimeList.php | 29 +++++++++++++++++++ 4 files changed, 63 insertions(+), 2 deletions(-) create mode 100644 src/Appwrite/Utopia/Response/Model/ProviderRepositoryFrameworkList.php create mode 100644 src/Appwrite/Utopia/Response/Model/ProviderRepositoryRuntimeList.php diff --git a/app/init/models.php b/app/init/models.php index dd97b03652..c92295ae33 100644 --- a/app/init/models.php +++ b/app/init/models.php @@ -117,7 +117,9 @@ use Appwrite\Utopia\Response\Model\Project; use Appwrite\Utopia\Response\Model\Provider; use Appwrite\Utopia\Response\Model\ProviderRepository; use Appwrite\Utopia\Response\Model\ProviderRepositoryFramework; +use Appwrite\Utopia\Response\Model\ProviderRepositoryFrameworkList; use Appwrite\Utopia\Response\Model\ProviderRepositoryRuntime; +use Appwrite\Utopia\Response\Model\ProviderRepositoryRuntimeList; use Appwrite\Utopia\Response\Model\ResourceToken; use Appwrite\Utopia\Response\Model\Row; use Appwrite\Utopia\Response\Model\Rule; @@ -190,8 +192,8 @@ Response::setModel(new BaseList('Site Templates List', Response::MODEL_TEMPLATE_ Response::setModel(new BaseList('Functions List', Response::MODEL_FUNCTION_LIST, 'functions', Response::MODEL_FUNCTION)); Response::setModel(new BaseList('Function Templates List', Response::MODEL_TEMPLATE_FUNCTION_LIST, 'templates', Response::MODEL_TEMPLATE_FUNCTION)); Response::setModel(new BaseList('Installations List', Response::MODEL_INSTALLATION_LIST, 'installations', Response::MODEL_INSTALLATION)); -Response::setModel(new BaseList('Framework Provider Repositories List', Response::MODEL_PROVIDER_REPOSITORY_FRAMEWORK_LIST, 'frameworkProviderRepositories', Response::MODEL_PROVIDER_REPOSITORY_FRAMEWORK)); -Response::setModel(new BaseList('Runtime Provider Repositories List', Response::MODEL_PROVIDER_REPOSITORY_RUNTIME_LIST, 'runtimeProviderRepositories', Response::MODEL_PROVIDER_REPOSITORY_RUNTIME)); +Response::setModel(new ProviderRepositoryFrameworkList()); +Response::setModel(new ProviderRepositoryRuntimeList()); Response::setModel(new BaseList('Branches List', Response::MODEL_BRANCH_LIST, 'branches', Response::MODEL_BRANCH)); Response::setModel(new BaseList('Frameworks List', Response::MODEL_FRAMEWORK_LIST, 'frameworks', Response::MODEL_FRAMEWORK)); Response::setModel(new BaseList('Runtimes List', Response::MODEL_RUNTIME_LIST, 'runtimes', Response::MODEL_RUNTIME)); diff --git a/src/Appwrite/Platform/Modules/VCS/Http/Installations/Repositories/XList.php b/src/Appwrite/Platform/Modules/VCS/Http/Installations/Repositories/XList.php index d5b2b48175..b4172fabdf 100644 --- a/src/Appwrite/Platform/Modules/VCS/Http/Installations/Repositories/XList.php +++ b/src/Appwrite/Platform/Modules/VCS/Http/Installations/Repositories/XList.php @@ -313,6 +313,7 @@ class XList extends Action }, $repos); $response->dynamic(new Document([ + 'type' => $type, $type === 'framework' ? 'frameworkProviderRepositories' : 'runtimeProviderRepositories' => $repos, 'total' => $total, ]), ($type === 'framework') ? Response::MODEL_PROVIDER_REPOSITORY_FRAMEWORK_LIST : Response::MODEL_PROVIDER_REPOSITORY_RUNTIME_LIST); diff --git a/src/Appwrite/Utopia/Response/Model/ProviderRepositoryFrameworkList.php b/src/Appwrite/Utopia/Response/Model/ProviderRepositoryFrameworkList.php new file mode 100644 index 0000000000..d1982e2f84 --- /dev/null +++ b/src/Appwrite/Utopia/Response/Model/ProviderRepositoryFrameworkList.php @@ -0,0 +1,29 @@ + 'framework', + ]; + + public function __construct() + { + parent::__construct( + 'Framework Provider Repositories List', + Response::MODEL_PROVIDER_REPOSITORY_FRAMEWORK_LIST, + 'frameworkProviderRepositories', + Response::MODEL_PROVIDER_REPOSITORY_FRAMEWORK + ); + + $this->addRule('type', [ + 'type' => self::TYPE_STRING, + 'description' => 'Provider repository list type.', + 'default' => 'framework', + 'example' => 'framework', + ]); + } +} diff --git a/src/Appwrite/Utopia/Response/Model/ProviderRepositoryRuntimeList.php b/src/Appwrite/Utopia/Response/Model/ProviderRepositoryRuntimeList.php new file mode 100644 index 0000000000..f7ef1d7b5f --- /dev/null +++ b/src/Appwrite/Utopia/Response/Model/ProviderRepositoryRuntimeList.php @@ -0,0 +1,29 @@ + 'runtime', + ]; + + public function __construct() + { + parent::__construct( + 'Runtime Provider Repositories List', + Response::MODEL_PROVIDER_REPOSITORY_RUNTIME_LIST, + 'runtimeProviderRepositories', + Response::MODEL_PROVIDER_REPOSITORY_RUNTIME + ); + + $this->addRule('type', [ + 'type' => self::TYPE_STRING, + 'description' => 'Provider repository list type.', + 'default' => 'runtime', + 'example' => 'runtime', + ]); + } +} From 463e5acf5069bf2331f2b95321f2fad6e4d84c42 Mon Sep 17 00:00:00 2001 From: Atharva Deosthale Date: Thu, 16 Apr 2026 16:57:19 +0530 Subject: [PATCH 14/14] compose fixes --- app/views/install/compose.phtml | 23 +---------------------- 1 file changed, 1 insertion(+), 22 deletions(-) diff --git a/app/views/install/compose.phtml b/app/views/install/compose.phtml index ef4d4a1fe4..1bf36b7f6d 100644 --- a/app/views/install/compose.phtml +++ b/app/views/install/compose.phtml @@ -120,7 +120,6 @@ $hostPath = rtrim($this->getParam('hostPath', ''), '/'); - _APP_DB_SCHEMA - _APP_DB_USER - _APP_DB_PASS - - _APP_DB_ADAPTER - _APP_SMTP_HOST - _APP_SMTP_PORT - _APP_SMTP_SECURE @@ -256,7 +255,6 @@ $hostPath = rtrim($this->getParam('hostPath', ''), '/'); - _APP_DB_SCHEMA - _APP_DB_USER - _APP_DB_PASS - - _APP_DB_ADAPTER - _APP_USAGE_STATS - _APP_LOGGING_CONFIG @@ -287,7 +285,6 @@ $hostPath = rtrim($this->getParam('hostPath', ''), '/'); - _APP_DB_SCHEMA - _APP_DB_USER - _APP_DB_PASS - - _APP_DB_ADAPTER - _APP_LOGGING_CONFIG appwrite-worker-webhooks: @@ -315,7 +312,6 @@ $hostPath = rtrim($this->getParam('hostPath', ''), '/'); - _APP_DB_SCHEMA - _APP_DB_USER - _APP_DB_PASS - - _APP_DB_ADAPTER - _APP_REDIS_HOST - _APP_REDIS_PORT - _APP_REDIS_USER @@ -356,7 +352,6 @@ $hostPath = rtrim($this->getParam('hostPath', ''), '/'); - _APP_DB_SCHEMA - _APP_DB_USER - _APP_DB_PASS - - _APP_DB_ADAPTER - _APP_STORAGE_DEVICE - _APP_STORAGE_S3_ACCESS_KEY - _APP_STORAGE_S3_SECRET @@ -416,7 +411,6 @@ $hostPath = rtrim($this->getParam('hostPath', ''), '/'); - _APP_DB_SCHEMA - _APP_DB_USER - _APP_DB_PASS - - _APP_DB_ADAPTER - _APP_LOGGING_CONFIG appwrite-worker-builds: @@ -453,7 +447,6 @@ $hostPath = rtrim($this->getParam('hostPath', ''), '/'); - _APP_DB_SCHEMA - _APP_DB_USER - _APP_DB_PASS - - _APP_DB_ADAPTER - _APP_LOGGING_CONFIG - _APP_VCS_GITHUB_APP_NAME - _APP_VCS_GITHUB_PRIVATE_KEY @@ -529,7 +522,6 @@ $hostPath = rtrim($this->getParam('hostPath', ''), '/'); - _APP_DB_SCHEMA - _APP_DB_USER - _APP_DB_PASS - - _APP_DB_ADAPTER - _APP_LOGGING_CONFIG appwrite-worker-executions: @@ -592,7 +584,6 @@ $hostPath = rtrim($this->getParam('hostPath', ''), '/'); - _APP_DB_SCHEMA - _APP_DB_USER - _APP_DB_PASS - - _APP_DB_ADAPTER - _APP_FUNCTIONS_TIMEOUT - _APP_SITES_TIMEOUT - _APP_COMPUTE_BUILD_TIMEOUT @@ -630,7 +621,6 @@ $hostPath = rtrim($this->getParam('hostPath', ''), '/'); - _APP_DB_SCHEMA - _APP_DB_USER - _APP_DB_PASS - - _APP_DB_ADAPTER - _APP_REDIS_HOST - _APP_REDIS_PORT - _APP_REDIS_USER @@ -673,7 +663,6 @@ $hostPath = rtrim($this->getParam('hostPath', ''), '/'); - _APP_DB_SCHEMA - _APP_DB_USER - _APP_DB_PASS - - _APP_DB_ADAPTER - _APP_LOGGING_CONFIG - _APP_SMS_FROM - _APP_SMS_PROVIDER @@ -734,7 +723,6 @@ $hostPath = rtrim($this->getParam('hostPath', ''), '/'); - _APP_DB_SCHEMA - _APP_DB_USER - _APP_DB_PASS - - _APP_DB_ADAPTER - _APP_LOGGING_CONFIG - _APP_MIGRATIONS_FIREBASE_CLIENT_ID - _APP_MIGRATIONS_FIREBASE_CLIENT_SECRET @@ -773,7 +761,6 @@ $hostPath = rtrim($this->getParam('hostPath', ''), '/'); - _APP_DB_SCHEMA - _APP_DB_USER - _APP_DB_PASS - - _APP_DB_ADAPTER - _APP_MAINTENANCE_INTERVAL - _APP_MAINTENANCE_RETENTION_EXECUTION - _APP_MAINTENANCE_RETENTION_CACHE @@ -806,7 +793,6 @@ $hostPath = rtrim($this->getParam('hostPath', ''), '/'); - _APP_DB_SCHEMA - _APP_DB_USER - _APP_DB_PASS - - _APP_DB_ADAPTER - _APP_REDIS_HOST - _APP_REDIS_PORT - _APP_REDIS_USER @@ -839,7 +825,6 @@ $hostPath = rtrim($this->getParam('hostPath', ''), '/'); - _APP_DB_SCHEMA - _APP_DB_USER - _APP_DB_PASS - - _APP_DB_ADAPTER - _APP_REDIS_HOST - _APP_REDIS_PORT - _APP_REDIS_USER @@ -871,7 +856,6 @@ $hostPath = rtrim($this->getParam('hostPath', ''), '/'); - _APP_DB_SCHEMA - _APP_DB_USER - _APP_DB_PASS - - _APP_DB_ADAPTER - _APP_REDIS_HOST - _APP_REDIS_PORT - _APP_REDIS_USER @@ -907,7 +891,6 @@ $hostPath = rtrim($this->getParam('hostPath', ''), '/'); - _APP_DB_SCHEMA - _APP_DB_USER - _APP_DB_PASS - - _APP_DB_ADAPTER appwrite-task-scheduler-executions: image: /: @@ -936,7 +919,6 @@ $hostPath = rtrim($this->getParam('hostPath', ''), '/'); - _APP_DB_SCHEMA - _APP_DB_USER - _APP_DB_PASS - - _APP_DB_ADAPTER appwrite-task-scheduler-messages: image: /: @@ -965,7 +947,6 @@ $hostPath = rtrim($this->getParam('hostPath', ''), '/'); - _APP_DB_SCHEMA - _APP_DB_USER - _APP_DB_PASS - - _APP_DB_ADAPTER appwrite-assistant: @@ -1068,13 +1049,12 @@ $hostPath = rtrim($this->getParam('hostPath', ''), '/'); image: mongo:8.2.5 container_name: appwrite-mongodb <<: *x-logging + restart: unless-stopped networks: - appwrite volumes: - appwrite-mongodb:/data/db - appwrite-mongodb-keyfile:/data/keyfile - ports: - - "27017:27017" environment: - MONGO_INITDB_ROOT_USERNAME=root - MONGO_INITDB_ROOT_PASSWORD=${_APP_DB_ROOT_PASS} @@ -1205,7 +1185,6 @@ volumes: appwrite-mongodb: appwrite-mongodb-keyfile: - appwrite-mongodb-config: appwrite-redis: appwrite-cache: