From ddd560b2044fea9d5ade56de0bcc95288c22cc2b Mon Sep 17 00:00:00 2001 From: myrmidex Date: Sat, 15 Aug 2026 10:35:33 +0200 Subject: [PATCH] 150 - Delete unconvertible channels instead of resolving them via Lemmy --- ...eric_community_id_on_platform_channels.php | 67 ++++++++-------- .../Feature/StoreCommunityIdMigrationTest.php | 77 +++++++++---------- 2 files changed, 66 insertions(+), 78 deletions(-) diff --git a/database/migrations/2024_01_01_000015_store_numeric_community_id_on_platform_channels.php b/database/migrations/2024_01_01_000015_store_numeric_community_id_on_platform_channels.php index 4f907e4f..da461755 100644 --- a/database/migrations/2024_01_01_000015_store_numeric_community_id_on_platform_channels.php +++ b/database/migrations/2024_01_01_000015_store_numeric_community_id_on_platform_channels.php @@ -1,26 +1,26 @@ orderBy('id') - ->get() - ->mapWithKeys(fn (object $channel) => [$channel->id => $this->resolve($channel)]); + $this->deleteChannelsWithUnconvertibleIds(); Schema::table('platform_channels', function (Blueprint $table) { $table->dropUnique('platform_channels_channel_id_unique'); @@ -30,11 +30,9 @@ public function up(): void $table->unsignedBigInteger('remote_community_id')->nullable()->after('channel_id'); }); - foreach ($resolved as $id => $communityId) { - DB::table('platform_channels') - ->where('id', $id) - ->update(['remote_community_id' => $communityId]); - } + DB::table('platform_channels')->update([ + 'remote_community_id' => DB::raw('CAST(channel_id AS UNSIGNED)'), + ]); Schema::table('platform_channels', function (Blueprint $table) { $table->dropColumn('channel_id'); @@ -67,33 +65,28 @@ public function down(): void }); } - private function resolve(object $channel): int + /** + * Routes, keywords, route articles and channel posts cascade from the + * database. article_publications does not have its foreign key until + * 000023, which deletes whatever this leaves orphaned. + */ + private function deleteChannelsWithUnconvertibleIds(): void { - if (is_numeric($channel->channel_id)) { - return (int) $channel->channel_id; + $doomed = DB::table('platform_channels') + ->get(['id', 'name', 'channel_id']) + ->reject(fn (object $channel) => ctype_digit((string) $channel->channel_id)) + ->pluck('name', 'id'); + + if ($doomed->isEmpty()) { + return; } - $instance = DB::table('platform_instances')->find($channel->platform_instance_id); + Log::warning(sprintf( + 'Deleting %d channel(s) whose community id is a slug rather than a numeric id: %s. Recreate them from the Channels page.', + $doomed->count(), + $doomed->implode(', '), + )); - if (! $instance) { - throw new RuntimeException("Channel {$channel->id} has no platform instance; cannot resolve its community id."); - } - - $account = PlatformAccount::where('instance_url', $instance->url) - ->where('is_active', true) - ->first(); - - if (! $account) { - throw new RuntimeException("No active account for {$instance->url}; cannot resolve community '{$channel->channel_id}'."); - } - - $api = new LemmyApiService($instance->url); - $token = $api->login($account->username, $account->password); - - if (! $token) { - throw new RuntimeException("Could not authenticate against {$instance->url} to resolve community '{$channel->channel_id}'."); - } - - return $api->getCommunityId($channel->channel_id, $token); + DB::table('platform_channels')->whereIn('id', $doomed->keys())->delete(); } }; diff --git a/tests/Feature/StoreCommunityIdMigrationTest.php b/tests/Feature/StoreCommunityIdMigrationTest.php index 6dc9c759..e0112840 100644 --- a/tests/Feature/StoreCommunityIdMigrationTest.php +++ b/tests/Feature/StoreCommunityIdMigrationTest.php @@ -2,7 +2,6 @@ namespace Tests\Feature; -use App\Models\PlatformAccount; use App\Models\PlatformInstance; use Illuminate\Database\Schema\Blueprint; use Illuminate\Foundation\Testing\RefreshDatabase; @@ -37,39 +36,31 @@ private function restoreSlugColumn(): void }); } - private function seedChannel(PlatformInstance $instance, string $slug): int + private function seedChannel(PlatformInstance $instance, string $channelId, string $name): int { return DB::table('platform_channels')->insertGetId([ 'platform_instance_id' => $instance->id, - 'name' => $slug, - 'display_name' => ucfirst($slug), - 'channel_id' => $slug, + 'name' => $name, + 'display_name' => ucfirst($name), + 'channel_id' => $channelId, 'is_active' => true, 'created_at' => now(), 'updated_at' => now(), ]); } - private function instanceWithAccount(): PlatformInstance - { - $instance = PlatformInstance::factory()->create(['url' => 'https://lemmy.test']); - PlatformAccount::factory()->create(['instance_url' => 'https://lemmy.test', 'is_active' => true]); - - return $instance; - } - - public function test_it_replaces_the_slug_with_the_resolved_community_id(): void + private function prepare(): PlatformInstance { $this->restoreSlugColumn(); DB::table('platform_channels')->delete(); - $instance = $this->instanceWithAccount(); - $id = $this->seedChannel($instance, 'news'); + return PlatformInstance::factory()->create(['url' => 'https://lemmy.test']); + } - Http::fake([ - '*/api/v3/user/login*' => Http::response(['jwt' => 'token']), - '*/api/v3/community*' => Http::response(['community_view' => ['community' => ['id' => 8]]]), - ]); + public function test_it_keeps_a_channel_whose_id_is_already_numeric(): void + { + $instance = $this->prepare(); + $id = $this->seedChannel($instance, '8', 'news'); $this->runMigration(); @@ -77,34 +68,38 @@ public function test_it_replaces_the_slug_with_the_resolved_community_id(): void $this->assertSame('news', DB::table('platform_channels')->where('id', $id)->value('name')); } - public function test_it_aborts_when_a_community_cannot_be_resolved(): void + public function test_it_deletes_a_channel_whose_id_is_still_a_slug(): void { - $this->restoreSlugColumn(); - DB::table('platform_channels')->delete(); - - $instance = $this->instanceWithAccount(); - $this->seedChannel($instance, 'gone'); - - Http::fake([ - '*/api/v3/user/login*' => Http::response(['jwt' => 'token']), - '*/api/v3/community*' => Http::response('not found', 404), - ]); - - $this->expectException(\Exception::class); + $instance = $this->prepare(); + $id = $this->seedChannel($instance, 'news', 'news'); $this->runMigration(); + + $this->assertDatabaseMissing('platform_channels', ['id' => $id]); } - public function test_it_aborts_when_the_instance_has_no_active_account(): void + public function test_it_keeps_numeric_channels_while_deleting_slug_ones(): void { - $this->restoreSlugColumn(); - DB::table('platform_channels')->delete(); - - $instance = PlatformInstance::factory()->create(['url' => 'https://no-account.test']); - $this->seedChannel($instance, 'news'); - - $this->expectException(\RuntimeException::class); + $instance = $this->prepare(); + $kept = $this->seedChannel($instance, '8', 'news'); + $deleted = $this->seedChannel($instance, 'nieuws', 'nieuws'); $this->runMigration(); + + $this->assertDatabaseHas('platform_channels', ['id' => $kept]); + $this->assertDatabaseMissing('platform_channels', ['id' => $deleted]); + } + + public function test_it_makes_no_http_requests(): void + { + Http::preventStrayRequests(); + + $instance = $this->prepare(); + $this->seedChannel($instance, 'news', 'news'); + $this->seedChannel($instance, '8', 'other'); + + $this->runMigration(); + + Http::assertNothingSent(); } }