Skip to content

Named operations stopped working after upgrade to 3.0.5 #5235

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

Closed
k0d3r1s opened this issue Nov 27, 2022 · 8 comments
Closed

Named operations stopped working after upgrade to 3.0.5 #5235

k0d3r1s opened this issue Nov 27, 2022 · 8 comments

Comments

@k0d3r1s
Copy link

k0d3r1s commented Nov 27, 2022

API Platform version(s) affected: 3.0.5

Description
Named operations not working after upgrade to 3.0.5

How to reproduce

#[
    ApiResource(
        shortName: 'User',
        operations: [
            new Post(
                uriTemplate: '/password/reset',
                openapiContext: [
                    'summary' => 'Create and send password reset token.',
                ],
                denormalizationContext: ['groups' => [PasswordResource::RESET], ],
                name: 'password_reset',
            ),
            new Post(
                uriTemplate: '/password/set',
                openapiContext: [
                    'summary' => 'Set new password after token has been validated and stored in session.',
                ],
                denormalizationContext: ['groups' => [PasswordResource::SET], ],
                name: 'password_set',
            ),
            new Get(
                uriTemplate: '/password/reset/{token}',
                openapiContext: [
                    'summary' => 'Validate password reset token and store in session.',
                ],
                name: 'password_reset_token',
            ),
        ],
        routePrefix: '/users',
        stateless: false,
        normalizationContext: ['groups' => [PasswordResource::RESET_TOKEN]],
        denormalizationContext: ['groups' => [PasswordResource::RESET, PasswordResource::SET]],
        paginationEnabled: false,
        provider: PasswordDataProvider::class,
        processor: PasswordDataProcessor::class,
    )
]
class PasswordResource

As far as I can debug (I'm new to api platform, so I can't really tell what is wrong here), it has something to do with ApiPlatform\Metadata\Resource\Factory\OperationDefaultsTrait or something before it is used. It fails in private function getOperationWithDefaults(ApiResource $resource, Operation $operation, bool $generated = false): array function on if (!$operations->has($operation->getName())) { line.
Before 3.0.5 incoming ApiResource $resource had defined operations array with keys 0, 1, 2, ... etc, but as of 3.0.5 incoming resource has operations array with already defined names like password_set, password_reset etc, which fails this name check function as there can't be two routes with same name.
If I don't define name property, it generates routes as expected but then I can't tell operations apart in processor nor generate path to named route.

Additional Context
Worked before 3.0.5

@Aerendir
Copy link
Contributor

I did not still debugged what happened, but some of my routes stop to work with this new path release. Only some of them: PHPUnit tests fail.

There is something broken, but I don't know what at the moment.

This comment is just to confirm the issue.

@Aerendir
Copy link
Contributor

May be linked to #5234

@soyuka
Copy link
Member

soyuka commented Nov 28, 2022

#5217

it doesn't look like you have a route called the same right ? bring me an error + stack trace please.

Note that you can also use debug:api-resource to actually check what the Metadata computed inside your operations. I'm wondering if token is correct as there's no URI Variables that tells API Platform where to look at. Can I see the code of your resource?

@k0d3r1s
Copy link
Author

k0d3r1s commented Nov 28, 2022

<?php declare(strict_types = 1);

namespace App\ApiResource;

use ApiPlatform\Metadata\ApiProperty;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\Post;
use App\DataProcessor\PasswordDataProcessor;
use App\DataProvider\PasswordDataProvider;
use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\Validator\Constraints as Assert;

#[
    ApiResource(
        shortName: 'User',
        operations: [
            new Post(
                uriTemplate: '/password/reset',
                openapiContext: [
                    'summary' => 'Create and send password reset token.',
                ],
                denormalizationContext: ['groups' => [PasswordResource::RESET], ],
                name: 'password_reset',
            ),
            new Post(
                uriTemplate: '/password/set',
                openapiContext: [
                    'summary' => 'Set new password after token has been validated and stored in session.',
                ],
                denormalizationContext: ['groups' => [PasswordResource::SET], ],
                name: 'password_set',
            ),
            new Get(
                uriTemplate: '/password/reset/{token}',
                openapiContext: [
                    'summary' => 'Validate password reset token and store in session.',
                ],
                name: 'password_reset_token',
            ),
        ],
        routePrefix: '/users',
        stateless: false,
        normalizationContext: ['groups' => [PasswordResource::RESET_TOKEN]],
        denormalizationContext: ['groups' => [PasswordResource::RESET, PasswordResource::SET]],
        paginationEnabled: false,
        provider: PasswordDataProvider::class,
        processor: PasswordDataProcessor::class,
    )
]
class PasswordResource
{
    public const PREFIX = 'user_password_';

    public const SET = self::PREFIX . 'set';
    public const RESET = self::PREFIX . 'reset';
    public const RESET_TOKEN = self::PREFIX . 'reset_token';

    #[ApiProperty(identifier: true)]
    #[Groups([self::RESET_TOKEN])]
    public ?string $token = null;

    #[Groups([self::RESET_TOKEN, self::RESET])]
    public ?string $email = null;

    #[Groups([self::SET])]
    #[Assert\Length(min: 6, max: 30)]
    public string $newPassword;

    #[Groups([self::SET])]
    #[Assert\IdenticalTo(propertyPath: 'newPassword', message: 'Atkārtoti ievadītā parole nesakrīt ar sākotnējo.')]
    public string $newPasswordRepeat;
}

There is no actual error per say but in 3.0.5. it only generates last defined operation with some auto generated name and not the one i gave it. you can see what warnings come up in api-resource dump file for 3.0.5. i attached. i attached dump for 3.0.4. as well to see the difference.
You can see in 3.0.4. dump also that it correctly generates all defined operations and not only last defined as in 3.0.5.
There are no other changes in project other than core package value change between 3.0.4 and 3.0.5

debug-api-resource-305.txt
debug-api-resource-304.txt

@k0d3r1s
Copy link
Author

k0d3r1s commented Nov 28, 2022

I don't think issue here is token route cause if i comment out get operation and leave remaining 2 post operations, behaviour does not change -> only last operation is defined and with wrong name

@soyuka
Copy link
Member

soyuka commented Nov 28, 2022

oh yeah this is no bueno looking at it

soyuka added a commit to soyuka/core that referenced this issue Nov 28, 2022
soyuka added a commit to soyuka/core that referenced this issue Nov 28, 2022
@soyuka
Copy link
Member

soyuka commented Nov 28, 2022

let's close this as dup of #5234

@Babyd12
Copy link

Babyd12 commented Jan 27, 2024

if you used the API platform: 3.0.5
the value expected by uriTemplate for the parameter is {id}
if you use {userId} it will no longer work.

This is what I deduced after two days of debugging.

In your case: you have the choice, either you do
If you have an id in the middle of your uir which is your case, you need an id at the end of this same uri.
Example:
❌uriTemplate: '/warehouses/{warehouseId}/inventory',
✔️uriTemplate: '/warehouses/warehouseId/inventory/{id}',

In the case where you have a uri at the end only, it will have the form {id} and not {userId}
Example:

❌uriTemplate: '/warehouses/inventory/{warehouseId}',
✔️uriTemplate: '/warehouses/inventory/{id}',

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

4 participants