api-platform / core

The server component of API Platform: hypermedia and GraphQL APIs in minutes
https://api-platform.com
MIT License
2.38k stars 846 forks source link

Mixing REST and GraphQL operation names #6421

Open danaki opened 2 weeks ago

danaki commented 2 weeks ago

API Platform version: 3,3

My resource description looks like so:

#[ApiResource(
    security: "is_granted('ROLE_USER')",
    order: ['id' => 'DESC'],
    normalizationContext: ['groups' => ["batch:query"]],
    denormalizationContext: ['groups' => ["batch:mutation"]],
    operations: [
        new Get(),
        new GetCollection(),
        new Post(
            name: 'create',
            controller: CreateBatchController::class,
        ),
        new Post(
            name: 'start',
            controller: BatchTransitionStartController::class,
            uriTemplate: '/batches/{id}/start',
            denormalizationContext: ['groups' => ["batch:transition"]]
        ),
        new Post(
            name: 'finish',
            controller: BatchTransitionFinishController::class,
            uriTemplate: '/batches/{id}/finish',
            denormalizationContext: ['groups' => ["batch:transition"]]
        ),
        new Post(
            name: 'pause',
            controller: BatchTransitionPauseController::class,
            uriTemplate: '/batches/{id}/pause',
            denormalizationContext: ['groups' => ["batch:transition"]]
        )
    ],
    graphQlOperations: [
        new Query(),
        new QueryCollection(),
        new Mutation(
            name: 'create',
            resolver: CreateBatchMutationResolver::class
        ),
        new Mutation(
            name: 'start',
            resolver: BatchTransitionMutationResolver::class,
            denormalizationContext: ['groups' => ["batch:transition"]]
        ),
        new Mutation(
            name: 'finish',
            resolver: BatchTransitionMutationResolver::class,
            denormalizationContext: ['groups' => ["batch:transition"]]
        ),
        new Mutation(
            name: 'pause',
            resolver: BatchTransitionMutationResolver::class,
            denormalizationContext: ['groups' => ["batch:transition"]]
        ),
    ]
)]

My goal is to reproduce the same API for REST and GraphQL endpoints. Having this getting strange server error in

vendor\/api-platform\/core\/src\/GraphQl\/Type\/TypeConverter.php","line":185

after some digging I see exception firing:

        if (!$operation instanceof Operation) {
            throw new OperationNotFoundException();
        }

following

$operation = $resourceMetadataCollection->getOperation($operationName);
``

FIX. In my case, for example:
    new Post(
        name: 'start',
        controller: BatchTransitionStartController::class,
        uriTemplate: '/batches/{id}/start',
    ),
and
    new Mutation(
        name: 'start',
        resolver: BatchTransitionMutationResolver::class
    ),


both have "start" as a name, while I can't change the name for Mutation since it will be reflected in autogenerated GraphQL operation naming thus breaking API for existing users, It's safe to name the Post to let's say "start_rest" providing uriTemplate and error is gone. It's either a bug or documentation issue leading to cryptic error messages.
soyuka commented 2 weeks ago

using controllers is not recommended, also why do you want to change rest operation names?

danaki commented 2 weeks ago

Is there other way to achieve custom POST targets not using controllers? I found these in documentation, may be I miss something?

Because if Post(name: "abc") and Mutation(name: "abc") collide because of the name in the same ApiResource, it produces error in TypeConverter.php, line 185. An exception with no meaningful details, leaving guessing what happened. This is definitely must be reflected in docs or fixed somehow.

I'm changing REST operation names because for GraphQL changing name means changing exported API like if model="Animal" muation's name="pet", the call becomes available as "petAnimal" while REST seems just ignoring this name exposing what is written in uriTemplate only. And because my app is based on GraphQL primarily and I'm just enabling REST, if I change graphql's namings I will break existing API and clients will break too.

soyuka commented 2 weeks ago

I don't understand if you use controllers just use symfony to declare your route you don't need resources especially for RPC/non-REST operations.