diff --git a/app/Http/Controllers/PublicWikiController.php b/app/Http/Controllers/PublicWikiController.php index ca4d3e7da..c4f8fc155 100644 --- a/app/Http/Controllers/PublicWikiController.php +++ b/app/Http/Controllers/PublicWikiController.php @@ -29,7 +29,7 @@ public function index(Request $request) { ]); $params = array_merge(self::$defaultParams, $request->input()); - $query = Wiki::query(); + $query = Wiki::query()->with('wikiLatestProfile'); if (array_key_exists('is_featured', $params)) { $query = $query->where([ diff --git a/app/Http/Resources/PublicWikiResource.php b/app/Http/Resources/PublicWikiResource.php index 37e1e92ef..3deb10d7f 100644 --- a/app/Http/Resources/PublicWikiResource.php +++ b/app/Http/Resources/PublicWikiResource.php @@ -16,6 +16,11 @@ public function toArray($request): array { 'sitename' => $this->sitename, 'wiki_site_stats' => $this->wikiSiteStats, 'logo_url' => $logoSetting ? $logoSetting->value : null, + 'reuse_prototype' => $this->wikiLatestProfile + ? $this->wikiLatestProfile->purpose === 'data_hub' + && $this->wikiLatestProfile->temporality === 'permanent' + && $this->wikiLatestProfile->audience === 'wide' + : false, ]; } } diff --git a/routes/api.php b/routes/api.php index 0522a0491..f014ffbcb 100644 --- a/routes/api.php +++ b/routes/api.php @@ -57,5 +57,6 @@ }); $router->apiResource('wiki', 'PublicWikiController')->only(['index', 'show']); + $router->apiResource('reusePrototype', 'PublicWikiController')->only(['index']); $router->apiResource('wikiConversionData', 'ConversionMetricController')->only(['index']); }); diff --git a/tests/Http/Controllers/PublicWikiControllerTest.php b/tests/Http/Controllers/PublicWikiControllerTest.php new file mode 100644 index 000000000..7faf1eeae --- /dev/null +++ b/tests/Http/Controllers/PublicWikiControllerTest.php @@ -0,0 +1,104 @@ +create([ + 'domain' => 'controller-test.wikibase.cloud', + 'sitename' => 'controller-test', + ]); + + WikiProfile::create([ + 'wiki_id' => $wiki->id, + 'purpose' => 'data_hub', + 'temporality' => 'permanent', + 'audience' => 'wide', + ]); + + $controller = new PublicWikiController; + $resource = $controller->show($wiki->id); + + $this->assertInstanceOf(PublicWikiResource::class, $resource); + $this->assertSame(true, $resource->toArray(new Request)['reuse_prototype']); + } + + public function testIndexEagerLoadsWikiLatestProfileOnceForCollection(): void { + for ($i = 1; $i <= 3; $i++) { + $wiki = Wiki::factory()->create([ + 'domain' => "index-eager-load-test-{$i}.wikibase.cloud", + 'sitename' => "Index Eager Load Test Site {$i}", + ]); + + WikiProfile::create([ + 'wiki_id' => $wiki->id, + 'purpose' => 'data_hub', + 'temporality' => 'permanent', + 'audience' => 'wide', + ]); + } + + $wikiProfileQueryCount = 0; + DB::listen(function (QueryExecuted $query) use (&$wikiProfileQueryCount): void { + if (str_contains($query->sql, 'wiki_profiles')) { + $wikiProfileQueryCount++; + } + }); + + $controller = new PublicWikiController; + $resourceCollection = $controller->index(new Request); + + $this->assertSame(1, $wikiProfileQueryCount); + $this->assertTrue($resourceCollection->first()->relationLoaded('wikiLatestProfile')); + } + + public function testIndexReturnsFalseWhenWikiHasNoLatestProfile(): void { + $wikiWithoutProfile = Wiki::factory()->create([ + 'domain' => 'no-profile.wikibase.cloud', + 'sitename' => 'No Profile Test Site', + ]); + + $controller = new PublicWikiController; + $request = new Request; + $resourceCollection = $controller->index($request); + + $resource = $resourceCollection->firstWhere('id', $wikiWithoutProfile->id); + + $this->assertNotNull($resource); + $this->assertFalse($resource->toArray($request)['reuse_prototype']); + } + + public function testIndexReturnsFalseWhenWikiLatestProfileDoesNotMatchReusePrototype(): void { + $incompleteProfileWiki = Wiki::factory()->create([ + 'domain' => 'incomplete-profile.wikibase.cloud', + 'sitename' => 'Incomplete Profile Test Site', + ]); + WikiProfile::create([ + 'wiki_id' => $incompleteProfileWiki->id, + 'purpose' => 'other', + 'temporality' => 'temporary', + 'audience' => 'other', + ]); + + $controller = new PublicWikiController; + $request = new Request; + $resourceCollection = $controller->index($request); + + $resource = $resourceCollection->firstWhere('id', $incompleteProfileWiki->id); + + $this->assertNotNull($resource); + $this->assertFalse($resource->toArray($request)['reuse_prototype']); + } +} diff --git a/tests/Routes/Wiki/PublicWikiTest.php b/tests/Routes/Wiki/PublicWikiTest.php index c7ed46fbb..f3269088e 100644 --- a/tests/Routes/Wiki/PublicWikiTest.php +++ b/tests/Routes/Wiki/PublicWikiTest.php @@ -3,6 +3,7 @@ namespace Tests\Routes\Wiki; use App\Wiki; +use App\WikiProfile; use App\WikiSetting; use App\WikiSiteStats; use Illuminate\Foundation\Testing\DatabaseTransactions; @@ -18,12 +19,14 @@ class PublicWikiTest extends TestCase { protected function setUp(): void { parent::setUp(); Wiki::query()->delete(); + WikiProfile::query()->delete(); WikiSiteStats::query()->delete(); WikiSetting::query()->delete(); } protected function tearDown(): void { Wiki::query()->delete(); + WikiProfile::query()->delete(); WikiSiteStats::query()->delete(); WikiSetting::query()->delete(); parent::tearDown(); @@ -279,4 +282,52 @@ public function testLogoUrl() { ) ->assertJsonPath('data.1.logo_url', null); } + + public function testReusePrototype() { + $reusableWiki = Wiki::factory()->create([ + 'domain' => 'reusable.wikibase.cloud', 'sitename' => 'asite', + ]); + WikiSiteStats::factory()->create([ + 'wiki_id' => $reusableWiki->id, + ]); + WikiProfile::create([ + 'wiki_id' => $reusableWiki->id, + 'purpose' => 'data_hub', + 'temporality' => 'permanent', + 'audience' => 'wide', + ]); + + $nonReusableWiki = Wiki::factory()->create([ + 'domain' => 'non-reusable.wikibase.cloud', 'sitename' => 'bsite', + ]); + WikiSiteStats::factory()->create([ + 'wiki_id' => $nonReusableWiki->id, + ]); + WikiProfile::create([ + 'wiki_id' => $nonReusableWiki->id, + 'purpose' => 'other', + 'temporality' => 'other', + 'audience' => 'other', + ]); + + $noProfileWiki = Wiki::factory()->create([ + 'domain' => 'no-profile.wikibase.cloud', 'sitename' => 'csite', + ]); + WikiSiteStats::factory()->create([ + 'wiki_id' => $noProfileWiki->id, + ]); + + $this->json('GET', 'reusePrototype') + ->assertStatus(200) + ->assertJsonPath('data.0.domain', 'reusable.wikibase.cloud') + ->assertJsonPath('data.0.reuse_prototype', true) + ->assertJsonPath('data.1.domain', 'non-reusable.wikibase.cloud') + ->assertJsonPath('data.1.reuse_prototype', false) + ->assertJsonPath('data.2.domain', 'no-profile.wikibase.cloud') + ->assertJsonPath('data.2.reuse_prototype', false); + + $this->json('GET', $this->route . '/' . $reusableWiki->id) + ->assertStatus(200) + ->assertJsonPath('data.reuse_prototype', true); + } }