From 148925ea187f7c8c9f29ace8e53f2e648ab0b4d5 Mon Sep 17 00:00:00 2001 From: Ollie Date: Sat, 11 Apr 2026 00:13:12 +0100 Subject: [PATCH 1/4] PoC: Add `reuse_prototype` field to `/wiki` API endpoint Bug: T421877 --- app/Http/Controllers/PublicWikiController.php | 2 +- app/Http/Resources/PublicWikiResource.php | 15 +++++++++++++++ app/Providers/AppServiceProvider.php | 12 ++++++++++++ 3 files changed, 28 insertions(+), 1 deletion(-) 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..423f37c46 100644 --- a/app/Http/Resources/PublicWikiResource.php +++ b/app/Http/Resources/PublicWikiResource.php @@ -16,6 +16,21 @@ public function toArray($request): array { 'sitename' => $this->sitename, 'wiki_site_stats' => $this->wikiSiteStats, 'logo_url' => $logoSetting ? $logoSetting->value : null, + + // TODO: delete these three fields before merging; here to easily prove the `reuse_prototype` logic works + 'test_purpose' => $this->wikiLatestProfile ? $this->wikiLatestProfile->purpose : null, + 'test_temporality' => $this->wikiLatestProfile ? $this->wikiLatestProfile->temporality : null, + 'test_audience' => $this->wikiLatestProfile ? $this->wikiLatestProfile->audience : null, + + // TODO: As the `$this->wikiLatestProfile` property can be accessed regardless of if + // `->with('wikiLatestProfile')` is used in the controller, we are unable to return null if + // `$this->wikiLatestProfile` isn't set. We should either look into addressing this, or remove the + // `$this->wikiLatestProfile ? ... : null` conditional. + 'reuse_prototype' => $this->wikiLatestProfile + ? $this->wikiLatestProfile->purpose === 'data_hub' + && $this->wikiLatestProfile->temporality === 'permanent' + && $this->wikiLatestProfile->audience === 'wide' + : null, ]; } } diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 38f07d753..99eb8956d 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -4,6 +4,7 @@ use App\Http\Curl\CurlRequest; use App\Http\Curl\HttpRequest; +use Illuminate\Database\Events\QueryExecuted; use Illuminate\Queue\Events\JobFailed; use Illuminate\Support\Facades\Queue; use Illuminate\Support\ServiceProvider; @@ -25,5 +26,16 @@ public function boot(): void { $wrappedException = new \Exception("Executing Job '$name' failed.", 1, $event->exception); report($wrappedException); }); + + // TODO: delete this listener before merging or is it useful to keep in the local environment? + if ($this->app->environment('local')) { + \Event::listen(QueryExecuted::class, function (QueryExecuted $query) { + \Log::debug('Query Executed: ', [ + 'sql' => $query->sql, + 'bindings' => $query->bindings, + 'connection' => $query->connectionName, + ]); + }); + } } } From 8ac9b3d8511dd1221952d8e046ab93d5aef1de92 Mon Sep 17 00:00:00 2001 From: Dat Date: Fri, 22 May 2026 17:05:09 +0200 Subject: [PATCH 2/4] add test for reuse_prototype --- tests/Routes/Wiki/PublicWikiTest.php | 47 ++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/tests/Routes/Wiki/PublicWikiTest.php b/tests/Routes/Wiki/PublicWikiTest.php index c7ed46fbb..90d830c79 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,48 @@ 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', $this->route) + ->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', null); + } } From 045fe2bf2bde945857d1cf569da555da73830d7f Mon Sep 17 00:00:00 2001 From: Dat Date: Fri, 22 May 2026 17:16:01 +0200 Subject: [PATCH 3/4] Add load-state-aware reuse_prototype resource logic --- app/Http/Resources/PublicWikiResource.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/app/Http/Resources/PublicWikiResource.php b/app/Http/Resources/PublicWikiResource.php index 423f37c46..c9d38f9c6 100644 --- a/app/Http/Resources/PublicWikiResource.php +++ b/app/Http/Resources/PublicWikiResource.php @@ -22,14 +22,14 @@ public function toArray($request): array { 'test_temporality' => $this->wikiLatestProfile ? $this->wikiLatestProfile->temporality : null, 'test_audience' => $this->wikiLatestProfile ? $this->wikiLatestProfile->audience : null, - // TODO: As the `$this->wikiLatestProfile` property can be accessed regardless of if - // `->with('wikiLatestProfile')` is used in the controller, we are unable to return null if - // `$this->wikiLatestProfile` isn't set. We should either look into addressing this, or remove the - // `$this->wikiLatestProfile ? ... : null` conditional. - 'reuse_prototype' => $this->wikiLatestProfile - ? $this->wikiLatestProfile->purpose === 'data_hub' - && $this->wikiLatestProfile->temporality === 'permanent' - && $this->wikiLatestProfile->audience === 'wide' + // Checking relation load state before reading it to avoid N+1 query + // This relies on the controller to eager load `wikiLatestProfile` relationship + 'reuse_prototype' => $this->relationLoaded('wikiLatestProfile') + ? ($this->wikiLatestProfile + ? $this->wikiLatestProfile->purpose === 'data_hub' + && $this->wikiLatestProfile->temporality === 'permanent' + && $this->wikiLatestProfile->audience === 'wide' + : null) : null, ]; } From d57a0fa01482ce4d4969275e3425bb489a2af985 Mon Sep 17 00:00:00 2001 From: Dat Date: Fri, 22 May 2026 17:18:06 +0200 Subject: [PATCH 4/4] Remove temporary reuse_prototype debug fields --- app/Http/Resources/PublicWikiResource.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/app/Http/Resources/PublicWikiResource.php b/app/Http/Resources/PublicWikiResource.php index c9d38f9c6..c01e34f58 100644 --- a/app/Http/Resources/PublicWikiResource.php +++ b/app/Http/Resources/PublicWikiResource.php @@ -17,11 +17,6 @@ public function toArray($request): array { 'wiki_site_stats' => $this->wikiSiteStats, 'logo_url' => $logoSetting ? $logoSetting->value : null, - // TODO: delete these three fields before merging; here to easily prove the `reuse_prototype` logic works - 'test_purpose' => $this->wikiLatestProfile ? $this->wikiLatestProfile->purpose : null, - 'test_temporality' => $this->wikiLatestProfile ? $this->wikiLatestProfile->temporality : null, - 'test_audience' => $this->wikiLatestProfile ? $this->wikiLatestProfile->audience : null, - // Checking relation load state before reading it to avoid N+1 query // This relies on the controller to eager load `wikiLatestProfile` relationship 'reuse_prototype' => $this->relationLoaded('wikiLatestProfile')