mirror of
https://github.com/AnimeThemes/animethemes-server.git
synced 2026-07-11 01:24:46 +02:00
fix(graphql): fix format exceptions (#1049)
This commit is contained in:
@@ -8,7 +8,6 @@ use App\Concerns\Actions\GraphQL\ConstrainsEagerLoads;
|
||||
use App\Concerns\Actions\GraphQL\PaginatesModels;
|
||||
use App\Concerns\Actions\GraphQL\SortsModels;
|
||||
use App\Enums\GraphQL\SortType;
|
||||
use App\Exceptions\GraphQL\ClientValidationException;
|
||||
use App\GraphQL\Argument\SortArgument;
|
||||
use App\GraphQL\Criteria\Sort\RelationSortCriteria;
|
||||
use App\GraphQL\Criteria\Sort\SortCriteria;
|
||||
@@ -22,6 +21,7 @@ use Illuminate\Contracts\Pagination\Paginator;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
|
||||
class IndexAction
|
||||
{
|
||||
@@ -57,7 +57,17 @@ class IndexAction
|
||||
$page = Arr::get($args, 'page', 1);
|
||||
|
||||
$maxCount = Config::get('graphql.pagination_values.max_count');
|
||||
throw_if($maxCount !== null && $first > $maxCount, ClientValidationException::class, "Maximum first value is {$maxCount}. Got {$first}. Fetch in smaller chuncks.");
|
||||
|
||||
Validator::make(['first' => $first], [
|
||||
'first' => [
|
||||
'required', 'integer', 'min:1',
|
||||
function ($attribute, $value, $fail) use ($maxCount, $first): void {
|
||||
if ($maxCount !== null && $value > $maxCount) {
|
||||
$fail("You may request at most {$maxCount} items. Got {$first}. Fetch in smaller chuncks.");
|
||||
}
|
||||
},
|
||||
],
|
||||
])->validate();
|
||||
|
||||
$searchBuilder->withPagination($first, $page);
|
||||
|
||||
|
||||
@@ -27,42 +27,37 @@ trait ConstrainsEagerLoads
|
||||
{
|
||||
$resolveInfo = new CustomResolveInfo($resolveInfo);
|
||||
|
||||
$fields = Arr::get($resolveInfo->getFieldSelectionWithAliases(100), "{$fieldName}.{$fieldName}.selectionSet")
|
||||
$selection = Arr::get($resolveInfo->getFieldSelectionWithAliases(100), "{$fieldName}.{$fieldName}.selectionSet")
|
||||
?? $resolveInfo->getFieldSelectionWithAliases(100);
|
||||
|
||||
$this->processEagerLoadForType($query, $fields, $type);
|
||||
$this->processEagerLoadForType($query, $selection, $type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process recursively the relations available for the given type.
|
||||
*/
|
||||
private function processEagerLoadForType(Builder $builder, array $fields, BaseType|BaseUnion $type): void
|
||||
private function processEagerLoadForType(Builder $builder, array $selection, BaseType|BaseUnion $type): void
|
||||
{
|
||||
$eagerLoadRelations = [];
|
||||
|
||||
/** @var array<int, Relation> $relations */
|
||||
$relations = collect($type->relations())
|
||||
->filter(fn (Relation $relation) => Arr::has($fields, $relation->getName()))
|
||||
->filter(fn (Relation $relation) => Arr::has($selection, $relation->getName()))
|
||||
->all();
|
||||
|
||||
foreach ($relations as $relation) {
|
||||
$name = $relation->getName();
|
||||
$path = $relation->getRelationName();
|
||||
|
||||
$relationSelection = Arr::get($fields, "{$name}.{$name}");
|
||||
|
||||
$relationArgs = Arr::get($relationSelection, 'args');
|
||||
$relationSelection = Arr::get($selection, "{$name}.{$name}");
|
||||
|
||||
$relationType = $relation->getBaseType();
|
||||
|
||||
$eagerLoadRelations[$path] = function (EloquentRelation $eloquentRelation) use ($relationSelection, $relationArgs, $relationType, $relation): void {
|
||||
if ($eloquentRelation instanceof MorphTo) {
|
||||
$this->processMorphToRelation($relationSelection, $relationType, $eloquentRelation);
|
||||
} elseif ($relationType instanceof BaseUnion) {
|
||||
$this->processUnion($relationSelection, $eloquentRelation, $relationType);
|
||||
} else {
|
||||
$this->processGenericRelation($eloquentRelation, $relationArgs, $relationSelection, $relationType, $relation);
|
||||
}
|
||||
$eagerLoadRelations[$relation->getRelationName()] = function (EloquentRelation $eloquentRelation) use ($relationSelection, $relationType, $relation): void {
|
||||
match (true) {
|
||||
$eloquentRelation instanceof MorphTo => $this->processMorphToRelation($relationSelection, $relationType, $eloquentRelation),
|
||||
$relationType instanceof BaseUnion => $this->processUnion($relationSelection, $relationType, $eloquentRelation),
|
||||
default => $this->processGenericRelation($relationSelection, $relationType, $eloquentRelation, $relation),
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -84,7 +79,7 @@ trait ConstrainsEagerLoads
|
||||
|
||||
$morphConstrains = [];
|
||||
foreach ($types as $type) {
|
||||
$typeSelection = Arr::get($unions, "{$type->getName()}.selectionSet");
|
||||
$typeSelection = Arr::get($unions, "{$type->getName()}.selectionSet", []);
|
||||
|
||||
$morphConstrains[$type->model()] = function (Builder $query) use ($typeSelection, $type): void {
|
||||
$this->processEagerLoadForType($query, $typeSelection, $type);
|
||||
@@ -97,10 +92,8 @@ trait ConstrainsEagerLoads
|
||||
/**
|
||||
* Process a union relation by applying the eager loads for each type in the union.
|
||||
*/
|
||||
private function processUnion(array $selection, EloquentRelation $relation, BaseUnion $union): void
|
||||
private function processUnion(array $selection, BaseUnion $union, EloquentRelation $relation): void
|
||||
{
|
||||
$builder = $relation->getQuery();
|
||||
|
||||
$unions = Arr::get($selection, 'selectionSet.data.data.unions', []);
|
||||
|
||||
$types = collect($union->baseTypes())
|
||||
@@ -110,17 +103,19 @@ trait ConstrainsEagerLoads
|
||||
foreach ($types as $type) {
|
||||
$typeSelection = Arr::get($unions, "{$type->getName()}.selectionSet", []);
|
||||
|
||||
$this->processEagerLoadForType($builder, $typeSelection, $type);
|
||||
$this->processEagerLoadForType($relation->getQuery(), $typeSelection, $type);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a generic relation by applying filters, sorting and eager loads.
|
||||
*/
|
||||
private function processGenericRelation(EloquentRelation $relation, array $args, array $selection, BaseType $type, Relation $graphqlRelation): void
|
||||
private function processGenericRelation(array $selection, BaseType $type, EloquentRelation $relation, Relation $graphqlRelation): void
|
||||
{
|
||||
$builder = $relation->getQuery();
|
||||
|
||||
$args = Arr::get($selection, 'args');
|
||||
|
||||
$this->filter($builder, $args, $type);
|
||||
|
||||
$this->sort($builder, $args, $type, $relation, $graphqlRelation);
|
||||
|
||||
@@ -4,11 +4,11 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Concerns\Actions\GraphQL;
|
||||
|
||||
use App\Exceptions\GraphQL\ClientValidationException;
|
||||
use Illuminate\Contracts\Pagination\Paginator;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
|
||||
trait PaginatesModels
|
||||
{
|
||||
@@ -18,7 +18,17 @@ trait PaginatesModels
|
||||
$page = Arr::get($args, 'page');
|
||||
|
||||
$maxCount = Config::get('graphql.pagination_values.max_count');
|
||||
throw_if($maxCount !== null && $first > $maxCount, ClientValidationException::class, "Maximum first value is {$maxCount}. Got {$first}. Fetch in smaller chuncks.");
|
||||
|
||||
Validator::make(['first' => $first], [
|
||||
'first' => [
|
||||
'required', 'integer', 'min:1',
|
||||
function ($attribute, $value, $fail) use ($maxCount, $first): void {
|
||||
if ($maxCount !== null && $value > $maxCount) {
|
||||
$fail("You may request at most {$maxCount} items. Got {$first}. Fetch in smaller chuncks.");
|
||||
}
|
||||
},
|
||||
],
|
||||
])->validate();
|
||||
|
||||
return $builder->paginate($first, page: $page);
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ class DatabaseSyncCommand extends BaseCommand
|
||||
}
|
||||
|
||||
if (! $this->option('drop')) {
|
||||
Schema::withoutForeignKeyConstraints(function () {
|
||||
Schema::withoutForeignKeyConstraints(function (): void {
|
||||
foreach ([
|
||||
...DumpDocumentAction::allowedTables(),
|
||||
...DumpWikiAction::allowedTables(),
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Exceptions\GraphQL;
|
||||
|
||||
use GraphQL\Error\Error;
|
||||
|
||||
/**
|
||||
* Thrown when client arguments are missing or wrong.
|
||||
*/
|
||||
class ClientValidationException extends Error
|
||||
{
|
||||
public function isClientSafe(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -11,8 +11,6 @@ use App\GraphQL\Schema\Mutations\BaseMutation;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\App;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Rebing\GraphQL\Error\ValidationError;
|
||||
|
||||
/**
|
||||
* @template TModel of \Illuminate\Database\Eloquent\Model
|
||||
@@ -37,8 +35,6 @@ abstract class BaseController
|
||||
*
|
||||
* @param class-string<BaseMutation> $mutation
|
||||
* @return array<string, mixed>
|
||||
*
|
||||
* @throws ValidationError
|
||||
*/
|
||||
public function validated(array $args, string $mutation): array
|
||||
{
|
||||
@@ -46,11 +42,7 @@ abstract class BaseController
|
||||
|
||||
$validator = Validator::make($args, $mutationInstance->rulesForValidation($args));
|
||||
|
||||
try {
|
||||
$validated = $validator->validated();
|
||||
} catch (ValidationException $e) {
|
||||
throw new ValidationError($e->getMessage(), $validator);
|
||||
}
|
||||
$validated = $validator->validated();
|
||||
|
||||
return [
|
||||
...$validated,
|
||||
|
||||
@@ -5,12 +5,12 @@ declare(strict_types=1);
|
||||
namespace App\GraphQL\Controllers\User;
|
||||
|
||||
use App\Contracts\Models\Likeable;
|
||||
use App\Exceptions\GraphQL\ClientValidationException;
|
||||
use App\GraphQL\Controllers\BaseController;
|
||||
use App\GraphQL\Schema\Mutations\Models\User\LikeMutation;
|
||||
use App\GraphQL\Schema\Mutations\Models\User\UnlikeMutation;
|
||||
use App\Models\User\Like;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
/**
|
||||
@@ -23,41 +23,27 @@ class LikeController extends BaseController
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $args
|
||||
*
|
||||
* @throws ClientValidationException
|
||||
*/
|
||||
public function store($root, array $args): Model&Likeable
|
||||
public function store($root, array $args): Model
|
||||
{
|
||||
$validated = $this->validated($args, LikeMutation::class);
|
||||
|
||||
foreach ($validated as $likeable) {
|
||||
if ($likeable instanceof Model && $likeable instanceof Likeable) {
|
||||
$likeable->like(Auth::user());
|
||||
/** @var Model&Likeable $likeable */
|
||||
$likeable = Arr::first($validated);
|
||||
|
||||
return $likeable;
|
||||
}
|
||||
}
|
||||
|
||||
throw new ClientValidationException('One resource is required to like.');
|
||||
return $likeable->like(Auth::user());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $args
|
||||
*
|
||||
* @throws ClientValidationException
|
||||
*/
|
||||
public function destroy($root, array $args): Model&Likeable
|
||||
public function destroy($root, array $args): Model
|
||||
{
|
||||
$validated = $this->validated($args, UnlikeMutation::class);
|
||||
|
||||
foreach ($validated as $likeable) {
|
||||
if ($likeable instanceof Model && $likeable instanceof Likeable) {
|
||||
$likeable->unlike(Auth::user());
|
||||
/** @var Model&Likeable $likeable */
|
||||
$likeable = Arr::first($validated);
|
||||
|
||||
return $likeable;
|
||||
}
|
||||
}
|
||||
|
||||
throw new ClientValidationException('One resource is required to unlike.');
|
||||
return $likeable->unlike(Auth::user());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ declare(strict_types=1);
|
||||
namespace App\GraphQL\Controllers\Wiki\Anime;
|
||||
|
||||
use App\Actions\GraphQL\IndexAction;
|
||||
use App\Exceptions\GraphQL\ClientValidationException;
|
||||
use App\GraphQL\Controllers\BaseController;
|
||||
use App\GraphQL\Schema\Fields\Wiki\Anime\AnimeYear\AnimeYearSeason\AnimeYearSeasonSeasonField;
|
||||
use App\GraphQL\Schema\Fields\Wiki\Anime\AnimeYear\AnimeYearSeasonField;
|
||||
@@ -19,6 +18,7 @@ use Illuminate\Contracts\Pagination\Paginator;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
|
||||
/**
|
||||
* @extends BaseController<Anime>
|
||||
@@ -35,11 +35,20 @@ class AnimeYearsController extends BaseController
|
||||
$fieldSelection = $resolveInfo->getFieldSelection(1);
|
||||
|
||||
// Restrict 'animes' field to a unique year.
|
||||
throw_if(
|
||||
($year === null || count($year) > 1) && (Arr::get($fieldSelection, 'season.anime') || Arr::get($fieldSelection, 'seasons.anime')),
|
||||
ClientValidationException::class,
|
||||
"Please provide a unique 'year' argument to query the animes field."
|
||||
);
|
||||
Validator::make(
|
||||
[
|
||||
'year' => $year,
|
||||
],
|
||||
[
|
||||
'year' => [
|
||||
function ($attribute, $value, $fail) use ($fieldSelection): void {
|
||||
if (($value === null || count($value) > 1) && (Arr::get($fieldSelection, 'season.anime') || Arr::get($fieldSelection, 'seasons.anime'))) {
|
||||
$fail('Exactly one year is required when requesting anime.');
|
||||
}
|
||||
},
|
||||
],
|
||||
]
|
||||
)->validate();
|
||||
|
||||
return Anime::query()
|
||||
->distinct([Anime::ATTRIBUTE_YEAR, Anime::ATTRIBUTE_SEASON])
|
||||
|
||||
@@ -5,12 +5,10 @@ declare(strict_types=1);
|
||||
namespace App\GraphQL\Filter;
|
||||
|
||||
use App\Enums\Http\Api\Filter\ComparisonOperator;
|
||||
use App\Exceptions\GraphQL\ClientValidationException;
|
||||
use App\GraphQL\Argument\Argument;
|
||||
use App\GraphQL\Argument\FilterArgument;
|
||||
use GraphQL\Type\Definition\Type;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
|
||||
abstract class Filter
|
||||
{
|
||||
@@ -63,14 +61,10 @@ abstract class Filter
|
||||
protected function validateFilterValues(array $filterValues): void
|
||||
{
|
||||
foreach ($filterValues as $filterValue) {
|
||||
try {
|
||||
Validator::make(
|
||||
[$this->fieldName => $filterValue],
|
||||
[$this->fieldName => $this->getRules()],
|
||||
)->validate();
|
||||
} catch (ValidationException $e) {
|
||||
throw new ClientValidationException($e->getMessage());
|
||||
}
|
||||
Validator::make(
|
||||
[$this->fieldName => $filterValue],
|
||||
[$this->fieldName => $this->getRules()],
|
||||
)->validate();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,11 +6,18 @@ namespace App\GraphQL\Handler;
|
||||
|
||||
use Error as PhpError;
|
||||
use Exception;
|
||||
use GraphQL\Error\DebugFlag;
|
||||
use GraphQL\Error\Error as GraphQLError;
|
||||
use GraphQL\Error\FormattedError;
|
||||
use GraphQL\Server\RequestError;
|
||||
use Illuminate\Contracts\Debug\ExceptionHandler;
|
||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Rebing\GraphQL\Error\AuthorizationError;
|
||||
use Rebing\GraphQL\Error\ProvidesErrorCategory;
|
||||
use Rebing\GraphQL\Error\ValidationError;
|
||||
use Throwable;
|
||||
|
||||
class ErrorHandler
|
||||
{
|
||||
@@ -51,4 +58,47 @@ class ErrorHandler
|
||||
|
||||
return array_map($formatter, $errors);
|
||||
}
|
||||
|
||||
// /**
|
||||
// * @return array<string,mixed>
|
||||
// * @see ExecutionResult::setErrorFormatter
|
||||
// */
|
||||
public static function formatError(GraphQLError $e): array
|
||||
{
|
||||
$debug = Config::get('app.debug') ? (DebugFlag::INCLUDE_DEBUG_MESSAGE | DebugFlag::INCLUDE_TRACE) : DebugFlag::NONE;
|
||||
$formatter = FormattedError::prepareFormatter(null, $debug);
|
||||
$error = $formatter($e);
|
||||
|
||||
$previous = $e->getPrevious();
|
||||
|
||||
if ($previous instanceof Throwable) {
|
||||
if ($previous instanceof ModelNotFoundException) {
|
||||
$error['message'] = $previous->getMessage();
|
||||
$error['extensions'] = [
|
||||
'category' => 'model_not_found',
|
||||
'model' => $previous->getModel(),
|
||||
];
|
||||
}
|
||||
|
||||
if ($previous instanceof ValidationException) {
|
||||
$error['message'] = 'validation';
|
||||
$error['extensions'] = [
|
||||
'category' => 'validation',
|
||||
'validation' => $previous->validator->errors()->getMessages(),
|
||||
];
|
||||
}
|
||||
|
||||
if ($previous instanceof ValidationError) {
|
||||
$error['extensions']['validation'] = $previous->getValidatorMessages()->getMessages();
|
||||
}
|
||||
|
||||
if ($previous instanceof ProvidesErrorCategory) {
|
||||
$error['extensions']['category'] = $previous->getCategory();
|
||||
}
|
||||
} elseif ($e instanceof ProvidesErrorCategory) {
|
||||
$error['extensions']['category'] = $e->getCategory();
|
||||
}
|
||||
|
||||
return $error;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,6 +48,9 @@ class LikeAnimeThemeEntryField extends Field implements BindableField, Creatable
|
||||
{
|
||||
return [
|
||||
Str::of('prohibits:')->append(LikeController::ATTRIBUTE_PLAYLIST)->__toString(),
|
||||
'required_without_all:'.implode(',', [
|
||||
LikeController::ATTRIBUTE_PLAYLIST,
|
||||
]),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -58,6 +61,9 @@ class LikeAnimeThemeEntryField extends Field implements BindableField, Creatable
|
||||
{
|
||||
return [
|
||||
Str::of('prohibits:')->append(LikeController::ATTRIBUTE_PLAYLIST)->__toString(),
|
||||
'required_without_all:'.implode(',', [
|
||||
LikeController::ATTRIBUTE_PLAYLIST,
|
||||
]),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,6 +48,9 @@ class LikePlaylistField extends Field implements BindableField, CreatableField,
|
||||
{
|
||||
return [
|
||||
Str::of('prohibits:')->append(LikeController::ATTRIBUTE_ENTRY)->__toString(),
|
||||
'required_without_all:'.implode(',', [
|
||||
LikeController::ATTRIBUTE_ENTRY,
|
||||
]),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -58,6 +61,9 @@ class LikePlaylistField extends Field implements BindableField, CreatableField,
|
||||
{
|
||||
return [
|
||||
Str::of('prohibits:')->append(LikeController::ATTRIBUTE_ENTRY)->__toString(),
|
||||
'required_without_all:'.implode(',', [
|
||||
LikeController::ATTRIBUTE_ENTRY,
|
||||
]),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,10 +10,11 @@ use App\GraphQL\Controllers\User\LikeController;
|
||||
use App\GraphQL\Schema\Fields\Field;
|
||||
use App\GraphQL\Schema\Mutations\BaseMutation;
|
||||
use App\GraphQL\Schema\Types\User\LikeType;
|
||||
use App\GraphQL\Schema\Unions\LikedUnion;
|
||||
use App\Models\User\Like;
|
||||
use GraphQL\Type\Definition\ResolveInfo;
|
||||
use GraphQL\Type\Definition\Type;
|
||||
use Illuminate\Support\Facades\App;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
|
||||
class LikeMutation extends BaseMutation
|
||||
{
|
||||
@@ -27,6 +28,11 @@ class LikeMutation extends BaseMutation
|
||||
return 'Like a model';
|
||||
}
|
||||
|
||||
public function authorize($root, array $args, $ctx, ?ResolveInfo $resolveInfo = null, $selectFields = null): bool
|
||||
{
|
||||
return ($this->response = Gate::inspect('create', [Like::class, ...[]]))->allowed();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the arguments for the like mutation.
|
||||
*
|
||||
@@ -56,9 +62,9 @@ class LikeMutation extends BaseMutation
|
||||
/**
|
||||
* The base return type of the mutation.
|
||||
*/
|
||||
public function baseType(): LikedUnion
|
||||
public function baseType(): LikeType
|
||||
{
|
||||
return new LikedUnion();
|
||||
return new LikeType();
|
||||
}
|
||||
|
||||
public function type(): Type
|
||||
|
||||
@@ -10,10 +10,11 @@ use App\GraphQL\Controllers\User\LikeController;
|
||||
use App\GraphQL\Schema\Fields\Field;
|
||||
use App\GraphQL\Schema\Mutations\BaseMutation;
|
||||
use App\GraphQL\Schema\Types\User\LikeType;
|
||||
use App\GraphQL\Schema\Unions\LikedUnion;
|
||||
use App\Models\User\Like;
|
||||
use GraphQL\Type\Definition\ResolveInfo;
|
||||
use GraphQL\Type\Definition\Type;
|
||||
use Illuminate\Support\Facades\App;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
|
||||
class UnlikeMutation extends BaseMutation
|
||||
{
|
||||
@@ -27,6 +28,11 @@ class UnlikeMutation extends BaseMutation
|
||||
return 'Unlike a model';
|
||||
}
|
||||
|
||||
public function authorize($root, array $args, $ctx, ?ResolveInfo $resolveInfo = null, $selectFields = null): bool
|
||||
{
|
||||
return ($this->response = Gate::inspect('delete', [Like::class, ...[]]))->allowed();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the arguments for the unlike mutation.
|
||||
*
|
||||
@@ -56,9 +62,9 @@ class UnlikeMutation extends BaseMutation
|
||||
/**
|
||||
* The base return type of the mutation.
|
||||
*/
|
||||
public function baseType(): LikedUnion
|
||||
public function baseType(): LikeType
|
||||
{
|
||||
return new LikedUnion();
|
||||
return new LikeType();
|
||||
}
|
||||
|
||||
public function type(): Type
|
||||
|
||||
@@ -8,8 +8,8 @@ use App\GraphQL\Argument\Argument;
|
||||
use App\GraphQL\Schema\Queries\BaseQuery;
|
||||
use App\GraphQL\Schema\Types\Admin\FeaturedThemeType;
|
||||
use App\Models\Admin\FeaturedTheme;
|
||||
use Closure;
|
||||
use GraphQL\Type\Definition\ResolveInfo;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Facades\Date;
|
||||
|
||||
class CurrentFeaturedThemeQuery extends BaseQuery
|
||||
@@ -48,7 +48,7 @@ class CurrentFeaturedThemeQuery extends BaseQuery
|
||||
* @param array<string, mixed> $args
|
||||
* @return FeaturedTheme|null
|
||||
*/
|
||||
public function resolve($root, array $args, $context, ResolveInfo $resolveInfo, Closure $getSelectFields)
|
||||
public function resolve($root, array $args, $context, ResolveInfo $resolveInfo): ?Model
|
||||
{
|
||||
$builder = FeaturedTheme::query();
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ declare(strict_types=1);
|
||||
namespace App\GraphQL\Schema\Queries\Wiki;
|
||||
|
||||
use App\Enums\Models\Wiki\ResourceSite;
|
||||
use App\Exceptions\GraphQL\ClientValidationException;
|
||||
use App\GraphQL\Argument\Argument;
|
||||
use App\GraphQL\Schema\Queries\BaseQuery;
|
||||
use App\GraphQL\Schema\Types\Wiki\AnimeType;
|
||||
@@ -18,6 +17,8 @@ use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Illuminate\Validation\Rules\Enum;
|
||||
use Rebing\GraphQL\Support\Facades\GraphQL;
|
||||
|
||||
class FindAnimeByExternalSiteQuery extends BaseQuery
|
||||
@@ -71,16 +72,18 @@ class FindAnimeByExternalSiteQuery extends BaseQuery
|
||||
*/
|
||||
public function resolve($root, array $args, $context, ResolveInfo $resolveInfo)
|
||||
{
|
||||
Validator::make($args, [
|
||||
self::ATTRIBUTE_SITE => ['required', new Enum(ResourceSite::class)],
|
||||
self::ATTRIBUTE_ID => ['required_without:link', 'max:100'],
|
||||
self::ATTRIBUTE_LINK => ['required_without:id'],
|
||||
])->validate();
|
||||
|
||||
$builder = Anime::query();
|
||||
|
||||
$site = Arr::get($args, self::ATTRIBUTE_SITE);
|
||||
$externalId = Arr::get($args, self::ATTRIBUTE_ID);
|
||||
$link = Arr::get($args, self::ATTRIBUTE_LINK);
|
||||
|
||||
throw_if(is_null($externalId) && is_null($link), ClientValidationException::class, 'At least "id" or "link" is required.');
|
||||
|
||||
throw_if($externalId !== null && count($externalId) > 100, ClientValidationException::class, 'The "Id" parameter cannot contain more than 100 integer values.');
|
||||
|
||||
$builder->whereRelation(Anime::RELATION_RESOURCES, function (Builder $query) use ($site, $externalId, $link): void {
|
||||
$query->where(ExternalResource::ATTRIBUTE_SITE, $site);
|
||||
|
||||
|
||||
@@ -128,7 +128,7 @@ abstract class Relation
|
||||
/** @var Collection $collection */
|
||||
$collection = $root->{$this->getRelationName()};
|
||||
|
||||
$first = Arr::get($args, 'first');
|
||||
$first = max(1, Arr::integer($args, 'first'));
|
||||
$page = Arr::get($args, 'page');
|
||||
|
||||
return new LengthAwarePaginator(
|
||||
|
||||
@@ -5,6 +5,7 @@ declare(strict_types=1);
|
||||
namespace App\GraphQL\Schema\Types;
|
||||
|
||||
use App\Contracts\GraphQL\Fields\DeprecatedField;
|
||||
use App\Contracts\GraphQL\Fields\DisplayableField;
|
||||
use App\GraphQL\Schema\Fields\Field;
|
||||
use App\GraphQL\Schema\Relations\Relation;
|
||||
use Illuminate\Support\Str;
|
||||
@@ -77,6 +78,7 @@ abstract class BaseType extends RebingType
|
||||
]);
|
||||
|
||||
$fields = collect($this->fieldClasses())
|
||||
->filter(fn (Field $field): bool => $field instanceof DisplayableField && $field->canBeDisplayed())
|
||||
->mapWithKeys(fn (Field $field): array => [
|
||||
$field->getName() => [
|
||||
'type' => $field->type(),
|
||||
|
||||
@@ -12,6 +12,13 @@ use Illuminate\Auth\Access\Response;
|
||||
|
||||
class LikePolicy extends BasePolicy
|
||||
{
|
||||
public function create(User $user): Response
|
||||
{
|
||||
return $user->can(CrudPermission::DELETE->format(Like::class))
|
||||
? Response::allow()
|
||||
: Response::deny();
|
||||
}
|
||||
|
||||
public function delete(User $user, mixed $value): Response
|
||||
{
|
||||
return $user->can(CrudPermission::DELETE->format(Like::class))
|
||||
|
||||
+1
-1
@@ -89,7 +89,7 @@ return [
|
||||
// 'message' => '',
|
||||
// 'locations' => []
|
||||
// ]
|
||||
'error_formatter' => [Rebing\GraphQL\GraphQL::class, 'formatError'],
|
||||
'error_formatter' => [App\GraphQL\Handler\ErrorHandler::class, 'formatError'],
|
||||
|
||||
/*
|
||||
* Custom Error Handling
|
||||
|
||||
Reference in New Issue
Block a user