diff --git a/app/Contracts/Repository.php b/app/Contracts/Repository.php index d198cc0c..91ddd1d6 100644 --- a/app/Contracts/Repository.php +++ b/app/Contracts/Repository.php @@ -33,6 +33,4 @@ public function tableName(): string; public function scrape(int|string $id): array; public function insert(array $attributes): bool; - - public function random(int $numberOfRandomItems = 1): Collection; } diff --git a/app/Dto/Concerns/HasLimitParameter.php b/app/Dto/Concerns/HasLimitParameter.php index eb0b119e..2a642bca 100644 --- a/app/Dto/Concerns/HasLimitParameter.php +++ b/app/Dto/Concerns/HasLimitParameter.php @@ -13,6 +13,8 @@ * @OA\Parameter( * name="limit", * in="query", + * required=false, + * description="Maximum limit (and the default number of entries returned) is 25 for all endpoints except for Random endpoints where the maximum limit is 5 and the default number of entries returned is 1.", * @OA\Schema(type="integer") * ), */ diff --git a/app/Dto/Concerns/HasLimitParameterWithSmallerMax.php b/app/Dto/Concerns/HasLimitParameterWithSmallerMax.php new file mode 100644 index 00000000..fe95f165 --- /dev/null +++ b/app/Dto/Concerns/HasLimitParameterWithSmallerMax.php @@ -0,0 +1,17 @@ +has("limit")) { - $properties->put("limit", max_results_per_page( - property_exists(static::class, "defaultLimit") ? static::$defaultLimit : null)); - } + // // BUG: this causes override and always sets the default limit to config value + // even if the property `limit` does not exist + // max_results_per_page never accepts $defaultLimit and will always return config value + // if (property_exists(static::class, "limit") && !$properties->has("limit")) { + // $properties->put("limit", max_results_per_page( + // property_exists(static::class, "defaultLimit") ? static::$defaultLimit : null)); + // } // we want to cast "true" and "false" string values to boolean before validation, so let's take all properties // of the class which are bool or bool|Optional type, and using their name read the values from the incoming diff --git a/app/Dto/QueryRandomAnimeCommand.php b/app/Dto/QueryRandomAnimeCommand.php index 023ac9ed..004908e8 100644 --- a/app/Dto/QueryRandomAnimeCommand.php +++ b/app/Dto/QueryRandomAnimeCommand.php @@ -13,5 +13,6 @@ */ final class QueryRandomAnimeCommand extends Data implements DataRequest { - use HasSfwParameter, HasUnapprovedParameter; + use HasSfwParameter, + HasUnapprovedParameter; } diff --git a/app/Dto/QueryRandomAnimeListCommand.php b/app/Dto/QueryRandomAnimeListCommand.php new file mode 100644 index 00000000..89249ec1 --- /dev/null +++ b/app/Dto/QueryRandomAnimeListCommand.php @@ -0,0 +1,20 @@ + + */ +final class QueryRandomAnimeListCommand extends Data implements DataRequest +{ + use HasSfwParameter, + HasUnapprovedParameter, + HasLimitParameterWithSmallerMax; +} diff --git a/app/Dto/QueryRandomCharacterCommand.php b/app/Dto/QueryRandomCharacterCommand.php index ff71e1d4..b2a5e747 100644 --- a/app/Dto/QueryRandomCharacterCommand.php +++ b/app/Dto/QueryRandomCharacterCommand.php @@ -3,6 +3,7 @@ namespace App\Dto; use App\Contracts\DataRequest; +use App\Dto\Concerns\HasLimitParameterWithSmallerMax; use App\Http\Resources\V4\CharacterResource; use Spatie\LaravelData\Data; diff --git a/app/Dto/QueryRandomCharacterListCommand.php b/app/Dto/QueryRandomCharacterListCommand.php new file mode 100644 index 00000000..8bf0e9c0 --- /dev/null +++ b/app/Dto/QueryRandomCharacterListCommand.php @@ -0,0 +1,16 @@ + + */ +final class QueryRandomCharacterListCommand extends Data implements DataRequest +{ + use HasLimitParameterWithSmallerMax; +} diff --git a/app/Dto/QueryRandomMangaCommand.php b/app/Dto/QueryRandomMangaCommand.php index e24b158c..ae1ce69b 100644 --- a/app/Dto/QueryRandomMangaCommand.php +++ b/app/Dto/QueryRandomMangaCommand.php @@ -13,5 +13,6 @@ */ final class QueryRandomMangaCommand extends Data implements DataRequest { - use HasSfwParameter, HasUnapprovedParameter; + use HasSfwParameter, + HasUnapprovedParameter; } diff --git a/app/Dto/QueryRandomMangaListCommand.php b/app/Dto/QueryRandomMangaListCommand.php new file mode 100644 index 00000000..e8c6e777 --- /dev/null +++ b/app/Dto/QueryRandomMangaListCommand.php @@ -0,0 +1,20 @@ + + */ +final class QueryRandomMangaListCommand extends Data implements DataRequest +{ + use HasSfwParameter, + HasUnapprovedParameter, + HasLimitParameterWithSmallerMax; +} diff --git a/app/Dto/QueryRandomPersonListCommand.php b/app/Dto/QueryRandomPersonListCommand.php new file mode 100644 index 00000000..e5075088 --- /dev/null +++ b/app/Dto/QueryRandomPersonListCommand.php @@ -0,0 +1,16 @@ + + */ +final class QueryRandomPersonListCommand extends Data implements DataRequest +{ + use HasLimitParameterWithSmallerMax; +} diff --git a/app/Dto/QueryRandomUserCommand.php b/app/Dto/QueryRandomUserCommand.php index 7a39bcfc..18bd3051 100644 --- a/app/Dto/QueryRandomUserCommand.php +++ b/app/Dto/QueryRandomUserCommand.php @@ -3,6 +3,7 @@ namespace App\Dto; use App\Contracts\DataRequest; +use App\Dto\Concerns\HasLimitParameterWithSmallerMax; use App\Http\Resources\V4\ProfileResource; use Spatie\LaravelData\Data; diff --git a/app/Dto/QueryRandomUserListCommand.php b/app/Dto/QueryRandomUserListCommand.php new file mode 100644 index 00000000..d6c3046a --- /dev/null +++ b/app/Dto/QueryRandomUserListCommand.php @@ -0,0 +1,16 @@ + + */ +final class QueryRandomUserListCommand extends Data implements DataRequest +{ + use HasLimitParameterWithSmallerMax; +} diff --git a/app/Dto/QuerySpecificAnimeSeasonCommand.php b/app/Dto/QuerySpecificAnimeSeasonCommand.php index 7a94899d..40761cb3 100644 --- a/app/Dto/QuerySpecificAnimeSeasonCommand.php +++ b/app/Dto/QuerySpecificAnimeSeasonCommand.php @@ -24,8 +24,6 @@ final class QuerySpecificAnimeSeasonCommand extends QueryAnimeSeasonCommand #[WithCast(EnumCast::class, AnimeSeasonEnum::class), EnumValidation(AnimeSeasonEnum::class)] public AnimeSeasonEnum $season; - protected static int $defaultLimit = 30; - public static function messages(...$args): array { return [ diff --git a/app/Features/QueryAnimeSchedulesHandler.php b/app/Features/QueryAnimeSchedulesHandler.php index 573d94c8..1f408d55 100644 --- a/app/Features/QueryAnimeSchedulesHandler.php +++ b/app/Features/QueryAnimeSchedulesHandler.php @@ -8,6 +8,7 @@ use App\Http\Resources\V4\AnimeCollection; use App\Support\CachedData; use Illuminate\Support\Env; +use Spatie\LaravelData\Optional; /** * @implements RequestHandler @@ -24,7 +25,7 @@ public function __construct(private readonly AnimeRepository $repository) public function handle($request) { $requestParams = collect($request->all()); - $limit = $requestParams->get("limit"); + $limit = $request->limit instanceof Optional ? max_results_per_page() : $request->limit; $results = $this->repository->getCurrentlyAiring($request->filter); // apply sfw, kids and unapproved filters /** @noinspection PhpUndefinedMethodInspection */ diff --git a/app/Features/QueryAnimeSeasonHandlerBase.php b/app/Features/QueryAnimeSeasonHandlerBase.php index 67a8cedf..0498a84d 100644 --- a/app/Features/QueryAnimeSeasonHandlerBase.php +++ b/app/Features/QueryAnimeSeasonHandlerBase.php @@ -12,6 +12,7 @@ use Illuminate\Contracts\Database\Query\Builder; use Illuminate\Http\JsonResponse; use Illuminate\Support\Carbon; +use Spatie\LaravelData\Optional; use Symfony\Component\HttpFoundation\Exception\BadRequestException; /** @@ -32,11 +33,12 @@ public function handle($request): JsonResponse { $requestParams = collect($request->all()); $type = $requestParams->has("filter") ? $request->filter : null; + $limit = $request->limit instanceof Optional ? max_results_per_page() : $request->limit; $results = $this->getSeasonItems($request, $type); // apply sfw, kids and unapproved filters /** @noinspection PhpUndefinedMethodInspection */ $results = $results->filter($requestParams); - $results = $results->paginate($request->limit, ["*"], null, $request->page); + $results = $results->paginate($limit, ["*"], null, $request->page); $animeCollection = new AnimeCollection($results); $response = $animeCollection->response(); diff --git a/app/Features/QueryRandomAnimeHandler.php b/app/Features/QueryRandomAnimeHandler.php index 1ad4abbc..372fdfa4 100644 --- a/app/Features/QueryRandomAnimeHandler.php +++ b/app/Features/QueryRandomAnimeHandler.php @@ -20,13 +20,12 @@ public function handle($request): AnimeResource { $queryable = Anime::query(); - $o = Optional::create(); - $sfwParam = $request->sfw === $o ? false : $request->sfw; - $unapprovedParam = $request->unapproved === $o ? false : $request->unapproved; + $sfwParam = $request->sfw instanceof Optional ? false : $request->sfw; + $unapprovedParam = $request->unapproved instanceof Optional ? false : $request->unapproved; - return new AnimeResource( - $queryable->random(1, $sfwParam, $unapprovedParam)->first() - ); + $results = $queryable->random(1, $sfwParam, $unapprovedParam); + + return new AnimeResource($results->first()); } /** diff --git a/app/Features/QueryRandomAnimeListHandler.php b/app/Features/QueryRandomAnimeListHandler.php new file mode 100644 index 00000000..31f51316 --- /dev/null +++ b/app/Features/QueryRandomAnimeListHandler.php @@ -0,0 +1,40 @@ + + */ +final class QueryRandomAnimeListHandler implements RequestHandler +{ + /** + * @inheritDoc + */ + public function handle($request): AnimeCollection + { + $queryable = Anime::query(); + + $sfwParam = $request->sfw instanceof Optional ? false : $request->sfw; + $unapprovedParam = $request->unapproved instanceof Optional ? false : $request->unapproved; + $limit = $request->limit instanceof Optional ? 1 : $request->limit; + + $results = $queryable->random($limit, $sfwParam, $unapprovedParam); + + return new AnimeCollection($results, false); + } + + /** + * @inheritDoc + */ + public function requestClass(): string + { + return QueryRandomAnimeListCommand::class; + } +} diff --git a/app/Features/QueryRandomCharacterHandler.php b/app/Features/QueryRandomCharacterHandler.php index c66a2cdf..00cd5ff0 100644 --- a/app/Features/QueryRandomCharacterHandler.php +++ b/app/Features/QueryRandomCharacterHandler.php @@ -2,20 +2,27 @@ namespace App\Features; -use App\Contracts\CharacterRepository; +use App\Character; +use App\Contracts\RequestHandler; use App\Dto\QueryRandomCharacterCommand; use App\Http\Resources\V4\CharacterResource; -use Illuminate\Http\Resources\Json\JsonResource; -use Illuminate\Support\Collection; /** - * @extends QueryRandomItemHandler + * @extends QueryRandomCharacterHandler */ -final class QueryRandomCharacterHandler extends QueryRandomItemHandler +final class QueryRandomCharacterHandler implements RequestHandler { - public function __construct(CharacterRepository $repository) + + /** + * @inheritDoc + */ + public function handle($request): CharacterResource { - parent::__construct($repository); + return new CharacterResource( + Character::query() + ->random(1) + ->first() + ); } /** @@ -25,9 +32,4 @@ public function requestClass(): string { return QueryRandomCharacterCommand::class; } - - protected function resource(Collection $results): JsonResource - { - return new CharacterResource($results->first()); - } } diff --git a/app/Features/QueryRandomCharacterListHandler.php b/app/Features/QueryRandomCharacterListHandler.php new file mode 100644 index 00000000..e7a1e930 --- /dev/null +++ b/app/Features/QueryRandomCharacterListHandler.php @@ -0,0 +1,35 @@ + + */ +final class QueryRandomCharacterListHandler implements RequestHandler +{ + /** + * @inheritDoc + */ + public function handle($request): CharacterCollection + { + $queryable = Character::query(); + $limit = $request->limit instanceof Optional ? 1 : $request->limit; + $results = $queryable->random($limit); + + return new CharacterCollection($results, false); + } + + /** + * @inheritDoc + */ + public function requestClass(): string + { + return QueryRandomCharacterListCommand::class; + } +} diff --git a/app/Features/QueryRandomItemHandler.php b/app/Features/QueryRandomItemHandler.php deleted file mode 100644 index 6db9e1f2..00000000 --- a/app/Features/QueryRandomItemHandler.php +++ /dev/null @@ -1,34 +0,0 @@ - - */ -abstract class QueryRandomItemHandler implements RequestHandler -{ - protected function __construct(protected readonly Repository $repository) - { - } - - /** - * @inheritDoc - */ - public function handle($request) - { - $results = $this->repository->random(); - return $this->resource($results); - } - - protected abstract function resource(Collection $results): JsonResource; -} diff --git a/app/Features/QueryRandomMangaHandler.php b/app/Features/QueryRandomMangaHandler.php index f4864575..452dbed3 100644 --- a/app/Features/QueryRandomMangaHandler.php +++ b/app/Features/QueryRandomMangaHandler.php @@ -16,17 +16,15 @@ final class QueryRandomMangaHandler implements RequestHandler /** * @inheritDoc */ - public function handle($request) + public function handle($request): MangaResource { $queryable = Manga::query(); - $o = Optional::create(); - $sfwParam = $request->sfw === $o ? false : $request->sfw; - $unapprovedParam = $request->unapproved === $o ? false : $request->unapproved; + $sfwParam = $request->sfw instanceof Optional ? false : $request->sfw; + $unapprovedParam = $request->unapproved instanceof Optional ? false : $request->unapproved; + $results = $queryable->random(1, $sfwParam, $unapprovedParam); - return new MangaResource( - $queryable->random(1, $sfwParam, $unapprovedParam)->first() - ); + return new MangaResource($results->first()); } /** diff --git a/app/Features/QueryRandomMangaListHandler.php b/app/Features/QueryRandomMangaListHandler.php new file mode 100644 index 00000000..d51befe9 --- /dev/null +++ b/app/Features/QueryRandomMangaListHandler.php @@ -0,0 +1,40 @@ + + */ +final class QueryRandomMangaListHandler implements RequestHandler +{ + /** + * @inheritDoc + */ + public function handle($request): MangaCollection + { + $queryable = Manga::query(); + + $sfwParam = $request->sfw instanceof Optional ? false : $request->sfw; + $unapprovedParam = $request->unapproved instanceof Optional ? false : $request->unapproved; + $limit = $request->limit instanceof Optional ? 1 : $request->limit; + + $results = $queryable->random($limit, $sfwParam, $unapprovedParam); + + return new MangaCollection($results, false); + } + + /** + * @inheritDoc + */ + public function requestClass(): string + { + return QueryRandomMangaListCommand::class; + } +} diff --git a/app/Features/QueryRandomPersonHandler.php b/app/Features/QueryRandomPersonHandler.php index 80a4daf0..8027acf0 100644 --- a/app/Features/QueryRandomPersonHandler.php +++ b/app/Features/QueryRandomPersonHandler.php @@ -2,20 +2,27 @@ namespace App\Features; -use App\Contracts\PeopleRepository; +use App\Contracts\RequestHandler; use App\Dto\QueryRandomPersonCommand; use App\Http\Resources\V4\PersonResource; -use Illuminate\Http\Resources\Json\JsonResource; -use Illuminate\Support\Collection; +use App\Person; /** - * @extends QueryRandomItemHandler + * @extends RequestHandler */ -final class QueryRandomPersonHandler extends QueryRandomItemHandler +final class QueryRandomPersonHandler implements RequestHandler { - public function __construct(PeopleRepository $repository) + + /** + * @inheritDoc + */ + public function handle($request): PersonResource { - parent::__construct($repository); + return new PersonResource( + Person::query() + ->random() + ->first() + ); } /** @@ -25,9 +32,4 @@ public function requestClass(): string { return QueryRandomPersonCommand::class; } - - protected function resource(Collection $results): JsonResource - { - return new PersonResource($results->first()); - } } diff --git a/app/Features/QueryRandomPersonListHandler.php b/app/Features/QueryRandomPersonListHandler.php new file mode 100644 index 00000000..af7181be --- /dev/null +++ b/app/Features/QueryRandomPersonListHandler.php @@ -0,0 +1,37 @@ + + */ +final class QueryRandomPersonListHandler implements RequestHandler +{ + + /** + * @inheritDoc + */ + public function handle($request): PersonCollection + { + $queryable = Person::query(); + $limit = $request->limit instanceof Optional ? 1 : $request->limit; + $results = $queryable->random($limit); + + return new PersonCollection($results, false); + } + + /** + * @inheritDoc + */ + public function requestClass(): string + { + return QueryRandomPersonListCommand::class; + } +} diff --git a/app/Features/QueryRandomUserHandler.php b/app/Features/QueryRandomUserHandler.php index 2e29c95b..db89a37a 100644 --- a/app/Features/QueryRandomUserHandler.php +++ b/app/Features/QueryRandomUserHandler.php @@ -2,27 +2,25 @@ namespace App\Features; -use App\Contracts\UserRepository; +use App\Contracts\RequestHandler; use App\Dto\QueryRandomUserCommand; use App\Http\Resources\V4\ProfileResource; -use Illuminate\Http\Resources\Json\JsonResource; -use Illuminate\Support\Collection; +use App\Profile; /** - * @extends QueryRandomItemHandler + * @extends RequestHandler */ -final class QueryRandomUserHandler extends QueryRandomItemHandler +final class QueryRandomUserHandler implements RequestHandler { - public function __construct(UserRepository $repository) + /** + * @inheritDoc + */ + public function handle($request): ProfileResource { - parent::__construct($repository); - } + $queryable = Profile::query(); + $results = $queryable->random(1); - protected function resource(Collection $results): JsonResource - { - return new ProfileResource( - $results->first() - ); + return new ProfileResource($results->first()); } /** diff --git a/app/Features/QueryRandomUserListHandler.php b/app/Features/QueryRandomUserListHandler.php new file mode 100644 index 00000000..73ef794b --- /dev/null +++ b/app/Features/QueryRandomUserListHandler.php @@ -0,0 +1,37 @@ + + */ +final class QueryRandomUserListHandler implements RequestHandler +{ + /** + * @inheritDoc + */ + public function handle($request): UserCollection + { + $queryable = Profile::query(); + $limit = $request->limit instanceof Optional ? 1 : $request->limit; + $results = $queryable->random($limit); + + return new UserCollection($results, false); + } + + /** + * @inheritDoc + */ + public function requestClass(): string + { + return QueryRandomUserListCommand::class; + } +} diff --git a/app/Http/Controllers/V4DB/RandomController.php b/app/Http/Controllers/V4DB/RandomController.php index 826de4bd..a778bb6c 100644 --- a/app/Http/Controllers/V4DB/RandomController.php +++ b/app/Http/Controllers/V4DB/RandomController.php @@ -3,10 +3,14 @@ namespace App\Http\Controllers\V4DB; use App\Dto\QueryRandomAnimeCommand; +use App\Dto\QueryRandomAnimeListCommand; use App\Dto\QueryRandomCharacterCommand; +use App\Dto\QueryRandomCharacterListCommand; use App\Dto\QueryRandomMangaCommand; use App\Dto\QueryRandomPersonCommand; +use App\Dto\QueryRandomPersonListCommand; use App\Dto\QueryRandomUserCommand; +use App\Dto\QueryRandomUserListCommand; class RandomController extends Controller @@ -40,9 +44,12 @@ class RandomController extends Controller * operationId="getRandomAnime", * tags={"random"}, * + * @OA\Parameter(ref="#/components/parameters/sfw"), + * @OA\Parameter(ref="#/components/parameters/unapproved"), + * * @OA\Response( * response="200", - * description="Returns a random anime resource", + * description="Returns a single random anime resource", * @OA\JsonContent( * @OA\Property( * property="data", @@ -61,15 +68,53 @@ public function anime(QueryRandomAnimeCommand $command) return $this->mediator->send($command); } + /** + * @OA\Get( + * path="/random/list/anime", + * operationId="getRandomAnimeList", + * tags={"random"}, + * + * @OA\Parameter(ref="#/components/parameters/sfw"), + * @OA\Parameter(ref="#/components/parameters/unapproved"), + * @OA\Parameter(ref="#/components/parameters/limit"), + * + * @OA\Response( + * response="200", + * description="Returns multiple anime resources. You can use `limit` to control the number of items returned. By default it returns 1 and maximum 5", + * @OA\JsonContent( + * @OA\Property( + * property="data", + * type="array", + * @OA\Items( + * type="object", + * ref="#/components/schemas/anime" + * ), + * ), + * ) + * ), + * @OA\Response( + * response="400", + * description="Error: Bad request. When required parameters were not supplied.", + * ), + * ), + */ + public function animeList(QueryRandomAnimeListCommand $command) + { + return $this->mediator->send($command); + } + /** * @OA\Get( * path="/random/manga", * operationId="getRandomManga", * tags={"random"}, * + * @OA\Parameter(ref="#/components/parameters/sfw"), + * @OA\Parameter(ref="#/components/parameters/unapproved"), + * * @OA\Response( * response="200", - * description="Returns a random manga resource", + * description="Returns a single random manga resource or multiple resources in an array when `limit` is supplied", * @OA\JsonContent( * @OA\Property( * property="data", @@ -88,15 +133,53 @@ public function manga(QueryRandomMangaCommand $command) return $this->mediator->send($command); } + /** + * @OA\Get( + * path="/random/list/manga", + * operationId="getRandomMangaList", + * tags={"random"}, + * + * @OA\Parameter(ref="#/components/parameters/sfw"), + * @OA\Parameter(ref="#/components/parameters/unapproved"), + * @OA\Parameter(ref="#/components/parameters/limit"), + * + * @OA\Response( + * response="200", + * description="Returns multiple manga resources. You can use `limit` to control the number of items returned. By default it returns 1 and maximum 5", + * @OA\JsonContent( + * @OA\Property( + * property="data", + * type="array", + * @OA\Items( + * type="object", + * ref="#/components/schemas/manga" + * ), + * ), + * ) + * ), + * @OA\Response( + * response="400", + * description="Error: Bad request. When required parameters were not supplied.", + * ), + * ), + */ + public function mangaList(QueryRandomAnimeListCommand $command) + { + return $this->mediator->send($command); + } + /** * @OA\Get( * path="/random/characters", * operationId="getRandomCharacters", * tags={"random"}, * + * @OA\Parameter(ref="#/components/parameters/sfw"), + * @OA\Parameter(ref="#/components/parameters/unapproved"), + * * @OA\Response( * response="200", - * description="Returns a random character resource", + * description="Returns a single random character resource or multiple resources in an array when `limit` is supplied", * @OA\JsonContent( * @OA\Property( * property="data", @@ -115,15 +198,53 @@ public function characters(QueryRandomCharacterCommand $command) return $this->mediator->send($command); } + /** + * @OA\Get( + * path="/random/list/characters", + * operationId="getRandomCharactersList", + * tags={"random"}, + * + * @OA\Parameter(ref="#/components/parameters/sfw"), + * @OA\Parameter(ref="#/components/parameters/unapproved"), + * @OA\Parameter(ref="#/components/parameters/limit"), + * + * @OA\Response( + * response="200", + * description="Returns multiple character resources. You can use `limit` to control the number of items returned. By default it returns 1 and maximum 5", + * @OA\JsonContent( + * @OA\Property( + * property="data", + * type="array", + * @OA\Items( + * type="object", + * ref="#/components/schemas/character" + * ), + * ), + * ) + * ), + * @OA\Response( + * response="400", + * description="Error: Bad request. When required parameters were not supplied.", + * ), + * ), + */ + public function charactersList(QueryRandomCharacterListCommand $command) + { + return $this->mediator->send($command); + } + /** * @OA\Get( * path="/random/people", * operationId="getRandomPeople", * tags={"random"}, * + * @OA\Parameter(ref="#/components/parameters/sfw"), + * @OA\Parameter(ref="#/components/parameters/unapproved"), + * * @OA\Response( * response="200", - * description="Returns a random person resource", + * description="Returns a single random person resource or multiple resources in an array when `limit` is supplied", * @OA\JsonContent( * @OA\Property( * property="data", @@ -142,15 +263,53 @@ public function people(QueryRandomPersonCommand $command) return $this->mediator->send($command); } + /** + * @OA\Get( + * path="/random/list/people", + * operationId="getRandomPeopleList", + * tags={"random"}, + * + * @OA\Parameter(ref="#/components/parameters/sfw"), + * @OA\Parameter(ref="#/components/parameters/unapproved"), + * @OA\Parameter(ref="#/components/parameters/limit"), + * + * @OA\Response( + * response="200", + * description="Returns multiple people resources. You can use `limit` to control the number of items returned. By default it returns 1 and maximum 5", + * @OA\JsonContent( + * @OA\Property( + * property="data", + * type="array", + * @OA\Items( + * type="object", + * ref="#/components/schemas/person" + * ), + * ), + * ) + * ), + * @OA\Response( + * response="400", + * description="Error: Bad request. When required parameters were not supplied.", + * ), + * ), + */ + public function peopleList(QueryRandomPersonListCommand $command) + { + return $this->mediator->send($command); + } + /** * @OA\Get( * path="/random/users", * operationId="getRandomUsers", * tags={"random"}, * + * @OA\Parameter(ref="#/components/parameters/sfw"), + * @OA\Parameter(ref="#/components/parameters/unapproved"), + * * @OA\Response( * response="200", - * description="Returns a random user profile resource", + * description="Returns a single random user profile resource or multiple resources in an array when `limit` is supplied", * @OA\JsonContent( * @OA\Property( * property="data", @@ -168,4 +327,39 @@ public function users(QueryRandomUserCommand $command) { return $this->mediator->send($command); } + + /** + * @OA\Get( + * path="/random/list/users", + * operationId="getRandomUsersList", + * tags={"random"}, + * + * @OA\Parameter(ref="#/components/parameters/sfw"), + * @OA\Parameter(ref="#/components/parameters/unapproved"), + * @OA\Parameter(ref="#/components/parameters/limit"), + * + * @OA\Response( + * response="200", + * description="Returns multiple user resources. You can use `limit` to control the number of items returned. By default it returns 1 and maximum 5", + * @OA\JsonContent( + * @OA\Property( + * property="data", + * type="array", + * @OA\Items( + * type="object", + * ref="#/components/schemas/user_profile" + * ), + * ), + * ) + * ), + * @OA\Response( + * response="400", + * description="Error: Bad request. When required parameters were not supplied.", + * ), + * ), + */ + public function usersList(QueryRandomUserListCommand $command) + { + return $this->mediator->send($command); + } } diff --git a/app/Http/Middleware/MicroCaching.php b/app/Http/Middleware/MicroCaching.php index b3377b95..a3048864 100644 --- a/app/Http/Middleware/MicroCaching.php +++ b/app/Http/Middleware/MicroCaching.php @@ -5,18 +5,21 @@ use App\Http\HttpHelper; use App\Support\JikanConfig; use Closure; -use Illuminate\Support\Env; use Illuminate\Support\Facades\Cache; -use Jikan\Exception\BadResponseException; class MicroCaching { private const NO_CACHING = [ 'RandomController@anime', + 'RandomController@animeList', 'RandomController@manga', + 'RandomController@mangaList', 'RandomController@characters', + 'RandomController@charactersList', 'RandomController@people', + 'RandomController@peopleList', 'RandomController@users', + 'RandomController@usersList', 'InsightsController@main' ]; diff --git a/app/Http/Resources/V4/AnimeCollection.php b/app/Http/Resources/V4/AnimeCollection.php index 12ddfcb4..334d8cda 100644 --- a/app/Http/Resources/V4/AnimeCollection.php +++ b/app/Http/Resources/V4/AnimeCollection.php @@ -35,22 +35,28 @@ class AnimeCollection extends ResourceCollection */ public $collects = 'App\Http\Resources\V4\AnimeResource'; - private $pagination; + private ?array $pagination = null; - public function __construct($resource) + public function __construct($resource, bool $paginated = true) { - $this->pagination = [ - 'last_visible_page' => $resource->lastPage(), - 'has_next_page' => $resource->hasMorePages(), - 'current_page' => $resource->currentPage(), - 'items' => [ - 'count' => $resource->count(), - 'total' => $resource->total(), - 'per_page' => $resource->perPage(), - ], - ]; + if ($paginated) { + $this->pagination = [ + 'last_visible_page' => $resource->lastPage(), + 'has_next_page' => $resource->hasMorePages(), + 'current_page' => $resource->currentPage(), + 'items' => [ + 'count' => $resource->count(), + 'total' => $resource->total(), + 'per_page' => $resource->perPage(), + ], + ]; + + $this->collection = $resource->getCollection(); + } - $this->collection = $resource->getCollection(); + if (!$paginated) { + $this->collection = $resource; + } parent::__construct($resource); } @@ -63,6 +69,12 @@ public function __construct($resource) */ public function toArray($request) { + if ($this->pagination === null) { + return [ + 'data' => $this->collection + ]; + } + return [ 'pagination' => $this->pagination, 'data' => $this->collection @@ -75,4 +87,4 @@ public function withResponse($request, $response) unset($jsonResponse['links'],$jsonResponse['meta']); $response->setContent(json_encode($jsonResponse)); } -} \ No newline at end of file +} diff --git a/app/Http/Resources/V4/CharacterCollection.php b/app/Http/Resources/V4/CharacterCollection.php index d7539ad3..0ff8cc6a 100644 --- a/app/Http/Resources/V4/CharacterCollection.php +++ b/app/Http/Resources/V4/CharacterCollection.php @@ -35,22 +35,28 @@ class CharacterCollection extends ResourceCollection */ public $collects = 'App\Http\Resources\V4\CharacterResource'; - private $pagination; + private ?array $pagination = null; - public function __construct(LengthAwarePaginator $resource) + public function __construct($resource, bool $paginated = true) { - $this->pagination = [ - 'last_visible_page' => $resource->lastPage(), - 'has_next_page' => $resource->hasMorePages(), - 'current_page' => $resource->currentPage(), - 'items' => [ - 'count' => $resource->count(), - 'total' => $resource->total(), - 'per_page' => $resource->perPage(), - ], - ]; + if ($paginated) { + $this->pagination = [ + 'last_visible_page' => $resource->lastPage(), + 'has_next_page' => $resource->hasMorePages(), + 'current_page' => $resource->currentPage(), + 'items' => [ + 'count' => $resource->count(), + 'total' => $resource->total(), + 'per_page' => $resource->perPage(), + ], + ]; + + $this->collection = $resource->getCollection(); + } - $this->collection = $resource->getCollection(); + if (!$paginated) { + $this->collection = $resource; + } parent::__construct($resource); } @@ -63,6 +69,12 @@ public function __construct(LengthAwarePaginator $resource) */ public function toArray($request) { + if ($this->pagination === null) { + return [ + 'data' => $this->collection + ]; + } + return [ 'pagination' => $this->pagination, 'data' => $this->collection @@ -75,4 +87,4 @@ public function withResponse($request, $response) unset($jsonResponse['links'],$jsonResponse['meta']); $response->setContent(json_encode($jsonResponse)); } -} \ No newline at end of file +} diff --git a/app/Http/Resources/V4/MangaCollection.php b/app/Http/Resources/V4/MangaCollection.php index 99c6cadb..82109f7c 100644 --- a/app/Http/Resources/V4/MangaCollection.php +++ b/app/Http/Resources/V4/MangaCollection.php @@ -36,22 +36,28 @@ class MangaCollection extends ResourceCollection */ public $collects = 'App\Http\Resources\V4\MangaResource'; - private $pagination; + private ?array $pagination = null; - public function __construct($resource) + public function __construct($resource, bool $paginated = true) { - $this->pagination = [ - 'last_visible_page' => $resource->lastPage(), - 'has_next_page' => $resource->hasMorePages(), - 'current_page' => $resource->currentPage(), - 'items' => [ - 'count' => $resource->count(), - 'total' => $resource->total(), - 'per_page' => $resource->perPage(), - ], - ]; + if ($paginated) { + $this->pagination = [ + 'last_visible_page' => $resource->lastPage(), + 'has_next_page' => $resource->hasMorePages(), + 'current_page' => $resource->currentPage(), + 'items' => [ + 'count' => $resource->count(), + 'total' => $resource->total(), + 'per_page' => $resource->perPage(), + ], + ]; + + $this->collection = $resource->getCollection(); + } - $this->collection = $resource->getCollection(); + if (!$paginated) { + $this->collection = $resource; + } parent::__construct($resource); } @@ -64,6 +70,12 @@ public function __construct($resource) */ public function toArray($request) { + if ($this->pagination === null) { + return [ + 'data' => $this->collection + ]; + } + return [ 'pagination' => $this->pagination, 'data' => $this->collection @@ -76,4 +88,4 @@ public function withResponse($request, $response) unset($jsonResponse['links'], $jsonResponse['meta']); $response->setContent(json_encode($jsonResponse)); } -} \ No newline at end of file +} diff --git a/app/Http/Resources/V4/PersonCollection.php b/app/Http/Resources/V4/PersonCollection.php index b23451f8..2e642999 100644 --- a/app/Http/Resources/V4/PersonCollection.php +++ b/app/Http/Resources/V4/PersonCollection.php @@ -37,22 +37,28 @@ class PersonCollection extends ResourceCollection */ public $collects = 'App\Http\Resources\V4\PersonResource'; - private $pagination; + private ?array $pagination = null; - public function __construct(LengthAwarePaginator $resource) + public function __construct($resource, bool $paginated = true) { - $this->pagination = [ - 'last_visible_page' => $resource->lastPage(), - 'has_next_page' => $resource->hasMorePages(), - 'current_page' => $resource->currentPage(), - 'items' => [ - 'count' => $resource->count(), - 'total' => $resource->total(), - 'per_page' => $resource->perPage(), - ], - ]; + if ($paginated) { + $this->pagination = [ + 'last_visible_page' => $resource->lastPage(), + 'has_next_page' => $resource->hasMorePages(), + 'current_page' => $resource->currentPage(), + 'items' => [ + 'count' => $resource->count(), + 'total' => $resource->total(), + 'per_page' => $resource->perPage(), + ], + ]; + + $this->collection = $resource->getCollection(); + } - $this->collection = $resource->getCollection(); + if (!$paginated) { + $this->collection = $resource; + } parent::__construct($resource); } @@ -65,6 +71,12 @@ public function __construct(LengthAwarePaginator $resource) */ public function toArray($request) { + if ($this->pagination === null) { + return [ + 'data' => $this->collection + ]; + } + return [ 'pagination' => $this->pagination, 'data' => $this->collection diff --git a/app/Http/Resources/V4/UserCollection.php b/app/Http/Resources/V4/UserCollection.php index b7f90a87..cf05b5e5 100644 --- a/app/Http/Resources/V4/UserCollection.php +++ b/app/Http/Resources/V4/UserCollection.php @@ -22,4 +22,4 @@ public function toArray($request) 'data' => $this->collection ]; } -} \ No newline at end of file +} diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 4b8fb545..8eda934e 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -307,10 +307,15 @@ private function registerRequestHandlers() // automatically resolvable dependencies or no dependencies at all $requestHandlersWithNoDependencies = [ Features\QueryRandomAnimeHandler::class, + Features\QueryRandomAnimeListHandler::class, Features\QueryRandomMangaHandler::class, + Features\QueryRandomMangaListHandler::class, Features\QueryRandomCharacterHandler::class, + Features\QueryRandomCharacterListHandler::class, Features\QueryRandomPersonHandler::class, + Features\QueryRandomPersonListHandler::class, Features\QueryRandomUserHandler::class, + Features\QueryRandomUserListHandler::class, Features\QueryAnimeSchedulesHandler::class, Features\QueryCurrentAnimeSeasonHandler::class, Features\QuerySpecificAnimeSeasonHandler::class, diff --git a/app/Repositories/DatabaseRepository.php b/app/Repositories/DatabaseRepository.php index 200b59e8..844cedd6 100644 --- a/app/Repositories/DatabaseRepository.php +++ b/app/Repositories/DatabaseRepository.php @@ -74,11 +74,6 @@ public function insert(array $attributes): bool return true; } - public function random(int $numberOfRandomItems = 1): Collection - { - return $this->queryable(true)->random($numberOfRandomItems); - } - private function getModelClass(): string { return get_class($this->queryable(true)->newModelInstance()); diff --git a/app/Rules/Attributes/MaxLimitWithFallback.php b/app/Rules/Attributes/MaxLimitWithFallback.php index 15606262..c1bf75c3 100644 --- a/app/Rules/Attributes/MaxLimitWithFallback.php +++ b/app/Rules/Attributes/MaxLimitWithFallback.php @@ -9,8 +9,8 @@ #[Attribute(Attribute::TARGET_PROPERTY)] final class MaxLimitWithFallback extends Rule { - public function __construct() + public function __construct(?int $fallbackLimit = null) { - parent::__construct(new MaxResultsPerPageRule(max_results_per_page())); + parent::__construct(new MaxResultsPerPageRule($fallbackLimit ?? max_results_per_page())); } } diff --git a/app/Rules/MaxResultsPerPageRule.php b/app/Rules/MaxResultsPerPageRule.php index bac6ccda..f1b2217a 100644 --- a/app/Rules/MaxResultsPerPageRule.php +++ b/app/Rules/MaxResultsPerPageRule.php @@ -9,14 +9,14 @@ final class MaxResultsPerPageRule implements Rule private mixed $value; private int $fallbackLimit; - public function __construct($fallbackLimit = 25) + public function __construct(?int $fallbackLimit = null) { - $this->fallbackLimit = $fallbackLimit; + $this->fallbackLimit = $fallbackLimit ?? max_results_per_page(); } public function passes($attribute, $value): bool { - $this->value = $value; + $this->value = $value; // $value is being override to 25 here if (!is_numeric($value)) { return false; @@ -26,7 +26,7 @@ public function passes($attribute, $value): bool $value = intval($value); } - if ($value > max_results_per_page()) { + if ($value > $this->fallbackLimit) { return false; } @@ -36,6 +36,6 @@ public function passes($attribute, $value): bool public function message(): array|string { $mrpp = max_results_per_page($this->fallbackLimit); - return "Value {$this->value} is higher than the configured '$mrpp' max value."; + return "Value {$this->value} is higher than the configured {$this->fallbackLimit} max value."; } } diff --git a/routes/web.v4.php b/routes/web.v4.php index 0d064746..87b1f3b2 100644 --- a/routes/web.v4.php +++ b/routes/web.v4.php @@ -556,6 +556,33 @@ function() use ($router) { $router->get('/users', [ 'uses' => 'RandomController@users', ]); + + $router->group( + [ + 'prefix' => '/list' + ], + function() use ($router) { + $router->get('/anime', [ + 'uses' => 'RandomController@animeList', + ]); + + $router->get('/manga', [ + 'uses' => 'RandomController@mangaList', + ]); + + $router->get('/characters', [ + 'uses' => 'RandomController@charactersList', + ]); + + $router->get('/people', [ + 'uses' => 'RandomController@peopleList', + ]); + + $router->get('/users', [ + 'uses' => 'RandomController@usersList', + ]); + } + ); } ); diff --git a/storage/api-docs/api-docs.json b/storage/api-docs/api-docs.json index a8bc1b78..688647db 100644 --- a/storage/api-docs/api-docs.json +++ b/storage/api-docs/api-docs.json @@ -2022,9 +2022,17 @@ "random" ], "operationId": "getRandomAnime", + "parameters": [ + { + "$ref": "#/components/parameters/sfw" + }, + { + "$ref": "#/components/parameters/unapproved" + } + ], "responses": { "200": { - "description": "Returns a random anime resource", + "description": "Returns a single random anime resource", "content": { "application/json": { "schema": { @@ -2044,15 +2052,65 @@ } } }, + "/random/list/anime": { + "get": { + "tags": [ + "random" + ], + "operationId": "getRandomAnimeList", + "parameters": [ + { + "$ref": "#/components/parameters/sfw" + }, + { + "$ref": "#/components/parameters/unapproved" + }, + { + "$ref": "#/components/parameters/limit" + } + ], + "responses": { + "200": { + "description": "Returns multiple anime resources. You can use `limit` to control the number of items returned. By default it returns 1 and maximum 5", + "content": { + "application/json": { + "schema": { + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/anime" + } + } + }, + "type": "object" + } + } + } + }, + "400": { + "description": "Error: Bad request. When required parameters were not supplied." + } + } + } + }, "/random/manga": { "get": { "tags": [ "random" ], "operationId": "getRandomManga", + "parameters": [ + { + "$ref": "#/components/parameters/sfw" + }, + { + "$ref": "#/components/parameters/unapproved" + } + ], "responses": { "200": { - "description": "Returns a random manga resource", + "description": "Returns a single random manga resource or multiple resources in an array when `limit` is supplied", "content": { "application/json": { "schema": { @@ -2072,15 +2130,65 @@ } } }, + "/random/list/manga": { + "get": { + "tags": [ + "random" + ], + "operationId": "getRandomMangaList", + "parameters": [ + { + "$ref": "#/components/parameters/sfw" + }, + { + "$ref": "#/components/parameters/unapproved" + }, + { + "$ref": "#/components/parameters/limit" + } + ], + "responses": { + "200": { + "description": "Returns multiple manga resources. You can use `limit` to control the number of items returned. By default it returns 1 and maximum 5", + "content": { + "application/json": { + "schema": { + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/manga" + } + } + }, + "type": "object" + } + } + } + }, + "400": { + "description": "Error: Bad request. When required parameters were not supplied." + } + } + } + }, "/random/characters": { "get": { "tags": [ "random" ], "operationId": "getRandomCharacters", + "parameters": [ + { + "$ref": "#/components/parameters/sfw" + }, + { + "$ref": "#/components/parameters/unapproved" + } + ], "responses": { "200": { - "description": "Returns a random character resource", + "description": "Returns a single random character resource or multiple resources in an array when `limit` is supplied", "content": { "application/json": { "schema": { @@ -2100,15 +2208,65 @@ } } }, + "/random/list/characters": { + "get": { + "tags": [ + "random" + ], + "operationId": "getRandomCharactersList", + "parameters": [ + { + "$ref": "#/components/parameters/sfw" + }, + { + "$ref": "#/components/parameters/unapproved" + }, + { + "$ref": "#/components/parameters/limit" + } + ], + "responses": { + "200": { + "description": "Returns multiple character resources. You can use `limit` to control the number of items returned. By default it returns 1 and maximum 5", + "content": { + "application/json": { + "schema": { + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/character" + } + } + }, + "type": "object" + } + } + } + }, + "400": { + "description": "Error: Bad request. When required parameters were not supplied." + } + } + } + }, "/random/people": { "get": { "tags": [ "random" ], "operationId": "getRandomPeople", + "parameters": [ + { + "$ref": "#/components/parameters/sfw" + }, + { + "$ref": "#/components/parameters/unapproved" + } + ], "responses": { "200": { - "description": "Returns a random person resource", + "description": "Returns a single random person resource or multiple resources in an array when `limit` is supplied", "content": { "application/json": { "schema": { @@ -2128,15 +2286,65 @@ } } }, + "/random/list/people": { + "get": { + "tags": [ + "random" + ], + "operationId": "getRandomPeopleList", + "parameters": [ + { + "$ref": "#/components/parameters/sfw" + }, + { + "$ref": "#/components/parameters/unapproved" + }, + { + "$ref": "#/components/parameters/limit" + } + ], + "responses": { + "200": { + "description": "Returns multiple people resources. You can use `limit` to control the number of items returned. By default it returns 1 and maximum 5", + "content": { + "application/json": { + "schema": { + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/person" + } + } + }, + "type": "object" + } + } + } + }, + "400": { + "description": "Error: Bad request. When required parameters were not supplied." + } + } + } + }, "/random/users": { "get": { "tags": [ "random" ], "operationId": "getRandomUsers", + "parameters": [ + { + "$ref": "#/components/parameters/sfw" + }, + { + "$ref": "#/components/parameters/unapproved" + } + ], "responses": { "200": { - "description": "Returns a random user profile resource", + "description": "Returns a single random user profile resource or multiple resources in an array when `limit` is supplied", "content": { "application/json": { "schema": { @@ -2156,6 +2364,48 @@ } } }, + "/random/list/users": { + "get": { + "tags": [ + "random" + ], + "operationId": "getRandomUsersList", + "parameters": [ + { + "$ref": "#/components/parameters/sfw" + }, + { + "$ref": "#/components/parameters/unapproved" + }, + { + "$ref": "#/components/parameters/limit" + } + ], + "responses": { + "200": { + "description": "Returns multiple user resources. You can use `limit` to control the number of items returned. By default it returns 1 and maximum 5", + "content": { + "application/json": { + "schema": { + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/user_profile" + } + } + }, + "type": "object" + } + } + } + }, + "400": { + "description": "Error: Bad request. When required parameters were not supplied." + } + } + } + }, "/recommendations/anime": { "get": { "tags": [ @@ -9059,6 +9309,8 @@ "limit": { "name": "limit", "in": "query", + "description": "Maximum limit (and the default number of entries returned) is 25 for all endpoints except for Random endpoints where the maximum limit is 5 and the default number of entries returned is 1.", + "required": false, "schema": { "type": "integer" }