diff --git a/composer.json b/composer.json index 019570d..42ca844 100644 --- a/composer.json +++ b/composer.json @@ -21,7 +21,7 @@ "utopia-php/cli": "0.23.*", "utopia-php/http": "^2.0@RC", "utopia-php/queue": "0.18.*", - "utopia-php/servers": "0.4.*" + "utopia-php/servers": "dev-feat-param-enum-metadata as 0.4.0" }, "require-dev": { "phpunit/phpunit": "^9.3", diff --git a/composer.lock b/composer.lock index e1c2aab..63feec7 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "7a013a6f9eec8fc648647b7d0657346e", + "content-hash": "1d5ad8dea4b2c78b74039f808f469bab", "packages": [ { "name": "brick/math", @@ -2502,16 +2502,16 @@ }, { "name": "utopia-php/servers", - "version": "0.4.0", + "version": "dev-feat-param-enum-metadata", "source": { "type": "git", "url": "https://github.com/utopia-php/servers.git", - "reference": "7db346ef377503efe0acafe0791085270cd9ed70" + "reference": "333e93c5e5c629a1ae059003f570a48bc5a0eeb6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/servers/zipball/7db346ef377503efe0acafe0791085270cd9ed70", - "reference": "7db346ef377503efe0acafe0791085270cd9ed70", + "url": "https://api.github.com/repos/utopia-php/servers/zipball/333e93c5e5c629a1ae059003f570a48bc5a0eeb6", + "reference": "333e93c5e5c629a1ae059003f570a48bc5a0eeb6", "shasum": "" }, "require": { @@ -2550,9 +2550,9 @@ ], "support": { "issues": "https://github.com/utopia-php/servers/issues", - "source": "https://github.com/utopia-php/servers/tree/0.4.0" + "source": "https://github.com/utopia-php/servers/tree/feat-param-enum-metadata" }, - "time": "2026-05-05T04:08:30+00:00" + "time": "2026-05-20T11:51:30+00:00" }, { "name": "utopia-php/telemetry", @@ -2611,16 +2611,16 @@ }, { "name": "utopia-php/validators", - "version": "0.2.2", + "version": "0.2.3", "source": { "type": "git", "url": "https://github.com/utopia-php/validators.git", - "reference": "5d7d494e64457cd4eb67fdcfd9481f2c89796aa6" + "reference": "9770269c8ed8e6909934965fa8722103c7434c23" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/validators/zipball/5d7d494e64457cd4eb67fdcfd9481f2c89796aa6", - "reference": "5d7d494e64457cd4eb67fdcfd9481f2c89796aa6", + "url": "https://api.github.com/repos/utopia-php/validators/zipball/9770269c8ed8e6909934965fa8722103c7434c23", + "reference": "9770269c8ed8e6909934965fa8722103c7434c23", "shasum": "" }, "require": { @@ -2650,9 +2650,9 @@ ], "support": { "issues": "https://github.com/utopia-php/validators/issues", - "source": "https://github.com/utopia-php/validators/tree/0.2.2" + "source": "https://github.com/utopia-php/validators/tree/0.2.3" }, - "time": "2026-04-27T16:30:24+00:00" + "time": "2026-05-14T08:05:44+00:00" } ], "packages-dev": [ @@ -4519,10 +4519,18 @@ "time": "2025-11-17T20:03:58+00:00" } ], - "aliases": [], + "aliases": [ + { + "package": "utopia-php/servers", + "version": "dev-feat-param-enum-metadata", + "alias": "0.4.0", + "alias_normalized": "0.4.0.0" + } + ], "minimum-stability": "stable", "stability-flags": { - "utopia-php/http": 5 + "utopia-php/http": 5, + "utopia-php/servers": 20 }, "prefer-stable": false, "prefer-lowest": false, diff --git a/src/Platform/Action.php b/src/Platform/Action.php index bdf6afa..55e24bc 100644 --- a/src/Platform/Action.php +++ b/src/Platform/Action.php @@ -170,9 +170,10 @@ public function getParams(): array * @param bool $deprecated * @param string $example * @param array $aliases + * @param Enum|null $enum * @return self */ - public function param(string $key, mixed $default, Validator|callable $validator, string $description = '', bool $optional = false, array $injections = [], bool $skipValidation = false, bool $deprecated = false, string $example = '', array $aliases = []): self + public function param(string $key, mixed $default, Validator|callable $validator, string $description = '', bool $optional = false, array $injections = [], bool $skipValidation = false, bool $deprecated = false, string $example = '', array $aliases = [], ?Enum $enum = null): self { $param = [ 'default' => $default, @@ -184,6 +185,7 @@ public function param(string $key, mixed $default, Validator|callable $validator 'deprecated' => $deprecated, // TODO: @Meldiron implement tests 'example' => $example, 'aliases' => $aliases, + 'enum' => $enum, ]; $this->options['param:'.$key] = array_merge($param, ['type' => 'param']); $this->params[$key] = $param; diff --git a/src/Platform/Enum.php b/src/Platform/Enum.php new file mode 100644 index 0000000..ab613e5 --- /dev/null +++ b/src/Platform/Enum.php @@ -0,0 +1,21 @@ +|null $map Mapping of whitelist values to generated enum case names. + * @param list|null $exclude Whitelist values to omit from generated enums. + */ + public function __construct( + public ?string $name = null, + public ?array $map = null, + public ?array $exclude = null, + ) { + } +} diff --git a/src/Platform/Platform.php b/src/Platform/Platform.php index ef2b3b6..4b1249f 100644 --- a/src/Platform/Platform.php +++ b/src/Platform/Platform.php @@ -115,7 +115,7 @@ protected function initHttp(array $services): void switch ($option['type']) { case 'param': $key = substr($key, stripos($key, ':') + 1); - $hook->param($key, $option['default'], $option['validator'], $option['description'], $option['optional'], $option['injections'], $option['skipValidation'], $option['deprecated'], $option['example'], aliases: $option['aliases'] ?? []); + $hook->param($key, $option['default'], $option['validator'], $option['description'], $option['optional'], $option['injections'], $option['skipValidation'], $option['deprecated'], $option['example'], aliases: $option['aliases'] ?? [], enum: $option['enum'] ?? null); break; case 'injection': $hook->inject($option['name']); @@ -165,7 +165,7 @@ protected function initTasks(array $services): void switch ($option['type']) { case 'param': $key = substr($key, stripos($key, ':') + 1); - $hook->param($key, $option['default'], $option['validator'], $option['description'], $option['optional'], $option['injections'], $option['skipValidation'], $option['deprecated'], $option['example'], aliases: $option['aliases'] ?? []); + $hook->param($key, $option['default'], $option['validator'], $option['description'], $option['optional'], $option['injections'], $option['skipValidation'], $option['deprecated'], $option['example'], aliases: $option['aliases'] ?? [], enum: $option['enum'] ?? null); break; case 'injection': $hook->inject($option['name']); @@ -222,7 +222,7 @@ protected function initWorker(array $services, string $workerName): void switch ($option['type']) { case 'param': $key = substr($key, stripos($key, ':') + 1); - $hook->param($key, $option['default'], $option['validator'], $option['description'], $option['optional'], $option['injections'], $option['skipValidation'], $option['deprecated'], $option['example'], aliases: $option['aliases'] ?? []); + $hook->param($key, $option['default'], $option['validator'], $option['description'], $option['optional'], $option['injections'], $option['skipValidation'], $option['deprecated'], $option['example'], aliases: $option['aliases'] ?? [], enum: $option['enum'] ?? null); break; case 'injection': $hook->inject($option['name']); diff --git a/tests/Platform/TestActionWithParams.php b/tests/Platform/TestActionWithParams.php index 34cea91..cf39707 100644 --- a/tests/Platform/TestActionWithParams.php +++ b/tests/Platform/TestActionWithParams.php @@ -3,9 +3,11 @@ namespace Utopia\Tests; use Utopia\Platform\Action; +use Utopia\Platform\Enum; use Utopia\Validator\Boolean; use Utopia\Validator\Range; use Utopia\Validator\Text; +use Utopia\Validator\WhiteList; class TestActionWithParams extends Action { @@ -19,8 +21,9 @@ public function __construct() ->param('age', 0, new Range(0, 150), 'User age.', true, example: '25') ->param('active', false, new Boolean(true), 'Is active.', true, deprecated: true, example: 'true') ->param('email', '', new Text(256), 'User email.', true, aliases: ['emailAddress', 'userEmail'], example: 'user@example.com') + ->param('status', 'draft', new WhiteList(['draft', 'published']), 'Status.', optional: true, enum: new Enum(name: 'ArticleStatus', map: ['draft' => 'Draft', 'published' => 'Published'])) ->inject('response') - ->callback(function ($name, $age, $active, $email, $response) { + ->callback(function ($name, $age, $active, $email, $status, $response) { $response->send('OK'); }); } diff --git a/tests/e2e/HTTPServicesTest.php b/tests/e2e/HTTPServicesTest.php index 82ad833..832b82b 100644 --- a/tests/e2e/HTTPServicesTest.php +++ b/tests/e2e/HTTPServicesTest.php @@ -167,5 +167,22 @@ public function testActionParamFieldsForwardedToRoute() // Verify params without aliases have empty array $this->assertArrayHasKey('aliases', $params['name'], 'Param name should have aliases key'); $this->assertEquals([], $params['name']['aliases']); + + // Verify enum is forwarded to Route params + $this->assertArrayHasKey('enum', $params['status'], 'Param status should have enum key on Route'); + $this->assertInstanceOf(\Utopia\Platform\Enum::class, $params['status']['enum']); + $this->assertEquals('ArticleStatus', $params['status']['enum']->name); + $this->assertEquals(['draft' => 'Draft', 'published' => 'Published'], $params['status']['enum']->map); + + // Verify enum is stored on Action params + $action = new TestActionWithParams(); + $actionParams = $action->getParams(); + + $this->assertInstanceOf(\Utopia\Platform\Enum::class, $actionParams['status']['enum']); + $this->assertEquals('ArticleStatus', $actionParams['status']['enum']->name); + $this->assertEquals(['draft' => 'Draft', 'published' => 'Published'], $actionParams['status']['enum']->map); + + // Verify params without enum are null on Action + $this->assertNull($actionParams['name']['enum']); } }