From 022fb6a05701fa5a08f6357c82c1b95fbf0fe4b6 Mon Sep 17 00:00:00 2001 From: Antoine Bluchet Date: Mon, 12 Dec 2022 14:22:38 +0100 Subject: [PATCH 1/5] chore: behat contexts ^3.3.6 w/ soyuka/contexts (#5262) --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index dc449da81ca..d2473e4d562 100644 --- a/composer.json +++ b/composer.json @@ -56,7 +56,7 @@ "psr/log": "^1.0 || ^2.0 || ^3.0", "ramsey/uuid": "^3.7 || ^4.0", "ramsey/uuid-doctrine": "^1.4", - "soyuka/contexts": "dev-main", + "soyuka/contexts": "^3.3.6", "soyuka/stubs-mongodb": "^1.0", "symfony/asset": "^3.4 || ^4.4 || ^5.1 || ^6.0", "symfony/browser-kit": "^4.4 || ^5.1 || ^6.0", From 983a7132688d8ac845f3c8186af0907dde60a0f3 Mon Sep 17 00:00:00 2001 From: Vincent Chalamon Date: Tue, 20 Dec 2022 14:37:54 +0100 Subject: [PATCH 2/5] chore: update GitHub Actions --- .github/workflows/ci.yml | 147 ++++++++++++++++++++------------------- 1 file changed, 76 insertions(+), 71 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 19c99aef1b6..a870c48610d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -40,7 +40,7 @@ jobs: fail-fast: false steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Setup PHP uses: shivammathur/setup-php@v2 with: @@ -66,7 +66,7 @@ jobs: SYMFONY_PHPUNIT_VERSION: '9.5' steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Setup PHP uses: shivammathur/setup-php@v2 with: @@ -77,9 +77,9 @@ jobs: ini-values: memory_limit=-1 - name: Get composer cache directory id: composercache - run: echo "::set-output name=dir::$(composer config cache-files-dir)" + run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT - name: Cache dependencies - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: ${{ steps.composercache.outputs.dir }} key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }} @@ -91,7 +91,7 @@ jobs: - name: Install PHPUnit run: vendor/bin/simple-phpunit --version - name: Cache PHPStan results - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: /tmp/phpstan key: phpstan-php${{ matrix.php }}-${{ github.sha }} @@ -129,7 +129,7 @@ jobs: fail-fast: false steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Setup PHP uses: shivammathur/setup-php@v2 with: @@ -140,9 +140,9 @@ jobs: ini-values: memory_limit=-1 - name: Get composer cache directory id: composercache - run: echo "::set-output name=dir::$(composer config cache-files-dir)" + run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT - name: Cache dependencies - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: ${{ steps.composercache.outputs.dir }} key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }} @@ -159,7 +159,7 @@ jobs: # https://github.com/doctrine/dbal/issues/5570 - name: Fix Doctrine dependencies if: (startsWith(matrix.php, '7.1') || startsWith(matrix.php, '7.2') || startsWith(matrix.php, '7.3')) - run: composer require "doctrine/orm:<2.13" --dev --no-interaction --no-progress --ansi + run: composer require "doctrine/orm:<2.13" -W --dev --no-interaction --no-progress --ansi - name: Update project dependencies run: composer update --no-interaction --no-progress --ansi - name: Require Symfony components @@ -182,15 +182,16 @@ jobs: run: ./vendor/bin/simple-phpunit --stop-on-failure tests/Metadata/Resource/Factory/ - name: Upload test artifacts if: always() - uses: actions/upload-artifact@v1 + uses: actions/upload-artifact@v3 with: name: phpunit-logs-php${{ matrix.php }} path: build/logs/phpunit continue-on-error: true - name: Upload coverage results to Codecov if: matrix.coverage - uses: codecov/codecov-action@v1 + uses: codecov/codecov-action@v3 with: + directory: build/logs/phpunit name: phpunit-php${{ matrix.php }} flags: phpunit fail_ci_if_error: true @@ -224,7 +225,7 @@ jobs: fail-fast: false steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Setup PHP uses: shivammathur/setup-php@v2 with: @@ -235,9 +236,9 @@ jobs: ini-values: memory_limit=-1 - name: Get composer cache directory id: composercache - run: echo "::set-output name=dir::$(composer config cache-files-dir)" + run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT - name: Cache dependencies - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: ${{ steps.composercache.outputs.dir }} key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }} @@ -256,7 +257,7 @@ jobs: # https://github.com/doctrine/dbal/issues/5570 - name: Fix Doctrine dependencies if: (startsWith(matrix.php, '7.1') || startsWith(matrix.php, '7.2') || startsWith(matrix.php, '7.3')) - run: composer require "doctrine/orm:<2.13" --dev --no-interaction --no-progress --ansi + run: composer require "doctrine/orm:<2.13" -W --dev --no-interaction --no-progress --ansi - name: Require Symfony components if: (!startsWith(matrix.php, '7.1')) run: composer require symfony/uid --dev --no-interaction --no-progress --ansi @@ -300,15 +301,16 @@ jobs: continue-on-error: true - name: Upload test artifacts if: always() - uses: actions/upload-artifact@v1 + uses: actions/upload-artifact@v3 with: name: behat-logs-php${{ matrix.php }} path: build/logs/behat continue-on-error: true - name: Upload coverage results to Codecov if: matrix.coverage - uses: codecov/codecov-action@v1 + uses: codecov/codecov-action@v3 with: + directory: build/logs/behat name: behat-php${{ matrix.php }} flags: behat fail_ci_if_error: true @@ -330,7 +332,7 @@ jobs: tests/Fixtures/app/console api:openapi:export --spec-version=3 -o build/out/openapi/openapi_v3.json tests/Fixtures/app/console api:openapi:export --spec-version=3 --yaml -o build/out/openapi/openapi_v3.yaml - name: Setup node - uses: actions/setup-node@v2 + uses: actions/setup-node@v3 with: node-version: '14' - name: Validate OpenAPI documents @@ -341,7 +343,7 @@ jobs: npx git+https://github.com/soyuka/swagger-cli#master validate build/out/openapi/openapi_v3.yaml - name: Upload OpenAPI artifacts if: always() - uses: actions/upload-artifact@v1 + uses: actions/upload-artifact@v3 with: name: openapi-docs-php${{ matrix.php }} path: build/out/openapi @@ -358,7 +360,7 @@ jobs: fail-fast: false steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Setup PHP uses: shivammathur/setup-php@v2 with: @@ -369,9 +371,9 @@ jobs: ini-values: memory_limit=-1 - name: Get composer cache directory id: composercache - run: echo "::set-output name=dir::$(composer config cache-files-dir)" + run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT - name: Cache dependencies - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: ${{ steps.composercache.outputs.dir }} key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }} @@ -398,7 +400,7 @@ jobs: fail-fast: false steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Setup PHP uses: shivammathur/setup-php@v2 with: @@ -409,9 +411,9 @@ jobs: ini-values: memory_limit=-1 - name: Get composer cache directory id: composercache - run: echo "::set-output name=dir::$(composer config cache-files-dir)" + run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT - name: Cache dependencies - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: ${{ steps.composercache.outputs.dir }} key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }} @@ -443,7 +445,7 @@ jobs: PGPASSWORD: apiplatformrocks steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Setup postgres run: | sudo systemctl start postgresql @@ -460,9 +462,9 @@ jobs: ini-values: memory_limit=-1 - name: Get composer cache directory id: composercache - run: echo "::set-output name=dir::$(composer config cache-files-dir)" + run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT - name: Cache dependencies - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: ${{ steps.composercache.outputs.dir }} key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }} @@ -501,7 +503,7 @@ jobs: DATABASE_URL: mysql://root:root@127.0.0.1/api_platform_test steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Setup PHP uses: shivammathur/setup-php@v2 with: @@ -512,9 +514,9 @@ jobs: ini-values: memory_limit=-1 - name: Get composer cache directory id: composercache - run: echo "::set-output name=dir::$(composer config cache-files-dir)" + run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT - name: Cache dependencies - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: ${{ steps.composercache.outputs.dir }} key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }} @@ -544,7 +546,7 @@ jobs: MONGODB_URL: mongodb://localhost:27017 steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Setup MongoDB run: | sudo apt update @@ -566,9 +568,9 @@ jobs: run: echo "COVERAGE=1" >> $GITHUB_ENV - name: Get composer cache directory id: composercache - run: echo "::set-output name=dir::$(composer config cache-files-dir)" + run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT - name: Cache dependencies - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: ${{ steps.composercache.outputs.dir }} key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }} @@ -601,14 +603,15 @@ jobs: continue-on-error: true - name: Upload test artifacts if: always() - uses: actions/upload-artifact@v1 + uses: actions/upload-artifact@v3 with: name: behat-logs-php${{ matrix.php }} path: build/logs/behat continue-on-error: true - name: Upload coverage results to Codecov - uses: codecov/codecov-action@v1 + uses: codecov/codecov-action@v3 with: + directory: build/logs/behat name: behat-php${{ matrix.php }} flags: behat fail_ci_if_error: true @@ -636,7 +639,7 @@ jobs: APP_ENV: elasticsearch steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Configure sysctl limits run: | sudo swapoff -a @@ -657,9 +660,9 @@ jobs: ini-values: memory_limit=-1 - name: Get composer cache directory id: composercache - run: echo "::set-output name=dir::$(composer config cache-files-dir)" + run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT - name: Cache dependencies - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: ${{ steps.composercache.outputs.dir }} key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }} @@ -688,7 +691,7 @@ jobs: SYMFONY_DEPRECATIONS_HELPER: max[total]=0 steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Setup PHP uses: shivammathur/setup-php@v2 with: @@ -699,9 +702,9 @@ jobs: ini-values: memory_limit=-1 - name: Get composer cache directory id: composercache - run: echo "::set-output name=dir::$(composer config cache-files-dir)" + run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT - name: Cache dependencies - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: ${{ steps.composercache.outputs.dir }} key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }} @@ -732,7 +735,7 @@ jobs: SYMFONY_DEPRECATIONS_HELPER: max[self]=0 steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Setup PHP uses: shivammathur/setup-php@v2 with: @@ -743,11 +746,11 @@ jobs: ini-values: memory_limit=-1 - name: Get composer cache directory id: composercache - run: echo "::set-output name=dir::$(composer config cache-files-dir)" + run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT - name: Allow unstable project dependencies run: composer config minimum-stability dev - name: Cache dependencies - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: ${{ steps.composercache.outputs.dir }} key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }} @@ -776,7 +779,7 @@ jobs: fail-fast: false steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Setup PHP uses: shivammathur/setup-php@v2 with: @@ -789,11 +792,11 @@ jobs: run: sudo apt-get install moreutils - name: Get composer cache directory id: composercache - run: echo "::set-output name=dir::$(composer config cache-files-dir)" + run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT - name: Allow unstable project dependencies run: composer config minimum-stability dev - name: Cache dependencies - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: ${{ steps.composercache.outputs.dir }} key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }} @@ -822,7 +825,7 @@ jobs: fail-fast: false steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Setup PHP uses: shivammathur/setup-php@v2 with: @@ -832,9 +835,9 @@ jobs: ini-values: memory_limit=-1 - name: Get composer cache directory id: composercache - run: echo "::set-output name=dir::$(composer config cache-files-dir)" + run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT - name: Cache dependencies - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: ${{ steps.composercache.outputs.dir }} key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }} @@ -861,7 +864,7 @@ jobs: vendor/bin/behat --out=std --format=progress --format=junit --out=build/logs/behat/junit --profile=default --no-interaction - name: Upload test artifacts if: always() - uses: actions/upload-artifact@v1 + uses: actions/upload-artifact@v3 with: name: behat-logs-php${{ matrix.php }} path: build/logs/behat @@ -874,7 +877,7 @@ jobs: tests/Fixtures/app/console api:openapi:export --spec-version=3 -o build/out/openapi/openapi_v3.json tests/Fixtures/app/console api:openapi:export --spec-version=3 --yaml -o build/out/openapi/openapi_v3.yaml - name: Setup node - uses: actions/setup-node@v2 + uses: actions/setup-node@v3 with: node-version: '14' - name: Validate OpenAPI documents @@ -885,7 +888,7 @@ jobs: npx git+https://github.com/soyuka/swagger-cli#master validate build/out/openapi/openapi_v3.yaml - name: Upload OpenAPI artifacts if: always() - uses: actions/upload-artifact@v1 + uses: actions/upload-artifact@v3 with: name: openapi-docs-php${{ matrix.php }} path: build/out/openapi @@ -905,7 +908,7 @@ jobs: fail-fast: false steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Setup MongoDB run: | sudo apt update @@ -924,9 +927,9 @@ jobs: ini-values: memory_limit=-1 - name: Get composer cache directory id: composercache - run: echo "::set-output name=dir::$(composer config cache-files-dir)" + run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT - name: Cache dependencies - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: ${{ steps.composercache.outputs.dir }} key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }} @@ -953,7 +956,7 @@ jobs: vendor/bin/behat --out=std --format=progress --format=junit --out=build/logs/behat/junit --profile=mongodb --no-interaction - name: Upload test artifacts if: always() - uses: actions/upload-artifact@v1 + uses: actions/upload-artifact@v3 with: name: behat-logs-php${{ matrix.php }} path: build/logs/behat @@ -975,7 +978,7 @@ jobs: DATABASE_URL: sqlite:///%kernel.project_dir%/var/data.db steps: - name: Checkout - uses: actions/checkout@v1 + uses: actions/checkout@v3 - name: Setup PHP with pre-release PECL extension uses: shivammathur/setup-php@v2 with: @@ -986,9 +989,10 @@ jobs: ini-values: memory_limit=-1 - name: Get composer cache directory id: composercache - run: echo "::set-output name=dir::$(composer config cache-files-dir)" + shell: bash + run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT - name: Cache dependencies - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: ${{ steps.composercache.outputs.dir }} key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }} @@ -1024,7 +1028,7 @@ jobs: DATABASE_URL: sqlite:///%kernel.project_dir%/var/data.db steps: - name: Checkout - uses: actions/checkout@v1 + uses: actions/checkout@v3 - name: Setup PHP with pre-release PECL extension uses: shivammathur/setup-php@v2 with: @@ -1035,9 +1039,10 @@ jobs: ini-values: memory_limit=-1 - name: Get composer cache directory id: composercache - run: echo "::set-output name=dir::$(composer config cache-files-dir)" + shell: bash + run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT - name: Cache dependencies - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: ${{ steps.composercache.outputs.dir }} key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }} @@ -1075,7 +1080,7 @@ jobs: fail-fast: false steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Setup PHP uses: shivammathur/setup-php@v2 with: @@ -1086,9 +1091,9 @@ jobs: ini-values: memory_limit=-1 - name: Get composer cache directory id: composercache - run: echo "::set-output name=dir::$(composer config cache-files-dir)" + run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT - name: Cache dependencies - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: ${{ steps.composercache.outputs.dir }} key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }} @@ -1119,7 +1124,7 @@ jobs: fail-fast: false steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Setup PHP uses: shivammathur/setup-php@v2 with: @@ -1130,9 +1135,9 @@ jobs: ini-values: memory_limit=-1 - name: Get composer cache directory id: composercache - run: echo "::set-output name=dir::$(composer config cache-files-dir)" + run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT - name: Cache dependencies - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: ${{ steps.composercache.outputs.dir }} key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }} @@ -1162,7 +1167,7 @@ jobs: tests/Fixtures/app/console api:openapi:export --spec-version=3 -o build/out/openapi/openapi_v3.json tests/Fixtures/app/console api:openapi:export --spec-version=3 --yaml -o build/out/openapi/openapi_v3.yaml - name: Setup node - uses: actions/setup-node@v2 + uses: actions/setup-node@v3 with: node-version: '14' - name: Validate OpenAPI documents From f22fa73f41663f2c6a2391d3c1b8623098a51a0d Mon Sep 17 00:00:00 2001 From: Vincent Chalamon Date: Tue, 20 Dec 2022 14:23:29 +0100 Subject: [PATCH 3/5] fix(elasticsearch): elasticsearch BC --- .../Extension/AbstractFilterExtension.php | 58 ++++++++++++- .../ConstantScoreFilterExtension.php | 35 +++++++- .../DataProvider/Extension/SortExtension.php | 84 +++++++++++++++++- .../Extension/SortFilterExtension.php | 30 ++++++- .../Serializer/DocumentNormalizer.php | 22 ----- .../Serializer/ItemNormalizer.php | 86 ++++++++++++++++++- .../Serializer/DocumentNormalizer.php | 2 - .../Serializer/ItemNormalizer.php | 2 - .../Bundle/Resources/config/elasticsearch.xml | 16 ---- .../Resources/config/legacy/elasticsearch.xml | 12 +++ .../Resources/config/v3/elasticsearch.xml | 16 ++++ src/deprecation.php | 2 - 12 files changed, 306 insertions(+), 59 deletions(-) delete mode 100644 src/Core/Bridge/Elasticsearch/Serializer/DocumentNormalizer.php diff --git a/src/Core/Bridge/Elasticsearch/DataProvider/Extension/AbstractFilterExtension.php b/src/Core/Bridge/Elasticsearch/DataProvider/Extension/AbstractFilterExtension.php index 4db73e50a48..e01d219795c 100644 --- a/src/Core/Bridge/Elasticsearch/DataProvider/Extension/AbstractFilterExtension.php +++ b/src/Core/Bridge/Elasticsearch/DataProvider/Extension/AbstractFilterExtension.php @@ -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 + */ +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; } diff --git a/src/Core/Bridge/Elasticsearch/DataProvider/Extension/ConstantScoreFilterExtension.php b/src/Core/Bridge/Elasticsearch/DataProvider/Extension/ConstantScoreFilterExtension.php index 34c17757c37..8ec9326ec7a 100644 --- a/src/Core/Bridge/Elasticsearch/DataProvider/Extension/ConstantScoreFilterExtension.php +++ b/src/Core/Bridge/Elasticsearch/DataProvider/Extension/ConstantScoreFilterExtension.php @@ -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 + */ +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; } } diff --git a/src/Core/Bridge/Elasticsearch/DataProvider/Extension/SortExtension.php b/src/Core/Bridge/Elasticsearch/DataProvider/Extension/SortExtension.php index efaac85fe8f..06ee807d54c 100644 --- a/src/Core/Bridge/Elasticsearch/DataProvider/Extension/SortExtension.php +++ b/src/Core/Bridge/Elasticsearch/DataProvider/Extension/SortExtension.php @@ -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 + */ +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]; } } diff --git a/src/Core/Bridge/Elasticsearch/DataProvider/Extension/SortFilterExtension.php b/src/Core/Bridge/Elasticsearch/DataProvider/Extension/SortFilterExtension.php index 41d41d46eee..a752d486ad0 100644 --- a/src/Core/Bridge/Elasticsearch/DataProvider/Extension/SortFilterExtension.php +++ b/src/Core/Bridge/Elasticsearch/DataProvider/Extension/SortFilterExtension.php @@ -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 + */ +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; } } diff --git a/src/Core/Bridge/Elasticsearch/Serializer/DocumentNormalizer.php b/src/Core/Bridge/Elasticsearch/Serializer/DocumentNormalizer.php deleted file mode 100644 index eb59f592f40..00000000000 --- a/src/Core/Bridge/Elasticsearch/Serializer/DocumentNormalizer.php +++ /dev/null @@ -1,22 +0,0 @@ - - * - * 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\Elasticsearch\Serializer; - -class_exists(\ApiPlatform\Elasticsearch\Serializer\DocumentNormalizer::class); - -if (false) { - final class DocumentNormalizer extends \ApiPlatform\Elasticsearch\Serializer\DocumentNormalizer - { - } -} diff --git a/src/Core/Bridge/Elasticsearch/Serializer/ItemNormalizer.php b/src/Core/Bridge/Elasticsearch/Serializer/ItemNormalizer.php index d263305ffe6..f364e8729ae 100644 --- a/src/Core/Bridge/Elasticsearch/Serializer/ItemNormalizer.php +++ b/src/Core/Bridge/Elasticsearch/Serializer/ItemNormalizer.php @@ -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 + */ +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; } } diff --git a/src/Elasticsearch/Serializer/DocumentNormalizer.php b/src/Elasticsearch/Serializer/DocumentNormalizer.php index eb60aa71a1b..fb0cabe4de9 100644 --- a/src/Elasticsearch/Serializer/DocumentNormalizer.php +++ b/src/Elasticsearch/Serializer/DocumentNormalizer.php @@ -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); diff --git a/src/Elasticsearch/Serializer/ItemNormalizer.php b/src/Elasticsearch/Serializer/ItemNormalizer.php index 3e055bde29a..cad183402f3 100644 --- a/src/Elasticsearch/Serializer/ItemNormalizer.php +++ b/src/Elasticsearch/Serializer/ItemNormalizer.php @@ -111,5 +111,3 @@ public function setSerializer(SerializerInterface $serializer) $this->decorated->setSerializer($serializer); } } - -class_alias(ItemNormalizer::class, \ApiPlatform\Core\Bridge\Elasticsearch\Serializer\ItemNormalizer::class); diff --git a/src/Symfony/Bundle/Resources/config/elasticsearch.xml b/src/Symfony/Bundle/Resources/config/elasticsearch.xml index 974d95b0427..339b1c62b5b 100644 --- a/src/Symfony/Bundle/Resources/config/elasticsearch.xml +++ b/src/Symfony/Bundle/Resources/config/elasticsearch.xml @@ -45,22 +45,6 @@ - - - - - - - - - - - - - - - - diff --git a/src/Symfony/Bundle/Resources/config/legacy/elasticsearch.xml b/src/Symfony/Bundle/Resources/config/legacy/elasticsearch.xml index a68c67a78bd..cab9dfd381b 100644 --- a/src/Symfony/Bundle/Resources/config/legacy/elasticsearch.xml +++ b/src/Symfony/Bundle/Resources/config/legacy/elasticsearch.xml @@ -5,6 +5,18 @@ xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> + + + + + + + + + + + + diff --git a/src/Symfony/Bundle/Resources/config/v3/elasticsearch.xml b/src/Symfony/Bundle/Resources/config/v3/elasticsearch.xml index 72b02844000..f5c335c36b2 100644 --- a/src/Symfony/Bundle/Resources/config/v3/elasticsearch.xml +++ b/src/Symfony/Bundle/Resources/config/v3/elasticsearch.xml @@ -6,6 +6,22 @@ + + + + + + + + + + + + + + + + diff --git a/src/deprecation.php b/src/deprecation.php index 671b8e595cc..712603d4c95 100644 --- a/src/deprecation.php +++ b/src/deprecation.php @@ -85,8 +85,6 @@ class_alias($interfaceName, $oldInterfaceName); ApiPlatform\Core\Bridge\Elasticsearch\Metadata\Document\DocumentMetadata::class => ApiPlatform\Elasticsearch\Metadata\Document\DocumentMetadata::class, ApiPlatform\Core\Bridge\Elasticsearch\Serializer\NameConverter\InnerFieldsNameConverter::class => ApiPlatform\Elasticsearch\Serializer\NameConverter\InnerFieldsNameConverter::class, - ApiPlatform\Core\Bridge\Elasticsearch\Serializer\DocumentNormalizer::class => ApiPlatform\Elasticsearch\Serializer\DocumentNormalizer::class, - ApiPlatform\Core\Bridge\Elasticsearch\Serializer\ItemNormalizer::class => ApiPlatform\Elasticsearch\Serializer\ItemNormalizer::class, ApiPlatform\Core\Bridge\Elasticsearch\Util\FieldDatatypeTrait::class => ApiPlatform\Elasticsearch\Util\FieldDatatypeTrait::class, ApiPlatform\Core\Bridge\Elasticsearch\DataProvider\Paginator::class => ApiPlatform\Elasticsearch\Paginator::class, From 5baea781cf20249032fac337728da2f5617789db Mon Sep 17 00:00:00 2001 From: Danny v W Date: Fri, 30 Dec 2022 11:15:20 +0100 Subject: [PATCH 4/5] fix(metadata): fix extra properties method (#5294) --- .../Factory/UriTemplateResourceMetadataCollectionFactory.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Metadata/Resource/Factory/UriTemplateResourceMetadataCollectionFactory.php b/src/Metadata/Resource/Factory/UriTemplateResourceMetadataCollectionFactory.php index 65bc62d349e..22f4d4c5436 100644 --- a/src/Metadata/Resource/Factory/UriTemplateResourceMetadataCollectionFactory.php +++ b/src/Metadata/Resource/Factory/UriTemplateResourceMetadataCollectionFactory.php @@ -132,7 +132,7 @@ private function configureUriVariables($operation) return $this->normalizeUriVariables($operation); } - $hasUserConfiguredUriVariables = !($operation->getExtraProperties['is_legacy_resource_metadata'] ?? false); + $hasUserConfiguredUriVariables = !($operation->getExtraProperties()['is_legacy_resource_metadata'] ?? false); if (!$operation->getUriVariables()) { $hasUserConfiguredUriVariables = false; $operation = $operation->withUriVariables($this->transformLinksToUriVariables($this->linkFactory->createLinksFromIdentifiers($operation))); From 3cbb9a7975fb310a7d590fc1ec33f169c3b4810a Mon Sep 17 00:00:00 2001 From: Antoine Bluchet Date: Tue, 10 Jan 2023 17:20:54 +0100 Subject: [PATCH 5/5] chore: remove phpstan false positive (#5316) * chore: remove phpstan false positive * moar * foo --- src/Doctrine/Orm/Util/QueryBuilderHelper.php | 4 ++-- src/Test/DoctrineMongoDbOdmSetup.php | 2 +- src/Test/DoctrineMongoDbOdmTestCase.php | 2 +- tests/Doctrine/Orm/Extension/EagerLoadingExtensionTest.php | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Doctrine/Orm/Util/QueryBuilderHelper.php b/src/Doctrine/Orm/Util/QueryBuilderHelper.php index 2d6790da33c..48845002dcb 100644 --- a/src/Doctrine/Orm/Util/QueryBuilderHelper.php +++ b/src/Doctrine/Orm/Util/QueryBuilderHelper.php @@ -43,9 +43,9 @@ public static function addJoinOnce(QueryBuilder $queryBuilder, QueryNameGenerato $query = "$alias.$association"; if (Join::LEFT_JOIN === $joinType || QueryChecker::hasLeftJoin($queryBuilder)) { - $queryBuilder->leftJoin($query, $associationAlias, $conditionType, $condition); // @phpstan-ignore-line + $queryBuilder->leftJoin($query, $associationAlias, $conditionType, $condition); } else { - $queryBuilder->innerJoin($query, $associationAlias, $conditionType, $condition); // @phpstan-ignore-line + $queryBuilder->innerJoin($query, $associationAlias, $conditionType, $condition); } return $associationAlias; diff --git a/src/Test/DoctrineMongoDbOdmSetup.php b/src/Test/DoctrineMongoDbOdmSetup.php index db1052522fb..ed23666aef4 100644 --- a/src/Test/DoctrineMongoDbOdmSetup.php +++ b/src/Test/DoctrineMongoDbOdmSetup.php @@ -39,7 +39,7 @@ class DoctrineMongoDbOdmSetup public static function createAnnotationMetadataConfiguration(array $paths, bool $isDevMode = false, string $proxyDir = null, string $hydratorDir = null, Cache $cache = null): Configuration { $config = self::createConfiguration($isDevMode, $proxyDir, $hydratorDir, $cache); - $config->setMetadataDriverImpl($config->newDefaultAnnotationDriver($paths)); // @phpstan-ignore-line + $config->setMetadataDriverImpl($config->newDefaultAnnotationDriver($paths)); return $config; } diff --git a/src/Test/DoctrineMongoDbOdmTestCase.php b/src/Test/DoctrineMongoDbOdmTestCase.php index 45c3b8f78fb..cf5de7121a7 100644 --- a/src/Test/DoctrineMongoDbOdmTestCase.php +++ b/src/Test/DoctrineMongoDbOdmTestCase.php @@ -40,7 +40,7 @@ public static function createTestDocumentManager($paths = []) $config->setHydratorDir(sys_get_temp_dir()); $config->setProxyNamespace('SymfonyTests\Doctrine'); $config->setHydratorNamespace('SymfonyTests\Doctrine'); - $config->setMetadataDriverImpl(new AnnotationDriver(new AnnotationReader(), $paths)); // @phpstan-ignore-line + $config->setMetadataDriverImpl(new AnnotationDriver(new AnnotationReader(), $paths)); if (method_exists($config, 'setMetadataCache')) { $config->setMetadataCache(new ArrayAdapter()); } else { diff --git a/tests/Doctrine/Orm/Extension/EagerLoadingExtensionTest.php b/tests/Doctrine/Orm/Extension/EagerLoadingExtensionTest.php index 966c0421556..25bf5e1f44f 100644 --- a/tests/Doctrine/Orm/Extension/EagerLoadingExtensionTest.php +++ b/tests/Doctrine/Orm/Extension/EagerLoadingExtensionTest.php @@ -830,7 +830,7 @@ public function testApplyToCollectionWithExistingJoin(string $joinType): void $queryBuilderProphecy->getEntityManager()->willReturn($emProphecy); $queryBuilderProphecy->getDQLPart('join')->willReturn([ 'o' => [ - new Join($joinType, 'o.relatedDummy', 'existing_join_alias'), // @phpstan-ignore-line + new Join($joinType, 'o.relatedDummy', 'existing_join_alias'), ], ]); $queryBuilderProphecy->getDQLPart('select')->willReturn([]);