Skip to content

Commit

Permalink
Improved error handler with changing the way to intercept the errors.
Browse files Browse the repository at this point in the history
  • Loading branch information
jBernavaPrah committed Jun 5, 2022
1 parent 4098e07 commit e51a54f
Show file tree
Hide file tree
Showing 19 changed files with 287 additions and 212 deletions.
2 changes: 1 addition & 1 deletion .php_cs.cache

Large diffs are not rendered by default.

11 changes: 3 additions & 8 deletions phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,8 @@ includes:
- vendor/phpstan/phpstan-phpunit/rules.neon

parameters:
level: max
level: 5
paths:
- src
# - tests
ignoreErrors:
- '#\$callback of method Illuminate\\Support\\Collection(.*)::map\(\).*#'
- '#\$callback of method Illuminate\\Support\\Collection(.*)::reject\(\).*#'
- '#Unable to resolve the template type TNode in call to method static method Nuwave\\Lighthouse\\Schema\\AST\\ASTHelper::firstByName\(\)#'
- '#Unable to resolve the template type TMapValue in call to method Illuminate\\Support\\Collection<\(int\|string\),string>::map\(\)#'
- '#Unable to resolve the template type TKey in call to function collect#'
# - tests

25 changes: 12 additions & 13 deletions src/ASTManipulator.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@

namespace JBernavaPrah\LighthouseErrorHandler;


use GraphQL\Language\AST\InterfaceTypeDefinitionNode;
use GraphQL\Language\AST\Node;
use GraphQL\Language\AST\ObjectTypeDefinitionNode;
use GraphQL\Language\AST\TypeDefinitionNode;
use GraphQL\Language\Parser;
use Illuminate\Contracts\Config\Repository as Config;
use Illuminate\Support\Collection;
use Nuwave\Lighthouse\Events\ManipulateAST;
use Nuwave\Lighthouse\Exceptions\DefinitionException;
use Nuwave\Lighthouse\Schema\AST\DocumentAST;
Expand All @@ -16,11 +17,11 @@

class ASTManipulator
{

public function __construct(protected Config $config,
protected FieldManipulator $fieldManipulator,
protected ErrorService $errorService)
{
public function __construct(
protected Config $config,
protected FieldManipulator $fieldManipulator,
protected ErrorService $errorService
) {
}

/**
Expand All @@ -47,22 +48,22 @@ public function handle(ManipulateAST $manipulateAST): void
*/
protected function searchAndAddErrorsToDocument(DocumentAST $documentAST): void
{

$errors = $this->errorService->searchThrowableErrors();

collect($errors)->each(fn(TypeDefinitionNode $node) => $documentAST->setTypeDefinition($node));

Collection::wrap($errors)
->each(fn (TypeDefinitionNode&Node $node) => $documentAST->setTypeDefinition($node));
}

protected function manipulateFieldsType(DocumentAST $documentAST, string $parentNodeType): void
{

/** @var ObjectTypeDefinitionNode|null $parentNode */
$parentNode = $documentAST->types[$parentNodeType] ?? null;
if (!$parentNode) return;
if (! $parentNode) {
return;
}

$this->fieldManipulator->manipulate($documentAST, $parentNode);

}


Expand All @@ -75,6 +76,4 @@ interface Error {
GRAPHQL
);
}


}
44 changes: 0 additions & 44 deletions src/Commands/GenerateValidationCodeEnum.php

This file was deleted.

14 changes: 4 additions & 10 deletions src/Error.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,16 @@

namespace JBernavaPrah\LighthouseErrorHandler;


use GraphQL\Type\Definition\ResolveInfo;
use Nuwave\Lighthouse\Support\Contracts\GraphQLContext;

abstract class Error extends \Exception
{
abstract public static function definition(): string;

abstract public function resolver(mixed $root, array $args, GraphQLContext $context, ResolveInfo $info): array;
abstract public function resolver(): array;

public function resolve(mixed $root, array $args, GraphQLContext $context, ResolveInfo $info): array
public function resolve(): array
{

return array_merge([
"__typename" => (new \ReflectionClass(static::class))->getShortName(),
], $this->resolver($root, $args, $context, $info));
'__typename' => (new \ReflectionClass(static::class))->getShortName(),
], $this->resolver());
}

}
37 changes: 13 additions & 24 deletions src/ErrorService.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,7 @@
use GraphQL\Language\Parser;
use HaydenPierce\ClassFinder\ClassFinder;
use Illuminate\Contracts\Config\Repository;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;
use Jasny\PhpdocParser\PhpdocParser;
use Jasny\PhpdocParser\Tag\MultiTag;
use Jasny\PhpdocParser\Tag\PhpDocumentor\TypeTag;
use Jasny\PhpdocParser\TagSet;
use JBernavaPrah\LighthouseErrorHandler\Errors\AuthenticationError;
use JBernavaPrah\LighthouseErrorHandler\Errors\AuthorizationError;
use JBernavaPrah\LighthouseErrorHandler\Errors\ValidationError;
Expand All @@ -25,7 +20,6 @@

class ErrorService
{

/**
* @var array|string[]
*/
Expand All @@ -47,29 +41,26 @@ public function searchThrowableErrors(): array
$errorNamespaces = $this->config->get('lighthouse.namespaces.errors') ?? [];

return collect($errorNamespaces)
->merge(["JBernavaPrah\\LighthouseErrorHandler\\Errors"])
->merge(['JBernavaPrah\\LighthouseErrorHandler\\Errors'])
->flatten()
->map(fn(string $namespace) => ClassFinder::getClassesInNamespace($namespace, ClassFinder::RECURSIVE_MODE))
->map(fn (string $namespace) => ClassFinder::getClassesInNamespace($namespace, ClassFinder::RECURSIVE_MODE))
->flatten()
->reject(fn(string $class) => !is_subclass_of($class, Error::class))
->mapWithKeys(fn(string $class) => [$class => Parser::parse($class::definition())])
->each(fn(DocumentNode $documentNode, string $class) => $this->validateErrorDocumentNode($documentNode, $class))
->map(fn(DocumentNode $documentNode, string $_) => $this->extractTypeDefinitionFromDocumentNode($documentNode))
->reject(fn (string $class) => ! is_subclass_of($class, Error::class))
->mapWithKeys(fn (string $class) => [$class => Parser::parse($class::definition())])
->each(fn (DocumentNode $documentNode, string $class) => $this->validateErrorDocumentNode($documentNode, $class))
->map(fn (DocumentNode $documentNode, string $_) => $this->extractTypeDefinitionFromDocumentNode($documentNode))
->flatten()
->toArray();

}

protected function extractTypeDefinitionFromDocumentNode(DocumentNode $documentNode): array
{

$nodes = [];
foreach ($documentNode->definitions as $node) {
$nodes[] = $node;
}

return $nodes;

}

/**
Expand All @@ -78,16 +69,16 @@ protected function extractTypeDefinitionFromDocumentNode(DocumentNode $documentN
*/
protected function validateErrorDocumentNode(DocumentNode $documentNode, string $class)
{

$className = (new ReflectionClass($class))->getShortName();

foreach ($documentNode->definitions as $definition) {
assert($definition instanceof TypeDefinitionNode);
if ($definition->name->value === $className) return true;
if ($definition->name->value === $className) {
return true;
}
}

throw new DefinitionException("Impossible to find the root definition on class {$class}. ");

}

/**
Expand All @@ -104,9 +95,9 @@ public function getThrowableErrorsFromField(FieldDefinitionNode $node): array
return collect($this->getRelatedClassException($node))
->merge($this->defaultErrorClasses)
->flatten()
->reject(fn(string $error) => !class_exists($error) || !is_subclass_of($error, Error::class))
->reject(fn (string $error) => ! class_exists($error) || ! is_subclass_of($error, Error::class))
->flatten()
->map(fn(string $class) => (new ReflectionClass($class))->getShortName())
->map(fn (string $class) => (new ReflectionClass($class))->getShortName())
->toArray();
}

Expand All @@ -125,15 +116,14 @@ protected function getRelatedClassException(FieldDefinitionNode $node): array
$className = Utils::namespaceClassname(
(string)Str::of($node->name->value)->camel()->ucfirst(),
$namespaces,
fn($error): bool => class_exists($error),
fn ($error): bool => class_exists($error),
);

if (!$className) {
if (! $className) {
return [];
}

return $this->getDocumentedClassException($className, '__invoke');

}

/**
Expand All @@ -144,7 +134,6 @@ protected function getRelatedClassException(FieldDefinitionNode $node): array
*/
protected function getDocumentedClassException(string $class, string $method): array
{

$attributes = (new ReflectionClass($class))->getMethod($method)->getAttributes(HasError::class);

$throws = [];
Expand Down
29 changes: 3 additions & 26 deletions src/ErrorableDirective.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,16 @@

namespace JBernavaPrah\LighthouseErrorHandler;

use Illuminate\Auth\Access\AuthorizationException;
use JBernavaPrah\LighthouseErrorHandler\Errors\AuthenticationError;
use JBernavaPrah\LighthouseErrorHandler\Errors\AuthorizationError;
use JBernavaPrah\LighthouseErrorHandler\Errors\ValidationError;
use Closure;
use Exception;
use GraphQL\Type\Definition\ResolveInfo;
use Illuminate\Auth\AuthenticationException;
use Illuminate\Validation\ValidationException;
use Nuwave\Lighthouse\Schema\Directives\BaseDirective;
use Nuwave\Lighthouse\Schema\Values\FieldValue;
use Nuwave\Lighthouse\Support\Contracts\FieldMiddleware;
use Nuwave\Lighthouse\Support\Contracts\GraphQLContext;

final class ErrorableDirective extends BaseDirective implements FieldMiddleware
{


public static function definition(): string
{
return /** @lang GraphQL */ <<<'GRAPHQL'
Expand All @@ -29,7 +21,7 @@ public static function definition(): string
"""
directive @errorable(
"""
Use this type when there are no errors.
Resolve to this type when there are no errors.
"""
defaultType: String!
) on FIELD_DEFINITION
Expand All @@ -54,25 +46,10 @@ public function handleField(FieldValue $fieldValue, Closure $next): FieldValue
$previousResolver = $fieldValue->getResolver();

$fieldValue->setResolver(function ($root, array $args, GraphQLContext $context, ResolveInfo $info) use ($previousResolver) {

try {
$this->resolveType->setResolveType($this->directiveArgValue("defaultType"));
return $previousResolver($root, $args, $context, $info);
} catch (AuthorizationException $exception) {
return AuthorizationError::fromLaravel($exception)->resolve($root, $args, $context, $info);
} catch (AuthenticationException $exception) {
return AuthenticationError::fromLaravel($exception)->resolve($root, $args, $context, $info);
} catch (ValidationException $exception) {
return ValidationError::fromLaravel($exception)->resolve($root, $args, $context, $info);
} catch (\Nuwave\Lighthouse\Exceptions\ValidationException $exception) {
return ValidationError::fromLighthouse($exception)->resolve($root, $args, $context, $info);
} catch (Error $exception) {
return $exception->resolve($root, $args, $context, $info);
}

$this->resolveType->setResolveType($this->directiveArgValue('defaultType'));
return $previousResolver($root, $args, $context, $info);
});

return $next($fieldValue);
}

}
7 changes: 2 additions & 5 deletions src/Errors/AuthenticationError.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,11 @@
namespace JBernavaPrah\LighthouseErrorHandler\Errors;

use JBernavaPrah\LighthouseErrorHandler\Error;
use GraphQL\Type\Definition\ResolveInfo;
use JetBrains\PhpStorm\ArrayShape;
use JetBrains\PhpStorm\Pure;
use Nuwave\Lighthouse\Support\Contracts\GraphQLContext;

class AuthenticationError extends Error
{

/**
* @var array<string|int, string>
*/
Expand Down Expand Up @@ -44,8 +41,8 @@ public static function definition(): string
GRAPHQL;
}

#[ArrayShape(['message' => "string", 'guards' => "mixed"])]
public function resolver(mixed $root, array $args, GraphQLContext $context, ResolveInfo $info): array
#[ArrayShape(['message' => 'string', 'guards' => 'mixed'])]
public function resolver(): array
{
return [
'message' => $this->getMessage(),
Expand Down
11 changes: 4 additions & 7 deletions src/Errors/AuthorizationError.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,13 @@

namespace JBernavaPrah\LighthouseErrorHandler\Errors;

use JBernavaPrah\LighthouseErrorHandler\Error;
use GraphQL\Type\Definition\ResolveInfo;
use Illuminate\Auth\Access\AuthorizationException;
use JBernavaPrah\LighthouseErrorHandler\Error;
use JetBrains\PhpStorm\ArrayShape;
use JetBrains\PhpStorm\Pure;
use Nuwave\Lighthouse\Support\Contracts\GraphQLContext;

class AuthorizationError extends Error
{

public static function definition(): string
{
return /** @lang GraphQL */ <<<GRAPHQL
Expand All @@ -29,11 +26,11 @@ public static function definition(): string
return new self($exception->getMessage(), $exception->getCode(), $exception);
}

#[ArrayShape(["message" => "string"])]
public function resolver(mixed $root, array $args, GraphQLContext $context, ResolveInfo $info): array
#[ArrayShape(['message' => 'string'])]
public function resolver(): array
{
return [
"message" => $this->getMessage()
'message' => $this->getMessage(),
];
}
}
Loading

0 comments on commit e51a54f

Please sign in to comment.