diff --git a/.github/workflows/reviewdog.yml b/.github/workflows/reviewdog.yml new file mode 100644 index 0000000..c027d01 --- /dev/null +++ b/.github/workflows/reviewdog.yml @@ -0,0 +1,29 @@ +name: Check coding styles + +on: [pull_request] + +jobs: + phpcs: + name: runner / phpcs + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + + - name: phpcs + uses: chrfritsch/action-drupal-coder@v1 + with: + github_token: ${{ secrets.github_token }} + level: error + filter_mode: nofilter + + misspell: + name: runner / misspell + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: reviewdog/action-misspell@v1 + with: + github_token: ${{ secrets.github_token }} + reporter: github-check + level: warning + locale: "US" diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..bc62441 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,81 @@ +name: Run module tests + +on: + pull_request: + paths-ignore: + - '**.md' + + schedule: + - cron: '0 6 * * *' + +env: + DRUPAL_TESTING_TEST_CODING_STYLES: false + DRUPAL_TESTING_DATABASE_USER: root + DRUPAL_TESTING_DATABASE_PASSWORD: root + DRUPAL_TESTING_DATABASE_ENGINE: mysql + DRUPAL_TESTING_HTTP_PORT: 8888 + +jobs: + build: + + runs-on: ubuntu-20.04 + + strategy: + matrix: + DRUPAL_TESTING_DRUPAL_VERSION: ['~8.9.0', '~9.1.0'] + PHP_VERSION: [ '7.4' ] + + steps: + - uses: actions/checkout@v2 + + - uses: actions/setup-node@v1 + with: + node-version: '12.x' + + - uses: shivammathur/setup-php@v2 + with: + coverage: none + php-version: ${{ matrix.PHP_VERSION }} + extensions: Imagick, gd, pdo_mysql + + - name: Start MySql service + run: | + sudo /etc/init.d/mysql start + mysql -uroot -proot -e "ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'root';" + + - name: Cache composer dependencies + uses: actions/cache@v1 + with: + path: ~/.composer/cache + key: ${{ runner.os }}-composer-cache-${{ hashFiles('**/composer.json') }} + restore-keys: ${{ runner.os }}-composer-cache- + + - name: Get build environment + run: composer global require thunder/drupal-testing + + - name: Prepare the build + run: test-drupal-project prepare_build + env: + DRUPAL_TESTING_DRUPAL_VERSION: ${{ matrix.DRUPAL_TESTING_DRUPAL_VERSION }} + + - name: Build the docroot + run: test-drupal-project build + + - name: Test for deprecations + run: test-drupal-project deprecation + if: ${{ matrix.DRUPAL_TESTING_DRUPAL_VERSION != '~9.1.0' }} + + - name: Install drupal + run: test-drupal-project install + env: + DRUPAL_TESTING_TEST_DEPRECATION: false + + - name: Setup Apache + uses: thunder/apache-shiva-php-action@v1 + with: + php-version: ${{ matrix.PHP_VERSION }} + site-directory: /tmp/test/graphql-views/install/web + http-port: ${{ env.DRUPAL_TESTING_HTTP_PORT }} + + - name: Run the tests + run: test-drupal-project diff --git a/.scrutinizer.yml b/.scrutinizer.yml deleted file mode 100644 index 6620688..0000000 --- a/.scrutinizer.yml +++ /dev/null @@ -1,6 +0,0 @@ -filter: - excluded_paths: - - 'tests/*' - -checks: - php: true diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 79ff593..0000000 --- a/.travis.yml +++ /dev/null @@ -1,118 +0,0 @@ -language: php -sudo: false - -php: - - 7.3 - - 7.2 - - 7.1 - - 7.0 - -services: - - mysql - -env: - global: - - DRUPAL_GRAPHQL=8.x-3.x - - DRUPAL_BUILD_DIR=$TRAVIS_BUILD_DIR/../drupal - - SIMPLETEST_DB=mysql://root:@127.0.0.1/graphql - - TRAVIS=true - matrix: - - DRUPAL_CORE=8.7.x - - DRUPAL_CORE=8.8.x - -matrix: - # Don't wait for the allowed failures to build. - fast_finish: true - include: - - php: 7.3 - env: - - DRUPAL_CORE=8.7.x - # Only run code coverage on the latest php and drupal versions. - - WITH_PHPDBG_COVERAGE=true - allow_failures: - # Allow the code coverage report to fail. - - php: 7.3 - env: - - DRUPAL_CORE=8.7.x - # Only run code coverage on the latest php and drupal versions. - - WITH_PHPDBG_COVERAGE=true - -mysql: - database: graphql - username: root - encoding: utf8 - -# Cache composer downloads. -cache: - directories: - - $HOME/.composer - -before_install: - # Disable xdebug. - - phpenv config-rm xdebug.ini - - # Determine the php settings file location. - - if [[ $TRAVIS_PHP_VERSION = hhvm* ]]; - then export PHPINI=/etc/hhvm/php.ini; - else export PHPINI=$HOME/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini; - fi - - # Disable the default memory limit. - - echo memory_limit = -1 >> $PHPINI - - # Update composer. - - composer self-update - -install: - # Create the database. - - mysql -e 'create database graphql' - - # Download Drupal 8 core from the Github mirror because it is faster. - - git clone --branch $DRUPAL_CORE --depth 1 https://github.com/drupal/drupal.git $DRUPAL_BUILD_DIR - - git clone --branch $DRUPAL_GRAPHQL --depth 1 https://github.com/drupal-graphql/graphql.git $DRUPAL_BUILD_DIR/modules/graphql - - # Reference the module in the build site. - - ln -s $TRAVIS_BUILD_DIR $DRUPAL_BUILD_DIR/modules/graphql_views - - # Copy the customized phpunit configuration file to the core directory so - # the relative paths are correct. - - cp $DRUPAL_BUILD_DIR/modules/graphql/phpunit.xml.dist $DRUPAL_BUILD_DIR/core/phpunit.xml - - # When running with phpdbg we need to replace all code occurrences that check - # for 'cli' with 'phpdbg'. Some files might be write protected, hence the - # fallback. - - if [[ "$WITH_PHPDBG_COVERAGE" == "true" ]]; - then grep -rl 'cli' $DRUPAL_BUILD_DIR/core $DRUPAL_BUILD_DIR/modules | xargs sed -i "s/'cli'/'phpdbg'/g" || true; - fi - - # Bring in the module dependencies without requiring a merge plugin. The - # require also triggers a full 'composer install'. - - composer --working-dir=$DRUPAL_BUILD_DIR require webonyx/graphql-php:^0.12.5 - - # For Drupal < 8.8 we have to manually upgrade zend-stdlib to avoid PHP 7.3 - # incompatibilities. - - if [[ "$DRUPAL_CORE" = "8.6.x" || "$DRUPAL_CORE" = "8.7.x" ]]; - then composer --working-dir=$DRUPAL_BUILD_DIR require zendframework/zend-stdlib:3.2.1; - fi - - # For Drupal < 8.8 we have to manually upgrade phpunit to avoid PHP 7.3 - # incompatibilities. - - if [[ "$DRUPAL_CORE" = "8.6.x" || "$DRUPAL_CORE" = "8.7.x" ]]; - then composer --working-dir=$DRUPAL_BUILD_DIR run-script drupal-phpunit-upgrade; - fi - -script: - # Run the unit tests using phpdbg if the environment variable is 'true'. - - if [[ "$WITH_PHPDBG_COVERAGE" == "true" ]]; - then phpdbg -qrr $DRUPAL_BUILD_DIR/vendor/bin/phpunit --configuration $DRUPAL_BUILD_DIR/core/phpunit.xml --coverage-clover $TRAVIS_BUILD_DIR/coverage.xml $TRAVIS_BUILD_DIR; - fi - - # Run the unit tests with standard php otherwise. - - if [[ "$WITH_PHPDBG_COVERAGE" != "true" ]]; - then $DRUPAL_BUILD_DIR/vendor/bin/phpunit --configuration $DRUPAL_BUILD_DIR/core/phpunit.xml $TRAVIS_BUILD_DIR; - fi - -after_success: - - if [[ "$WITH_PHPDBG_COVERAGE" == "true" ]]; - then bash <(curl -s https://codecov.io/bash); - fi diff --git a/README.md b/README.md index e649493..fb00c8a 100644 --- a/README.md +++ b/README.md @@ -1,25 +1,16 @@ # GraphQL Views for Drupal -[![Build Status](https://img.shields.io/travis/drupal-graphql/graphql-views.svg)](https://travis-ci.org/drupal-graphql/graphql-views) -[![Code Coverage](https://img.shields.io/codecov/c/github/drupal-graphql/graphql-views.svg)](https://codecov.io/gh/drupal-graphql/graphql-views) -[![Code Quality](https://img.shields.io/scrutinizer/g/drupal-graphql/graphql-views.svg)](https://scrutinizer-ci.com/g/drupal-graphql/graphql-views/?branch=8.x-1.x) +[![Build Status](https://github.com/drupal-graphql/graphql-views/workflows/.github/workflows/test.yml/badge.svg)](https://github.com/drupal-graphql/graphql-views/actions) [Drupal GraphQL]: https://github.com/drupal-graphql/graphql With `graphql_views` enabled a `GraphQL` views display can be added to any view in the system. -Results can be sorted, filtered based on content fields, and relationships can be added. There is also the option to return either the full entities, just a selection of fields, or even search results taken straight from a search server. +Results can be sorted, filtered based on content fields, and relationships can be added. Any `GraphQL` views display will provide a field that will adapt to the views configuration: -- The fields name will be composed of the views and displays machine names or configured manually. - If the view is configured with pagination, the field will accept pager arguments and return the result list and count field instead of the entity list directly. - Any exposed filters will be added to the `filters` input type that can be used to pass filter values into the view. -- Any contextual filters will be added to the `contextual_filters` input type. -- If a contextual filters validation criteria match an existing GraphQL type, the field will be added to this type too, and the value will be populated from the current result context. - -Read more on: -- https://www.amazeelabs.com/en/blog/graphql-drupalers-part-4-fetching-entities -- https://www.amazeelabs.com/en/blog/drupal-graphql-batteries-included Please also refer to the main [Drupal GraphQL] module for further information. diff --git a/composer.json b/composer.json index 723d9ed..90ce9e5 100644 --- a/composer.json +++ b/composer.json @@ -1,8 +1,11 @@ { - "name": "drupal/graphql_views", - "type": "drupal-module", - "description": "Exposes your Drupal Views data model through a GraphQL schema.", - "homepage": "http://drupal.org/project/graphql_views", - "license": "GPL-2.0+", - "minimum-stability": "dev" + "name": "drupal/graphql_views", + "type": "drupal-module", + "description": "Exposes your Drupal Views data model through a GraphQL schema.", + "homepage": "http://drupal.org/project/graphql_views", + "license": "GPL-2.0+", + "minimum-stability": "dev", + "require": { + "drupal/graphql": "^4.1" + } } diff --git a/config/schema/graphql_views.views.schema.yml b/config/schema/graphql_views.views.schema.yml index 3c55ce4..5ef4caf 100644 --- a/config/schema/graphql_views.views.schema.yml +++ b/config/schema/graphql_views.views.schema.yml @@ -1,7 +1,3 @@ views.display.graphql: type: views_display label: 'GraphQL display options' - mapping: - graphql_query_name: - type: string - label: 'GraphQL query name' diff --git a/graphql_views.info.yml b/graphql_views.info.yml index 9364b5d..eff160d 100644 --- a/graphql_views.info.yml +++ b/graphql_views.info.yml @@ -2,9 +2,7 @@ name: GraphQL Views type: module description: 'Adds support for views.' package: GraphQL -core: 8.x -core_version_requirement: ^8 || ^9 +core_version_requirement: ^8.9 || ^9 dependencies: - - graphql:graphql_core + - drupal:graphql - drupal:views - - drupal:system (>=8.4) \ No newline at end of file diff --git a/phpcs.xml.dist b/phpcs.xml.dist deleted file mode 100644 index 35dff3c..0000000 --- a/phpcs.xml.dist +++ /dev/null @@ -1,16 +0,0 @@ - - - Default PHP CodeSniffer configuration for GraphQL. - . - - - - - - src/Annotation - src/Core/Annotation - - - - - \ No newline at end of file diff --git a/src/Plugin/Deriver/Enums/ViewSortByDeriver.php b/src/Plugin/Deriver/Enums/ViewSortByDeriver.php deleted file mode 100644 index 4851f1f..0000000 --- a/src/Plugin/Deriver/Enums/ViewSortByDeriver.php +++ /dev/null @@ -1,51 +0,0 @@ -entityTypeManager->hasDefinition('view')) { - $viewStorage = $this->entityTypeManager->getStorage('view'); - - foreach (Views::getApplicableViews('graphql_display') as list($viewId, $displayId)) { - /** @var \Drupal\views\ViewEntityInterface $view */ - $view = $viewStorage->load($viewId); - if (!$type = $this->getRowResolveType($view, $displayId)) { - continue; - } - - /** @var \Drupal\graphql_views\Plugin\views\display\GraphQL $display */ - $display = $this->getViewDisplay($view, $displayId); - $sorts = array_filter($display->getOption('sorts') ?: [], function ($sort) { - return $sort['exposed']; - }); - $sorts = array_reduce($sorts, function ($carry, $sort) { - $carry[strtoupper($sort['id'])] = [ - 'value' => $sort['id'], - 'description' => $sort['expose']['label'], - ]; - return $carry; - }, []); - - if (!empty($sorts)) { - $id = implode('-', [$viewId, $displayId, 'view']); - $this->derivatives["$viewId-$displayId"] = [ - 'name' => StringHelper::camelCase($id, 'sort', 'by'), - 'values' => $sorts, - ] + $basePluginDefinition; - } - } - } - - return parent::getDerivativeDefinitions($basePluginDefinition); - } - -} diff --git a/src/Plugin/Deriver/Fields/ViewDeriver.php b/src/Plugin/Deriver/Fields/ViewDeriver.php deleted file mode 100644 index fe8577b..0000000 --- a/src/Plugin/Deriver/Fields/ViewDeriver.php +++ /dev/null @@ -1,57 +0,0 @@ -entityTypeManager->hasDefinition('view')) { - $viewStorage = $this->entityTypeManager->getStorage('view'); - - foreach (Views::getApplicableViews('graphql_display') as list($viewId, $displayId)) { - /** @var \Drupal\views\ViewEntityInterface $view */ - $view = $viewStorage->load($viewId); - if (!$this->getRowResolveType($view, $displayId)) { - continue; - } - - /** @var \Drupal\graphql_views\Plugin\views\display\GraphQL $display */ - $display = $this->getViewDisplay($view, $displayId); - - $id = implode('-', [$viewId, $displayId, 'view']); - $info = $this->getArgumentsInfo($display->getOption('arguments') ?: []); - $arguments = []; - $arguments += $this->getContextualArguments($info, $id); - $arguments += $this->getPagerArguments($display); - $arguments += $this->getSortArguments($display, $id); - $arguments += $this->getFilterArguments($display, $id); - $types = $this->getTypes($info); - - $this->derivatives[$id] = [ - 'id' => $id, - 'name' => $display->getGraphQLQueryName(), - 'type' => $display->getGraphQLResultName(), - 'parents' => $types, - 'arguments' => $arguments, - 'view' => $viewId, - 'display' => $displayId, - 'paged' => $this->isPaged($display), - 'arguments_info' => $info, - ] + $this->getCacheMetadataDefinition($view, $display) + $basePluginDefinition; - } - } - - return parent::getDerivativeDefinitions($basePluginDefinition); - } - -} diff --git a/src/Plugin/Deriver/Fields/ViewResultCountDeriver.php b/src/Plugin/Deriver/Fields/ViewResultCountDeriver.php deleted file mode 100644 index fa48865..0000000 --- a/src/Plugin/Deriver/Fields/ViewResultCountDeriver.php +++ /dev/null @@ -1,47 +0,0 @@ -entityTypeManager->hasDefinition('view')) { - $viewStorage = $this->entityTypeManager->getStorage('view'); - - foreach (Views::getApplicableViews('graphql_display') as list($viewId, $displayId)) { - /** @var \Drupal\views\ViewEntityInterface $view */ - $view = $viewStorage->load($viewId); - /** @var \Drupal\graphql_views\Plugin\views\display\GraphQL $display */ - $display = $this->getViewDisplay($view, $displayId); - if (!$this->isPaged($display)) { - continue; - } - - if (!$this->getRowResolveType($view, $displayId)) { - continue; - } - - $id = implode('-', [$viewId, $displayId, 'result', 'count']); - $this->derivatives[$id] = [ - 'id' => $id, - 'type' => 'Int', - 'parents' => [$display->getGraphQLResultName()], - 'view' => $viewId, - 'display' => $displayId, - ] + $this->getCacheMetadataDefinition($view, $display) + $basePluginDefinition; - } - } - - return parent::getDerivativeDefinitions($basePluginDefinition); - } - -} diff --git a/src/Plugin/Deriver/Fields/ViewResultListDeriver.php b/src/Plugin/Deriver/Fields/ViewResultListDeriver.php deleted file mode 100644 index 04e5017..0000000 --- a/src/Plugin/Deriver/Fields/ViewResultListDeriver.php +++ /dev/null @@ -1,47 +0,0 @@ -entityTypeManager->hasDefinition('view')) { - $viewStorage = $this->entityTypeManager->getStorage('view'); - - foreach (Views::getApplicableViews('graphql_display') as list($viewId, $displayId)) { - /** @var \Drupal\views\ViewEntityInterface $view */ - $view = $viewStorage->load($viewId); - if (!$type = $this->getRowResolveType($view, $displayId)) { - continue; - } - - /** @var \Drupal\graphql_views\Plugin\views\display\GraphQL $display */ - $display = $this->getViewDisplay($view, $displayId); - - $id = implode('-', [$viewId, $displayId, 'result', 'list']); - $style = $this->getViewStyle($view, $displayId); - $this->derivatives[$id] = [ - 'id' => $id, - 'type' => StringHelper::listType($type), - 'parents' => [$display->getGraphQLResultName()], - 'view' => $viewId, - 'display' => $displayId, - 'uses_fields' => $style->usesFields(), - ] + $this->getCacheMetadataDefinition($view, $display) + $basePluginDefinition; - } - } - - return parent::getDerivativeDefinitions($basePluginDefinition); - } - -} diff --git a/src/Plugin/Deriver/Fields/ViewRowFieldDeriver.php b/src/Plugin/Deriver/Fields/ViewRowFieldDeriver.php deleted file mode 100644 index f42f33f..0000000 --- a/src/Plugin/Deriver/Fields/ViewRowFieldDeriver.php +++ /dev/null @@ -1,54 +0,0 @@ -entityTypeManager->hasDefinition('view')) { - $viewStorage = $this->entityTypeManager->getStorage('view'); - - foreach (Views::getApplicableViews('graphql_display') as list($viewId, $displayId)) { - /** @var \Drupal\views\ViewEntityInterface $view */ - $view = $viewStorage->load($viewId); - /** @var \Drupal\graphql_views\Plugin\views\display\GraphQL $display */ - $display = $this->getViewDisplay($view, $displayId); - $rowPlugin = $display->getPlugin('row'); - - // This deriver only supports our custom field row plugin. - if (!$rowPlugin instanceof GraphQLFieldRow) { - continue; - } - - foreach ($display->getHandlers('field') as $name => $field) { - $id = implode('-', [$viewId, $displayId, 'field', $name]); - $alias = $rowPlugin->getFieldKeyAlias($name); - $type = $rowPlugin->getFieldType($name); - - $this->derivatives[$id] = [ - 'id' => $id, - 'name' => $alias, - 'type' => $type, - 'parents' => [$display->getGraphQLRowName()], - 'view' => $viewId, - 'display' => $displayId, - 'field' => $alias, - ] + $this->getCacheMetadataDefinition($view, $display) + $basePluginDefinition; - } - } - } - - return parent::getDerivativeDefinitions($basePluginDefinition); - } - -} diff --git a/src/Plugin/Deriver/InputTypes/ViewContextualFilterInputDeriver.php b/src/Plugin/Deriver/InputTypes/ViewContextualFilterInputDeriver.php deleted file mode 100644 index d7e0825..0000000 --- a/src/Plugin/Deriver/InputTypes/ViewContextualFilterInputDeriver.php +++ /dev/null @@ -1,52 +0,0 @@ -entityTypeManager->hasDefinition('view')) { - $viewStorage = $this->entityTypeManager->getStorage('view'); - - foreach (Views::getApplicableViews('graphql_display') as list($viewId, $displayId)) { - /** @var \Drupal\views\ViewEntityInterface $view */ - $view = $viewStorage->load($viewId); - if (!$this->getRowResolveType($view, $displayId)) { - continue; - } - - $display = $this->getViewDisplay($view, $displayId); - $argumentsInfo = $this->getArgumentsInfo($display->getOption('arguments') ?: []); - if (!empty($argumentsInfo)) { - $id = implode('_', [ - $viewId, $displayId, 'view', 'contextual', 'filter', 'input', - ]); - - $this->derivatives[$id] = [ - 'id' => $id, - 'name' => StringHelper::camelCase($viewId, $displayId, 'view', 'contextual', 'filter', 'input'), - 'fields' => array_fill_keys(array_keys($argumentsInfo), [ - 'type' => 'String', - ]), - 'view' => $viewId, - 'display' => $displayId, - ] + $this->getCacheMetadataDefinition($view, $display) + $basePluginDefinition; - } - } - } - - return parent::getDerivativeDefinitions($basePluginDefinition); - } - -} diff --git a/src/Plugin/Deriver/InputTypes/ViewFilterInputDeriver.php b/src/Plugin/Deriver/InputTypes/ViewFilterInputDeriver.php deleted file mode 100644 index 78c727f..0000000 --- a/src/Plugin/Deriver/InputTypes/ViewFilterInputDeriver.php +++ /dev/null @@ -1,141 +0,0 @@ -entityTypeManager->hasDefinition('view')) { - $viewStorage = $this->entityTypeManager->getStorage('view'); - - foreach (Views::getApplicableViews('graphql_display') as list($viewId, $displayId)) { - /** @var \Drupal\views\ViewEntityInterface $view */ - $view = $viewStorage->load($viewId); - if (!$this->getRowResolveType($view, $displayId)) { - continue; - } - - /** @var \Drupal\graphql_views\Plugin\views\display\GraphQL $display */ - $display = $this->getViewDisplay($view, $displayId); - $id = implode('_', [$viewId, $displayId, 'view', 'filter', 'input']); - - // Re-key filters by filter identifier. - $filters = array_reduce(array_filter($display->getOption('filters') ?: [], function($filter) { - return array_key_exists('exposed', $filter) && $filter['exposed']; - }), function($carry, $current) { - return $carry + [ - $current['expose']['identifier'] => $current, - ]; - }, []); - - // If there are no exposed filters, don't create the derivative. - if (empty($filters)) { - continue; - } - - $fields = array_map(function($filter) use ($basePluginDefinition) { - if ($this->isGenericInputFilter($filter)) { - return $this->createGenericInputFilterDefinition($filter, $basePluginDefinition); - } - - return [ - 'type' => $filter['expose']['multiple'] ? StringHelper::listType('String') : 'String', - ]; - }, $filters); - - $this->derivatives[$id] = [ - 'id' => $id, - 'name' => $display->getGraphQLFilterInputName(), - 'fields' => $fields, - 'view' => $viewId, - 'display' => $displayId, - ] + $this->getCacheMetadataDefinition($view, $display) + $basePluginDefinition; - } - - } - return parent::getDerivativeDefinitions($basePluginDefinition); - } - - /** - * Checks if a filter definition is a generic input filter. - * - * @param mixed $filter - * $filter['value'] = []; - * $filter['value'] = [ - * "text", - * "test" - * ]; - * $filter['value'] = [ - * 'distance' => 10, - * 'distance2' => 30, - * ... - * ]; - * @return bool - */ - public function isGenericInputFilter($filter) { - if (!is_array($filter['value']) || count($filter['value']) == 0) { - return false; - } - - $firstKey = array_keys($filter['value'])[0]; - return is_string($firstKey); - } - - /** - * Creates a definition for a generic input filter. - * - * @param mixed $filter - * $filter['value'] = []; - * $filter['value'] = [ - * "text", - * "test" - * ]; - * $filter['value'] = [ - * 'distance' => 10, - * 'distance2' => 30, - * ... - * ]; - * @param mixed $basePluginDefinition - * @return array - */ - public function createGenericInputFilterDefinition($filter, $basePluginDefinition) { - $filterId = $filter['expose']['identifier']; - - $id = implode('_', [ - $filter['expose']['multiple'] ? $filterId : $filterId . '_multi', - 'view', - 'filter', - 'input', - ]); - - $fields = []; - foreach ($filter['value'] as $fieldKey => $fieldDefaultValue) { - $fields[$fieldKey] = [ - 'type' => 'String', - ]; - } - - $genericInputFilter = [ - 'id' => $id, - 'name' => StringHelper::camelCase($id), - 'fields' => $fields, - ] + $basePluginDefinition; - - $this->derivatives[$id] = $genericInputFilter; - - return [ - 'type' => $filter['expose']['multiple'] ? StringHelper::listType($genericInputFilter['name']) : $genericInputFilter['name'], - ]; - } -} diff --git a/src/Plugin/Deriver/Types/ViewResultTypeDeriver.php b/src/Plugin/Deriver/Types/ViewResultTypeDeriver.php deleted file mode 100644 index 2d4c71e..0000000 --- a/src/Plugin/Deriver/Types/ViewResultTypeDeriver.php +++ /dev/null @@ -1,43 +0,0 @@ -entityTypeManager->hasDefinition('view')) { - $viewStorage = $this->entityTypeManager->getStorage('view'); - - foreach (Views::getApplicableViews('graphql_display') as list($viewId, $displayId)) { - /** @var \Drupal\views\ViewEntityInterface $view */ - $view = $viewStorage->load($viewId); - if (!$this->getRowResolveType($view, $displayId)) { - continue; - } - - /** @var \Drupal\graphql_views\Plugin\views\display\GraphQL $display */ - $display = $this->getViewDisplay($view, $displayId); - - $id = implode('-', [$viewId, $displayId, 'result']); - $this->derivatives[$id] = [ - 'id' => $id, - 'name' => $display->getGraphQLResultName(), - 'view' => $viewId, - 'display' => $displayId, - ] + $this->getCacheMetadataDefinition($view, $display) + $basePluginDefinition; - } - } - - return parent::getDerivativeDefinitions($basePluginDefinition); - } - -} diff --git a/src/Plugin/Deriver/Types/ViewRowTypeDeriver.php b/src/Plugin/Deriver/Types/ViewRowTypeDeriver.php deleted file mode 100644 index 0a69781..0000000 --- a/src/Plugin/Deriver/Types/ViewRowTypeDeriver.php +++ /dev/null @@ -1,49 +0,0 @@ -entityTypeManager->hasDefinition('view')) { - $viewStorage = $this->entityTypeManager->getStorage('view'); - - foreach (Views::getApplicableViews('graphql_display') as list($viewId, $displayId)) { - /** @var \Drupal\views\ViewEntityInterface $view */ - $view = $viewStorage->load($viewId); - if (!$this->getRowResolveType($view, $displayId)) { - continue; - } - - $style = $this->getViewStyle($view, $displayId); - // This deriver only supports style plugins that use fields. - if (!$style->usesFields()) { - continue; - } - - /** @var \Drupal\graphql_views\Plugin\views\display\GraphQL $display */ - $display = $this->getViewDisplay($view, $displayId); - - $id = implode('-', [$viewId, $displayId, 'row']); - $this->derivatives[$id] = [ - 'id' => $id, - 'name' => $display->getGraphQLRowName(), - 'view' => $viewId, - 'display' => $displayId, - ] + $this->getCacheMetadataDefinition($view, $display) + $basePluginDefinition; - } - } - - return parent::getDerivativeDefinitions($basePluginDefinition); - } - -} diff --git a/src/Plugin/Deriver/ViewDeriverBase.php b/src/Plugin/Deriver/ViewDeriverBase.php deleted file mode 100644 index 685088d..0000000 --- a/src/Plugin/Deriver/ViewDeriverBase.php +++ /dev/null @@ -1,113 +0,0 @@ -get('entity_type.manager'), - $container->get('plugin.manager.graphql.interface') - ); - } - - /** - * Creates a ViewDeriver object. - * - * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager - * An entity type manager instance. - * @param \Drupal\Component\Plugin\PluginManagerInterface $interfacePluginManager - * The plugin manager for graphql interfaces. - */ - public function __construct( - EntityTypeManagerInterface $entityTypeManager, - PluginManagerInterface $interfacePluginManager - ) { - $this->interfacePluginManager = $interfacePluginManager; - $this->entityTypeManager = $entityTypeManager; - } - - /** - * Retrieves the entity type id of an entity by its base or data table. - * - * @param string $table - * The base or data table of an entity. - * - * @return string - * The id of the entity type that the given base table belongs to. - */ - protected function getEntityTypeByTable($table) { - if (!isset($this->dataTables)) { - $this->dataTables = []; - - foreach ($this->entityTypeManager->getDefinitions() as $entityTypeId => $entityType) { - if ($dataTable = $entityType->getDataTable()) { - $this->dataTables[$dataTable] = $entityType->id(); - } - if ($baseTable = $entityType->getBaseTable()) { - $this->dataTables[$baseTable] = $entityType->id(); - } - } - } - - return !empty($this->dataTables[$table]) ? $this->dataTables[$table] : NULL; - } - - /** - * Retrieves the type the view's rows resolve to. - * - * @param \Drupal\views\ViewEntityInterface $view - * The view entity. - * @param string $displayId - * Interface plugin manager. - * - * @return null|string - * The name of the type or NULL if the type could not be derived. - */ - protected function getRowResolveType(ViewEntityInterface $view, $displayId) { - return $this->traitGetRowResolveType($view, $displayId, $this->interfacePluginManager); - } - -} diff --git a/src/Plugin/GraphQL/DataProducer/Views.php b/src/Plugin/GraphQL/DataProducer/Views.php new file mode 100644 index 0000000..223ccbe --- /dev/null +++ b/src/Plugin/GraphQL/DataProducer/Views.php @@ -0,0 +1,258 @@ +get('entity_type.manager')); + } + + /** + * {@inheritdoc} + */ + public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entityTypeManager) { + parent::__construct($configuration, $plugin_id, $plugin_definition); + $this->entityTypeManager = $entityTypeManager; + } + + /** + * Resolve the data. + * + * @param string $view_id + * The view ID. + * @param string $display_id + * The display ID. + * @param int|null $offset + * Offset of the query. + * @param int|null $page_size + * Number of items on page. + * @param int|null $page + * Number of page. + * @param string $sort_by + * Fields to sort by. + * @param string $sort_direction + * Direction to sort. ASC or DESC. + * @param array $filter + * Exposed filters. + * @param \Drupal\graphql\GraphQL\Execution\FieldContext $fieldContext + * Context to set cache on. + * + * @return array|null + * List of entities. + * + * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException + * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException + */ + public function resolve(string $view_id, string $display_id, $offset, $page_size, $page, $sort_by, $sort_direction, array $filter, FieldContext $fieldContext) { + + /** @var \Drupal\views\Entity\View $view */ + $view = \Drupal::entityTypeManager()->getStorage('view')->load($view_id); + + $executable = $view->getExecutable(); + $executable->setDisplay($display_id); + + // Set paging parameters. + if (empty($page_size)) { + $page_size = $this->getPagerLimit($executable->getDisplay()); + } + if (empty($page)) { + $page = $this->getPagerOffset($executable->getDisplay()); + } + if ($this->isPaged($executable->getDisplay())) { + $executable->setItemsPerPage($page_size); + $executable->setCurrentPage($page); + } + + if ($offset) { + $executable->setOffset($offset); + } + + $available_filters = $executable->getDisplay()->getOption('filters'); + $input = $this->extractExposedInput($sort_by, $sort_direction, $filter, $available_filters); + $executable->setExposedInput($input); + + // This is a workaround for the Taxonomy Term filter which requires a full + // exposed form to be sent OR the display being an attachment to just + // accept input values. + $executable->is_attachment = TRUE; + $executable->exposed_raw_input = $input; + + $executable->preExecute(); + $executable->execute(); + + $result = $executable->render($display_id); + + /** @var \Drupal\Core\Cache\CacheableMetadata $cache */ + if ($cache = $result['cache']) { + $cache->setCacheContexts( + array_filter($cache->getCacheContexts(), function ($context) { + // Don't emit the url cache contexts. + return $context !== 'url' && strpos($context, 'url.') !== 0; + }) + ); + $fieldContext->addCacheableDependency($cache); + } + + return [ + 'results' => $result['rows'], + 'count' => $result['view']->total_rows, + ]; + } + + /** + * Check if a pager is configured. + * + * @param \Drupal\views\Plugin\views\display\DisplayPluginInterface $display + * The display configuration. + * + * @return bool + * Flag indicating if the view is configured with a pager. + */ + protected function isPaged(DisplayPluginInterface $display) { + $pagerOptions = $display->getOption('pager'); + return isset($pagerOptions['type']) && in_array($pagerOptions['type'], [ + 'full', + 'mini', + ]); + } + + /** + * Get the configured default limit. + * + * @param \Drupal\views\Plugin\views\display\DisplayPluginInterface $display + * The display configuration. + * + * @return int + * The default limit. + */ + protected function getPagerLimit(DisplayPluginInterface $display) { + $pagerOptions = $display->getOption('pager'); + return NestedArray::getValue($pagerOptions, [ + 'options', + 'items_per_page', + ]) ?: 0; + } + + /** + * Get the configured default offset. + * + * @param \Drupal\views\Plugin\views\display\DisplayPluginInterface $display + * The display configuration. + * + * @return int + * The default offset. + */ + protected function getPagerOffset(DisplayPluginInterface $display) { + $pagerOptions = $display->getOption('pager'); + return NestedArray::getValue($pagerOptions, [ + 'options', + 'offset', + ]) ?: 0; + } + + /** + * Retrieves sort and filter arguments from the provided field args. + * + * @param string $sort_by + * Fields to sort by. + * @param string $sort_direction + * Direction to sort. ASC or DESC. + * @param array $filter + * Exposed filters. + * @param array $available_filters + * The available filters for the configured view. + * + * @return array + * The array of sort and filter arguments to execute the view with. + */ + protected function extractExposedInput($sort_by, $sort_direction, array $filter, array $available_filters) { + // Prepare arguments for use as exposed form input. + $input = array_filter([ + // Sorting arguments. + 'sort_by' => $sort_by, + 'sort_order' => $sort_direction, + ]); + + // If some filters are missing from the input, set them to an empty string + // explicitly. Otherwise views module generates "Undefined index" notice. + foreach ($available_filters as $filterRow) { + if (!isset($filterRow['expose']['identifier'])) { + continue; + } + + $inputKey = $filterRow['expose']['identifier']; + if (!isset($filter[$inputKey])) { + $input[$inputKey] = $filterRow['value']; + } + else { + $input[$inputKey] = $filter[$inputKey]; + } + } + return $input; + } + +} diff --git a/src/Plugin/GraphQL/Enums/ViewSortBy.php b/src/Plugin/GraphQL/Enums/ViewSortBy.php deleted file mode 100644 index 9a3b730..0000000 --- a/src/Plugin/GraphQL/Enums/ViewSortBy.php +++ /dev/null @@ -1,16 +0,0 @@ - [ - 'value' => 'ASC', - 'description' => 'Sort in ascending order.', - ], - 'DESC' => [ - 'value' => 'DESC', - 'description' => 'Sort in descending order.', - ], - ]; - } - -} diff --git a/src/Plugin/GraphQL/Fields/Entity/Fields/View/ViewDerivative.php b/src/Plugin/GraphQL/Fields/Entity/Fields/View/ViewDerivative.php deleted file mode 100644 index d582f5d..0000000 --- a/src/Plugin/GraphQL/Fields/Entity/Fields/View/ViewDerivative.php +++ /dev/null @@ -1,141 +0,0 @@ -getValue(); - $this->pluginDefinition['view'] = $values['target_id']; - $this->pluginDefinition['display'] = $values['display_id']; - $view = EntityView::load($values['target_id']); - $display = $this->getViewDisplay($view, $values['display_id']); - $this->pluginDefinition['paged'] = $this->isPaged($display); - $this->pluginDefinition['arguments_info'] = $this->getArgumentsInfo($display->getOption('arguments') ?: []); - $this->pluginDefinition = array_merge($this->pluginDefinition, $this->getCacheMetadataDefinition($view, $display)); - $this->setOverridenViewDefaults($value, $args); - $this->setViewDefaultValues($display, $args); - return parent::resolveValues($value, $args, $context, $info); - } - - /** - * Get configuration values from views reference field. - * - * @param mixed $value - * The current object value. - * - * @return array|mixed - * Return unserialized data. - */ - protected function getViewReferenceConfiguration($value) { - $values = $value->getValue(); - return isset($values['data']) ? unserialize($values['data']) : []; - } - - /** - * Set default display settings. - * - * @param mixed $value - * The current object value. - * @param array $args - * Arguments where the default view settings needs to be added. - */ - protected function setOverridenViewDefaults($value, array &$args) { - $viewReferenceConfiguration = $this->getViewReferenceConfiguration($value); - if (!empty($viewReferenceConfiguration['pager'])) { - $this->pluginDefinition['paged'] = in_array($viewReferenceConfiguration['pager'], [ - 'full', - 'mini', - ]); - } - - if (!isset($args['pageSize']) && !empty($viewReferenceConfiguration['limit'])) { - $args['pageSize'] = $viewReferenceConfiguration['limit']; - } - - if (!isset($args['offset']) && !empty($viewReferenceConfiguration['offset'])) { - $args['offset'] = $viewReferenceConfiguration['offset']; - } - - /* Expected format: {"contextualFilter": {"key": "value","keyN": "valueN"}} */ - if (!isset($args['contextualFilter']) && !empty($viewReferenceConfiguration['argument'])) { - $argument = json_decode($viewReferenceConfiguration['argument'], TRUE); - if (isset($argument['contextualFilter']) && !empty($argument['contextualFilter'])) { - $args['contextualFilter'] = $argument['contextualFilter']; - } - } - } - - /** - * Set default display settings. - * - * @param \Drupal\views\Plugin\views\display\DisplayPluginInterface $display - * The display configuration. - * @param array $args - * Arguments where the default view settings needs to be added. - */ - protected function setViewDefaultValues(DisplayPluginInterface $display, array &$args) { - if (!isset($args['pageSize']) && $this->pluginDefinition['paged']) { - $args['pageSize'] = $this->getPagerLimit($display); - } - if (!isset($args['page']) && $this->pluginDefinition['paged']) { - $args['page'] = $this->getPagerOffset($display); - } - } - -} diff --git a/src/Plugin/GraphQL/Fields/View.php b/src/Plugin/GraphQL/Fields/View.php deleted file mode 100644 index 4b4aa25..0000000 --- a/src/Plugin/GraphQL/Fields/View.php +++ /dev/null @@ -1,197 +0,0 @@ -entityTypeManager = $entityTypeManager; - parent::__construct($configuration, $pluginId, $pluginDefinition); - } - - /** - * {@inheritdoc} - */ - public static function create(ContainerInterface $container, array $configuration, $pluginId, $pluginDefinition) { - return new static( - $configuration, - $pluginId, - $pluginDefinition, - $container->get('entity_type.manager') - ); - } - - /** - * {@inheritdoc} - */ - public function resolveValues($value, array $args, ResolveContext $context, ResolveInfo $info) { - $storage = $this->entityTypeManager->getStorage('view'); - $definition = $this->getPluginDefinition(); - - /** @var \Drupal\views\Entity\View $view */ - if ($view = $storage->load($definition['view'])) { - $executable = $view->getExecutable(); - $executable->setDisplay($definition['display']); - - // Set view contextual filters. - /* @see \Drupal\graphql_views\ViewDeriverHelperTrait::getArgumentsInfo() */ - if (!empty($definition['arguments_info'])) { - $arguments = $this->extractContextualFilters($value, $args); - $executable->setArguments($arguments); - } - - $filters = $executable->getDisplay()->getOption('filters');; - $input = $this->extractExposedInput($value, $args, $filters); - $executable->setExposedInput($input); - - // This is a workaround for the Taxonomy Term filter which requires a full - // exposed form to be sent OR the display being an attachment to just - // accept input values. - $executable->is_attachment = TRUE; - $executable->exposed_raw_input = $input; - - if (!empty($definition['paged'])) { - // Set paging parameters. - $executable->setItemsPerPage($args['pageSize']); - $executable->setCurrentPage($args['page']); - } - - if (isset($args['offset']) && !empty($args['offset'])) { - $executable->setOffset($args['offset']); - } - - $result = $executable->render($definition['display']); - /** @var \Drupal\Core\Cache\CacheableMetadata $cache */ - if ($cache = $result['cache']) { - $cache->setCacheContexts( - array_filter($cache->getCacheContexts(), function ($context) { - // Don't emit the url cache contexts. - return $context !== 'url' && strpos($context, 'url.') !== 0; - }) - ); - } - yield $result; - } - } - - /** - * {@inheritdoc} - */ - protected function getCacheDependencies(array $result, $value, array $args, ResolveContext $context, ResolveInfo $info) { - return array_map(function ($item) { - return $item['cache']; - }, $result); - } - - /** - * Retrieves the contextual filter argument from the parent value or args. - * - * @param $value - * The resolved parent value. - * @param $args - * The arguments provided to the field. - * - * @return array - * An array of arguments containing the contextual filter value from the - * parent or provided args if any. - */ - protected function extractContextualFilters($value, $args) { - $definition = $this->getPluginDefinition(); - $arguments = []; - - foreach ($definition['arguments_info'] as $argumentId => $argumentInfo) { - if (isset($args['contextualFilter'][$argumentId])) { - $arguments[$argumentInfo['index']] = $args['contextualFilter'][$argumentId]; - } - elseif ( - $value instanceof EntityInterface && - $value->getEntityTypeId() === $argumentInfo['entity_type'] && - (empty($argumentInfo['bundles']) || - in_array($value->bundle(), $argumentInfo['bundles'], TRUE)) - ) { - $arguments[$argumentInfo['index']] = $value->id(); - } - else { - $arguments[$argumentInfo['index']] = NULL; - } - } - - return $arguments; - } - - /** - * Retrieves sort and filter arguments from the provided field args. - * - * @param $value - * The resolved parent value. - * @param $args - * The array of arguments provided to the field. - * @param $filters - * The available filters for the configured view. - * - * @return array - * The array of sort and filter arguments to execute the view with. - */ - protected function extractExposedInput($value, $args, $filters) { - // Prepare arguments for use as exposed form input. - $input = array_filter([ - // Sorting arguments. - 'sort_by' => isset($args['sortBy']) ? $args['sortBy'] : NULL, - 'sort_order' => isset($args['sortDirection']) ? $args['sortDirection'] : NULL, - ]); - - // If some filters are missing from the input, set them to an empty string - // explicitly. Otherwise views module generates "Undefined index" notice. - foreach ($filters as $filterKey => $filterRow) { - if (!isset($filterRow['expose']['identifier'])) { - continue; - } - - $inputKey = $filterRow['expose']['identifier']; - if (!isset($args['filter'][$inputKey])) { - $input[$inputKey] = $filterRow['value']; - } else { - $input[$inputKey] = $args['filter'][$inputKey]; - } - } - - return $input; - } - -} diff --git a/src/Plugin/GraphQL/Fields/ViewResultCount.php b/src/Plugin/GraphQL/Fields/ViewResultCount.php deleted file mode 100644 index a54e7cb..0000000 --- a/src/Plugin/GraphQL/Fields/ViewResultCount.php +++ /dev/null @@ -1,33 +0,0 @@ -total_rows); - } - } - -} diff --git a/src/Plugin/GraphQL/Fields/ViewResultList.php b/src/Plugin/GraphQL/Fields/ViewResultList.php deleted file mode 100644 index 1026b74..0000000 --- a/src/Plugin/GraphQL/Fields/ViewResultList.php +++ /dev/null @@ -1,33 +0,0 @@ -getPluginDefinition(); - if (isset($value[$definition['field']])) { - yield $value[$definition['field']]; - } - } - -} diff --git a/src/Plugin/GraphQL/InputTypes/ViewContextualFilterInput.php b/src/Plugin/GraphQL/InputTypes/ViewContextualFilterInput.php deleted file mode 100644 index c208941..0000000 --- a/src/Plugin/GraphQL/InputTypes/ViewContextualFilterInput.php +++ /dev/null @@ -1,18 +0,0 @@ -pluginDefinition['view'] === $view->id() && $this->pluginDefinition['display'] == $view->current_display) { - return TRUE; - } - } - - return parent::applies($object, $context, $info); - } - -} diff --git a/src/Plugin/GraphQL/Types/ViewRowType.php b/src/Plugin/GraphQL/Types/ViewRowType.php deleted file mode 100644 index 7c54185..0000000 --- a/src/Plugin/GraphQL/Types/ViewRowType.php +++ /dev/null @@ -1,18 +0,0 @@ -getGraphQLName(); - } - - /** - * Gets the result name. - * - * @return string - * Result name. - */ - public function getGraphQLResultName() { - return $this->getGraphQLName('result', TRUE); - } - - /** - * Gets the row name. - * - * @return string - * Row name. - */ - public function getGraphQLRowName() { - return $this->getGraphQLName('row', TRUE); - } - - /** - * Gets the filter input name.. - * - * @return string - * Result name. - */ - public function getGraphQLFilterInputName() { - return $this->getGraphQLName('filter_input', TRUE); - } - - /** - * Gets the contextual filter input name. - * - * @return string - * Result name. - */ - public function getGraphQLContextualFilterInputName() { - return $this->getGraphQLName('contextual_filter_input', TRUE); - } - - /** - * Returns the formatted name. - * - * @param string|null $suffix - * Id suffix, eg. row, result. - * @param bool $type - * Whether to use camel- or snake case. Uses camel case if TRUE. Defaults to - * FALSE. - * - * @return string The id. - * The id. - */ - public function getGraphQLName($suffix = NULL, $type = FALSE) { - $queryName = strip_tags($this->getOption('graphql_query_name')); - - if (empty($queryName)) { - $viewId = $this->view->id(); - $displayId = $this->display['id']; - $parts = [$viewId, $displayId, 'view', $suffix]; - return $type ? call_user_func_array([StringHelper::class, 'camelCase'], $parts) : call_user_func_array([StringHelper::class, 'propCase'], $parts); - } - - $parts = array_filter([$queryName, $suffix]); - return $type ? call_user_func_array([StringHelper::class, 'camelCase'], $parts) : call_user_func_array([StringHelper::class, 'propCase'], $parts); - } - /** * {@inheritdoc} */ @@ -200,43 +119,6 @@ public function optionsSummary(&$categories, &$options) { '#weight' => -10, ], ]; - - $options['graphql_query_name'] = [ - 'category' => 'graphql', - 'title' => $this->t('Query name'), - 'value' => views_ui_truncate($this->getGraphQLQueryName(), 24), - ]; - } - - /** - * {@inheritdoc} - */ - public function buildOptionsForm(&$form, FormStateInterface $form_state) { - parent::buildOptionsForm($form, $form_state); - - switch ($form_state->get('section')) { - case 'graphql_query_name': - $form['#title'] .= $this->t('Query name'); - $form['graphql_query_name'] = [ - '#type' => 'textfield', - '#description' => $this->t('This will be the graphQL query name.'), - '#default_value' => $this->getGraphQLQueryName(), - ]; - break; - } - } - - /** - * {@inheritdoc} - */ - public function submitOptionsForm(&$form, FormStateInterface $form_state) { - parent::submitOptionsForm($form, $form_state); - $section = $form_state->get('section'); - switch ($section) { - case 'graphql_query_name': - $this->setOption($section, $form_state->getValue($section)); - break; - } } /** @@ -262,4 +144,5 @@ public function render() { 'cache' => CacheableMetadata::createFromRenderArray($this->view->element), ]; } + } diff --git a/src/Plugin/views/exposed_form/GraphQL.php b/src/Plugin/views/exposed_form/GraphQL.php index 0865bb2..ac0f631 100644 --- a/src/Plugin/views/exposed_form/GraphQL.php +++ b/src/Plugin/views/exposed_form/GraphQL.php @@ -26,4 +26,5 @@ public function renderExposedForm($block = FALSE) { return NULL; } + } diff --git a/src/Plugin/views/row/GraphQLEntityRow.php b/src/Plugin/views/row/GraphQLEntityRow.php index 82b4fbb..1fab4f5 100644 --- a/src/Plugin/views/row/GraphQLEntityRow.php +++ b/src/Plugin/views/row/GraphQLEntityRow.php @@ -4,7 +4,6 @@ use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Entity\EntityRepositoryInterface; -use Drupal\Core\Entity\EntityTypeBundleInfo; use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Entity\Plugin\DataType\EntityAdapter; use Drupal\Core\Language\LanguageManagerInterface; @@ -35,18 +34,11 @@ class GraphQLEntityRow extends RowPluginBase { protected $usesOptions = FALSE; /** - * Contains the entity type of this row plugin instance. - * - * @var \Drupal\Core\Entity\EntityTypeInterface - */ - protected $entityType; - - /** - * The entity type bundle info. + * The language manager. * - * @var \Drupal\Core\Entity\EntityTypeBundleInfo + * @var \Drupal\Core\Language\LanguageManagerInterface */ - protected $entityTypeBundleInfo; + protected $languageManager; /** * The entity type manager. @@ -63,42 +55,44 @@ class GraphQLEntityRow extends RowPluginBase { protected $entityRepository; /** - * The language manager. - * - * @var \Drupal\Core\Language\LanguageManagerInterface + * {@inheritdoc} */ - protected $languageManager; + public static function create(ContainerInterface $container, array $configuration, $pluginId, $pluginDefinition) { + $plugin = parent::create($container, $configuration, $pluginId, $pluginDefinition); + $plugin->setLanguageManager($container->get('language_manager')); + $plugin->setEntityTypeManager($container->get('entity_type.manager')); + $plugin->setEntityRepository($container->get('entity.repository')); + return $plugin; + } /** - * {@inheritdoc} + * Set the language manager. * - * @param \Drupal\Core\Entity\EntityTypeBundleInfo $entityTypeBundleInfo - * The entity type manager. * @param \Drupal\Core\Language\LanguageManagerInterface $languageManager * The language manager. */ - public function __construct(array $configuration, $pluginId, $pluginDefinition, EntityTypeBundleInfo $entityTypeBundleInfo, LanguageManagerInterface $languageManager, EntityTypeManagerInterface $entityTypeManager, EntityRepositoryInterface $entityRepository) { - parent::__construct($configuration, $pluginId, $pluginDefinition); - - $this->entityTypeBundleInfo = $entityTypeBundleInfo; + protected function setLanguageManager(LanguageManagerInterface $languageManager) { $this->languageManager = $languageManager; + } + + /** + * Set the entity type manager. + * + * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager + * The entity type manager. + */ + protected function setEntityTypeManager(EntityTypeManagerInterface $entityTypeManager) { $this->entityTypeManager = $entityTypeManager; - $this->entityRepository = $entityRepository; } /** - * {@inheritdoc} + * Set the entity repository. + * + * @param \Drupal\Core\Entity\EntityRepositoryInterface $entityRepository + * The entity repository. */ - public static function create(ContainerInterface $container, array $configuration, $pluginId, $pluginDefinition) { - return new static( - $configuration, - $pluginId, - $pluginDefinition, - $container->get('entity_type.bundle.info'), - $container->get('language_manager'), - $container->get('entity_type.manager'), - $container->get('entity.repository') - ); + protected function setEntityRepository(EntityRepositoryInterface $entityRepository) { + $this->entityRepository = $entityRepository; } /** @@ -123,20 +117,6 @@ protected function getEntityTranslationRenderer() { return NULL; } - /** - * {@inheritdoc} - */ - public function getEntityTypeManager() { - return $this->entityTypeManager; - } - - /** - * {@inheritdoc} - */ - public function getEntityRepository() { - return $this->entityRepository; - } - /** * {@inheritdoc} */ @@ -151,15 +131,22 @@ public function getEntityTypeId() { /** * {@inheritdoc} */ - protected function getEntityTypeBundleInfo() { - return $this->entityTypeBundleInfo; + protected function getLanguageManager() { + return $this->languageManager; } /** * {@inheritdoc} */ - protected function getLanguageManager() { - return $this->languageManager; + protected function getEntityTypeManager() { + return $this->entityTypeManager; + } + + /** + * {@inheritdoc} + */ + protected function getEntityRepository() { + return $this->entityRepository; } /** diff --git a/src/Plugin/views/row/GraphQLFieldRow.php b/src/Plugin/views/row/GraphQLFieldRow.php deleted file mode 100644 index 863b552..0000000 --- a/src/Plugin/views/row/GraphQLFieldRow.php +++ /dev/null @@ -1,234 +0,0 @@ -options['field_options'])) { - $options = (array) $this->options['field_options']; - // Prepare a trimmed version of replacement aliases. - $aliases = static::extractFromOptionsArray('alias', $options); - $this->replacementAliases = array_filter(array_map('trim', $aliases)); - // Prepare an array of raw output field options. - $this->rawOutputOptions = static::extractFromOptionsArray('raw_output', $options); - $this->typeOptions = static::extractFromOptionsArray('type', $options); - } - } - - /** - * {@inheritdoc} - */ - protected function defineOptions() { - $options = parent::defineOptions(); - $options['field_options'] = ['default' => []]; - - return $options; - } - - /** - * {@inheritdoc} - */ - public function buildOptionsForm(&$form, FormStateInterface $form_state) { - parent::buildOptionsForm($form, $form_state); - - $form['field_options'] = [ - '#type' => 'table', - '#header' => [ - $this->t('Field'), - $this->t('Alias'), - $this->t('Raw output'), - $this->t('Type'), - ], - '#empty' => $this->t('You have no fields. Add some to your view.'), - '#tree' => TRUE, - ]; - - $options = $this->options['field_options']; - - if ($fields = $this->view->display_handler->getOption('fields')) { - foreach ($fields as $id => $field) { - // Don't show the field if it has been excluded. - if (!empty($field['exclude'])) { - continue; - } - - $form['field_options'][$id]['field'] = [ - '#markup' => $id, - ]; - - $form['field_options'][$id]['alias'] = [ - '#title' => $this->t('Alias for @id', ['@id' => $id]), - '#title_display' => 'invisible', - '#type' => 'textfield', - '#default_value' => isset($options[$id]['alias']) ? $options[$id]['alias'] : '', - '#element_validate' => [[$this, 'validateAliasName']], - ]; - - $form['field_options'][$id]['raw_output'] = [ - '#title' => $this->t('Raw output for @id', ['@id' => $id]), - '#title_display' => 'invisible', - '#type' => 'checkbox', - '#default_value' => isset($options[$id]['raw_output']) ? $options[$id]['raw_output'] : '', - ]; - - $form['field_options'][$id]['type'] = [ - '#type' => 'select', - '#options' => [ - 'String' => $this->t('String'), - 'Int' => $this->t('Int'), - 'Float' => $this->t('Float'), - 'Boolean' => $this->t('Boolean'), - ], - '#default_value' => isset($options[$id]['type']) ? $options[$id]['type'] : 'String', - ]; - } - } - } - - /** - * Form element validation handler. - */ - public function validateAliasName($element, FormStateInterface $form_state) { - if (preg_match('@[^A-Za-z0-9_-]+@', $element['#value'])) { - $form_state->setError($element, $this->t('The machine-readable name must contain only letters, numbers, dashes and underscores.')); - } - } - - /** - * {@inheritdoc} - */ - public function validateOptionsForm(&$form, FormStateInterface $form_state) { - // Collect an array of aliases to validate. - $aliases = static::extractFromOptionsArray('alias', $form_state->getValue(['row_options', 'field_options'])); - - // If array filter returns empty, no values have been entered. Unique keys - // should only be validated if we have some. - if (($filtered = array_filter($aliases)) && (array_unique($filtered) !== $filtered)) { - $form_state->setErrorByName('aliases', $this->t('All field aliases must be unique')); - } - } - - /** - * {@inheritdoc} - */ - public function render($row) { - $output = []; - - foreach ($this->view->field as $id => $field) { - // If the raw output option has been set, just get the raw value. - if (!empty($this->rawOutputOptions[$id])) { - $value = $field->getValue($row); - } - // Otherwise, pass this through the field advancedRender() method. - else { - $value = $field->advancedRender($row); - } - - // Omit excluded fields from the rendered output. - if (empty($field->options['exclude'])) { - $output[$this->getFieldKeyAlias($id)] = $value; - } - } - - return $output; - } - - /** - * Return an alias for a field ID, as set in the options form. - * - * @param string $id - * The field id to lookup an alias for. - * - * @return string - * The matches user entered alias, or the original ID if nothing is found. - */ - public function getFieldKeyAlias($id) { - if (isset($this->replacementAliases[$id])) { - return $this->replacementAliases[$id]; - } - - return $id; - } - - /** - * Return a GraphQL field type, as set in the options form. - * - * @param string $id - * The field id to lookup a type for. - * - * @return string - * The matches user entered type, or String. - */ - public function getFieldType($id) { - if (isset($this->typeOptions[$id])) { - return $this->typeOptions[$id]; - } - - return 'String'; - } - - /** - * Extracts a set of option values from a nested options array. - * - * @param string $key - * The key to extract from each array item. - * @param array $options - * The options array to return values from. - * - * @return array - * A regular one dimensional array of values. - */ - protected static function extractFromOptionsArray($key, array $options) { - return array_map(function($item) use ($key) { - return isset($item[$key]) ? $item[$key] : NULL; - }, $options); - } - -} diff --git a/src/Plugin/views/style/GraphQL.php b/src/Plugin/views/style/GraphQL.php index 08b98f2..10d48df 100644 --- a/src/Plugin/views/style/GraphQL.php +++ b/src/Plugin/views/style/GraphQL.php @@ -49,4 +49,5 @@ public function render() { return $rows; } + } diff --git a/src/ViewDeriverHelperTrait.php b/src/ViewDeriverHelperTrait.php deleted file mode 100644 index a64325b..0000000 --- a/src/ViewDeriverHelperTrait.php +++ /dev/null @@ -1,385 +0,0 @@ - [ - 'type' => StringHelper::camelCase($id, 'contextual', 'filter', 'input'), - ], - ]; - } - - return []; - } - - /** - * Helper function to retrieve the sort arguments if any are exposed. - * - * @param \Drupal\views\Plugin\views\display\DisplayPluginInterface $display - * The display plugin. - * @param string $id - * The plugin derivative id. - * - * @return array - * The sort arguments if any exposed sorts are available. - */ - protected function getSortArguments(DisplayPluginInterface $display, $id) { - $sorts = array_filter($display->getOption('sorts') ?: [], function ($sort) { - return $sort['exposed']; - }); - return $sorts ? [ - 'sortDirection' => [ - 'type' => 'ViewSortDirection', - 'default' => 'asc', - ], - 'sortBy' => [ - 'type' => StringHelper::camelCase($id, 'sort', 'by'), - ], - ] : []; - } - - /** - * Helper function to return the filter argument if applicable. - * - * @param \Drupal\views\Plugin\views\display\DisplayPluginInterface $display - * The display plugin. - * @param string $id - * The plugin derivative id. - * - * @return array - * The filter argument if any exposed filters are available. - */ - protected function getFilterArguments(DisplayPluginInterface $display, $id) { - $filters = array_filter($display->getOption('filters') ?: [], function ($filter) { - return array_key_exists('exposed', $filter) && $filter['exposed']; - }); - - return !empty($filters) ? [ - 'filter' => [ - 'type' => $display->getGraphQLFilterInputName(), - ], - ] : []; - } - - /** - * Helper function to retrieve the pager arguments if the display is paged. - * - * @param \Drupal\views\Plugin\views\display\DisplayPluginInterface $display - * The display plugin. - * - * @return array - * An array of pager arguments if the view display is paged. - */ - protected function getPagerArguments(DisplayPluginInterface $display) { - return $this->isPaged($display) ? [ - 'page' => ['type' => 'Int', 'default' => $this->getPagerOffset($display)], - 'pageSize' => [ - 'type' => 'Int', - 'default' => $this->getPagerLimit($display), - ], - ] : []; - } - - /** - * Helper function to retrieve the types that the view can be attached to. - * - * @param array $arguments - * An array containing information about the available arguments. - * @param array $types - * Types where it needs to be added. - * - * @return array - * An array of additional types the view can be embedded in. - */ - protected function getTypes(array $arguments, array $types = ['Root']) { - - if (empty($arguments)) { - return $types; - } - - foreach ($arguments as $argument) { - // Depending on whether bundles are known, we expose the view field - // either on the interface (e.g. Node) or on the type (e.g. NodePage) - // level. Here we specify types managed by other graphql_* modules, - // yet we don't define these modules as dependencies. If types are not - // in the schema, the resulting GraphQL field will be attached to - // nowhere, so it won't go into the schema. - if (empty($argument['bundles']) && empty($argument['entity_type'])) { - continue; - } - - if (empty($argument['bundles'])) { - $types = array_merge($types, [StringHelper::camelCase($argument['entity_type'])]); - } - else { - $types = array_merge($types, array_map(function ($bundle) use ($argument) { - return StringHelper::camelCase($argument['entity_type'], $bundle); - }, array_keys($argument['bundles']))); - } - } - - return $types; - } - - /** - * Check if a pager is configured. - * - * @param \Drupal\views\Plugin\views\display\DisplayPluginInterface $display - * The display configuration. - * - * @return bool - * Flag indicating if the view is configured with a pager. - */ - protected function isPaged(DisplayPluginInterface $display) { - $pagerOptions = $display->getOption('pager'); - return isset($pagerOptions['type']) && in_array($pagerOptions['type'], [ - 'full', - 'mini', - ]); - } - - /** - * Returns a view display object. - * - * @param \Drupal\views\ViewEntityInterface $view - * The view object. - * @param string $displayId - * The display ID to use. - * - * @return \Drupal\views\Plugin\views\display\DisplayPluginInterface - * The view display object. - */ - protected function getViewDisplay(ViewEntityInterface $view, $displayId) { - $viewExecutable = $view->getExecutable(); - $viewExecutable->setDisplay($displayId); - return $viewExecutable->getDisplay(); - } - - /** - * Get the configured default limit. - * - * @param \Drupal\views\Plugin\views\display\DisplayPluginInterface $display - * The display configuration. - * - * @return int - * The default limit. - */ - protected function getPagerLimit(DisplayPluginInterface $display) { - $pagerOptions = $display->getOption('pager'); - return NestedArray::getValue($pagerOptions, [ - 'options', - 'items_per_page', - ]) ?: 0; - } - - /** - * Get the configured default offset. - * - * @param \Drupal\views\Plugin\views\display\DisplayPluginInterface $display - * The display configuration. - * - * @return int - * The default offset. - */ - protected function getPagerOffset(DisplayPluginInterface $display) { - $pagerOptions = $display->getOption('pager'); - return NestedArray::getValue($pagerOptions, [ - 'options', - 'offset', - ]) ?: 0; - } - - /** - * Check if a certain interface exists. - * - * @param string $interface - * The GraphQL interface name. - * @param \Drupal\Component\Plugin\PluginManagerInterface $interfacePluginManager - * Plugin interface manager. - * - * @return bool - * Boolean flag indicating if the interface exists. - */ - protected function interfaceExists($interface, PluginManagerInterface $interfacePluginManager) { - return (bool) array_filter($interfacePluginManager->getDefinitions(), function ($definition) use ($interface) { - return $definition['name'] === $interface; - }); - } - - /** - * Retrieves the type the view's rows resolve to. - * - * @param \Drupal\views\ViewEntityInterface $view - * The view entity. - * @param string $displayId - * The id of the current display. - * @param \Drupal\Component\Plugin\PluginManagerInterface $interfacePluginManager - * Interface plugin manager. - * - * @return null|string - * The name of the type or NULL if the type could not be derived. - */ - protected function getRowResolveType(ViewEntityInterface $view, $displayId, PluginManagerInterface $interfacePluginManager) { - /** @var \Drupal\graphql_views\Plugin\views\display\GraphQL $display */ - $display = $this->getViewDisplay($view, $displayId); - $rowPlugin = $display->getPlugin('row'); - - if ($rowPlugin instanceof GraphQLFieldRow) { - return StringHelper::camelCase($display->getGraphQLRowName()); - } - - if ($rowPlugin instanceof GraphQLEntityRow) { - $executable = $view->getExecutable(); - $executable->setDisplay($displayId); - - if ($entityType = $executable->getBaseEntityType()) { - $typeName = $entityType->id(); - $typeNameCamel = StringHelper::camelCase($typeName); - if ($this->interfaceExists($typeNameCamel, $interfacePluginManager)) { - $filters = $executable->getDisplay()->getOption('filters'); - $dataTable = $entityType->getDataTable(); - $bundleKey = $entityType->getKey('bundle'); - - foreach ($filters as $filter) { - $isBundleFilter = $filter['table'] == $dataTable && $filter['field'] == $bundleKey; - $isSingleValued = is_array($filter['value']) && count($filter['value']) == 1; - $isExposed = isset($filter['exposed']) && $filter['exposed']; - if ($isBundleFilter && $isSingleValued && !$isExposed) { - $bundle = reset($filter['value']); - $typeName .= "_$bundle"; - break; - } - } - - return StringHelper::camelCase($typeName); - } - } - - return 'Entity'; - } - - return NULL; - } - - /** - * Returns a view style object. - * - * @param \Drupal\views\ViewEntityInterface $view - * The view object. - * @param string $displayId - * The display ID to use. - * - * @return \Drupal\views\Plugin\views\style\StylePluginBase - * The view style object. - */ - protected function getViewStyle(ViewEntityInterface $view, $displayId) { - $viewExecutable = $view->getExecutable(); - $viewExecutable->setDisplay($displayId); - return $viewExecutable->getStyle(); - } - - /** - * Returns cache metadata plugin definitions. - * - * @param \Drupal\views\ViewEntityInterface $view - * The view object. - * @param \Drupal\views\Plugin\views\display\DisplayPluginInterface $display - * The view display. - * - * @return array - * The cache metadata definitions for the plugin definition. - */ - protected function getCacheMetadataDefinition(ViewEntityInterface $view, DisplayPluginInterface $display) { - $metadata = $display->getCacheMetadata() - ->addCacheTags($view->getCacheTags()) - ->addCacheContexts($view->getCacheContexts()) - ->mergeCacheMaxAge($view->getCacheMaxAge()); - - return [ - 'schema_cache_tags' => $metadata->getCacheTags(), - 'schema_cache_max_age' => $metadata->getCacheMaxAge(), - 'response_cache_contexts' => array_filter($metadata->getCacheContexts(), function ($context) { - // Don't emit the url cache contexts. - return $context !== 'url' && strpos($context, 'url.') !== 0; - }), - ]; - } - - /** - * Returns information about view arguments (contextual filters). - * - * @param array $viewArguments - * The "arguments" option of a view display. - * - * @return array - * Arguments information keyed by the argument ID. Subsequent array keys: - * - index: argument index. - * - entity_type: target entity type. - * - bundles: target bundles (can be empty). - * - * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException - */ - protected function getArgumentsInfo(array $viewArguments) { - $argumentsInfo = []; - /* @var \Drupal\Core\Entity\EntityTypeManager $entityTypeManager */ - $entityTypeManager = \Drupal::service('entity_type.manager'); - - $index = 0; - foreach ($viewArguments as $argumentId => $argument) { - $info = [ - 'index' => $index, - 'entity_type' => NULL, - 'bundles' => [], - ]; - - if (isset($argument['entity_type']) && isset($argument['entity_field'])) { - $entityType = $entityTypeManager->getDefinition($argument['entity_type']); - if ($entityType) { - $idField = $entityType->getKey('id'); - if ($idField === $argument['entity_field']) { - $info['entity_type'] = $argument['entity_type']; - if ( - $argument['specify_validation'] && - strpos($argument['validate']['type'], 'entity:') === 0 && - !empty($argument['validate_options']['bundles']) - ) { - $info['bundles'] = $argument['validate_options']['bundles']; - } - } - } - } - - $argumentsInfo[$argumentId] = $info; - $index++; - } - - return $argumentsInfo; - } - -} diff --git a/tests/modules/graphql_views_test/graphql_views_test.module b/tests/modules/graphql_views_test/graphql_views_test.module index 513c30b..54a29fc 100644 --- a/tests/modules/graphql_views_test/graphql_views_test.module +++ b/tests/modules/graphql_views_test/graphql_views_test.module @@ -1,7 +1,15 @@ storage->id() . ':' . $view->current_display; diff --git a/tests/queries/paged.gql b/tests/queries/paged.gql index ce4a2ea..8f4bfbe 100644 --- a/tests/queries/paged.gql +++ b/tests/queries/paged.gql @@ -26,4 +26,4 @@ entityLabel } } -} \ No newline at end of file +} diff --git a/tests/queries/simple.gql b/tests/queries/simple.gql index 8c928df..e3f8474 100644 --- a/tests/queries/simple.gql +++ b/tests/queries/simple.gql @@ -4,4 +4,4 @@ entityLabel } } -} \ No newline at end of file +} diff --git a/tests/src/Kernel/ContextualViewsTest.php b/tests/src/Kernel/ContextualViewsTest.php index 87fdf5e..551f2e7 100644 --- a/tests/src/Kernel/ContextualViewsTest.php +++ b/tests/src/Kernel/ContextualViewsTest.php @@ -14,7 +14,7 @@ class ContextualViewsTest extends ViewsTestBase { /** * {@inheritdoc} */ - protected function setUp() { + protected function setUp(): void { parent::setUp(); $this->createContentType(['type' => 'test2']); } @@ -22,7 +22,7 @@ protected function setUp() { /** * {@inheritdoc} */ - protected function defaultCacheContexts() { + protected function defaultCacheContexts(): array { return array_merge([ 'languages:language_content', 'languages:language_interface', @@ -34,7 +34,7 @@ protected function defaultCacheContexts() { /** * {@inheritdoc} */ - protected function defaultCacheTags() { + protected function defaultCacheTags(): array { return array_merge([ 'config:field.storage.node.field_tags', ], parent::defaultCacheTags()); @@ -44,6 +44,7 @@ protected function defaultCacheTags() { * Test if view contextual filters are set properly. */ public function testContextualViewArgs() { + $this->markTestSkipped('Not supported right now.'); $test2Node = $this->createNode(['type' => 'test2']); $this->graphQlProcessor()->processQuery( @@ -84,31 +85,4 @@ public function testContextualViewArgs() { ]); } - /** - * Test if view fields are attached to correct types. - */ - public function testContextualViewFields() { - $schema = $this->introspect(); - - $field = 'graphqlTestContextualTitleArgView'; - $this->assertArrayHasKey($field, $schema['types']['Query']['fields']); - $this->assertArrayNotHasKey($field, $schema['types']['Node']['fields']); - $this->assertArrayNotHasKey($field, $schema['types']['NodeTest']['fields']); - - $field = 'graphqlTestContextualNodeView'; - $this->assertArrayHasKey($field, $schema['types']['Query']['fields']); - $this->assertArrayHasKey($field, $schema['types']['Node']['fields']); - $this->assertArrayHasKey($field, $schema['types']['NodeTest']['fields']); - - $field = 'graphqlTestContextualNodetestView'; - $this->assertArrayHasKey($field, $schema['types']['Query']['fields']); - $this->assertArrayNotHasKey($field, $schema['types']['Node']['fields']); - $this->assertArrayHasKey($field, $schema['types']['NodeTest']['fields']); - - $field = 'graphqlTestContextualNodeAndNodetestView'; - $this->assertArrayHasKey($field, $schema['types']['Query']['fields']); - $this->assertArrayHasKey($field, $schema['types']['Node']['fields']); - $this->assertArrayHasKey($field, $schema['types']['NodeTest']['fields']); - } - } diff --git a/tests/src/Kernel/ViewsTest.php b/tests/src/Kernel/ViewsTest.php index add0dd1..e01c015 100644 --- a/tests/src/Kernel/ViewsTest.php +++ b/tests/src/Kernel/ViewsTest.php @@ -2,7 +2,6 @@ namespace Drupal\Tests\graphql_views\Kernel; - /** * Test views support in GraphQL. * @@ -13,8 +12,10 @@ class ViewsTest extends ViewsTestBase { /** * {@inheritdoc} */ - protected function defaultCacheContexts() { + protected function defaultCacheContexts(): array { return array_merge([ + 'languages:language_content', + 'languages:language_interface', 'user.permissions', 'user.node_grants:view', ], parent::defaultCacheContexts()); @@ -24,6 +25,36 @@ protected function defaultCacheContexts() { * Test that the view returns both nodes. */ public function testSimpleView() { + $schema = <<setUpSchema($schema); + + $this->mockResolver('Query', 'graphqlTestSimpleView', + $this->builder->produce('views') + ->map('view_id', $this->builder->fromValue('graphql_test')) + ->map('display_id', $this->builder->fromValue('simple')) + ->map('page', $this->builder->fromArgument('page')) + ->map('page_size', $this->builder->fromArgument('pageSize')) + ); + + $this->mockResolver('Node', 'entityLabel', + $this->builder->produce('entity_label') + ->map('entity', $this->builder->fromParent()) + ); + $query = $this->getQueryFromFile('simple.gql'); $this->assertResults($query, [], [ 'graphqlTestSimpleView' => [ @@ -50,6 +81,36 @@ public function testSimpleView() { * Test paging support. */ public function testPagedView() { + $schema = <<setUpSchema($schema); + + $this->mockResolver('Query', 'graphqlTestPagedView', + $this->builder->produce('views') + ->map('view_id', $this->builder->fromValue('graphql_test')) + ->map('display_id', $this->builder->fromValue('paged')) + ->map('page', $this->builder->fromArgument('page')) + ->map('page_size', $this->builder->fromArgument('pageSize')) + ); + + $this->mockResolver('Node', 'entityLabel', + $this->builder->produce('entity_label') + ->map('entity', $this->builder->fromParent()) + ); + $query = $this->getQueryFromFile('paged.gql'); $this->assertResults($query, [], [ 'page_one' => [ @@ -97,6 +158,52 @@ public function testPagedView() { * Test sorting behavior. */ public function testSortedView() { + $schema = <<setUpSchema($schema); + + $this->mockResolver('Query', 'graphqlTestSortedView', + $this->builder->produce('views') + ->map('view_id', $this->builder->fromValue('graphql_test')) + ->map('display_id', $this->builder->fromValue('sorted')) + ->map('sort_direction', $this->builder->fromArgument('sortDirection')) + ->map('sort_by', $this->builder->compose( + $this->builder->fromArgument('sortBy'), + $this->builder->callback(function ($direction) { + $map = ['TITLE' => 'title', 'NID' => 'nid']; + return $map[$direction] ?? NULL; + }) + )) + ); + + $this->mockResolver('Node', 'entityLabel', + $this->builder->produce('entity_label') + ->map('entity', $this->builder->fromParent()) + ); + $query = $this->getQueryFromFile('sorted.gql'); $this->assertResults($query, [], [ 'default' => [ @@ -152,15 +259,59 @@ public function testSortedView() { * Test filter behavior. */ public function testFilteredView() { + $schema = <<setUpSchema($schema); + + $this->mockResolver('Query', 'graphqlTestFilteredView', + $this->builder->produce('views') + ->map('view_id', $this->builder->fromValue('graphql_test')) + ->map('display_id', $this->builder->fromValue('filtered')) + ->map('filter', $this->builder->compose( + $this->builder->fromArgument('filter'), + $this->builder->callback(function ($filter) { + $mapped = []; + $map = ['TITLE' => 'title', 'NID' => 'nid']; + foreach ($filter as $key => $value) { + if (isset($map[$key])) { + $mapped = [$map[$key] => $value]; + } + } + return $mapped; + }) + )) + ); + + $this->mockResolver('Node', 'entityLabel', + $this->builder->produce('entity_label') + ->map('entity', $this->builder->fromParent()) + ); + $query = <<assertResults($query, [], [ @@ -184,9 +335,53 @@ public function testFilteredView() { * Test filter behavior. */ public function testMultiValueFilteredView() { + $schema = <<setUpSchema($schema); + + $this->mockResolver('Query', 'graphqlTestFilteredView', + $this->builder->produce('views') + ->map('view_id', $this->builder->fromValue('graphql_test')) + ->map('display_id', $this->builder->fromValue('filtered')) + ->map('filter', $this->builder->compose( + $this->builder->fromArgument('filter'), + $this->builder->callback(function ($filter) { + $mapped = []; + $map = ['field_tags' => 'field_tags']; + foreach ($filter as $key => $value) { + if (isset($map[$key])) { + $mapped = [$map[$key] => $value]; + } + } + return $mapped; + }) + )) + ); + + $this->mockResolver('Node', 'entityLabel', + $this->builder->produce('entity_label') + ->map('entity', $this->builder->fromParent()) + ); + $query = <<setUpSchema($schema); + + $this->mockResolver('Query', 'graphqlTestFilteredView', + $this->builder->produce('views') + ->map('view_id', $this->builder->fromValue('graphql_test')) + ->map('display_id', $this->builder->fromValue('filtered')) + ->map('filter', $this->builder->compose( + $this->builder->fromArgument('filter'), + $this->builder->callback(function ($filter) { + $mapped = []; + $map = ['node_type' => 'node_type']; + foreach ($filter as $key => $value) { + if (isset($map[$key])) { + $mapped = [$map[$key] => $value]; + } + } + return $mapped; + }) + )) + ); + + $this->mockResolver('Node', 'entityLabel', + $this->builder->produce('entity_label') + ->map('entity', $this->builder->fromParent()) + ); + $query = <<setUpSchema($schema); + + $this->mockResolver('Query', 'graphqlBundleTestGraphql1View', + $this->builder->produce('views') + ->map('view_id', $this->builder->fromValue('graphql_bundle_test')) + ->map('display_id', $this->builder->fromValue('graphql_1')) + ); + + $this->mockResolver('Node', 'entityLabel', + $this->builder->produce('entity_label') + ->map('entity', $this->builder->fromParent()) + ); + $query = $this->getQueryFromFile('single_bundle_filter.gql'); $this->assertResults($query, [], [ 'withSingleBundleFilter' => [ diff --git a/tests/src/Kernel/ViewsTestBase.php b/tests/src/Kernel/ViewsTestBase.php index ca35782..be1c244 100644 --- a/tests/src/Kernel/ViewsTestBase.php +++ b/tests/src/Kernel/ViewsTestBase.php @@ -5,7 +5,7 @@ use Drupal\taxonomy\Entity\Term; use Drupal\taxonomy\Entity\Vocabulary; use Drupal\Tests\field\Traits\EntityReferenceTestTrait; -use Drupal\Tests\graphql_core\Kernel\GraphQLContentTestBase; +use Drupal\Tests\graphql\Kernel\GraphQLTestBase; use Drupal\Tests\node\Traits\ContentTypeCreationTrait; use Drupal\Tests\node\Traits\NodeCreationTrait; @@ -14,7 +14,7 @@ * * @group graphql_views */ -abstract class ViewsTestBase extends GraphQLContentTestBase { +abstract class ViewsTestBase extends GraphQLTestBase { use NodeCreationTrait; use ContentTypeCreationTrait; use EntityReferenceTestTrait; @@ -22,14 +22,11 @@ abstract class ViewsTestBase extends GraphQLContentTestBase { /** * {@inheritdoc} */ - public static $modules = [ - 'node', - 'field', + protected static $modules = [ 'filter', 'text', 'views', 'taxonomy', - 'graphql_core', 'graphql_views', 'graphql_views_test', ]; @@ -44,11 +41,12 @@ abstract class ViewsTestBase extends GraphQLContentTestBase { /** * {@inheritdoc} */ - protected function setUp() { + protected function setUp(): void { parent::setUp(); $this->installEntitySchema('view'); $this->installEntitySchema('taxonomy_term'); $this->installConfig(['node', 'filter', 'views', 'graphql_views_test']); + $this->createContentType(['type' => 'test']); $this->createEntityReferenceField('node', 'test', 'field_tags', 'Tags', 'taxonomy_term'); Vocabulary::create([ @@ -76,13 +74,14 @@ protected function setUp() { ]); $terms['C']->save(); - foreach ($this->letters as $index => $letter) { + foreach ($this->letters as $letter) { $this->createNode([ 'title' => 'Node ' . $letter, 'type' => 'test', 'field_tags' => $terms[$letter], ])->save(); } + } }