rebing / graphql-laravel

Laravel wrapper for Facebook's GraphQL
MIT License
2.13k stars 266 forks source link

compatibility with lumen 5.8 #236

Closed MarceloPetrucio closed 5 years ago

MarceloPetrucio commented 5 years ago

Hello everyone,

I'm having internal problems in this package, I don't know exactly where the problem is because it is not returning the error of where it is going wrong.

I did all the installation according to the initial readme and had no problem installing, but when I try to do a query, or a post on the route "localhost:4000/graphql" or try to run the GraphQL - Playground it doesn't recognize the server

Has anyone gone through this or know where I can look to find out the problem?

rebing commented 5 years ago

Please specify the error

owenvoke commented 5 years ago

Is there anything in the storage/logs? I've managed to get it working on Lumen 5.8, except Introspection queries result in a 500, so not sure if this is what you're referring to?

renepardon commented 5 years ago

HI @rebing I tried this package today for the first time with lumen.

My installation:

I copied the example files and adjusted namespaces. Also added them to corresponding place within the graphql.php config file. But even with commented configuration schema it doesn't work for unknown reason. Maybe you can have a look and see what's going wrong here. :)

The error:

[2019-04-23 12:51:34] local.ERROR: Rebing\GraphQL\Exception\SchemaNotFound: Type 
    query IntrospectionQuery {
      __schema {
        queryType { name }
        mutationType { name }
        subscriptionType { name }
        types {
          ...FullType
        }
        directives {
          name
          description
          locations
          args {
            ...InputValue
          }
        }
      }
    }
    fragment FullType on __Type {
      kind
      name
      description
      fields(includeDeprecated: true) {
        name
        description
        args {
          ...InputValue
        }
        type {
          ...TypeRef
        }
        isDeprecated
        deprecationReason
      }
      inputFields {
        ...InputValue
      }
      interfaces {
        ...TypeRef
      }
      enumValues(includeDeprecated: true) {
        name
        description
        isDeprecated
        deprecationReason
      }
      possibleTypes {
        ...TypeRef
      }
    }
    fragment InputValue on __InputValue {
      name
      description
      type { ...TypeRef }
      defaultValue
    }
    fragment TypeRef on __Type {
      kind
      name
      ofType {
        kind
        name
        ofType {
          kind
          name
          ofType {
            kind
            name
            ofType {
              kind
              name
              ofType {
                kind
                name
                ofType {
                  kind
                  name
                  ofType {
                    kind
                    name
                  }
                }
              }
            }
          }
        }
      }
    }
  /IntrospectionQuery not found. in /var/www/html/app/vendor/rebing/graphql-laravel/src/Rebing/GraphQL/GraphQL.php:359
Stack trace:
#0 /var/www/html/app/vendor/rebing/graphql-laravel/src/Rebing/GraphQL/GraphQL.php(40): Rebing\GraphQL\GraphQL->getSchemaConfiguration('\n    query Intr...')
#1 /var/www/html/app/vendor/rebing/graphql-laravel/src/Rebing/GraphQL/GraphQL.php(100): Rebing\GraphQL\GraphQL->schema('\n    query Intr...')
#2 /var/www/html/app/vendor/rebing/graphql-laravel/src/Rebing/GraphQL/GraphQL.php(91): Rebing\GraphQL\GraphQL->queryAndReturnResult('\n    query Intr...', NULL, Array)
#3 /var/www/html/app/vendor/rebing/graphql-laravel/src/Rebing/GraphQL/GraphQLController.php(52): Rebing\GraphQL\GraphQL->query('\n    query Intr...', NULL, Array)
#4 [internal function]: Rebing\GraphQL\GraphQLController->query(Object(Laravel\Lumen\Http\Request), '\n    query Intr...')
#5 /var/www/html/app/vendor/illuminate/container/BoundMethod.php(32): call_user_func_array(Array, Array)
#6 /var/www/html/app/vendor/illuminate/container/BoundMethod.php(90): Illuminate\Container\BoundMethod::Illuminate\Container\{closure}()
#7 /var/www/html/app/vendor/illuminate/container/BoundMethod.php(34): Illuminate\Container\BoundMethod::callBoundMethod(Object(Laravel\Lumen\Application), Array, Object(Closure))
#8 /var/www/html/app/vendor/illuminate/container/Container.php(580): Illuminate\Container\BoundMethod::call(Object(Laravel\Lumen\Application), Array, Array, NULL)
#9 /var/www/html/app/vendor/laravel/lumen-framework/src/Concerns/RoutesRequests.php(376): Illuminate\Container\Container->call(Array, Array)
#10 /var/www/html/app/vendor/laravel/lumen-framework/src/Concerns/RoutesRequests.php(342): Laravel\Lumen\Application->callControllerCallable(Array, Array)
#11 /var/www/html/app/vendor/laravel/lumen-framework/src/Concerns/RoutesRequests.php(316): Laravel\Lumen\Application->callLumenController(Object(Rebing\GraphQL\GraphQLController), 'query', Array)
#12 /var/www/html/app/vendor/laravel/lumen-framework/src/Concerns/RoutesRequests.php(278): Laravel\Lumen\Application->callControllerAction(Array)
#13 /var/www/html/app/vendor/laravel/lumen-framework/src/Concerns/RoutesRequests.php(258): Laravel\Lumen\Application->callActionOnArrayBasedRoute(Array)
#14 /var/www/html/app/vendor/laravel/lumen-framework/src/Concerns/RoutesRequests.php(416): Laravel\Lumen\Application->Laravel\Lumen\Concerns\{closure}(Object(Laravel\Lumen\Http\Request))
#15 /var/www/html/app/vendor/laravel/lumen-framework/src/Concerns/RoutesRequests.php(259): Laravel\Lumen\Application->sendThroughPipeline(Array, Object(Closure))
#16 /var/www/html/app/vendor/laravel/lumen-framework/src/Concerns/RoutesRequests.php(165): Laravel\Lumen\Application->handleFoundRoute(Array)
#17 /var/www/html/app/vendor/laravel/lumen-framework/src/Concerns/RoutesRequests.php(416): Laravel\Lumen\Application->Laravel\Lumen\Concerns\{closure}(Object(Laravel\Lumen\Http\Request))
#18 /var/www/html/app/vendor/laravel/lumen-framework/src/Concerns/RoutesRequests.php(171): Laravel\Lumen\Application->sendThroughPipeline(Array, Object(Closure))
#19 /var/www/html/app/vendor/laravel/lumen-framework/src/Concerns/RoutesRequests.php(108): Laravel\Lumen\Application->dispatch(NULL)
#20 /var/www/html/app/public/index.php(28): Laravel\Lumen\Application->run()
#21 {main}

composer.json:

{
    "name": "laravel/lumen",
    "description": "The Laravel Lumen Framework.",
    "keywords": ["framework", "laravel", "lumen"],
    "license": "MIT",
    "type": "project",
    "require": {
        "php": ">=7.1.3",
        "illuminate/redis": "^5.8",
        "laravel/lumen-framework": "5.8.*",
        "predis/predis": "^1.1",
        "rebing/graphql-laravel": "^1.20",
        "thedevsaddam/lumen-route-list": "^2.0",
        "vlucas/phpdotenv": "^3.3"
    },
    "require-dev": {
        "fzaninotto/faker": "^1.4",
        "phpunit/phpunit": "^7.0",
        "mockery/mockery": "^1.0"
    },
    "autoload": {
        "classmap": [
            "database/seeds",
            "database/factories"
        ],
        "psr-4": {
            "App\\": "app/"
        }
    },
    "autoload-dev": {
        "classmap": [
            "tests/"
        ]
    },
    "scripts": {
        "post-root-package-install": [
            "@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
        ]
    },
    "config": {
        "preferred-install": "dist",
        "sort-packages": true,
        "optimize-autoloader": true
    },
    "minimum-stability": "dev",
    "prefer-stable": true
}

config/graphql.php

<?php

use App\GraphQL\Mutation\LoginMutation;
use App\GraphQL\Query\UserQuery;
use App\GraphQL\Query\UsersQuery;
use App\GraphQL\Type\LocationType;
use App\GraphQL\Type\UserProfileType;
use App\GraphQL\Type\UserType;

return [

    // The prefix for routes
    'prefix'                 => 'graphql',

    // The routes to make GraphQL request. Either a string that will apply
    // to both query and mutation or an array containing the key 'query' and/or
    // 'mutation' with the according Route
    //
    // Example:
    //
    // Same route for both query and mutation
    //
    // 'routes' => 'path/to/query/{graphql_schema?}',
    //
    // or define each route
    //
    // 'routes' => [
    //     'query' => 'query/{graphql_schema?}',
    //     'mutation' => 'mutation/{graphql_schema?}',
    // ]
    //
    'routes'                 => '{graphql_schema?}',

    // The controller to use in GraphQL request. Either a string that will apply
    // to both query and mutation or an array containing the key 'query' and/or
    // 'mutation' with the according Controller and method
    //
    // Example:
    //
    // 'controllers' => [
    //     'query' => '\Rebing\GraphQL\GraphQLController@query',
    //     'mutation' => '\Rebing\GraphQL\GraphQLController@mutation'
    // ]
    //
    'controllers'            => \Rebing\GraphQL\GraphQLController::class . '@query',

    // Any middleware for the graphql route group
    'middleware'             => [],

    // Additional route group attributes
    //
    // Example:
    //
    // 'route_group_attributes' => ['guard' => 'api']
    //
    'route_group_attributes' => [],

    // The name of the default schema used when no argument is provided
    // to GraphQL::schema() or when the route is used without the graphql_schema
    // parameter.
    'default_schema'         => 'default',

    // The schemas for query and/or mutation. It expects an array of schemas to provide
    // both the 'query' fields and the 'mutation' fields.
    //
    // You can also provide a middleware that will only apply to the given schema
    //
    // Example:
    //
    //
    //  'schemas' => [
    //      'default' => [
    //          'query' => [
    //              'users' => 'App\GraphQL\Query\UsersQuery'
    //          ],
    //          'mutation' => [
    //
    //          ]
    //      ],
    //      'user' => [
    //          'query' => [
    //              'profile' => 'App\GraphQL\Query\ProfileQuery'
    //          ],
    //          'mutation' => [
    //
    //          ],
    //          'middleware' => ['auth'],
    //      ],
    //      'user/me' => [
    //          'query' => [
    //              'profile' => 'App\GraphQL\Query\MyProfileQuery'
    //          ],
    //          'mutation' => [
    //
    //          ],
    //          'middleware' => ['auth'],
    //      ],
    //  ]
    //
    'schemas'                => [
        'default' => [
            'query'      => [
                // 'user'  => UserQuery::class,
                // 'users' => UsersQuery::class,
            ],
            'mutation'   => [
                // 'login' => LoginMutation::class,
            ],
            'middleware' => [],
            'method'     => ['get', 'post'],
        ],
    ],

    // The types available in the application. You can then access it from the
    // facade like this: GraphQL::type('user')
    //
    // Example:
    //
    // 'types' => [
    //     'user' => 'App\GraphQL\Type\UserType'
    // ]
    //
    'types'                  => [
        // 'user'         => UserType::class,
        // 'user_profile' => UserProfileType::class,
        // 'location'     => LocationType::class,
    ],

    // This callable will be passed the Error object for each errors GraphQL catch.
    // The method should return an array representing the error.
    // Typically:
    // [
    //     'message' => '',
    //     'locations' => []
    // ]
    'error_formatter'        => ['\Rebing\GraphQL\GraphQL', 'formatError'],

    /**
     * Custom Error Handling
     *
     * Expected handler signature is: function (array $errors, callable $formatter): array
     *
     * The default handler will pass exceptions to laravel Error Handling mechanism
     */
    'errors_handler'         => ['\Rebing\GraphQL\GraphQL', 'handleErrors'],

    // You can set the key, which will be used to retrieve the dynamic variables
    'params_key'             => 'variables',

    /*
     * Options to limit the query complexity and depth. See the doc
     * @ https://github.com/webonyx/graphql-php#security
     * for details. Disabled by default.
     */
    'security'               => [
        'query_max_complexity'  => null,
        'query_max_depth'       => null,
        'disable_introspection' => false,
    ],

    /*
     * You can define your own pagination type.
     * Reference \Rebing\GraphQL\Support\PaginationType::class
     */
    'pagination_type'        => \Rebing\GraphQL\Support\PaginationType::class,

    /*
     * Config for GraphiQL (see (https://github.com/graphql/graphiql).
     */
    'graphiql'               => [
        'prefix'     => '/graphiql/{graphql_schema?}',
        'controller' => \Rebing\GraphQL\GraphQLController::class . '@graphiql',
        'middleware' => [],
        'view'       => 'graphql::graphiql',
        'display'    => env('ENABLE_GRAPHIQL', true),
    ],
];
owenvoke commented 5 years ago

@renepardon, Having tested the package on Lumen 5.6 and 5.7 I don't think this is specifically an issue with Lumen 5.8 as I experienced the same Introspection problem on all of them.

I think it's down to the named query (i.e. query IntrospectionQuery), because without that, it works as expected. However, this is automatically generated by Insomnia for me.


Testing a bit more, it may be the following in the controller: https://github.com/rebing/graphql-laravel/blob/28ac9e783be5764039bf25e8b58622749f71c745/src/Rebing/GraphQL/GraphQLController.php#L17

When Insomnia sends the request, the contents of $request->request->all() is the entire query as well as the operationName. These then are imploded with a / as the glue. Which results in:

query IntrospectionQuery {
...
}
/IntrospectionQuery
renepardon commented 5 years ago

Yes, I see @pxgamer This doesn't look correct. Right now I will try to fix this by myself to just extend the existing controller and overwrite this part. If I find a stable and working solution, I will post it. I will try with laravel to see how it should look like/how it may work properly.

renepardon commented 5 years ago

@pxgamer @rebing @MarceloPetrucio

I came to this solution:

    if (is_lumen() && $request->request->count() > 1) {
        $schema = implode('/', $request->route()[2]);
    } elseif ($request->route()->parameters && count($request->route()->parameters) > 1) {
        $schema = implode('/', $request->route()->parameters);
    }

Can you please evaluate and verify this? Lumen has no public "parameters" property on the Route object.

dzanfardino commented 5 years ago

I'm also experiencing this issue with Lumen 5.8, can the fix be merged ?

mfn commented 5 years ago

There's no PR yet so I would appreciate someone providing one.

Not sure how it's with tests, but we should have a test here too (but I'm wiling to invest time if we get a PR for a start).

mfn commented 5 years ago

Just noticed: this looks very similar to https://github.com/rebing/graphql-laravel/issues/204

Even the code lines mentioned there. They're not exactly the same but look very similar.

mfn commented 5 years ago

I've just merged a PR which I believe fixes this problem.

Basically https://github.com/rebing/graphql-laravel/issues/236#issuecomment-486116951 already had the solution that in Lumen we need to acces $routes[2]; I just improved the change and also added an integration test for this.

There's no release yet so if you're eager, you need to test with dev-master. Also be aware that the master branch has other changes we may require further adaptions to make it work (types mostly).

If it still doesn't work, please create a new issue with details to reproduce it; thank you!

kieuminhcanh commented 5 years ago

Still error with Lumen 5.8. It's alway return Internal server error when I do exactly guide from homepage

mfn commented 5 years ago

Internal server error

Please open a new issue with the latest version and more details. "Internal server error" means there has to be an exception stracktrace somewhere, thanks!