Closed vincentchalamon closed 2 years ago
API Platform version(s) affected: 2.7-beta.4
Description
You can not define a GetCollection
without defining a Get
operation. If you do it'll throw this error:
How to reproduce
Define a GetCollection
without a Get
and then try to execute the GetCollection
Possible Solution
Either make it possible to do that, or at least throw an exception that's more clear why it's not working.
Edit: fixed by #4881
Hi!
Issue https://github.com/api-platform/core/issues/4727 (The disable_type_enforcement option is ignored (custom GraphQl mutation name's)
I checked version v2.7.0-beta.4 - the bug is still there
Hello @vincentchalamon , Yes it works for me too. Thank for your job !
Hi! API Platform version(s) affected: 2.7-beta.5 SF 5.4.10 PHP 8.1
Description: Bidirectional/Unidirectional many-to-many not working.
{ "type": "https://tools.ietf.org/html/rfc2616#section-10",
"title": "An error occurred",
"detail": "[Semantical Error] line 0, col 115 near 'partners IN (SELECT': Error: Invalid PathExpression. StateFieldPathExpression or SingleValuedAssociationField expected."}
Example entities:
class Partner implements TimestampableInterface
{
use TimestampableTrait;
#[ORM\Id]
#[ORM\Column(type: 'uuid')]
private UuidInterface $id;
/**
* @var Collection<int, Courier>
*/
#[ORM\ManyToMany(targetEntity: 'App\Infrastructure\Doctrine\Entity\Courier\Courier', mappedBy: 'partners', fetch: 'EXTRA_LAZY')]
private Collection $couriers;
}
class Courier implements TimestampableInterface, UserInterface, PasswordAuthenticatedUserInterface
{
use TimestampableTrait;
#[ORM\Id]
#[ORM\Column(type: 'uuid')]
private UuidInterface $id;
/**
* @var Collection<int, Partner>
*/
#[ORM\ManyToMany(targetEntity: 'App\Infrastructure\Doctrine\Entity\Partner\Partner', inversedBy: 'couriers', fetch: 'EXTRA_LAZY')]
#[ORM\JoinTable(name: 'cd_partners_couries')]
private Collection $partners;
}
@KDederichs
As you can see on the documentation, you have to define Get item operation.
Get collections and Get item are mandatory operations
#[ApiResource(operations: [
new Get(
controller: NotFoundAction::class,
read: false,
output: false
),
new GetCollection()
])]
But this code generate for me a 404 Not Found. I have to inverse the order to get a success response .
#[ApiResource(operations: [
new GetCollection(),
new Get(
controller: NotFoundAction::class,
read: false,
output: false
)
])]
Edit: fixed by #4881
API Platform version(s) affected: 2.7-rc2
SF 5.4.10
PHP 8.1
I'm having multiple issues with the 2.7-rc2
release. This is the first time I tried to upgrade from 2.6.8
(where I have no deprecations)
/.well-known/genid
)id
, subscription
, createdAt
, permissions
, ...POST /api/bands
{
"title": "Wieni rock and roll"
}
Response in 2.6.8
Response in 2.7-rc2
I'll try to ask for some time to debug this further.
I tried to kindof replicate our setup using a fork of the demo. But there I got a whole other set of issues 🤯
You can find my fork at RobinHoutevelts/api-pf-2.7.rc2-regression
I changed /tests/Api/BooksTest.php. It passes in 2.6.8
but fails in 2.7-rc2
docker compose exec php bin/phpunit tests/Api/BooksTest.php
Hello,
I have an error on test when I use assertMatchesResourceCollectionJsonSchema
assertion.
In my ApiProperty I use the jsonSchemaContext property to specify the format of datetime.
#[ApiProperty(jsonSchemaContext: ['type' => 'string', 'format' => 'YYYY-MM-DD'])]
The error : hydra:member[1].date: Invalid date-time "2022-04-10", expected format YYYY-MM-DDThh:mm:ssZ or YYYY-MM-DDThh:mm:ss+hh:mm
API Platform version : 2.7-rc2 SF 6.1.2 PHP 8.1
@KDederichs As you can see on the documentation, you have to define Get item operation. Get collections and Get item are mandatory operations
@Yoann-TYT I had the same issue, I found on my own with the documentation after wasting an hour. The exception should be a little more precise, don't you think?
It is gonna change soon: https://github.com/api-platform/core/issues/4879
Hello, I found a problem with Api platform > 2.7.0-beta-2 when i use provider and output dto.
API Platform version(s) affected: >2.7-beta-2
SF >=5.4.|| >=6.0
PHP 7.4 || 8.1
Error
"@context": "/api/contexts/Error",
"@type": "hydra:Error",
"hydra:title": "An error occurred",
"hydra:description": "Operation \"\" not found for resource \"CarOutput\".",
"trace": [...]
You can see an exemple of non working code here : https://github.com/Zul3s/api-platform_2.7.-_demo .
Just run and try to execute /api/cars
This problem was introduced with this changement : https://github.com/api-platform/core/compare/v2.7.0-beta.2...v2.7.0-beta.3#diff-d87efa25fbf52904daa06047002652f08645e3ad4a7f4985ffeec5a7c8e39c4fR145
If we delete output declaration, it's work fine, but we loose api documentation.
I don't know how i can help you ?
@Zul3s it looks like the same bug as #4879. Can you try #4881 to see if it fixes your issue?
Hi @vincentchalamon , I try your branch, but i still have the problem.
@Zul3s OK I've seen the issue from your project. I'm working on it and will try to publish a fix on 2.7 soon
Hi,
So, I repost here my comment https://github.com/api-platform/core/pull/4881#issuecomment-1209445989
From my testing of the latest 2.7 (kudos to the team!) GraphQl will need a fix as well when there is a field with sub collection like this minimal code (videos.collection.xxx) :
{
collectionPageQueryVideoGroups {
collection {
videos {
collection {
thumbnailPath
}
}
}
}
}
I get the error:
{
"errors": [
{
"debugMessage": "[Semantical Error] line 0, col 41 near 'groups IN (SELECT': Error: Invalid PathExpression. StateFieldPathExpression or SingleValuedAssociationField expected.",
"message": "Internal server error",
"extensions": {
"category": "internal"
},
"locations": [
{
"line": 4,
"column": 7
}
],
"path": [
"collectionPageQueryVideoGroups",
"collection",
0,
"videos"
],
"trace": [
{
"file": "/home/david/Works/OLYBe/graphql-api/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Parser.php",
"line": 546,
"call": "Doctrine\\ORM\\Query\\QueryException::semanticalError()"
},
{
"file": "/home/david/Works/OLYBe/graphql-api/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Parser.php",
"line": 862,
"call": "Doctrine\\ORM\\Query\\Parser::semanticalError()"
},
{
"file": "/home/david/Works/OLYBe/graphql-api/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Parser.php",
"line": 319,
"call": "Doctrine\\ORM\\Query\\Parser::processDeferredPathExpressions()"
},
{
"file": "/home/david/Works/OLYBe/graphql-api/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Parser.php",
"line": 408,
"call": "Doctrine\\ORM\\Query\\Parser::getAST()"
},
{
"file": "/home/david/Works/OLYBe/graphql-api/vendor/doctrine/orm/lib/Doctrine/ORM/Query.php",
"line": 274,
"call": "Doctrine\\ORM\\Query\\Parser::parse()"
},
{
"file": "/home/david/Works/OLYBe/graphql-api/vendor/doctrine/orm/lib/Doctrine/ORM/Query.php",
"line": 286,
"call": "Doctrine\\ORM\\Query::parse()"
},
{
"file": "/home/david/Works/OLYBe/graphql-api/vendor/doctrine/orm/lib/Doctrine/ORM/AbstractQuery.php",
"line": 1188,
"call": "Doctrine\\ORM\\Query::_doExecute()"
},
{
"file": "/home/david/Works/OLYBe/graphql-api/vendor/doctrine/orm/lib/Doctrine/ORM/AbstractQuery.php",
"line": 1142,
"call": "Doctrine\\ORM\\AbstractQuery::executeIgnoreQueryCache()"
},
{
"file": "/home/david/Works/OLYBe/graphql-api/vendor/doctrine/orm/lib/Doctrine/ORM/AbstractQuery.php",
"line": 878,
"call": "Doctrine\\ORM\\AbstractQuery::execute()"
},
{
"file": "/home/david/Works/OLYBe/graphql-api/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Pagination/Paginator.php",
"line": 171,
"call": "Doctrine\\ORM\\AbstractQuery::getResult()"
},
{
"file": "/home/david/Works/OLYBe/graphql-api/vendor/api-platform/core/src/Doctrine/Orm/AbstractPaginator.php",
"line": 69,
"call": "Doctrine\\ORM\\Tools\\Pagination\\Paginator::getIterator()"
},
{
"file": "/home/david/Works/OLYBe/graphql-api/vendor/api-platform/core/src/Doctrine/Orm/AbstractPaginator.php",
"line": 77,
"call": "ApiPlatform\\Doctrine\\Orm\\AbstractPaginator::getIterator()"
},
{
"file": "/home/david/Works/OLYBe/graphql-api/vendor/api-platform/core/src/GraphQl/Resolver/Stage/SerializeStage.php",
"line": 130,
"call": "ApiPlatform\\Doctrine\\Orm\\AbstractPaginator::count()"
},
{
"file": "/home/david/Works/OLYBe/graphql-api/vendor/api-platform/core/src/GraphQl/Resolver/Stage/SerializeStage.php",
"line": 98,
"call": "ApiPlatform\\GraphQl\\Resolver\\Stage\\SerializeStage::serializeCursorBasedPaginatedCollection()"
},
{
"file": "/home/david/Works/OLYBe/graphql-api/vendor/api-platform/core/src/GraphQl/Resolver/Factory/CollectionResolverFactory.php",
"line": 99,
"call": "ApiPlatform\\GraphQl\\Resolver\\Stage\\SerializeStage::__invoke()"
},
{
"file": "/home/david/Works/OLYBe/graphql-api/vendor/webonyx/graphql-php/src/Executor/ReferenceExecutor.php",
"line": 623,
"call": "ApiPlatform\\GraphQl\\Resolver\\Factory\\CollectionResolverFactory::ApiPlatform\\GraphQl\\Resolver\\Factory\\{closure}()"
},
{
"file": "/home/david/Works/OLYBe/graphql-api/vendor/webonyx/graphql-php/src/Executor/ReferenceExecutor.php",
"line": 550,
"call": "GraphQL\\Executor\\ReferenceExecutor::resolveFieldValueOrError()"
},
{
"file": "/home/david/Works/OLYBe/graphql-api/vendor/webonyx/graphql-php/src/Executor/ReferenceExecutor.php",
"line": 1195,
"call": "GraphQL\\Executor\\ReferenceExecutor::resolveField()"
},
{
"file": "/home/david/Works/OLYBe/graphql-api/vendor/webonyx/graphql-php/src/Executor/ReferenceExecutor.php",
"line": 1145,
"call": "GraphQL\\Executor\\ReferenceExecutor::executeFields()"
},
{
"file": "/home/david/Works/OLYBe/graphql-api/vendor/webonyx/graphql-php/src/Executor/ReferenceExecutor.php",
"line": 1106,
"call": "GraphQL\\Executor\\ReferenceExecutor::collectAndExecuteSubfields()"
},
{
"file": "/home/david/Works/OLYBe/graphql-api/vendor/webonyx/graphql-php/src/Executor/ReferenceExecutor.php",
"line": 793,
"call": "GraphQL\\Executor\\ReferenceExecutor::completeObjectValue()"
},
{
"file": "/home/david/Works/OLYBe/graphql-api/vendor/webonyx/graphql-php/src/Executor/ReferenceExecutor.php",
"line": 654,
"call": "GraphQL\\Executor\\ReferenceExecutor::completeValue()"
},
{
"file": "/home/david/Works/OLYBe/graphql-api/vendor/webonyx/graphql-php/src/Executor/ReferenceExecutor.php",
"line": 887,
"call": "GraphQL\\Executor\\ReferenceExecutor::completeValueCatchingError()"
},
{
"file": "/home/david/Works/OLYBe/graphql-api/vendor/webonyx/graphql-php/src/Executor/ReferenceExecutor.php",
"line": 761,
"call": "GraphQL\\Executor\\ReferenceExecutor::completeListValue()"
},
{
"file": "/home/david/Works/OLYBe/graphql-api/vendor/webonyx/graphql-php/src/Executor/ReferenceExecutor.php",
"line": 654,
"call": "GraphQL\\Executor\\ReferenceExecutor::completeValue()"
},
{
"file": "/home/david/Works/OLYBe/graphql-api/vendor/webonyx/graphql-php/src/Executor/ReferenceExecutor.php",
"line": 557,
"call": "GraphQL\\Executor\\ReferenceExecutor::completeValueCatchingError()"
},
{
"file": "/home/david/Works/OLYBe/graphql-api/vendor/webonyx/graphql-php/src/Executor/ReferenceExecutor.php",
"line": 1195,
"call": "GraphQL\\Executor\\ReferenceExecutor::resolveField()"
},
{
"file": "/home/david/Works/OLYBe/graphql-api/vendor/webonyx/graphql-php/src/Executor/ReferenceExecutor.php",
"line": 1145,
"call": "GraphQL\\Executor\\ReferenceExecutor::executeFields()"
},
{
"file": "/home/david/Works/OLYBe/graphql-api/vendor/webonyx/graphql-php/src/Executor/ReferenceExecutor.php",
"line": 1106,
"call": "GraphQL\\Executor\\ReferenceExecutor::collectAndExecuteSubfields()"
},
{
"file": "/home/david/Works/OLYBe/graphql-api/vendor/webonyx/graphql-php/src/Executor/ReferenceExecutor.php",
"line": 793,
"call": "GraphQL\\Executor\\ReferenceExecutor::completeObjectValue()"
},
{
"file": "/home/david/Works/OLYBe/graphql-api/vendor/webonyx/graphql-php/src/Executor/ReferenceExecutor.php",
"line": 654,
"call": "GraphQL\\Executor\\ReferenceExecutor::completeValue()"
},
{
"file": "/home/david/Works/OLYBe/graphql-api/vendor/webonyx/graphql-php/src/Executor/ReferenceExecutor.php",
"line": 557,
"call": "GraphQL\\Executor\\ReferenceExecutor::completeValueCatchingError()"
},
{
"file": "/home/david/Works/OLYBe/graphql-api/vendor/webonyx/graphql-php/src/Executor/ReferenceExecutor.php",
"line": 1195,
"call": "GraphQL\\Executor\\ReferenceExecutor::resolveField()"
},
{
"file": "/home/david/Works/OLYBe/graphql-api/vendor/webonyx/graphql-php/src/Executor/ReferenceExecutor.php",
"line": 264,
"call": "GraphQL\\Executor\\ReferenceExecutor::executeFields()"
},
{
"file": "/home/david/Works/OLYBe/graphql-api/vendor/webonyx/graphql-php/src/Executor/ReferenceExecutor.php",
"line": 215,
"call": "GraphQL\\Executor\\ReferenceExecutor::executeOperation()"
},
{
"file": "/home/david/Works/OLYBe/graphql-api/vendor/webonyx/graphql-php/src/Executor/Executor.php",
"line": 156,
"call": "GraphQL\\Executor\\ReferenceExecutor::doExecute()"
},
{
"file": "/home/david/Works/OLYBe/graphql-api/vendor/webonyx/graphql-php/src/GraphQL.php",
"line": 162,
"call": "GraphQL\\Executor\\Executor::promiseToExecute()"
},
{
"file": "/home/david/Works/OLYBe/graphql-api/vendor/webonyx/graphql-php/src/GraphQL.php",
"line": 94,
"call": "GraphQL\\GraphQL::promiseToExecute()"
},
{
"file": "/home/david/Works/OLYBe/graphql-api/vendor/api-platform/core/src/GraphQl/Executor.php",
"line": 32,
"call": "GraphQL\\GraphQL::executeQuery()"
},
{
"file": "/home/david/Works/OLYBe/graphql-api/vendor/api-platform/core/src/GraphQl/Action/EntrypointAction.php",
"line": 86,
"call": "ApiPlatform\\GraphQl\\Executor::executeQuery()"
},
whereas my video resource is well defined with the Get operation (all my resources are)
#[APIP\ApiResource(
normalizationContext: ['groups' => ['videoGroup:read', 'timestamp']],
paginationItemsPerPage: 12,
operations: [new APIP\Get()],
graphQlOperations: [
new APIP\GraphQl\QueryCollection(
name: 'collectionPageQuery',
resolver: VideoGroupCollectionResolver::class,
paginationType: 'page'
),
new APIP\GraphQl\QueryCollection(
name: 'collectionPageQueryEnriched',
resolver: VideoGroupEnrichedCollectionResolver::class,
paginationType: 'page'
),
],
)]
#[ORM\Entity(repositoryClass: VideoGroupRepository::class)]
#[ORM\Table(name: 'videogroup')]
class VideoGroup
{
use EnableTrait;
#[ORM\Id]
#[ORM\Column(type: 'uuid', unique: true)]
#[ORM\GeneratedValue(strategy: 'CUSTOM')]
#[ORM\CustomIdGenerator(class: UuidGenerator::class)]
private ?UuidInterface $uuid = null;
#[Groups(['videoGroup:read'])]
#[ORM\ManyToMany(targetEntity: Video::class, mappedBy: 'groups', fetch: 'EXTRA_LAZY')]
private Collection $videos;
public function getUuid(): ?UuidInterface
{
return $this->uuid;
}
public function getVideos(): ?Collection
{
return $this->videos;
}
public function setVideos(ArrayCollection $videos): self
{
$this->videos = $videos;
return $this;
}
}
#[APIP\ApiResource(
normalizationContext: ['groups' => ['video:read', 'propKey:read', 'propValue:read', 'teacher:read', 'timestamp']],
order: ['createdAt' => 'DESC'],
paginationItemsPerPage: 12,
operations: [new APIP\Get()],
)]
#[ORM\Entity(repositoryClass: VideoRepository::class)]
class Video
{
#[ORM\Id]
#[ORM\Column(type: 'uuid', unique: true)]
#[ORM\GeneratedValue(strategy: 'CUSTOM')]
#[ORM\CustomIdGenerator(class: UuidGenerator::class)]
private ?UuidInterface $uuid = null;
#[Groups(['video:read'])]
#[ORM\ManyToMany(targetEntity: VideoGroup::class, inversedBy: 'videos', fetch: 'EXTRA_LAZY')]
#[ORM\JoinTable(name: 'videos_groups')]
#[ORM\JoinColumn(name: 'video_uuid', referencedColumnName: 'uuid')]
#[ORM\InverseJoinColumn(name: 'videogroup_uuid', referencedColumnName: 'uuid')]
private Collection $groups;
public function __construct()
{
$this->groups = new ArrayCollection();
}
public function getUuid(): ?UuidInterface
{
return $this->uuid;
}
public function getGroups(): ?Collection
{
return $this->groups;
}
public function setGroups(ArrayCollection $groups): self
{
$this->groups = $groups;
return $this;
}
}
I tried adding operations: [new APIP\GetCollection(), new APIP\Get()]
, without more success
@webda2l Could you try to use a Link
like this:
#[Groups(['videoGroup:read'])]
#[ORM\ManyToMany(targetEntity: Video::class, mappedBy: 'groups', fetch: 'EXTRA_LAZY')]
#[Link(toProperty: 'groups')]
private Collection $videos;
Not totally sure, but worth a try!
@webda2l Could you try to use a
Link
like this:#[Groups(['videoGroup:read'])] #[ORM\ManyToMany(targetEntity: Video::class, mappedBy: 'groups', fetch: 'EXTRA_LAZY')] #[Link(toProperty: 'groups')] private Collection $videos;
Not totally sure, but worth a try!
Perfect, good catch! Thanks!
About other founds during this 3.0 in progress migration, I found that with a resource paginationEnabled: false
like this:
#[APIP\ApiResource(
normalizationContext: ['groups' => ['propValue:read']],
paginationEnabled: false,
order: ['weight' => 'DESC'],
operations: [new APIP\Get()],
graphQlOperations: [
...
class PropertyValue
And the use of a field of this resource as a collection from another resource in graphql. Previously i was doing:
videos: collectionPageQueryVideos (
slug: $videoSlug
) {
collection {
materials {
_id
...
}
...
But now, I have to embed with a collection, like:
videos: collectionPageQueryVideos (
slug: $videoSlug
) {
collection {
materials {
collection {
_id
...
}
}
...
I am ok with the new behavior that seems more correct to me. But it seems not set in the https://github.com/api-platform/core/blob/main/CHANGELOG.md as BC break, or I missed it, or the new behavior is a mistake?
Not sure why, but if you can bissect it and find which commit does it, it would be great!
Not sure why, but if you can bissect it and find which commit does it, it would be great!
Ok will try to do this a bit later.
Not sure why, but if you can bissect it and find which commit does it, it would be great!
So the change appear in 2.7.beta1, paginationEnabled: false,
works ok when the resource is directly used, but not when the resource is embed by another. Like I said previously, not a big deal, because it's a collection, pagination enabled or not, it's better this way I think. But a BC.
// alpha7
{
videos {
collection {
id
materials {
id
slug
}
}
}
}
// beta1
{
videos {
collection {
id
materials {
collection {
id
slug
}
}
}
}
}
#[APIP\ApiResource(
normalizationContext: ['groups' => ['video:read', 'propKey:read', 'propValue:read', 'teacher:read', 'timestamp']],
order: ['createdAt' => 'DESC'],
paginationItemsPerPage: 12,
operations: [new APIP\Get()],
graphQlOperations: [
new APIP\GraphQl\QueryCollection(
name: 'collection_query',
paginationType: 'page'
),
],
)]
class Video
{
...
#[Groups(['video:read'])]
#[ORM\ManyToMany(targetEntity: PropertyValue::class)]
#[ORM\JoinTable(name: 'videos_materials')]
#[ORM\JoinColumn(name: 'video_uuid', referencedColumnName: 'uuid')]
#[ORM\InverseJoinColumn(name: 'videopropvalue_id', referencedColumnName: 'id')]
private Collection $materials;
// #[Groups(['video:read'])]
// #[ORM\ManyToMany(targetEntity: PropertyValue::class)]
// #[ORM\JoinTable(name: 'videos_bodyparts')]
// #[ORM\JoinColumn(name: 'video_uuid', referencedColumnName: 'uuid')]
// #[ORM\InverseJoinColumn(name: 'videopropvalue_id', referencedColumnName: 'id')]
// private Collection $bodyParts;
...
}
#[APIP\ApiResource(
normalizationContext: ['groups' => ['propValue:read']],
paginationEnabled: false,
operations: [new APIP\Get()],
graphQlOperations: [
new APIP\GraphQl\QueryCollection(
name: 'collection_query',
paginationType: 'page'
),
],
)]
...
class PropertyValue
{
...
Note that there is another issue, maybe related to a missed Link again (I met another case, with a different error, after your comment on a ManyToMany that was required this time a #[APIP\Link(toClass: Company::class, toProperty: 'users')]
: this is a bit tricky these Link): note that in the code upper, there is a conflict between the both PropertyValue Collection $materials & $bodyParts, and it requires me to comment the $bodyParts one if I want that the $materials one return something.
Last feedback on the 2.7 that can be a bit severe at a security level :/ Previously I had this configuration, to disable all graphql & rest operation by default and it was ok.
api_platform:
metadata_backward_compatibility_layer: false
defaults:
stateless: true
graphql: null
itemOperations: []
collectionOperations: []
pagination_items_per_page: 10
pagination_client_items_per_page: true
But now, with the 2.7, with this same configuration, some mutation has been enabled on some of my entities whose I didn't enable graphql, on this entity by example:
#[APIP\ApiResource(
security: "is_granted('ROLE_USER')",
normalizationContext: ['groups' => ['company:read']],
paginationType: 'page',
operations: [new APIP\Get()],
)]
class Company
So the change appear in 2.7.beta1,
paginationEnabled: false,
works ok when the resource is directly used, but not when the resource is embed by another. Like I said previously, not a big deal, because it's a collection, pagination enabled or not, it's better this way I think. But a BC.
Thanks for the search!
It's a bug: since the pagination is disabled for PropertyValue
, it should also affect its behavior when this resource is nested inside a query.
I'm guessing the bug comes because Operation
has been used instead of operationName
and resourceClass
has been not used anymore: the root operation of the "main" resource is probably taken instead of the "current" resource one.
If you have some time, could you add a failing test in 2.7? It will help me fix this bug.
Your conflict seems to be a bug too.
For the security part, you probably need to change your default configuration like this:
api_platform:
metadata_backward_compatibility_layer: false
defaults:
stateless: true
graphql_operations: []
operations: []
pagination_items_per_page: 10
pagination_client_items_per_page: true
So the change appear in 2.7.beta1,
paginationEnabled: false,
works ok when the resource is directly used, but not when the resource is embed by another. Like I said previously, not a big deal, because it's a collection, pagination enabled or not, it's better this way I think. But a BC.Thanks for the search! It's a bug: since the pagination is disabled for
PropertyValue
, it should also affect its behavior when this resource is nested inside a query. I'm guessing the bug comes becauseOperation
has been used instead ofoperationName
andresourceClass
has been not used anymore: the root operation of the "main" resource is probably taken instead of the "current" resource one. If you have some time, could you add a failing test in 2.7? It will help me fix this bug.Your conflict seems to be a bug too.
For the security part, you probably need to change your default configuration like this:
api_platform: metadata_backward_compatibility_layer: false defaults: stateless: true graphql_operations: [] operations: [] pagination_items_per_page: 10 pagination_client_items_per_page: true
You're welcome. I'll see if I have time later this week ok.
For the security part, I tried quickly, same bad result with the latest 2.7 like the 3.0, no :/
In 2.7-rc.2 it seems the the open api docs can't handle a GroupSequenceProvider. PHP 8.1.9
I have a setup like this:
class A
{
#[NotBlank]
#[Valid]
private B $recipient;
}
#[GroupSequenceProvider]
class B implements GroupSequenceProviderInterface
{
private bool $someCondition = false;
#[NotBlank(groups: ['other_group])]
private ?string $someValue = null;
public function getGroupSequence(): array|GroupSequence
{
$groups = ['B'];
if ($someCondition) {
$groups[] = 'other_group';
}
return $groups;
}
}
And it's giving me this error:
Symfony\Component\Validator\Exception\GroupDefinitionException:
The group "GroupSequence" is missing in the group sequence.
at vendor/symfony/validator/Mapping/ClassMetadata.php:414
at Symfony\Component\Validator\Mapping\ClassMetadata->setGroupSequence(object(GroupSequence))
(vendor/symfony/validator/Mapping/Loader/AnnotationLoader.php:48)
at Symfony\Component\Validator\Mapping\Loader\AnnotationLoader->loadClassMetadata(object(ClassMetadata))
(vendor/symfony/validator/Mapping/Loader/LoaderChain.php:54)
at Symfony\Component\Validator\Mapping\Loader\LoaderChain->loadClassMetadata(object(ClassMetadata))
(vendor/symfony/validator/Mapping/Factory/LazyLoadingMetadataFactory.php:101)
at Symfony\Component\Validator\Mapping\Factory\LazyLoadingMetadataFactory->getMetadataFor('Symfony\\Component\\Validator\\Constraints\\GroupSequence')
(vendor/symfony/validator/Validator/RecursiveValidator.php:80)
at Symfony\Component\Validator\Validator\RecursiveValidator->getMetadataFor('Symfony\\Component\\Validator\\Constraints\\GroupSequence')
(vendor/symfony/validator/Validator/TraceableValidator.php:51)
at Symfony\Component\Validator\Validator\TraceableValidator->getMetadataFor('Symfony\\Component\\Validator\\Constraints\\GroupSequence')
(vendor/api-platform/core/src/Symfony/Validator/Metadata/Property/ValidatorPropertyMetadataFactory.php:103)
at ApiPlatform\Symfony\Validator\Metadata\Property\ValidatorPropertyMetadataFactory->create('Symfony\\Component\\Validator\\Constraints\\GroupSequence', 'groups', array('enable_getter_setter_extraction' => true))
(vendor/api-platform/core/src/Metadata/Property/Factory/CachedPropertyMetadataFactory.php:47)
at ApiPlatform\Metadata\Property\Factory\CachedPropertyMetadataFactory->ApiPlatform\Metadata\Property\Factory\{closure}()
(vendor/api-platform/core/src/Util/CachedTrait.php:44)
at ApiPlatform\Metadata\Property\Factory\CachedPropertyMetadataFactory->getCached('property_metadata_db9d5a55e96a484ac7c53512d53972df', object(Closure))
(vendor/api-platform/core/src/Metadata/Property/Factory/CachedPropertyMetadataFactory.php:48)
at ApiPlatform\Metadata\Property\Factory\CachedPropertyMetadataFactory->create('Symfony\\Component\\Validator\\Constraints\\GroupSequence', 'groups', array('enable_getter_setter_extraction' => true))
(vendor/api-platform/core/src/JsonSchema/SchemaFactory.php:132)
at ApiPlatform\JsonSchema\SchemaFactory->buildSchema('Symfony\\Component\\Validator\\Constraints\\GroupSequence', 'jsonld', 'output', null, object(Schema), array(), false)
(vendor/api-platform/core/src/Hydra/JsonSchema/SchemaFactory.php:76)
at ApiPlatform\Hydra\JsonSchema\SchemaFactory->buildSchema('Symfony\\Component\\Validator\\Constraints\\GroupSequence', 'jsonld', 'output', null, object(Schema), array(), false)
(vendor/api-platform/core/src/JsonSchema/TypeFactory.php:157)
at ApiPlatform\JsonSchema\TypeFactory->getClassType('Symfony\\Component\\Validator\\Constraints\\GroupSequence', 'jsonld', null, array(), object(Schema))
(vendor/api-platform/core/src/JsonSchema/TypeFactory.php:90)
at ApiPlatform\JsonSchema\TypeFactory->makeBasicType(object(Type), 'jsonld', null, array(), object(Schema))
(vendor/api-platform/core/src/JsonSchema/TypeFactory.php:77)
at ApiPlatform\JsonSchema\TypeFactory->getType(object(Type), 'jsonld', null, array(), object(Schema))
(vendor/api-platform/core/src/JsonSchema/SchemaFactory.php:218)
at ApiPlatform\JsonSchema\SchemaFactory->buildPropertySchema(object(Schema), 'CreateBoxOrderRecipient.jsonld', 'groupSequence', object(ApiProperty), array(), 'jsonld')
(vendor/api-platform/core/src/JsonSchema/SchemaFactory.php:142)
at ApiPlatform\JsonSchema\SchemaFactory->buildSchema('App\\Entity\\DTO\\CreateBoxOrder\\CreateBoxOrderRecipient', 'jsonld', 'output', null, object(Schema), array(), false)
(vendor/api-platform/core/src/Hydra/JsonSchema/SchemaFactory.php:76)
at ApiPlatform\Hydra\JsonSchema\SchemaFactory->buildSchema('App\\Entity\\DTO\\CreateBoxOrder\\CreateBoxOrderRecipient', 'jsonld', 'output', null, object(Schema), array(), false)
(vendor/api-platform/core/src/JsonSchema/TypeFactory.php:157)
at ApiPlatform\JsonSchema\TypeFactory->getClassType('App\\Entity\\DTO\\CreateBoxOrder\\CreateBoxOrderRecipient', 'jsonld', null, array(), object(Schema))
(vendor/api-platform/core/src/JsonSchema/TypeFactory.php:90)
at ApiPlatform\JsonSchema\TypeFactory->makeBasicType(object(Type), 'jsonld', null, array(), object(Schema))
(vendor/api-platform/core/src/JsonSchema/TypeFactory.php:77)
at ApiPlatform\JsonSchema\TypeFactory->getType(object(Type), 'jsonld', null, array(), object(Schema))
(vendor/api-platform/core/src/JsonSchema/SchemaFactory.php:218)
at ApiPlatform\JsonSchema\SchemaFactory->buildPropertySchema(object(Schema), 'BoxOrder.CreateBoxOrderInput.jsonld', 'recipient', object(ApiProperty), array(), 'jsonld')
(vendor/api-platform/core/src/JsonSchema/SchemaFactory.php:142)
at ApiPlatform\JsonSchema\SchemaFactory->buildSchema('App\\Entity\\BoxOrder', 'jsonld', 'input', object(Post), object(Schema), array(), false)
(vendor/api-platform/core/src/Hydra/JsonSchema/SchemaFactory.php:76)
at ApiPlatform\Hydra\JsonSchema\SchemaFactory->buildSchema('App\\Entity\\BoxOrder', 'jsonld', 'input', object(Post), object(Schema), null, false)
(vendor/api-platform/core/src/OpenApi/Factory/OpenApiFactory.php:261)
at ApiPlatform\OpenApi\Factory\OpenApiFactory->collectPaths(object(ApiResource), object(ResourceMetadataCollection), object(Paths), object(ArrayObject))
(vendor/api-platform/core/src/OpenApi/Factory/OpenApiFactory.php:103)
at ApiPlatform\OpenApi\Factory\OpenApiFactory->__invoke(array('base_url' => '/'))
(src/OpenApi/JwtDecorator.php:21)
at App\OpenApi\JwtDecorator->__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('/srv/api/vendor/autoload_runtime.php')
(public/index.php:5)
Here's a minimal reproducer for that: https://github.com/KDederichs/api-platform_groupsequence (using the v3-rc template, the bug is also in there) Turns out it's already enough to designate a ApiResource as GroupSequenceProvider for it to break.
Yeah Ok found it, it's picking up on the getter for the GroupSequenceProvider and it's fixable by ignoring it in serialization. I guess I'll add a doc PR for that since I assume that's also an issue in older versions.
Hi,
How initialize input DTO for partial update on 2.7/3.0 ?
DataTransformerInitializerInterface is deprecated on 2.7, removed on 3.0 but i can't found an alternative for this.
I must use DenormalizerInterface ?
Not sure if this also is the place where to report 3.0.0-rc.2 issues, but I've created https://github.com/api-platform/core/issues/4897 for an exception on GraphiQL when twig
is not enabled.
Hello @yobrx , Api Platform >= 2.7 "replace" DateTransformer by State providers and State processors. You can found documentation here : https://api-platform.com/docs/main/core/dto/ . Please, make sur you use documentation for 2.7 (on the top at left).
( Also unclear to me how to migrate from DataTransformerInitializerInterface. Even after reading the docs. For example I just want to update the title
attribute without having to send all other attributes. )
@Zul3s Hello ! I have read the good page of documentation before post my message, and unlike version 2.6, 2.7 doesn't mention how to proceed for a patch :(
Minor thing -
the ApiPlatform\Core\Metadata\Resource\ResourceNameCollection
class exists in AP2.6, but it was moved to ApiPlatform\Metadata\Resource\ResourceNameCollection
, the Core
was removed. It is not a big problem, but kind of BC Break
@lchrusciel I think this behavior is normal: if you migrate from 2.6 to 2.7 you should have deprecations for the namespace changes.
Once you have resolved the deprecations, the upgrade path is to migrate your attributes and disable the BC layer (metadata_backward_compatibility_layer: false
).
And then you can upgrade to 3.0.
More details here: https://api-platform.com/docs/main/core/upgrade-guide/
Hello @vincentchalamon , Did you have any news ? Can i help you ?
Hi @Zul3s, the PR is still in review. But as a lot of people are still in holidays, reviews can be slower than usual.
Hi,
I'm currently testing the upgrade to API-P 2.7-rc2 and I have question regarding custom doctrine extensions. We are indeed using a lot of those currently and have implemented them in the 2.6 way.
On 2.7, these extensions are not passed any more in the $extensions parameter of our DataProviders.
Given:
services:
'App\DataProvider\CommitmentStepDataProvider':
arguments:
$extensions: !tagged api_platform.doctrine.orm.query_extension.collection
class CommitmentStepDataProvider implements ContextAwareCollectionDataProviderInterface
{
public function __construct(
iterable $extensions
) {
foreach ($extensions as $extension) {
dump($extension);
}
}
}
Is that normal that there is no backward compatibility here?
@MariusJam does the debug:container
with the extension tag shows your extensions ? What interface do you use ? The metadata flag was kept to default ?
Hi @soyuka,
debug:container
shows the extensions both in 2.6 and 2.7. However, in 2.7, the tags row is empty whereas it is not in 2.6:
---------------- ---------------------------------------------------
Option Value
---------------- ---------------------------------------------------
Service ID App\Doctrine\InscriptionCollectionFilterExtension
Class App\Doctrine\InscriptionCollectionFilterExtension
Tags -
Public no
Synthetic no
Lazy no
Shared yes
Abstract no
Autowired yes
Autoconfigured yes
---------------- ---------------------------------------------------
Concerning the interfaces, that depends of the extension but we can use:
But in each case, the tags row remains empty in 2.7.
For the metadata flag, I've just upgraded to 2.7 so far so yes, I kept it to default.
Okay this is because you rely on autoconfiguration, I'll provide a fix thanks for the details.
Hi!
The Cursor Based Pagination doesn't seems to work.
paginationViaCursor
Why has this issue been closed?
( Also unclear to me how to migrate from DataTransformerInitializerInterface. Even after reading the docs. For example I just want to update the
title
attribute without having to send all other attributes. )
So are there any suggestions how to work with input
dto without DataTransformerInitializer
for PUT requests?
Write here as comment the errors you may found on api-platform/core:2.7.x-dev installed on your project. Please mention: