Skip to content

Uupdate api-platform/core 3.2.6 error #5998

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

Closed
Napalm4577 opened this issue Nov 27, 2023 · 12 comments
Closed

Uupdate api-platform/core 3.2.6 error #5998

Napalm4577 opened this issue Nov 27, 2023 · 12 comments

Comments

@Napalm4577
Copy link

API Platform version(s) affected: 3.2.6

Description

I have a project on Symfony 6.3. I updated with composer, the library "api-platform/core" from 3.2.5 to 3.2.6 and I get error "'Warning: Undefined array key '$ref'" when I try to display the page /api

How to reproduce

Update library from 3.2.5 to 3.2.6 with composer

Possible Solution

Additional Context

{
  "@context": "\/api\/contexts\/Error",
  "@type": "hydra:Error",
  "hydra:title": "An error occurred",
  "hydra:description": "Warning: Undefined array key \u0022$ref\u0022",
  "trace": [
    {
      "namespace": "",
      "short_class": "",
      "class": "",
      "type": "",
      "function": "",
      "file": "\/var\/www\/isia-core\/vendor\/api-platform\/core\/src\/JsonSchema\/SchemaFactory.php",
      "line": 227,
      "args": []
    },
    {
      "namespace": "ApiPlatform\\JsonSchema",
      "short_class": "SchemaFactory",
      "class": "ApiPlatform\\JsonSchema\\SchemaFactory",
      "type": "-\u003E",
      "function": "buildPropertySchema",
      "file": "\/var\/www\/isia-core\/vendor\/api-platform\/core\/src\/JsonSchema\/SchemaFactory.php",
      "line": 142,
      "args": []
    },
    {
      "namespace": "ApiPlatform\\JsonSchema",
      "short_class": "SchemaFactory",
      "class": "ApiPlatform\\JsonSchema\\SchemaFactory",
      "type": "-\u003E",
      "function": "buildSchema",
      "file": "\/var\/www\/isia-core\/vendor\/api-platform\/core\/src\/Hydra\/JsonSchema\/SchemaFactory.php",
      "line": 70,
      "args": []
    },
    {
      "namespace": "ApiPlatform\\Hydra\\JsonSchema",
      "short_class": "SchemaFactory",
      "class": "ApiPlatform\\Hydra\\JsonSchema\\SchemaFactory",
      "type": "-\u003E",
      "function": "buildSchema",
      "file": "\/var\/www\/isia-core\/vendor\/api-platform\/core\/src\/OpenApi\/Factory\/OpenApiFactory.php",
      "line": 353,
      "args": []
    },
    {
      "namespace": "ApiPlatform\\OpenApi\\Factory",
      "short_class": "OpenApiFactory",
      "class": "ApiPlatform\\OpenApi\\Factory\\OpenApiFactory",
      "type": "-\u003E",
      "function": "collectPaths",
      "file": "\/var\/www\/isia-core\/vendor\/api-platform\/core\/src\/OpenApi\/Factory\/OpenApiFactory.php",
      "line": 97,
      "args": []
    },
    {
      "namespace": "ApiPlatform\\OpenApi\\Factory",
      "short_class": "OpenApiFactory",
      "class": "ApiPlatform\\OpenApi\\Factory\\OpenApiFactory",
      "type": "-\u003E",
      "function": "__invoke",
      "file": "\/var\/www\/isia-core\/vendor\/api-platform\/core\/src\/Symfony\/Bundle\/SwaggerUi\/SwaggerUiProvider.php",
      "line": 75,
      "args": []
    },
    {
      "namespace": "ApiPlatform\\Symfony\\Bundle\\SwaggerUi",
      "short_class": "SwaggerUiProvider",
      "class": "ApiPlatform\\Symfony\\Bundle\\SwaggerUi\\SwaggerUiProvider",
      "type": "-\u003E",
      "function": "provide",
      "file": "\/var\/www\/isia-core\/vendor\/api-platform\/core\/src\/Symfony\/Security\/State\/AccessCheckerProvider.php",
      "line": 53,
      "args": []
    },
    {
      "namespace": "ApiPlatform\\Symfony\\Security\\State",
      "short_class": "AccessCheckerProvider",
      "class": "ApiPlatform\\Symfony\\Security\\State\\AccessCheckerProvider",
      "type": "-\u003E",
      "function": "provide",
      "file": "\/var\/www\/isia-core\/vendor\/api-platform\/core\/src\/State\/Provider\/DeserializeProvider.php",
      "line": 47,
      "args": []
    },
    {
      "namespace": "ApiPlatform\\State\\Provider",
      "short_class": "DeserializeProvider",
      "class": "ApiPlatform\\State\\Provider\\DeserializeProvider",
      "type": "-\u003E",
      "function": "provide",
      "file": "\/var\/www\/isia-core\/vendor\/api-platform\/core\/src\/Symfony\/Security\/State\/AccessCheckerProvider.php",
      "line": 53,
      "args": []
    },
    {
      "namespace": "ApiPlatform\\Symfony\\Security\\State",
      "short_class": "AccessCheckerProvider",
      "class": "ApiPlatform\\Symfony\\Security\\State\\AccessCheckerProvider",
      "type": "-\u003E",
      "function": "provide",
      "file": "\/var\/www\/isia-core\/vendor\/api-platform\/core\/src\/Symfony\/Validator\/State\/QueryParameterValidateProvider.php",
      "line": 42,
      "args": []
    },
    {
      "namespace": "ApiPlatform\\Symfony\\Validator\\State",
      "short_class": "QueryParameterValidateProvider",
      "class": "ApiPlatform\\Symfony\\Validator\\State\\QueryParameterValidateProvider",
      "type": "-\u003E",
      "function": "provide",
      "file": "\/var\/www\/isia-core\/vendor\/api-platform\/core\/src\/Symfony\/Validator\/State\/ValidateProvider.php",
      "line": 32,
      "args": []
    },
    {
      "namespace": "ApiPlatform\\Symfony\\Validator\\State",
      "short_class": "ValidateProvider",
      "class": "ApiPlatform\\Symfony\\Validator\\State\\ValidateProvider",
      "type": "-\u003E",
      "function": "provide",
      "file": "\/var\/www\/isia-core\/vendor\/api-platform\/core\/src\/Symfony\/Security\/State\/AccessCheckerProvider.php",
      "line": 53,
      "args": []
    },
    {
      "namespace": "ApiPlatform\\Symfony\\Security\\State",
      "short_class": "AccessCheckerProvider",
      "class": "ApiPlatform\\Symfony\\Security\\State\\AccessCheckerProvider",
      "type": "-\u003E",
      "function": "provide",
      "file": "\/var\/www\/isia-core\/vendor\/api-platform\/core\/src\/State\/Provider\/ContentNegotiationProvider.php",
      "line": 56,
      "args": []
    },
    {
      "namespace": "ApiPlatform\\State\\Provider",
      "short_class": "ContentNegotiationProvider",
      "class": "ApiPlatform\\State\\Provider\\ContentNegotiationProvider",
      "type": "-\u003E",
      "function": "provide",
      "file": "\/var\/www\/isia-core\/vendor\/api-platform\/core\/src\/Documentation\/Action\/EntrypointAction.php",
      "line": 48,
      "args": []
    },
    {
      "namespace": "ApiPlatform\\Documentation\\Action",
      "short_class": "EntrypointAction",
      "class": "ApiPlatform\\Documentation\\Action\\EntrypointAction",
      "type": "-\u003E",
      "function": "__invoke",
      "file": "\/var\/www\/isia-core\/vendor\/symfony\/http-kernel\/HttpKernel.php",
      "line": 181,
      "args": []
    },
    {
      "namespace": "Symfony\\Component\\HttpKernel",
      "short_class": "HttpKernel",
      "class": "Symfony\\Component\\HttpKernel\\HttpKernel",
      "type": "-\u003E",
      "function": "handleRaw",
      "file": "\/var\/www\/isia-core\/vendor\/symfony\/http-kernel\/HttpKernel.php",
      "line": 76,
      "args": []
    },
    {
      "namespace": "Symfony\\Component\\HttpKernel",
      "short_class": "HttpKernel",
      "class": "Symfony\\Component\\HttpKernel\\HttpKernel",
      "type": "-\u003E",
      "function": "handle",
      "file": "\/var\/www\/isia-core\/vendor\/symfony\/http-kernel\/Kernel.php",
      "line": 197,
      "args": []
    },
    {
      "namespace": "Symfony\\Component\\HttpKernel",
      "short_class": "Kernel",
      "class": "Symfony\\Component\\HttpKernel\\Kernel",
      "type": "-\u003E",
      "function": "handle",
      "file": "\/var\/www\/isia-core\/public\/app_dev.php",
      "line": 28,
      "args": []
    }
  ]
}

Thanks guys

@soyuka soyuka transferred this issue from api-platform/api-platform Nov 27, 2023
@soyuka
Copy link
Member

soyuka commented Nov 27, 2023

I'd like to reproduce this, can you share your resources?

soyuka added a commit to soyuka/core that referenced this issue Nov 27, 2023
@Napalm4577
Copy link
Author

Hello @soyuka,

What do you mean by "Resources" ?

@soyuka
Copy link
Member

soyuka commented Nov 27, 2023

the classes you have ApiResource on that trigger this issue.

@ihmels
Copy link
Contributor

ihmels commented Nov 27, 2023

The error is caused by PR #5989. Previously, the method SchemaFactory::buildSchema() was always passed Schema::TYPE_OUTPUT as $type if it was called from the method SchemaFactory::buildPropertySchema(). Now the $parentType, which has the value Schema::TYPE_INPUT during schema generation, is always used. In SchemaFactory, line 86, this always leads to a return.

Simple example:

<?php

namespace App\Command;

use App\Entity\ProductCode;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Symfony\Component\Serializer\Annotation\Groups;

class SaveProduct
{
    /**
     * @var Collection<int, ProductCode>
     */
    #[Groups(['product:write'])]
    private Collection $codes;

    public function __construct()
    {
        $this->codes = new ArrayCollection();
    }

    /**
     * @return Collection<int, ProductCode>
     */
    public function getCodes(): Collection
    {
        return $this->codes;
    }

    public function addCode(ProductCode $code): void
    {
        if (!$this->codes->contains($code)) {
            $this->codes->add($code);
        }
    }

    public function removeCode(ProductCode $code): void
    {
        if ($this->codes->contains($code)) {
            $this->codes->removeElement($code);
        }
    }
}
<?php

namespace App\Entity;

use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Get;
use App\Repository\ProductCodeRepository;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Serializer\Annotation\Groups;

#[ApiResource]
#[Get]
#[ORM\Entity(repositoryClass: ProductCodeRepository::class)]
class ProductCode
{
    #[ORM\Id]
    #[ORM\GeneratedValue]
    #[ORM\Column]
    private ?int $id = null;

    #[ORM\Column(length: 180)]
    #[Groups(['product:write'])]
    private ?string $type = null;

    #[ORM\Column(length: 180)]
    #[Groups(['product:write'])]
    private ?string $value = null;

    #[ORM\ManyToOne(inversedBy: 'codes')]
    #[ORM\JoinColumn(nullable: false)]
    private ?Product $product = null;

    public function getId(): ?int
    {
        return $this->id;
    }

    public function getType(): ?string
    {
        return $this->type;
    }

    public function setType(string $type): void
    {
        $this->type = $type;
    }

    public function getValue(): ?string
    {
        return $this->value;
    }

    public function setValue(?string $value): void
    {
        $this->value = $value;
    }

    public function getProduct(): ?Product
    {
        return $this->product;
    }

    public function setProduct(?Product $product): void
    {
        $this->product = $product;
    }
}
<?php

namespace App\Entity;

use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Post;
use App\Command\SaveProduct;
use App\Repository\ProductRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Serializer\Annotation\Groups;

#[ORM\Entity(repositoryClass: ProductRepository::class)]
#[ApiResource]
#[Post(
    denormalizationContext: ['groups' => ['product:write']],
    input: SaveProduct::class,
)]
class Product
{
    #[ORM\Id]
    #[ORM\GeneratedValue]
    #[ORM\Column]
    private ?int $id = null;

    /**
     * @var Collection<int, ProductCode>
     */
    #[ORM\OneToMany(mappedBy: 'product', targetEntity: ProductCode::class, cascade: ['persist'], orphanRemoval: true)]
    private Collection $codes;

    public function __construct()
    {
        $this->codes = new ArrayCollection();
    }

    public function getId(): ?int
    {
        return $this->id;
    }

    /**
     * @return Collection<int, ProductCode>
     */
    public function getCodes(): Collection
    {
        return $this->codes;
    }

    public function addCode(ProductCode $code): void
    {
        if (!$this->codes->contains($code)) {
            $this->codes->add($code);
            $code->setProduct($this);
        }
    }

    public function removeCode(ProductCode $code): void
    {
        if ($this->codes->removeElement($code)) {
            // set the owning side to null (unless already changed)
            if ($code->getProduct() === $this) {
                $code->setProduct(null);
            }
        }
    }
}

@ihmels
Copy link
Contributor

ihmels commented Nov 27, 2023

@soyuka Your commit soyuka@85e6209 will fix this issue, but then there's still a bug:

Before 3.2.6:

{
    "components": {
        "schemas": {
            "Product.SaveProduct-product.write": {
                "type": "object",
                "description": "",
                "deprecated": false,
                "properties": {
                    "codes": {
                        "type": "array",
                        "items": {
                            "$ref": "#\/components\/schemas\/ProductCode-product.write"
                        }
                    }
                }
            }
        }
    }
}

After your commit:

{
    "components": {
        "schemas": {
            "Product.SaveProduct-product.write": {
                "type": "object",
                "description": "",
                "deprecated": false,
                "properties": {
                    "codes": {
                        "type": "array",
                        "items": {
                            "type": "unknown_type"
                        }
                    }
                }
            }
        }
    }
}

@soyuka
Copy link
Member

soyuka commented Nov 27, 2023

Thanks this is exactly what I needed, I knew there would be some edge case with that fix, tyvm!

@ihmels
Copy link
Contributor

ihmels commented Nov 27, 2023

You're welcome and thank you for this awesome API framework!

@Napalm4577
Copy link
Author

Thanks @ihmels to give a basic example. Mine is very complex 😄

@mbrodala
Copy link
Contributor

I can confirm that the $ref error is gone with soyuka@85e6209

soyuka added a commit to soyuka/core that referenced this issue Nov 27, 2023
Fixes api-platform#5998

A resource embedded in another class can be writable without having a
write operation (POST, PUT, PATCH).
@soyuka
Copy link
Member

soyuka commented Nov 27, 2023

@mbrodala it was also introducing an unkown-type I did a proper fix see #6001 I would love some feedbacks if you can test it (ping @ihmels), thanks!

soyuka added a commit to soyuka/core that referenced this issue Nov 27, 2023
Fixes api-platform#5998

A resource embedded in another class can be writable without having a
write operation (POST, PUT, PATCH).
soyuka added a commit to soyuka/core that referenced this issue Nov 27, 2023
Fixes api-platform#5998

A resource embedded in another class can be writable without having a
write operation (POST, PUT, PATCH).
soyuka added a commit that referenced this issue Nov 27, 2023
Fixes #5998

A resource embedded in another class can be writable without having a
write operation (POST, PUT, PATCH).
@soyuka soyuka closed this as completed Nov 28, 2023
@mbrodala
Copy link
Contributor

@soyuka Late but better than never: seems to work just fine. 👍

@Napalm4577
Copy link
Author

Thanks @soyuka

How we know when the fix will be released, maybe in the 3.2.7 ?

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

4 participants