Description:
Subresource creation using URI templates is not functioning as expected. When attempting to create only the subresource via the parent resource’s identifier in the URI, several issues occur, such as errors when the parent resource doesn’t exist, unexpected overriding of existing subresources, and failures when multiple subresources are involved. This behavior persists across different scenarios and requires attention to properly handle subresource creation through URI templates.
Steps to Reproduce:
Configure two resources that are related.
Set up a subresource POST operation using the following URI template: resources/{resourceId}/subresource.
Attempt to create only the subresource using the ID of an existing parent resource: resources/{existingId}/subresource.
Our resource configuration example with some outcomes:
We tested several scenarios (for clarity, required payload data is omitted in the examples below):
URI:/promotions/non-existent-code/coupons Operation:POST Initial state: None Expected: Parent resource not found Actual Result:
{
"@context": "/api/v2/contexts/Error",
"@type": "hydra:Error",
"hydra:title": "An error occurred",
"hydra:description": "PropertyAccessor requires a graph of objects or arrays to operate on, but it found type 'NULL' while trying to traverse path 'promotion.code' at property 'code'."
}
URI:/promotions/autumn/coupons Operation:POST Initial state: Promotion exists, no coupons Expected: Subresource created Actual Result:
{
"@context": "/api/v2/contexts/Error",
"@type": "hydra:Error",
"hydra:title": "An error occurred",
"hydra:description": "PropertyAccessor requires a graph of objects or arrays to operate on, but it found type 'NULL' while trying to traverse path 'promotion.code' at property 'code'."
}
URI:/promotions/autumn/coupons Operation:POST Initial state: Promotion exists, one coupon exists Expected: Second coupon created Actual Result:201, but the existing subresource was overridden instead of creating a new one.
URI:/promotions/autumn/coupons Operation:POST Initial state: Promotion exists, two coupons exist Expected: Third coupon created Actual Result:
{
"@context": "/api/v2/contexts/Error",
"@type": "hydra:Error",
"hydra:title": "An error occurred",
"hydra:description": "More than one result was found for query although one row or none was expected."
}
Workarounds:
We observed interesting behavior when allowing the payload to include relation information, although these scenarios are not valid. They might provide insight into the core issue.
URI:/promotions/autumn/coupons Operation:POST Initial state: Promotion exists, no coupons Payload:
{
"@context": "/api/v2/contexts/Error",
"@type": "hydra:Error",
"hydra:title": "An error occurred",
"hydra:description": "More than one result was found for query although one row or none was expected."
}
URI:/promotions/different-promotion-code-or-made-up-one/coupons Operation:POST Initial state: Promotion exists (autumn promotion), any number of coupons Payload:
Expected: Each request creates a new coupon resource Actual Result: Expected behavior.
Conclusion:
Scenario seven works, but it’s tricky since the operation is based on the payload rather than URL parameters. The issue seems related to how API Platform resolves links, particularly when the base resource is a subresource (in this example, <resource class="Sylius\Component\Core\Model\PromotionCoupon">).
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.
API Platform Version(s) Affected: 3.3.12
Description: Subresource creation using URI templates is not functioning as expected. When attempting to create only the subresource via the parent resource’s identifier in the URI, several issues occur, such as errors when the parent resource doesn’t exist, unexpected overriding of existing subresources, and failures when multiple subresources are involved. This behavior persists across different scenarios and requires attention to properly handle subresource creation through URI templates.
Steps to Reproduce:
POST
operation using the following URI template:resources/{resourceId}/subresource
.resources/{existingId}/subresource
.Our resource configuration example with some outcomes:
We tested several scenarios (for clarity, required payload data is omitted in the examples below):
URI:
/promotions/non-existent-code/coupons
Operation:
POST
Initial state: None
Expected: Parent resource not found
Actual Result:
URI:
/promotions/autumn/coupons
Operation:
POST
Initial state: Promotion exists, no coupons
Expected: Subresource created
Actual Result:
URI:
/promotions/autumn/coupons
Operation:
POST
Initial state: Promotion exists, one coupon exists
Expected: Second coupon created
Actual Result:
201
, but the existing subresource was overridden instead of creating a new one.URI:
/promotions/autumn/coupons
Operation:
POST
Initial state: Promotion exists, two coupons exist
Expected: Third coupon created
Actual Result:
Workarounds: We observed interesting behavior when allowing the payload to include relation information, although these scenarios are not valid. They might provide insight into the core issue.
URI:
/promotions/autumn/coupons
Operation:
POST
Initial state: Promotion exists, no coupons
Payload:
Expected: Coupon created
Actual Result:
URI:
/promotions/autumn/coupons
Operation:
POST
Initial state: Promotion exists, two coupons
Payload:
Expected: Third coupon created
Actual Result:
URI:
/promotions/different-promotion-code-or-made-up-one/coupons
Operation:
POST
Initial state: Promotion exists (autumn promotion), any number of coupons
Payload:
Expected: Each request creates a new coupon resource
Actual Result: Expected behavior.
Conclusion: Scenario seven works, but it’s tricky since the operation is based on the payload rather than URL parameters. The issue seems related to how API Platform resolves links, particularly when the base resource is a subresource (in this example,
<resource class="Sylius\Component\Core\Model\PromotionCoupon">
).