Skip to content

Commit

Permalink
Merge 6046382 into 39167de
Browse files Browse the repository at this point in the history
  • Loading branch information
soyuka committed Feb 13, 2021
2 parents 39167de + 6046382 commit 69f615a
Show file tree
Hide file tree
Showing 11 changed files with 148 additions and 6 deletions.
24 changes: 24 additions & 0 deletions features/main/overridden_operation.feature
Expand Up @@ -130,3 +130,27 @@ Feature: Create-Retrieve-Update-Delete with a Overridden Operation context
When I send a "DELETE" request to "/overridden_operation_dummies/1"
Then the response status code should be 204
And the response should be empty

@createSchema
Scenario: Use a POST operation to do a Remote Procedure Call without identifiers
When I add "Content-Type" header equal to "application/json"
And I send a "POST" request to "/rpc"
"""
{
"value": "Hello world"
}
"""
Then the response status code should be 202

@createSchema
Scenario: Use a POST operation to do a Remote Procedure Call without identifiers and with an output DTO
When I add "Content-Type" header equal to "application/json"
And I send a "POST" request to "/rpc_output"
"""
{
"value": "Hello world"
}
"""
Then the response status code should be 200
And the JSON node "success" should be equal to "YES"
And the JSON node "@type" should be equal to "RPC"
2 changes: 1 addition & 1 deletion src/Api/IdentifiersExtractor.php
Expand Up @@ -62,7 +62,7 @@ public function getIdentifiersFromResourceClass(string $resourceClass): array
return ['id'];
}

throw new RuntimeException(sprintf('No identifier defined "%s". You should add #[\ApiPlatform\Core\Annotation\ApiProperty(identifier: true)]" on the property identifying the resource."', $resourceClass));
throw new RuntimeException(sprintf('No identifier defined in "%s". You should add #[\ApiPlatform\Core\Annotation\ApiProperty(identifier: true)]" on the property identifying the resource."', $resourceClass));
}

return $identifiers;
Expand Down
7 changes: 6 additions & 1 deletion src/Bridge/Symfony/Routing/ApiLoader.php
Expand Up @@ -227,7 +227,12 @@ private function addRoute(RouteCollection $routeCollection, string $resourceClas
}
}

$operation['identifiers'] = (array) ($operation['identifiers'] ?? $resourceMetadata->getAttribute('identifiers', $this->identifiersExtractor ? $this->identifiersExtractor->getIdentifiersFromResourceClass($resourceClass) : ['id']));
if ($resourceMetadata->getItemOperations()) {
$operation['identifiers'] = (array) ($operation['identifiers'] ?? $resourceMetadata->getAttribute('identifiers', $this->identifiersExtractor ? $this->identifiersExtractor->getIdentifiersFromResourceClass($resourceClass) : ['id']));
} else {
$operation['identifiers'] = [];
}

$operation['has_composite_identifier'] = \count($operation['identifiers']) > 1 ? $resourceMetadata->getAttribute('composite_identifier', true) : false;
$path = trim(trim($resourceMetadata->getAttribute('route_prefix', '')), '/');
$path .= $this->operationPathResolver->resolveOperationPath($resourceShortName, $operation, $operationType, $operationName);
Expand Down
7 changes: 6 additions & 1 deletion src/JsonLd/Serializer/ObjectNormalizer.php
Expand Up @@ -14,6 +14,7 @@
namespace ApiPlatform\Core\JsonLd\Serializer;

use ApiPlatform\Core\Api\IriConverterInterface;
use ApiPlatform\Core\Exception\InvalidArgumentException;
use ApiPlatform\Core\JsonLd\AnonymousContextBuilderInterface;
use ApiPlatform\Core\Metadata\Property\PropertyMetadata;
use Symfony\Component\Serializer\Normalizer\CacheableSupportsMethodInterface;
Expand Down Expand Up @@ -84,7 +85,11 @@ public function normalize($object, $format = null, array $context = [])
}

if (isset($originalResource)) {
$context['output']['iri'] = $this->iriConverter->getIriFromItem($originalResource);
try {
$context['output']['iri'] = $this->iriConverter->getIriFromItem($originalResource);
} catch (InvalidArgumentException $e) {
// The original resource has no identifiers
}
$context['api_resource'] = $originalResource;
}

Expand Down
6 changes: 5 additions & 1 deletion src/OpenApi/Factory/OpenApiFactory.php
Expand Up @@ -138,7 +138,11 @@ private function collectPaths(ResourceMetadata $resourceMetadata, string $resour

$rootResourceClass = $resourceClass;
foreach ($operations as $operationName => $operation) {
$identifiers = (array) ($operation['identifiers'] ?? $resourceMetadata->getAttribute('identifiers', null === $this->identifiersExtractor ? ['id'] : $this->identifiersExtractor->getIdentifiersFromResourceClass($resourceClass)));
if (OperationType::COLLECTION === $operationType && !$resourceMetadata->getItemOperations()) {
$identifiers = [];
} else {
$identifiers = (array) ($operation['identifiers'] ?? $resourceMetadata->getAttribute('identifiers', null === $this->identifiersExtractor ? ['id'] : $this->identifiersExtractor->getIdentifiersFromResourceClass($resourceClass)));
}
if (\count($identifiers) > 1 ? $resourceMetadata->getAttribute('composite_identifier', true) : false) {
$identifiers = ['id'];
}
Expand Down
7 changes: 6 additions & 1 deletion src/Swagger/Serializer/DocumentationNormalizer.php
Expand Up @@ -197,7 +197,12 @@ public function normalize($object, $format = null, array $context = [])
foreach ($object->getResourceNameCollection() as $resourceClass) {
$resourceMetadata = $this->resourceMetadataFactory->create($resourceClass);
if ($this->identifiersExtractor) {
$resourceMetadata = $resourceMetadata->withAttributes(($resourceMetadata->getAttributes() ?: []) + ['identifiers' => $this->identifiersExtractor->getIdentifiersFromResourceClass($resourceClass)]);
$identifiers = [];
if ($resourceMetadata->getItemOperations()) {
$identifiers = $this->identifiersExtractor->getIdentifiersFromResourceClass($resourceClass);
}

$resourceMetadata = $resourceMetadata->withAttributes(($resourceMetadata->getAttributes() ?: []) + ['identifiers' => $identifiers]);
}
$resourceShortName = $resourceMetadata->getShortName();

Expand Down
2 changes: 1 addition & 1 deletion tests/Api/IdentifiersExtractorTest.php
Expand Up @@ -205,7 +205,7 @@ public function testGetsIdentifiersFromCorrectResourceClass(): void
public function testNoIdentifiers(): void
{
$this->expectException(RuntimeException::class);
$this->expectExceptionMessage('No identifier defined "ApiPlatform\\Core\\Tests\\Fixtures\\TestBundle\\Entity\\Dummy". You should add #[\ApiPlatform\Core\Annotation\ApiProperty(identifier: true)]" on the property identifying the resource.');
$this->expectExceptionMessage('No identifier defined in "ApiPlatform\\Core\\Tests\\Fixtures\\TestBundle\\Entity\\Dummy". You should add #[\ApiPlatform\Core\Annotation\ApiProperty(identifier: true)]" on the property identifying the resource.');
$propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
$propertyNameCollectionFactoryProphecy->create(Dummy::class)->willReturn(new PropertyNameCollection(['foo']));
$propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
Expand Down
@@ -0,0 +1,37 @@
<?php

/*
* This file is part of the API Platform project.
*
* (c) Kévin Dunglas <dunglas@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace ApiPlatform\Core\Tests\Fixtures\TestBundle\DataTransformer;

use ApiPlatform\Core\DataTransformer\DataTransformerInterface;
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Dto\RPCOutput;
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\RPC;

final class RPCOutputDataTransformer implements DataTransformerInterface
{
/**
* {@inheritdoc}
*/
public function transform($object, string $to, array $context = [])
{
return new RPCOutput();
}

/**
* {@inheritdoc}
*/
public function supportsTransformation($object, string $to, array $context = []): bool
{
return $object instanceof RPC && RPCOutput::class === $to;
}
}
19 changes: 19 additions & 0 deletions tests/Fixtures/TestBundle/Dto/RPCOutput.php
@@ -0,0 +1,19 @@
<?php

/*
* This file is part of the API Platform project.
*
* (c) Kévin Dunglas <dunglas@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace ApiPlatform\Core\Tests\Fixtures\TestBundle\Dto;

class RPCOutput
{
public $success = 'YES';
}
36 changes: 36 additions & 0 deletions tests/Fixtures/TestBundle/Entity/RPC.php
@@ -0,0 +1,36 @@
<?php

/*
* This file is part of the API Platform project.
*
* (c) Kévin Dunglas <dunglas@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity;

use ApiPlatform\Core\Annotation\ApiResource;
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Dto\RPCOutput;

/**
* RPC-like resource.
*
* @ApiResource(
* itemOperations={},
* collectionOperations={
* "post"={"status"=202, "messenger"=true, "path"="rpc", "output"=false},
* "post_output"={"method"="POST", "status"=200, "path"="rpc_output", "output"=RPCOutput::class}
* },
* )
*/
class RPC
{
/**
* @var string
*/
public $value;
}
7 changes: 7 additions & 0 deletions tests/Fixtures/app/config/config_common.yml
Expand Up @@ -371,3 +371,10 @@ services:
decorates: 'api_platform.graphql.type_converter'
arguments: ['@app.graphql.type_converter.inner']
public: false

app.data_transformer.rpc_output:
class: 'ApiPlatform\Core\Tests\Fixtures\TestBundle\DataTransformer\RPCOutputDataTransformer'
public: false
tags:
- { name: 'api_platform.data_transformer' }

0 comments on commit 69f615a

Please sign in to comment.