Skip to content

Commit 6ccbaa3

Browse files
committed
Fixes
1 parent 4f0d4c1 commit 6ccbaa3

File tree

98 files changed

+1370
-1469
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

98 files changed

+1370
-1469
lines changed

src/Api/IdentifiersExtractor.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ public function getIdentifiersFromItem($item, string $operationName = null, arra
5454
{
5555
$identifiers = [];
5656
$resourceClass = $this->getResourceClass($item, true);
57-
$operation = $context['operation'] ?? $this->resourceMetadataFactory->create($resourceClass)->getOperation($operationName);
57+
$operation = $context['operation'] ?? $this->resourceMetadataFactory->create($resourceClass)->getOperation($operationName, false, true);
5858

5959
$links = $operation instanceof GraphQlOperation ? $operation->getLinks() : $operation->getUriVariables();
6060
foreach ($links ?? [] as $link) {

src/Core/Bridge/Symfony/Bundle/Command/UpgradeApiResourceCommand.php

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
namespace ApiPlatform\Core\Bridge\Symfony\Bundle\Command;
1515

1616
use ApiPlatform\Core\Annotation\ApiResource;
17+
use ApiPlatform\Core\Api\IdentifiersExtractorInterface;
1718
use ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface;
1819
use ApiPlatform\Core\Operation\Factory\SubresourceOperationFactoryInterface;
1920
use ApiPlatform\Core\Upgrade\ColorConsoleDiffFormatter;
@@ -42,15 +43,17 @@ final class UpgradeApiResourceCommand extends Command
4243
private $subresourceOperationFactory;
4344
private $subresourceTransformer;
4445
private $reader;
46+
private $identifiersExtractor;
4547
private $localCache = [];
4648

47-
public function __construct(ResourceNameCollectionFactoryInterface $resourceNameCollectionFactory, ResourceMetadataFactoryInterface $resourceMetadataFactory, SubresourceOperationFactoryInterface $subresourceOperationFactory, SubresourceTransformer $subresourceTransformer, AnnotationReader $reader)
49+
public function __construct(ResourceNameCollectionFactoryInterface $resourceNameCollectionFactory, ResourceMetadataFactoryInterface $resourceMetadataFactory, SubresourceOperationFactoryInterface $subresourceOperationFactory, SubresourceTransformer $subresourceTransformer, AnnotationReader $reader, IdentifiersExtractorInterface $identifiersExtractor)
4850
{
4951
$this->resourceNameCollectionFactory = $resourceNameCollectionFactory;
5052
$this->resourceMetadataFactory = $resourceMetadataFactory;
5153
$this->subresourceOperationFactory = $subresourceOperationFactory;
5254
$this->subresourceTransformer = $subresourceTransformer;
5355
$this->reader = $reader;
56+
$this->identifiersExtractor = $identifiersExtractor;
5457

5558
parent::__construct();
5659
}
@@ -204,7 +207,7 @@ private function transformApiResource(InputInterface $input, OutputInterface $ou
204207
continue;
205208
}
206209

207-
$traverser->addVisitor(new UpgradeApiResourceVisitor($attribute, $isAnnotation));
210+
$traverser->addVisitor(new UpgradeApiResourceVisitor($attribute, $isAnnotation, $this->identifiersExtractor, $resourceClass));
208211

209212
$oldCode = file_get_contents($fileName);
210213
$oldStmts = $parser->parse($oldCode);

src/Core/Metadata/Resource/ApiResourceToLegacyResourceMetadataTrait.php

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
use ApiPlatform\Metadata\ApiResource;
1717
use ApiPlatform\Metadata\CollectionOperationInterface;
18+
use ApiPlatform\Metadata\HttpOperation;
1819
use Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter;
1920

2021
/**
@@ -38,10 +39,11 @@ private function transformResourceToResourceMetadata(ApiResource $resource): Res
3839
}
3940

4041
$arrayOperation['openapi_context']['operationId'] = $name;
42+
$arrayOperation['composite_identifier'] = $this->hasCompositeIdentifier($operation);
4143

42-
if ($operation->getExtraProperties()['is_alternate_resource_metadata'] ?? false) {
43-
// TODO ?
44-
// $arrayOperation['composite_identifier'] = $operation->getCompositeIdentifier() ?? false;
44+
if (HttpOperation::METHOD_POST === $operation->getMethod() && !$operation->getUriVariables()) {
45+
$collectionOperations[$name] = $arrayOperation;
46+
continue;
4547
}
4648

4749
if ($operation instanceof CollectionOperationInterface) {
@@ -110,4 +112,15 @@ private function transformUriVariablesToIdentifiers(array $arrayOperation): arra
110112

111113
return $arrayOperation;
112114
}
115+
116+
private function hasCompositeIdentifier(HttpOperation $operation): bool
117+
{
118+
foreach ($operation->getUriVariables() ?? [] as $parameterName => $uriVariable) {
119+
if ($uriVariable->getCompositeIdentifier()) {
120+
return true;
121+
}
122+
}
123+
124+
return false;
125+
}
113126
}

src/Core/Swagger/Serializer/DocumentationNormalizer.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -583,7 +583,7 @@ private function updatePostOperation(bool $v3, \ArrayObject $pathOperation, arra
583583
$identifiers = (array) $resourceMetadata
584584
->getTypedOperationAttribute($operationType, $operationName, 'identifiers', [], false);
585585

586-
$pathOperation = $this->addItemOperationParameters($v3, $pathOperation, $operationType, $operationName, $resourceMetadata, $resourceClass);
586+
$pathOperation = $this->addItemOperationParameters($v3, $pathOperation, $operationType, $operationName, $resourceMetadata, $resourceClass, OperationType::ITEM === $operationType ? false : true);
587587

588588
$successResponse = ['description' => sprintf('%s resource created', $resourceShortName)];
589589
[$successResponse, $defined] = $this->addSchemas($v3, $successResponse, $definitions, $resourceClass, $operationType, $operationName, $responseMimeTypes);
@@ -677,14 +677,14 @@ private function updateDeleteOperation(bool $v3, \ArrayObject $pathOperation, st
677677
return $this->addItemOperationParameters($v3, $pathOperation, $operationType, $operationName, $resourceMetadata, $resourceClass);
678678
}
679679

680-
private function addItemOperationParameters(bool $v3, \ArrayObject $pathOperation, string $operationType, string $operationName, ResourceMetadata $resourceMetadata, string $resourceClass): \ArrayObject
680+
private function addItemOperationParameters(bool $v3, \ArrayObject $pathOperation, string $operationType, string $operationName, ResourceMetadata $resourceMetadata, string $resourceClass, bool $isPost = false): \ArrayObject
681681
{
682682
$identifiers = (array) $resourceMetadata
683683
->getTypedOperationAttribute($operationType, $operationName, 'identifiers', [], false);
684684

685685
// Auto-generated routes in API Platform < 2.7 are considered as collection, hotfix this as the OpenApi Factory supports new operations anyways.
686686
// this also fixes a bug where we could not create POST item operations in API P 2.6
687-
if (OperationType::ITEM === $operationType && 'post' === substr($operationName, -4)) {
687+
if (OperationType::ITEM === $operationType && $isPost) {
688688
$operationType = OperationType::COLLECTION;
689689
}
690690

src/Core/Upgrade/UpgradeApiResourceVisitor.php

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,15 @@
1616
use ApiPlatform\Api\UrlGeneratorInterface;
1717
use ApiPlatform\Core\Annotation\ApiResource as LegacyApiResource;
1818
use ApiPlatform\Core\Annotation\ApiSubresource;
19+
use ApiPlatform\Core\Api\IdentifiersExtractorInterface;
1920
use ApiPlatform\Metadata\ApiResource;
2021
use ApiPlatform\Metadata\Delete;
2122
use ApiPlatform\Metadata\Get;
2223
use ApiPlatform\Metadata\GetCollection;
2324
use ApiPlatform\Metadata\GraphQl\Mutation;
2425
use ApiPlatform\Metadata\GraphQl\Query;
2526
use ApiPlatform\Metadata\GraphQl\QueryCollection;
27+
use ApiPlatform\Metadata\Link;
2628
use ApiPlatform\Metadata\Patch;
2729
use ApiPlatform\Metadata\Post;
2830
use ApiPlatform\Metadata\Put;
@@ -36,12 +38,16 @@ final class UpgradeApiResourceVisitor extends NodeVisitorAbstract
3638
use RemoveAnnotationTrait;
3739

3840
private LegacyApiResource $resourceAnnotation;
41+
private IdentifiersExtractorInterface $identifiersExtractor;
3942
private bool $isAnnotation = false;
43+
private string $resourceClass;
4044

41-
public function __construct(LegacyApiResource $resourceAnnotation, bool $isAnnotation = false)
45+
public function __construct(LegacyApiResource $resourceAnnotation, bool $isAnnotation, IdentifiersExtractorInterface $identifiersExtractor, string $resourceClass)
4246
{
4347
$this->resourceAnnotation = $resourceAnnotation;
4448
$this->isAnnotation = $isAnnotation;
49+
$this->identifiersExtractor = $identifiersExtractor;
50+
$this->resourceClass = $resourceClass;
4551
}
4652

4753
/**
@@ -80,6 +86,10 @@ public function enterNode(Node $node)
8086
$this->getGraphQlOperationsNamespaces($this->resourceAnnotation->graphql ?? [])
8187
));
8288

89+
if (true === !($this->resourceAnnotation->attributes['composite_identifier'] ?? true)) {
90+
$namespaces[] = Link::class;
91+
}
92+
8393
foreach ($node->stmts as $k => $stmt) {
8494
if (!$stmt instanceof Node\Stmt\Use_) {
8595
break;
@@ -202,6 +212,40 @@ public function enterNode(Node $node)
202212
continue;
203213
}
204214

215+
if ('compositeIdentifier' === $key) {
216+
if (false !== $value) {
217+
continue;
218+
}
219+
220+
$identifiers = $this->identifiersExtractor->getIdentifiersFromResourceClass($this->resourceClass);
221+
$identifierNodeItems = [];
222+
foreach ($identifiers as $identifier) {
223+
$identifierNodes = [
224+
'compositeIdentifier' => new Node\Expr\ConstFetch(new Node\Name('false')),
225+
'fromClass' => new Node\Expr\ClassConstFetch(
226+
new Node\Name(
227+
'self'
228+
),
229+
'class'
230+
),
231+
'identifiers' => new Node\Expr\Array_(
232+
[
233+
new Node\Expr\ArrayItem(new Node\Scalar\String_($identifier)),
234+
],
235+
['kind' => Node\Expr\Array_::KIND_SHORT]
236+
),
237+
];
238+
239+
$identifierNodeItems[] = new Node\Expr\ArrayItem(
240+
new Node\Expr\New_(new Node\Name('Link'), $this->arrayToArguments($identifierNodes)),
241+
new Node\Scalar\String_($identifier)
242+
);
243+
}
244+
245+
$arguments['uriVariables'] = new Node\Expr\Array_($identifierNodeItems, ['kind' => Node\Expr\Array_::KIND_SHORT]);
246+
continue;
247+
}
248+
205249
$arguments[$key] = $this->valueToNode($value);
206250
}
207251

src/Doctrine/Common/State/LinksHandlerTrait.php

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,10 @@
1313

1414
namespace ApiPlatform\Doctrine\Common\State;
1515

16+
use ApiPlatform\Exception\OperationNotFoundException;
1617
use ApiPlatform\Exception\RuntimeException;
1718
use ApiPlatform\Metadata\GraphQl\Operation as GraphQlOperation;
19+
use ApiPlatform\Metadata\GraphQl\Query;
1820
use ApiPlatform\Metadata\HttpOperation;
1921
use ApiPlatform\Metadata\Link;
2022
use ApiPlatform\Metadata\Operation;
@@ -42,8 +44,26 @@ private function getLinks(string $resourceClass, Operation $operation, array $co
4244
}
4345
}
4446

45-
$operation = $this->resourceMetadataCollectionFactory->create($linkClass)->getOperation($operation->getName());
46-
foreach ($operation instanceof GraphQlOperation ? $operation->getLinks() : $operation->getUriVariables() as $link) {
47+
// Using graphql, it's possible that we won't find a graphql operation of the same type (eg it is disabled).
48+
try {
49+
$resourceMetadataCollection = $this->resourceMetadataCollectionFactory->create($linkClass);
50+
$linkedOperation = $resourceMetadataCollection->getOperation($operation->getName());
51+
} catch (OperationNotFoundException $e) {
52+
if (!$operation instanceof GraphQlOperation) {
53+
throw $e;
54+
}
55+
56+
// Instead we'll look for the first Query available
57+
foreach ($resourceMetadataCollection as $resourceMetadata) {
58+
foreach ($resourceMetadata->getGraphQlOperations() as $operation) {
59+
if ($operation instanceof Query) {
60+
$linkedOperation = $operation;
61+
}
62+
}
63+
}
64+
}
65+
66+
foreach ($linkedOperation instanceof GraphQlOperation ? $linkedOperation->getLinks() : $linkedOperation->getUriVariables() as $link) {
4767
if ($resourceClass === $link->getToClass()) {
4868
$newLinks[] = $link;
4969
}
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the API Platform project.
5+
*
6+
* (c) Kévin Dunglas <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
declare(strict_types=1);
13+
14+
namespace ApiPlatform\Doctrine\Odm\Metadata\Resource;
15+
16+
use ApiPlatform\Doctrine\Common\State\PersistProcessor;
17+
use ApiPlatform\Doctrine\Common\State\RemoveProcessor;
18+
use ApiPlatform\Doctrine\Odm\State\CollectionProvider;
19+
use ApiPlatform\Doctrine\Odm\State\ItemProvider;
20+
use ApiPlatform\Metadata\CollectionOperationInterface;
21+
use ApiPlatform\Metadata\DeleteOperationInterface;
22+
use ApiPlatform\Metadata\Operation;
23+
use ApiPlatform\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface;
24+
use ApiPlatform\Metadata\Resource\ResourceMetadataCollection;
25+
use Doctrine\ODM\MongoDB\DocumentManager;
26+
use Doctrine\Persistence\ManagerRegistry;
27+
28+
final class DoctrineMongoDbOdmResourceCollectionMetadataFactory implements ResourceMetadataCollectionFactoryInterface
29+
{
30+
/**
31+
* @var ManagerRegistry
32+
*/
33+
private $managerRegistry;
34+
35+
/**
36+
* @var ResourceMetadataCollectionFactoryInterface
37+
*/
38+
private $decorated;
39+
40+
public function __construct(ManagerRegistry $managerRegistry, ResourceMetadataCollectionFactoryInterface $decorated)
41+
{
42+
$this->decorated = $decorated;
43+
$this->managerRegistry = $managerRegistry;
44+
}
45+
46+
/**
47+
* {@inheritDoc}
48+
*/
49+
public function create(string $resourceClass): ResourceMetadataCollection
50+
{
51+
$resourceMetadataCollection = $this->decorated->create($resourceClass);
52+
53+
foreach ($resourceMetadataCollection as $i => $resourceMetadata) {
54+
$operations = $resourceMetadata->getOperations();
55+
56+
if ($operations) {
57+
foreach ($resourceMetadata->getOperations() as $operationName => $operation) {
58+
if (!$this->managerRegistry->getManagerForClass($operation->getClass()) instanceof DocumentManager) {
59+
continue;
60+
}
61+
62+
$operations->add($operationName, $this->addDefaults($operation));
63+
}
64+
65+
$resourceMetadata = $resourceMetadata->withOperations($operations);
66+
}
67+
68+
$graphQlOperations = $resourceMetadata->getGraphQlOperations();
69+
70+
if ($graphQlOperations) {
71+
foreach ($graphQlOperations as $operationName => $graphQlOperation) {
72+
if (!$this->managerRegistry->getManagerForClass($graphQlOperation->getClass()) instanceof DocumentManager) {
73+
continue;
74+
}
75+
76+
$graphQlOperations[$operationName] = $this->addDefaults($graphQlOperation);
77+
}
78+
79+
$resourceMetadata = $resourceMetadata->withGraphQlOperations($graphQlOperations);
80+
}
81+
82+
$resourceMetadataCollection[$i] = $resourceMetadata;
83+
}
84+
85+
return $resourceMetadataCollection;
86+
}
87+
88+
private function addDefaults($operation): Operation
89+
{
90+
if (null === $operation->getProvider()) {
91+
$operation = $operation->withProvider($this->getProvider($operation));
92+
}
93+
94+
if (null === $operation->getProcessor()) {
95+
$operation = $operation->withProcessor($this->getProcessor($operation));
96+
}
97+
98+
return $operation;
99+
}
100+
101+
private function getProvider(Operation $operation): string
102+
{
103+
if ($operation instanceof CollectionOperationInterface) {
104+
return CollectionProvider::class;
105+
}
106+
107+
return ItemProvider::class;
108+
}
109+
110+
private function getProcessor(Operation $operation): string
111+
{
112+
if ($operation instanceof DeleteOperationInterface) {
113+
return RemoveProcessor::class;
114+
}
115+
116+
return PersistProcessor::class;
117+
}
118+
}

src/Doctrine/Odm/State/CollectionProvider.php

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -60,19 +60,19 @@ public function provide(Operation $operation, array $uriVariables = [], array $c
6060

6161
$aggregationBuilder = $repository->createAggregationBuilder();
6262

63-
$this->handleLinks($aggregationBuilder, $uriVariables, $context, $resourceClass, $operationName);
63+
$this->handleLinks($aggregationBuilder, $uriVariables, $context, $resourceClass, $operation);
6464

6565
foreach ($this->collectionExtensions as $extension) {
66-
$extension->applyToCollection($aggregationBuilder, $resourceClass, $operationName, $context);
66+
$extension->applyToCollection($aggregationBuilder, $resourceClass, $operation->getName(), $context);
6767

68-
if ($extension instanceof AggregationResultCollectionExtensionInterface && $extension->supportsResult($resourceClass, $operationName, $context)) {
69-
return $extension->getResult($aggregationBuilder, $resourceClass, $operationName, $context);
68+
if ($extension instanceof AggregationResultCollectionExtensionInterface && $extension->supportsResult($resourceClass, $operation->getName(), $context)) {
69+
return $extension->getResult($aggregationBuilder, $resourceClass, $operation->getName(), $context);
7070
}
7171
}
7272

7373
$resourceMetadata = $this->resourceMetadataCollectionFactory->create($resourceClass);
7474
try {
75-
$operation = $context['operation'] ?? $resourceMetadata->getOperation($operationName);
75+
$operation = $context['operation'] ?? $resourceMetadata->getOperation($operation->getName());
7676
$attribute = $operation->getExtraProperties()['doctrine_mongodb'] ?? [];
7777
} catch (OperationNotFoundException $e) {
7878
$attribute = $resourceMetadata->getOperation(null, true)->getExtraProperties()['doctrine_mongodb'] ?? [];

0 commit comments

Comments
 (0)