I have to migrate an API developed on APIPlatform v2 to APIPlatform v3. In this project the previous developer created custom patch actions as this (in a YAML file) :
reactivated:
security_post_denormalize: 'is_granted("OT", object)'
security_post_denormalize_message: "Vous n'avez pas l'accès à cette ressource."
method: PATCH
path: /o_ts/{id}/reactivated
controller: App\Controller\OTReactivated
openapi_context:
summary: Work Order reactivated
I transformed it to PHP 8 attribute like this :
#[ApiResource(
operations: [
new GetCollection(normalizationContext: ['groups' => ['ot_collection_read']]),
new Get(requirements: ['id' => '\d+'], normalizationContext: ['groups' => ['ot_read']]),
new Post(
securityPostDenormalize: 'is_granted("OT", object)',
securityPostDenormalizeMessage: "Vous n'avez pas l'accès à cette ressource.",
validationContext: ['groups' => [OTAddSequencedGroup::class]],
),
new Put(
denormalizationContext: ['groups' => ['ot_update_write', 'ot:attachments', 'attachment:write', 'attachment:subcontractingProductionCenter', 'attachment:customFieldValues', 'attachment_custom_field_value:field', 'custom_field_value:write']],
securityPostDenormalize: 'is_granted("OT", object)',
securityPostDenormalizeMessage: "Vous n'avez pas l'accès à cette ressource.",
validationContext: ['groups' => ['Default', OTDefaultSequencedGroup::class, 'attachment:valid:create', 'ot:address:update']]
),
new Patch(
requirements: ['id' => '\d+'],
denormalizationContext: ['groups' => ['ot_update_write', 'ot:attachments', 'attachment:write', 'attachment:subcontractingProductionCenter', 'attachment:customFieldValues', 'attachment_custom_field_value:field', 'custom_field_value:write']],
securityPostDenormalize: 'is_granted("OT", object)',
securityPostDenormalizeMessage: "Vous n'avez pas l'accès à cette ressource.",
validationContext: ['groups' => ['Default', OTDefaultSequencedGroup::class, 'attachment:valid:create', 'ot:address:update']]
),
],
normalizationContext: ['groups' => ['ot_read']],
denormalizationContext: ['groups' => ['ot_write']],
validationContext: ['groups' => OTDefaultSequencedGroup::class],
security: 'is_granted("ROLE_USER")'
)]
#[Patch(
uriTemplate: '/o_ts/{id}/reactivated',
controller: OTReactivated::class,
openapiContext: ['summary' => 'Work Order reactivated'],
securityPostDenormalize: 'is_granted("OT", object)',
securityPostDenormalizeMessage: "Vous n'avez pas l'accès à cette ressource.",
name: 'api_o_ts_reactivated',
)]
So far so good, the route is here, and the call work as expected. Except in the response. The generated iri is not correct. It seems that the ApiPlatform converter thinks the object id is "-id-/reactivated", but its not.
Look yourself.
The call (image) :
The beginning of the response:
{
"@context": "\/contexts\/OT",
"@id": "\/o_ts\/2\/reactivated",
"@type": "OT",
"id": 2
}
The @id should be : "/o_ts/2"
I know, the documentation says that it's not recommanded but the API is already done and in production … So I can't just change the routes …
So, does anybody have a solution to my problem?
I tried to add uriVariables like this :
#[Patch(
uriTemplate: '/o_ts/{id}/reactivated',
uriVariables: ['id' => new Link(fromClass: self::class, identifiers: ['id'])],
controller: OTClose::class,
openapiContext: ['summary' => 'Work Order reactivated'],
securityPostDenormalize: 'is_granted("OT", object)',
securityPostDenormalizeMessage: "Vous n'avez pas l'accès à cette ressource.",
name: 'api_o_ts_reactivated',
)]
I have to migrate an API developed on APIPlatform v2 to APIPlatform v3. In this project the previous developer created custom patch actions as this (in a YAML file) :
reactivated:
security_post_denormalize: 'is_granted("OT", object)'
security_post_denormalize_message: "Vous n'avez pas l'accès à cette ressource."
method: PATCH
path: /o_ts/{id}/reactivated
controller: App\Controller\OTReactivated
openapi_context:
summary: Work Order reactivated
I transformed it to PHP 8 attribute like this :
#[ApiResource(
operations: [
new GetCollection(normalizationContext: ['groups' => ['ot_collection_read']]),
new Get(requirements: ['id' => '\d+'], normalizationContext: ['groups' => ['ot_read']]),
new Post(
securityPostDenormalize: 'is_granted("OT", object)',
securityPostDenormalizeMessage: "Vous n'avez pas l'accès à cette ressource.",
validationContext: ['groups' => [OTAddSequencedGroup::class]],
),
new Put(
denormalizationContext: ['groups' => ['ot_update_write', 'ot:attachments', 'attachment:write', 'attachment:subcontractingProductionCenter', 'attachment:customFieldValues', 'attachment_custom_field_value:field', 'custom_field_value:write']],
securityPostDenormalize: 'is_granted("OT", object)',
securityPostDenormalizeMessage: "Vous n'avez pas l'accès à cette ressource.",
validationContext: ['groups' => ['Default', OTDefaultSequencedGroup::class, 'attachment:valid:create', 'ot:address:update']]
),
new Patch(
requirements: ['id' => '\d+'],
denormalizationContext: ['groups' => ['ot_update_write', 'ot:attachments', 'attachment:write', 'attachment:subcontractingProductionCenter', 'attachment:customFieldValues', 'attachment_custom_field_value:field', 'custom_field_value:write']],
securityPostDenormalize: 'is_granted("OT", object)',
securityPostDenormalizeMessage: "Vous n'avez pas l'accès à cette ressource.",
validationContext: ['groups' => ['Default', OTDefaultSequencedGroup::class, 'attachment:valid:create', 'ot:address:update']]
),
],
normalizationContext: ['groups' => ['ot_read']],
denormalizationContext: ['groups' => ['ot_write']],
validationContext: ['groups' => OTDefaultSequencedGroup::class],
security: 'is_granted("ROLE_USER")'
)]
#[Patch(
uriTemplate: '/o_ts/{id}/reactivated',
controller: OTReactivated::class,
openapiContext: ['summary' => 'Work Order reactivated'],
securityPostDenormalize: 'is_granted("OT", object)',
securityPostDenormalizeMessage: "Vous n'avez pas l'accès à cette ressource.",
name: 'api_o_ts_reactivated',
)]
So far so good, the route is here, and the call work as expected. Except in the response. The generated iri is not correct. It seems that the ApiPlatform converter thinks the object id is "-id-/reactivated", but its not.
Look yourself.
The call (image) :
The beginning of the response:
{
"@context": "\/contexts\/OT",
"@id": "\/o_ts\/2\/reactivated",
"@type": "OT",
"id": 2
}
The @id should be : "/o_ts/2"
I know, the documentation says that it's not recommanded but the API is already done and in production … So I can't just change the routes …
So, does anybody have a solution to my problem?
I tried to add uriVariables like this :
#[Patch(
uriTemplate: '/o_ts/{id}/reactivated',
uriVariables: ['id' => new Link(fromClass: self::class, identifiers: ['id'])],
controller: OTClose::class,
openapiContext: ['summary' => 'Work Order reactivated'],
securityPostDenormalize: 'is_granted("OT", object)',
securityPostDenormalizeMessage: "Vous n'avez pas l'accès à cette ressource.",
name: 'api_o_ts_reactivated',
)]
Share
Improve this question
edited Mar 10 at 7:27
Bruno Desprez
asked Mar 8 at 19:03
Bruno DesprezBruno Desprez
12 bronze badges
1 Answer
Reset to default 0So, I think I found the cause of my problem.
Everything is in the ApiPlatform\JsonLd\Serializer\ItemNormalizer.
Indeed, since v2.7 (or 3.0) the @id parameter is generated thanks to the iriConverter with the context operation passed to the method.
The operation is a Patch operation and the iriConverter take the input iri as @id.
To fix this, I made a custom normalizer which decorates the JsonLd one. Like this :
<?php
namespace App\Normalizer;
use ApiPlatform\Api\IriConverterInterface;
use ApiPlatform\Metadata\Operation;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
class CustomNormalizer implements NormalizerInterface
{
private NormalizerInterface $decorated;
public function __construct(private IriConverterInterface $iriConverter, private NormalizerInterface $decorated)
{
}
public function normalize($object, ?string $format = null, array $context = []): null|array|\ArrayObject|bool|float|int|string
{
$normalizedData = $this->decorated->normalize($object, $format, $context);
if (self::isCustomOperation($normalizedData, $context)) {
$normalizedData = $this->regenerateIdWithIriFromGetOperation($object, $context, $normalizedData);
}
return $normalizedData;
}
public function supportsNormalization($data, ?string $format = null): bool
{
return $this->decorated->supportsNormalization($data, $format);
}
private function regenerateIdWithIriFromGetOperation($object, array $context, array $normalizedData): array
{
// We force the converter to use the GET operation instead of the called operation to generate the iri
$iri = $this->iriConverter->getIriFromResource($object, context: $context);
$normalizedData['@id'] = $iri;
return $normalizedData;
}
private static function isCustomOperation($normalizedData, array $context): bool
{
if (!is_array($normalizedData) || !array_key_exists('@id', $normalizedData)) {
return false;
}
if (!($context['operation'] ?? null) instanceof Operation) {
return false;
}
$extraProps = $context['operation']->getExtraProperties();
return $extraProps['custom_operation'] ?? false;
}
}
Then, I added the custom normalizer in the YAML configuration (config/services.yaml) :
App\Normalizer\CustomNormalizer:
decorates: "api_platform.jsonld.normalizer.item"
And for the given operations, I added an extra property to only activate the custom normalizer on specific operations:
#[Patch(
uriTemplate: '/o_ts/{id}/reactivated',
uriVariables: ['id' => new Link(fromClass: self::class, identifiers: ['id'])],
controller: OTReactivated::class,
openapiContext: ['summary' => 'Work Order reactivated'],
securityPostDenormalize: 'is_granted("OT", object)',
securityPostDenormalizeMessage: "Vous n'avez pas l'accès à cette ressource.",
name: 'api_o_ts_reactivated',
extraProperties: ['custom_operation' => true],
)]
With that, the @id returned is the correct one!
发布者:admin,转转请注明出处:http://www.yc00.com/questions/1744888406a4599255.html
评论列表(0条)