Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions features/openapi/docs.feature
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,14 @@ Feature: Documentation support
}
}
"""
And the JSON node "paths./override_open_api_responses.post.responses" should be equal to:
"""
{
"204": {
"description": "User activated"
}
}
"""

Scenario: OpenAPI UI is enabled for docs endpoint
Given I add "Accept" header equal to "text/html"
Expand Down
69 changes: 37 additions & 32 deletions src/OpenApi/Factory/OpenApiFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ final class OpenApiFactory implements OpenApiFactoryInterface
use NormalizeOperationNameTrait;

public const BASE_URL = 'base_url';
public const OVERRIDE_OPENAPI_RESPONSES = 'open_api_override_responses';
private readonly Options $openApiOptions;
private readonly PaginationOptions $paginationOptions;
private ?RouteCollection $routeCollection = null;
Expand Down Expand Up @@ -284,37 +285,40 @@ private function collectPaths(ApiResource $resource, ResourceMetadataCollection
}

$existingResponses = $openapiOperation?->getResponses() ?: [];
// Create responses
switch ($method) {
case 'GET':
$successStatus = (string) $operation->getStatus() ?: 200;
$openapiOperation = $this->buildOpenApiResponse($existingResponses, $successStatus, sprintf('%s %s', $resourceShortName, $operation instanceof CollectionOperationInterface ? 'collection' : 'resource'), $openapiOperation, $operation, $responseMimeTypes, $operationOutputSchemas);
break;
case 'POST':
$successStatus = (string) $operation->getStatus() ?: 201;

$openapiOperation = $this->buildOpenApiResponse($existingResponses, $successStatus, sprintf('%s resource created', $resourceShortName), $openapiOperation, $operation, $responseMimeTypes, $operationOutputSchemas, $resourceMetadataCollection);

$openapiOperation = $this->buildOpenApiResponse($existingResponses, '400', 'Invalid input', $openapiOperation);

$openapiOperation = $this->buildOpenApiResponse($existingResponses, '422', 'Unprocessable entity', $openapiOperation);
break;
case 'PATCH':
case 'PUT':
$successStatus = (string) $operation->getStatus() ?: 200;
$openapiOperation = $this->buildOpenApiResponse($existingResponses, $successStatus, sprintf('%s resource updated', $resourceShortName), $openapiOperation, $operation, $responseMimeTypes, $operationOutputSchemas, $resourceMetadataCollection);
$openapiOperation = $this->buildOpenApiResponse($existingResponses, '400', 'Invalid input', $openapiOperation);
if (!isset($existingResponses[400])) {
$openapiOperation = $openapiOperation->withResponse(400, new Response('Invalid input'));
}
$openapiOperation = $this->buildOpenApiResponse($existingResponses, '422', 'Unprocessable entity', $openapiOperation);
break;
case 'DELETE':
$successStatus = (string) $operation->getStatus() ?: 204;

$openapiOperation = $this->buildOpenApiResponse($existingResponses, $successStatus, sprintf('%s resource deleted', $resourceShortName), $openapiOperation);

break;
$overrideResponses = $operation->getExtraProperties()[self::OVERRIDE_OPENAPI_RESPONSES] ?? $this->openApiOptions->getOverrideResponses();
if ($overrideResponses || !$existingResponses) {
// Create responses
switch ($method) {
case 'GET':
$successStatus = (string) $operation->getStatus() ?: 200;
$openapiOperation = $this->buildOpenApiResponse($existingResponses, $successStatus, sprintf('%s %s', $resourceShortName, $operation instanceof CollectionOperationInterface ? 'collection' : 'resource'), $openapiOperation, $operation, $responseMimeTypes, $operationOutputSchemas);
break;
case 'POST':
$successStatus = (string) $operation->getStatus() ?: 201;

$openapiOperation = $this->buildOpenApiResponse($existingResponses, $successStatus, sprintf('%s resource created', $resourceShortName), $openapiOperation, $operation, $responseMimeTypes, $operationOutputSchemas, $resourceMetadataCollection);

$openapiOperation = $this->buildOpenApiResponse($existingResponses, '400', 'Invalid input', $openapiOperation);

$openapiOperation = $this->buildOpenApiResponse($existingResponses, '422', 'Unprocessable entity', $openapiOperation);
break;
case 'PATCH':
case 'PUT':
$successStatus = (string) $operation->getStatus() ?: 200;
$openapiOperation = $this->buildOpenApiResponse($existingResponses, $successStatus, sprintf('%s resource updated', $resourceShortName), $openapiOperation, $operation, $responseMimeTypes, $operationOutputSchemas, $resourceMetadataCollection);
$openapiOperation = $this->buildOpenApiResponse($existingResponses, '400', 'Invalid input', $openapiOperation);
if (!isset($existingResponses[400])) {
$openapiOperation = $openapiOperation->withResponse(400, new Response('Invalid input'));
}
$openapiOperation = $this->buildOpenApiResponse($existingResponses, '422', 'Unprocessable entity', $openapiOperation);
break;
case 'DELETE':
$successStatus = (string) $operation->getStatus() ?: 204;

$openapiOperation = $this->buildOpenApiResponse($existingResponses, $successStatus, sprintf('%s resource deleted', $resourceShortName), $openapiOperation);

break;
}
}

if (!$operation instanceof CollectionOperationInterface && 'POST' !== $operation->getMethod()) {
Expand Down Expand Up @@ -594,7 +598,8 @@ private function getFiltersParameters(CollectionOperationInterface|HttpOperation
$data['openapi']['explode'] ?? ('array' === $schema['type']),
$data['openapi']['allowReserved'] ?? false,
$data['openapi']['example'] ?? null,
isset($data['openapi']['examples']
isset(
$data['openapi']['examples']
) ? new \ArrayObject($data['openapi']['examples']) : null
);
}
Expand Down
7 changes: 6 additions & 1 deletion src/OpenApi/Options.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

final class Options
{
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)
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)
{
}

Expand Down Expand Up @@ -103,4 +103,9 @@ public function getLicenseUrl(): ?string
{
return $this->licenseUrl;
}

public function getOverrideResponses(): bool
{
return $this->overrideResponses;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -946,6 +946,7 @@ private function registerOpenApiConfiguration(ContainerBuilder $container, array
$container->setParameter('api_platform.openapi.contact.email', $config['openapi']['contact']['email']);
$container->setParameter('api_platform.openapi.license.name', $config['openapi']['license']['name']);
$container->setParameter('api_platform.openapi.license.url', $config['openapi']['license']['url']);
$container->setParameter('api_platform.openapi.overrideResponses', $config['openapi']['overrideResponses']);

$loader->load('json_schema.xml');
}
Expand Down
1 change: 1 addition & 0 deletions src/Symfony/Bundle/DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,7 @@ private function addOpenApiSection(ArrayNodeDefinition $rootNode): void
->end()
->info('To pass extra configuration to Swagger UI, like docExpansion or filter.')
->end()
->booleanNode('overrideResponses')->defaultTrue()->info('Whether API Platform adds automatic responses to the OpenAPI documentation.')
->end()
->end()
->end();
Expand Down
1 change: 1 addition & 0 deletions src/Symfony/Bundle/Resources/config/openapi.xml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
<argument>%api_platform.openapi.termsOfService%</argument>
<argument>%api_platform.openapi.license.name%</argument>
<argument>%api_platform.openapi.license.url%</argument>
<argument>%api_platform.openapi.overrideResponses%</argument>
</service>
<service id="ApiPlatform\OpenApi\Options" alias="api_platform.openapi.options" />

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

/*
* This file is part of the API Platform project.
*
* (c) Kévin Dunglas <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace ApiPlatform\Tests\Fixtures\TestBundle\ApiResource\Issue6151;

use ApiPlatform\Metadata\Post;
use ApiPlatform\OpenApi\Factory\OpenApiFactory;
use ApiPlatform\OpenApi\Model\Operation;
use ApiPlatform\OpenApi\Model\Response;

#[Post(
uriTemplate: '/override_open_api_responses',
openapi: new Operation(
responses: [
'204' => new Response(
description: 'User activated',
),
]
),
extraProperties: [OpenApiFactory::OVERRIDE_OPENAPI_RESPONSES => false],
)]
final class OverrideOpenApiResponses
{
}
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@ private function runDefaultConfigTests(array $doctrineIntegrationsToLoad = ['orm
'url' => null,
],
'swagger_ui_extra_configuration' => [],
'overrideResponses' => true,
],
'maker' => [
'enabled' => true,
Expand Down