Skip to content

Conversation

@vincentchalamon
Copy link
Contributor

@vincentchalamon vincentchalamon commented Aug 5, 2022

Q A
Branch? 2.7
Tickets #4879
License MIT
Doc PRs api-platform/docs#1590, api-platform/docs#1592
  1. Generating a default NotExposed operation to generate the IRI if the resource has no Get operation available
  2. Disabling operation from OpenApi documentation
  3. Specifying the operation to use to generate the IRI
  4. Handling output on a resource with a GetCollection operation but with no Get operation available
  5. Miscellaneous

1. Generating a default NotExposed operation to generate the IRI if the resource has no Get operation available

Description

The following resource:

#[GetCollection] // path will be: /users
class User

When I call GET /users, I get the following error:

"Operation "" not found for resource "User"."

That's because API Platform cannot find any Get operation to generate the IRI for each collection member. A trick currently used is to manually add a Get operation with read: false, output: false, controller: NotFoundAction::class, which is not ideal.

Solution

Detect that this resource has no Get operation, and automatically add a NotExposed operation. This operation could be used to generate an IRI like /users/{id} is the resource has an identifier, or a Skolem IRI /.well-known/genid/{id} if the resource doesn't have any identifier.

TODO

  • Add NotExposedOperationResourceMetadataCollectionFactory
  • Add NotExposedAction
  • Add Behat tests
  • Add unit tests
  • Add documentation about NotExposed auto-generated operation

2. Disabling operation from OpenApi documentation

Description

The following resource:

#[GetCollection]
#[Get(uriTemplate: '/foo')] // I don't want this operation to be exposed on OpenApi documentation
class User

For any reason, I want an operation to not be exposed on the OpenApi documentation. It also implies the NotExposed auto-generated operation explained on the previous use case.

Even if it is available in the Symfony routing, it should not be available on the OpenApi documentation.

Solution

Add a openapi boolean option on Operation to disable this operation from the OpenApi documentation.

Note: in a later version, it should be better to merge openapiContext into openapi to improve DX and reduce confusion and multiple options.

#[GetCollection]
#[Get(openapi: false, uriTemplate: '/foo')]
class User

TODO

  • Add openapi boolean option on operation classes
  • Ignore operation from OpenApi documentation if openapi is strictly false
  • Update unit tests
  • Add documentation about openapi new operation option

3. Specifying the operation to use to generate the IRI

Description

The following resource:

#[GetCollection] // path will be: /users
#[Get] // path will be: /users/{id}
#[GetCollection(uriTemplate: '/companies/{companyId}/users', ...)]
#[Get(uriTemplate: '/companies/{companyId}/users/{id}', ...)]
class User

When I call GET /companies/{companyId}/users, the members IRI will be the first Get operation found, so /users/{id}. But it should be /companies/{companyId}/users/{id} to be consistent with the operation initially called.

Solution

Add a itemUriTemplate string option on Operation to specify which operation to use to generate the IRI. If this option is not set, then the default behavior is applied (select the first Get operation detected).

This option is only useful for collection operations (GetCollection and Post).

#[GetCollection] // the "itemUriTemplate" option is not used, it will use the first Get operation available
#[Get]
#[GetCollection(itemUriTemplate: '/companies/{companyId}/users/{id}', uriTemplate: '/companies/{companyId}/users', ...)]
#[Get(uriTemplate: '/companies/{companyId}/users/{id}', ...)]
class User

TODO

  • Add itemUriTemplate option on operation, and use it on $this->resourceMetadataCollectionFactory->getOperation() calls if available and from a collection operation context
  • Add Behat tests
  • Update unit tests
  • Add documentation about itemUriTemplate option

4. Handling output on a resource with a GetCollection operation but with no Get operation available

Description

The following resource:

#[GetCollection(output: UserOutput::class)] // path will be: /users
// A NotExposed operation is automatically generated

When I call GET /users on a GetCollection with output, API Platform is trying to find a Get operation available on the output resource to generate the IRI, but it is not a resource. The following error occurs:

"Operation "" not found for resource "User"."

Solution

The best solution would be to support ApiResource and ApiProperty on DTO, but it could be done in another PR. Meanwhile, it's better to return a Skolem IRI.

TODO

  • Generate a Skolem IRI
  • Add Behat tests

5. Miscellaneous

  • Fix Doctrine errors on PHPStan
  • Fix Swagger v2 generation

@vincentchalamon vincentchalamon self-assigned this Aug 5, 2022
@vincentchalamon vincentchalamon linked an issue Aug 5, 2022 that may be closed by this pull request
@vincentchalamon vincentchalamon force-pushed the feat/not-exposed-operation branch from 4cfc2d9 to 67c835a Compare August 5, 2022 14:28
@vincentchalamon vincentchalamon force-pushed the feat/not-exposed-operation branch 4 times, most recently from 1b98423 to 803b778 Compare August 5, 2022 19:19
@vincentchalamon vincentchalamon force-pushed the feat/not-exposed-operation branch 11 times, most recently from 8a7d622 to 1083fe9 Compare August 8, 2022 12:43
@vincentchalamon vincentchalamon force-pushed the feat/not-exposed-operation branch from 1083fe9 to 36d4d89 Compare August 8, 2022 12:47
@vincentchalamon vincentchalamon marked this pull request as ready for review August 8, 2022 13:07
@vincentchalamon vincentchalamon force-pushed the feat/not-exposed-operation branch 2 times, most recently from f754cb8 to 9e864ae Compare August 8, 2022 13:22
@vincentchalamon vincentchalamon force-pushed the feat/not-exposed-operation branch from d5cb364 to d332726 Compare August 10, 2022 12:52
@vincentchalamon vincentchalamon force-pushed the feat/not-exposed-operation branch from d332726 to bdcf7fd Compare August 10, 2022 13:13
Copy link
Member

@soyuka soyuka left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks nice work!

The route name is very Symfony specific, let's use `uriTemplate`
instead.
The skolem id generation was moved to the IriConverter and will be in
another class in a later commit.
@soyuka soyuka force-pushed the feat/not-exposed-operation branch from d568390 to ca771ce Compare August 31, 2022 15:13
soyuka added 2 commits August 31, 2022 17:15
Note that we still put this inside the Symfony class as we use the
router for the url GenerationStrategy feature which is for now tight to
the Symfony router
@soyuka soyuka force-pushed the feat/not-exposed-operation branch from ca771ce to 39ad894 Compare August 31, 2022 15:16

if (!isset($context['iri']) || false !== $context['iri']) {
$jsonLdContext['@id'] = $context['iri'] ?? $this->generateSkolemIri($object);
// Not using an IriConverter here is deprecated in 2.7, avoid spl_object_hash as it may collide
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe add a todo here to help clear this code in 3.0

return $this->operationCache[$operationName] = $operation;
}

if ($operation->getUriTemplate() === $operationName) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if ($operation->getUriTemplate() === $operationName) {
if ($operationName === $operation->getUriTemplate()) {

Also, the other if instances in this loop don't respect the Yoda Style.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Declare GetCollection operation only on resource

4 participants