Core: There is no PropertyInfo extractor supporting the class "DateInterval".

Created on 13 Jan 2020  路  14Comments  路  Source: api-platform/core

API Platform version(s) affected: 2.5.4

Description
When I upgrade api-platform/core 2.5.3 to 2.5.4 I have the following problem on api docs: "There is no PropertyInfo extractor supporting the class "DateInterval"."

How to reproduce
I think we just have to put a property with a DateInterval in an exposed entity and it will reproduce the problem.

Possible Solution
No idea

Additional Context

ApiPlatform\Core\Exception\RuntimeException:
There is no PropertyInfo extractor supporting the class "DateInterval".

  at vendor/api-platform/core/src/Bridge/Symfony/PropertyInfo/Metadata/Property/PropertyInfoPropertyNameCollectionFactory.php:46
  at ApiPlatform\Core\Bridge\Symfony\PropertyInfo\Metadata\Property\PropertyInfoPropertyNameCollectionFactory->create('DateInterval', array())
     (vendor/api-platform/core/src/Metadata/Property/Factory/InheritedPropertyNameCollectionFactory.php:44)
  at ApiPlatform\Core\Metadata\Property\Factory\InheritedPropertyNameCollectionFactory->create('DateInterval', array())
     (vendor/api-platform/core/src/Metadata/Property/Factory/ExtractorPropertyNameCollectionFactory.php:50)
  at ApiPlatform\Core\Metadata\Property\Factory\ExtractorPropertyNameCollectionFactory->create('DateInterval', array())
     (vendor/api-platform/core/src/Metadata/Property/Factory/ExtractorPropertyNameCollectionFactory.php:50)
  at ApiPlatform\Core\Metadata\Property\Factory\ExtractorPropertyNameCollectionFactory->create('DateInterval', array())
     (vendor/api-platform/core/src/Metadata/Property/Factory/CachedPropertyNameCollectionFactory.php:47)
  at ApiPlatform\Core\Metadata\Property\Factory\CachedPropertyNameCollectionFactory->ApiPlatform\Core\Metadata\Property\Factory\{closure}()
     (vendor/api-platform/core/src/Cache/CachedTrait.php:44)
  at ApiPlatform\Core\Metadata\Property\Factory\CachedPropertyNameCollectionFactory->getCached('property_name_collection_3bf8de46e9d114c9746a5da4376c201b', object(Closure))
     (vendor/api-platform/core/src/Metadata/Property/Factory/CachedPropertyNameCollectionFactory.php:48)
  at ApiPlatform\Core\Metadata\Property\Factory\CachedPropertyNameCollectionFactory->create('DateInterval', array())
     (vendor/api-platform/core/src/JsonSchema/SchemaFactory.php:135)
  at ApiPlatform\Core\JsonSchema\SchemaFactory->buildSchema('DateInterval', 'json', 'output', null, null, object(Schema), array(), false)
     (vendor/api-platform/core/src/Hydra/JsonSchema/SchemaFactory.php:52)
  at ApiPlatform\Core\Hydra\JsonSchema\SchemaFactory->buildSchema('DateInterval', 'json', 'output', null, null, object(Schema), array())
     (vendor/api-platform/core/src/JsonSchema/TypeFactory.php:118)
  at ApiPlatform\Core\JsonSchema\TypeFactory->getClassType('DateInterval', 'json', null, array(), object(Schema))
     (vendor/api-platform/core/src/JsonSchema/TypeFactory.php:69)
  at ApiPlatform\Core\JsonSchema\TypeFactory->getType(object(Type), 'json', null, array(), object(Schema))
     (vendor/api-platform/core/src/JsonSchema/SchemaFactory.php:199)
  at ApiPlatform\Core\JsonSchema\SchemaFactory->buildPropertySchema(object(Schema), 'Warehouse', 'operationTime', object(PropertyMetadata), array(), 'json')
     (vendor/api-platform/core/src/JsonSchema/SchemaFactory.php:146)
  at ApiPlatform\Core\JsonSchema\SchemaFactory->buildSchema('App\\Domain\\TourBuilding\\Model\\Entity\\Warehouse\\Warehouse', 'json', 'output', 'item', 'get', object(Schema), array(), false)
     (vendor/api-platform/core/src/Hydra/JsonSchema/SchemaFactory.php:52)
  at ApiPlatform\Core\Hydra\JsonSchema\SchemaFactory->buildSchema('App\\Domain\\TourBuilding\\Model\\Entity\\Warehouse\\Warehouse', 'json', 'output', 'item', 'get', object(Schema), null, false)
     (vendor/api-platform/core/src/Swagger/Serializer/DocumentationNormalizer.php:597)
  at ApiPlatform\Core\Swagger\Serializer\DocumentationNormalizer->getJsonSchema(false, object(ArrayObject), 'App\\Domain\\TourBuilding\\Model\\Entity\\Warehouse\\Warehouse', 'output', 'item', 'get', 'json', null, false)
     (vendor/api-platform/core/src/Swagger/Serializer/DocumentationNormalizer.php:294)
  at ApiPlatform\Core\Swagger\Serializer\DocumentationNormalizer->addSchemas(false, array('description' => 'Warehouse resource response'), object(ArrayObject), 'App\\Domain\\TourBuilding\\Model\\Entity\\Warehouse\\Warehouse', 'item', 'get', array('application/ld+json' => 'jsonld', 'application/json' => 'json', 'text/html' => 'html'))
     (vendor/api-platform/core/src/Swagger/Serializer/DocumentationNormalizer.php:343)
  at ApiPlatform\Core\Swagger\Serializer\DocumentationNormalizer->updateGetOperation(false, object(ArrayObject), array('application/ld+json' => 'jsonld', 'application/json' => 'json', 'text/html' => 'html'), 'item', object(ResourceMetadata), 'App\\Domain\\TourBuilding\\Model\\Entity\\Warehouse\\Warehouse', 'Warehouse', 'get', object(ArrayObject))
     (vendor/api-platform/core/src/Swagger/Serializer/DocumentationNormalizer.php:273)
  at ApiPlatform\Core\Swagger\Serializer\DocumentationNormalizer->getPathOperation(false, 'get', array('method' => 'GET', 'input_formats' => array('jsonld' => array('application/ld+json'), 'json' => array('application/json'), 'html' => array('text/html')), 'output_formats' => array('jsonld' => array('application/ld+json'), 'json' => array('application/json'), 'html' => array('text/html'))), 'GET', 'item', 'App\\Domain\\TourBuilding\\Model\\Entity\\Warehouse\\Warehouse', object(ResourceMetadata), object(ArrayObject), object(ArrayObject))
     (vendor/api-platform/core/src/Swagger/Serializer/DocumentationNormalizer.php:222)
  at ApiPlatform\Core\Swagger\Serializer\DocumentationNormalizer->addPaths(false, object(ArrayObject), object(ArrayObject), 'App\\Domain\\TourBuilding\\Model\\Entity\\Warehouse\\Warehouse', 'Warehouse', object(ResourceMetadata), 'item', object(ArrayObject))
     (vendor/api-platform/core/src/Swagger/Serializer/DocumentationNormalizer.php:187)
  at ApiPlatform\Core\Swagger\Serializer\DocumentationNormalizer->normalize(object(Documentation), 'json', array('spec_version' => 2))
     (vendor/api-platform/core/src/Swagger/Serializer/ApiGatewayNormalizer.php:51)
  at ApiPlatform\Core\Swagger\Serializer\ApiGatewayNormalizer->normalize(object(Documentation), 'json', array('spec_version' => 2))
     (vendor/symfony/serializer/Serializer.php:152)
  at Symfony\Component\Serializer\Serializer->normalize(object(Documentation), 'json', array('spec_version' => 2))
     (vendor/api-platform/core/src/Bridge/Symfony/Bundle/Action/SwaggerUiAction.php:142)
  at ApiPlatform\Core\Bridge\Symfony\Bundle\Action\SwaggerUiAction->getContext(object(Request), object(Documentation))
     (vendor/api-platform/core/src/Bridge/Symfony/Bundle/Action/SwaggerUiAction.php:116)
  at ApiPlatform\Core\Bridge\Symfony\Bundle\Action\SwaggerUiAction->__invoke(object(Request))
     (vendor/symfony/http-kernel/HttpKernel.php:146)
  at Symfony\Component\HttpKernel\HttpKernel->handleRaw(object(Request), 1)
     (vendor/symfony/http-kernel/HttpKernel.php:68)
  at Symfony\Component\HttpKernel\HttpKernel->handle(object(Request), 1, true)
     (vendor/symfony/http-kernel/Kernel.php:201)
  at Symfony\Component\HttpKernel\Kernel->handle(object(Request))
     (public/index.php:27)
bug unconfirmed

All 14 comments

Are you sure you imported \DateInterval?

I'm sure ^^, I test my app with multiple things like phpstan lvl 7 with some strict rules, I have some phpunit tests and behat test to validate that every things work ^^, and to be sure to have what I need in attributes I use php7.4 new features.

One exmple of code where I use DateInterval

<?php

declare(strict_types=1);

namespace App\Entity;

use App\Entity\ValueObject\Capacity;
use App\Entity\ValueObject\GeoAddress;
use App\Enum\StopTypeEnum;
use App\Enum\VehicleProfileEnum;
use DateInterval;
use Ramsey\Uuid\Uuid;

class Stop
{
    private string $id;
    private string $refId;
    private string $type;
    private ?string $contact = null;
    private GeoAddress $geoAddress;
    private array $restriction = VehicleProfileEnum::LIST;
    private DateInterval $operationTime;
    private Order $order;

    public function __construct(?string $id, string $refId, Order $order, string $type, GeoAddress $geoAddress, DateInterval $operationTime, iterable $capacities)
    {
        $this->id = null === $id ? Uuid::uuid4()->toString() : $id;
        $this->refId = $refId;
        $this->order = $order;
        $this->setType($type);
        $this->setGeoAddress($geoAddress);
        $this->setOperationTime($operationTime);
        $this->setCapacities($capacities);
    }
...
}

Ok, I found some new informations.
What it doesn't work it's when I set doctrine type to dateinterval:

    <field name="operationTime" type="dateinterval"/> 

Another thing it's when I write type as nullable with "?":

    private ?DateInterval $operationTime;

It can help you ?

~I suppose you have already written a (de)normalizer for DateInterval for converting to/from ISO 8601 duration?~

EDIT: Sorry, didn't realize there's already one in Symfony: https://github.com/symfony/symfony/blob/v5.0.4/src/Symfony/Component/Serializer/Normalizer/DateIntervalNormalizer.php

FYI, Same issue with \Iterator, all related to the new way to handle the Swagger doc in 2.5.4.

Oops...

@bastnic Do you think a whitelist (for classes like \DateInterval) + blacklist (for classes like \Iterator) would solve the issue?

I'm not sure about the actual algorithm to parse everything. This is a public method on an entity that i absolutly don't want to expose, before it was not even checked, not it's checked and it fails on it. Is it possible to add a try catch and then ignore resource that fails?

before it was not even checked,

Are you sure it's really not exposed? Previously that property should have been erroneously documented as type "string".

The relevant code that has been changed is in this method: https://github.com/api-platform/core/blob/21145c7669bf2383fb370f7a05a67149ccd21fc3/src/JsonSchema/TypeFactory.php#L78

@soyuka ~No, it's not the same as #3349. I know what this bug is. (I've introduced it lol...)~

We had a chat and it seems it's #3349 after all, not a problem in my code...

But we need special handling for \DateInterval anyway. Same for \Iterator (though I'm not sure what's the right thing to do for this one...)

I don't want to create another issue if it is related, unless you tell me to,

I'm facing a similar issue with the message:
"There is no PropertyInfo extractor supporting the class "App\Message\MessageInterface". As you can imagine, the MessageInterface is an interface.
I have an existing project, and I want to expose the ConversationMessage entity as an ApiResource:

/** @ApiResource **/
class ConversationMessage
{
    /** @var User **/
    private $user;
}

class User
{
    /** @var SomeClass **/
    private $someProperty;
}

class SomeClass
{
    public function getBusMessage(): ?MessageInterface
    {
    }
}

interface MessageInterface
{
}

There is not property related to the getBusMessage method and the App\Message namespace is excluded from the services autowiring.

I don't understand why the interface is analyzed. I'm stuck at the docs.json access...

thx

I don't want to create another issue if it is related, unless you tell me to,

I'm facing a similar issue with the message:
"There is no PropertyInfo extractor supporting the class "App\Message\MessageInterface". As you can imagine, the MessageInterface is an interface.
I have an existing project, and I want to expose the ConversationMessage entity as an ApiResource:

/** @ApiResource **/
class ConversationMessage
{
    /** @var User **/
    private $user;
}

class User
{
    /** @var SomeClass **/
    private $someProperty;
}

class SomeClass
{
    public function getBusMessage(): ?MessageInterface
    {
    }
}

interface MessageInterface
{
}

There is not property related to the getBusMessage method and the App\Message namespace is excluded from the services autowiring.

I don't understand why the interface is analyzed. I'm stuck at the docs.json access...

thx

In the meantime:

  1. I excluded properties from the ConversationMessage class using this approch: https://github.com/api-platform/core/issues/1120#issuecomment-410235533
  2. THEN I implemented DTO for good! Which is well suited for me and really great!
Was this page helpful?
0 / 5 - 0 ratings

Related issues

stipic picture stipic  路  3Comments

breitsmiley picture breitsmiley  路  3Comments

vViktorPL picture vViktorPL  路  3Comments

Tjeerd picture Tjeerd  路  3Comments

CvekCoding picture CvekCoding  路  3Comments