Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Can we get Lumen Support for this package? #28

Open
rajeev-k-tomy opened this issue Nov 29, 2020 · 1 comment
Open

Can we get Lumen Support for this package? #28

rajeev-k-tomy opened this issue Nov 29, 2020 · 1 comment

Comments

@rajeev-k-tomy
Copy link

Hi,

Thank you for the awesome work!

I was trying this package with Lumen and found that the package won't work with the framework.

I found the following issues mainly:

  1. config_path is not available in Lumen. Hence the the \TheCodingMachine\GraphQLite\Laravel\Providers\GraphQLiteServiceProvider::boot() method will fail.
  2. There is no web middleware available in the Lumen. Since the routes definition in the package depends on the web middleware, it will gracefully fail at the moment.
  3. Dependency on Illuminate\Contracts\Foundation\Application will again become a roadblock in the \TheCodingMachine\GraphQLite\Laravel\Providers\GraphQLiteServiceProvider::register() method. This is because Lumen uses different Application instance (Laravel\Lumen\Application).

I would appreciate any kind of help on this. Meanwhile, I will provide the nasty workaround for all these problems I faced below.

PS: I am using Lumen version ^8.0.

@rajeev-k-tomy
Copy link
Author

rajeev-k-tomy commented Nov 29, 2020

Hi a work around is given below:

  1. Updatebootstrap/app.php with below content.
$app->configure('graphqlite');

Then create config/graphqlite.php and add below content as per the doc suggests

use GraphQL\Error\Debug;

return [
    'controllers' => 'App\\Http\\Controller',
    'types' => 'App\\',
    'debug' => Debug::RETHROW_UNSAFE_EXCEPTIONS,
    'uri' => env('GRAPHQLITE_URI', '/graphql'),
    'middleware' => ['web'],
    'guard' => ['web'],
];
  1. Update bootstrap/app.php with web middleware definition. Here you should ideally add csrf token verification if I understand the doc correctly. For the time being, I am just providing it with the ExampleMiddleware that comes along with Lumen framework.
$app->routeMiddleware([
   'web' => App\Http\Middleware\ExampleMiddleware::class,
]);
  1. Finally you need to create a custom service Provider that wraps the \TheCodingMachine\GraphQLite\Laravel\Providers\GraphQLiteServiceProvider and modify the boot() and register() method in order to avoid the issues I listed out in the above question.

So
File: bootstrap/app.php

$app->register(App\Providers\GraphQLiteProvider::class);

File: app/Providers/GraphQLiteProvider.php

<?php
namespace App\Providers;

use GraphQL\Error\Debug;
use GraphQL\Server\ServerConfig;
use GraphQL\Server\StandardServer;
use GraphQL\Type\Schema as WebonyxSchema;
use Illuminate\Contracts\Auth\Factory as AuthFactory;
use Illuminate\Contracts\Events\Dispatcher;
use Laminas\Diactoros\ResponseFactory;
use Laminas\Diactoros\ServerRequestFactory;
use Laminas\Diactoros\StreamFactory;
use Laminas\Diactoros\UploadedFileFactory;
use Psr\Http\Message\ResponseFactoryInterface;
use Psr\Http\Message\ServerRequestFactoryInterface;
use Psr\Http\Message\StreamFactoryInterface;
use Psr\Http\Message\UploadedFileFactoryInterface;
use Symfony\Bridge\PsrHttpMessage\Factory\PsrHttpFactory;
use Symfony\Bridge\PsrHttpMessage\HttpMessageFactoryInterface;
use Symfony\Component\Cache\Adapter\ApcuAdapter;
use Symfony\Component\Cache\Adapter\PhpFilesAdapter;
use Symfony\Component\Cache\Psr16Cache;
use TheCodingMachine\GraphQLite\Context\Context;
use TheCodingMachine\GraphQLite\Exceptions\WebonyxErrorHandler;
use TheCodingMachine\GraphQLite\Laravel\Controllers\GraphQLiteController;
use TheCodingMachine\GraphQLite\Laravel\Listeners\CachePurger;
use TheCodingMachine\GraphQLite\Laravel\Mappers\PaginatorTypeMapperFactory;
use TheCodingMachine\GraphQLite\Laravel\Mappers\Parameters\ValidateFieldMiddleware;
use TheCodingMachine\GraphQLite\Laravel\SanePsr11ContainerAdapter;
use TheCodingMachine\GraphQLite\Laravel\Security\AuthenticationService;
use TheCodingMachine\GraphQLite\Laravel\Security\AuthorizationService;
use TheCodingMachine\GraphQLite\Schema;
use TheCodingMachine\GraphQLite\SchemaFactory;
use TheCodingMachine\GraphQLite\Security\AuthenticationServiceInterface;

class GraphQLiteProvider
    extends \TheCodingMachine\GraphQLite\Laravel\Providers\GraphQLiteServiceProvider
{
    public function boot(Dispatcher $events)
    {
        $this->loadRoutesFrom($this->getParentClassDirectory() . '/../routes/routes.php');
        $events->listen('cache:clearing', CachePurger::class);
    }

    public function register()
    {
        $this->app->bind(WebonyxSchema::class, Schema::class);

        if (!$this->app->has(ServerRequestFactoryInterface::class)) {
            $this->app->bind(ServerRequestFactoryInterface::class, ServerRequestFactory::class);
        }
        if (!$this->app->has(StreamFactoryInterface::class)) {
            $this->app->bind(StreamFactoryInterface::class, StreamFactory::class);
        }
        if (!$this->app->has(UploadedFileFactoryInterface::class)) {
            $this->app->bind(UploadedFileFactoryInterface::class, UploadedFileFactory::class);
        }
        if (!$this->app->has(ResponseFactoryInterface::class)) {
            $this->app->bind(ResponseFactoryInterface::class, ResponseFactory::class);
        }

        $this->app->bind(HttpMessageFactoryInterface::class, PsrHttpFactory::class);

        $this->app->singleton(GraphQLiteController::class, function ($app) {
            $debug = config('graphqlite.debug', Debug::RETHROW_UNSAFE_EXCEPTIONS);

            return new GraphQLiteController($app[StandardServer::class], $app[HttpMessageFactoryInterface::class], $debug);
        });

        $this->app->singleton(StandardServer::class, static function ($app) {
            return new StandardServer($app[ServerConfig::class]);
        });

        $this->app->singleton(ServerConfig::class, static function ($app) {
            $serverConfig = new ServerConfig();
            $serverConfig->setSchema($app[Schema::class]);
            $serverConfig->setErrorFormatter([WebonyxErrorHandler::class, 'errorFormatter']);
            $serverConfig->setErrorsHandler([WebonyxErrorHandler::class, 'errorHandler']);
            $serverConfig->setContext(new Context());
            return $serverConfig;
        });

        $this->app->singleton('graphqliteCache', static function () {
            if (extension_loaded('apcu') && ini_get('apc.enabled')) {
                return new Psr16Cache(new ApcuAdapter());
            } else {
                return new Psr16Cache(new PhpFilesAdapter());
            }
        });

        $this->app->singleton(CachePurger::class, static function ($app) {
            return new CachePurger($app['graphqliteCache']);
        });

        $this->app->singleton(AuthenticationService::class, function($app) {
            $guard = config('graphqlite.guard', $this->app['config']['auth.defaults.guard']);
            if (!is_array($guard)) {
                $guard = [$guard];
            }
            return new AuthenticationService($app[AuthFactory::class], $guard);
        });

        $this->app->bind(AuthenticationServiceInterface::class, AuthenticationService::class);

        $this->app->singleton(SchemaFactory::class, function ($app) {
            $service = new SchemaFactory($app->make('graphqliteCache'), new SanePsr11ContainerAdapter($app));
            $service->setAuthenticationService($app[AuthenticationService::class]);
            $service->setAuthorizationService($app[AuthorizationService::class]);
            $service->addParameterMiddleware($app[ValidateFieldMiddleware::class]);

            $service->addTypeMapperFactory($app[PaginatorTypeMapperFactory::class]);

            $controllers = config('graphqlite.controllers', 'App\\Http\\Controllers');
            if (!is_iterable($controllers)) {
                $controllers = [ $controllers ];
            }
            $types = config('graphqlite.types', 'App\\');
            if (!is_iterable($types)) {
                $types = [ $types ];
            }
            foreach ($controllers as $namespace) {
                $service->addControllerNamespace($namespace);
            }
            foreach ($types as $namespace) {
                $service->addTypeNamespace($namespace);
            }

            if ($this->app->environment('production')) {
                $service->prodMode();
            }

            return $service;
        });

        $this->app->singleton(Schema::class, function ($app) {
            /** @var SchemaFactory $schemaFactory */
            $schemaFactory = $app->make(SchemaFactory::class);

            return $schemaFactory->createSchema();
        });
    }


    private function getParentClassDirectory(): string
    {
        $reflection = new \ReflectionObject($this);

        return pathinfo($reflection->getParentClass()->getFileName(), PATHINFO_DIRNAME);
    }
}

With these changes, you are good to go.

Cheers,
Rajeev

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant