mirror of
https://github.com/jikan-me/jikan-rest.git
synced 2025-02-20 11:23:35 +08:00
fixed tests part1
This commit is contained in:
parent
1e302fb62c
commit
5e922f7cd9
@ -1,7 +1,7 @@
|
||||
[](#jikan-rest-api-v4---unofficial-myanimelistnet-rest-api)
|
||||
|
||||
# Jikan REST API v4 - Unofficial MyAnimeList.net REST API
|
||||
[](http://isitmaintained.com/project/jikan-me/jikan-rest "Average time to resolve an issue") [](http://isitmaintained.com/project/jikan-me/jikan-rest "Percentage of issues still open") []() [](https://discordapp.com/invite/4tvCr36)
|
||||
[](http://isitmaintained.com/project/jikan-me/jikan-rest "Average time to resolve an issue") [](http://isitmaintained.com/project/jikan-me/jikan-rest "Percentage of issues still open") []() [](https://discordapp.com/invite/4tvCr36)
|
||||
|
||||
Jikan is a REST API for [MyAnimeList.net](https://myanimelist.net). It scrapes the website to satisfy the need for API functionality that MyAnimeList.net lacks.
|
||||
|
||||
@ -25,7 +25,7 @@ Please read the [manual installation guide](https://github.com/jikan-me/jikan-re
|
||||
For any additional help, join our [Discord server](http://discord.jikan.moe/).
|
||||
|
||||
### 🐳 Docker Installation
|
||||
We distribute the app as a container image so you can just run it:
|
||||
We distribute the app as a container image, so you can just run it:
|
||||
```bash
|
||||
docker run -d --name=jikan-rest -p 8080:8080 -v ./.env:/app/.env jikanme/jikan-rest:latest
|
||||
```
|
||||
|
@ -18,7 +18,7 @@ class Anime extends JikanApiSearchableModel
|
||||
{
|
||||
use HasFactory, MediaFilters, FilteredByLetter;
|
||||
|
||||
protected array $filters = ["order_by", "status", "type", "sort", "max_score", "min_score", "score", "rating", "start_date", "end_date", "producer", "producers", "letter"];
|
||||
protected array $filters = ["order_by", "status", "type", "sort", "max_score", "min_score", "score", "rating", "start_date", "end_date", "producer", "producers", "letter", "genres", "genres_exclude"];
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
@ -133,12 +133,6 @@ class Anime extends JikanApiSearchableModel
|
||||
];
|
||||
}
|
||||
|
||||
/** @noinspection PhpUnused */
|
||||
public function filterByLetter(\Laravel\Scout\Builder|\Illuminate\Database\Eloquent\Builder $query, string $value): \Laravel\Scout\Builder|\Illuminate\Database\Eloquent\Builder
|
||||
{
|
||||
return $query->where("title", "like", "{$value}%");
|
||||
}
|
||||
|
||||
/** @noinspection PhpUnused */
|
||||
public function filterByType(\Laravel\Scout\Builder|\Illuminate\Database\Eloquent\Builder $query, AnimeTypeEnum $value): \Laravel\Scout\Builder|\Illuminate\Database\Eloquent\Builder
|
||||
{
|
||||
@ -152,15 +146,15 @@ class Anime extends JikanApiSearchableModel
|
||||
}
|
||||
|
||||
/** @noinspection PhpUnused */
|
||||
public function filterByStartDate(\Laravel\Scout\Builder|\Illuminate\Database\Eloquent\Builder $query, CarbonImmutable $date): \Laravel\Scout\Builder|\Illuminate\Database\Eloquent\Builder
|
||||
public function filterByStartDate(\Laravel\Scout\Builder|\Illuminate\Database\Eloquent\Builder $query, CarbonImmutable $value): \Laravel\Scout\Builder|\Illuminate\Database\Eloquent\Builder
|
||||
{
|
||||
return $query->where("aired.from", $date->setTime(0, 0)->toAtomString());
|
||||
return $query->where("aired.from", ">=", $value->setTime(0, 0)->toAtomString());
|
||||
}
|
||||
|
||||
/** @noinspection PhpUnused */
|
||||
public function filterByEndDate(\Laravel\Scout\Builder|\Illuminate\Database\Eloquent\Builder $query, CarbonImmutable $date): \Laravel\Scout\Builder|\Illuminate\Database\Eloquent\Builder
|
||||
public function filterByEndDate(\Laravel\Scout\Builder|\Illuminate\Database\Eloquent\Builder $query, CarbonImmutable $value): \Laravel\Scout\Builder|\Illuminate\Database\Eloquent\Builder
|
||||
{
|
||||
return $query->where("aired.to", $date->setTime(0, 0)->toAtomString());
|
||||
return $query->where("aired.to", "<=", $value->setTime(0, 0)->toAtomString());
|
||||
}
|
||||
|
||||
public function filterByProducer(\Laravel\Scout\Builder|\Illuminate\Database\Eloquent\Builder $query, string $value): \Laravel\Scout\Builder|\Illuminate\Database\Eloquent\Builder
|
||||
|
@ -8,11 +8,21 @@ trait MediaFilters
|
||||
{
|
||||
public function filterByMaxScore(\Laravel\Scout\Builder|\Illuminate\Database\Eloquent\Builder $query, mixed $value): \Laravel\Scout\Builder|\Illuminate\Database\Eloquent\Builder
|
||||
{
|
||||
// if the client specifies the "max" possible value, ignore it, in that case they want everything included
|
||||
// https://github.com/jikan-me/jikan-rest/issues/309
|
||||
if (floatval($value) == 10) {
|
||||
return $query;
|
||||
}
|
||||
return $query->where("score", "<=", floatval($value));
|
||||
}
|
||||
|
||||
public function filterByMinScore(\Laravel\Scout\Builder|\Illuminate\Database\Eloquent\Builder $query, mixed $value): \Laravel\Scout\Builder|\Illuminate\Database\Eloquent\Builder
|
||||
{
|
||||
// if the client specifies the "max" possible value, ignore it, in that case they want everything included
|
||||
// https://github.com/jikan-me/jikan-rest/issues/309
|
||||
if (floatval($value) == 0) {
|
||||
return $query;
|
||||
}
|
||||
return $query->where("score", ">=", floatval($value));
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,6 @@ trait ResolvesPaginatorParams
|
||||
$limit = $limit ?? $default_max_results_per_page;
|
||||
$page = $page ?? 1;
|
||||
|
||||
return compact($limit, $page);
|
||||
return compact("limit", "page");
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ use App\Enums\AnimeOrderByEnum;
|
||||
use App\Enums\AnimeRatingEnum;
|
||||
use App\Enums\AnimeStatusEnum;
|
||||
use App\Http\Resources\V4\AnimeCollection;
|
||||
use App\Rules\Attributes\EnumValidation;
|
||||
use Spatie\Enum\Laravel\Rules\EnumRule;
|
||||
use Spatie\LaravelData\Attributes\MapInputName;
|
||||
use Spatie\LaravelData\Attributes\MapOutputName;
|
||||
@ -23,10 +24,10 @@ use Spatie\LaravelData\Optional;
|
||||
*/
|
||||
final class AnimeSearchCommand extends MediaSearchCommand implements DataRequest
|
||||
{
|
||||
#[WithCast(EnumCast::class, AnimeStatusEnum::class)]
|
||||
#[WithCast(EnumCast::class, AnimeStatusEnum::class), EnumValidation(AnimeStatusEnum::class)]
|
||||
public AnimeStatusEnum|Optional $status;
|
||||
|
||||
#[WithCast(EnumCast::class, AnimeRatingEnum::class)]
|
||||
#[WithCast(EnumCast::class, AnimeRatingEnum::class), EnumValidation(AnimeRatingEnum::class)]
|
||||
public AnimeRatingEnum|Optional $rating;
|
||||
|
||||
#[IntegerType, Min(1)]
|
||||
@ -35,16 +36,11 @@ final class AnimeSearchCommand extends MediaSearchCommand implements DataRequest
|
||||
#[Prohibits("producer"), StringType]
|
||||
public string|Optional $producers;
|
||||
|
||||
#[MapInputName("order_by"), MapOutputName("order_by"), WithCast(EnumCast::class, AnimeOrderByEnum::class)]
|
||||
#[
|
||||
MapInputName("order_by"),
|
||||
MapOutputName("order_by"),
|
||||
WithCast(EnumCast::class, AnimeOrderByEnum::class),
|
||||
EnumValidation(AnimeOrderByEnum::class)
|
||||
]
|
||||
public AnimeOrderByEnum|Optional $orderBy;
|
||||
|
||||
public static function rules(): array
|
||||
{
|
||||
return [
|
||||
...parent::rules(),
|
||||
"status" => [new EnumRule(AnimeStatusEnum::class)],
|
||||
"rating" => [new EnumRule(AnimeRatingEnum::class)],
|
||||
"order_by" => [new EnumRule(AnimeOrderByEnum::class)]
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ use App\Contracts\DataRequest;
|
||||
use App\Enums\MangaOrderByEnum;
|
||||
use App\Enums\MangaStatusEnum;
|
||||
use App\Http\Resources\V4\MangaCollection;
|
||||
use App\Rules\Attributes\EnumValidation;
|
||||
use Spatie\Enum\Laravel\Rules\EnumRule;
|
||||
use Spatie\LaravelData\Attributes\MapInputName;
|
||||
use Spatie\LaravelData\Attributes\MapOutputName;
|
||||
@ -19,21 +20,17 @@ use Spatie\LaravelData\Optional;
|
||||
*/
|
||||
final class MangaSearchCommand extends MediaSearchCommand implements DataRequest
|
||||
{
|
||||
#[WithCast(EnumCast::class, MangaStatusEnum::class)]
|
||||
#[WithCast(EnumCast::class, MangaStatusEnum::class), EnumValidation(MangaStatusEnum::class)]
|
||||
public MangaStatusEnum|Optional $status;
|
||||
|
||||
#[StringType]
|
||||
public string|Optional $magazines;
|
||||
|
||||
#[MapInputName("order_by"), MapOutputName("order_by"), WithCast(EnumCast::class, MangaOrderByEnum::class)]
|
||||
#[
|
||||
MapInputName("order_by"),
|
||||
MapOutputName("order_by"),
|
||||
EnumValidation(MangaOrderByEnum::class),
|
||||
WithCast(EnumCast::class, MangaOrderByEnum::class)
|
||||
]
|
||||
public MangaOrderByEnum|Optional $orderBy;
|
||||
|
||||
public static function rules(): array
|
||||
{
|
||||
return [
|
||||
...parent::rules(),
|
||||
"status" => new EnumRule(MangaStatusEnum::class),
|
||||
"order_by" => new EnumRule(MangaOrderByEnum::class)
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -3,16 +3,17 @@
|
||||
namespace App\Dto;
|
||||
|
||||
use Carbon\CarbonImmutable;
|
||||
use Illuminate\Validation\Validator;
|
||||
use Spatie\LaravelData\Attributes\MapInputName;
|
||||
use Spatie\LaravelData\Attributes\MapOutputName;
|
||||
use Spatie\LaravelData\Attributes\Validation\AfterOrEqual;
|
||||
use Spatie\LaravelData\Attributes\Validation\BeforeOrEqual;
|
||||
use Spatie\LaravelData\Attributes\Validation\Between;
|
||||
use Spatie\LaravelData\Attributes\Validation\DateFormat;
|
||||
use Spatie\LaravelData\Attributes\Validation\GreaterThanOrEqualTo;
|
||||
use Spatie\LaravelData\Attributes\Validation\LessThanOrEqualTo;
|
||||
use Spatie\LaravelData\Attributes\Validation\Numeric;
|
||||
use Spatie\LaravelData\Attributes\Validation\Prohibits;
|
||||
use Spatie\LaravelData\Attributes\Validation\Required;
|
||||
use Spatie\LaravelData\Attributes\Validation\Sometimes;
|
||||
use Spatie\LaravelData\Attributes\WithCast;
|
||||
use Spatie\LaravelData\Attributes\WithTransformer;
|
||||
use Spatie\LaravelData\Casts\DateTimeInterfaceCast;
|
||||
@ -21,10 +22,10 @@ use Spatie\LaravelData\Transformers\DateTimeInterfaceTransformer;
|
||||
|
||||
class MediaSearchCommand extends SearchCommand
|
||||
{
|
||||
#[MapInputName("min_score"), MapOutputName("min_score"), Between(1.00, 9.99), Numeric]
|
||||
#[MapInputName("min_score"), MapOutputName("min_score"), Between(0.00, 10.00), Numeric]
|
||||
public float|Optional $minScore;
|
||||
|
||||
#[MapInputName("max_score"), MapOutputName("max_score"), Between(1.00, 9.99), Numeric]
|
||||
#[MapInputName("max_score"), MapOutputName("max_score"), Between(1.00, 10.00), Numeric]
|
||||
public float|Optional $maxScore;
|
||||
|
||||
#[Between(1.00, 9.99), Numeric, Prohibits(["min_score", "max_score"])]
|
||||
@ -37,11 +38,29 @@ class MediaSearchCommand extends SearchCommand
|
||||
#[MapInputName("genres_exclude"), MapOutputName("genres_exclude")]
|
||||
public string|Optional $genresExclude;
|
||||
|
||||
#[WithCast(DateTimeInterfaceCast::class), WithTransformer(DateTimeInterfaceTransformer::class)]
|
||||
#[BeforeOrEqual("end_date"), DateFormat("Y-m-d")]
|
||||
#[
|
||||
BeforeOrEqual("end_date"),
|
||||
DateFormat("Y-m-d"),
|
||||
Sometimes,
|
||||
Required,
|
||||
WithCast(DateTimeInterfaceCast::class),
|
||||
WithTransformer(DateTimeInterfaceTransformer::class)
|
||||
]
|
||||
public CarbonImmutable|Optional $start_date;
|
||||
|
||||
#[WithCast(DateTimeInterfaceCast::class), WithTransformer(DateTimeInterfaceTransformer::class)]
|
||||
#[AfterOrEqual("start_date"), DateFormat("Y-m-d")]
|
||||
#[
|
||||
AfterOrEqual("start_date"),
|
||||
DateFormat("Y-m-d"),
|
||||
Sometimes,
|
||||
Required,
|
||||
WithCast(DateTimeInterfaceCast::class),
|
||||
WithTransformer(DateTimeInterfaceTransformer::class)
|
||||
]
|
||||
public CarbonImmutable|Optional $end_date;
|
||||
|
||||
public static function withValidator(Validator $validator): void
|
||||
{
|
||||
$validator->sometimes("min_score", "lte:max_score", fn ($input) => !empty($input->max_score));
|
||||
$validator->sometimes("max_score", "gte:min_score", fn ($input) => !empty($input->min_score));
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ use App\Casts\EnumCast;
|
||||
use App\Dto\Concerns\HasLimitParameter;
|
||||
use App\Dto\Concerns\HasPageParameter;
|
||||
use App\Enums\SortDirection;
|
||||
use App\Rules\Attributes\EnumValidation;
|
||||
use Spatie\Enum\Laravel\Rules\EnumRule;
|
||||
use Spatie\LaravelData\Attributes\Validation\Alpha;
|
||||
use Spatie\LaravelData\Attributes\Validation\Max;
|
||||
@ -26,16 +27,9 @@ class SearchCommand extends Data
|
||||
#[Max(255), StringType]
|
||||
public string|Optional $q;
|
||||
|
||||
#[WithCast(EnumCast::class, SortDirection::class)]
|
||||
#[WithCast(EnumCast::class, SortDirection::class), EnumValidation(SortDirection::class)]
|
||||
public SortDirection|Optional $sort;
|
||||
|
||||
#[Size(1), StringType, Alpha]
|
||||
public string|Optional $letter;
|
||||
|
||||
public static function rules(): array
|
||||
{
|
||||
return [
|
||||
"sort" => [new EnumRule(SortDirection::class)]
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ use App\Concerns\HasRequestFingerprint;
|
||||
use App\Contracts\DataRequest;
|
||||
use App\Enums\GenderEnum;
|
||||
use App\Http\Resources\V4\UserCollection;
|
||||
use App\Rules\Attributes\EnumValidation;
|
||||
use Spatie\Enum\Laravel\Rules\EnumRule;
|
||||
use Spatie\LaravelData\Attributes\Validation\StringType;
|
||||
use Spatie\LaravelData\Attributes\WithCast;
|
||||
@ -23,17 +24,9 @@ final class UsersSearchCommand extends SearchCommand implements DataRequest
|
||||
|
||||
public int|Optional $maxAge;
|
||||
|
||||
#[WithCast(EnumCast::class, GenderEnum::class)]
|
||||
#[WithCast(EnumCast::class, GenderEnum::class), EnumValidation(GenderEnum::class)]
|
||||
public GenderEnum|Optional $gender;
|
||||
|
||||
#[StringType]
|
||||
public string|Optional $location;
|
||||
|
||||
public static function rules(): array
|
||||
{
|
||||
return [
|
||||
...parent::rules(),
|
||||
"gender" => [new EnumRule(GenderEnum::class)]
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -115,6 +115,7 @@ class GithubReport
|
||||
"Please fill out the details below.\n\n**Summary:**\n\n**Steps to reproduce:**\n\n\n\n ### Additional Details \n **Jikan Parser Version**: ```{$this->jikanVersion}```\n**PHP:** ```{$this->phpVersion}```\n**Redis**: ```{$this->redisRunning}```\n**Exception:** ```{$this->name}```\n**Code:** ```{$this->code}```\n**Message:** ```{$this->error}```\n**Trace:** ```{$this->trace}```\n**Request:** `{$this->requestMethod} {$this->requestUri}`\n"
|
||||
);
|
||||
|
||||
// https://github.com/jikan-me/jikan-rest/issues/new?assignees=&labels=i%3A+bug%2C+i%3A+needs+triage&template=bug.md&title=
|
||||
return "https://github.com/{$this->repo}/issues/new?title={$title}&body={$body}";
|
||||
}
|
||||
|
||||
|
@ -113,9 +113,9 @@ class Handler extends ExceptionHandler
|
||||
return response()
|
||||
->json([
|
||||
'status' => 400,
|
||||
'type' => 'BadRequestException',
|
||||
'message' => $e->getMessage(),
|
||||
'error' => null
|
||||
'type' => 'ValidationException',
|
||||
'messages' => $e->validator->getMessageBag()->getMessages(),
|
||||
'error' => 'Invalid or incomplete request. Make sure your request is correct. https://docs.api.jikan.moe/'
|
||||
], 400);
|
||||
}
|
||||
|
||||
|
@ -18,6 +18,10 @@ trait FilterResolver
|
||||
|
||||
private function resolveCustomFilter($filterName, $values)
|
||||
{
|
||||
$filterMethodName = "filterBy" . ucfirst(Str::camel($filterName));
|
||||
if (method_exists($this, $filterMethodName)) {
|
||||
$filterName = $filterMethodName;
|
||||
}
|
||||
return $this->getClosure($this->makeCallable($filterName), $values);
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,7 @@ class Manga extends JikanApiSearchableModel
|
||||
{
|
||||
use HasFactory, MediaFilters, FilteredByLetter;
|
||||
|
||||
protected array $filters = ["order_by", "status", "type", "sort", "max_score", "min_score", "score", "start_date", "end_date", "magazine", "magazines", "letter"];
|
||||
protected array $filters = ["order_by", "status", "type", "sort", "max_score", "min_score", "score", "start_date", "end_date", "magazine", "magazines", "letter", "genres", "genres_exclude"];
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
@ -58,15 +58,15 @@ class Manga extends JikanApiSearchableModel
|
||||
}
|
||||
|
||||
/** @noinspection PhpUnused */
|
||||
public function filterByStartDate(\Laravel\Scout\Builder|\Illuminate\Database\Eloquent\Builder $query, CarbonImmutable $date): \Laravel\Scout\Builder|\Illuminate\Database\Eloquent\Builder
|
||||
public function filterByStartDate(\Laravel\Scout\Builder|\Illuminate\Database\Eloquent\Builder $query, CarbonImmutable $value): \Laravel\Scout\Builder|\Illuminate\Database\Eloquent\Builder
|
||||
{
|
||||
return $query->where("published.from", $date->setTime(0, 0)->toAtomString());
|
||||
return $query->where("published.from", ">=", $value->setTime(0, 0)->toAtomString());
|
||||
}
|
||||
|
||||
/** @noinspection PhpUnused */
|
||||
public function filterByEndDate(\Laravel\Scout\Builder|\Illuminate\Database\Eloquent\Builder $query, CarbonImmutable $date): \Laravel\Scout\Builder|\Illuminate\Database\Eloquent\Builder
|
||||
public function filterByEndDate(\Laravel\Scout\Builder|\Illuminate\Database\Eloquent\Builder $query, CarbonImmutable $value): \Laravel\Scout\Builder|\Illuminate\Database\Eloquent\Builder
|
||||
{
|
||||
return $query->where("published.to", $date->setTime(0, 0)->toAtomString());
|
||||
return $query->where("published.to", "<=", $value->setTime(0, 0)->toAtomString());
|
||||
}
|
||||
|
||||
public function filterByMagazine(\Laravel\Scout\Builder|\Illuminate\Database\Eloquent\Builder $query, string $value): \Laravel\Scout\Builder|\Illuminate\Database\Eloquent\Builder
|
||||
|
@ -15,7 +15,6 @@ use App\Contracts\Repository;
|
||||
use App\Contracts\RequestHandler;
|
||||
use App\Contracts\UnitOfWork;
|
||||
use App\Contracts\UserRepository;
|
||||
use App\Http\Middleware\EndpointCacheTtlMiddleware;
|
||||
use App\Macros\CollectionOffsetGetFirst;
|
||||
use App\Macros\ResponseJikanCacheFlags;
|
||||
use App\Macros\To2dArrayWithDottedKeys;
|
||||
@ -30,6 +29,7 @@ use App\Repositories\DefaultPeopleRepository;
|
||||
use App\Repositories\DefaultProducerRepository;
|
||||
use App\Repositories\DefaultUserRepository;
|
||||
use App\Repositories\MangaGenresRepository;
|
||||
use App\Services\DefaultBuilderPaginatorService;
|
||||
use App\Services\DefaultCachedScraperService;
|
||||
use App\Services\DefaultQueryBuilderService;
|
||||
use App\Services\DefaultScoutSearchService;
|
||||
@ -82,11 +82,7 @@ class AppServiceProvider extends ServiceProvider
|
||||
// cache options class is used to share the request scope level cache settings
|
||||
$this->app->singleton(CacheOptions::class);
|
||||
$this->app->singleton(CachedScraperService::class, DefaultCachedScraperService::class);
|
||||
if ($this->getSearchIndexesEnabledConfig($this->app)) {
|
||||
$this->app->bind(QueryBuilderPaginatorService::class, ScoutBuilderPaginatorService::class);
|
||||
} else {
|
||||
$this->app->bind(QueryBuilderPaginatorService::class, EloquentBuilderPaginatorService::class);
|
||||
}
|
||||
$this->app->bind(QueryBuilderPaginatorService::class, DefaultBuilderPaginatorService::class);
|
||||
$this->registerModelRepositories();
|
||||
$this->registerRequestHandlers();
|
||||
}
|
||||
|
16
app/Rules/Attributes/EnumValidation.php
Normal file
16
app/Rules/Attributes/EnumValidation.php
Normal file
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace App\Rules\Attributes;
|
||||
|
||||
use Attribute;
|
||||
use Spatie\Enum\Laravel\Rules\EnumRule;
|
||||
use Spatie\LaravelData\Attributes\Validation\Rule;
|
||||
|
||||
#[Attribute(Attribute::TARGET_PROPERTY)]
|
||||
final class EnumValidation extends Rule
|
||||
{
|
||||
public function __construct(string $enumClass)
|
||||
{
|
||||
parent::__construct(new EnumRule($enumClass));
|
||||
}
|
||||
}
|
23
app/Services/DefaultBuilderPaginatorService.php
Normal file
23
app/Services/DefaultBuilderPaginatorService.php
Normal file
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
|
||||
|
||||
final class DefaultBuilderPaginatorService implements QueryBuilderPaginatorService
|
||||
{
|
||||
public function __construct(
|
||||
private readonly EloquentBuilderPaginatorService $eloquentBuilderPaginatorService,
|
||||
private readonly ScoutBuilderPaginatorService $scoutBuilderPaginatorService)
|
||||
{
|
||||
}
|
||||
|
||||
public function paginate(\Illuminate\Database\Eloquent\Builder|\Laravel\Scout\Builder $builder, ?int $limit = null, ?int $page = null): LengthAwarePaginator
|
||||
{
|
||||
if ($builder instanceof \Laravel\Scout\Builder) {
|
||||
return $this->scoutBuilderPaginatorService->paginate($builder, $limit, $page);
|
||||
}
|
||||
|
||||
return $this->eloquentBuilderPaginatorService->paginate($builder, $limit, $page);
|
||||
}
|
||||
}
|
@ -5,7 +5,7 @@ namespace App\Services;
|
||||
use App\Concerns\ResolvesPaginatorParams;
|
||||
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
|
||||
|
||||
class ScoutBuilderPaginatorService implements QueryBuilderPaginatorService
|
||||
final class ScoutBuilderPaginatorService implements QueryBuilderPaginatorService
|
||||
{
|
||||
use ResolvesPaginatorParams;
|
||||
|
||||
|
@ -3,7 +3,9 @@
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\CarbonDateRange;
|
||||
use App\Http\QueryBuilder\AnimeSearchQueryBuilder;
|
||||
use App\Enums\AnimeRatingEnum;
|
||||
use App\Enums\AnimeTypeEnum;
|
||||
use App\Enums\MangaTypeEnum;
|
||||
use App\Testing\JikanDataGenerator;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Carbon;
|
||||
@ -88,7 +90,7 @@ abstract class JikanMediaModelFactory extends JikanModelFactory implements Media
|
||||
return fn($i) => $randomDate->copy()->addDays($i);
|
||||
})()),
|
||||
"rating" => ((function() {
|
||||
$validRatingItems = array_values(AnimeSearchQueryBuilder::MAP_RATING);
|
||||
$validRatingItems = AnimeRatingEnum::toValues();
|
||||
$validRatingItemsCount = count($validRatingItems);
|
||||
return fn($i) => $validRatingItems[$i % $validRatingItemsCount];
|
||||
})()),
|
||||
@ -98,7 +100,7 @@ abstract class JikanMediaModelFactory extends JikanModelFactory implements Media
|
||||
return fn($i) => $alphabet[$i % $alphabetCount];
|
||||
})()),
|
||||
"type" => ((function() {
|
||||
$types = array_values(AnimeSearchQueryBuilder::MAP_TYPES);
|
||||
$types = $this->modelName() === "App\Anime" ? AnimeTypeEnum::toValues() : MangaTypeEnum::toValues();
|
||||
$typesCount = count($types);
|
||||
return fn($i) => $types[$i % $typesCount];
|
||||
})()),
|
||||
|
@ -53,11 +53,19 @@ class AnimeSearchEndpointTest extends TestCase
|
||||
];
|
||||
}
|
||||
|
||||
public function partialDatesParameterProvider(): array
|
||||
{
|
||||
return [
|
||||
[["start_date" => "2012"]],
|
||||
[["start_date" => "2012-05"]],
|
||||
[["end_date" => "2015"]],
|
||||
[["end_date" => "2015-05"]]
|
||||
];
|
||||
}
|
||||
|
||||
public function startDatesParameterProvider(): array
|
||||
{
|
||||
return [
|
||||
[["start_date" => "2022"]],
|
||||
[["start_date" => "2012-05"]],
|
||||
[["start_date" => "2012-05-12"]],
|
||||
[["start_date" => "2012-04-01"]],
|
||||
[["start_date" => "2012-04-28"]],
|
||||
@ -68,8 +76,6 @@ class AnimeSearchEndpointTest extends TestCase
|
||||
public function endDatesParameterProvider(): array
|
||||
{
|
||||
return [
|
||||
[["end_date" => "2022"]],
|
||||
[["end_date" => "2012-05"]],
|
||||
[["end_date" => "2012-05-12"]],
|
||||
[["end_date" => "2012-05-12", "page" => 1]],
|
||||
];
|
||||
@ -78,8 +84,6 @@ class AnimeSearchEndpointTest extends TestCase
|
||||
public function startAndEndDatesParameterProvider(): array
|
||||
{
|
||||
return [
|
||||
[["start_date" => "2021", "end_date" => "2022"]],
|
||||
[["start_date" => "2021-01", "end_date" => "2021-02"]],
|
||||
[["start_date" => "2021-01-01", "end_date" => "2021-03-22"]],
|
||||
[["start_date" => "2021-01-01", "end_date" => "2021-03-22", "page" => 1]],
|
||||
];
|
||||
@ -127,10 +131,10 @@ class AnimeSearchEndpointTest extends TestCase
|
||||
public function invalidScoreParameterProvider(): array
|
||||
{
|
||||
return [
|
||||
[["max_score" => "634638"], 15],
|
||||
[["min_score" => "673473"], 0],
|
||||
[["max_score" => "72344", "min_score" => "3532325"], 0],
|
||||
[["max_score" => 1, "min_score" => 5], 0],
|
||||
[["max_score" => "634638"]],
|
||||
[["min_score" => "673473"]],
|
||||
[["max_score" => "72344", "min_score" => "3532325"]],
|
||||
[["max_score" => 1, "min_score" => 5]],
|
||||
];
|
||||
}
|
||||
|
||||
@ -198,15 +202,26 @@ class AnimeSearchEndpointTest extends TestCase
|
||||
/**
|
||||
* @dataProvider emptyDateRangeProvider
|
||||
*/
|
||||
public function testSearchByEmptyDatesShouldDoNothing($params)
|
||||
public function testSearchByEmptyDatesShouldRaiseValidationError($params)
|
||||
{
|
||||
$this->generateFiveSpecificAndTenRandomElementsInDb($params);
|
||||
|
||||
$content = $this->getJsonResponse($params);
|
||||
|
||||
$this->seeStatusCode(200);
|
||||
$this->assertPaginationData(15);
|
||||
$this->assertCount(15, $content["data"]);
|
||||
$this->seeStatusCode(400);
|
||||
$this->assertEquals("ValidationException", data_get($content, "type"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider partialDatesParameterProvider
|
||||
*/
|
||||
public function testSearchByStartDateShouldRaiseIfPartialDate($params)
|
||||
{
|
||||
$this->generateFiveSpecificAndTenRandomElementsInDb($params);
|
||||
$content = $this->getJsonResponse($params);
|
||||
|
||||
$this->seeStatusCode(400);
|
||||
$this->assertEquals("ValidationException", data_get($content, "type"));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -345,25 +360,20 @@ class AnimeSearchEndpointTest extends TestCase
|
||||
$this->generateFiveSpecificAndTenRandomElementsInDb($params);
|
||||
$content = $this->getJsonResponse($params);
|
||||
|
||||
$this->seeStatusCode(200);
|
||||
$this->assertPaginationData(15);
|
||||
$this->assertIsArray($content["data"]);
|
||||
// it should return all, and disregard the gibberish filter
|
||||
$this->assertCount(15, $content["data"]);
|
||||
$this->seeStatusCode(400);
|
||||
$this->assertEquals("ValidationException", data_get($content, "type"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider invalidScoreParameterProvider
|
||||
*/
|
||||
public function testSearchByInvalidScoreParameters($params, $expectedCount)
|
||||
public function testSearchByInvalidScoreParameters($params)
|
||||
{
|
||||
$this->generateFiveSpecificAndTenRandomElementsInDb($params);
|
||||
$content = $this->getJsonResponse($params);
|
||||
|
||||
$this->seeStatusCode(200);
|
||||
$this->assertPaginationData($expectedCount);
|
||||
$this->assertIsArray($content["data"]);
|
||||
$this->assertCount($expectedCount, $content["data"]);
|
||||
$this->seeStatusCode(400);
|
||||
$this->assertEquals("ValidationException", data_get($content, "type"));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -489,10 +499,8 @@ class AnimeSearchEndpointTest extends TestCase
|
||||
$this->generateFiveSpecificAndTenRandomElementsInDb($params);
|
||||
$content = $this->getJsonResponse($params);
|
||||
|
||||
$this->seeStatusCode(200);
|
||||
$this->assertPaginationData($expectedCount);
|
||||
$this->assertIsArray($content["data"]);
|
||||
$this->assertCount($expectedCount, $content["data"]);
|
||||
$this->seeStatusCode(400);
|
||||
$this->assertEquals("ValidationException", data_get($content, "type"));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -511,7 +519,6 @@ class AnimeSearchEndpointTest extends TestCase
|
||||
|
||||
public function testSearchByInvalidLetterParameter()
|
||||
{
|
||||
$expectedCount = 0;
|
||||
$this->generateFiveSpecificAndTenRandomElementsInDb([
|
||||
"letter" => "a"
|
||||
]);
|
||||
@ -519,10 +526,8 @@ class AnimeSearchEndpointTest extends TestCase
|
||||
"letter" => "asd"
|
||||
]);
|
||||
|
||||
$this->seeStatusCode(200);
|
||||
$this->assertPaginationData($expectedCount);
|
||||
$this->assertIsArray($content["data"]);
|
||||
$this->assertCount($expectedCount, $content["data"]);
|
||||
$this->seeStatusCode(400);
|
||||
$this->assertEquals("ValidationException", data_get($content, "type"));
|
||||
}
|
||||
|
||||
public function testTypeSenseSearchPagination()
|
||||
|
@ -55,11 +55,19 @@ class MangaSearchEndpointTest extends TestCase
|
||||
];
|
||||
}
|
||||
|
||||
public function partialDatesParameterProvider(): array
|
||||
{
|
||||
return [
|
||||
[["start_date" => "2012"]],
|
||||
[["start_date" => "2012-05"]],
|
||||
[["end_date" => "2015"]],
|
||||
[["end_date" => "2015-05"]]
|
||||
];
|
||||
}
|
||||
|
||||
public function startDatesParameterProvider(): array
|
||||
{
|
||||
return [
|
||||
[["start_date" => "2022"]],
|
||||
[["start_date" => "2012-05"]],
|
||||
[["start_date" => "2012-05-12"]],
|
||||
[["start_date" => "2012-04-01"]],
|
||||
[["start_date" => "2012-04-28"]],
|
||||
@ -70,8 +78,6 @@ class MangaSearchEndpointTest extends TestCase
|
||||
public function endDatesParameterProvider(): array
|
||||
{
|
||||
return [
|
||||
[["end_date" => "2022"]],
|
||||
[["end_date" => "2012-05"]],
|
||||
[["end_date" => "2012-05-12"]],
|
||||
[["end_date" => "2012-05-12", "page" => 1]],
|
||||
];
|
||||
@ -80,8 +86,6 @@ class MangaSearchEndpointTest extends TestCase
|
||||
public function startAndEndDatesParameterProvider(): array
|
||||
{
|
||||
return [
|
||||
[["start_date" => "2021", "end_date" => "2022"]],
|
||||
[["start_date" => "2021-01", "end_date" => "2021-02"]],
|
||||
[["start_date" => "2021-01-01", "end_date" => "2021-03-22"]],
|
||||
[["start_date" => "2021-01-01", "end_date" => "2021-03-22", "page" => 1]],
|
||||
];
|
||||
@ -123,10 +127,10 @@ class MangaSearchEndpointTest extends TestCase
|
||||
public function invalidScoreParameterProvider(): array
|
||||
{
|
||||
return [
|
||||
[["max_score" => "634638"], 15],
|
||||
[["min_score" => "673473"], 0],
|
||||
[["max_score" => "72344", "min_score" => "3532325"], 0],
|
||||
[["max_score" => 1, "min_score" => 5], 0],
|
||||
[["max_score" => "634638"]],
|
||||
[["min_score" => "673473"]],
|
||||
[["max_score" => "72344", "min_score" => "3532325"]],
|
||||
[["max_score" => 1, "min_score" => 5]],
|
||||
];
|
||||
}
|
||||
|
||||
@ -185,17 +189,29 @@ class MangaSearchEndpointTest extends TestCase
|
||||
/**
|
||||
* @dataProvider emptyDateRangeProvider
|
||||
*/
|
||||
public function testSearchByEmptyDatesShouldDoNothing($params)
|
||||
public function testSearchByEmptyDatesShouldRaiseValidationError($params)
|
||||
{
|
||||
$this->generateFiveSpecificAndTenRandomElementsInDb($params);
|
||||
|
||||
$content = $this->getJsonResponse($params);
|
||||
|
||||
$this->seeStatusCode(200);
|
||||
$this->assertPaginationData(15);
|
||||
$this->assertCount(15, $content["data"]);
|
||||
$this->seeStatusCode(400);
|
||||
$this->assertEquals("ValidationException", data_get($content, "type"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider partialDatesParameterProvider
|
||||
*/
|
||||
public function testSearchByStartDateShouldRaiseIfPartialDate($params)
|
||||
{
|
||||
$this->generateFiveSpecificAndTenRandomElementsInDb($params);
|
||||
$content = $this->getJsonResponse($params);
|
||||
|
||||
$this->seeStatusCode(400);
|
||||
$this->assertEquals("ValidationException", data_get($content, "type"));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @dataProvider startDatesParameterProvider
|
||||
*/
|
||||
@ -264,7 +280,10 @@ class MangaSearchEndpointTest extends TestCase
|
||||
// this is mainly focused on mongodb features
|
||||
$startDate = "2015-02-01";
|
||||
$carbonStartDate = Carbon::parse($startDate);
|
||||
Manga::factory(5)->create();
|
||||
$fo = Manga::factory(5);
|
||||
$fo->create($fo->serializeStateDefinition([
|
||||
"published" => new CarbonDateRange(Carbon::parse("2002-01-01"), Carbon::parse("2002-02-02"))
|
||||
]));
|
||||
$f = Manga::factory(1);
|
||||
$f->create($f->serializeStateDefinition([
|
||||
"published" => new CarbonDateRange($carbonStartDate, null)
|
||||
@ -282,12 +301,15 @@ class MangaSearchEndpointTest extends TestCase
|
||||
public function testSearchWithEndDateEqualToParam()
|
||||
{
|
||||
// we test here whether the filtering works by start date
|
||||
// if the start date parameter's value exactly matches
|
||||
// if the end date parameter's value exactly matches
|
||||
// with one item in the database.
|
||||
// this is mainly focused on mongodb features
|
||||
$endDate = "2015-03-28";
|
||||
$carbonEndDate = Carbon::parse($endDate);
|
||||
Manga::factory(5)->create();
|
||||
$fo = Manga::factory(5);
|
||||
$fo->create($fo->serializeStateDefinition([
|
||||
"published" => new CarbonDateRange(Carbon::parse("2022-01-01"), Carbon::parse("2022-02-02"))
|
||||
]));
|
||||
$f = Manga::factory(1);
|
||||
$f->create($f->serializeStateDefinition([
|
||||
"published" => new CarbonDateRange(Carbon::parse("2015-01-05"), $carbonEndDate)
|
||||
@ -326,25 +348,20 @@ class MangaSearchEndpointTest extends TestCase
|
||||
$this->generateFiveSpecificAndTenRandomElementsInDb($params);
|
||||
$content = $this->getJsonResponse($params);
|
||||
|
||||
$this->seeStatusCode(200);
|
||||
$this->assertPaginationData(15);
|
||||
$this->assertIsArray($content["data"]);
|
||||
// it should return all, and disregard the gibberish filter
|
||||
$this->assertCount(15, $content["data"]);
|
||||
$this->seeStatusCode(400);
|
||||
$this->assertEquals("ValidationException", data_get($content, "type"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider invalidScoreParameterProvider
|
||||
*/
|
||||
public function testSearchByInvalidScoreParameters($params, $expectedCount)
|
||||
public function testSearchByInvalidScoreParameters($params)
|
||||
{
|
||||
$this->generateFiveSpecificAndTenRandomElementsInDb($params);
|
||||
$content = $this->getJsonResponse($params);
|
||||
|
||||
$this->seeStatusCode(200);
|
||||
$this->assertPaginationData($expectedCount);
|
||||
$this->assertIsArray($content["data"]);
|
||||
$this->assertCount($expectedCount, $content["data"]);
|
||||
$this->seeStatusCode(400);
|
||||
$this->assertEquals("ValidationException", data_get($content, "type"));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -487,10 +504,8 @@ class MangaSearchEndpointTest extends TestCase
|
||||
"letter" => "asd"
|
||||
]);
|
||||
|
||||
$this->seeStatusCode(200);
|
||||
$this->assertPaginationData($expectedCount);
|
||||
$this->assertIsArray($content["data"]);
|
||||
$this->assertCount($expectedCount, $content["data"]);
|
||||
$this->seeStatusCode(400);
|
||||
$this->assertEquals("ValidationException", data_get($content, "type"));
|
||||
}
|
||||
|
||||
public function testTypeSenseSearchPagination()
|
||||
|
Loading…
x
Reference in New Issue
Block a user