Resource IRI and `@id` using the wrong operation
API Platform version(s) affected: 2.7 up to 3.1
Description
Hello,
I am not fully sure if that's a bug, a breaking change we have to deal with, or something we're doing wrong, so I'm sorry if this was already answered.
I am hitting a situation where the IRI returned for a PATCH route in the @id jsonld context field is not referencing the correct IRI, but the operation route. It was working fine before turning BC flag OFF, and I am able to consistantly reproduce on v3.1 as well, which is why I am not fully sure of the expected behaviour at the end.
As far as my debug went, it happens when the resource has several item operations, using uriTemplate, and the new IriConverter is used. The old IriConverter seemed to always use the first item operation, while the new is using the current operation.
How to reproduce
Here is a small and fresh symfony app reproducing the issue: https://github.com/n-valverde/apip-bad-jsonld-id
There is one simple test: https://github.com/n-valverde/apip-bad-jsonld-id/blob/master/tests/FooTest.php
This test is green with BC flag ON, and turns red showing the @id issue when BC flag is OFF.
The resource is using two GET operations in this case for the sake of simplicity, but I initially hit the issue on a PATCH route, and can reproduce with a PATCH on the reproducer as well, so that seem really related to item operations.
Possible Solution
I don't know if that's an expected behaviour, and if it's not, I'm not sure where the issue is. On the patch route, the IRI is initially correct in the $request attribute _api_write_item_iri set from the WriteListener, but when coming back from a controller which is returning a resource, or from a processor doing the same, the resource is then serialized from another listener, and the current operation is passed to the IriConverter, which is ultimately causing the issue.
Additional context My real resource definition looks like this (simplified with relevant things only)
new Get(),
new Patch(uriTemplate: '/colorways/{id}/transition', processor: SomeProcessor::class),
With such a definition, @id was previously /colorways/{id} on the PATCH route, and becomes /colorways/{id}/transition (with {id} placeholder resolved of course)
Thanks!
I think that itemUriTemplate helps to solve that kind of use cases especially https://github.com/api-platform/core/pull/5663. In fact we improved the whole system on 3.1, in 2.7 it was always the first "GET" unless it was a collection or a post. Now we use the current operation, and itemUriTemplate if defined.
Hello @soyuka , thanks for the hint, unfortunately I don't think this can help, or maybe I'm missing something. AFAIK itemUriTemplate only applies to GetCollection and Post operations, so only collection operations, while my issue happens on item operations.
As a workaround, I'm using a custom normalizer replacing @id with _api_write_item_iri when it's set in request attributes, it fixes my test but I'm not really sure if that's not going to break something else 🙃
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
@n-valverde @soyuka I have the same issue for a custom controller (using ADR pattern). I want to have a GET operation which returns a specific entity resource, and the @id attribute is set to the specific URI set for that custom GET endpoint. I tried to work around this by creating an ApiResource with a uriTemplate setting for that custom endpoint URI and specify on the GET operation the uriTemplate for the item entity resource which works, but then I can't seem to add the open api documentation for the ApiResource of the custom endpoint. It gets overwritten by the entity's item endpoint (as it should because of the operation uriTemplate set for it) sighs
Hi @mega , the issue does exist indeed, on our end we worked around it by creating a special IriOverrideNormalizer, at the end the definition looks like that
new Get(name: "foo"),
new Get(name: "bar", uriTemplate: '/foo/{id}/bar', extraProperties: ["iri_override" => "foo"]), // This targets the operation name, and our custom normalizer then replace the iri with the one for that target operation
We can't change uriTemplate because that would also change the route, and can't use itemUriTemplate because that's dedicated to GetCollection and Post, while this issue can exist with any operation.
I'm not sure about what you say with api doc, you should be able to override the doc yourself no matter what (but it should not be required with our workaround), so it's hard to tell what's wrong without showing some code.
Have the same issue since Upgrade to 3.1.
Previously i had an uriTemplate: '/seminar_modules/slug/{slug}', wich worked great together with itemUriTemplate: '/seminar_modules/{id}', in the GET route. Since Upgrade it worked, but a SubResource-Collection also got the /seminar_modules/{id} assigned. The "single" SubResources were not affected.
Is there any solution, beside of your custom one?
On Debugging the OperationContextTrait, in Line 35 it removes the item_uri_template from parent operation, but in Line 42 it adds it again. $operation is still the parent.
Is that a bug? @soyuka
If so, i could create a separate ticket and try to add an reproducable test
Also here is an unset of iri, uri_variables and item_uri_template. That is missing after the collection childContext get
If i add it there, it seems fixed. But currently IDK if it affects other parts