Mercure doesn't update DELETE method when use SoftDeleatable extensions - symfony

For a project, I use Api Platform and Mercure. I currently have a problem with the SoftDeleatable extension. Without this extension, when I delete an entity, Mercure receives the DELETE. But if I use the SoftDeleatable extension, the deletion is done correctly but Mercure does not receive the DELETE.
Code Entity:
/**
* #ORM\Entity(repositoryClass=MatchesRepository::class)
* #Gedmo\SoftDeleteable(fieldName="deletedAt", timeAware=false, hardDelete=false)
* #ApiResource(
* mercure=true,
* attributes={
* "normalization_context"={"groups"="read"},
* "denormalization_context"={"groups"="write"}
* },
* collectionOperations={
* "get",
* "post"={"security"="is_granted('ROLE_USER')"}
* },
* itemOperations={
* "get",
* "put"={"security"="is_granted('ROLE_ADMIN') or object.owner == user"},
* "delete"={
* "security"="is_granted('ROLE_ADMIN') or object.owner == user"
* },
* }
* )
*/
class Matches
{
Just added mercure=true for enable mercure
PS: I doesn't test if change the DELETE function work or not but SoftDeleatable do it normally :/

Related

How to enable pagination for Sub Resource?

In my API config file, I disabled pagination :
api_platform.yaml :
api_platform:
collection:
pagination:
enabled: false
But I want to enable pagination for a specific subresource :
Parent class :
/**
* #ORM\Entity(repositoryClass="App\Repository\ForumRepository")
* #ApiResource(
* collectionOperations={
* "get",
* },
* itemOperations={"get"},
* )
*/
class Forum
{
...
/**
* #var ForumSubject[]|ArrayCollection
*
* #ORM\OneToMany(
* targetEntity="App\Entity\ForumSubject",
* mappedBy="forum",
* )
* #ApiSubresource(maxDepth=1)
*/
private $subjects;
...
Children class :
/**
* #ORM\Entity(repositoryClass="App\Repository\ForumSubjectRepository")
* #ApiResource(
* collectionOperations={
* "post",
* },
* itemOperations={
* "get",
* "put",
* "delete"
* },
* )
*/
class ForumSubject
{
...
Everything works fine I can access my sub-resources :
/api/forums/a21cb5db-aed7-45a6-884f-3d3e6d7abd8c/subjects
(Return all subjects of forum, route name : api_forums_subjects_get_subresource ).
But I am unable to enable paging on this route, the documentation does not mention anything.
After research I tried this, but nothing works :
Does not work in parent or children class :
* #ApiResource(
* ...
* subresourceOperations={
* "api_forums_subjects_get_subresource"={"pagination_enabled"=true}
* }
Does not work in parent class ; error un children class (The operation "api_forums_subjects_get_subresource" cannot be found in the Swagger specification).
* #ApiResource(
* ...
* collectionOperations={
* "get",
* "api_forums_subjects_get_subresource"={"pagination_enabled"=true}
* },
After looking in the API Platform source code, here is the solution :
In children class :
* #ApiResource(
* ...
* collectionOperations={
* ...
* "api_forums_subjects_get_subresource"={
* "method"="get", // don't forget : otherwise trigger a strange swagger error
* "pagination_enabled"=true
* }

ApiPlatform - implement security authorization on subresource route

I'm using Symfony5 and ApiPlatform
I have a User entity and a Product entity.
I want to list all my user's products through a subressource, to do so I've implemented my user class as follow :
/**
* #ApiResource(
* attributes={
* "normalization_context"={"groups"={"user:read", "user:list"}},
* "denormalization_context"={"groups"={"user:put", "user:post"}}
* },
* subresourceOperations={
* "api_users_consultations_get_subresource"={
* "method"="GET",
* "security"="is_granted('ROLE_ADMIN')"
* }
* },
* collectionOperations={
* "get"={
* "method"="GET",
* "security"="is_granted('ROLE_ADMIN')",
* "normalization_context"={"groups"={"user:list"}}
* },
* "post"={
* "method"="POST",
* "security_post_denormalize"="is_granted('POST', object)",
* "denormalization_context"={"groups"={"user:post"}}
* }
* },
* itemOperations={
* "get"={
* "method"="GET",
* "security"="is_granted('GET', object)",
* "normalization_context"={"groups"={"user:read"}}
* }
* }
* )
* #Gedmo\SoftDeleteable(fieldName="deletedAt", timeAware=false, hardDelete=false)
* #ORM\Entity(repositoryClass=UserRepository::class)
*/
class User
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
* #Groups({"user:read", "user:list"})
*
*/
private $id;
/**
* #ORM\OneToMany(targetEntity=Product::class, mappedBy="user")
* #ApiSubresource()
*/
private $product;
}
It does create a route /users/{id}/products and return me what I want.
The part where I'm blocking is when I want to add authorization to this route:
ROLE_ADMIN can access this route
ROLE_USER who own the ressource can access it
All the other roles will receive FORBIDDEN
To do so I've followed the doc : https://api-platform.com/docs/core/subresources/#using-serialization-groups
Added subresourceOperations to #ApiSubresource annotation
recovered the name of my grenerated route api_users_consultations_get_subresource through the bin/console debug:router command
and simply set a security=is_granted('ROLE_ADMIN') method like for other operations.
or security=is_granted('SUB_LIST', object) to hit the voter
but when I run my tests I get 200 where I should receive 403, the UserVoter or ProductVoter are not triggered nor the is_granted('ROLE_ADMIN') rule.
As if the subresourceOperations annotation wasn't recognize by the ApiPlatform.
I've also tried changing the name of the operation from api_users_consultations_get_subresource to :
consultations_get_subresource
api_consultations_get_subresource
clients_get_subresource
api_clients_get_subresource
and different other variants as I saw on Github it solved the issue in some cases (like here https://github.com/api-platform/api-platform/issues/1581#issuecomment-662503549) but it has not worked for me.
So I'm wondering is there something I havn't done to implement it correctly ?
Is it a known issue of ApiPlatform ?
Does anyone see where my logic is failing ?
Is there another way to setup security on subresource routes ?
Are there more docs on security realted to subresource? I have not find a lot of material on this particular subject

Subresources routes in symfony 5 with API-platform

I am trying to declare a subresource in my symfony app. I followed the api-platform doc about it: https://api-platform.com/docs/core/subresources/
The subresource does appear in the routes, but not under its parent resource.
The routes I currently have:
api_files_get_collection GET ANY ANY /api/files.{_format}
api_files_post_collection POST ANY ANY /api/files.{_format}
api_files_get_item GET ANY ANY /api/files/{id}.{_format}
api_files_patch_item PATCH ANY ANY /api/files/{id}.{_format}
api_files_put_item PUT ANY ANY /api/files/{id}.{_format}
api_external_subscription_requests_get_collection GET ANY ANY /api/external_subscription_requests.{_format}
api_external_subscription_requests_post_publication_collection POST ANY ANY /api/subscribe
api_external_subscription_requests_get_item GET ANY ANY /api/external_subscription_requests/{id}.{_format}
The routes I would like to have:
api_files_get_collection GET ANY ANY /api/files.{_format}
api_files_post_collection POST ANY ANY /api/files.{_format}
api_files_get_item GET ANY ANY /api/files/{id}.{_format}
api_files_patch_item PATCH ANY ANY /api/files/{id}.{_format}
api_files_put_item PUT ANY ANY /api/files/{id}.{_format}
api_files_external_subscription_requests_get_collection GET ANY ANY /api/files/{id}/external_subscription_requests.{_format}
api_files_external_subscription_requests_post_publication_collection POST ANY ANY /api/files/{id}/subscribe
api_files_external_subscription_requests_get_item GET ANY ANY /api/files/{id}/external_subscription_requests/{id}.{_format}
Code in App\Entity\File.php:
/**
* #ORM\Entity
* #ApiResource(
* attributes={"order"={"createdAt": "DESC"}},
* collectionOperations={
* "get"={
* "normalization_context"={"groups"={"model:timestampable", "file:collection:read"}},
* },
* "post",
* },
* itemOperations={
* "get"={
* "normalization_context"={"groups"={"model:timestampable", "file:item:read"}},
* },
* "patch",
* "put"
* },
* )
*/
class File
{
// ...
/**
* #ORM\OneToMany(targetEntity="App\Entity\ExternalSubscriptionRequest", cascade={"all"}, mappedBy="file")
* #Groups({
* "file:collection:read",
* "file:item:read"
* })
* #ApiSubresource()
*/
private $external_subscription_requests;
// ...
}
Code in App\Entity\ExternalSubscriptionRequest.php:
/**
* #ORM\Entity
* #ApiResource(
* collectionOperations={
* "get",
* "post_publication"={
* "method"="POST",
* "path"="/subscribe",
* "controller"=SubscribeToConso::class,
* }
* },
* itemOperations={
* "get",
* },
* )
*/
class ExternalSubscriptionRequest
{
// ...
/**
* #var File the file this request was made for
*
* #ORM\ManyToOne(targetEntity="App\Entity\File", inversedBy="external_subscription_requests")
* #ORM\JoinColumn(referencedColumnName="id", nullable=false)
*
* #Groups({
* "external_subscription_request:collection:read",
* "external_subscription_request:item:read"
* })
*/
public $file;
// ...
}

Symfony API Platform : file upload on update

I'm stuck with something with API Platform and Vich Uploader for a PUT request, the POST is working juste fine.
Here is my header for MediaObject entity :
/**
* #ORM\Entity
* #ApiResource(
* iri="http://schema.org/MediaObject",
* normalizationContext={
* "groups"={"media_object_read"}
* },
* collectionOperations={
* "post"={
* "controller"=CreateMediaObjectAction::class,
* "deserialize"=false,
* "security"="is_granted('ROLE_USER')",
* "validation_groups"={"Default", "media_object_create"},
* "openapi_context"={
* "requestBody"={
* "content"={
* "multipart/form-data"={
* "schema"={
* "type"="object",
* "properties"={
* "file"={
* "type"="string",
* "format"="binary"
* }
* }
* }
* }
* }
* }
* }
* },
* "get",
* },
* itemOperations={
* "get",
* "put"={"controller"=UpdateMediaObjectAction::class,
* "deserialize"=false,
* "security"="is_granted('ROLE_USER')",
* "validation_groups"={"Default", "media_object_update"},
* "openapi_context"={
* "requestBody"={
* "content"={
* "multipart/form-data"={
* "schema"={
* "type"="object",
* "properties"={
* "file"={
* "type"="string",
* "format"="binary"
* }
* }
* }
* }
* }
* }
* }},
* "delete"={
* "security"="is_granted('ROLE_USER')"
* }
* }
* )
* #Vich\Uploadable
*/
class MediaObject
{...}
In swagger it throws an error because the file is not attached to the request. It strange because I have exactly the same file input field, with just an ID parameter added.
Someone has manage to do that ?
Found solution by myself !
Finally, the problem is that PHP does'nt handle well file in FormData on PUT, but well on POST.
So if you got the same issue change to "method"="POST" in the "put" part from itemOperations.

How can I change api-platform {id} to a different name?

I am using api-platform with Symfony 4. It works fine, but I would like to change a GET url from:
/booking/{id} to /booking/{bookingId}
I'm using my own DTO object and custom data provider (not Doctrine ORM).
Here are the current #ApiResource and #ApiProperty definitions that work fine:
/**
*
* #ApiResource(
* itemOperations={
* "get"={
* "path"="/booking/{id}",
* },
* "api_bookings_get_item"={
* "swagger_context"={
* "operationId"="getBookingItem",
* "summary"="Retrieves details on a booking",
* "parameters"= {
* {
* "name"="id",
* "description"="Booking ID",
* "default"="15000",
* "in"="path",
* "required"=true,
* "type"="string"
* }
* },
* "responses"={
* "200"={
* "description"="Results retrieved"
* },
* "404"={
* "description"="Booking not found"
* }
* }
* }
* }
* },
* collectionOperations={}
* )
*/
final class Booking
{
/**
* #var string
* #Assert\NotBlank
*
* #ApiProperty(
* identifier=true,
* attributes={
* "swagger_context"={
* "description"="Booking ID",
* "required"=true,
* "type"="string",
* "example"="123456"
* }
* }
* }
*/
public $id;
// other variables
}
However, if I change all the references from 'id' to 'bookingId' it stops working and I get a 404 error. Here are the changes I made to the above code:
"path"="/booking/{bookingId}"
"name"="bookingId"
public $bookingId;
Is api-platform hard-coded to use 'id' as an identifier? Is there any way to change this?
In Api-platform the id parameter is hardcoded:
namespace ApiPlatform\Core\DataProvider;
private function extractIdentifiers(array $parameters, array $attributes)
{
if (isset($attributes['item_operation_name'])) {
if (!isset($parameters['id'])) {
throw new InvalidIdentifierException('Parameter "id" not found');
}
but you can create your own operation and use the parameter name that you want there is a great example in docs custom operations
You can customize the apiResource identifier via:
/**
* #ApiProperty(identifier=false)
*/
private $id;
and:
/**
* #ApiProperty(identifier=true)
*/
private $uuid;
where uuid is the new identifier to be used in request URLs.
For reference: symfonycasts did an excellent tutorial here:
https://symfonycasts.com/screencast/api-platform-extending/uuid-identifier

Resources