-
-
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
Conversation
dad282d
to
9c17235
Compare
9c17235
to
b80fc12
Compare
Playing a bit more with this and when adding test I think I need to introduce a new service like a FormatProvider that will be called by every service that actually receives |
c9fb1f1
to
9fc3c53
Compare
I'm for keeping only |
PR updated to only support the simplest declaration form. |
e345956
to
98997fa
Compare
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.
To me there is a flaw in the logic. The service shouldn't depend of the request, it should use the attributes directly to find the accepted formats for every resource class, then the Swagger documentation and the listener should use this new service to respectively display the available formats or throw an exception when a non-supported format is requested.
src/Api/FormatsProvider.php
Outdated
private $formats; | ||
private $resourceMetadataFactory; | ||
|
||
public function __construct(ResourceMetadataFactoryInterface $resourceMetadataFactory, array $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.
I suggest to renamed formats
in configuredFormats
, it will be mor explicit
src/Api/FormatsProvider.php
Outdated
*/ | ||
public function fromRequest(Request $request): array | ||
{ | ||
if ($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.
It looks buggy: if a different format can be configured per resource, then you must test for $this->formats[$resourceClass]
or the bad formats will be returned (for instance the ones of the previously requested resource class).
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.
I'm not storing key/values just the format for one operation right now
Ok I'll think about your comment @dunglas. I'm the impression that you expect this service to register all acceptables format per resource, but as the format could be defined at the operation level, that seems a bit weird to me. |
Sorry for coming late to the party and thanks for pinging me @antograssiot. I'm +1 on the fact that it should go into its own service and become part of the resource metadata rather than the request as @dunglas stated. Even if you allow to specify the formats on operation level, it is still resource meta data that can have different sources, not only annotations. |
98997fa
to
d8f6ea2
Compare
@dunglas it has been updated and doesn't rely on the SF |
@Toflar it already leverages the |
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.
@Toflar it already leverages the getOperationAttribute() method from the resource metadata so already doesn't rely on annotation only but would work with Yaml or XML like every other attributes. Did I missed something ?
I was just mentioning again 😄 PR looks already a lot better now, excellent work! I left a few comments, mainly about improving DX when upgrading to v3 later on but also on the general concept about how the configuration should behave. I hope it helps, keep up the work!
src/Api/FormatsProvider.php
Outdated
public function __construct(ResourceMetadataFactoryInterface $resourceMetadataFactory, array $formats) | ||
{ | ||
$this->resourceMetadataFactory = $resourceMetadataFactory; | ||
$this->configuredFormats = $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.
Could you also rename $formats
to $configuredFormats
to be consistent here?
src/Api/FormatsProvider.php
Outdated
/** | ||
* Finds formats from a Resource class. | ||
*/ | ||
public function getFormatsFromAttributes(array $attributes): array |
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.
Not sure if I like this concept here. I mean it's a matter of how we want it to work. Right now the specified formats on the resource would override the generally configured formats. But if I think about it, the generally configured formats are general. So what I expect this annotation to do is to configure additional formats for this specific resource. So imho it should merge the configured formats with the ones defined on the resource. I understand that it can be a use case to say everything should be available in json
except for this one resource so maybe how it behaves should be a configuration option too?
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.
That's indeed one of the concerns I have too. it could be painful if you add a general format, you'll then need to update all annotation.
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.
Yeah for me the general ones are the ones that should be available for all endpoints. So merging seems more appropriate to me.
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.
I like the idea to override the default formats, it's pretty useful (at the operation level at least) I think. IMHO, If we merge the configured formats, we also have to find a way to be able to completely override the default 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.
Yeah, thinking about additionalFormats
and formats
. The first just adds and the other overrides and you're done.
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.
ok let's try to get this merged with formats
only and I'll add additionalFormats
in a following PR if @api-platform/core-team agrees. That will be pretty easy anyway
{ | ||
$this->negotiator = $negotiator; | ||
$this->formats = $formats; | ||
$this->formatsProvider = $formatsProvider; |
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.
You could remove the typehint and do something like this:
public function __construct(Negotiator $negotiator, $formatProvider)
{
// BC
if (\is_array($formatProvider)) {
// deprecated notice but set anyway
return;
}
if (!$formatProvider instanceof FormatsProviderInterface) {
// throw new \InvalidArgumentException......
}
}
@@ -51,6 +55,10 @@ public function onKernelRequest(GetResponseEvent $event) | |||
return; | |||
} | |||
|
|||
if (null !== $this->formatsProvider) { |
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.
Would need a comment to be removed once BC is removed for Version 3.
|
||
public function __construct(SerializerInterface $serializer, SerializerContextBuilderInterface $serializerContextBuilder, array $formats) | ||
public function __construct(SerializerInterface $serializer, SerializerContextBuilderInterface $serializerContextBuilder, array $formats, FormatsProviderInterface $formatsProvider = null) |
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.
Same here with the constructor. I just want to omit we add another 5 arguments to the constructor because it makes migration from 2 to 3 harder.
@@ -129,6 +130,13 @@ | |||
*/ | |||
private $forceEager; | |||
|
|||
/** | |||
* @see https://github.com/Haehnchen/idea-php-annotation-plugin/issues/112 |
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
PR has been updated, new review to come
src/Api/FormatsProvider.php
Outdated
{ | ||
$this->resourceMetadataFactory = $resourceMetadataFactory; | ||
$this->configuredFormats = $formats; | ||
$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.
public $formats = [];
src/Api/FormatsProvider.php
Outdated
if (!is_numeric($format)) { | ||
$resourceFormats[$format] = (array) $value; | ||
continue; | ||
} elseif (array_key_exists($value, $this->configuredFormats)) { |
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.
if
instead of elseif
.
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 comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add the @throws
annotation in the PHPDoc?
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.
done
src/Api/FormatsProvider.php
Outdated
|
||
$resourceMetadata = $this->resourceMetadataFactory->create($attributes['resource_class']); | ||
|
||
if (!$formats = (array) $resourceMetadata->getOperationAttribute($attributes, 'formats', [], true)) { |
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.
About (array)
, is there a way to get something different from an array?
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.
Yes I guess by using "formats"="json"
instead of "formats"={"json"}
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.
But as the key is plural, the user could easily understand it expects an array. Otherwise if the value is not an array, maybe throw an exception? WDYT @meyerbaptiste?
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.
I can if you all prefer, it was almost costless
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.
on a second thought, I buy the 'plural' argument, I've removed the typehint, added an exception and test.
src/Api/FormatsProvider.php
Outdated
return $this->configuredFormats; | ||
} | ||
|
||
$resourceMetadata = $this->resourceMetadataFactory->create($attributes['resource_class']); |
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.
What if resource_class
is not set?
d8f6ea2
to
b026b6c
Compare
fa62570
to
e7aca3c
Compare
1a75d55
to
d19daa2
Compare
d19daa2
to
031cbf3
Compare
I'm not sure why circle-ci would try to test the code on PHP5.6 🤔 ... |
try to force push |
199c17d
to
2201faa
Compare
2201faa
to
615746f
Compare
01e3b11
to
60706c9
Compare
I've rebased and updated with the last fixer rules. Anything else to be done on this one ? |
@@ -58,7 +58,7 @@ | |||
<argument>%api_platform.title%</argument> | |||
<argument>%api_platform.description%</argument> | |||
<argument>%api_platform.version%</argument> | |||
<argument>%api_platform.formats%</argument> | |||
<argument type="service" id="api_platform.formats_provider"/> |
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.
missing space before />
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.
updated 👍
Add unit tests Update service injection with deprecation error and exception Fix missing use of FormatsProvider into DocumentAction Only support the shortest mime types declaration Update service injection in documentation to trigger deprecation warning Avoid another way to badly declare format overrding. update phpdoc
60706c9
to
ab48edb
Compare
Thanks! |
* Add documentation on enabling format for specific operation api-platform/core#1798 * Update content-negotiation.md
As suggesting in the issue, this PR will allow to use the following:
todo:
formats
fallback attributes in the @ApiResource annotationping @vincentchalamon, @Toflar