Skip to content

Commit a2b7515

Browse files
committed
feat(openapi): disable response override
fixes #6151
1 parent 71b4f35 commit a2b7515

File tree

7 files changed

+88
-33
lines changed

7 files changed

+88
-33
lines changed

features/openapi/docs.feature

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,14 @@ Feature: Documentation support
266266
}
267267
}
268268
"""
269+
And the JSON node "paths./override_open_api_responses.post.responses" should be equal to:
270+
"""
271+
{
272+
"204": {
273+
"description": "User activated"
274+
}
275+
}
276+
"""
269277

270278
Scenario: OpenAPI UI is enabled for docs endpoint
271279
Given I add "Accept" header equal to "text/html"

src/OpenApi/Factory/OpenApiFactory.php

Lines changed: 37 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ final class OpenApiFactory implements OpenApiFactoryInterface
6060
use NormalizeOperationNameTrait;
6161

6262
public const BASE_URL = 'base_url';
63+
public const OVERRIDE_OPENAPI_RESPONSES = 'open_api_override_responses';
6364
private readonly Options $openApiOptions;
6465
private readonly PaginationOptions $paginationOptions;
6566
private ?RouteCollection $routeCollection = null;
@@ -284,37 +285,40 @@ private function collectPaths(ApiResource $resource, ResourceMetadataCollection
284285
}
285286

286287
$existingResponses = $openapiOperation?->getResponses() ?: [];
287-
// Create responses
288-
switch ($method) {
289-
case 'GET':
290-
$successStatus = (string) $operation->getStatus() ?: 200;
291-
$openapiOperation = $this->buildOpenApiResponse($existingResponses, $successStatus, sprintf('%s %s', $resourceShortName, $operation instanceof CollectionOperationInterface ? 'collection' : 'resource'), $openapiOperation, $operation, $responseMimeTypes, $operationOutputSchemas);
292-
break;
293-
case 'POST':
294-
$successStatus = (string) $operation->getStatus() ?: 201;
295-
296-
$openapiOperation = $this->buildOpenApiResponse($existingResponses, $successStatus, sprintf('%s resource created', $resourceShortName), $openapiOperation, $operation, $responseMimeTypes, $operationOutputSchemas, $resourceMetadataCollection);
297-
298-
$openapiOperation = $this->buildOpenApiResponse($existingResponses, '400', 'Invalid input', $openapiOperation);
299-
300-
$openapiOperation = $this->buildOpenApiResponse($existingResponses, '422', 'Unprocessable entity', $openapiOperation);
301-
break;
302-
case 'PATCH':
303-
case 'PUT':
304-
$successStatus = (string) $operation->getStatus() ?: 200;
305-
$openapiOperation = $this->buildOpenApiResponse($existingResponses, $successStatus, sprintf('%s resource updated', $resourceShortName), $openapiOperation, $operation, $responseMimeTypes, $operationOutputSchemas, $resourceMetadataCollection);
306-
$openapiOperation = $this->buildOpenApiResponse($existingResponses, '400', 'Invalid input', $openapiOperation);
307-
if (!isset($existingResponses[400])) {
308-
$openapiOperation = $openapiOperation->withResponse(400, new Response('Invalid input'));
309-
}
310-
$openapiOperation = $this->buildOpenApiResponse($existingResponses, '422', 'Unprocessable entity', $openapiOperation);
311-
break;
312-
case 'DELETE':
313-
$successStatus = (string) $operation->getStatus() ?: 204;
314-
315-
$openapiOperation = $this->buildOpenApiResponse($existingResponses, $successStatus, sprintf('%s resource deleted', $resourceShortName), $openapiOperation);
316-
317-
break;
288+
$overrideResponses = $operation->getExtraProperties()[self::OVERRIDE_OPENAPI_RESPONSES] ?? $this->openApiOptions->getOverrideResponses();
289+
if ($overrideResponses || !$existingResponses) {
290+
// Create responses
291+
switch ($method) {
292+
case 'GET':
293+
$successStatus = (string) $operation->getStatus() ?: 200;
294+
$openapiOperation = $this->buildOpenApiResponse($existingResponses, $successStatus, sprintf('%s %s', $resourceShortName, $operation instanceof CollectionOperationInterface ? 'collection' : 'resource'), $openapiOperation, $operation, $responseMimeTypes, $operationOutputSchemas);
295+
break;
296+
case 'POST':
297+
$successStatus = (string) $operation->getStatus() ?: 201;
298+
299+
$openapiOperation = $this->buildOpenApiResponse($existingResponses, $successStatus, sprintf('%s resource created', $resourceShortName), $openapiOperation, $operation, $responseMimeTypes, $operationOutputSchemas, $resourceMetadataCollection);
300+
301+
$openapiOperation = $this->buildOpenApiResponse($existingResponses, '400', 'Invalid input', $openapiOperation);
302+
303+
$openapiOperation = $this->buildOpenApiResponse($existingResponses, '422', 'Unprocessable entity', $openapiOperation);
304+
break;
305+
case 'PATCH':
306+
case 'PUT':
307+
$successStatus = (string) $operation->getStatus() ?: 200;
308+
$openapiOperation = $this->buildOpenApiResponse($existingResponses, $successStatus, sprintf('%s resource updated', $resourceShortName), $openapiOperation, $operation, $responseMimeTypes, $operationOutputSchemas, $resourceMetadataCollection);
309+
$openapiOperation = $this->buildOpenApiResponse($existingResponses, '400', 'Invalid input', $openapiOperation);
310+
if (!isset($existingResponses[400])) {
311+
$openapiOperation = $openapiOperation->withResponse(400, new Response('Invalid input'));
312+
}
313+
$openapiOperation = $this->buildOpenApiResponse($existingResponses, '422', 'Unprocessable entity', $openapiOperation);
314+
break;
315+
case 'DELETE':
316+
$successStatus = (string) $operation->getStatus() ?: 204;
317+
318+
$openapiOperation = $this->buildOpenApiResponse($existingResponses, $successStatus, sprintf('%s resource deleted', $resourceShortName), $openapiOperation);
319+
320+
break;
321+
}
318322
}
319323

320324
if (!$operation instanceof CollectionOperationInterface && 'POST' !== $operation->getMethod()) {
@@ -594,7 +598,8 @@ private function getFiltersParameters(CollectionOperationInterface|HttpOperation
594598
$data['openapi']['explode'] ?? ('array' === $schema['type']),
595599
$data['openapi']['allowReserved'] ?? false,
596600
$data['openapi']['example'] ?? null,
597-
isset($data['openapi']['examples']
601+
isset(
602+
$data['openapi']['examples']
598603
) ? new \ArrayObject($data['openapi']['examples']) : null
599604
);
600605
}

src/OpenApi/Options.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
final class Options
1717
{
18-
public function __construct(private readonly string $title, private readonly string $description = '', private readonly string $version = '', private readonly bool $oAuthEnabled = false, private readonly ?string $oAuthType = null, private readonly ?string $oAuthFlow = null, private readonly ?string $oAuthTokenUrl = null, private readonly ?string $oAuthAuthorizationUrl = null, private readonly ?string $oAuthRefreshUrl = null, private readonly array $oAuthScopes = [], private readonly array $apiKeys = [], private readonly ?string $contactName = null, private readonly ?string $contactUrl = null, private readonly ?string $contactEmail = null, private readonly ?string $termsOfService = null, private readonly ?string $licenseName = null, private readonly ?string $licenseUrl = null)
18+
public function __construct(private readonly string $title, private readonly string $description = '', private readonly string $version = '', private readonly bool $oAuthEnabled = false, private readonly ?string $oAuthType = null, private readonly ?string $oAuthFlow = null, private readonly ?string $oAuthTokenUrl = null, private readonly ?string $oAuthAuthorizationUrl = null, private readonly ?string $oAuthRefreshUrl = null, private readonly array $oAuthScopes = [], private readonly array $apiKeys = [], private readonly ?string $contactName = null, private readonly ?string $contactUrl = null, private readonly ?string $contactEmail = null, private readonly ?string $termsOfService = null, private readonly ?string $licenseName = null, private readonly ?string $licenseUrl = null, private bool $overrideResponses = true)
1919
{
2020
}
2121

@@ -103,4 +103,9 @@ public function getLicenseUrl(): ?string
103103
{
104104
return $this->licenseUrl;
105105
}
106+
107+
public function getOverrideResponses(): bool
108+
{
109+
return $this->overrideResponses;
110+
}
106111
}

src/Symfony/Bundle/DependencyInjection/ApiPlatformExtension.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -946,6 +946,7 @@ private function registerOpenApiConfiguration(ContainerBuilder $container, array
946946
$container->setParameter('api_platform.openapi.contact.email', $config['openapi']['contact']['email']);
947947
$container->setParameter('api_platform.openapi.license.name', $config['openapi']['license']['name']);
948948
$container->setParameter('api_platform.openapi.license.url', $config['openapi']['license']['url']);
949+
$container->setParameter('api_platform.openapi.overrideResponses', $config['openapi']['overrideResponses']);
949950

950951
$loader->load('json_schema.xml');
951952
}

src/Symfony/Bundle/DependencyInjection/Configuration.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -488,6 +488,7 @@ private function addOpenApiSection(ArrayNodeDefinition $rootNode): void
488488
->end()
489489
->info('To pass extra configuration to Swagger UI, like docExpansion or filter.')
490490
->end()
491+
->booleanNode('overrideResponses')->defaultTrue()->info('Whether API Platform adds automatic responses to the OpenAPI documentation.')
491492
->end()
492493
->end()
493494
->end();

src/Symfony/Bundle/Resources/config/openapi.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
<argument>%api_platform.openapi.termsOfService%</argument>
4848
<argument>%api_platform.openapi.license.name%</argument>
4949
<argument>%api_platform.openapi.license.url%</argument>
50+
<argument>%api_platform.openapi.overrideResponses%</argument>
5051
</service>
5152
<service id="ApiPlatform\OpenApi\Options" alias="api_platform.openapi.options" />
5253

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the API Platform project.
5+
*
6+
* (c) Kévin Dunglas <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
declare(strict_types=1);
13+
14+
namespace ApiPlatform\Tests\Fixtures\TestBundle\ApiResource\Issue6151;
15+
16+
use ApiPlatform\Metadata\Post;
17+
use ApiPlatform\OpenApi\Factory\OpenApiFactory;
18+
use ApiPlatform\OpenApi\Model\Operation;
19+
use ApiPlatform\OpenApi\Model\Response;
20+
21+
#[Post(
22+
uriTemplate: '/override_open_api_responses',
23+
openapi: new Operation(
24+
responses: [
25+
'204' => new Response(
26+
description: 'User activated',
27+
),
28+
]
29+
),
30+
extraProperties: [OpenApiFactory::OVERRIDE_OPENAPI_RESPONSES => false],
31+
)]
32+
final class OverrideOpenApiResponses
33+
{
34+
}

0 commit comments

Comments
 (0)