Merge pull request #515 from jikan-me/hotfix/aired-between-query

🚑 Fixed issues around the season endpoint
This commit is contained in:
Irfan (Nekomata) 2024-03-05 17:58:14 +05:00 committed by GitHub
commit a11a69b580
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 122 additions and 32 deletions

View File

@ -5,15 +5,12 @@ namespace App\Repositories;
use App\Anime;
use App\Contracts\AnimeRepository;
use App\Contracts\Repository;
use App\Enums\AnimeRatingEnum;
use App\Enums\AnimeScheduleFilterEnum;
use App\Enums\AnimeSeasonEnum;
use App\Enums\AnimeStatusEnum;
use App\Enums\AnimeTypeEnum;
use Illuminate\Contracts\Database\Query\Builder as EloquentBuilder;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Support\Carbon;
use Jikan\Helper\Constants;
use Laravel\Scout\Builder as ScoutBuilder;
/**
@ -120,44 +117,49 @@ final class DefaultAnimeRepository extends DatabaseRepository implements AnimeRe
?string $premiered = null
): EloquentBuilder
{
/** @noinspection PhpParamsInspection */
$queryable = $this->queryable(true);
$airedFilter = ["aired.from" => [
$airedFilter = ['aired.from' => [
'$gte' => $from->toAtomString(),
'$lte' => $to->modify("last day of this month")->toAtomString()
'$lte' => $to->modify('last day of this month')->toAtomString()
]];
$finalFilter = [];
// if the premiered parameter for the filter is not null, look for those items which have a premiered attribute set,
// and equals to the parameter value, OR look for those items which doesn't have premired attribute set,
// they don't have a garbled aired string and their aired.from date is within the from-to parameters range
// they don't have a garbled aired string and their aired.from date is within the from-to parameters range.
// Additionally, we want to include all those items which are carry overs from previous seasons.
if ($premiered !== null) {
$finalFilter['$or'] = [
["premiered" => $premiered],
['premiered' => $premiered],
[
"premiered" => null,
"aired.string" => [
'$not' => ['$regex' => "{$from->year} to ?"]
'premiered' => null,
'aired.string' => [
'$nin' => ["{$from->year} to ?"]
],
...$airedFilter
],
// this condition will include "continuing" items from previous seasons
[
'aired.from' => ['$lte' => $from->toAtomString()],
'airing' => true
]
];
} else {
$finalFilter = array_merge($finalFilter, $airedFilter);
$finalFilter["aired.string"] = [
'$not' => ['$regex' => "{$from->year} to ?"]
$finalFilter['aired.string'] = [
'$nin' => ["{$from->year} to ?"]
];
}
if (!is_null($type)) {
$finalFilter["type"] = $type->label;
$finalFilter['type'] = $type->label;
}
$queryable = $queryable->whereRaw($finalFilter);
return $queryable->orderBy("members", "desc");
return $queryable->orderBy('members', 'desc');
}
public function getUpcomingSeasonItems(

View File

@ -65,7 +65,7 @@ final class CachedData implements ArrayAccess
$expiry = $this->expiry();
return time() > $expiry;
return Carbon::now()->unix() > $expiry;
}
public function toArray(): array

View File

@ -3,6 +3,7 @@
namespace App\Support;
use Laravel\Scout\Builder as ScoutBuilder;
use Illuminate\Contracts\Database\Query\Builder;
use Jenssegers\Mongodb\Eloquent\Builder as MongoDbBuilder;
class RepositoryQueryBase
{
@ -15,7 +16,7 @@ class RepositoryQueryBase
{
}
protected function queryable(bool $createNew = false): Builder
protected function queryable(bool $createNew = false): Builder|MongoDbBuilder
{
if ($createNew) {
$callback = $this->getQueryable;

View File

@ -25,7 +25,7 @@ class SeasonControllerTest extends TestCase
$state = $f->serializeStateDefinition([
"aired" => new CarbonDateRange($carbonStartDate, null)
]);
$state["aired"]["string"] = "Jan 1, 2024 to ?";
$state["aired"]["string"] = "2024 to ?";
$state["premiered"] = null;
$state["status"] = "Not yet aired";
$state["airing"] = false;
@ -49,4 +49,91 @@ class SeasonControllerTest extends TestCase
$this->assertIsArray($content["data"]);
$this->assertCount(1, $content["data"]);
}
public function testShouldNotFilterOutFutureAiringDates()
{
Carbon::setTestNow(Carbon::parse("2024-01-11"));
// an item in the future airing
$f = Anime::factory(1);
$startDate = "2024-02-24";
$carbonStartDate = Carbon::parse($startDate);
$state = $f->serializeStateDefinition([
"aired" => new CarbonDateRange($carbonStartDate, null)
]);
$state["aired"]["string"] = "Feb 24, 2024 to ?";
$state["premiered"] = null;
$state["status"] = "Not yet aired";
$state["airing"] = false;
$f->create($state);
// the "garbled" wrong item
$f = Anime::factory(1);
$startDate = "2024-01-01";
$carbonStartDate = Carbon::parse($startDate);
$state = $f->serializeStateDefinition([
"aired" => new CarbonDateRange($carbonStartDate, null)
]);
$state["aired"]["string"] = "2024 to ?";
$state["premiered"] = null;
$state["status"] = "Not yet aired";
$state["airing"] = false;
$f->create($state);
// the absolutely correct item
$f = Anime::factory(1);
$state = $f->serializeStateDefinition([
"aired" => new CarbonDateRange(Carbon::parse("2024-01-10"), Carbon::parse("2024-02-15"))
]);
$state["premiered"] = "Winter 2024";
$state["status"] = "Currently Airing";
$state["airing"] = true;
$f->create($state);
$content = $this->getJsonResponse([], "/v4/seasons/2024/winter");
$this->seeStatusCode(200);
$this->assertIsArray($content["data"]);
$this->assertCount(2, $content["data"]);
}
public function testShouldNotFilterOutContinuingItemsFromPreviousSeasons()
{
Carbon::setTestNow(Carbon::parse("2024-01-11"));
// an item in the future airing
$f = Anime::factory(1);
$startDate = "2024-02-24";
$carbonStartDate = Carbon::parse($startDate);
$state = $f->serializeStateDefinition([
"aired" => new CarbonDateRange($carbonStartDate, null)
]);
$state["aired"]["string"] = "Feb 24, 2024 to ?";
$state["premiered"] = null;
$state["status"] = "Not yet aired";
$state["airing"] = false;
$f->create($state);
// the absolutely correct item
$f = Anime::factory(1);
$state = $f->serializeStateDefinition([
"aired" => new CarbonDateRange(Carbon::parse("2024-01-10"), Carbon::parse("2024-02-15"))
]);
$state["premiered"] = "Winter 2024";
$state["status"] = "Currently Airing";
$state["airing"] = true;
$f->create($state);
// the continuing item
$f = Anime::factory(1);
$state = $f->serializeStateDefinition([
"aired" => new CarbonDateRange(Carbon::parse("2023-10-10"), null)
]);
$state["premiered"] = "Fall 2023";
$state["status"] = "Currently Airing";
$state["airing"] = true;
$f->create($state);
$content = $this->getJsonResponse([], "/v4/seasons/2024/winter");
$this->seeStatusCode(200);
$this->assertIsArray($content["data"]);
$this->assertCount(3, $content["data"]);
}
}

View File

@ -51,7 +51,7 @@ final class DefaultCachedScraperServiceTest extends TestCase
public function testIfFindListReturnsNotExpiredItems()
{
$testRequestHash = $this->requestHash();
$now = Carbon::now();
$now = Carbon::createFromDate(2022, 1, 11, "UTC")->addHours(8)->addMinutes(12);
Carbon::setTestNow($now);
// the cached data in the database
// this should be an array of arrays as builder->get() returns multiple items
@ -76,14 +76,14 @@ final class DefaultCachedScraperServiceTest extends TestCase
public function testIfFindListUpdatesCacheIfItemsExpired()
{
$testRequestHash = $this->requestHash();
$now = Carbon::now();
$now = Carbon::createFromDate(2022, 1, 11, "UTC")->addHours(8)->addMinutes(12);
Carbon::setTestNow($now);
// the cached data in the database
// this should be an array of arrays as builder->get() returns multiple items
$dummyResults = collect([[
"request_hash" => $testRequestHash,
"modifiedAt" => new UTCDateTime($now->sub("2 days")->getPreciseTimestamp(3)),
"modifiedAt" => new UTCDateTime($now->copy()->subDays(2)->getPreciseTimestamp(3)),
"results" => [
["dummy" => "dummy1"],
["dummy" => "dummy2"]
@ -128,7 +128,7 @@ final class DefaultCachedScraperServiceTest extends TestCase
public function testIfFindListUpdatesCacheIfCacheIsEmpty()
{
$testRequestHash = $this->requestHash();
$now = Carbon::now();
$now = Carbon::createFromDate(2022, 1, 11, "UTC")->addHours(8)->addMinutes(12);
Carbon::setTestNow($now);
// the data returned by the scraper
@ -175,7 +175,7 @@ final class DefaultCachedScraperServiceTest extends TestCase
{
$malId = 1;
$testRequestHash = $this->requestHash();
$now = Carbon::now();
$now = Carbon::createFromDate(2022, 1, 11, "UTC")->addHours(8)->addMinutes(12);
Carbon::setTestNow($now);
$mockModel = Anime::factory()->makeOne([
"mal_id" => $malId,
@ -197,7 +197,7 @@ final class DefaultCachedScraperServiceTest extends TestCase
{
$malId = 1;
$testRequestHash = $this->requestHash();
$now = Carbon::now();
$now = Carbon::createFromDate(2022, 1, 11, "UTC")->addHours(8)->addMinutes(12);
Carbon::setTestNow($now);
$mockModel = Anime::factory()->makeOne([
"mal_id" => $malId,
@ -224,11 +224,11 @@ final class DefaultCachedScraperServiceTest extends TestCase
{
$malId = 1;
$testRequestHash = $this->requestHash();
$now = Carbon::now();
$now = Carbon::createFromDate(2022, 1, 11, "UTC")->addHours(8)->addMinutes(12);
$mockModel = Anime::factory()->makeOne([
"mal_id" => $malId,
"modifiedAt" => new UTCDateTime($now->sub("3 days")->getPreciseTimestamp(3)),
"createdAt" => new UTCDateTime($now->sub("3 days")->getPreciseTimestamp(3))
"modifiedAt" => new UTCDateTime($now->copy()->sub("3 days")->getPreciseTimestamp(3)),
"createdAt" => new UTCDateTime($now->copy()->sub("3 days")->getPreciseTimestamp(3))
]);
$now = Carbon::now();
Carbon::setTestNow($now);
@ -264,7 +264,7 @@ final class DefaultCachedScraperServiceTest extends TestCase
{
$username = "kompot";
$testRequestHash = $this->requestHash();
$now = Carbon::now();
$now = Carbon::createFromDate(2022, 1, 11, "UTC")->addHours(8)->addMinutes(12);
Carbon::setTestNow($now);
$mockModel = Profile::factory()->makeOne([
"username" => $username,
@ -287,7 +287,7 @@ final class DefaultCachedScraperServiceTest extends TestCase
{
$username = "kompot";
$testRequestHash = $this->requestHash();
$now = Carbon::now();
$now = Carbon::createFromDate(2022, 1, 11, "UTC")->addHours(8)->addMinutes(12);
Carbon::setTestNow($now);
$mockModel = Profile::factory()->makeOne([
"username" => $username,
@ -319,12 +319,12 @@ final class DefaultCachedScraperServiceTest extends TestCase
$malId = 1;
$username = "kompot";
$testRequestHash = $this->requestHash();
$now = Carbon::now();
$now = Carbon::createFromDate(2022, 1, 11, "UTC")->addHours(8)->addMinutes(12);
$mockModel = Profile::factory()->makeOne([
"mal_id" => $malId,
"username" => $username,
"modifiedAt" => new UTCDateTime($now->sub("3 days")->getPreciseTimestamp(3)),
"createdAt" => new UTCDateTime($now->sub("3 days")->getPreciseTimestamp(3))
"modifiedAt" => new UTCDateTime($now->copy()->sub("3 days")->getPreciseTimestamp(3)),
"createdAt" => new UTCDateTime($now->copy()->sub("3 days")->getPreciseTimestamp(3))
]);
$now = Carbon::now();
Carbon::setTestNow($now);