diff --git a/src/Attributes/Group.php b/src/Attributes/Group.php index 4d58e509..6b13fb16 100644 --- a/src/Attributes/Group.php +++ b/src/Attributes/Group.php @@ -19,5 +19,31 @@ public function __construct( * by the name (with `SORT_LOCALE_STRING` sorting flag). */ public readonly int $weight = PHP_INT_MAX, + + /** + * The parent tag name. Used to organize tags into hierarchical groups + * via the native OpenAPI 3.2.0 Tag Object `parent` field. + */ + public readonly ?string $parent = null, + + /** + * A short summary of the tag. + */ + public readonly ?string $summary = null, + + /** + * The kind of tag: "navigation" (default) or "api". + */ + public readonly ?string $kind = null, + + /** + * URL for additional external documentation for this tag. + */ + public readonly ?string $externalDocsUrl = null, + + /** + * Description of the external documentation for this tag. + */ + public readonly ?string $externalDocsDescription = null, ) {} } diff --git a/src/Attributes/Tag.php b/src/Attributes/Tag.php new file mode 100644 index 00000000..a733b945 --- /dev/null +++ b/src/Attributes/Tag.php @@ -0,0 +1,53 @@ +tags = $this->makeTagsFromGroupAttributes($context->groups); + $document->tags = $this->makeTagsFromGroupAttributes($context->groups, $context->tags); } /** * @param Collection> $groupsAttributes + * @param Collection> $tagAttributes * @return Tag[] */ - private function makeTagsFromGroupAttributes(Collection $groupsAttributes) + private function makeTagsFromGroupAttributes(Collection $groupsAttributes, Collection $tagAttributes = new Collection) { /** @var Collection $tags */ $tags = $groupsAttributes->reduce(function (Collection $acc, ReflectionAttribute $attribute) { @@ -47,11 +50,86 @@ private function makeTagsFromGroupAttributes(Collection $groupsAttributes) $tag->setAttribute('weight', $weight); } + if ($group->parent !== null && $tag->parent === null) { + $tag->parent = $group->parent; + } + + if ($group->summary !== null && $tag->summary === null) { + $tag->summary = $group->summary; + } + + if ($group->kind !== null && $tag->kind === null) { + $tag->kind = $group->kind; + } + + if ($group->externalDocsUrl !== null && $tag->externalDocs === null) { + $tag->externalDocs = new ExternalDocumentation( + url: $group->externalDocsUrl, + description: $group->externalDocsDescription, + ); + } + $acc->offsetSet($name, $tag); return $acc; }, collect()); + $this->applyDeclaredTags($tags, $tagAttributes); + + $this->addMissingParentTags($tags); + return $tags->sortBy(fn (Tag $t) => $t->getAttribute('weight', INF))->values()->all(); } + + /** + * Applies `Tag` attributes, which describe a tag without claiming any endpoint. + * + * A group of the same name wins on every field it already states, so declaring a + * tag never overrides where an endpoint says it belongs. + * + * @param Collection $tags + * @param Collection> $tagAttributes + */ + private function applyDeclaredTags(Collection $tags, Collection $tagAttributes): void + { + foreach ($tagAttributes as $attribute) { + $declaration = $attribute->newInstance(); + + /** @var Tag $tag */ + $tag = $tags->get($declaration->name, new Tag($declaration->name)); + + $tag->description ??= $declaration->description; + $tag->parent ??= $declaration->parent; + $tag->summary ??= $declaration->summary; + $tag->kind ??= $declaration->kind; + + if ($declaration->weight !== PHP_INT_MAX && $tag->getAttribute('weight') === null) { + $tag->setAttribute('weight', $declaration->weight); + } + + if ($declaration->externalDocsUrl !== null && $tag->externalDocs === null) { + $tag->externalDocs = new ExternalDocumentation( + url: $declaration->externalDocsUrl, + description: $declaration->externalDocsDescription, + ); + } + + $tags->offsetSet($declaration->name, $tag); + } + } + + /** + * A tag naming a parent nothing declares would leave the document invalid: the + * spec requires the named parent to exist in the API description. + * + * @param Collection $tags + */ + private function addMissingParentTags(Collection $tags): void + { + $tags->pluck('parent') + ->filter() + ->unique() + ->reject(fn (string $parent): bool => $tags->has($parent)) + ->each(fn (string $parent) => $tags->offsetSet($parent, new Tag($parent))); + } } diff --git a/src/Generator.php b/src/Generator.php index 3c003ac3..fa645802 100644 --- a/src/Generator.php +++ b/src/Generator.php @@ -163,7 +163,7 @@ private function createOperationsSorter(): array private function makeOpenApi(GeneratorConfig $config) { - $openApi = OpenApi::make('3.1.0') + $openApi = OpenApi::make('3.2.0') ->setComponents(new Components) ->setInfo( InfoObject::make($config->get('ui.title', $default = config('app.name')) ?: $default) diff --git a/src/OpenApiContext.php b/src/OpenApiContext.php index d0903a2c..7504f592 100644 --- a/src/OpenApiContext.php +++ b/src/OpenApiContext.php @@ -3,6 +3,7 @@ namespace Dedoc\Scramble; use Dedoc\Scramble\Attributes\Group; +use Dedoc\Scramble\Attributes\Tag; use Dedoc\Scramble\Diagnostics\DiagnosticsCollector; use Dedoc\Scramble\Support\Generator\OpenApi; use Dedoc\Scramble\Support\ProNudge\ProNudgeCollector; @@ -19,6 +20,10 @@ public function __construct( * @var Collection> */ public Collection $groups = new Collection, + /** + * @var Collection> + */ + public Collection $tags = new Collection, public DiagnosticsCollector $diagnostics = new DiagnosticsCollector, public ProNudgeCollector $proNudge = new ProNudgeCollector, ) {} diff --git a/src/Support/Generator/ExternalDocumentation.php b/src/Support/Generator/ExternalDocumentation.php new file mode 100644 index 00000000..eb9c62a5 --- /dev/null +++ b/src/Support/Generator/ExternalDocumentation.php @@ -0,0 +1,19 @@ + $this->description, + 'url' => $this->url, + ]); + } +} diff --git a/src/Support/Generator/OpenApi.php b/src/Support/Generator/OpenApi.php index d8ef5565..4fc254ae 100644 --- a/src/Support/Generator/OpenApi.php +++ b/src/Support/Generator/OpenApi.php @@ -4,6 +4,8 @@ class OpenApi { + use WithExtensions; + public string $version; public InfoObject $info; @@ -126,6 +128,8 @@ public function toArray() $result['components'] = $serializedComponents; } + $result = array_merge($result, $this->extensionPropertiesToArray()); + return $result; } } diff --git a/src/Support/Generator/Tag.php b/src/Support/Generator/Tag.php index 3d256432..311876d6 100644 --- a/src/Support/Generator/Tag.php +++ b/src/Support/Generator/Tag.php @@ -7,6 +7,14 @@ class Tag use WithAttributes; use WithExtensions; + public ?string $summary = null; + + public ?string $parent = null; + + public ?string $kind = null; + + public ?ExternalDocumentation $externalDocs = null; + public function __construct( public string $name, public ?string $description = null, @@ -17,6 +25,10 @@ public function toArray(): mixed $result = array_filter([ 'name' => $this->name, 'description' => $this->description, + 'summary' => $this->summary, + 'parent' => $this->parent, + 'kind' => $this->kind, + 'externalDocs' => $this->externalDocs?->toArray(), ]); return array_merge( diff --git a/src/Support/OperationExtensions/RequestEssentialsExtension.php b/src/Support/OperationExtensions/RequestEssentialsExtension.php index 27d120bf..7e8f0ceb 100644 --- a/src/Support/OperationExtensions/RequestEssentialsExtension.php +++ b/src/Support/OperationExtensions/RequestEssentialsExtension.php @@ -4,6 +4,7 @@ use Dedoc\Scramble\Attributes\Endpoint; use Dedoc\Scramble\Attributes\Group; +use Dedoc\Scramble\Attributes\Tag as TagAttribute; use Dedoc\Scramble\Extensions\OperationExtension; use Dedoc\Scramble\GeneratorConfig; use Dedoc\Scramble\Infer; @@ -218,6 +219,28 @@ private function getOperationId(RouteInfo $routeInfo) /** * @return ReflectionAttribute[] */ + /** + * Collects `Tag` attributes, which declare a tag without claiming the endpoint. + * + * A parent tag holds no endpoints of its own, so nothing would otherwise carry + * its description into the document. + */ + private function collectDeclaredTags(RouteInfo $routeInfo): void + { + $reflection = $routeInfo->reflectionAction(); + + $attributes = [ + ...($reflection?->getAttributes(TagAttribute::class, ReflectionAttribute::IS_INSTANCEOF) ?? []), + ...($reflection instanceof ReflectionMethod + ? $reflection->getDeclaringClass()->getAttributes(TagAttribute::class, ReflectionAttribute::IS_INSTANCEOF) + : []), + ]; + + foreach ($attributes as $attribute) { + $this->openApiContext->tags->push($attribute); + } + } + private function getTagsAnnotatedByGroups(RouteInfo $routeInfo): array { $reflection = $routeInfo->reflectionAction(); @@ -234,6 +257,8 @@ private function getTagsAnnotatedByGroups(RouteInfo $routeInfo): array private function attachTagsToOpenApi(RouteInfo $routeInfo): void { + $this->collectDeclaredTags($routeInfo); + if (! $groups = $this->getTagsAnnotatedByGroups($routeInfo)) { return; } diff --git a/tests/Attributes/GroupTest.php b/tests/Attributes/GroupTest.php index 787850f4..d879ae4a 100644 --- a/tests/Attributes/GroupTest.php +++ b/tests/Attributes/GroupTest.php @@ -188,3 +188,193 @@ class GroupTest_B5_Controller #[GroupTest_OrdersGroup] public function __invoke() {} } + +it('sets native parent field on tags with parent parameter', function () { + RouteFacade::get('api/users', GroupTest_Users_Controller::class); + RouteFacade::get('api/profiles', GroupTest_Profiles_Controller::class); + + Scramble::routes(fn (Route $r) => in_array($r->uri, ['api/users', 'api/profiles'])); + + $openApiDoc = app()->make(Generator::class)(); + + expect($openApiDoc)->not->toHaveKey('x-tagGroups') + ->and(collect($openApiDoc['tags'])->firstWhere('name', 'users'))->toMatchArray([ + 'name' => 'users', + 'parent' => 'User Management', + ]) + ->and(collect($openApiDoc['tags'])->firstWhere('name', 'profiles'))->toMatchArray([ + 'name' => 'profiles', + 'parent' => 'User Management', + ]); +}); +#[Group(name: 'users', parent: 'User Management', weight: 1)] +class GroupTest_Users_Controller +{ + public function __invoke() {} +} +#[Group(name: 'profiles', parent: 'User Management', weight: 2)] +class GroupTest_Profiles_Controller +{ + public function __invoke() {} +} + +it('auto-creates missing parent tags', function () { + RouteFacade::get('api/users', GroupTest_Users_Controller::class); + RouteFacade::get('api/profiles', GroupTest_Profiles_Controller::class); + + Scramble::routes(fn (Route $r) => in_array($r->uri, ['api/users', 'api/profiles'])); + + $openApiDoc = app()->make(Generator::class)(); + + $tagNames = array_column($openApiDoc['tags'], 'name'); + + expect($tagNames)->toContain('User Management'); +}); + +it('sets native parent field with multiple groups and correct ordering', function () { + RouteFacade::get('api/products', GroupTest_Products_Controller::class); + RouteFacade::get('api/users2', GroupTest_Users2_Controller::class); + RouteFacade::get('api/profiles2', GroupTest_Profiles2_Controller::class); + + Scramble::routes(fn (Route $r) => in_array($r->uri, ['api/products', 'api/users2', 'api/profiles2'])); + + $openApiDoc = app()->make(Generator::class)(); + + expect($openApiDoc)->not->toHaveKey('x-tagGroups') + ->and(collect($openApiDoc['tags'])->firstWhere('name', 'products'))->toMatchArray([ + 'name' => 'products', + 'parent' => 'Products', + ]) + ->and(collect($openApiDoc['tags'])->firstWhere('name', 'users2'))->toMatchArray([ + 'name' => 'users2', + 'parent' => 'User Management', + ]) + ->and(collect($openApiDoc['tags'])->firstWhere('name', 'profiles2'))->toMatchArray([ + 'name' => 'profiles2', + 'parent' => 'User Management', + ]); +}); +#[Group(name: 'products', parent: 'Products', weight: 0)] +class GroupTest_Products_Controller +{ + public function __invoke() {} +} +#[Group(name: 'users2', parent: 'User Management', weight: 1)] +class GroupTest_Users2_Controller +{ + public function __invoke() {} +} +#[Group(name: 'profiles2', parent: 'User Management', weight: 2)] +class GroupTest_Profiles2_Controller +{ + public function __invoke() {} +} + +it('does not set parent field when no tags have parent', function () { + RouteFacade::get('api/simple', GroupTest_Simple_Controller::class); + + Scramble::routes(fn (Route $r) => $r->uri === 'api/simple'); + + $openApiDoc = app()->make(Generator::class)(); + + $tag = collect($openApiDoc['tags'])->firstWhere('name', 'simple'); + + expect($tag)->not->toHaveKey('parent') + ->and($openApiDoc)->not->toHaveKey('x-tagGroups'); +}); +#[Group(name: 'simple')] +class GroupTest_Simple_Controller +{ + public function __invoke() {} +} + +it('includes ungrouped tags without parent field alongside grouped tags', function () { + RouteFacade::get('api/grouped', GroupTest_Grouped_Controller::class); + RouteFacade::get('api/ungrouped', GroupTest_Ungrouped_Controller::class); + + Scramble::routes(fn (Route $r) => in_array($r->uri, ['api/grouped', 'api/ungrouped'])); + + $openApiDoc = app()->make(Generator::class)(); + + $groupedTag = collect($openApiDoc['tags'])->firstWhere('name', 'grouped'); + $ungroupedTag = collect($openApiDoc['tags'])->firstWhere('name', 'ungrouped'); + + expect($groupedTag)->toMatchArray([ + 'name' => 'grouped', + 'parent' => 'My Group', + ]) + ->and($ungroupedTag)->not->toHaveKey('parent') + ->and($openApiDoc)->not->toHaveKey('x-tagGroups'); +}); +#[Group(name: 'grouped', parent: 'My Group')] +class GroupTest_Grouped_Controller +{ + public function __invoke() {} +} +#[Group(name: 'ungrouped')] +class GroupTest_Ungrouped_Controller +{ + public function __invoke() {} +} + +it('sets summary field on tag', function () { + $openApiDoc = generateForRoute(fn () => RouteFacade::get('api/summary-test', GroupTest_Summary_Controller::class)); + + expect($openApiDoc['tags'][0])->toMatchArray([ + 'name' => 'Summarized', + 'description' => 'Full description', + 'summary' => 'Short summary', + ]); +}); +#[Group(name: 'Summarized', description: 'Full description', summary: 'Short summary')] +class GroupTest_Summary_Controller +{ + public function __invoke() {} +} + +it('sets kind field on tag', function () { + $openApiDoc = generateForRoute(fn () => RouteFacade::get('api/kind-test', GroupTest_Kind_Controller::class)); + + expect($openApiDoc['tags'][0])->toMatchArray([ + 'name' => 'ApiTag', + 'kind' => 'api', + ]); +}); +#[Group(name: 'ApiTag', kind: 'api')] +class GroupTest_Kind_Controller +{ + public function __invoke() {} +} + +it('sets externalDocs on tag', function () { + $openApiDoc = generateForRoute(fn () => RouteFacade::get('api/extdocs-test', GroupTest_ExternalDocs_Controller::class)); + + expect($openApiDoc['tags'][0])->toMatchArray([ + 'name' => 'Documented', + 'externalDocs' => [ + 'description' => 'More info', + 'url' => 'https://example.com/docs', + ], + ]); +}); +#[Group(name: 'Documented', externalDocsUrl: 'https://example.com/docs', externalDocsDescription: 'More info')] +class GroupTest_ExternalDocs_Controller +{ + public function __invoke() {} +} + +it('sets externalDocs on tag with url only', function () { + $openApiDoc = generateForRoute(fn () => RouteFacade::get('api/extdocs-url-test', GroupTest_ExternalDocsUrl_Controller::class)); + + expect($openApiDoc['tags'][0])->toMatchArray([ + 'name' => 'UrlOnly', + 'externalDocs' => [ + 'url' => 'https://example.com/docs', + ], + ]); +}); +#[Group(name: 'UrlOnly', externalDocsUrl: 'https://example.com/docs')] +class GroupTest_ExternalDocsUrl_Controller +{ + public function __invoke() {} +} diff --git a/tests/Attributes/TagTest.php b/tests/Attributes/TagTest.php new file mode 100644 index 00000000..6d9b3841 --- /dev/null +++ b/tests/Attributes/TagTest.php @@ -0,0 +1,96 @@ + $r->uri === 'api/a'); + + $openApiDoc = app()->make(Generator::class)(); + + expect($openApiDoc['tags'])->toBe([ + ['name' => 'Restaurants', 'description' => 'Public restaurant profiles.', 'parent' => 'Browsing'], + ['name' => 'Browsing', 'description' => 'What anyone can look at without an account.'], + ]); +}); + +it('leaves the endpoint in its own group', function () { + RouteFacade::get('api/a', TagTest_A_Controller::class); + + Scramble::routes(fn (Route $r) => $r->uri === 'api/a'); + + $openApiDoc = app()->make(Generator::class)(); + + expect($openApiDoc['paths']['/a']['get']['tags'])->toBe(['Restaurants']); +}); + +#[Group('Restaurants', 'Public restaurant profiles.', parent: 'Browsing')] +#[Tag('Browsing', 'What anyone can look at without an account.')] +class TagTest_A_Controller +{ + public function __invoke() {} +} + +it('declares more than one tag from a single class', function () { + RouteFacade::get('api/b', TagTest_B_Controller::class); + + Scramble::routes(fn (Route $r) => $r->uri === 'api/b'); + + $openApiDoc = app()->make(Generator::class)(); + + expect(collect($openApiDoc['tags'])->pluck('name')->all()) + ->toBe(['Orders', 'Ordering', 'Browsing']); +}); + +#[Group('Orders', parent: 'Ordering')] +#[Tag('Ordering', 'Building a cart and paying for it.')] +#[Tag('Browsing', 'What anyone can look at without an account.')] +class TagTest_B_Controller +{ + public function __invoke() {} +} + +it('nests a parent under a parent of its own', function () { + RouteFacade::get('api/c', TagTest_C_Controller::class); + + Scramble::routes(fn (Route $r) => $r->uri === 'api/c'); + + $openApiDoc = app()->make(Generator::class)(); + + expect($openApiDoc['tags'])->toBe([ + ['name' => 'Invoices', 'parent' => 'Finances'], + ['name' => 'Finances', 'description' => 'What was earned and charged.', 'parent' => 'Business'], + ['name' => 'Business'], + ]); +}); + +#[Group('Invoices', parent: 'Finances')] +#[Tag('Finances', 'What was earned and charged.', parent: 'Business')] +class TagTest_C_Controller +{ + public function __invoke() {} +} + +it('keeps what the group states when a tag declares the same name', function () { + RouteFacade::get('api/d', TagTest_D_Controller::class); + + Scramble::routes(fn (Route $r) => $r->uri === 'api/d'); + + $openApiDoc = app()->make(Generator::class)(); + + expect($openApiDoc['tags'][0]) + ->toBe(['name' => 'Catalog', 'description' => 'What the group says.']); +}); + +#[Group('Catalog', 'What the group says.')] +#[Tag('Catalog', 'What the declaration says.')] +class TagTest_D_Controller +{ + public function __invoke() {} +} diff --git a/tests/__snapshots__/ErrorsResponsesTest__it_adds_authorization_error_response__1.json b/tests/__snapshots__/ErrorsResponsesTest__it_adds_authorization_error_response__1.json index 18e2711c..660c97eb 100644 --- a/tests/__snapshots__/ErrorsResponsesTest__it_adds_authorization_error_response__1.json +++ b/tests/__snapshots__/ErrorsResponsesTest__it_adds_authorization_error_response__1.json @@ -1,5 +1,5 @@ { - "openapi": "3.1.0", + "openapi": "3.2.0", "info": { "title": "Laravel", "version": "0.0.1" diff --git a/tests/__snapshots__/ErrorsResponsesTest__it_adds_authorization_error_response_for_gate_authorize__1.json b/tests/__snapshots__/ErrorsResponsesTest__it_adds_authorization_error_response_for_gate_authorize__1.json index 8ffadfac..c11a0386 100644 --- a/tests/__snapshots__/ErrorsResponsesTest__it_adds_authorization_error_response_for_gate_authorize__1.json +++ b/tests/__snapshots__/ErrorsResponsesTest__it_adds_authorization_error_response_for_gate_authorize__1.json @@ -1,5 +1,5 @@ { - "openapi": "3.1.0", + "openapi": "3.2.0", "info": { "title": "Laravel", "version": "0.0.1" diff --git a/tests/__snapshots__/ErrorsResponsesTest__it_adds_errors_responses_with_custom_requests__1.json b/tests/__snapshots__/ErrorsResponsesTest__it_adds_errors_responses_with_custom_requests__1.json index 866e4191..29bd9d93 100644 --- a/tests/__snapshots__/ErrorsResponsesTest__it_adds_errors_responses_with_custom_requests__1.json +++ b/tests/__snapshots__/ErrorsResponsesTest__it_adds_errors_responses_with_custom_requests__1.json @@ -1,5 +1,5 @@ { - "openapi": "3.1.0", + "openapi": "3.2.0", "info": { "title": "Laravel", "version": "0.0.1" diff --git a/tests/__snapshots__/ErrorsResponsesTest__it_adds_not_found_error_response_with_Authorize__using__1.json b/tests/__snapshots__/ErrorsResponsesTest__it_adds_not_found_error_response_with_Authorize__using__1.json index 64086813..523179b3 100644 --- a/tests/__snapshots__/ErrorsResponsesTest__it_adds_not_found_error_response_with_Authorize__using__1.json +++ b/tests/__snapshots__/ErrorsResponsesTest__it_adds_not_found_error_response_with_Authorize__using__1.json @@ -1,5 +1,5 @@ { - "openapi": "3.1.0", + "openapi": "3.2.0", "info": { "title": "Laravel", "version": "0.0.1" diff --git a/tests/__snapshots__/ErrorsResponsesTest__it_adds_not_found_error_response_with_can_directive__1.json b/tests/__snapshots__/ErrorsResponsesTest__it_adds_not_found_error_response_with_can_directive__1.json index 64086813..523179b3 100644 --- a/tests/__snapshots__/ErrorsResponsesTest__it_adds_not_found_error_response_with_can_directive__1.json +++ b/tests/__snapshots__/ErrorsResponsesTest__it_adds_not_found_error_response_with_can_directive__1.json @@ -1,5 +1,5 @@ { - "openapi": "3.1.0", + "openapi": "3.2.0", "info": { "title": "Laravel", "version": "0.0.1" diff --git a/tests/__snapshots__/ErrorsResponsesTest__it_adds_validation_error_response__1.json b/tests/__snapshots__/ErrorsResponsesTest__it_adds_validation_error_response__1.json index b3f19140..e8d7eef7 100644 --- a/tests/__snapshots__/ErrorsResponsesTest__it_adds_validation_error_response__1.json +++ b/tests/__snapshots__/ErrorsResponsesTest__it_adds_validation_error_response__1.json @@ -1,5 +1,5 @@ { - "openapi": "3.1.0", + "openapi": "3.2.0", "info": { "title": "Laravel", "version": "0.0.1" diff --git a/tests/__snapshots__/ErrorsResponsesTest__it_adds_validation_error_response_when_documented_in_phpdoc__1.json b/tests/__snapshots__/ErrorsResponsesTest__it_adds_validation_error_response_when_documented_in_phpdoc__1.json index 37fa9db4..d7cbba67 100644 --- a/tests/__snapshots__/ErrorsResponsesTest__it_adds_validation_error_response_when_documented_in_phpdoc__1.json +++ b/tests/__snapshots__/ErrorsResponsesTest__it_adds_validation_error_response_when_documented_in_phpdoc__1.json @@ -1,5 +1,5 @@ { - "openapi": "3.1.0", + "openapi": "3.2.0", "info": { "title": "Laravel", "version": "0.0.1" diff --git a/tests/__snapshots__/ErrorsResponsesTest__it_adds_validation_error_response_with_facade_made_validators__1.json b/tests/__snapshots__/ErrorsResponsesTest__it_adds_validation_error_response_with_facade_made_validators__1.json index bccee341..24ff21c5 100644 --- a/tests/__snapshots__/ErrorsResponsesTest__it_adds_validation_error_response_with_facade_made_validators__1.json +++ b/tests/__snapshots__/ErrorsResponsesTest__it_adds_validation_error_response_with_facade_made_validators__1.json @@ -1,5 +1,5 @@ { - "openapi": "3.1.0", + "openapi": "3.2.0", "info": { "title": "Laravel", "version": "0.0.1" diff --git a/tests/__snapshots__/RequestBodyExtensionTest__it_allows_to_use_validation_on_form_request__1.json b/tests/__snapshots__/RequestBodyExtensionTest__it_allows_to_use_validation_on_form_request__1.json index 867383a6..d215f5d4 100644 --- a/tests/__snapshots__/RequestBodyExtensionTest__it_allows_to_use_validation_on_form_request__1.json +++ b/tests/__snapshots__/RequestBodyExtensionTest__it_allows_to_use_validation_on_form_request__1.json @@ -1,5 +1,5 @@ { - "openapi": "3.1.0", + "openapi": "3.2.0", "info": { "title": "Laravel", "version": "0.0.1" diff --git a/tests/__snapshots__/ResourceCollectionResponseTest__it_attaches_additional_data_to_the_response_documentation__1.json b/tests/__snapshots__/ResourceCollectionResponseTest__it_attaches_additional_data_to_the_response_documentation__1.json index baf44e4c..0562503f 100644 --- a/tests/__snapshots__/ResourceCollectionResponseTest__it_attaches_additional_data_to_the_response_documentation__1.json +++ b/tests/__snapshots__/ResourceCollectionResponseTest__it_attaches_additional_data_to_the_response_documentation__1.json @@ -1,5 +1,5 @@ { - "openapi": "3.1.0", + "openapi": "3.2.0", "info": { "title": "Laravel", "version": "0.0.1" diff --git a/tests/__snapshots__/ResponseDocumentingTest__manually_annotated_responses_support__1.json b/tests/__snapshots__/ResponseDocumentingTest__manually_annotated_responses_support__1.json index 40d22db1..71046299 100644 --- a/tests/__snapshots__/ResponseDocumentingTest__manually_annotated_responses_support__1.json +++ b/tests/__snapshots__/ResponseDocumentingTest__manually_annotated_responses_support__1.json @@ -1,5 +1,5 @@ { - "openapi": "3.1.0", + "openapi": "3.2.0", "info": { "title": "Laravel", "version": "0.0.1" diff --git a/tests/__snapshots__/ResponseDocumentingTest__multiple_responses_support__1.json b/tests/__snapshots__/ResponseDocumentingTest__multiple_responses_support__1.json index 06d76538..99d8f7bd 100644 --- a/tests/__snapshots__/ResponseDocumentingTest__multiple_responses_support__1.json +++ b/tests/__snapshots__/ResponseDocumentingTest__multiple_responses_support__1.json @@ -1,5 +1,5 @@ { - "openapi": "3.1.0", + "openapi": "3.2.0", "info": { "title": "Laravel", "version": "0.0.1" diff --git a/tests/__snapshots__/ResponseDocumentingTest__response____json___call_support_with_phpdoc_help__1.json b/tests/__snapshots__/ResponseDocumentingTest__response____json___call_support_with_phpdoc_help__1.json index 72b7e29e..c528ebeb 100644 --- a/tests/__snapshots__/ResponseDocumentingTest__response____json___call_support_with_phpdoc_help__1.json +++ b/tests/__snapshots__/ResponseDocumentingTest__response____json___call_support_with_phpdoc_help__1.json @@ -1,5 +1,5 @@ { - "openapi": "3.1.0", + "openapi": "3.2.0", "info": { "title": "Laravel", "version": "0.0.1" diff --git a/tests/__snapshots__/ResponseDocumentingTest__response____noContent___call_support__1.json b/tests/__snapshots__/ResponseDocumentingTest__response____noContent___call_support__1.json index ac0fc302..e19e4289 100644 --- a/tests/__snapshots__/ResponseDocumentingTest__response____noContent___call_support__1.json +++ b/tests/__snapshots__/ResponseDocumentingTest__response____noContent___call_support__1.json @@ -1,5 +1,5 @@ { - "openapi": "3.1.0", + "openapi": "3.2.0", "info": { "title": "Laravel", "version": "0.0.1" diff --git a/tests/__snapshots__/ValidationRulesDocumentingTest__it_extracts_rules_from_Validator__make_facade_call__1.json b/tests/__snapshots__/ValidationRulesDocumentingTest__it_extracts_rules_from_Validator__make_facade_call__1.json index 969d98f2..807f0021 100644 --- a/tests/__snapshots__/ValidationRulesDocumentingTest__it_extracts_rules_from_Validator__make_facade_call__1.json +++ b/tests/__snapshots__/ValidationRulesDocumentingTest__it_extracts_rules_from_Validator__make_facade_call__1.json @@ -1,5 +1,5 @@ { - "openapi": "3.1.0", + "openapi": "3.2.0", "info": { "title": "Laravel", "version": "0.0.1" diff --git a/tests/__snapshots__/ValidationRulesDocumentingTest__it_extracts_rules_from_request__validate_call__1.json b/tests/__snapshots__/ValidationRulesDocumentingTest__it_extracts_rules_from_request__validate_call__1.json index eaa9d2e7..cdeacb6a 100644 --- a/tests/__snapshots__/ValidationRulesDocumentingTest__it_extracts_rules_from_request__validate_call__1.json +++ b/tests/__snapshots__/ValidationRulesDocumentingTest__it_extracts_rules_from_request__validate_call__1.json @@ -1,5 +1,5 @@ { - "openapi": "3.1.0", + "openapi": "3.2.0", "info": { "title": "Laravel", "version": "0.0.1" diff --git a/tests/__snapshots__/ValidationRulesDocumentingTest__it_supports_manual_authentication_info__1.json b/tests/__snapshots__/ValidationRulesDocumentingTest__it_supports_manual_authentication_info__1.json index 455f1fec..11818cc5 100644 --- a/tests/__snapshots__/ValidationRulesDocumentingTest__it_supports_manual_authentication_info__1.json +++ b/tests/__snapshots__/ValidationRulesDocumentingTest__it_supports_manual_authentication_info__1.json @@ -1,5 +1,5 @@ { - "openapi": "3.1.0", + "openapi": "3.2.0", "info": { "title": "Laravel", "version": "0.0.1" diff --git a/tests/__snapshots__/ValidationRulesDocumentingTest__it_supports_validation_rules_and_form_request_at_the_same_time__1.json b/tests/__snapshots__/ValidationRulesDocumentingTest__it_supports_validation_rules_and_form_request_at_the_same_time__1.json index 605a55b1..b35eab28 100644 --- a/tests/__snapshots__/ValidationRulesDocumentingTest__it_supports_validation_rules_and_form_request_at_the_same_time__1.json +++ b/tests/__snapshots__/ValidationRulesDocumentingTest__it_supports_validation_rules_and_form_request_at_the_same_time__1.json @@ -1,5 +1,5 @@ { - "openapi": "3.1.0", + "openapi": "3.2.0", "info": { "title": "Laravel", "version": "0.0.1"