Skip to content

Commit

Permalink
fix(elasticsearch): elasticsearch BC
Browse files Browse the repository at this point in the history
  • Loading branch information
vincentchalamon committed Dec 20, 2022
1 parent d3a584c commit f22fa73
Show file tree
Hide file tree
Showing 12 changed files with 306 additions and 59 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,62 @@

namespace ApiPlatform\Core\Bridge\Elasticsearch\DataProvider\Extension;

class_exists(\ApiPlatform\Elasticsearch\Extension\AbstractFilterExtension::class);
use ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface;
use Psr\Container\ContainerInterface;

if (false) {
class AbstractFilterExtension extends \ApiPlatform\Elasticsearch\Extension\AbstractFilterExtension
/**
* Abstract class for easing the implementation of a filter extension.
*
* @experimental
*
* @author Baptiste Meyer <baptiste.meyer@gmail.com>
*/
abstract class AbstractFilterExtension implements RequestBodySearchCollectionExtensionInterface
{
private $resourceMetadataFactory;
private $filterLocator;

public function __construct(ResourceMetadataFactoryInterface $resourceMetadataFactory, ContainerInterface $filterLocator)
{
$this->resourceMetadataFactory = $resourceMetadataFactory;
$this->filterLocator = $filterLocator;
}

/**
* {@inheritdoc}
*/
public function applyToCollection(array $requestBody, string $resourceClass, ?string $operationName = null, array $context = []): array
{
$resourceMetadata = $this->resourceMetadataFactory->create($resourceClass);
$resourceFilters = $resourceMetadata->getCollectionOperationAttribute($operationName, 'filters', [], true);

if (!$resourceFilters) {
return $requestBody;
}

$context['filters'] = $context['filters'] ?? [];
$clauseBody = [];

foreach ($resourceFilters as $filterId) {
if ($this->filterLocator->has($filterId) && is_a($filter = $this->filterLocator->get($filterId), $this->getFilterInterface())) {
$clauseBody = $filter->apply($clauseBody, $resourceClass, $operationName, $context);
}
}

if (!$clauseBody) {
return $requestBody;
}

return $this->alterRequestBody($requestBody, $clauseBody);
}

/**
* Gets the related filter interface.
*/
abstract protected function getFilterInterface(): string;

/**
* Alters the request body.
*/
abstract protected function alterRequestBody(array $requestBody, array $clauseBody): array;
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,39 @@

namespace ApiPlatform\Core\Bridge\Elasticsearch\DataProvider\Extension;

class_exists(\ApiPlatform\Elasticsearch\Extension\ConstantScoreFilterExtension::class);
use ApiPlatform\Core\Bridge\Elasticsearch\DataProvider\Filter\ConstantScoreFilterInterface;

if (false) {
final class ConstantScoreFilterExtension extends \ApiPlatform\Elasticsearch\Extension\ConstantScoreFilterExtension
/**
* Applies filter clauses while executing a constant score query.
*
* @see https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-constant-score-query.html
*
* @experimental
*
* @author Baptiste Meyer <baptiste.meyer@gmail.com>
*/
final class ConstantScoreFilterExtension extends AbstractFilterExtension
{
/**
* {@inheritdoc}
*/
protected function getFilterInterface(): string
{
return ConstantScoreFilterInterface::class;
}

/**
* {@inheritdoc}
*/
protected function alterRequestBody(array $requestBody, array $clauseBody): array
{
$requestBody['query'] = $requestBody['query'] ?? [];
$requestBody['query'] += [
'constant_score' => [
'filter' => $clauseBody,
],
];

return $requestBody;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,88 @@

namespace ApiPlatform\Core\Bridge\Elasticsearch\DataProvider\Extension;

class_exists(\ApiPlatform\Elasticsearch\Extension\SortExtension::class);
use ApiPlatform\Core\Api\ResourceClassResolverInterface;
use ApiPlatform\Core\Bridge\Elasticsearch\Api\IdentifierExtractorInterface;
use ApiPlatform\Core\Bridge\Elasticsearch\Util\FieldDatatypeTrait;
use ApiPlatform\Core\Metadata\Property\Factory\PropertyMetadataFactoryInterface;
use ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface;
use Symfony\Component\Serializer\NameConverter\NameConverterInterface;

if (false) {
final class SortExtension extends \ApiPlatform\Elasticsearch\Extension\SortExtension
/**
* Applies selected sorting while querying resource collection.
*
* @see https://www.elastic.co/guide/en/elasticsearch/reference/current/search-request-sort.html
*
* @experimental
*
* @author Baptiste Meyer <baptiste.meyer@gmail.com>
*/
final class SortExtension implements RequestBodySearchCollectionExtensionInterface
{
use FieldDatatypeTrait;

private $defaultDirection;
private $identifierExtractor;
private $resourceMetadataFactory;
private $nameConverter;

public function __construct(ResourceMetadataFactoryInterface $resourceMetadataFactory, IdentifierExtractorInterface $identifierExtractor, PropertyMetadataFactoryInterface $propertyMetadataFactory, ResourceClassResolverInterface $resourceClassResolver, ?NameConverterInterface $nameConverter = null, ?string $defaultDirection = null)
{
$this->resourceMetadataFactory = $resourceMetadataFactory;
$this->identifierExtractor = $identifierExtractor;
$this->propertyMetadataFactory = $propertyMetadataFactory;
$this->resourceClassResolver = $resourceClassResolver;
$this->nameConverter = $nameConverter;
$this->defaultDirection = $defaultDirection;
}

/**
* {@inheritdoc}
*/
public function applyToCollection(array $requestBody, string $resourceClass, ?string $operationName = null, array $context = []): array
{
$orders = [];

if (
null !== ($defaultOrder = $this->resourceMetadataFactory->create($resourceClass)->getAttribute('order'))
&& \is_array($defaultOrder)
) {
foreach ($defaultOrder as $property => $direction) {
if (\is_int($property)) {
$property = $direction;
$direction = 'asc';
}

$orders[] = $this->getOrder($resourceClass, $property, $direction);
}
} elseif (null !== $this->defaultDirection) {
$orders[] = $this->getOrder(
$resourceClass,
$this->identifierExtractor->getIdentifierFromResourceClass($resourceClass),
$this->defaultDirection
);
}

if (!$orders) {
return $requestBody;
}

$requestBody['sort'] = array_merge_recursive($requestBody['sort'] ?? [], $orders);

return $requestBody;
}

private function getOrder(string $resourceClass, string $property, string $direction): array
{
$order = ['order' => strtolower($direction)];

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

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

return [$property => $order];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,34 @@

namespace ApiPlatform\Core\Bridge\Elasticsearch\DataProvider\Extension;

class_exists(\ApiPlatform\Elasticsearch\Extension\SortFilterExtension::class);
use ApiPlatform\Core\Bridge\Elasticsearch\DataProvider\Filter\SortFilterInterface;

if (false) {
final class SortFilterExtension extends \ApiPlatform\Elasticsearch\Extension\SortFilterExtension
/**
* Applies filters on the sort parameter while querying resource collection.
*
* @see https://www.elastic.co/guide/en/elasticsearch/reference/current/search-request-sort.html
*
* @experimental
*
* @author Baptiste Meyer <baptiste.meyer@gmail.com>
*/
final class SortFilterExtension extends AbstractFilterExtension
{
/**
* {@inheritdoc}
*/
protected function getFilterInterface(): string
{
return SortFilterInterface::class;
}

/**
* {@inheritdoc}
*/
protected function alterRequestBody(array $requestBody, array $clauseBody): array
{
$requestBody['sort'] = array_merge_recursive($requestBody['sort'] ?? [], $clauseBody);

return $requestBody;
}
}
22 changes: 0 additions & 22 deletions src/Core/Bridge/Elasticsearch/Serializer/DocumentNormalizer.php

This file was deleted.

86 changes: 83 additions & 3 deletions src/Core/Bridge/Elasticsearch/Serializer/ItemNormalizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,90 @@

namespace ApiPlatform\Core\Bridge\Elasticsearch\Serializer;

class_exists(\ApiPlatform\Elasticsearch\Serializer\ItemNormalizer::class);
use ApiPlatform\Core\Bridge\Elasticsearch\Api\IdentifierExtractorInterface;
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface;
use Symfony\Component\Serializer\Exception\LogicException;
use Symfony\Component\Serializer\Mapping\ClassDiscriminatorResolverInterface;
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface;
use Symfony\Component\Serializer\NameConverter\NameConverterInterface;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;

if (false) {
final class ItemNormalizer extends \ApiPlatform\Elasticsearch\Serializer\ItemNormalizer
/**
* Item denormalizer for Elasticsearch.
*
* @experimental
*
* @author Baptiste Meyer <baptiste.meyer@gmail.com>
*/
final class ItemNormalizer extends ObjectNormalizer
{
public const FORMAT = 'elasticsearch';

private $identifierExtractor;

public function __construct(IdentifierExtractorInterface $identifierExtractor, ClassMetadataFactoryInterface $classMetadataFactory = null, NameConverterInterface $nameConverter = null, PropertyAccessorInterface $propertyAccessor = null, PropertyTypeExtractorInterface $propertyTypeExtractor = null, ClassDiscriminatorResolverInterface $classDiscriminatorResolver = null, callable $objectClassResolver = null, array $defaultContext = [])
{
parent::__construct($classMetadataFactory, $nameConverter, $propertyAccessor, $propertyTypeExtractor, $classDiscriminatorResolver, $objectClassResolver, $defaultContext);

$this->identifierExtractor = $identifierExtractor;
}

/**
* {@inheritdoc}
*/
public function supportsDenormalization($data, $type, $format = null): bool
{
return self::FORMAT === $format && parent::supportsDenormalization($data, $type, $format);
}

/**
* {@inheritdoc}
*
* @return mixed
*/
public function denormalize($data, $class, $format = null, array $context = [])
{
if (\is_string($data['_id'] ?? null) && \is_array($data['_source'] ?? null)) {
$data = $this->populateIdentifier($data, $class)['_source'];
}

return parent::denormalize($data, $class, $format, $context);
}

/**
* {@inheritdoc}
*/
public function supportsNormalization($data, $format = null): bool
{
// prevent the use of lower priority normalizers (e.g. serializer.normalizer.object) for this format
return self::FORMAT === $format;
}

/**
* {@inheritdoc}
*
* @throws LogicException
*
* @return mixed
*/
public function normalize($object, $format = null, array $context = [])
{
throw new LogicException(sprintf('%s is a write-only format.', self::FORMAT));
}

/**
* Populates the resource identifier with the document identifier if not present in the original JSON document.
*/
private function populateIdentifier(array $data, string $class): array
{
$identifier = $this->identifierExtractor->getIdentifierFromResourceClass($class);
$identifier = null === $this->nameConverter ? $identifier : $this->nameConverter->normalize($identifier, $class, self::FORMAT);

if (!isset($data['_source'][$identifier])) {
$data['_source'][$identifier] = $data['_id'];
}

return $data;
}
}
2 changes: 0 additions & 2 deletions src/Elasticsearch/Serializer/DocumentNormalizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -119,5 +119,3 @@ private function populateIdentifier(array $data, string $class): array
return $data;
}
}

class_alias(DocumentNormalizer::class, \ApiPlatform\Core\Bridge\Elasticsearch\Serializer\DocumentNormalizer::class);
2 changes: 0 additions & 2 deletions src/Elasticsearch/Serializer/ItemNormalizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -111,5 +111,3 @@ public function setSerializer(SerializerInterface $serializer)
$this->decorated->setSerializer($serializer);
}
}

class_alias(ItemNormalizer::class, \ApiPlatform\Core\Bridge\Elasticsearch\Serializer\ItemNormalizer::class);
16 changes: 0 additions & 16 deletions src/Symfony/Bundle/Resources/config/elasticsearch.xml
Original file line number Diff line number Diff line change
Expand Up @@ -45,22 +45,6 @@
<argument type="service" id="api_platform.name_converter" on-invalid="ignore" />
</service>

<service id="api_platform.elasticsearch.normalizer.item" class="ApiPlatform\Elasticsearch\Serializer\ItemNormalizer" decorates="api_platform.serializer.normalizer.item" public="false">
<argument type="service" id="api_platform.elasticsearch.normalizer.item.inner" />
</service>

<service id="api_platform.elasticsearch.normalizer.document" class="ApiPlatform\Elasticsearch\Serializer\DocumentNormalizer" public="false">
<argument type="service" id="api_platform.metadata.resource.metadata_collection_factory.retro_compatible" />
<argument type="service" id="serializer.mapping.class_metadata_factory" />
<argument type="service" id="api_platform.elasticsearch.name_converter.inner_fields" />
<argument type="service" id="serializer.property_accessor" />
<argument type="service" id="property_info" on-invalid="ignore" />
<argument type="service" id="serializer.mapping.class_discriminator_resolver" on-invalid="ignore" />

<!-- Run after serializer.normalizer.data_uri but before serializer.normalizer.object -->
<tag name="serializer.normalizer" priority="-922" />
</service>

<service id="api_platform.elasticsearch.item_data_provider" class="ApiPlatform\Core\Bridge\Elasticsearch\DataProvider\ItemDataProvider" public="false">
<argument type="service" id="api_platform.elasticsearch.client" />
<argument type="service" id="api_platform.elasticsearch.metadata.document.metadata_factory" />
Expand Down

0 comments on commit f22fa73

Please sign in to comment.