Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Swagger] Undefined array key "$ref" since 3.1.6 #5501

Closed
DeLm0re opened this issue Mar 27, 2023 · 19 comments
Closed

[Swagger] Undefined array key "$ref" since 3.1.6 #5501

DeLm0re opened this issue Mar 27, 2023 · 19 comments

Comments

@DeLm0re
Copy link

DeLm0re commented Mar 27, 2023

API Platform version(s) affected: 3.1.6

Description

Since this new version, it is impossible to acces my swagger documentation via /api

For instance for a simple entrypoint like this one

new Metadata\GetCollection(
    routeName: 'api_get_user',
    openapi: new Model\Operation(
        responses: [
            Response::HTTP_UNAUTHORIZED => new Model\Response(ResponseMessage::UNAUTHORIZED),
        ],
        parameters: [
            new Model\Parameter(
                name: 'id',
                in: 'query',
                description: 'The User identifier',
                schema: ['type' => 'object'],
                style: 'deepObject',
                explode: true,
            ),
            new Model\Parameter(
                name: 'name|lastname|username',
                in: 'query',
                description: 'The User denomination',
                schema: ['type' => 'object'],
                style: 'deepObject',
                explode: true,
            ),
        ],
    ),
),

causes a 500 error

Capture d’écran 2023-03-27 à 18 12 04

Additional Context

I'm working on MacOs, Php 8.1, Symfony 6.2.7, doctrine-bundle 2.9.
Everything is working fine on 3.1.5

@twisted1919
Copy link

I get the same error on a decorated service, i.e:

final readonly class JwtDecorator implements OpenApiFactoryInterface
{
    public function __construct(
        private OpenApiFactoryInterface $decorated,
        private UrlGeneratorInterface $router
    ) {
    }

    public function __invoke(array $context = []): OpenApi
    {
        $openApi = ($this->decorated)($context); // <- This triggers the error.
    ...

@cyrilverloop
Copy link

Hi, same problem since 3.1.6 with PHP 8.2.4 and Symfony 6.2.7.

@hurtreljulie
Copy link

Same problem here

@j-schumann
Copy link

Same problem here. This happens only for some schemas as far as I can tell, they are created in
L155 of the TypeFactory

$subSchema = $this->schemaFactory->buildSchema($className, $format, Schema::TYPE_OUTPUT, null, $subSchema, $serializerContext, false);

without the $ref: https://i.imgur.com/0xVVun6.png

Maybe related to Resources that have no (exposed) endpoints and thus no definition (yet)?
E.g. it happens for me with embeded Images that are annotated with:

#[ApiResource(
    types: 'https://schema.org/MediaObject',
    operations: [
        new NotExposed(),
    ],
)]

Also: I'm very suprised about the introduced BC break with that patch version: ApiPlatform\OpenApi\Factory\OpenApiFactory::OPENAPI_DEFINITION_NAME no longer exists (but is moved to the SchemaFactory) and causes runtime errors in my code.
Shouldn't this be marked deprecated until the next major and refer to the SchemaFactory::OPENAPI_DEFINITION_NAME for the time being?
I use this constant in my custom ResourceMetadataCollectionFactory wrapper, and it was not marked "internal" before (and is not now) .

@cyrilverloop
Copy link

The problem exist in "dev" environment and not in "prod".

@benjaminmal
Copy link

I can confirm downgrading to 3.1.5 (PHP 8.1.11, Symfony 6.2.7) fix the problem.

@soyuka
Copy link
Member

soyuka commented Mar 30, 2023

Hi, not sure from where the problem occurs:
e471622 is the change we made to the Schema.

Can someone check if your use case breaks after introducing this commit? Also please send me a full stack trace!

Also: I'm very suprised about the introduced BC break with that patch version: ApiPlatform\OpenApi\Factory\OpenApiFactory::OPENAPI_DEFINITION_NAME no longer exists (but is moved to the SchemaFactory) and causes runtime errors in my code.

My fault, I'll reintroduce this I shouldn't have removed it.

@DeLm0re
Copy link
Author

DeLm0re commented Mar 30, 2023

Here is the stack trace I have for my part

Warning: Undefined array key "$ref"

#0 /vendor/api-platform/core/src/JsonSchema/TypeFactory.php(75): ApiPlatform\JsonSchema\TypeFactory->getClassType('App\\Entity\\Acti...', true, 'json', true, Array, Object(ApiPlatform\JsonSchema\Schema))
#1 /vendor/api-platform/core/src/JsonSchema/TypeFactory.php(66): ApiPlatform\JsonSchema\TypeFactory->makeBasicType(Object(Symfony\Component\PropertyInfo\Type), 'json', true, Array, Object(ApiPlatform\JsonSchema\Schema))
#2 /vendor/api-platform/core/src/JsonSchema/SchemaFactory.php(216): ApiPlatform\JsonSchema\TypeFactory->getType(Object(Symfony\Component\PropertyInfo\Type), 'json', true, Array, Object(ApiPlatform\JsonSchema\Schema))
#3 /vendor/api-platform/core/src/JsonSchema/SchemaFactory.php(136): ApiPlatform\JsonSchema\SchemaFactory->buildPropertySchema(Object(ApiPlatform\JsonSchema\Schema), 'ActivitySector-...', 'mainActivitySec...', Object(ApiPlatform\Metadata\ApiProperty), Array, 'json')
#4 /vendor/api-platform/core/src/Hydra/JsonSchema/SchemaFactory.php(70): ApiPlatform\JsonSchema\SchemaFactory->buildSchema('App\\Entity\\Acti...', 'json', 'output', Object(ApiPlatform\Metadata\GetCollection), Object(ApiPlatform\JsonSchema\Schema), Array, true)
#5 /vendor/api-platform/core/src/OpenApi/Factory/OpenApiFactory.php(237): ApiPlatform\Hydra\JsonSchema\SchemaFactory->buildSchema('App\\Entity\\Acti...', 'json', 'output', Object(ApiPlatform\Metadata\GetCollection), Object(ApiPlatform\JsonSchema\Schema), NULL, true)
#6 /vendor/api-platform/core/src/OpenApi/Factory/OpenApiFactory.php(91): ApiPlatform\OpenApi\Factory\OpenApiFactory->collectPaths(Object(Linkweb\Annotation\ApiResource), Object(ApiPlatform\Metadata\Resource\ResourceMetadataCollection), Object(ApiPlatform\OpenApi\Model\Paths), Object(ArrayObject))
#7 /vendor/api-platform/core/src/Symfony/Bundle/SwaggerUi/SwaggerUiAction.php(45): ApiPlatform\OpenApi\Factory\OpenApiFactory->__invoke(Array)
#8 /vendor/symfony/http-kernel/HttpKernel.php(163): ApiPlatform\Symfony\Bundle\SwaggerUi\SwaggerUiAction->__invoke(Object(Symfony\Component\HttpFoundation\Request))
#9 /vendor/symfony/http-kernel/HttpKernel.php(74): Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object(Symfony\Component\HttpFoundation\Request), 1)
#10 /vendor/symfony/http-kernel/Kernel.php(184): Symfony\Component\HttpKernel\HttpKernel->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#11 /vendor/symfony/runtime/Runner/Symfony/HttpKernelRunner.php(35): Symfony\Component\HttpKernel\Kernel->handle(Object(Symfony\Component\HttpFoundation\Request))
#12 /vendor/autoload_runtime.php(29): Symfony\Component\Runtime\Runner\Symfony\HttpKernelRunner->run()
#13 /public/index.php(5): require_once('/Users/dev/Docu...')
#14 {main}

@soyuka
Copy link
Member

soyuka commented Mar 30, 2023

Is the patch e471622 the problem? I'd also like the resources you use or a way to reproduce to fix this asap

@paullallier
Copy link
Contributor

paullallier commented Mar 30, 2023

@soyuka: I'm virtually certain it's that patch, but I'm in the middle of a code change and can't easier test it (I don't trust my git shelve changes skills...), but I checked before and I had the problem with 3.1.6 and not when I rolled back to 3.1.5.

I suspect it's not the way that most people triggered the issue, but I was doing this:
api-platform/docs#1697

If you replace the ApiResource attribute on any Entity with #[ApiResource(operations: [])] and reload the swagger UI, you should see the bug.

I suspect declaring an Entity with only GetCollection operations would also show it - and be more realistic.

@DeLm0re
Copy link
Author

DeLm0re commented Mar 30, 2023

@soyuka A simple resource having #[ApiResource(operations: [])] is NOT enought for the bug to appear.

The simplest entity doc I tried to create which causes the bug looks like this

#[ApiResource(
    operations: [
      new Metadata\GetCollection(
          routeName: 'api_get_activitySector',
          openapi: new Model\Operation(
              responses: [
                  Response::HTTP_UNAUTHORIZED => new Model\Response(ResponseMessage::UNAUTHORIZED),
              ],
          ),
      ),
      formats: ['json'],
      normalizationContext: ['groups' => 'activitySector-read', 'swagger_definition_name' => 'Read'],
      denormalizationContext: ['groups' => 'activitySector-write', 'swagger_definition_name' => 'Write'],
    ],
)]

Whatever you put inside your entity does not matter. Maybe if you want to test it on a dummy API, you should put an id & a name. It should be enought.

Also notice that - and as @cyrilverloop pointed out - I can confirm that the bug only appears in dev environment.

@DeLm0re
Copy link
Author

DeLm0re commented Mar 30, 2023

@soyuka After some digging & xdebug, it seems that the error occurs here

$version = $schema->getVersion();
$subSchema = new Schema($version);
$subSchema->setDefinitions($schema->getDefinitions()); // Populate definitions of the main schema
if (null === $this->schemaFactory) {
throw new \LogicException('The schema factory must be injected by calling the "setSchemaFactory" method.');
}
$subSchema = $this->schemaFactory->buildSchema($className, $format, Schema::TYPE_OUTPUT, null, $subSchema, $serializerContext, false);
return ['$ref' => $subSchema['$ref']];

Because $subSchema is not an array at the return point but a ApiPlatform\JsonSchema\Schema (a PHP \ArrayObject) or at least the code can not access the index $ref

I would suggest something like this instead

return ['$ref' => $subSchema->offsetExists('$ref') ? $subSchema->offsetGet('$ref') : null];

@twisted1919
Copy link

@DeLm0re / @soyuka - the above is my case as well, I had to temporary modify the code to return:

return ['$ref' => $subSchema['$ref'] ?? '']; 

Only to be able to continue with my daily development tasks.

@paullallier
Copy link
Contributor

paullallier commented Mar 30, 2023

A simple resource having #[ApiResource(operations: [])] is NOT enought for the bug to appear.

Odd. It definitely triggers it for me, but maybe we have something else different in our setups. Your example is definitely a more realistic one though.

@soyuka
Copy link
Member

soyuka commented Mar 31, 2023

/edit: patch is no good

Also can someone with the problem ping me on the Symfony slack? I still can't reproduce a failing test case and it's hard for me to find the correct fix. Just using an empty $ref is not good enough, we should find that $ref.

@Andreas-Sommer
Copy link

Andreas-Sommer commented Mar 31, 2023

A simple resource having #[ApiResource(operations: [])] is NOT enought for the bug to appear.

Odd. It definitely triggers it for me, but maybe we have something else different in our setups. Your example is definitely a more realistic one though.

Is there a better way removing operations?
I am using some API resources as a subresource without any operations.

@soyuka
Copy link
Member

soyuka commented Mar 31, 2023

I've a reproducer thanks to @paullallier, trying to release a patch by the end of the day!

soyuka added a commit to soyuka/core that referenced this issue Mar 31, 2023
fixes api-platform#5501

The locations relation inside BrokenDocs is a Resource (named Related) but its only operation is a NotExposed. Still, serializer groups are set, and therefore it is a "readableLink" so we actually want to compute the schema, even if it's not accessible directly, it is accessible through that relation.
soyuka added a commit to soyuka/core that referenced this issue Mar 31, 2023
fixes api-platform#5501

The locations relation inside BrokenDocs is a Resource (named Related) but its only operation is a NotExposed. Still, serializer groups are set, and therefore it is a "readableLink" so we actually want to compute the schema, even if it's not accessible directly, it is accessible through that relation.
soyuka added a commit that referenced this issue Mar 31, 2023
fixes #5501

The locations relation inside BrokenDocs is a Resource (named Related) but its only operation is a NotExposed. Still, serializer groups are set, and therefore it is a "readableLink" so we actually want to compute the schema, even if it's not accessible directly, it is accessible through that relation.
@soyuka soyuka closed this as completed Mar 31, 2023
@soyuka
Copy link
Member

soyuka commented Mar 31, 2023

expect a release in the hour

@cyrilverloop
Copy link

3.1.7 seems to fix the problem for me. Thanks !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

9 participants