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

Merge 2.4 onto master #2557

Merged
merged 27 commits into from
Feb 26, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
7bb70f6
Add autoconfiguration for tag 'api_platform.data_transformer'
GregoireHebert Feb 20, 2019
2944053
Merge pull request #2534 from GregoireHebert/autoconfigure-dataTransf…
soyuka Feb 20, 2019
89947c9
fix error case when denormalizing relations with plain identifiers. n…
bendavies Feb 20, 2019
e6cd64c
Fix normalization output Data Transformer for Json format
Feb 20, 2019
c3a9f4a
Merge pull request #2540 from birkof/json_dto_transformer_fix
soyuka Feb 20, 2019
1539c99
fix jsonapi call to data transformer
soyuka Feb 20, 2019
ef1d2ad
Respect the RouterInterface by using a ResourceNotFoundException
soyuka Feb 20, 2019
c391f2a
Merge pull request #2543 from soyuka/fix-jsonapi-datatransformer
soyuka Feb 20, 2019
8909336
Merge pull request #2544 from soyuka/fix-2514-bis
soyuka Feb 21, 2019
63716ce
Merge pull request #2536 from bendavies/demormalize-plain-identifier-…
soyuka Feb 21, 2019
19157de
add missing cache pool adapter override
bendavies Feb 21, 2019
e4ea2cc
Merge pull request #2548 from bendavies/fix-cache-adapter-decoration
soyuka Feb 22, 2019
9a0e4c2
DataTransformer supports with prenormalized data
soyuka Feb 21, 2019
cdb70b7
Merge pull request #2547 from soyuka/io-denormalize
soyuka Feb 22, 2019
9a74e35
Test ContextBuilder with anonymous objects
soyuka Feb 22, 2019
7e68139
Merge pull request #2554 from soyuka/fix-2551
soyuka Feb 22, 2019
955c4da
Make use of the new AdvancedNameConverterInterface
soyuka Feb 15, 2019
f91d3d8
Allow limit 0 for MongoDB
alanpoulain Feb 20, 2019
16dc017
Merge pull request #2523 from soyuka/fix-2342
soyuka Feb 25, 2019
42079f3
Merge pull request #2549 from alanpoulain/mongodb-limit-0
soyuka Feb 25, 2019
6e9e907
add feature reproducing bug
bendavies Feb 1, 2019
4a79bdd
do not try to infer discriminator mapping if object to populate is al…
bendavies Feb 1, 2019
2ab5c4f
always try to provide a _api_write_item_iri/Content-Location for cont…
bendavies Feb 1, 2019
7dd4428
add further coverage for creating abstract resources with a discrimin…
bendavies Feb 1, 2019
5f5dfa2
work around deps=low sqlite doctrine bug: https://github.com/doctrine…
bendavies Feb 25, 2019
8f88c9e
Merge pull request #2482 from bendavies/prioritize-object-to-populate
soyuka Feb 26, 2019
a58b37c
Merge remote-tracking branch 'upstream/2.4' into merge
soyuka Feb 26, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion features/hydra/collection.feature
Original file line number Diff line number Diff line change
Expand Up @@ -396,7 +396,6 @@ Feature: Collections support
}
"""

@!mongodb
@createSchema
Scenario: Allow passing 0 to `itemsPerPage`
When I send a "GET" request to "/dummies?itemsPerPage=0"
Expand Down
68 changes: 61 additions & 7 deletions features/main/crud_abstract.feature
Original file line number Diff line number Diff line change
Expand Up @@ -80,20 +80,20 @@ Feature: Create-Retrieve-Update-Delete on abstract resource
"""

Scenario: Update a concrete resource
When I add "Content-Type" header equal to "application/ld+json"
And I send a "PUT" request to "/concrete_dummies/1" with body:
When I add "Content-Type" header equal to "application/ld+json"
And I send a "PUT" request to "/concrete_dummies/1" with body:
"""
{
"@id": "/concrete_dummies/1",
"instance": "Become real",
"name": "A nice dummy"
}
"""
Then the response status code should be 200
And the response should be in JSON
And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8"
And the header "Content-Location" should be equal to "/concrete_dummies/1"
And the JSON should be equal to:
Then the response status code should be 200
And the response should be in JSON
And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8"
And the header "Content-Location" should be equal to "/concrete_dummies/1"
And the JSON should be equal to:
"""
{
"@context": "/contexts/ConcreteDummy",
Expand All @@ -105,7 +105,61 @@ Feature: Create-Retrieve-Update-Delete on abstract resource
}
"""

Scenario: Update a concrete resource using abstract resource uri
When I add "Content-Type" header equal to "application/ld+json"
And I send a "PUT" request to "/abstract_dummies/1" with body:
"""
{
"@id": "/concrete_dummies/1",
"instance": "Become surreal",
"name": "A nicer dummy"
}
"""
Then the response status code should be 200
And the response should be in JSON
And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8"
And the header "Content-Location" should be equal to "/concrete_dummies/1"
And the JSON should be equal to:
"""
{
"@context": "/contexts/ConcreteDummy",
"@id": "/concrete_dummies/1",
"@type": "ConcreteDummy",
"instance": "Become surreal",
"id": 1,
"name": "A nicer dummy"
}
"""

Scenario: Delete a resource
When I send a "DELETE" request to "/abstract_dummies/1"
Then the response status code should be 204
And the response should be empty

@createSchema
Scenario: Create a concrete resource with discriminator
When I add "Content-Type" header equal to "application/ld+json"
And I send a "POST" request to "/abstract_dummies" with body:
"""
{
"discr": "concrete",
"instance": "Concrete",
"name": "My Dummy"
}
"""
Then the response status code should be 201
And the response should be in JSON
And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8"
And the header "Content-Location" should be equal to "/concrete_dummies/1"
And the header "Location" should be equal to "/concrete_dummies/1"
And the JSON should be equal to:
"""
{
"@context": "/contexts/ConcreteDummy",
"@id": "/concrete_dummies/1",
"@type": "ConcreteDummy",
"instance": "Concrete",
"id": 1,
"name": "My Dummy"
}
"""
2 changes: 2 additions & 0 deletions phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,5 @@ parameters:
- '#Method ApiPlatform\\Core\\Bridge\\Doctrine\\MongoDbOdm\\Filter\\(Abstract|Boolean|Date|Exists|Numeric|Order|Range|Search)Filter::isPropertyNested\(\) invoked with 2 parameters, 1 required\.#'
- '#Method ApiPlatform\\Core\\Bridge\\Doctrine\\MongoDbOdm\\Filter\\(Abstract|Boolean|Date|Exists|Numeric|Order|Range|Search)Filter::splitPropertyParts\(\) invoked with 2 parameters, 1 required\.#'
- '#Method ApiPlatform\\Core\\DataProvider\\CollectionDataProviderInterface::getCollection\(\) invoked with 3 parameters, 1-2 required\.#'
- '#Method Symfony\\Component\\Serializer\\NameConverter\\NameConverterInterface::normalize\(\) invoked with 3 parameters, 1 required\.#'
- '#Method Symfony\\Component\\Serializer\\NameConverter\\NameConverterInterface::normalize\(\) invoked with 4 parameters, 1 required\.#'
12 changes: 9 additions & 3 deletions src/Bridge/Doctrine/MongoDbOdm/Extension/PaginationExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,18 @@ public function applyToCollection(Builder $aggregationBuilder, string $resourceC
throw new RuntimeException(sprintf('The repository for "%s" must be an instance of "%s".', $resourceClass, DocumentRepository::class));
}

$resultsAggregationBuilder = $repository->createAggregationBuilder()->skip($offset);
if ($limit > 0) {
$resultsAggregationBuilder->limit($limit);
} else {
// Results have to be 0 but MongoDB does not support a limit equal to 0.
$resultsAggregationBuilder->match()->field(Paginator::LIMIT_ZERO_MARKER_FIELD)->equals(Paginator::LIMIT_ZERO_MARKER);
}

$aggregationBuilder
->facet()
->field('results')->pipeline(
$repository->createAggregationBuilder()
->skip($offset)
->limit($limit)
$resultsAggregationBuilder
)
->field('count')->pipeline(
$repository->createAggregationBuilder()
Expand Down
24 changes: 23 additions & 1 deletion src/Bridge/Doctrine/MongoDbOdm/Paginator.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@
*/
final class Paginator implements \IteratorAggregate, PaginatorInterface
{
public const LIMIT_ZERO_MARKER_FIELD = '___';
public const LIMIT_ZERO_MARKER = 'limit0';

/**
* @var Iterator
*/
Expand Down Expand Up @@ -76,7 +79,7 @@ public function __construct(Iterator $mongoDbOdmIterator, UnitOfWork $unitOfWork
* skip/limit parameters of the query, the values set in the facet stage are used instead.
*/
$this->firstResult = $this->getStageInfo($resultsFacetInfo, '$skip');
$this->maxResults = $this->getStageInfo($resultsFacetInfo, '$limit');
$this->maxResults = $this->hasLimitZeroStage($resultsFacetInfo) ? 0 : $this->getStageInfo($resultsFacetInfo, '$limit');
$this->totalItems = $mongoDbOdmIterator->toArray()[0]['count'][0]['count'] ?? 0;
}

Expand All @@ -85,6 +88,10 @@ public function __construct(Iterator $mongoDbOdmIterator, UnitOfWork $unitOfWork
*/
public function getCurrentPage(): float
{
if (0 >= $this->maxResults) {
return 1.;
}

return floor($this->firstResult / $this->maxResults) + 1.;
}

Expand All @@ -93,6 +100,10 @@ public function getCurrentPage(): float
*/
public function getLastPage(): float
{
if (0 >= $this->maxResults) {
return 1.;
}

return ceil($this->totalItems / $this->maxResults) ?: 1.;
}

Expand Down Expand Up @@ -161,4 +172,15 @@ private function getStageInfo(array $resultsFacetInfo, string $stage): int

throw new InvalidArgumentException("$stage stage was not applied to the facet stage of the aggregation pipeline.");
}

private function hasLimitZeroStage(array $resultsFacetInfo): bool
{
foreach ($resultsFacetInfo as $resultFacetInfo) {
if (self::LIMIT_ZERO_MARKER === ($resultFacetInfo['$match'][self::LIMIT_ZERO_MARKER_FIELD] ?? null)) {
return true;
}
}

return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -89,11 +89,11 @@ private function getOrder(string $resourceClass, string $property, string $direc
$order = ['order' => strtolower($direction)];

if (null !== $nestedPath = $this->getNestedFieldPath($resourceClass, $property)) {
$nestedPath = null === $this->nameConverter ? $nestedPath : $this->nameConverter->normalize($nestedPath);
$nestedPath = null === $this->nameConverter ? $nestedPath : $this->nameConverter->normalize($nestedPath, $resourceClass);
$order['nested'] = ['path' => $nestedPath];
}

$property = null === $this->nameConverter ? $property : $this->nameConverter->normalize($property);
$property = null === $this->nameConverter ? $property : $this->nameConverter->normalize($property, $resourceClass);

return [$property => $order];
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,9 @@ public function apply(array $clauseBody, string $resourceClass, ?string $operati
continue;
}

$property = null === $this->nameConverter ? $property : $this->nameConverter->normalize($property);
$property = null === $this->nameConverter ? $property : $this->nameConverter->normalize($property, $resourceClass, null, $context);
$nestedPath = $this->getNestedFieldPath($resourceClass, $property);
$nestedPath = null === $nestedPath || null === $this->nameConverter ? $nestedPath : $this->nameConverter->normalize($nestedPath);
$nestedPath = null === $nestedPath || null === $this->nameConverter ? $nestedPath : $this->nameConverter->normalize($nestedPath, $resourceClass, null, $context);

$searches[] = $this->getQuery($property, $values, $nestedPath);
}
Expand Down
4 changes: 2 additions & 2 deletions src/Bridge/Elasticsearch/DataProvider/Filter/OrderFilter.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,11 @@ public function apply(array $clauseBody, string $resourceClass, ?string $operati
$order = ['order' => $direction];

if (null !== $nestedPath = $this->getNestedFieldPath($resourceClass, $property)) {
$nestedPath = null === $this->nameConverter ? $nestedPath : $this->nameConverter->normalize($nestedPath);
$nestedPath = null === $this->nameConverter ? $nestedPath : $this->nameConverter->normalize($nestedPath, $resourceClass, null, $context);
$order['nested'] = ['path' => $nestedPath];
}

$property = null === $this->nameConverter ? $property : $this->nameConverter->normalize($property);
$property = null === $this->nameConverter ? $property : $this->nameConverter->normalize($property, $resourceClass, null, $context);
$orders[] = [$property => $order];
}

Expand Down
2 changes: 1 addition & 1 deletion src/Bridge/Elasticsearch/Serializer/ItemNormalizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ public function normalize($object, $format = null, array $context = [])
private function populateIdentifier(array $data, string $class): array
{
$identifier = $this->identifierExtractor->getIdentifierFromResourceClass($class);
$identifier = null === $this->nameConverter ? $identifier : $this->nameConverter->normalize($identifier);
$identifier = null === $this->nameConverter ? $identifier : $this->nameConverter->normalize($identifier, $class, self::FORMAT);

if (!isset($data['_source'][$identifier])) {
$data['_source'][$identifier] = $data['_id'];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

namespace ApiPlatform\Core\Bridge\Elasticsearch\Serializer\NameConverter;

use Symfony\Component\Serializer\NameConverter\AdvancedNameConverterInterface;
use Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter;
use Symfony\Component\Serializer\NameConverter\NameConverterInterface;

Expand All @@ -23,7 +24,7 @@
*
* @author Baptiste Meyer <baptiste.meyer@gmail.com>
*/
final class InnerFieldsNameConverter implements NameConverterInterface
final class InnerFieldsNameConverter implements AdvancedNameConverterInterface
{
private $decorated;

Expand All @@ -35,25 +36,25 @@ public function __construct(?NameConverterInterface $decorated = null)
/**
* {@inheritdoc}
*/
public function normalize($propertyName)
public function normalize($propertyName, string $class = null, string $format = null, $context = [])
{
return $this->convertInnerFields($propertyName, true);
return $this->convertInnerFields($propertyName, true, $class, $format, $context);
}

/**
* {@inheritdoc}
*/
public function denormalize($propertyName)
public function denormalize($propertyName, string $class = null, string $format = null, $context = [])
{
return $this->convertInnerFields($propertyName, false);
return $this->convertInnerFields($propertyName, false, $class, $format, $context);
}

private function convertInnerFields(string $propertyName, bool $normalization): string
private function convertInnerFields(string $propertyName, bool $normalization, string $class = null, string $format = null, $context = []): string
{
$convertedProperties = [];

foreach (explode('.', $propertyName) as $decomposedProperty) {
$convertedProperties[] = $this->decorated->{$normalization ? 'normalize' : 'denormalize'}($decomposedProperty);
$convertedProperties[] = $this->decorated->{$normalization ? 'normalize' : 'denormalize'}($decomposedProperty, $class, $format, $context);
}

return implode('.', $convertedProperties);
Expand Down
2 changes: 1 addition & 1 deletion src/Bridge/NelmioApiDoc/Parser/ApiPlatformParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ private function getPropertyMetadata(ResourceMetadata $resourceMetadata, string
($propertyMetadata->isReadable() && self::OUT_PREFIX === $io) ||
($propertyMetadata->isWritable() && self::IN_PREFIX === $io)
) {
$normalizedPropertyName = $this->nameConverter ? $this->nameConverter->normalize($propertyName) : $propertyName;
$normalizedPropertyName = $this->nameConverter ? $this->nameConverter->normalize($propertyName, $resourceClass) : $propertyName;
$data[$normalizedPropertyName] = $this->parseProperty($resourceMetadata, $propertyMetadata, $io, null, $visited);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
use ApiPlatform\Core\DataProvider\CollectionDataProviderInterface;
use ApiPlatform\Core\DataProvider\ItemDataProviderInterface;
use ApiPlatform\Core\DataProvider\SubresourceDataProviderInterface;
use ApiPlatform\Core\DataTransformer\DataTransformerInterface;
use ApiPlatform\Core\Exception\RuntimeException;
use ApiPlatform\Core\GraphQl\Type\Definition\TypeInterface as GraphQlTypeInterface;
use Doctrine\Common\Annotations\Annotation;
Expand Down Expand Up @@ -79,10 +80,6 @@ public function prepend(ContainerBuilder $container)
if (!isset($propertyInfoConfig['enabled'])) {
$container->prependExtensionConfig('framework', ['property_info' => ['enabled' => true]]);
}

if (isset($serializerConfig['name_converter'])) {
$container->prependExtensionConfig('api_platform', ['name_converter' => $serializerConfig['name_converter']]);
}
}

/**
Expand Down Expand Up @@ -149,6 +146,7 @@ public function load(array $configs, ContainerBuilder $container)
$this->registerMercureConfiguration($container, $config, $loader, $useDoctrine);
$this->registerMessengerConfiguration($config, $loader);
$this->registerElasticsearchConfiguration($container, $config, $loader);
$this->registerDataTransformerConfiguration($container);
}

/**
Expand Down Expand Up @@ -447,6 +445,7 @@ private function registerCacheConfiguration(ContainerBuilder $container)
$container->register('api_platform.cache.route_name_resolver', ArrayAdapter::class);
$container->register('api_platform.cache.identifiers_extractor', ArrayAdapter::class);
$container->register('api_platform.cache.subresource_operation_factory', ArrayAdapter::class);
$container->register('api_platform.elasticsearch.cache.metadata.document', ArrayAdapter::class);
}

/**
Expand Down Expand Up @@ -602,4 +601,10 @@ private function registerElasticsearchConfiguration(ContainerBuilder $container,
$container->setParameter('api_platform.elasticsearch.hosts', $config['elasticsearch']['hosts']);
$container->setParameter('api_platform.elasticsearch.mapping', $config['elasticsearch']['mapping']);
}

private function registerDataTransformerConfiguration(ContainerBuilder $container)
{
$container->registerForAutoconfiguration(DataTransformerInterface::class)
->addTag('api_platform.data_transformer');
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?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\Bridge\Symfony\Bundle\DependencyInjection\Compiler;

use ApiPlatform\Core\Exception\RuntimeException;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;

/**
* Injects the metadata aware name converter if available.
*
* @internal
*
* @author Antoine Bluchet <soyuka@gmail.com>
*/
final class MetadataAwareNameConverterPass implements CompilerPassInterface
{
/**
* {@inheritdoc}
*
* @throws RuntimeException
*/
public function process(ContainerBuilder $container)
{
if (!$container->hasAlias('api_platform.name_converter') && $container->hasDefinition('serializer.name_converter.metadata_aware')) {
$container->setAlias('api_platform.name_converter', 'serializer.name_converter.metadata_aware');
}
}
}
8 changes: 1 addition & 7 deletions src/Bridge/Symfony/Bundle/Resources/config/api.xml
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@
<argument type="service" id="api_platform.item_data_provider" on-invalid="ignore" />
<argument>%api_platform.allow_plain_identifiers%</argument>
<argument>null</argument>
<argument type="service" id="api_platform.data_transformer" on-invalid="ignore" />
<argument type="tagged" tag="api_platform.data_transformer" on-invalid="ignore" />
<argument type="service" id="api_platform.metadata.resource.metadata_factory" on-invalid="ignore" />
<argument>true</argument>

Expand Down Expand Up @@ -287,12 +287,6 @@
<service id="api_platform.cache.subresource_operation_factory" parent="cache.system" public="false">
<tag name="cache.pool" />
</service>

<service id="api_platform.data_transformer.chain_transformer" class="ApiPlatform\Core\DataTransformer\ChainDataTransformer" public="false">
<argument type="tagged" tag="api_platform.data_transformer" />
</service>

<service id="api_platform.data_transformer" alias="api_platform.data_transformer.chain_transformer" />
</services>

</container>
Loading