Skip to content

Adding the ability to type-hint on a Symfony request #27

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

Merged
merged 1 commit into from
Jul 12, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions Context/SymfonyGraphQLContext.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php


namespace TheCodingMachine\Graphqlite\Bundle\Context;


use Symfony\Component\HttpFoundation\Request;

class SymfonyGraphQLContext implements SymfonyRequestContextInterface
{
/**
* @var Request
*/
private $request;

public function __construct(Request $request)
{
$this->request = $request;
}

/**
* @return Request
*/
public function getRequest(): Request
{
return $this->request;
}
}
13 changes: 13 additions & 0 deletions Context/SymfonyRequestContextInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

namespace TheCodingMachine\Graphqlite\Bundle\Context;

use Symfony\Component\HttpFoundation\Request;

interface SymfonyRequestContextInterface
{
/**
* @return Request
*/
public function getRequest(): Request;
}
10 changes: 10 additions & 0 deletions Controller/GraphQL/InvalidUserPasswordException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php


namespace TheCodingMachine\Graphqlite\Bundle\Controller\GraphQL;


class InvalidUserPasswordException
{

}
10 changes: 10 additions & 0 deletions Controller/GraphQL/LoginController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php


namespace TheCodingMachine\Graphqlite\Bundle\Controller\GraphQL;


class LoginController
{

}
23 changes: 16 additions & 7 deletions Controller/GraphqliteController.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use GraphQL\Error\Error;
use GraphQL\Executor\ExecutionResult;
use GraphQL\Executor\Promise\Promise;
use GraphQL\Server\ServerConfig;
use GraphQL\Server\StandardServer;
use GraphQL\Upload\UploadMiddleware;
use function in_array;
Expand All @@ -27,6 +28,7 @@
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;
use TheCodingMachine\Graphqlite\Bundle\Context\SymfonyGraphQLContext;

/**
* Listens to every single request and forward Graphql requests to Graphql Webonix standardServer.
Expand All @@ -37,14 +39,16 @@ class GraphqliteController
* @var HttpMessageFactoryInterface
*/
private $httpMessageFactory;
/** @var StandardServer */
private $standardServer;
/** @var bool|int */
private $debug;
/**
* @var ServerConfig
*/
private $serverConfig;

public function __construct(StandardServer $standardServer, HttpMessageFactoryInterface $httpMessageFactory = null, ?int $debug = Debug::RETHROW_UNSAFE_EXCEPTIONS)
public function __construct(ServerConfig $serverConfig, HttpMessageFactoryInterface $httpMessageFactory = null, ?int $debug = Debug::RETHROW_UNSAFE_EXCEPTIONS)
{
$this->standardServer = $standardServer;
$this->serverConfig = $serverConfig;
$this->httpMessageFactory = $httpMessageFactory ?: new DiactorosFactory();
$this->debug = $debug ?? false;
}
Expand Down Expand Up @@ -84,12 +88,17 @@ public function handleRequest(Request $request): Response
$uploadMiddleware = new UploadMiddleware();
$psr7Request = $uploadMiddleware->processRequest($psr7Request);

return $this->handlePsr7Request($psr7Request);
return $this->handlePsr7Request($psr7Request, $request);
}

private function handlePsr7Request(ServerRequestInterface $request): JsonResponse
private function handlePsr7Request(ServerRequestInterface $request, Request $symfonyRequest): JsonResponse
{
$result = $this->standardServer->executePsrRequest($request);
// Let's put the request in the context.
$serverConfig = clone $this->serverConfig;
$serverConfig->setContext(new SymfonyGraphQLContext($symfonyRequest));

$standardService = new StandardServer($serverConfig);
$result = $standardService->executePsrRequest($request);

if ($result instanceof ExecutionResult) {
return new JsonResponse($result->toArray($this->debug), $this->decideHttpStatusCode($result));
Expand Down
28 changes: 28 additions & 0 deletions Mappers/RequestParameter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php


namespace TheCodingMachine\Graphqlite\Bundle\Mappers;


use GraphQL\Type\Definition\ResolveInfo;
use TheCodingMachine\Graphqlite\Bundle\Context\SymfonyRequestContextInterface;
use TheCodingMachine\GraphQLite\GraphQLException;
use TheCodingMachine\GraphQLite\Parameters\ParameterInterface;

class RequestParameter implements ParameterInterface
{

/**
* @param array<string, mixed> $args
* @param mixed $context
*
* @return mixed
*/
public function resolve(?object $source, array $args, $context, ResolveInfo $info)
{
if (!$context instanceof SymfonyRequestContextInterface) {
throw new GraphQLException('Cannot type-hint on a Symfony Request object in your query/mutation/field. The request context must implement SymfonyRequestContextInterface.');
}
return $context->getRequest();
}
}
25 changes: 25 additions & 0 deletions Mappers/RequestParameterMapper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php


namespace TheCodingMachine\Graphqlite\Bundle\Mappers;


use phpDocumentor\Reflection\DocBlock;
use phpDocumentor\Reflection\Type;
use ReflectionParameter;
use Symfony\Component\HttpFoundation\Request;
use TheCodingMachine\GraphQLite\Annotations\ParameterAnnotations;
use TheCodingMachine\GraphQLite\Mappers\Parameters\ParameterMapperInterface;
use TheCodingMachine\GraphQLite\Parameters\ParameterInterface;

class RequestParameterMapper implements ParameterMapperInterface
{

public function mapParameter(ReflectionParameter $parameter, DocBlock $docBlock, ?Type $paramTagType, ParameterAnnotations $parameterAnnotations): ?ParameterInterface
{
if ($parameter->getType()->getName() === Request::class) {
return new RequestParameter();
}
return null;
}
}
8 changes: 4 additions & 4 deletions Resources/config/container/graphqlite.xml
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,6 @@

<service id="TheCodingMachine\GraphQLite\Security\AuthorizationServiceInterface" alias="TheCodingMachine\Graphqlite\Bundle\Security\AuthorizationService" />

<service id="GraphQL\Server\StandardServer">
<argument type="service" id="GraphQL\Server\ServerConfig" />
</service>

<service id="GraphQL\Server\ServerConfig">
<call method="setSchema">
<argument type="service" id="TheCodingMachine\GraphQLite\Schema"/>
Expand All @@ -62,6 +58,10 @@
</service>

<service id="TheCodingMachine\Graphqlite\Bundle\Controller\GraphqliteController" public="true" />

<service id="TheCodingMachine\Graphqlite\Bundle\Mappers\RequestParameterMapper">
<tag name="graphql.parameter_mapper"/>
</service>
</services>

</container>
10 changes: 10 additions & 0 deletions Tests/Fixtures/Controller/TestGraphqlController.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use GraphQL\Error\Error;
use Porpaginas\Arrays\ArrayResult;
use Symfony\Component\HttpFoundation\Request;
use TheCodingMachine\GraphQLite\Annotations\FailWith;
use TheCodingMachine\GraphQLite\Annotations\Logged;
use TheCodingMachine\GraphQLite\Annotations\Right;
Expand Down Expand Up @@ -102,4 +103,13 @@ public function withUserRight(): string
{
return 'foo';
}

/**
* @Query()
* @return string
*/
public function getUri(Request $request): string
{
return $request->getPathInfo();
}
}
31 changes: 26 additions & 5 deletions Tests/FunctionalTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

class FunctionalTest extends TestCase
{
public function testServiceWiring()
public function testServiceWiring(): void
{
$kernel = new GraphqliteTestingKernel('test', true);
$kernel->boot();
Expand Down Expand Up @@ -68,7 +68,7 @@ public function testServiceWiring()
], $result);
}

public function testServiceAutowiring()
public function testServiceAutowiring(): void
{
$kernel = new GraphqliteTestingKernel('test', true);
$kernel->boot();
Expand Down Expand Up @@ -98,7 +98,7 @@ public function testServiceAutowiring()
], $result);
}

public function testErrors()
public function testErrors(): void
{
$kernel = new GraphqliteTestingKernel('test', true);
$kernel->boot();
Expand Down Expand Up @@ -134,7 +134,7 @@ public function testErrors()
$this->assertSame(404, $response->getStatusCode(), $response->getContent());
}

public function testLoggedMiddleware()
public function testLoggedMiddleware(): void
{
$kernel = new GraphqliteTestingKernel('test', true);
$kernel->boot();
Expand All @@ -155,7 +155,7 @@ public function testLoggedMiddleware()
], $result);
}

public function testLoggedMiddleware2()
public function testLoggedMiddleware2(): void
{
$kernel = new GraphqliteTestingKernel('test', true);
$kernel->boot();
Expand Down Expand Up @@ -185,6 +185,27 @@ public function testLoggedMiddleware2()

}

public function testInjectQuery(): void
{
$kernel = new GraphqliteTestingKernel('test', true);
$kernel->boot();

$request = Request::create('/graphql', 'GET', ['query' => '
{
uri
}']);

$response = $kernel->handle($request);

$result = json_decode($response->getContent(), true);

$this->assertSame([
'data' => [
'uri' => '/graphql'
]
], $result);
}

private function logIn(ContainerInterface $container)
{
// put a token into the storage so the final calls can function
Expand Down