├── .github ├── ISSUE_TEMPLATE.md ├── PULL_REQUEST_TEMPLATE.md └── workflows │ └── ci.yml ├── .gitignore ├── .php-cs-fixer.dist.php ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── composer.json ├── docs ├── auth-token.png ├── custom-http-headers.md ├── custom-parameters.md ├── custom-rendering.md ├── graphql-endpoint.md └── libraries-versions.md ├── phpunit.xml.dist ├── src ├── Config │ ├── GraphQLEndpoint │ │ ├── GraphQLEndpointInvalidSchemaException.php │ │ ├── Helpers │ │ │ └── OverblogGraphQLBundleEndpointResolver.php │ │ ├── RootResolver.php │ │ └── RouteResolver.php │ ├── GraphiQLControllerEndpoint.php │ ├── GraphiQLViewConfig.php │ └── GraphiQLViewJavaScriptLibraries.php ├── Controller │ └── GraphiQLController.php ├── DependencyInjection │ ├── Compiler │ │ └── Endpoints │ │ │ ├── DefaultEndpointWiringPass.php │ │ │ └── OverblogGraphQLBundleEndpointWiringPass.php │ ├── Configuration.php │ └── OverblogGraphiQLExtension.php ├── OverblogGraphiQLBundle.php └── Resources │ ├── config │ ├── routing.xml │ ├── schema │ │ └── overblog_graphiql-0.1.xsd │ └── services.xml │ └── views │ └── GraphiQL │ ├── auth.html.twig │ └── index.html.twig └── tests ├── Config └── GraphQLEndpoint │ ├── Helpers │ └── OverblogGraphQLBundleEndpointResolverTest.php │ ├── RootResolverTest.php │ └── RouteResolverTest.php ├── Controller └── GraphiQLControllerTest.php ├── Fixtures └── TestKernel.php ├── ForwardCompatTestCaseTrait.php ├── Functional └── DependencyInjection │ ├── Fixtures │ ├── Xml │ │ ├── TestKernel.php │ │ └── config.xml │ └── Yaml │ │ ├── TestKernel.php │ │ └── config.yml │ ├── Xml │ └── ConfigurationTest.php │ └── Yaml │ └── ConfigurationTest.php ├── Integration └── OverblogGraphQLBundle │ ├── Controller │ └── GraphiQLControllerTest.php │ ├── Fixtures │ ├── Resources │ │ └── config │ │ │ └── routing.xml │ └── TestKernel.php │ └── TestCase.php ├── TestCase.php └── TestKernel.php /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | | Q | A 2 | | ---------------- | ----- 3 | | Bug report? | yes/no 4 | | Feature request? | yes/no 5 | | BC Break report? | yes/no 6 | | RFC? | yes/no 7 | | Version/Branch | x.y.z 8 | 9 | 12 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | | Q | A 2 | | ------------- | --- 3 | | Bug fix? | yes/no 4 | | New feature? | yes/no 5 | | BC breaks? | yes/no 6 | | Deprecations? | yes/no 7 | | Tests pass? | yes/no 8 | | Documented? | yes/no 9 | | Fixed tickets | #... 10 | | License | MIT 11 | 12 | 16 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | pull_request: null 5 | push: 6 | branches: 7 | - "*.*" 8 | - "master" 9 | schedule: 10 | - cron: "0 4 * * *" 11 | 12 | jobs: 13 | tests: 14 | runs-on: ubuntu-22.04 15 | strategy: 16 | fail-fast: false 17 | matrix: 18 | php-version: 19 | - '8.0' 20 | - '8.1' 21 | - '8.2' 22 | - '8.3' 23 | - '8.4' 24 | symfony-version: 25 | - '5.4.*' 26 | - '6.0.*' 27 | - '6.2.*' 28 | - '6.4.*' 29 | - '7.0.*' 30 | - '7.1.*' 31 | dependencies: 32 | - 'lowest' 33 | - 'highest' 34 | remove-dependencies: [ '' ] 35 | coverage: [ 'none' ] 36 | exclude: 37 | - php-version: '8.0' 38 | symfony-version: '6.2.*' 39 | - php-version: '8.0' 40 | symfony-version: '6.4.*' 41 | - php-version: '8.0' 42 | symfony-version: '7.0.*' 43 | - php-version: '8.0' 44 | symfony-version: '7.1.*' 45 | - php-version: '8.1' 46 | symfony-version: '7.0.*' 47 | - php-version: '8.1' 48 | symfony-version: '7.1.*' 49 | steps: 50 | - name: "Checkout" 51 | uses: "actions/checkout@v2" 52 | 53 | - name: "Install PHP" 54 | uses: "shivammathur/setup-php@v2" 55 | with: 56 | tools: flex 57 | php-version: "${{ matrix.php-version }}" 58 | coverage: "none" 59 | 60 | - name: "Change stability" 61 | if: "matrix.stability != ''" 62 | run: perl -pi -e 's/^}$/,"minimum-stability":"'"${{ matrix.minimum-stability }}"'"}/' composer.json && cat composer.json 63 | 64 | - name: "Webonyx GraphQL version" 65 | if: "matrix.graphql-version != ''" 66 | run: composer require "webonyx/graphql-php:${{ matrix.graphql-version }}" --dev --no-update 67 | 68 | - name: Remove dependencies 69 | if: "matrix.remove-dependencies != ''" 70 | run: composer remove --no-update ${{ matrix.remove-dependencies }} 71 | 72 | - name: "Install dependencies" 73 | uses: ramsey/composer-install@1.3.0 74 | with: 75 | dependency-versions: ${{ matrix.dependencies }} 76 | env: 77 | SYMFONY_REQUIRE: "${{ matrix.symfony-version }}" 78 | 79 | - name: "Run tests" 80 | run: composer test 81 | 82 | coding-standard: 83 | runs-on: ubuntu-22.04 84 | name: Coding Standard 85 | steps: 86 | - name: "Checkout" 87 | uses: "actions/checkout@v4" 88 | 89 | - name: "Install PHP" 90 | uses: "shivammathur/setup-php@v2" 91 | with: 92 | tools: flex 93 | php-version: "8.2" 94 | coverage: "none" 95 | 96 | - name: "Install dependencies" 97 | uses: "ramsey/composer-install@v2" 98 | 99 | - name: "Check coding standard" 100 | run: composer run check-cs 101 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | phpunit.xml 2 | /build 3 | /vendor 4 | /bin 5 | composer.lock 6 | composer.phar 7 | /.php-cs-fixer.php 8 | /*.cache 9 | 10 | phpbench.phar 11 | phpbench.phar.* 12 | /.phpbench_storage 13 | php-cs-fixer.phar 14 | -------------------------------------------------------------------------------- /.php-cs-fixer.dist.php: -------------------------------------------------------------------------------- 1 | exclude('Annotation') 5 | ->name('*.php') 6 | ->in([__DIR__.'/src', __DIR__.'/tests']) 7 | ; 8 | 9 | return (new PhpCsFixer\Config()) 10 | ->setRules( 11 | [ 12 | '@Symfony' => true, 13 | '@PHP80Migration' => true, 14 | '@PHP80Migration:risky' => true, 15 | 'ordered_imports' => ['imports_order' => ['class', 'function', 'const']], 16 | 'general_phpdoc_annotation_remove' => [ 17 | 'annotations' => [ 18 | 'author', 19 | 'category', 20 | 'copyright', 21 | 'created', 22 | 'license', 23 | 'package', 24 | 'since', 25 | 'subpackage', 26 | 'version', 27 | ], 28 | ], 29 | 'fully_qualified_strict_types' => true, 30 | 'single_line_throw' => false, 31 | 'phpdoc_to_comment' => false, 32 | 'no_superfluous_phpdoc_tags' => ['allow_mixed' => true], 33 | 'global_namespace_import' => ['import_functions' => true, 'import_classes' => true, 'import_constants' => true], 34 | 'phpdoc_summary' => false, 35 | 'single_line_comment_style' => false, 36 | 'phpdoc_no_alias_tag' => ['replacements' => ['type' => 'var']], 37 | 'no_mixed_echo_print' => ['use' => 'echo'], 38 | ] 39 | ) 40 | ->setFinder($finder) 41 | ->setUsingCache(true) 42 | ; 43 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Contributing 2 | ============ 3 | 4 | Running tests 5 | -------------- 6 | 7 | In the bundle directory: 8 | 9 | ```bash 10 | ./vendor/bin/phpunit 11 | ``` 12 | 13 | Giving some love to PHP CS 14 | --------------------------- 15 | 16 | ```bash 17 | composer require --dev 'friendsofphp/php-cs-fixer:^2.0' 18 | bin/php-cs-fixer fix 19 | ``` 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Overblog 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | OverblogGraphiQLBundle 2 | ====================== 3 | 4 | This Symfony bundle provides integration of [GraphiQL](https://github.com/graphql/graphiql) interface to your Symfony application 5 | 6 | [![Build Status](https://travis-ci.org/overblog/GraphiQLBundle.svg?branch=master)](https://travis-ci.org/overblog/GraphiQLBundle) 7 | [![Coverage Status](https://coveralls.io/repos/github/overblog/GraphiQLBundle/badge.svg?branch=master)](https://coveralls.io/github/overblog/GraphiQLBundle?branch=master) 8 | [![Latest Stable Version](https://poser.pugx.org/overblog/graphiql-bundle/version)](https://packagist.org/packages/overblog/graphiql-bundle) 9 | [![Latest Unstable Version](https://poser.pugx.org/overblog/graphiql-bundle/v/unstable)](https://packagist.org/packages/overblog/graphiql-bundle) 10 | [![Total Downloads](https://poser.pugx.org/overblog/graphiql-bundle/downloads)](https://packagist.org/packages/overblog/graphiql-bundle) 11 | 12 | Installation 13 | ------------ 14 | 15 | **a)** Download the bundle 16 | 17 | In the project directory: 18 | 19 | ```bash 20 | composer require --dev overblog/graphiql-bundle 21 | ``` 22 | 23 | Symfony Flex installation 24 | ------------ 25 | 26 | **Note** If you are using Symfony Standard go to the next section 27 | 28 | **a)** Accept the contrib recipes installation from Symfony Flex 29 | 30 | ``` 31 | - WARNING overblog/graphiql-bundle (0.1): From github.com/symfony/recipes-contrib 32 | The recipe for this package comes from the "contrib" repository, which is open to community contributions. 33 | Do you want to execute this recipe? 34 | [y] Yes 35 | [n] No 36 | [a] Yes for all packages, only for the current installation session 37 | [p] Yes permanently, never ask again for this project 38 | (defaults to n): 39 | ``` 40 | 41 | **b)** In case you don't have twig 42 | 43 | In the project directory: 44 | 45 | ```bash 46 | composer require twig 47 | ``` 48 | 49 | If you are using twig ONLY for graphiql you might want to use `--dev` during composer require 50 | 51 | Symfony Standard installation 52 | ------------ 53 | 54 | **a)** Enable the bundle in the 'dev' section 55 | 56 | ```php 57 | // in app/AppKernel.php 58 | class AppKernel extends Kernel 59 | { 60 | // ... 61 | 62 | public function registerBundles() 63 | { 64 | if (in_array($this->getEnvironment(), array('dev', 'test'))) { 65 | // ... 66 | $bundles[] = new Overblog\GraphiQLBundle\OverblogGraphiQLBundle(); 67 | } 68 | } 69 | } 70 | ``` 71 | 72 | **b)** Enable GraphiQL endpoint 73 | 74 | ```yaml 75 | # in app/config/routing_dev.yml 76 | overblog_graphiql_endpoint: 77 | resource: "@OverblogGraphiQLBundle/Resources/config/routing.xml" 78 | ``` 79 | 80 | Done 81 | ------------ 82 | 83 | It's done now, navigate to `/graphiql` in your project url 84 | 85 | More 86 | ------------ 87 | 88 | * [Custom HTTP headers](docs/custom-http-headers.md) 89 | * [Custom page rendering](docs/custom-rendering.md) 90 | * [Custom GraphiQL parameters](docs/custom-parameters.md) 91 | * [Define JavaScript libraries' versions](docs/libraries-versions.md) 92 | * [Define a custom GraphQL endpoint](docs/graphql-endpoint.md) 93 | 94 | Community 95 | --------- 96 | 97 | * Get some support on [Symfony devs Slack](https://symfony.com/slack-invite) 98 | on the dedicated channel **overblog-graphql**. 99 | * Follow us on [GitHub](https://github.com/overblog) 100 | 101 | Contributing 102 | ------------ 103 | 104 | * [See contributing documentation](CONTRIBUTING.md) 105 | * [Thanks to all contributors](https://github.com/overblog/GraphiQLBundle/graphs/contributors) 106 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "overblog/graphiql-bundle", 3 | "type": "symfony-bundle", 4 | "description": "Symfony GraphiQLBundle makes possible to render the UI into your symfony project", 5 | "keywords": [ 6 | "GraphQL", "GraphiQL" 7 | ], 8 | "license": "MIT", 9 | "authors": [ 10 | { 11 | "name": "Renato Mefi", 12 | "email": "renato@mefi.in" 13 | }, 14 | { 15 | "name": "Overblog", 16 | "homepage": "http://www.over-blog.com" 17 | } 18 | ], 19 | "config" : { 20 | "bin-dir": "bin", 21 | "sort-packages": true 22 | }, 23 | "require": { 24 | "php": "^8.0", 25 | "symfony/http-foundation": "^5.4 || ^6.0 || ^7.0", 26 | "symfony/twig-bundle": "^5.4 || ^6.0 || ^7.0", 27 | "symfony/routing": "^5.4 || ^6.0 || ^7.00" 28 | }, 29 | "require-dev": { 30 | "overblog/graphql-bundle": "dev-master", 31 | "webonyx/graphql-php": "^15.4", 32 | "phpunit/phpunit": "^9.5.10", 33 | "symfony/browser-kit": "^5.4 || ^6.0 || ^7.0", 34 | "symfony/config": "^5.4 || ^6.0 || ^7.0", 35 | "symfony/console": "^5.4 || ^6.0 || ^7.0", 36 | "symfony/dependency-injection": "^5.4 || ^6.0 || ^7.0", 37 | "symfony/dom-crawler": "^5.4 || ^6.0 || ^7.0", 38 | "symfony/expression-language": "^5.4 || ^6.0 || ^7.0", 39 | "symfony/framework-bundle": "^5.4 || ^6.0 || ^7.0", 40 | "symfony/finder": "^5.4 || ^6.0 || ^7.0", 41 | "symfony/templating": "^5.4 || ^6.0 || ^7.0", 42 | "symfony/yaml": "^5.4 || ^6.0 || ^7.0", 43 | "twig/twig": "^3.3.3" 44 | }, 45 | "autoload": { 46 | "psr-4": { "Overblog\\GraphiQLBundle\\": "src/" } 47 | }, 48 | "autoload-dev": { 49 | "psr-4": { "Overblog\\GraphiQLBundle\\Tests\\": "tests/" } 50 | }, 51 | "scripts": { 52 | "test": "bin/phpunit --color=always", 53 | "install-cs": "test -f php-cs-fixer.phar || wget https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/releases/download/v3.48.0/php-cs-fixer.phar -O php-cs-fixer.phar", 54 | "fix-cs": [ 55 | "@install-cs", 56 | "@php php-cs-fixer.phar fix --diff -v --allow-risky=yes --ansi" 57 | ], 58 | "check-cs": [ 59 | "@install-cs", 60 | "@php php-cs-fixer.phar fix --dry-run --diff -v --allow-risky=yes --ansi" 61 | ], 62 | "code-quality": [ 63 | "rm composer.lock", 64 | "@composer install --ansi", 65 | "@check-cs" 66 | ] 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /docs/auth-token.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/overblog/GraphiQLBundle/68520e0732380d9acd03977e049d9490ee78a6a3/docs/auth-token.png -------------------------------------------------------------------------------- /docs/custom-http-headers.md: -------------------------------------------------------------------------------- 1 | Custom HTTP headers 2 | ============== 3 | 4 | GraphiQL, provided by this bundle, sends the following default headers on each request: 5 | 6 | ```js 7 | headers = { 8 | "Accept": "application/json", 9 | "Content-Type": "application/json" 10 | }; 11 | ``` 12 | 13 | Headers sent by GraphiQL can be modified. 14 | For example, let's assume an `access-token` header is required in development. 15 | The header can be added the following way: 16 | 17 | 1. Override the default GraphiQL template: 18 | 19 | ```yaml 20 | # config/packages/graphiql.yaml or app/config/config.yml for Symfony without Flex 21 | overblog_graphiql: 22 | template: "GraphiQL/index.html.twig" 23 | ``` 24 | 2. Create a new template: 25 | 26 | ```twig 27 | {# templates/GraphiQL/index.html.twig #} 28 | {% extends '@OverblogGraphiQL/GraphiQL/index.html.twig' %} 29 | 30 | {% block graphql_fetcher_headers %} 31 | headers = { 32 | "Accept": "application/json", 33 | "Content-Type": "application/json", 34 | "access-token": "sometoken" 35 | }; 36 | {% endblock graphql_fetcher_headers %} 37 | ``` 38 | 39 | Or append headers instead of replacing the default one: 40 | 41 | ```twig 42 | {# templates/GraphiQL/index.html.twig #} 43 | {% extends '@OverblogGraphiQL/GraphiQL/index.html.twig' %} 44 | 45 | {% block graphql_fetcher_headers %} 46 | {{ parent() }} 47 | headers["access-token"] = "sometoken"; 48 | {% endblock graphql_fetcher_headers %} 49 | ``` 50 | -------------------------------------------------------------------------------- /docs/custom-parameters.md: -------------------------------------------------------------------------------- 1 | Custom GraphiQL parameters 2 | ========================== 3 | 4 | By default, only the `fetcher` parameter is passed to GraphiQL's React component. 5 | To add more: 6 | 7 | 1. Override the default GraphiQL template: 8 | 9 | ```yaml 10 | # config/packages/graphiql.yaml or app/config/config.yml for Symfony without Flex 11 | overblog_graphiql: 12 | template: "GraphiQL/index.html.twig" 13 | ``` 14 | 15 | 2. Create a new template: 16 | 17 | ```twig 18 | {# templates/GraphiQL/index.html.twig #} 19 | {% extends '@OverblogGraphiQL/GraphiQL/index.html.twig' %} 20 | 21 | {% block graphiql_params %} 22 | {{ parent() }}, 23 | defaultQuery: `query SomeQuery($param: String) { 24 | items(param: $param) { 25 | someField 26 | } 27 | }` 28 | {% endblock graphiql_params %} 29 | ``` 30 | -------------------------------------------------------------------------------- /docs/custom-rendering.md: -------------------------------------------------------------------------------- 1 | Custom rendering 2 | =============== 3 | 4 | It may be useful to change template to provide additional fields for more comfortable providing of [custom headers](custom-http-headers.md). 5 | 6 | It can be done the following way: 7 | 8 | 1. Override the default GraphiQL template: 9 | 10 | ```yaml 11 | # config/packages/graphiql.yaml or app/config/config.yml for Symfony without Flex 12 | overblog_graphiql: 13 | template: "GraphiQL/index.html.twig" 14 | ``` 15 | 2. Create a new template: 16 | 17 | You have to override block `graphiql_render` and soon of all you have to override block `graphql_fetcher_headers`. 18 | 19 | ```twig 20 | {# templates/GraphiQL/index.html.twig #} 21 | {% extends '@OverblogGraphiQL/GraphiQL/index.html.twig' %} 22 | 23 | {% block graphiql_render %} 24 | ReactDOM.render( 25 | {# add your custom implementation here #} 26 | , 27 | document.body 28 | ) 29 | {% endblock graphiql_render %} 30 | ``` 31 | 32 | ### Example 33 | 34 | See template [@OverblogGraphiQL/GraphiQL/auth.html.twig](../src/Resources/views/GraphiQL/auth.html.twig). How it looks like: 35 | 36 | ![This is an image](auth-token.png) 37 | 38 | There is: 39 | 40 | 1. Header used for the authorization. 41 | 2. Header value (token) for the authorization. 42 | 3. Button to load schema when the header value (token) typed. 43 | 44 | So, you can extend base template [@OverblogGraphiQL/GraphiQL/index.html.twig](../src/Resources/views/GraphiQL/index.html.twig) or use that. -------------------------------------------------------------------------------- /docs/graphql-endpoint.md: -------------------------------------------------------------------------------- 1 | Custom GraphQL endpoint 2 | ======= 3 | 4 | If your graphql endpoint is not the default one coming with the 5 | [overblog/graphql-bundle](https://github.com/overblog/GraphQLBundle), then you might want to tell graphiql how to 6 | resolve the route depending on a schema name. 7 | 8 | By default it uses `Overblog\GraphiQLBundle\Config\GraphQLEndpoint\Helpers\OverblogGraphQLBundleEndpointResolver`. 9 | 10 | ### Configuration 11 | 12 | Just set the `overblog_graphiql.endpoint_resolver` parameter like this: 13 | 14 | ```yaml 15 | # in app/config/config.yml 16 | overblog_graphiql: 17 | # ... 18 | endpoint_resolver: \App\GraphiQL\EndpointResolver 19 | ``` 20 | 21 | ### The Resolver 22 | 23 | The resolver must be something like this: 24 | 25 | ```php 26 | $name], 43 | ]; 44 | } 45 | } 46 | ``` 47 | 48 | It must return an array of packed params which will be passed to `RouterInterface::generate` like this 49 | `$router->generate(...$returnedValueOfTheGetByNameMethod)`. 50 | -------------------------------------------------------------------------------- /docs/libraries-versions.md: -------------------------------------------------------------------------------- 1 | JavaScript libraries' versions 2 | ============== 3 | 4 | You might want to use custom versions of the libraries included by GraphiQL. 5 | This can be achieved in the bundle configuration, example: 6 | 7 | ```yaml 8 | overblog_graphiql: 9 | javascript_libraries: 10 | graphiql: "0.11" 11 | react: "15.6" 12 | fetch: "2.0" 13 | ``` 14 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | tests 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/Config/GraphQLEndpoint/GraphQLEndpointInvalidSchemaException.php: -------------------------------------------------------------------------------- 1 | $name], 20 | ]; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Config/GraphQLEndpoint/RootResolver.php: -------------------------------------------------------------------------------- 1 | getBySchema('default'); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Config/GraphQLEndpoint/RouteResolver.php: -------------------------------------------------------------------------------- 1 | router = $router; 25 | $this->routeCollection = $routeCollection; 26 | } 27 | 28 | public function getBySchema($name) 29 | { 30 | $route = null; 31 | 32 | if (is_callable($this->routeCollection)) { 33 | $route = call_user_func($this->routeCollection, $name); 34 | } 35 | 36 | if (null === $route && array_key_exists($name, $this->routeCollection)) { 37 | $route = $this->routeCollection[$name]; 38 | } 39 | 40 | if (!is_array($route)) { 41 | throw GraphQLEndpointInvalidSchemaException::forSchemaAndResolver($name, self::class); 42 | } 43 | 44 | return $this->router->generate(...$route); 45 | } 46 | 47 | public function getDefault() 48 | { 49 | return $this->getBySchema('default'); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Config/GraphiQLControllerEndpoint.php: -------------------------------------------------------------------------------- 1 | javaScriptLibraries = $javaScriptLibraries; 18 | $this->template = $template; 19 | } 20 | 21 | /** 22 | * @return GraphiQLViewJavaScriptLibraries 23 | */ 24 | public function getJavaScriptLibraries() 25 | { 26 | return $this->javaScriptLibraries; 27 | } 28 | 29 | /** 30 | * @return string 31 | */ 32 | public function getTemplate() 33 | { 34 | return $this->template; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Config/GraphiQLViewJavaScriptLibraries.php: -------------------------------------------------------------------------------- 1 | graphiQLVersion = $graphiQLVersion; 24 | $this->reactVersion = $reactVersion; 25 | $this->fetchVersion = $fetchVersion; 26 | } 27 | 28 | /** 29 | * @return string 30 | */ 31 | public function getGraphiQLVersion() 32 | { 33 | return $this->graphiQLVersion; 34 | } 35 | 36 | /** 37 | * @return string 38 | */ 39 | public function getReactVersion() 40 | { 41 | return $this->reactVersion; 42 | } 43 | 44 | /** 45 | * @return string 46 | */ 47 | public function getFetchVersion() 48 | { 49 | return $this->fetchVersion; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Controller/GraphiQLController.php: -------------------------------------------------------------------------------- 1 | twig = $twig; 35 | $this->viewConfig = $viewConfig; 36 | $this->graphQLEndpoint = $graphQLEndpoint; 37 | } 38 | 39 | public function indexAction($schemaName = null) 40 | { 41 | $endpoint = null === $schemaName ? $this->graphQLEndpoint->getDefault() : $this->graphQLEndpoint->getBySchema($schemaName); 42 | 43 | return new Response($this->twig->render( 44 | $this->viewConfig->getTemplate(), 45 | [ 46 | 'endpoint' => $endpoint, 47 | 'versions' => [ 48 | 'graphiql' => $this->viewConfig->getJavaScriptLibraries()->getGraphiQLVersion(), 49 | 'react' => $this->viewConfig->getJavaScriptLibraries()->getReactVersion(), 50 | 'fetch' => $this->viewConfig->getJavaScriptLibraries()->getFetchVersion(), 51 | ], 52 | ] 53 | )); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/DependencyInjection/Compiler/Endpoints/DefaultEndpointWiringPass.php: -------------------------------------------------------------------------------- 1 | getDefinition('overblog_graphiql.controller.graphql.endpoint'); 18 | 19 | if (GraphiQLControllerEndpoint::class !== $endpointDefinition->getClass()) { 20 | return; 21 | } 22 | 23 | $endpointDefinition->setClass(RootResolver::class); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/DependencyInjection/Compiler/Endpoints/OverblogGraphQLBundleEndpointWiringPass.php: -------------------------------------------------------------------------------- 1 | getParameter('kernel.bundles_metadata'); 17 | 18 | if (!array_key_exists('OverblogGraphQLBundle', $bundles)) { 19 | return; 20 | } 21 | 22 | $endpointDefinition = $container->getDefinition('overblog_graphiql.controller.graphql.endpoint'); 23 | $endpointDefinition->setClass(RouteResolver::class); 24 | 25 | $endpointDefinition->setArguments([ 26 | new Reference('router'), 27 | [$container->getParameter('overblog_graphiql.endpoint_resolver'), 'getByName'], 28 | ]); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/DependencyInjection/Configuration.php: -------------------------------------------------------------------------------- 1 | getRootNode(); 20 | 21 | $rootNode 22 | ->addDefaultsIfNotSet() 23 | ->children() 24 | ->scalarNode('endpoint_resolver') 25 | ->defaultValue(OverblogGraphQLBundleEndpointResolver::class) 26 | ->end() 27 | ->scalarNode('template') 28 | ->info('In case you need it\'s possible to replace GraphiQL twig template') 29 | ->defaultValue('@OverblogGraphiQL/GraphiQL/index.html.twig') 30 | ->end() 31 | ->arrayNode('javascript_libraries') 32 | ->addDefaultsIfNotSet() 33 | ->children() 34 | ->scalarNode('graphiql')->defaultValue('0.11')->end() 35 | ->scalarNode('react')->defaultValue('15.6')->end() 36 | ->scalarNode('fetch')->defaultValue('2.0')->end() 37 | ->end() 38 | ->end() 39 | ->end() 40 | ; 41 | 42 | return $treeBuilder; 43 | } 44 | 45 | /** 46 | * @param string|null $name 47 | * @param string $type 48 | * 49 | * @return ArrayNodeDefinition|\Symfony\Component\Config\Definition\Builder\NodeDefinition 50 | */ 51 | private static function getRootNodeWithoutDeprecation(TreeBuilder $builder, $name, $type = 'array') 52 | { 53 | // BC layer for symfony/config 4.1 and older 54 | return method_exists($builder, 'getRootNode') ? $builder->getRootNode() : $builder->root($name, $type); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/DependencyInjection/OverblogGraphiQLExtension.php: -------------------------------------------------------------------------------- 1 | processConfiguration($configuration, $configs); 22 | 23 | $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); 24 | $loader->load('services.xml'); 25 | 26 | $container->setParameter('overblog_graphiql.endpoint_resolver', $config['endpoint_resolver']); 27 | 28 | $graphiQLViewConfigJSLibraries = $container->getDefinition('overblog_graphiql.view.config.javascript_libraries'); 29 | $graphiQLViewConfigJSLibraries->setArguments([ 30 | $config['javascript_libraries']['graphiql'], 31 | $config['javascript_libraries']['react'], 32 | $config['javascript_libraries']['fetch'], 33 | ]); 34 | 35 | $graphiQLViewConfig = $container->getDefinition('overblog_graphiql.view.config'); 36 | $graphiQLViewConfig->setArguments([ 37 | new Reference('overblog_graphiql.view.config.javascript_libraries'), 38 | $config['template'], 39 | ]); 40 | } 41 | 42 | public function getAlias(): string 43 | { 44 | return 'overblog_graphiql'; 45 | } 46 | 47 | /** 48 | * {@inheritdoc} 49 | */ 50 | public function getXsdValidationBasePath(): string 51 | { 52 | return __DIR__.'/../Resources/config/schema'; 53 | } 54 | 55 | /** 56 | * {@inheritdoc} 57 | */ 58 | public function getNamespace(): string 59 | { 60 | return 'http://over-blog.com/schema/dic/overblog_graphiql'; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/OverblogGraphiQLBundle.php: -------------------------------------------------------------------------------- 1 | addCompilerPass(new OverblogGraphQLBundleEndpointWiringPass()); 21 | 22 | // DefaultEndpointWiringPass should always be the last one to 23 | // provide the route in case no other wiring has succeeded 24 | $container->addCompilerPass(new DefaultEndpointWiringPass()); 25 | } 26 | 27 | public function getContainerExtension(): ?ExtensionInterface 28 | { 29 | return new OverblogGraphiQLExtension(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Resources/config/routing.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | Overblog\GraphiQLBundle\Controller\GraphiQLController::indexAction 9 | 10 | 11 | 12 | Overblog\GraphiQLBundle\Controller\GraphiQLController::indexAction 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/Resources/config/schema/overblog_graphiql-0.1.xsd: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/Resources/config/services.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 11 | 12 | 15 | 16 | 19 | 20 | 23 | 24 | 25 | 26 | 27 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /src/Resources/views/GraphiQL/auth.html.twig: -------------------------------------------------------------------------------- 1 | {% extends '@OverblogGraphiQL/GraphiQL/index.html.twig' %} 2 | 3 | {% block style %} 4 | {{ parent() }} 5 | 10 | {% endblock %} 11 | 12 | {% block graphql_fetcher_headers %} 13 | {{ parent() }} 14 | 15 | {% block graphql_fetcher_headers_extra %} 16 | let headerName = document.getElementById('x_header_name').value; 17 | let token = getToken() || document.token || null; 18 | if (headerName && token) { 19 | headers[headerName] = token; 20 | } 21 | {% endblock graphql_fetcher_headers_extra %} 22 | {% endblock graphql_fetcher_headers %} 23 | 24 | {% block graphiql_render %} 25 | let getToken = function () { 26 | let field = document.getElementById('x_header_value'); 27 | return field ? field.value : null; 28 | }; 29 | let renderPage = function () { 30 | ReactDOM.render( 31 | React.createElement('div', {style: {height: '100%'}}, 32 | React.createElement('div', {style: {background: '#f6f6f6', padding: '5px 15px'}}, 33 | React.createElement('select', { 34 | id: 'x_header_name', 35 | className: 'x-header', 36 | style: {height: '24px'}, 37 | title: 'Header used for the authorization', 38 | }, 39 | React.createElement('option', {value: 'Authorization'}, 'Authorization'), 40 | React.createElement('option', {value: 'X-Auth-Token'}, 'X-Auth-Token'), 41 | ), 42 | React.createElement('input', { 43 | id: 'x_header_value', 44 | type: 'text', 45 | className: 'x-header', 46 | placeholder: 'Set token (usually add prefix "Bearer" for "Authorization" header)', 47 | style: {height: '16px', width: '400px'}, 48 | title: 'Header value (token) for the authorization', 49 | value: getToken() || document.token || null, 50 | }), 51 | React.createElement('button', { 52 | className: 'x-header', 53 | onClick: () => { 54 | document.token = getToken(); 55 | document.body.innerHTML = ''; 56 | document.body.outerHTML = 'Loading...'; 57 | renderPage(); 58 | }, 59 | style: {height: '24px'}, 60 | title: 'Click the button to load schema when the token specified', 61 | }, 62 | 'Reload schema', 63 | ), 64 | ), 65 | React.createElement(GraphiQL, { 66 | fetcher: graphQLFetcher 67 | }), 68 | ), 69 | document.body, 70 | ); 71 | }; 72 | renderPage(); 73 | {% endblock graphiql_render %} -------------------------------------------------------------------------------- /src/Resources/views/GraphiQL/index.html.twig: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {% block head %} 5 | {% block style %} 6 | 14 | 15 | {% endblock style %} 16 | {% if versions.fetch matches '/^[~^]?2/' %} 17 | 18 | {% else %} 19 | 20 | {% endif %} 21 | {% if versions.react matches '/^[~^]?15/' %} 22 | 23 | 24 | {% else %} 25 | 26 | 27 | {% endif %} 28 | 29 | {% block title %}GraphiQL{% endblock title %} 30 | {% endblock head %} 31 | 32 | 33 | {% block body %} 34 | {% block body_loading %}Loading...{% endblock body_loading %} 35 | {% block body_script %} 36 | 79 | {% endblock body_script %} 80 | {% endblock body %} 81 | 82 | 83 | -------------------------------------------------------------------------------- /tests/Config/GraphQLEndpoint/Helpers/OverblogGraphQLBundleEndpointResolverTest.php: -------------------------------------------------------------------------------- 1 | assertSame(['overblog_graphql_endpoint'], $defaultRoute); 16 | } 17 | 18 | /** 19 | * @dataProvider getSchemas 20 | */ 21 | public function testGetWithSchemaName($schemaName, $expectedResult): void 22 | { 23 | $route = OverblogGraphQLBundleEndpointResolver::getByName($schemaName); 24 | $this->assertSame($expectedResult, $route); 25 | } 26 | 27 | public function getSchemas() 28 | { 29 | return [ 30 | ['bla', [ 31 | 'overblog_graphql_multiple_endpoint', 32 | ['schemaName' => 'bla'], 33 | ]], 34 | ['star-wars', [ 35 | 'overblog_graphql_multiple_endpoint', 36 | ['schemaName' => 'star-wars'], 37 | ]], 38 | ]; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /tests/Config/GraphQLEndpoint/RootResolverTest.php: -------------------------------------------------------------------------------- 1 | subject(); 24 | 25 | $this->expectException(GraphQLEndpointInvalidSchemaException::class); 26 | $this->expectExceptionMessage('Schema "any" isn\'t valid for resolver "Overblog\GraphiQLBundle\Config\GraphQLEndpoint\RootResolver"'); 27 | 28 | $rootResolver->getBySchema('any'); 29 | } 30 | 31 | public function testDefaultSchema(): void 32 | { 33 | $rootResolver = $this->subject(); 34 | 35 | $this->assertEquals('/', $rootResolver->getDefault()); 36 | $this->assertEquals('/', $rootResolver->getBySchema('default')); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /tests/Config/GraphQLEndpoint/RouteResolverTest.php: -------------------------------------------------------------------------------- 1 | router = $this->createMock(RouterInterface::class); 21 | } 22 | 23 | /** 24 | * @return RouteResolver 25 | */ 26 | private function subject(array $routeCollection) 27 | { 28 | return new RouteResolver( 29 | $this->router, 30 | $routeCollection 31 | ); 32 | } 33 | 34 | public function testInvalidRoute(): void 35 | { 36 | $routeResolver = $this->subject([]); 37 | 38 | $this->expectException(GraphQLEndpointInvalidSchemaException::class); 39 | $this->expectExceptionMessage('Schema "default" isn\'t valid for resolver "Overblog\GraphiQLBundle\Config\GraphQLEndpoint\RouteResolver"'); 40 | 41 | $routeResolver->getDefault(); 42 | } 43 | 44 | public function testArrayRoutes(): void 45 | { 46 | $this->router->expects($this->exactly(3)) 47 | ->method('generate') 48 | ->withConsecutive( 49 | ['route_schema_default'], 50 | ['route_schema_default'], 51 | ['route_schema_star_wars'] 52 | ) 53 | ->willReturnOnConsecutiveCalls('/', '/', '/star-wars'); 54 | 55 | $routeCollection = [ 56 | 'default' => ['route_schema_default'], 57 | 'starWars' => ['route_schema_star_wars'], 58 | ]; 59 | 60 | $routeResolver = $this->subject($routeCollection); 61 | 62 | $this->assertEquals('/', $routeResolver->getDefault()); 63 | $this->assertEquals('/', $routeResolver->getBySchema('default')); 64 | $this->assertEquals('/star-wars', $routeResolver->getBySchema('starWars')); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /tests/Controller/GraphiQLControllerTest.php: -------------------------------------------------------------------------------- 1 | request('GET', '/graphiql/second'); 17 | $response = $client->getResponse(); 18 | 19 | $this->assertInstanceOf(Response::class, $response); 20 | $this->assertSame(500, $response->getStatusCode()); 21 | } 22 | 23 | public function testDefaultSchema(): void 24 | { 25 | $client = static::createClient(); 26 | 27 | $client->request('GET', '/graphiql'); 28 | $response = $client->getResponse(); 29 | 30 | $this->assertInstanceOf(Response::class, $response); 31 | $this->assertSame(200, $response->getStatusCode(), $response->getContent()); 32 | $this->assertStringContainsString('Loading...', $response->getContent()); 33 | } 34 | 35 | public function testDefaultSchemaViaMultipleRoute(): void 36 | { 37 | $client = static::createClient(); 38 | 39 | $client->request('GET', '/graphiql/default'); 40 | $response = $client->getResponse(); 41 | 42 | $this->assertInstanceOf(Response::class, $response); 43 | $this->assertSame(200, $response->getStatusCode()); 44 | $this->assertStringContainsString('Loading...', $response->getContent()); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /tests/Fixtures/TestKernel.php: -------------------------------------------------------------------------------- 1 | load(function (ContainerBuilder $container): void { 31 | $container->loadFromExtension('framework', [ 32 | 'secret' => 'test', 33 | 'test' => true, 34 | 'assets' => ['enabled' => false], 35 | 'router' => ['resource' => '%kernel.project_dir%/src/Resources/config/routing.xml'], 36 | ]); 37 | }); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /tests/ForwardCompatTestCaseTrait.php: -------------------------------------------------------------------------------- 1 | hasReturnType()) { 15 | eval(' 16 | namespace Overblog\GraphiQLBundle\Tests; 17 | 18 | /** 19 | * @internal 20 | */ 21 | trait ForwardCompatTestCaseTrait 22 | { 23 | protected function tearDown(): void 24 | { 25 | static::ensureKernelShutdown(); 26 | static::$kernel = null; 27 | } 28 | } 29 | '); 30 | } else { 31 | /** 32 | * @internal 33 | */ 34 | trait ForwardCompatTestCaseTrait 35 | { 36 | protected function tearDown(): void 37 | { 38 | static::ensureKernelShutdown(); 39 | static::$kernel = null; 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /tests/Functional/DependencyInjection/Fixtures/Xml/TestKernel.php: -------------------------------------------------------------------------------- 1 | load(__DIR__.'/config.xml'); 31 | $loader->load(function (ContainerBuilder $container): void { 32 | $container->loadFromExtension('framework', [ 33 | 'assets' => ['enabled' => false], 34 | ]); 35 | }); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /tests/Functional/DependencyInjection/Fixtures/Xml/config.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /tests/Functional/DependencyInjection/Fixtures/Yaml/TestKernel.php: -------------------------------------------------------------------------------- 1 | load(__DIR__.'/config.yml'); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /tests/Functional/DependencyInjection/Fixtures/Yaml/config.yml: -------------------------------------------------------------------------------- 1 | framework: 2 | test: ~ 3 | secret: test 4 | router: 5 | resource: "%kernel.project_dir%/Resources/config/routing.xml" 6 | profiler: 7 | enabled: false 8 | assets: 9 | enabled: false 10 | 11 | overblog_graphiql: ~ 12 | -------------------------------------------------------------------------------- /tests/Functional/DependencyInjection/Xml/ConfigurationTest.php: -------------------------------------------------------------------------------- 1 | str_replace('\\', '_', __NAMESPACE__)]); 20 | } 21 | 22 | public function testSuccessConfiguration(): void 23 | { 24 | /** @var TestKernel $kernel */ 25 | $kernel = static::$kernel; 26 | 27 | $this->assertTrue($kernel->isBooted()); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /tests/Functional/DependencyInjection/Yaml/ConfigurationTest.php: -------------------------------------------------------------------------------- 1 | str_replace('\\', '_', __NAMESPACE__)]); 20 | } 21 | 22 | public function testSuccessConfiguration(): void 23 | { 24 | /** @var TestKernel $kernel */ 25 | $kernel = static::$kernel; 26 | 27 | $this->assertTrue($kernel->isBooted()); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /tests/Integration/OverblogGraphQLBundle/Controller/GraphiQLControllerTest.php: -------------------------------------------------------------------------------- 1 | request('GET', '/graphiql'); 17 | $response = $client->getResponse(); 18 | 19 | $this->assertInstanceOf(Response::class, $response); 20 | $this->assertSame(200, $response->getStatusCode()); 21 | $this->assertStringContainsString('Loading...', $response->getContent()); 22 | $this->assertStringContainsString('var endpoint = "\/"', $response->getContent()); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /tests/Integration/OverblogGraphQLBundle/Fixtures/Resources/config/routing.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /tests/Integration/OverblogGraphQLBundle/Fixtures/TestKernel.php: -------------------------------------------------------------------------------- 1 | load(function (ContainerBuilder $container): void { 33 | $container->loadFromExtension('framework', [ 34 | 'secret' => 'test', 35 | 'test' => true, 36 | 'assets' => ['enabled' => false], 37 | 'router' => [ 38 | 'resource' => __DIR__.'/Resources/config/routing.xml', 39 | ], 40 | ]); 41 | }); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /tests/Integration/OverblogGraphQLBundle/TestCase.php: -------------------------------------------------------------------------------- 1 | testCase = null !== $testCase ? $testCase : false; 16 | parent::__construct($environment, $debug); 17 | } 18 | 19 | public function getCacheDir(): string 20 | { 21 | return sys_get_temp_dir().'/OverblogGraphQLBundle/'.Kernel::VERSION.'/'.$this->testCase.'/cache/'.$this->environment; 22 | } 23 | 24 | public function getLogDir(): string 25 | { 26 | return sys_get_temp_dir().'/OverblogGraphQLBundle/'.Kernel::VERSION.'/'.$this->testCase.'/logs'; 27 | } 28 | 29 | public function isBooted() 30 | { 31 | return $this->booted; 32 | } 33 | } 34 | --------------------------------------------------------------------------------