diff --git a/resources/views/components/field-details.blade.php b/resources/views/components/field-details.blade.php index 57200c1c..222a094a 100644 --- a/resources/views/components/field-details.blade.php +++ b/resources/views/components/field-details.blade.php @@ -69,3 +69,7 @@ } @endphp {!! Parsedown::instance()->text(trim($description)) !!} +@if(!empty($enumValues)) +Must be one of: +<ul style="list-style-type: square;">{!! implode(" ", array_map(fn($val) => "<li><code>$val</code></li>", $enumValues)) !!}</ul> +@endif diff --git a/resources/views/components/nested-fields.blade.php b/resources/views/components/nested-fields.blade.php index 1deb7c0b..e4137835 100644 --- a/resources/views/components/nested-fields.blade.php +++ b/resources/views/components/nested-fields.blade.php @@ -25,6 +25,7 @@ 'required' => $subfield['required'] ?? false, 'description' => $subfield['description'] ?? '', 'example' => $subfield['example'] ?? '', + 'enumValues' => $subfield['enumValues'] ?? null, 'endpointId' => $endpointId, 'hasChildren' => false, 'component' => 'body', @@ -66,6 +67,7 @@ 'required' => $subfield['required'] ?? false, 'description' => $subfield['description'] ?? '', 'example' => $subfield['example'] ?? '', + 'enumValues' => $field['enumValues'] ?? null, 'endpointId' => $endpointId, 'hasChildren' => false, 'component' => 'body', @@ -86,6 +88,7 @@ 'required' => $field['required'] ?? false, 'description' => $field['description'] ?? '', 'example' => $field['example'] ?? '', + 'enumValues' => $field['enumValues'] ?? null, 'endpointId' => $endpointId, 'hasChildren' => false, 'component' => 'body', diff --git a/resources/views/themes/default/endpoint.blade.php b/resources/views/themes/default/endpoint.blade.php index 94e77598..8da30799 100644 --- a/resources/views/themes/default/endpoint.blade.php +++ b/resources/views/themes/default/endpoint.blade.php @@ -132,6 +132,7 @@ 'required' => $parameter->required, 'description' => $parameter->description, 'example' => $parameter->example ?? '', + 'enumValues' => $parameter->enumValues, 'endpointId' => $endpoint->endpointId(), 'component' => 'url', 'isInput' => true, @@ -156,6 +157,7 @@ 'required' => $parameter->required, 'description' => $parameter->description, 'example' => $parameter->example ?? '', + 'enumValues' => $parameter->enumValues, 'endpointId' => $endpoint->endpointId(), 'component' => 'query', 'isInput' => true, diff --git a/resources/views/themes/elements/components/field-details.blade.php b/resources/views/themes/elements/components/field-details.blade.php index ea41dedc..b2682fc0 100644 --- a/resources/views/themes/elements/components/field-details.blade.php +++ b/resources/views/themes/elements/components/field-details.blade.php @@ -36,6 +36,10 @@ class="svg-inline--fa fa-chevron-right fa-fw fa-sm sl-icon" role="img" {!! Parsedown::instance()->text($description) !!} </div> @endif + @if(!empty($enumValues)) + Must be one of: + <ul style="list-style-position: inside; list-style-type: square;">{!! implode(" ", array_map(fn($val) => "<li><code>$val</code></li>", $enumValues)) !!}</ul> + @endif @if($isArrayBody) <div class="sl-flex sl-items-baseline sl-text-base"> <div class="sl-font-mono sl-font-semibold sl-mr-2">array of:</div> diff --git a/resources/views/themes/elements/components/nested-fields.blade.php b/resources/views/themes/elements/components/nested-fields.blade.php index ec7583e7..1e2ab3de 100644 --- a/resources/views/themes/elements/components/nested-fields.blade.php +++ b/resources/views/themes/elements/components/nested-fields.blade.php @@ -15,6 +15,7 @@ 'required' => $field['required'] ?? false, 'description' => $field['description'] ?? '', 'example' => $field['example'] ?? '', + 'enumValues' => $field['enumValues'] ?? null, 'endpointId' => $endpointId, 'hasChildren' => !empty($field['__fields']), 'component' => 'body', diff --git a/resources/views/themes/elements/endpoint.blade.php b/resources/views/themes/elements/endpoint.blade.php index 61690c28..470c1617 100644 --- a/resources/views/themes/elements/endpoint.blade.php +++ b/resources/views/themes/elements/endpoint.blade.php @@ -81,6 +81,7 @@ class="sl-overflow-x-hidden sl-truncate sl-text-muted">{{ rtrim($baseUrl, '/') } 'required' => $parameter->required, 'description' => $parameter->description, 'example' => $parameter->example ?? '', + 'enumValues' => $parameter->enumValues, 'endpointId' => $endpoint->endpointId(), 'component' => 'url', 'isInput' => true, @@ -104,6 +105,7 @@ class="sl-overflow-x-hidden sl-truncate sl-text-muted">{{ rtrim($baseUrl, '/') } 'required' => $parameter->required, 'description' => $parameter->description, 'example' => $parameter->example ?? '', + 'enumValues' => $parameter->enumValues, 'endpointId' => $endpoint->endpointId(), 'component' => 'query', 'isInput' => true, diff --git a/src/Extracting/ParsesValidationRules.php b/src/Extracting/ParsesValidationRules.php index b7e7ebe4..2e1a47a8 100644 --- a/src/Extracting/ParsesValidationRules.php +++ b/src/Extracting/ParsesValidationRules.php @@ -204,7 +204,6 @@ protected function parseRule($rule, array &$parameterData, bool $independentOnly $cases = array_map(fn ($case) => $case->value, $type::cases()); $parameterData['type'] = gettype($cases[0]); $parameterData['enumValues'] = $cases; - $parameterData['description'] .= ' Must be one of ' . w::getListOfValuesAsFriendlyHtmlString($cases) . ' '; $parameterData['setter'] = fn () => Arr::random($cases); } @@ -466,8 +465,6 @@ protected function parseRule($rule, array &$parameterData, bool $independentOnly */ case 'in': $parameterData['enumValues'] = $arguments; - // Not using the rule description here because it only says "The attribute is invalid" - $parameterData['description'] .= ' Must be one of ' . w::getListOfValuesAsFriendlyHtmlString($arguments) . ' '; $parameterData['setter'] = function () use ($arguments) { return Arr::random($arguments); }; diff --git a/src/Extracting/Strategies/GetParamsFromAttributeStrategy.php b/src/Extracting/Strategies/GetParamsFromAttributeStrategy.php index b0bd36ce..42ff07cc 100644 --- a/src/Extracting/Strategies/GetParamsFromAttributeStrategy.php +++ b/src/Extracting/Strategies/GetParamsFromAttributeStrategy.php @@ -30,7 +30,10 @@ protected function normalizeParameterData(array $data): array { $data['type'] = static::normalizeTypeName($data['type']); if (is_null($data['example'])) { - $data['example'] = $this->generateDummyValue($data['type'], ['name' => $data['name']]); + $data['example'] = $this->generateDummyValue($data['type'], [ + 'name' => $data['name'], + 'enumValues' => $data['enumValues'], + ]); } else if ($data['example'] == 'No-example' || $data['example'] == 'No-example.') { $data['example'] = null; } diff --git a/src/Writing/OpenAPISpecWriter.php b/src/Writing/OpenAPISpecWriter.php index 4c9a6cd0..7059a7ae 100644 --- a/src/Writing/OpenAPISpecWriter.php +++ b/src/Writing/OpenAPISpecWriter.php @@ -510,11 +510,16 @@ public function generateFieldData($field): array })->all()), ]; } else { - return [ + $schema = [ 'type' => static::normalizeTypeName($field->type), 'description' => $field->description ?: '', 'example' => $field->example, ]; + if (!empty($field->enumValues)) { + $schema['enum'] = $field->enumValues; + } + + return $schema; } } @@ -523,30 +528,30 @@ protected function operationId(OutputEndpointData $endpoint): string if ($endpoint->metadata->title) return preg_replace('/[^\w+]/', '', Str::camel($endpoint->metadata->title)); $parts = preg_split('/[^\w+]/', $endpoint->uri, -1, PREG_SPLIT_NO_EMPTY); - return Str::lower($endpoint->httpMethods[0]) . join('', array_map(fn ($part) => ucfirst($part), $parts)); + return Str::lower($endpoint->httpMethods[0]) . join('', array_map(fn($part) => ucfirst($part), $parts)); } /** * Given an array, return an object if the array is empty. To be used with fields that are * required by OpenAPI spec to be objects, since empty arrays get serialised as []. */ - protected function objectIfEmpty(array $field): array | \stdClass + protected function objectIfEmpty(array $field): array|\stdClass { return count($field) > 0 ? $field : new \stdClass(); } /** - * Given a value, generate the schema for it. The schema consists of: {type:, example:, properties: (if value is an object)}, - * and possibly a description for each property. - * The $endpoint and $path are used for looking up response field descriptions. + * Given a value, generate the schema for it. The schema consists of: {type:, example:, properties: (if value is an + * object)}, and possibly a description for each property. The $endpoint and $path are used for looking up response + * field descriptions. */ public function generateSchemaForValue(mixed $value, OutputEndpointData $endpoint, string $path): array { if ($value instanceof \stdClass) { - $value = (array) $value; + $value = (array)$value; $properties = []; // Recurse into the object - foreach($value as $subField => $subValue){ + foreach ($value as $subField => $subValue) { $subFieldPath = sprintf('%s.%s', $path, $subField); $properties[$subField] = $this->generateSchemaForValue($subValue, $endpoint, $subFieldPath); }