Skip to content

Instantly share code, notes, and snippets.

@pbalan
Created October 22, 2021 09:42
Show Gist options
  • Save pbalan/1627bcd88b3da44ed15716739d3ba108 to your computer and use it in GitHub Desktop.
Save pbalan/1627bcd88b3da44ed15716739d3ba108 to your computer and use it in GitHub Desktop.
graphql
<?php
namespace App\Exceptions;
use Exception;
use Nuwave\Lighthouse\Exceptions\RendersErrorsExtensions;
class GraphQLException extends Exception implements RendersErrorsExtensions
{
/**
* @var @string
*/
protected $reason;
public function __construct(string $message, $reason)
{
parent::__construct($message);
$this->reason = json_encode($reason);
}
/**
* Returns true when exception message is safe to be displayed to a client.
*
* @api
* @return bool
*/
public function isClientSafe(): bool
{
return true;
}
/**
* Returns string describing a category of the error.
*
* Value "graphql" is reserved for errors produced by query parsing or validation, do not use it.
*
* @api
* @return string
*/
public function getCategory(): string
{
return 'custom';
}
/**
* Return the content that is put in the "extensions" part
* of the returned error.
*
* @return array
*/
public function extensionsContent(): array
{
return [
'some' => 'additional information',
'reason' => $this->reason,
];
}
}
<?php
namespace App\Traits;
use App\User;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log;
trait HasOwner
{
static $belongsTo = 'BelongsTo';
public static function boot()
{
try {
parent::boot();
if (Auth::check()) {
$user = User::where('auth0_id', Auth::user()->sub)->first();
static::creating(function (Model $model) use ($user) {
foreach ($model->relationships() as $field => $relation) {
try {
if (property_exists($model, 'notOwner') && is_array($model->notOwner) && in_array($relation['foreign'], $model->notOwner)) {
continue;
}
if (self::$belongsTo === $relation['type'] && User::class === $relation['model'] && null !== $user) {
$model->{$relation['foreign']} = $user->id;
}
} catch(\Exception $ex) {
Log::info($user->toArray());
Log::info(sprintf('field: %s, foreign: %s', $field, $relation['foreign']));
Log::info($ex->getMessage());
throw $ex;
}
}
});
static::saving(function (Model $model) use ($user) {
foreach ($model->relationships() as $field => $relation) {
try {
if (property_exists($model, 'notOwner') && is_array($model->notOwner) && in_array($relation['foreign'], $model->notOwner)) {
continue;
}
if (self::$belongsTo === $relation['type'] && User::class === $relation['model'] && null !== $user) {
$model->{$relation['foreign']} = $user->id;
}
} catch(\Exception $ex) {
Log::info($user->toArray());
Log::info(sprintf('field: %s, foreign: %s', $field, $relation['foreign']));
Log::info($ex->getMessage());
throw $ex;
}
}
});
}
} catch (\Exception $e) {
Log::info($e->getMessage());
throw $e;
}
}
public function scopeIsOwner($query)
{
$field = 'user_id';
if (User::class === get_class($query->getModel())) {
$field = 'id';
}
$user = User::where('auth0_id', Auth::user()->sub)->first();
return $query->where($field, $user->id);
}
}
<?php
namespace App\GraphQL\Directives;
use Nuwave\Lighthouse\Exceptions\AuthenticationException;
use Illuminate\Contracts\Auth\Factory as Auth;
use GraphQL\Language\AST\Node;
use GraphQL\Language\AST\NodeList;
use GraphQL\Language\AST\FieldDefinitionNode;
use GraphQL\Language\AST\ObjectTypeExtensionNode;
use GraphQL\Type\Definition\ResolveInfo;
use Nuwave\Lighthouse\Support\Contracts\FieldManipulator;
use Nuwave\Lighthouse\Support\Contracts\GraphQLContext;
use Nuwave\Lighthouse\Schema\Values\FieldValue;
use GraphQL\Language\Parser;
use Nuwave\Lighthouse\Schema\AST\ASTHelper;
use Nuwave\Lighthouse\Schema\AST\DocumentAST;
use Nuwave\Lighthouse\Support\Contracts\CreatesContext;
use Nuwave\Lighthouse\Schema\Directives\BaseDirective;
use Nuwave\Lighthouse\Support\Contracts\FieldMiddleware;
class ProtectDirective extends BaseDirective implements FieldManipulator, FieldMiddleware
{
/**
* The authentication factory instance.
*
* @var \Illuminate\Contracts\Auth\Factory
*/
protected $auth;
/** @var CreatesContext */
protected $createsContext;
/**
* Create a new middleware instance.
*
* @param \Illuminate\Contracts\Auth\Factory $auth
* @return void
*/
public function __construct(Auth $auth, CreatesContext $createsContext)
{
$this->auth = $auth;
$this->createsContext = $createsContext;
}
/**
* Directive name.
*
* @return string
*/
public function name(): string
{
return 'protect';
}
/**
* Resolve the field directive.
*
* @param FieldValue $fieldValue
* @param \Closure $next
*
* @return FieldValue
*/
public function handleField(FieldValue $fieldValue, \Closure $next)
{
$resolver = $fieldValue->getResolver();
return $next($fieldValue->setResolver(function ($root, array $args, GraphQLContext $context, ResolveInfo $resolveInfo) use ($resolver) {
$guards = $this->directiveArgValue('guards', []);
$this->authenticate($context->request, $guards);
return $resolver(
$root,
$args,
$this->createsContext->generate($context->request()),
$resolveInfo
);
}));
}
/**
* @param Node $node
* @param DocumentAST $documentAST
*
* @return DocumentAST
*/
public function manipulateFieldDefinition(DocumentAST &$documentAST, FieldDefinitionNode &$fieldDefinition, &$parentType): DocumentAST
{
$args = $this->directiveArgValue('guards', []);
$node = $this->setProtectDirectiveOnFields($fieldDefinition, $args);
$documentAST->setDefinition($node);
return $documentAST;
}
/**
* @param FieldDefinitionNode|ObjectTypeExtensionNode $objectType
*
* @throws \Nuwave\Lighthouse\Exceptions\DirectiveException
*
* @return FieldDefinitionNode|ObjectTypeExtensionNode
*/
protected function setProtectDirectiveOnFields($objectType, array $args)
{
$objectType->fields = new NodeList(
collect($objectType->fields)
->map(function (FieldDefinitionNode $fieldDefinition) use ($args) {
$existingProtectDirective = ASTHelper::directiveDefinition(
$fieldDefinition,
$this->name()
);
if ($existingProtectDirective){
return $fieldDefinition;
} else {
$protectArgValue = collect($args)->implode('", "');
$directive = !empty($args)
? Parser::directive("@protect(guards: [\"$protectArgValue\"])")
: Parser::directive("@protect");
$fieldDefinition->directives = $fieldDefinition->directives->merge([$directive]);
return $fieldDefinition;
}
})
->toArray()
);
return $objectType;
}
/**
* Determine if the user is logged in to any of the given guards.
*
* @param \Illuminate\Http\Request $request
* @param array $guards
* @return void
*
* @throws \App\Containers\Core\GraphQL\Exceptions\AuthException
*/
protected function authenticate($request, array $guards)
{
if (empty($guards)) {
$guards = [null];
}
foreach ($guards as $guard) {
if ($this->auth->guard($guard)->check()) {
return $this->auth->shouldUse($guard);
}
}
throw new AuthenticationException("You are not authorized to view this resource");
}
public static function definition(): string
{
// TODO: Implement definition() method.
}
}
<?php
namespace App\Traits;
use Illuminate\Database\Eloquent\Relations\Relation;
use Illuminate\Support\Facades\Log;
use ReflectionClass;
use ReflectionMethod;
trait Relationship
{
public function relationships()
{
$model = new static;
$relationships = [];
foreach ((new ReflectionClass($model))->getMethods(ReflectionMethod::IS_PUBLIC) as $method) {
if (
$method->class != get_class($model) ||
!empty($method->getParameters()) ||
$method->getName() == __FUNCTION__
) {
continue;
}
$forgin = null;
try {
$return = $method->invoke($model);
if ($return instanceof Relation) {
$returnReflection = new ReflectionClass($return);
$ownerKey = null;
if ($returnReflection->hasMethod('getOwnerKey'))
$ownerKey = $return->getOwnerKey();
else {
$segments = explode('.', $return->getQualifiedParentKeyName());
$ownerKey = $segments[count($segments) - 1];
}
if ($returnReflection->hasMethod('getForeignKey')) {
$forgin = $return->getForeignKey();
} else if ($returnReflection->hasMethod('getForeignKeyName')) {
$forgin = $return->getForeignKeyName();
} else if ($returnReflection->hasMethod('getForeignPivotKeyName')) {
$forgin = $return->getForeignPivotKeyName();
}
$relationships[$method->getName()] = [
'type' => $returnReflection->getShortName(),
'model' => (new ReflectionClass($return->getRelated()))->getName(),
'owner' => $ownerKey,
'foreign' => $forgin
];
}
} catch (\Exception $e) {
Log::info("Error while creating relationship matrix", [$e->getMessage()]);
}
}
return $relationships;
}
}
type Admin @model(class: "\\Modules\\Example\\Entities\\Admin") {
id: ID!
admin: User @belongsTo
addedBy: User @belongsTo
created_at: DateTime!
updated_at: DateTime!
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment