diff --git a/src/Relay.php b/src/Relay.php index c8b5505..dfad292 100644 --- a/src/Relay.php +++ b/src/Relay.php @@ -376,6 +376,12 @@ protected function getSchemeParameter(string $name, array $property, string $def return new AnyOfSchema($itemsSchema, $name, $description); } + $options = data_get($property, 'enum'); + + if (is_array($options) && $options !== [] && $this->isEnumOptions($options)) { + return new EnumSchema($name, $description, array_values($options)); + } + $itemsSchema = $this->getSchemeParameter('', data_get($property, 'items', []), $definitionName); return match ($type) { @@ -389,6 +395,23 @@ protected function getSchemeParameter(string $name, array $property, string $def }; } + /** + * EnumSchema resolves its own type from the options, so anything it cannot + * type has to fall through to the regular resolution. + * + * @param array $options + */ + protected function isEnumOptions(array $options): bool + { + foreach ($options as $option) { + if (! is_string($option) && ! is_int($option) && ! is_float($option)) { + return false; + } + } + + return true; + } + /** * @param array $property */ diff --git a/tests/TestDoubles/RelayFake.php b/tests/TestDoubles/RelayFake.php index 1082384..66fa683 100644 --- a/tests/TestDoubles/RelayFake.php +++ b/tests/TestDoubles/RelayFake.php @@ -206,6 +206,29 @@ protected function fetchToolDefinitions(): array 'required' => ['nameOrId'], ], ], + [ + 'name' => 'enum_tool', + 'description' => 'A tool with enum parameters', + 'inputSchema' => [ + 'type' => 'object', + 'properties' => [ + 'status' => [ + 'type' => 'string', + 'description' => 'The status to filter on', + 'enum' => ['open', 'closed'], + ], + 'categories' => [ + 'type' => 'array', + 'description' => 'The categories to filter on', + 'items' => [ + 'type' => 'string', + 'enum' => ['news', 'sport'], + ], + ], + ], + 'required' => ['status'], + ], + ], ]; } diff --git a/tests/Unit/RelayTest.php b/tests/Unit/RelayTest.php index 6056f5f..7a5b8fd 100644 --- a/tests/Unit/RelayTest.php +++ b/tests/Unit/RelayTest.php @@ -6,6 +6,8 @@ use Illuminate\Support\Facades\Cache; use Prism\Prism\Schema\AnyOfSchema; +use Prism\Prism\Schema\ArraySchema; +use Prism\Prism\Schema\EnumSchema; use Prism\Prism\Tool; use Prism\Relay\Exceptions\ServerConfigurationException; use Prism\Relay\Exceptions\ToolDefinitionException; @@ -87,7 +89,7 @@ $tools = $relay->tools(); // Test we have the tools we expect - expect($tools)->toHaveCount(7); + expect($tools)->toHaveCount(8); }); it('handles different parameter types correctly in tools', function (): void { @@ -155,3 +157,31 @@ 'description' => 'Parameter nameOrId for union_tool', ]); }); + +it('maps json schema enums to enum schemas', function (): void { + $relay = new RelayFake($this->serverName); + + $tool = $relay->tools()[7]; + $status = $tool->parameters()['status']; + + expect($status) + ->toBeInstanceOf(EnumSchema::class) + ->and($status->toArray())->toBe([ + 'description' => 'The status to filter on', + 'enum' => ['open', 'closed'], + 'type' => 'string', + ]); +}); + +it('maps json schema enums nested in array items', function (): void { + $relay = new RelayFake($this->serverName); + + $tool = $relay->tools()[7]; + $categories = $tool->parameters()['categories']; + + expect($categories) + ->toBeInstanceOf(ArraySchema::class) + ->and($categories->items) + ->toBeInstanceOf(EnumSchema::class) + ->and($categories->items->options)->toBe(['news', 'sport']); +});