Skip to content
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

Subresource Creation Fails When Attempting to Create Only the Subresource #6622

Open
Rafikooo opened this issue Sep 18, 2024 · 0 comments
Open

Comments

@Rafikooo
Copy link

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:

  1. Configure two resources that are related.
  2. Set up a subresource POST operation using the following URI template: resources/{resourceId}/subresource.
  3. Attempt to create only the subresource using the ID of an existing parent resource: resources/{existingId}/subresource.

Our resource configuration example with some outcomes:

<resource class="Sylius\Component\Core\Model\PromotionCoupon">
    <operations>
        <operation class="ApiPlatform\Metadata\Post"
                   uriTemplate="/admin/promotions/{promotionCode}/coupons">
            <uriVariables>
                <uriVariable parameterName="promotionCode" fromClass="Sylius\Component\Core\Model\Promotion"
                             fromProperty="coupons"/>
            </uriVariables>
        </operation>
    </operations>
</resource>

We tested several scenarios (for clarity, required payload data is omitted in the examples below):

  1. 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'."
    }
  2. 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'."
    }
  3. 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.

  4. 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.

  1. URI: /promotions/autumn/coupons
    Operation: POST
    Initial state: Promotion exists, no coupons
    Payload:

    {
        ...
        "promotion": "api/v2/admin/promotions/autumn"
    }

    Expected: Coupon created
    Actual Result:

    • First request: coupon created.
    • Second identical request: coupon overridden instead of creating a new one.
  2. URI: /promotions/autumn/coupons
    Operation: POST
    Initial state: Promotion exists, two coupons
    Payload:

    {
        ...
        "promotion": "api/v2/admin/promotions/autumn"
    }

    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."
    }
  3. URI: /promotions/different-promotion-code-or-made-up-one/coupons
    Operation: POST
    Initial state: Promotion exists (autumn promotion), any number of coupons
    Payload:

    {
        ...
        "promotion": "api/v2/admin/promotions/autumn"
    }

    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">).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant