Closed vincentchalamon closed 2 years ago
PHP 8.0 Symfony 5.4 API Platform 2.7.0-alpha.5 (metadata_backward_compatibility_layer: false)
Setting provider on operation is not working. Error "Provider not found on operation \"api/reset-password-request/{resetPasswordToken}_patch\""
<operation class="ApiPlatform\Metadata\Patch"
messenger="input"
input="App\Command\ResetPassword"
output="false"
status="202"
uriTemplate="/reset-password-request/{resetPasswordToken}"
provider="App\State\ResetPasswordProvider"
>
Probably related to? https://github.com/api-platform/core/issues/4613#issuecomment-1112233470
Is your provider registered as a Symfony service ? It's a basic service locator you can find that out using debug:container
@soyuka, it seems the latest version ignores the route_prefix
prefix settings
config/packages/api_platform.yaml
api_platform:
...
metadata_backward_compatibility_layer: false
openapi:
backward_compatibility_layer: false
defaults:
route_prefix: '/api'
GET /users/{id} Retrieves a User resource.
Instead of
GET /api/users/{id} Retrieves a User resource.
Tested with d876e6638acad4b82887a4f93fd4eca361584d00
Also: https://github.com/api-platform/core/pull/4755 (fix is in #4754)
@soyuka I have done some debugging for https://github.com/api-platform/core/issues/4613#issuecomment-1112233470 and it looks like the value is overwritten in https://github.com/api-platform/core/blob/main/src/Metadata/Property/Factory/LegacyPropertyMetadataFactory.php#L82
I'm trying to upgrade to 2.7 alpha 5 with the bc layer disabled. I used the CLI to convert the operations.
On calling the /docs endpoint, I get the exception below:
ErrorException:
Warning: Undefined array key "$ref"
at vendor/api-platform/core/src/JsonSchema/TypeFactory.php:153
at ApiPlatform\JsonSchema\TypeFactory->getClassType('UserInterface\\Bank\\Rest\\BankAccountProjection', 'jsonld', true, array('groups' => array('bank_statement_entry_detail_overview_normalize')), object(Schema))
(vendor/api-platform/core/src/JsonSchema/TypeFactory.php:84)
at ApiPlatform\JsonSchema\TypeFactory->makeBasicType(object(Type), 'jsonld', true, array('groups' => array('bank_statement_entry_detail_overview_normalize')), object(Schema))
(vendor/api-platform/core/src/JsonSchema/TypeFactory.php:71)
at ApiPlatform\JsonSchema\TypeFactory->getType(object(Type), 'jsonld', true, array('groups' => array('bank_statement_entry_detail_overview_normalize')), object(Schema))
(vendor/api-platform/core/src/JsonSchema/SchemaFactory.php:269)
at ApiPlatform\JsonSchema\SchemaFactory->buildPropertySchema(object(Schema), 'BankStatementEntryDetailOverviewProjection.jsonld-bank_statement_entry_detail_overview_normalize', 'bankAccount', object(ApiProperty), array('groups' => array('bank_statement_entry_detail_overview_normalize')), 'jsonld')
(vendor/api-platform/core/src/JsonSchema/SchemaFactory.php:180)
at ApiPlatform\JsonSchema\SchemaFactory->buildSchema('UserInterface\\Bank\\Rest\\BankStatementEntryDetailOverviewProjection', 'jsonld', 'output', null, '_api_/engagements/{id}/bank_statement_entry_detail_overviews.{_format}_get_collection', object(Schema), array('groups' => array('bank_statement_entry_detail_overview_normalize')), true)
(vendor/api-platform/core/src/Hydra/JsonSchema/SchemaFactory.php:75)
at ApiPlatform\Hydra\JsonSchema\SchemaFactory->buildSchema('UserInterface\\Bank\\Rest\\BankStatementEntryDetailOverviewProjection', 'jsonld', 'output', null, '_api_/engagements/{id}/bank_statement_entry_detail_overviews.{_format}_get_collection', object(Schema), null, true)
(vendor/api-platform/core/src/OpenApi/Factory/OpenApiFactory.php:177)
at ApiPlatform\OpenApi\Factory\OpenApiFactory->collectPaths(object(ApiResource), object(ResourceMetadataCollection), object(Paths), object(ArrayObject))
(vendor/api-platform/core/src/OpenApi/Factory/OpenApiFactory.php:104)
at ApiPlatform\OpenApi\Factory\OpenApiFactory->__invoke(array('base_url' => '/'))
(vendor/api-platform/core/src/Symfony/Bundle/SwaggerUi/SwaggerUiAction.php:66)
at ApiPlatform\Symfony\Bundle\SwaggerUi\SwaggerUiAction->__invoke(object(Request))
(vendor/api-platform/core/src/Core/Bridge/Symfony/Bundle/Action/SwaggerUiAction.php:136)
at ApiPlatform\Core\Bridge\Symfony\Bundle\Action\SwaggerUiAction->__invoke(object(Request))
(vendor/symfony/http-kernel/HttpKernel.php:152)
at Symfony\Component\HttpKernel\HttpKernel->handleRaw(object(Request), 1)
(vendor/symfony/http-kernel/HttpKernel.php:74)
at Symfony\Component\HttpKernel\HttpKernel->handle(object(Request), 1, true)
(vendor/symfony/http-kernel/Kernel.php:202)
at Symfony\Component\HttpKernel\Kernel->handle(object(Request))
(vendor/symfony/runtime/Runner/Symfony/HttpKernelRunner.php:35)
at Symfony\Component\Runtime\Runner\Symfony\HttpKernelRunner->run()
(vendor/autoload_runtime.php:29)
at require_once('/home/www/app/vendor/autoload_runtime.php')
(public/index.php:14)
Is it true that classes in namespace ApiPlatform\Core
are part of the "old" structure? So the class ApiPlatform\Core\Bridge\Symfony\Bundle\Action\SwaggerUiAction
in the stacktrace means the old bundle is being used?
I have in my bundles.php
:
<?php
return [
ApiPlatform\Symfony\Bundle\ApiPlatformBundle::class => ['all' => true],
];
api_platform.yaml
:
api_platform:
metadata_backward_compatibility_layer: false
title: "API Platform"
version: "0.0.0-alpha"
defaults:
pagination_client_enabled: true
pagination_client_items_per_page: true
pagination_items_per_page: 30
pagination_maximum_items_per_page: 100
collection:
pagination:
enabled_parameter_name: pagination
elasticsearch:
enabled: true
hosts: [ '%env(ELASTICSEARCH_URL)%' ]
formats:
jsonld: [ 'application/ld+json' ]
json: [ 'application/json' ]
xlsx: [ 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' ]
zip: [ 'application/zip' ]
http_cache:
invalidation:
enabled: false
mapping:
paths:
- '%kernel.project_dir%/src/UserInterface'
patch_formats:
json: [ 'application/merge-patch+json' ]
swagger:
versions: [ 3 ]
api_keys:
apiKey:
name: Authorization
type: header
exception_to_status:
# The 4 following handlers are registered by default, keep those lines to prevent unexpected side effects
ApiPlatform\Exception\InvalidArgumentException: !php/const Symfony\Component\HttpFoundation\Response::HTTP_BAD_REQUEST
ApiPlatform\Exception\FilterValidationException: !php/const Symfony\Component\HttpFoundation\Response::HTTP_BAD_REQUEST
Doctrine\ORM\OptimisticLockException: !php/const Symfony\Component\HttpFoundation\Response::HTTP_CONFLICT
Symfony\Component\Serializer\Exception\ExceptionInterface: !php/const Symfony\Component\HttpFoundation\Response::HTTP_BAD_REQUEST
# Validation exception
ApiPlatform\Symfony\Validator\Exception\ValidationException: !php/const Symfony\Component\HttpFoundation\Response::HTTP_UNPROCESSABLE_ENTITY
# Custom mapping
DomainException: !php/const Symfony\Component\HttpFoundation\Response::HTTP_BAD_REQUEST
Symfony\Component\Messenger\Exception\HandlerFailedException: !php/const Symfony\Component\HttpFoundation\Response::HTTP_BAD_REQUEST
Edit:
I have the following in my routes.yaml
:
api_doc:
path: /v1/docs
controller: api_platform.swagger.action.ui
If I change it to:
api_doc:
path: /v1/docs
controller: api_platform.action.documentation
I get:
Symfony\Component\Serializer\Exception\NotEncodableValueException:
Serialization for the format "html" is not supported.
at vendor/symfony/serializer/Serializer.php:130
at Symfony\Component\Serializer\Serializer->serialize(object(Documentation), 'html', array('base_url' => '', 'spec_version' => 3))
(vendor/api-platform/core/src/Symfony/EventListener/SerializeListener.php:145)
at ApiPlatform\Symfony\EventListener\SerializeListener->serializeRawData(object(ViewEvent), object(Request), object(Documentation))
(vendor/api-platform/core/src/Symfony/EventListener/SerializeListener.php:97)
at ApiPlatform\Symfony\EventListener\SerializeListener->onKernelView(object(ViewEvent), 'kernel.view', object(TraceableEventDispatcher))
(vendor/symfony/event-dispatcher/Debug/WrappedListener.php:111)
at Symfony\Component\EventDispatcher\Debug\WrappedListener->__invoke(object(ViewEvent), 'kernel.view', object(TraceableEventDispatcher))
(vendor/symfony/event-dispatcher/EventDispatcher.php:230)
at Symfony\Component\EventDispatcher\EventDispatcher->callListeners(array(object(WrappedListener), object(WrappedListener), object(WrappedListener), object(WrappedListener), object(WrappedListener), object(WrappedListener)), 'kernel.view', object(ViewEvent))
(vendor/symfony/event-dispatcher/EventDispatcher.php:59)
at Symfony\Component\EventDispatcher\EventDispatcher->dispatch(object(ViewEvent), 'kernel.view')
(vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php:152)
at Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcher->dispatch(object(ViewEvent), 'kernel.view')
(vendor/symfony/http-kernel/HttpKernel.php:159)
at Symfony\Component\HttpKernel\HttpKernel->handleRaw(object(Request), 1)
(vendor/symfony/http-kernel/HttpKernel.php:74)
at Symfony\Component\HttpKernel\HttpKernel->handle(object(Request), 1, true)
(vendor/symfony/http-kernel/Kernel.php:202)
at Symfony\Component\HttpKernel\Kernel->handle(object(Request))
(vendor/symfony/runtime/Runner/Symfony/HttpKernelRunner.php:35)
at Symfony\Component\Runtime\Runner\Symfony\HttpKernelRunner->run()
(vendor/autoload_runtime.php:29)
at require_once('/home/www/app/vendor/autoload_runtime.php')
(public/index.php:14)
If I change it to:
api_doc:
path: /v1/docs
controller: api_platform.swagger_ui.action
I get:
ErrorException:
Warning: Undefined array key "$ref"
at vendor/api-platform/core/src/JsonSchema/TypeFactory.php:153
at ApiPlatform\JsonSchema\TypeFactory->getClassType('UserInterface\\Bank\\Rest\\BankAccountProjection', 'jsonld', true, array('groups' => array('bank_statement_entry_detail_overview_normalize')), object(Schema))
(vendor/api-platform/core/src/JsonSchema/TypeFactory.php:84)
at ApiPlatform\JsonSchema\TypeFactory->makeBasicType(object(Type), 'jsonld', true, array('groups' => array('bank_statement_entry_detail_overview_normalize')), object(Schema))
(vendor/api-platform/core/src/JsonSchema/TypeFactory.php:71)
at ApiPlatform\JsonSchema\TypeFactory->getType(object(Type), 'jsonld', true, array('groups' => array('bank_statement_entry_detail_overview_normalize')), object(Schema))
(vendor/api-platform/core/src/JsonSchema/SchemaFactory.php:269)
at ApiPlatform\JsonSchema\SchemaFactory->buildPropertySchema(object(Schema), 'BankStatementEntryDetailOverviewProjection.jsonld-bank_statement_entry_detail_overview_normalize', 'bankAccount', object(ApiProperty), array('groups' => array('bank_statement_entry_detail_overview_normalize')), 'jsonld')
(vendor/api-platform/core/src/JsonSchema/SchemaFactory.php:180)
at ApiPlatform\JsonSchema\SchemaFactory->buildSchema('UserInterface\\Bank\\Rest\\BankStatementEntryDetailOverviewProjection', 'jsonld', 'output', null, '_api_/engagements/{id}/bank_statement_entry_detail_overviews.{_format}_get_collection', object(Schema), array('groups' => array('bank_statement_entry_detail_overview_normalize')), true)
(vendor/api-platform/core/src/Hydra/JsonSchema/SchemaFactory.php:75)
at ApiPlatform\Hydra\JsonSchema\SchemaFactory->buildSchema('UserInterface\\Bank\\Rest\\BankStatementEntryDetailOverviewProjection', 'jsonld', 'output', null, '_api_/engagements/{id}/bank_statement_entry_detail_overviews.{_format}_get_collection', object(Schema), null, true)
(vendor/api-platform/core/src/OpenApi/Factory/OpenApiFactory.php:177)
at ApiPlatform\OpenApi\Factory\OpenApiFactory->collectPaths(object(ApiResource), object(ResourceMetadataCollection), object(Paths), object(ArrayObject))
(vendor/api-platform/core/src/OpenApi/Factory/OpenApiFactory.php:104)
at ApiPlatform\OpenApi\Factory\OpenApiFactory->__invoke(array('base_url' => '/'))
(vendor/api-platform/core/src/Symfony/Bundle/SwaggerUi/SwaggerUiAction.php:66)
at ApiPlatform\Symfony\Bundle\SwaggerUi\SwaggerUiAction->__invoke(object(Request))
(vendor/symfony/http-kernel/HttpKernel.php:154)
at Symfony\Component\HttpKernel\HttpKernel->handleRaw(object(Request), 1)
(vendor/symfony/http-kernel/HttpKernel.php:74)
at Symfony\Component\HttpKernel\HttpKernel->handle(object(Request), 1, true)
(vendor/symfony/http-kernel/Kernel.php:202)
at Symfony\Component\HttpKernel\Kernel->handle(object(Request))
(vendor/symfony/runtime/Runner/Symfony/HttpKernelRunner.php:35)
at Symfony\Component\Runtime\Runner\Symfony\HttpKernelRunner->run()
(vendor/autoload_runtime.php:29)
at require_once('/home/www/app/vendor/autoload_runtime.php')
(public/index.php:14)
It looks like there's an error with the subresource operation which is generated from the old attribute:
<?php
#[ApiResource(
uriTemplate: '/engagements/{id}/bank_statement_entry_detail_overviews.{_format}',
uriVariables: ['id' => new Link(fromClass: EngagementProjection::class, identifiers: ['id'])],
normalizationContext: ['groups' => [self::NORMALIZATION_GROUP]],
operations: [new GetCollection()],
paginationEnabled: false,
)]
If I remove this, I still get the error, but on the next class which has a similar attribute.
If my resources are not also doctrine entities, how should the subresource be declared? In the docs it looks like the examples are for doctrine entities only.
If you have only this operation you could even do something like this:
#[GetCollection(
uriTemplate: '/engagements/{id}/bank_statement_entry_detail_overviews.{_format}',
uriVariables: ['id' => new Link(fromClass: EngagementProjection::class, identifiers: ['id'])],
normalizationContext: ['groups' => [self::NORMALIZATION_GROUP]],
paginationEnabled: false,
)]
The links system is plugged with Doctrine but you should be able to make it work with your own provider. Note that in beta.5 providers and processors are also declared on the resource (check the debug:api
command if you want to see what the defaults are).
The error above is tighten to the SchemaFactory, we forgot the bc layer on it and I just fixed it, will release asap hoping it fixes your issue.
@dannyvw https://github.com/api-platform/core/pull/4767 should fix it
The error above is tighten to the SchemaFactory, we forgot the bc layer on it and I just fixed it, will release asap hoping it fixes your issue.
Great thanks! I'll wait for the new release :)
But since I have metadata_backward_compatibility_layer: false
, is the BC layer still relevant?
Edit: @soyuka I just tested it with alpha 7 and the issue is still there.
it may be that the schema was not properly generated with new metadata but indeed it's weird, I need to try and reproduce this issue.
I don't know if it a bug or just a misconfiguration. but if I create a /duplicate custom operation on a resource, the @id returned in the response is the url of the route /duplicate
I think the error comes from vendor/api-platform/core/src/Symfony/Routing/IriConverter.php:175 The code comes from this test.
$client = static::createClient();
$response = $client->request('GET', '/api/invoices/9de5c55a-fe43-4acd-8f5a-5175bf51f6bd/duplicate', [
'auth_basic' => ['admin', 'admin'],
])->toArray();
$id = $response['@id']; // /api/invoices/cb10e2d4-9347-4291-9e37-11c5e01b975d/duplicate
ExtraProperties is not inherited by the operations.
Name Method Scheme Host Path
-------------------- -------- -------- ------ -------------------------------------
api_entrypoint ANY ANY ANY /api/{index}.{_format}
api_doc ANY ANY ANY /api/docs.{_format}
api_jsonld_context ANY ANY ANY /api/contexts/{shortName}.{_format}
_preview_error ANY ANY ANY /_error/{code}.{_format}
-------------------- -------- -------- ------ -------------------------------------
<?php
namespace App\Entity;
use ApiPlatform\Metadata\ApiResource; use App\Repository\TestRepository; use Doctrine\ORM\Mapping as ORM;
class Test {
#[ORM\GeneratedValue]
#[ORM\Column(type: 'integer')]
private $id;
public function getId(): ?int
{
return $this->id;
}
}
- Full repo of the project: https://github.com/pavleocom/apip-test
What's missing?
- CRUD operations aren't getting registered
- PHP 8.0.19, Symfony 6.0, Api-Platform v2.7.0-alpha.7
- Output from bin/console debug:router:
Name Method Scheme Host Path -------------------- -------- -------- ------ ------------------------------------- api_entrypoint ANY ANY ANY /api/{index}.{_format} api_doc ANY ANY ANY /api/docs.{_format} api_jsonld_context ANY ANY ANY /api/contexts/{shortName}.{_format} _preview_error ANY ANY ANY /_error/{code}.{_format} -------------------- -------- -------- ------ -------------------------------------
- Entity /src/Entity/Test.php:
<?php namespace App\Entity; use ApiPlatform\Metadata\ApiResource; use App\Repository\TestRepository; use Doctrine\ORM\Mapping as ORM; #[ORM\Entity(repositoryClass: TestRepository::class)] #[ApiResource] class Test { #[ORM\Id] #[ORM\GeneratedValue] #[ORM\Column(type: 'integer')] private $id; public function getId(): ?int { return $this->id; } }
- Full repo of the project: https://github.com/pavleocom/apip-test
What's missing?
Resolved by adding this:
#/src/config/api_platform.yaml
api_platform:
metadata_backward_compatibility_layer: false
When you are upgrading using the api:upgrade-resource it does tell you to set metadata_backward_compatibility_layer to false but I installed APIP 2.7 as a new project from scratch and documentation for APIP 2.7 operations does not mention anything about this.
CacheHeaders issues
@darthf1 maybe add the html
format in your configuration, I need to find time to dig into this a bit more
@soyuka you mean like this?
api_platform:
formats:
jsonld: [ 'application/ld+json' ]
json: [ 'application/json' ]
xlsx: [ 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' ]
zip: [ 'application/zip' ]
html: [ 'text/html' ] <--- added
Tried with beta1, stacktrace is the same (with different line numbers):
ErrorException:
Warning: Undefined array key "$ref"
at vendor/api-platform/core/src/JsonSchema/TypeFactory.php:159
at ApiPlatform\JsonSchema\TypeFactory->getClassType('UserInterface\\Bank\\Rest\\BankAccountProjection', 'jsonld', true, array('groups' => array('bank_statement_entry_detail_overview_normalize')), object(Schema))
(vendor/api-platform/core/src/JsonSchema/TypeFactory.php:90)
at ApiPlatform\JsonSchema\TypeFactory->makeBasicType(object(Type), 'jsonld', true, array('groups' => array('bank_statement_entry_detail_overview_normalize')), object(Schema))
(vendor/api-platform/core/src/JsonSchema/TypeFactory.php:77)
at ApiPlatform\JsonSchema\TypeFactory->getType(object(Type), 'jsonld', true, array('groups' => array('bank_statement_entry_detail_overview_normalize')), object(Schema))
(vendor/api-platform/core/src/JsonSchema/SchemaFactory.php:218)
at ApiPlatform\JsonSchema\SchemaFactory->buildPropertySchema(object(Schema), 'BankStatementEntryDetailOverviewProjection.jsonld-bank_statement_entry_detail_overview_normalize', 'bankAccount', object(ApiProperty), array('groups' => array('bank_statement_entry_detail_overview_normalize')), 'jsonld')
(vendor/api-platform/core/src/JsonSchema/SchemaFactory.php:142)
at ApiPlatform\JsonSchema\SchemaFactory->buildSchema('UserInterface\\Bank\\Rest\\BankStatementEntryDetailOverviewProjection', 'jsonld', 'output', object(GetCollection), object(Schema), array('groups' => array('bank_statement_entry_detail_overview_normalize')), true)
(vendor/api-platform/core/src/Hydra/JsonSchema/SchemaFactory.php:76)
at ApiPlatform\Hydra\JsonSchema\SchemaFactory->buildSchema('UserInterface\\Bank\\Rest\\BankStatementEntryDetailOverviewProjection', 'jsonld', 'output', object(GetCollection), object(Schema), null, true)
(vendor/api-platform/core/src/OpenApi/Factory/OpenApiFactory.php:177)
at ApiPlatform\OpenApi\Factory\OpenApiFactory->collectPaths(object(ApiResource), object(ResourceMetadataCollection), object(Paths), object(ArrayObject))
(vendor/api-platform/core/src/OpenApi/Factory/OpenApiFactory.php:104)
at ApiPlatform\OpenApi\Factory\OpenApiFactory->__invoke(array('base_url' => '/'))
(vendor/api-platform/core/src/Symfony/Bundle/SwaggerUi/SwaggerUiAction.php:66)
at ApiPlatform\Symfony\Bundle\SwaggerUi\SwaggerUiAction->__invoke(object(Request))
(vendor/symfony/http-kernel/HttpKernel.php:152)
at Symfony\Component\HttpKernel\HttpKernel->handleRaw(object(Request), 1)
(vendor/symfony/http-kernel/HttpKernel.php:74)
at Symfony\Component\HttpKernel\HttpKernel->handle(object(Request), 1, true)
(vendor/symfony/http-kernel/Kernel.php:202)
at Symfony\Component\HttpKernel\Kernel->handle(object(Request))
(vendor/symfony/runtime/Runner/Symfony/HttpKernelRunner.php:35)
at Symfony\Component\Runtime\Runner\Symfony\HttpKernelRunner->run()
(vendor/autoload_runtime.php:29)
at require_once('/home/www/app/vendor/autoload_runtime.php')
(public/index.php:14)
API Platform: v2.7.0-beta.1
Extending AbstractFilter triggers the following error on requestStack variable
A binding is configured for an argument named "$requestStack" for service "App\Doctrine\Filter\NameFilter", but no corresponding argument has been found. It may be unused and should be removed, or it may have a typo.
This is triggered by https://github.com/api-platform/core/blob/2.7/src/Symfony/Bundle/DependencyInjection/ApiPlatformExtension.php#L667 and should only be applied on the old AbstractFilter class.
Example class
<?php
use ApiPlatform\Doctrine\Orm\Filter\AbstractFilter;
final class NameFilter extends AbstractFilter
API Platform: v2.7.0-beta.1
When you run ./bin/console api:upgrade-resource
You get an exception:
In UpgradeApiResourceCommand.php line 196:
Attempted to load class "Differ" from namespace "SebastianBergmann\Diff".
Did you forget a "use" statement for "PhpParser\Internal\Differ"?
Such class/package is not set in composer.json.
You can bypass the error executing: ./bin/console api:upgrade-resource --silent --force
regression in ApiTestCase::assertMatchesResourceItemJsonSchema
given a resource with a normalization context on 2.6:
#[ApiResource(
collectionOperations: ['get'],
itemOperations: ['get'],
normalizationContext: ['groups' => ['policy-notice:read']],
)]
class PolicyNotice {}
converted to 2.7 Metadata:
#[ApiResource(
operations: [
new Get(),
new GetCollection(),
],
normalizationContext: ['groups' => ['policy-notice:read']],
)]
class PolicyNotice {}
this no longer works on 2.7
$this->assertMatchesResourceItemJsonSchema(PolicyNotice::class);
because 2.7 tries to "guess" the operation and can't find one, so creates a new one with the wrong (empty) serialization context.
in 2.7 I can fix this by manually providing the operation name:
$this->assertMatchesResourceItemJsonSchema(PolicyNotice::class, '_api_/policy-notices/{id}.{_format}_get');
API Platform: v2.7.0-beta.1 (metadata_backward_compatibility_layer: false)
When configuring skip_null_values it is set as string instead of boolean in https://github.com/api-platform/core/blob/main/src/Serializer/SerializerContextBuilder.php#L65
Operation xml configuration
<operation class="ApiPlatform\Metadata\Put">
<normalizationContext>
<values>
<value name="skip_null_values">false</value>
</values>
</normalizationContext>
</operation>
API Platform: v2.7.1-beta.1
ReflectionException: Class "(string|null)" does not exist
/app/vendor/api-platform/core/src/JsonSchema/SchemaFactory.php:287
/app/vendor/api-platform/core/src/JsonSchema/SchemaFactory.php:101
/app/vendor/api-platform/core/src/Hydra/JsonSchema/SchemaFactory.php:75
The line that triggers this error is:
$this->assertMatchesResourceItemJsonSchema(Program::class, 'get');
There are several public ?string $foo
similar properties in Program class.
cc @vincentchalamon
@bendavies added a fix for this at https://github.com/api-platform/core/pull/4796 released as beta.2
@welcoMattic note that operation names have changed, not sure I understand the issue. (they change only when using the legacy flag to false)
API Platform 2.7 Beta-1 Symfony 6.0 PHP 8.1
Issue:
Denormalizing IRIs into objects doesn't seem to work when using DTO input class.
It does work if input class is not used.
Stack trace:
{
"@context": "/contexts/Error",
"@type": "hydra:Error",
"hydra:title": "An error occurred",
"hydra:description": "Failed to denormalize attribute \"order\" value for class \"App\\Dto\\Order\\OrderItemDto\": Expected argument of type \"?App\\Entity\\Order\", \"string\" given at property path \"order\".",
"trace": [
{
"namespace": "",
"short_class": "",
"class": "",
"type": "",
"function": "",
"file": "***/vendor/symfony/serializer/Exception/NotNormalizableValueException.php",
"line": 31,
"args": []
},
{
"namespace": "Symfony\\Component\\Serializer\\Exception",
"short_class": "NotNormalizableValueException",
"class": "Symfony\\Component\\Serializer\\Exception\\NotNormalizableValueException",
"type": "::",
"function": "createForUnexpectedDataType",
"file": "***/vendor/symfony/serializer/Normalizer/AbstractObjectNormalizer.php",
"line": 403,
"args": []
},
{
"namespace": "Symfony\\Component\\Serializer\\Normalizer",
"short_class": "AbstractObjectNormalizer",
"class": "Symfony\\Component\\Serializer\\Normalizer\\AbstractObjectNormalizer",
"type": "->",
"function": "denormalize",
"file": "***/vendor/api-platform/core/src/Serializer/AbstractItemNormalizer.php",
"line": 314,
"args": []
},
{
"namespace": "ApiPlatform\\Serializer",
"short_class": "AbstractItemNormalizer",
"class": "ApiPlatform\\Serializer\\AbstractItemNormalizer",
"type": "->",
"function": "denormalize",
"file": "***/vendor/api-platform/core/src/Serializer/ItemNormalizer.php",
"line": 77,
"args": []
},
{
"namespace": "ApiPlatform\\Serializer",
"short_class": "ItemNormalizer",
"class": "ApiPlatform\\Serializer\\ItemNormalizer",
"type": "->",
"function": "denormalize",
"file": "***/src/Serializer/DecoratingNormalizer.php",
"line": 84,
"args": []
},
{
"namespace": "App\\Serializer",
"short_class": "DecoratingNormalizer",
"class": "App\\Serializer\\DecoratingNormalizer",
"type": "->",
"function": "denormalize",
"file": "***/vendor/symfony/serializer/Serializer.php",
"line": 238,
"args": []
},
{
"namespace": "Symfony\\Component\\Serializer",
"short_class": "Serializer",
"class": "Symfony\\Component\\Serializer\\Serializer",
"type": "->",
"function": "denormalize",
"file": "***/vendor/symfony/serializer/Serializer.php",
"line": 151,
"args": []
},
{
"namespace": "Symfony\\Component\\Serializer",
"short_class": "Serializer",
"class": "Symfony\\Component\\Serializer\\Serializer",
"type": "->",
"function": "deserialize",
"file": "***/vendor/api-platform/core/src/Symfony/EventListener/DeserializeListener.php",
"line": 137,
"args": []
},
{
"namespace": "ApiPlatform\\Symfony\\EventListener",
"short_class": "DeserializeListener",
"class": "ApiPlatform\\Symfony\\EventListener\\DeserializeListener",
"type": "->",
"function": "onKernelRequest",
"file": "***/vendor/symfony/event-dispatcher/Debug/WrappedListener.php",
"line": 111,
"args": []
},
{
"namespace": "Symfony\\Component\\EventDispatcher\\Debug",
"short_class": "WrappedListener",
"class": "Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener",
"type": "->",
"function": "__invoke",
"file": "***/vendor/symfony/event-dispatcher/EventDispatcher.php",
"line": 230,
"args": []
},
{
"namespace": "Symfony\\Component\\EventDispatcher",
"short_class": "EventDispatcher",
"class": "Symfony\\Component\\EventDispatcher\\EventDispatcher",
"type": "->",
"function": "callListeners",
"file": "***/vendor/symfony/event-dispatcher/EventDispatcher.php",
"line": 59,
"args": []
},
{
"namespace": "Symfony\\Component\\EventDispatcher",
"short_class": "EventDispatcher",
"class": "Symfony\\Component\\EventDispatcher\\EventDispatcher",
"type": "->",
"function": "dispatch",
"file": "***/vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php",
"line": 152,
"args": []
},
{
"namespace": "Symfony\\Component\\EventDispatcher\\Debug",
"short_class": "TraceableEventDispatcher",
"class": "Symfony\\Component\\EventDispatcher\\Debug\\TraceableEventDispatcher",
"type": "->",
"function": "dispatch",
"file": "***/vendor/symfony/http-kernel/HttpKernel.php",
"line": 128,
"args": []
},
{
"namespace": "Symfony\\Component\\HttpKernel",
"short_class": "HttpKernel",
"class": "Symfony\\Component\\HttpKernel\\HttpKernel",
"type": "->",
"function": "handleRaw",
"file": "***/vendor/symfony/http-kernel/HttpKernel.php",
"line": 74,
"args": []
},
{
"namespace": "Symfony\\Component\\HttpKernel",
"short_class": "HttpKernel",
"class": "Symfony\\Component\\HttpKernel\\HttpKernel",
"type": "->",
"function": "handle",
"file": "***/vendor/symfony/http-kernel/Kernel.php",
"line": 202,
"args": []
},
{
"namespace": "Symfony\\Component\\HttpKernel",
"short_class": "Kernel",
"class": "Symfony\\Component\\HttpKernel\\Kernel",
"type": "->",
"function": "handle",
"file": "***/vendor/symfony/runtime/Runner/Symfony/HttpKernelRunner.php",
"line": 35,
"args": []
},
{
"namespace": "Symfony\\Component\\Runtime\\Runner\\Symfony",
"short_class": "HttpKernelRunner",
"class": "Symfony\\Component\\Runtime\\Runner\\Symfony\\HttpKernelRunner",
"type": "->",
"function": "run",
"file": "***/vendor/autoload_runtime.php",
"line": 29,
"args": []
},
{
"namespace": "",
"short_class": "",
"class": "",
"type": "",
"function": "require_once",
"file": "***/public/index.php",
"line": 5,
"args": [
[
"string",
"***/vendor/autoload_runtime.php"
]
]
}
]
}
Entity: OrderItem (ApiResource attribute only)
#[ApiResource(
operations: [
new GetNotFound(),
new Post(
input: OrderItemDto::class,
processor: OrderAddItemProcessor::class,
denormalizationContext: [
'groups' => [
'Order:W$AddOrderItem',
],
],
),
],
)]
DTO: OrderItemDto
<?php
declare(strict_types=1);
namespace App\Dto\Order;
use App\Entity\Order;
use Symfony\Component\Serializer\Annotation as Serializer;
class OrderItemDto
{
#[Serializer\Groups([
'Order:W$AddOrderItem',
])]
public ?Order $order = null;
}
HTTP Request:
POST /order-items HTTP/1.1
Host: 127.0.0.1:8000
Authorization: Bearer ***
Content-Type: application/json
Content-Length: 49
{
"order": "/orders/gbZ5Llr8cU_a82CHDJwEEM"
}
@soyuka thankyou!
use Content-Type: application/ld+json
maybe ?
use
Content-Type: application/ld+json
maybe ?
Note when input class is not used (i.e. when entity class is used) IRIs do get converted into objects with the same http request. Nevertheless, I've tried changing the request header and it didn't make any difference.
@soyuka
I don't understand we have a test case covering this (https://github.com/api-platform/core/blob/main/features/jsonld/input_output.feature#L199) and I checked it works on 2.7. I need to try this without any Data Transformer though but I'm working hard on the 3.0. I'll come back to this, if you have the time to add a Behat test covering your issue in a PR against 2.7 it'd save me some time! Thanks!
It seems it's not just IRIs but rather denormalizer not being able to determine what type the property is supposed to be and thus just using the original value which is string (or number). For example, when I have a property of type DateTime in my input class, and the request contains a valid Y-m-d date string for that property, it fails with the same exception.
With all that said, I wasn't able to create a failing test covering this issue. I must be doing something wrong in my project? I've created a sample project which suffers from the same problem: https://github.com/pavleocom/apip-test
I'd be grateful if you or someone else could take a look and advise on what I might be doing wrong.
I have a very similar issue with a new project I use to experiment with the new metadata system of API Platform 2.7.
I have an API Resource with a Post operation mapped to an Input class (which is not an API Resource). One of the property of this input class is another class (also not an API Resource).
When I try to call the the operation, I get the following exception :
App\Dto\NouveauClient::__construct(): Argument #3 ($adresse) must be of type App\Dto\NouvelleAdresse, array given
With Xdebug I found that the method denormalize
of the ApiPlatform\JsonLd\Serializer\ItemNormalizer
call the method denormalize
of ApiPlatform\Serializer\AbstractItemNormalizer
and then the method denormalize
of Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer
(all of which are just parent calls).
The denormalize
of Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer
call getTypes
but at this step $this->propertyTypeExtractor
evaluates to null
so no type is returned and the raw value from my json payload (an array) is passed to the constructor of my input, hence the exception.
If I inject the service property_info
in ApiPlatform\JsonLd\Serializer\ItemNormalizer
and pass it all the way to Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer
then everything works just fine. However I'm not sure this is really the right fix because I can't reproduce the issue using the test suite (I've tried to modify this feature to mimic my case but it works just fine).
During my tests I'm using api-platform/core@v2.7.0-beta.1
and symfony/serializer@v6.1.1
.
Interesting findings @jonag thanks, I'll make sure that the property_info
is defined and let you know when its patched. I'm working on improvements in 3.0 that I'll backport to 2.7 once its stable enough (they should fix your issues above).
Hi,
Symfony 5.4 PHP 7.4 Easycorp/easyadmin-bundle : 3.5.21
API Platform version(s) affected: ^v2.7.0-beta.1
Description
When I try to access an EasyAdmin page, I get a 500 error:
`Serialization for the format "html" is not supported.
Symfony\Component\Serializer\Exception\NotEncodableValueException: Serialization for the format "html" is not supported.
at vendor/symfony/serializer/Serializer.php:130 at Symfony\Component\Serializer\Serializer->serialize(object(KeyValueStore), 'html', array()) (vendor/api-platform/core/src/Symfony/EventListener/SerializeListener.php:145) at ApiPlatform\Symfony\EventListener\SerializeListener->serializeRawData(object(ViewEvent), object(Request), object(KeyValueStore)) (vendor/api-platform/core/src/Symfony/EventListener/SerializeListener.php:97) at ApiPlatform\Symfony\EventListener\SerializeListener->onKernelView(object(ViewEvent), 'kernel.view', object(TraceableEventDispatcher)) (vendor/symfony/event-dispatcher/Debug/WrappedListener.php:117) at Symfony\Component\EventDispatcher\Debug\WrappedListener->__invoke(object(ViewEvent), 'kernel.view', object(TraceableEventDispatcher)) (vendor/symfony/event-dispatcher/EventDispatcher.php:230) at Symfony\Component\EventDispatcher\EventDispatcher->callListeners(array(object(WrappedListener), object(WrappedListener), object(WrappedListener), object(WrappedListener), object(WrappedListener), object(WrappedListener), object(WrappedListener), object(WrappedListener)), 'kernel.view', object(ViewEvent)) (vendor/symfony/event-dispatcher/EventDispatcher.php:59) at Symfony\Component\EventDispatcher\EventDispatcher->dispatch(object(ViewEvent), 'kernel.view') (vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php:154) at Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcher->dispatch(object(ViewEvent), 'kernel.view') (vendor/symfony/http-kernel/HttpKernel.php:157) at Symfony\Component\HttpKernel\HttpKernel->handleRaw(object(Request), 1) (vendor/symfony/http-kernel/HttpKernel.php:74) at Symfony\Component\HttpKernel\HttpKernel->handle(object(Request), 1, true) (vendor/symfony/http-kernel/Kernel.php:202) at Symfony\Component\HttpKernel\Kernel->handle(object(Request)) (public/index.php:55) `
How to reproduce
When you have projet with Symfony 5.4, EasyAdmin 3.5.21 and Api Platform v2.7.0-beta.1, you have to try access to an EasyAdmin CRUD page.
Possible Solution
I believe that the problem comes from the fact that ApiPlatform as well as EasyAdmin are connected to kernel.view event and that it is indeed the API Platform listeners which are executed first.
I don't know if the best solution is to manage a condition in the API Platform listeners to exclude the EasyAdmin case? Or only include API Platform cases? Or even change the priority of the listeners?
Additional Context
/
EDIT Fix : https://github.com/api-platform/core/pull/4828/commits Thank @vincentchalamon
@pavel-m10c @jonag I tagged a new beta, please let me know if it fixes the issue.
To everyone else: 3.0 is coming out tomorrow !
I tried the new beta on my test project and it seems to fix the issue 😍, thank you 👊🏼!
After upgrading from 2.7.0.beta-2 to 2.7.0.beta-3 a lot of operations are broken. It gives the following exception "Operation \"\" not found for resource \"ChangePassword\"."
It has possibly something to do with this commit https://github.com/api-platform/core/commit/7c2c3ececea38f6f2a51b027ac0eb1a590cb1c94
XML config for operation
<?xml version="1.0" ?>
<resources xmlns="https://api-platform.com/schema/metadata/resources-3.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://api-platform.com/schema/metadata/resources-3.0
https://api-platform.com/schema/metadata/resources.xsd">
<resource class="App\Command\ChangePassword" shortName="ChangePassword">
<operations>
<operation class="ApiPlatform\Metadata\Patch"
input="App\Command\ChangePassword"
output="false"
status="202"
uriTemplate="/change-password"
processor="App\State\ChangePasswordProcessor"
>
<denormalizationContext>
<values>
<value name="groups">change_password</value>
</values>
</denormalizationContext>
</operation>
</operations>
</resource>
</resources>
Stacktrace
{
"@context": "/api/v2/contexts/Error",
"@type": "hydra:Error",
"hydra:title": "An error occurred",
"hydra:description": "Operation \"\" not found for resource \"ChangePassword\".",
"trace": [
{
"namespace": "",
"short_class": "",
"class": "",
"type": "",
"function": "",
"file": "/var/www/vendor/api-platform/core/src/Metadata/Resource/ResourceMetadataCollection.php",
"line": 96,
"args": []
},
{
"namespace": "ApiPlatform\\Metadata\\Resource",
"short_class": "ResourceMetadataCollection",
"class": "ApiPlatform\\Metadata\\Resource\\ResourceMetadataCollection",
"type": "->",
"function": "handleNotFound",
"file": "/var/www/vendor/api-platform/core/src/Metadata/Resource/ResourceMetadataCollection.php",
"line": 80,
"args": [
[
"string",
""
],
[
"object",
"ApiPlatform\\Metadata\\ApiResource"
]
]
},
{
"namespace": "ApiPlatform\\Metadata\\Resource",
"short_class": "ResourceMetadataCollection",
"class": "ApiPlatform\\Metadata\\Resource\\ResourceMetadataCollection",
"type": "->",
"function": "getOperation",
"file": "/var/www/vendor/api-platform/core/src/Serializer/AbstractItemNormalizer.php",
"line": 709,
"args": [
[
"string",
""
]
]
},
{
"namespace": "ApiPlatform\\Serializer",
"short_class": "AbstractItemNormalizer",
"class": "ApiPlatform\\Serializer\\AbstractItemNormalizer",
"type": "->",
"function": "getFactoryOptions",
"file": "/var/www/vendor/api-platform/core/src/Serializer/AbstractItemNormalizer.php",
"line": 488,
"args": [
[
"array",
{
"groups": [
"string",
"change_password"
],
"resource_class": [
"string",
"App\\Command\\ChangePassword"
],
"skip_null_values": [
"boolean",
true
],
"operation_type": [
"string",
"item"
],
"iri_only": [
"boolean",
false
],
"request_uri": [
"string",
"/api/v2/change-password"
],
"uri": [
"string",
"https://localhost/api/v2/change-password"
],
"output": [
"array",
{
"class": [
"null",
null
]
}
],
"types": [
"null",
null
],
"uri_variables": [
"array",
[]
],
"api_allow_update": [
"boolean",
true
],
"deep_object_to_populate": [
"boolean",
true
],
"api_denormalize": [
"boolean",
true
],
"cache_key": [
"string",
"e930a22fa4bfe882fd66037813f771c4"
]
}
]
]
},
{
"namespace": "ApiPlatform\\Serializer",
"short_class": "AbstractItemNormalizer",
"class": "ApiPlatform\\Serializer\\AbstractItemNormalizer",
"type": "->",
"function": "getAllowedAttributes",
"file": "/var/www/vendor/symfony/serializer/Normalizer/AbstractObjectNormalizer.php",
"line": 354,
"args": [
[
"string",
"App\\Command\\ChangePassword"
],
[
"array",
{
"groups": [
"string",
"change_password"
],
"resource_class": [
"string",
"App\\Command\\ChangePassword"
],
"skip_null_values": [
"boolean",
true
],
"operation_type": [
"string",
"item"
],
"iri_only": [
"boolean",
false
],
"request_uri": [
"string",
"/api/v2/change-password"
],
"uri": [
"string",
"https://localhost/api/v2/change-password"
],
"output": [
"array",
{
"class": [
"null",
null
]
}
],
"types": [
"null",
null
],
"uri_variables": [
"array",
[]
],
"api_allow_update": [
"boolean",
true
],
"deep_object_to_populate": [
"boolean",
true
],
"api_denormalize": [
"boolean",
true
],
"cache_key": [
"string",
"e930a22fa4bfe882fd66037813f771c4"
]
}
],
[
"boolean",
true
]
]
},
{
"namespace": "Symfony\\Component\\Serializer\\Normalizer",
"short_class": "AbstractObjectNormalizer",
"class": "Symfony\\Component\\Serializer\\Normalizer\\AbstractObjectNormalizer",
"type": "->",
"function": "denormalize",
"file": "/var/www/vendor/api-platform/core/src/Serializer/AbstractItemNormalizer.php",
"line": 353,
"args": [
[
"array",
[]
],
[
"string",
"App\\Command\\ChangePassword"
],
[
"string",
"json"
],
[
"array",
{
"groups": [
"string",
"change_password"
],
"resource_class": [
"string",
"App\\Command\\ChangePassword"
],
"skip_null_values": [
"boolean",
true
],
"operation_type": [
"string",
"item"
],
"iri_only": [
"boolean",
false
],
"request_uri": [
"string",
"/api/v2/change-password"
],
"uri": [
"string",
"https://localhost/api/v2/change-password"
],
"output": [
"array",
{
"class": [
"null",
null
]
}
],
"types": [
"null",
null
],
"uri_variables": [
"array",
[]
],
"api_allow_update": [
"boolean",
true
],
"deep_object_to_populate": [
"boolean",
true
],
"api_denormalize": [
"boolean",
true
],
"cache_key": [
"string",
"e930a22fa4bfe882fd66037813f771c4"
]
}
]
]
},
{
"namespace": "ApiPlatform\\Serializer",
"short_class": "AbstractItemNormalizer",
"class": "ApiPlatform\\Serializer\\AbstractItemNormalizer",
"type": "->",
"function": "denormalize",
"file": "/var/www/vendor/api-platform/core/src/Serializer/ItemNormalizer.php",
"line": 77,
"args": [
[
"array",
[]
],
[
"string",
"App\\Command\\ChangePassword"
],
[
"string",
"json"
],
[
"array",
{
"groups": [
"string",
"change_password"
],
"resource_class": [
"string",
"App\\Command\\ChangePassword"
],
"skip_null_values": [
"boolean",
true
],
"operation_type": [
"string",
"item"
],
"iri_only": [
"boolean",
false
],
"request_uri": [
"string",
"/api/v2/change-password"
],
"uri": [
"string",
"https://localhost/api/v2/change-password"
],
"output": [
"array",
{
"class": [
"null",
null
]
}
],
"types": [
"null",
null
],
"uri_variables": [
"array",
[]
],
"api_allow_update": [
"boolean",
true
],
"deep_object_to_populate": [
"boolean",
true
],
"api_denormalize": [
"boolean",
true
]
}
]
]
},
{
"namespace": "ApiPlatform\\Serializer",
"short_class": "ItemNormalizer",
"class": "ApiPlatform\\Serializer\\ItemNormalizer",
"type": "->",
"function": "denormalize",
"file": "/var/www/vendor/symfony/serializer/Serializer.php",
"line": 238,
"args": [
[
"array",
[]
],
[
"string",
"App\\Command\\ChangePassword"
],
[
"string",
"json"
],
[
"array",
{
"groups": [
"string",
"change_password"
],
"resource_class": [
"string",
"App\\Command\\ChangePassword"
],
"skip_null_values": [
"boolean",
true
],
"operation_type": [
"string",
"item"
],
"iri_only": [
"boolean",
false
],
"request_uri": [
"string",
"/api/v2/change-password"
],
"uri": [
"string",
"https://localhost/api/v2/change-password"
],
"output": [
"array",
{
"class": [
"null",
null
]
}
],
"types": [
"null",
null
],
"uri_variables": [
"array",
[]
],
"api_allow_update": [
"boolean",
true
],
"deep_object_to_populate": [
"boolean",
true
]
}
]
]
},
{
"namespace": "Symfony\\Component\\Serializer",
"short_class": "Serializer",
"class": "Symfony\\Component\\Serializer\\Serializer",
"type": "->",
"function": "denormalize",
"file": "/var/www/vendor/api-platform/core/src/Serializer/AbstractItemNormalizer.php",
"line": 305,
"args": [
[
"array",
[]
],
[
"string",
"App\\Command\\ChangePassword"
],
[
"string",
"json"
],
[
"array",
{
"groups": [
"string",
"change_password"
],
"resource_class": [
"string",
"App\\Command\\ChangePassword"
],
"skip_null_values": [
"boolean",
true
],
"operation_type": [
"string",
"item"
],
"iri_only": [
"boolean",
false
],
"request_uri": [
"string",
"/api/v2/change-password"
],
"uri": [
"string",
"https://localhost/api/v2/change-password"
],
"output": [
"array",
{
"class": [
"null",
null
]
}
],
"types": [
"null",
null
],
"uri_variables": [
"array",
[]
],
"api_allow_update": [
"boolean",
true
],
"deep_object_to_populate": [
"boolean",
true
]
}
]
]
},
{
"namespace": "ApiPlatform\\Serializer",
"short_class": "AbstractItemNormalizer",
"class": "ApiPlatform\\Serializer\\AbstractItemNormalizer",
"type": "->",
"function": "denormalize",
"file": "/var/www/vendor/api-platform/core/src/Serializer/ItemNormalizer.php",
"line": 77,
"args": [
[
"array",
[]
],
[
"string",
"App\\Command\\ChangePassword"
],
[
"string",
"json"
],
[
"array",
{
"groups": [
"string",
"change_password"
],
"resource_class": [
"string",
"App\\Command\\ChangePassword"
],
"skip_null_values": [
"boolean",
true
],
"operation_type": [
"string",
"item"
],
"iri_only": [
"boolean",
false
],
"request_uri": [
"string",
"/api/v2/change-password"
],
"uri": [
"string",
"https://localhost/api/v2/change-password"
],
"output": [
"array",
{
"class": [
"null",
null
]
}
],
"types": [
"null",
null
],
"uri_variables": [
"array",
[]
],
"api_allow_update": [
"boolean",
true
],
"deep_object_to_populate": [
"boolean",
true
]
}
]
]
},
{
"namespace": "ApiPlatform\\Serializer",
"short_class": "ItemNormalizer",
"class": "ApiPlatform\\Serializer\\ItemNormalizer",
"type": "->",
"function": "denormalize",
"file": "/var/www/vendor/symfony/serializer/Serializer.php",
"line": 238,
"args": [
[
"array",
[]
],
[
"string",
"App\\Command\\ChangePassword"
],
[
"string",
"json"
],
[
"array",
{
"groups": [
"string",
"change_password"
],
"operation_name": [
"string",
"_api_/change-password_patch"
],
"operation": [
"object",
"ApiPlatform\\Metadata\\Patch"
],
"resource_class": [
"string",
"App\\Command\\ChangePassword"
],
"skip_null_values": [
"boolean",
true
],
"operation_type": [
"string",
"item"
],
"iri_only": [
"boolean",
false
],
"request_uri": [
"string",
"/api/v2/change-password"
],
"uri": [
"string",
"https://localhost/api/v2/change-password"
],
"input": [
"array",
{
"class": [
"string",
"App\\Command\\ChangePassword"
],
"name": [
"string",
"ChangePassword"
]
}
],
"output": [
"array",
{
"class": [
"null",
null
]
}
],
"types": [
"null",
null
],
"uri_variables": [
"array",
[]
],
"api_allow_update": [
"boolean",
true
],
"deep_object_to_populate": [
"boolean",
true
]
}
]
]
},
{
"namespace": "Symfony\\Component\\Serializer",
"short_class": "Serializer",
"class": "Symfony\\Component\\Serializer\\Serializer",
"type": "->",
"function": "denormalize",
"file": "/var/www/vendor/symfony/serializer/Serializer.php",
"line": 151,
"args": [
[
"array",
[]
],
[
"string",
"App\\Command\\ChangePassword"
],
[
"string",
"json"
],
[
"array",
{
"groups": [
"string",
"change_password"
],
"operation_name": [
"string",
"_api_/change-password_patch"
],
"operation": [
"object",
"ApiPlatform\\Metadata\\Patch"
],
"resource_class": [
"string",
"App\\Command\\ChangePassword"
],
"skip_null_values": [
"boolean",
true
],
"operation_type": [
"string",
"item"
],
"iri_only": [
"boolean",
false
],
"request_uri": [
"string",
"/api/v2/change-password"
],
"uri": [
"string",
"https://localhost/api/v2/change-password"
],
"input": [
"array",
{
"class": [
"string",
"App\\Command\\ChangePassword"
],
"name": [
"string",
"ChangePassword"
]
}
],
"output": [
"array",
{
"class": [
"null",
null
]
}
],
"types": [
"null",
null
],
"uri_variables": [
"array",
[]
],
"api_allow_update": [
"boolean",
true
],
"deep_object_to_populate": [
"boolean",
true
]
}
]
]
},
{
"namespace": "Symfony\\Component\\Serializer",
"short_class": "Serializer",
"class": "Symfony\\Component\\Serializer\\Serializer",
"type": "->",
"function": "deserialize",
"file": "/var/www/vendor/api-platform/core/src/Symfony/EventListener/DeserializeListener.php",
"line": 137,
"args": [
[
"array",
[]
],
[
"string",
"App\\Command\\ChangePassword"
],
[
"string",
"json"
],
[
"array",
{
"groups": [
"string",
"change_password"
],
"operation_name": [
"string",
"_api_/change-password_patch"
],
"operation": [
"object",
"ApiPlatform\\Metadata\\Patch"
],
"resource_class": [
"string",
"App\\Command\\ChangePassword"
],
"skip_null_values": [
"boolean",
true
],
"operation_type": [
"string",
"item"
],
"iri_only": [
"boolean",
false
],
"request_uri": [
"string",
"/api/v2/change-password"
],
"uri": [
"string",
"https://localhost/api/v2/change-password"
],
"input": [
"array",
{
"class": [
"string",
"App\\Command\\ChangePassword"
],
"name": [
"string",
"ChangePassword"
]
}
],
"output": [
"array",
{
"class": [
"null",
null
]
}
],
"types": [
"null",
null
],
"uri_variables": [
"array",
[]
],
"api_allow_update": [
"boolean",
true
],
"deep_object_to_populate": [
"boolean",
true
]
}
]
]
},
{
"namespace": "ApiPlatform\\Symfony\\EventListener",
"short_class": "DeserializeListener",
"class": "ApiPlatform\\Symfony\\EventListener\\DeserializeListener",
"type": "->",
"function": "onKernelRequest",
"file": "/var/www/vendor/symfony/event-dispatcher/Debug/WrappedListener.php",
"line": 117,
"args": [
[
"object",
"Symfony\\Component\\HttpKernel\\Event\\RequestEvent"
],
[
"string",
"kernel.request"
],
[
"object",
"Symfony\\Component\\HttpKernel\\Debug\\TraceableEventDispatcher"
]
]
},
{
"namespace": "Symfony\\Component\\EventDispatcher\\Debug",
"short_class": "WrappedListener",
"class": "Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener",
"type": "->",
"function": "__invoke",
"file": "/var/www/vendor/symfony/event-dispatcher/EventDispatcher.php",
"line": 230,
"args": [
[
"object",
"Symfony\\Component\\HttpKernel\\Event\\RequestEvent"
],
[
"string",
"kernel.request"
],
[
"object",
"Symfony\\Component\\HttpKernel\\Debug\\TraceableEventDispatcher"
]
]
},
{
"namespace": "Symfony\\Component\\EventDispatcher",
"short_class": "EventDispatcher",
"class": "Symfony\\Component\\EventDispatcher\\EventDispatcher",
"type": "->",
"function": "callListeners",
"file": "/var/www/vendor/symfony/event-dispatcher/EventDispatcher.php",
"line": 59,
"args": [
[
"array",
[
[
"object",
"Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener"
],
[
"object",
"Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener"
],
[
"object",
"Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener"
],
[
"object",
"Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener"
],
[
"object",
"Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener"
],
[
"object",
"Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener"
],
[
"object",
"Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener"
],
[
"object",
"Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener"
],
[
"object",
"Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener"
],
[
"object",
"Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener"
],
[
"object",
"Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener"
],
[
"object",
"Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener"
],
[
"object",
"Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener"
],
[
"object",
"Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener"
],
[
"object",
"Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener"
],
[
"object",
"Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener"
],
[
"object",
"Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener"
],
[
"object",
"Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener"
],
[
"object",
"Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener"
],
[
"object",
"Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener"
],
[
"object",
"Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener"
],
[
"object",
"Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener"
],
[
"object",
"Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener"
],
[
"object",
"Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener"
],
[
"object",
"Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener"
],
[
"object",
"Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener"
],
[
"object",
"Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener"
],
[
"object",
"Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener"
],
[
"object",
"Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener"
],
[
"object",
"Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener"
],
[
"object",
"Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener"
],
[
"object",
"Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener"
],
[
"object",
"Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener"
]
]
],
[
"string",
"kernel.request"
],
[
"object",
"Symfony\\Component\\HttpKernel\\Event\\RequestEvent"
]
]
},
{
"namespace": "Symfony\\Component\\EventDispatcher",
"short_class": "EventDispatcher",
"class": "Symfony\\Component\\EventDispatcher\\EventDispatcher",
"type": "->",
"function": "dispatch",
"file": "/var/www/vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php",
"line": 154,
"args": [
[
"object",
"Symfony\\Component\\HttpKernel\\Event\\RequestEvent"
],
[
"string",
"kernel.request"
]
]
},
{
"namespace": "Symfony\\Component\\EventDispatcher\\Debug",
"short_class": "TraceableEventDispatcher",
"class": "Symfony\\Component\\EventDispatcher\\Debug\\TraceableEventDispatcher",
"type": "->",
"function": "dispatch",
"file": "/var/www/vendor/symfony/http-kernel/HttpKernel.php",
"line": 128,
"args": [
[
"object",
"Symfony\\Component\\HttpKernel\\Event\\RequestEvent"
],
[
"string",
"kernel.request"
]
]
},
{
"namespace": "Symfony\\Component\\HttpKernel",
"short_class": "HttpKernel",
"class": "Symfony\\Component\\HttpKernel\\HttpKernel",
"type": "->",
"function": "handleRaw",
"file": "/var/www/vendor/symfony/http-kernel/HttpKernel.php",
"line": 74,
"args": [
[
"object",
"Symfony\\Component\\HttpFoundation\\Request"
],
[
"integer",
1
]
]
},
{
"namespace": "Symfony\\Component\\HttpKernel",
"short_class": "HttpKernel",
"class": "Symfony\\Component\\HttpKernel\\HttpKernel",
"type": "->",
"function": "handle",
"file": "/var/www/vendor/symfony/http-kernel/Kernel.php",
"line": 202,
"args": [
[
"object",
"Symfony\\Component\\HttpFoundation\\Request"
],
[
"integer",
1
],
[
"boolean",
true
]
]
},
{
"namespace": "Symfony\\Component\\HttpKernel",
"short_class": "Kernel",
"class": "Symfony\\Component\\HttpKernel\\Kernel",
"type": "->",
"function": "handle",
"file": "/var/www/vendor/symfony/runtime/Runner/Symfony/HttpKernelRunner.php",
"line": 35,
"args": [
[
"object",
"Symfony\\Component\\HttpFoundation\\Request"
]
]
},
{
"namespace": "Symfony\\Component\\Runtime\\Runner\\Symfony",
"short_class": "HttpKernelRunner",
"class": "Symfony\\Component\\Runtime\\Runner\\Symfony\\HttpKernelRunner",
"type": "->",
"function": "run",
"file": "/var/www/vendor/autoload_runtime.php",
"line": 35,
"args": []
},
{
"namespace": "",
"short_class": "",
"class": "",
"type": "",
"function": "require_once",
"file": "/var/www/public/index.php",
"line": 5,
"args": [
[
"string",
"/var/www/vendor/autoload_runtime.php"
]
]
}
]
}
@dannyvw yeah I backported the fix on inputs/outputs from 3.0. I assume you removed the cache? Can you give me the stack trace for these issues? It should just work as I added isResourceClass
everywhere where we try to get back an operation.
@soyuka Yes cache is removed. I have added the stack trace to the previous comment.
It looks quite similar to our test on User:
https://github.com/api-platform/core/blob/2.7/tests/Fixtures/TestBundle/Entity/User.php#L53-L55 or https://github.com/api-platform/core/blob/main/tests/Fixtures/TestBundle/Entity/User.php#L41
There's a new command debug:api
could you also give me the operation declaration? (give it in a gist or through slack) I'll try to reproduce.
Remove input class if this is the same as the resource class will fix the issue. Thanks for your help!
PHP: 8.1-fpm-alpine Symfony: 6.1.* API Platform: 3.0-beta
Using the following resource:
#[ApiResource(
operations: [
new GetCollection(),
new Post(),
],
)]
The following test fail:
public function testStats(): void
{
$this->client->request('GET', '/docs.json');
self::assertResponseIsSuccessful();
self::assertStringContainsString('/stats', (string) $this->client->getResponse()->getContent());
}
With the following error:
{"type":"https:\/\/tools.ietf.org\/html\/rfc2616#section-10","title":"An error occurred","detail":"Undefined array key \u0022$ref\u0022","trace":[{"namespace":"","short_class":"","class":"","type":"","function":"","file":"\/srv\/api\/vendor\/api-platform\/core\/src\/JsonSchema\/TypeFactory.php","line":159,"args":[]},
The bug does not occur anymore when reversing the operations order:
#[ApiResource(
operations: [
new Post(),
new GetCollection(),
],
)]
PHP: 8.1 Symfony: 5.4 API Platform: 3.0.0-beta.1
I have something similar as https://github.com/api-platform/core/issues/4613#issuecomment-1174770854 but changing order in XML configuration does not work for me.
@dannyvw can you share your XML configuration please?
@vincentchalamon It crashed on this file (I removed all context configurations) but i have similar files which are working correct. So it is a bit strange.. Everything works fine in the last 2.7 beta.
<?xml version="1.0" ?>
<resources xmlns="https://api-platform.com/schema/metadata/resources-3.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://api-platform.com/schema/metadata/resources-3.0
https://api-platform.com/schema/metadata/resources.xsd">
<resource class="App\Entity\User" shortName="User">
<operations>
<operation class="ApiPlatform\Metadata\GetCollection" />
<operation class="ApiPlatform\Metadata\Get" />
<operation class="ApiPlatform\Metadata\Post" />
<operation class="ApiPlatform\Metadata\Put" />
<operation class="ApiPlatform\Metadata\Delete" processor="App\State\UserDeleteProcessor" />
</operations>
</resource>
</resources>
With only the delete operation it does not work either. Also not when removing the processor configuration. If i only remove the delete operation in the above configuration it works fine.
<?xml version="1.0" ?>
<resources xmlns="https://api-platform.com/schema/metadata/resources-3.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://api-platform.com/schema/metadata/resources-3.0
https://api-platform.com/schema/metadata/resources.xsd">
<resource class="App\Entity\User" shortName="User">
<operations>
<operation class="ApiPlatform\Metadata\Delete" processor="App\State\UserDeleteProcessor" />
</operations>
</resource>
</resources>
I will try to do some more debugging.
Hello, No news for my comment : https://github.com/api-platform/core/issues/4613#issuecomment-1168654277. Can i help or more explain something ?
Hello @Zul3s,
I cannot reproduce your issue. I've juste created a project with a Book entity with a single name
property, using the following dependencies:
Can you create a GitHub project to reproduce your bug, please? Also, do you still have the issue with API Platform 2.7-beta.4?
Hello @vincentchalamon , Sure, here it is : https://github.com/Zul3s/api-platform_2.7.-_demo In my first comment, i ommit to say parameter metadata backward compatibility layer must be set at false. You can show on my repo, the commit who established the problem.
@Zul3s Thanks for reporting this issue. A fix has been started here: https://github.com/api-platform/core/pull/4828 I tried it locally, and it seems to solve your issue. Can you try this fix locally too, and confirm me it's ok for you?
@Zul3s wrong fix, I'm trying to find a better solution. Wait before testing locally, please
@Zul3s Fix is ready here: https://github.com/api-platform/core/pull/4828 (just pick the first commit) Feel free to try it locally :-)
API Platform version(s) affected: 2.7-beta.4 SF 6.1 PHP 8.1
Description
While trying to receive a file using a State Provider, api platform returns the api docs instead of the file.
How to reproduce
Install Vich Uploader like here: https://api-platform.com/docs/main/core/file-upload/
Use FlySystem as Storage
Add this Get operation to the MediaObject
new Get(provider: MediaObjectProvider::class, uriTemplate: '/media/{id}'),
with:
use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProviderInterface;
use App\Repository\MediaObjectRepository;
use Symfony\Component\HttpFoundation\StreamedResponse;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Uid\UuidV6;
use Vich\UploaderBundle\Storage\StorageInterface;
class MediaObjectProvider implements ProviderInterface
{
public function __construct(private StorageInterface $storage, private MediaObjectRepository $mediaObjectRepository)
{
}
public function provide(Operation $operation, array $uriVariables = [], array $context = [])
{
$media = $this->mediaObjectRepository->find(UuidV6::fromString($uriVariables['id']));
if (!$media || !$stream = $this->storage->resolveStream($media, 'file')) {
throw new NotFoundHttpException('Media not found');
}
$streamResponse = new StreamedResponse(static function () use ($stream) {
\stream_copy_to_stream($stream, \fopen('php://output', 'wb'));
});
return $streamResponse;
}
}
as provider.
When you then try to access the file via /media/
Possible Solution
I assume it's related to the text/html
content header you get when trying to access that via browser. Some way to ignore content headers and always serve a certain content would be neat for cases like that.
Write here as comment the errors you may found on api-platform/core:2.7.x-dev installed on your project. Please mention: