-
-
Notifications
You must be signed in to change notification settings - Fork 920
[RFR] Allow to specify formats per resources/operations #1798
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
<?php | ||
|
||
/* | ||
* This file is part of the API Platform project. | ||
* | ||
* (c) Kévin Dunglas <dunglas@gmail.com> | ||
* | ||
* 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\Core\Api; | ||
|
||
use ApiPlatform\Core\Exception\InvalidArgumentException; | ||
use ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface; | ||
|
||
/** | ||
* {@inheritdoc} | ||
* | ||
* @author Anthony GRASSIOT <antograssiot@free.fr> | ||
*/ | ||
final class FormatsProvider implements FormatsProviderInterface | ||
{ | ||
private $configuredFormats; | ||
private $resourceMetadataFactory; | ||
|
||
public function __construct(ResourceMetadataFactoryInterface $resourceMetadataFactory, array $configuredFormats) | ||
{ | ||
$this->resourceMetadataFactory = $resourceMetadataFactory; | ||
$this->configuredFormats = $configuredFormats; | ||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
* | ||
* @throws InvalidArgumentException | ||
*/ | ||
public function getFormatsFromAttributes(array $attributes): array | ||
{ | ||
if (!$attributes || !isset($attributes['resource_class'])) { | ||
return $this->configuredFormats; | ||
} | ||
|
||
$resourceMetadata = $this->resourceMetadataFactory->create($attributes['resource_class']); | ||
|
||
if (!$formats = $resourceMetadata->getOperationAttribute($attributes, 'formats', [], true)) { | ||
return $this->configuredFormats; | ||
} | ||
|
||
if (!\is_array($formats)) { | ||
throw new InvalidArgumentException(sprintf("The 'formats' attributes must be an array, %s given for resource class '%s'.", \gettype($formats), $attributes['resource_class'])); | ||
} | ||
|
||
return $this->getOperationFormats($formats); | ||
} | ||
|
||
/** | ||
* Filter and populate the acceptable formats. | ||
* | ||
* @throws InvalidArgumentException | ||
*/ | ||
private function getOperationFormats(array $annotationFormats): array | ||
{ | ||
$resourceFormats = []; | ||
foreach ($annotationFormats as $format => $value) { | ||
if (!is_numeric($format)) { | ||
$resourceFormats[$format] = (array) $value; | ||
continue; | ||
} | ||
if (!\is_string($value)) { | ||
throw new InvalidArgumentException(sprintf("The 'formats' attributes value must be a string when trying to include an already configured format, %s given.", \gettype($value))); | ||
} | ||
if (array_key_exists($value, $this->configuredFormats)) { | ||
$resourceFormats[$value] = $this->configuredFormats[$value]; | ||
continue; | ||
} | ||
|
||
throw new InvalidArgumentException(sprintf("You either need to add the format '%s' to your project configuration or declare a mime type for it in your annotation.", $value)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you add the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done |
||
} | ||
|
||
return $resourceFormats; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
<?php | ||
|
||
/* | ||
* This file is part of the API Platform project. | ||
* | ||
* (c) Kévin Dunglas <dunglas@gmail.com> | ||
* | ||
* 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\Core\Api; | ||
|
||
/** | ||
* Extracts formats for a given operation according to the retrieved Metadata. | ||
* | ||
* @author Anthony GRASSIOT <antograssiot@free.fr> | ||
*/ | ||
interface FormatsProviderInterface | ||
{ | ||
/** | ||
* Finds formats for an operation. | ||
*/ | ||
public function getFormatsFromAttributes(array $attributes): array; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,10 +13,13 @@ | |
|
||
namespace ApiPlatform\Core\Bridge\Symfony\Bundle\Action; | ||
|
||
use ApiPlatform\Core\Api\FormatsProviderInterface; | ||
use ApiPlatform\Core\Documentation\Documentation; | ||
use ApiPlatform\Core\Exception\InvalidArgumentException; | ||
use ApiPlatform\Core\Exception\RuntimeException; | ||
use ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface; | ||
use ApiPlatform\Core\Metadata\Resource\Factory\ResourceNameCollectionFactoryInterface; | ||
use ApiPlatform\Core\Util\RequestAttributesExtractor; | ||
use Symfony\Component\HttpFoundation\Request; | ||
use Symfony\Component\HttpFoundation\Response; | ||
use Symfony\Component\Routing\Generator\UrlGeneratorInterface; | ||
|
@@ -37,7 +40,7 @@ final class SwaggerUiAction | |
private $title; | ||
private $description; | ||
private $version; | ||
private $formats; | ||
private $formats = []; | ||
private $oauthEnabled; | ||
private $oauthClientId; | ||
private $oauthClientSecret; | ||
|
@@ -46,8 +49,12 @@ final class SwaggerUiAction | |
private $oauthTokenUrl; | ||
private $oauthAuthorizationUrl; | ||
private $oauthScopes; | ||
private $formatsProvider; | ||
|
||
public function __construct(ResourceNameCollectionFactoryInterface $resourceNameCollectionFactory, ResourceMetadataFactoryInterface $resourceMetadataFactory, NormalizerInterface $normalizer, \Twig_Environment $twig, UrlGeneratorInterface $urlGenerator, string $title = '', string $description = '', string $version = '', array $formats = [], $oauthEnabled = false, $oauthClientId = '', $oauthClientSecret = '', $oauthType = '', $oauthFlow = '', $oauthTokenUrl = '', $oauthAuthorizationUrl = '', $oauthScopes = []) | ||
/** | ||
* @throws InvalidArgumentException | ||
*/ | ||
public function __construct(ResourceNameCollectionFactoryInterface $resourceNameCollectionFactory, ResourceMetadataFactoryInterface $resourceMetadataFactory, NormalizerInterface $normalizer, \Twig_Environment $twig, UrlGeneratorInterface $urlGenerator, string $title = '', string $description = '', string $version = '', /* FormatsProviderInterface */ $formatsProvider = [], $oauthEnabled = false, $oauthClientId = '', $oauthClientSecret = '', $oauthType = '', $oauthFlow = '', $oauthTokenUrl = '', $oauthAuthorizationUrl = '', $oauthScopes = []) | ||
{ | ||
$this->resourceNameCollectionFactory = $resourceNameCollectionFactory; | ||
$this->resourceMetadataFactory = $resourceMetadataFactory; | ||
|
@@ -57,7 +64,6 @@ public function __construct(ResourceNameCollectionFactoryInterface $resourceName | |
$this->title = $title; | ||
$this->description = $description; | ||
$this->version = $version; | ||
$this->formats = $formats; | ||
$this->oauthEnabled = $oauthEnabled; | ||
$this->oauthClientId = $oauthClientId; | ||
$this->oauthClientSecret = $oauthClientSecret; | ||
|
@@ -66,10 +72,30 @@ public function __construct(ResourceNameCollectionFactoryInterface $resourceName | |
$this->oauthTokenUrl = $oauthTokenUrl; | ||
$this->oauthAuthorizationUrl = $oauthAuthorizationUrl; | ||
$this->oauthScopes = $oauthScopes; | ||
|
||
if (\is_array($formatsProvider)) { | ||
if ($formatsProvider) { | ||
// Only trigger notification for non-default argument | ||
@trigger_error('Using an array as formats provider is deprecated since API Platform 2.3 and will not be possible anymore in API Platform 3', E_USER_DEPRECATED); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is this about the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the only exception seems to be the AbstractFilter ... but I can update of course if necessary There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think to be coherent it's better to update the AbstractFilter ;). let it like this |
||
} | ||
$this->formats = $formatsProvider; | ||
|
||
return; | ||
} | ||
if (!$formatsProvider instanceof FormatsProviderInterface) { | ||
throw new InvalidArgumentException(sprintf('The "$formatsProvider" argument is expected to be an implementation of the "%s" interface.', FormatsProviderInterface::class)); | ||
} | ||
|
||
$this->formatsProvider = $formatsProvider; | ||
} | ||
|
||
public function __invoke(Request $request) | ||
{ | ||
// BC check to be removed in 3.0 | ||
if (null !== $this->formatsProvider) { | ||
$this->formats = $this->formatsProvider->getFormatsFromAttributes(RequestAttributesExtractor::extractAttributes($request)); | ||
} | ||
|
||
$documentation = new Documentation($this->resourceNameCollectionFactory->create(), $this->title, $this->description, $this->version, $this->formats); | ||
|
||
return new Response($this->twig->render('@ApiPlatform/SwaggerUi/index.html.twig', $this->getContext($request, $documentation))); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,8 +13,11 @@ | |
|
||
namespace ApiPlatform\Core\Documentation\Action; | ||
|
||
use ApiPlatform\Core\Api\FormatsProviderInterface; | ||
use ApiPlatform\Core\Documentation\Documentation; | ||
use ApiPlatform\Core\Exception\InvalidArgumentException; | ||
use ApiPlatform\Core\Metadata\Resource\Factory\ResourceNameCollectionFactoryInterface; | ||
use ApiPlatform\Core\Util\RequestAttributesExtractor; | ||
use Symfony\Component\HttpFoundation\Request; | ||
|
||
/** | ||
|
@@ -28,15 +31,32 @@ final class DocumentationAction | |
private $title; | ||
private $description; | ||
private $version; | ||
private $formats; | ||
private $formats = []; | ||
private $formatsProvider; | ||
|
||
public function __construct(ResourceNameCollectionFactoryInterface $resourceNameCollectionFactory, string $title = '', string $description = '', string $version = '', array $formats = []) | ||
/** | ||
* @throws InvalidArgumentException | ||
*/ | ||
public function __construct(ResourceNameCollectionFactoryInterface $resourceNameCollectionFactory, string $title = '', string $description = '', string $version = '', /* FormatsProviderInterface */ $formatsProvider = []) | ||
{ | ||
$this->resourceNameCollectionFactory = $resourceNameCollectionFactory; | ||
$this->title = $title; | ||
$this->description = $description; | ||
$this->version = $version; | ||
$this->formats = $formats; | ||
if (\is_array($formatsProvider)) { | ||
if ($formatsProvider) { | ||
// Only trigger notification for non-default argument | ||
@trigger_error('Using an array as formats provider is deprecated since API Platform 2.3 and will not be possible anymore in API Platform 3', E_USER_DEPRECATED); | ||
} | ||
$this->formats = $formatsProvider; | ||
|
||
return; | ||
} | ||
if (!$formatsProvider instanceof FormatsProviderInterface) { | ||
throw new InvalidArgumentException(sprintf('The "$formatsProvider" argument is expected to be an implementation of the "%s" interface.', FormatsProviderInterface::class)); | ||
} | ||
|
||
$this->formatsProvider = $formatsProvider; | ||
} | ||
|
||
public function __invoke(Request $request = null): Documentation | ||
|
@@ -47,6 +67,12 @@ public function __invoke(Request $request = null): Documentation | |
$context['api_gateway'] = true; | ||
} | ||
$request->attributes->set('_api_normalization_context', $request->attributes->get('_api_normalization_context', []) + $context); | ||
|
||
$attributes = RequestAttributesExtractor::extractAttributes($request); | ||
} | ||
// BC check to be removed in 3.0 | ||
if (null !== $this->formatsProvider) { | ||
$this->formats = $this->formatsProvider->getFormatsFromAttributes($attributes ?? []); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is no need to declare $this->formats = $this->formatsProvider->getFormatsFromAttributes(RequestAttributesExtractor::extractAttributes($request)); There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is needed because |
||
} | ||
|
||
return new Documentation($this->resourceNameCollectionFactory->create(), $this->title, $this->description, $this->version, $this->formats); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In my PR (#1685) it was mentioned that the core team does not want to add more root annotation configuration values but instead put it into
attributes
. Just saying that this may need to be modified but I let @dunglas decide on this. I don't remember the exact reason for this anymore because I actually like when things are specific 😄There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this just follows #1788, nothing more