From 69dfb897cf24cdd83d4e60d36e1283ffbcceded3 Mon Sep 17 00:00:00 2001 From: myrmidex Date: Sun, 8 Mar 2026 01:55:28 +0100 Subject: [PATCH] 61 - Remove onboarding create endpoints, use standard model endpoints --- .../Api/V1/OnboardingController.php | 241 ------------------ routes/api.php | 4 - .../Api/V1/OnboardingControllerTest.php | 196 -------------- 3 files changed, 441 deletions(-) diff --git a/app/Http/Controllers/Api/V1/OnboardingController.php b/app/Http/Controllers/Api/V1/OnboardingController.php index f39c83c..1972a8f 100644 --- a/app/Http/Controllers/Api/V1/OnboardingController.php +++ b/app/Http/Controllers/Api/V1/OnboardingController.php @@ -2,12 +2,6 @@ namespace App\Http\Controllers\Api\V1; -use App\Http\Requests\StoreFeedRequest; -use App\Http\Resources\FeedResource; -use App\Http\Resources\PlatformAccountResource; -use App\Http\Resources\PlatformChannelResource; -use App\Http\Resources\RouteResource; -use App\Jobs\ArticleDiscoveryJob; use App\Models\Feed; use App\Models\Language; use App\Models\PlatformAccount; @@ -15,18 +9,10 @@ use App\Models\PlatformInstance; use App\Models\Route; use App\Models\Setting; -use App\Services\Auth\LemmyAuthService; use Illuminate\Http\JsonResponse; -use Illuminate\Http\Request; -use Illuminate\Support\Facades\Validator; -use Illuminate\Validation\ValidationException; class OnboardingController extends BaseController { - public function __construct( - private readonly LemmyAuthService $lemmyAuthService - ) {} - /** * Get onboarding status - whether user needs onboarding */ @@ -111,233 +97,6 @@ public function options(): JsonResponse ], 'Onboarding options retrieved successfully.'); } - /** - * Create platform account for onboarding - */ - public function createPlatform(Request $request): JsonResponse - { - $validator = Validator::make($request->all(), [ - 'instance_url' => 'required|string|max:255|regex:/^[a-zA-Z0-9]([a-zA-Z0-9\-\.]*[a-zA-Z0-9])?$/', - 'username' => 'required|string|max:255', - 'password' => 'required|string|min:6', - 'platform' => 'required|in:lemmy', - ], [ - 'instance_url.regex' => 'Please enter a valid domain name (e.g., lemmy.world, belgae.social)' - ]); - - if ($validator->fails()) { - throw new ValidationException($validator); - } - - $validated = $validator->validated(); - - // Normalize the instance URL - prepend https:// if needed - $instanceDomain = $validated['instance_url']; - $fullInstanceUrl = 'https://' . $instanceDomain; - - try { - // Create or get platform instance - $platformInstance = PlatformInstance::firstOrCreate([ - 'url' => $fullInstanceUrl, - 'platform' => $validated['platform'], - ], [ - 'name' => ucfirst($instanceDomain), - 'is_active' => true, - ]); - - // Authenticate with Lemmy API using the full URL - $authResponse = $this->lemmyAuthService->authenticate( - $fullInstanceUrl, - $validated['username'], - $validated['password'] - ); - - // Create platform account with the current schema - $platformAccount = PlatformAccount::create([ - 'platform' => $validated['platform'], - 'instance_url' => $fullInstanceUrl, - 'username' => $validated['username'], - 'password' => $validated['password'], - 'settings' => [ - 'display_name' => $authResponse['person_view']['person']['display_name'] ?? null, - 'description' => $authResponse['person_view']['person']['bio'] ?? null, - 'person_id' => $authResponse['person_view']['person']['id'] ?? null, - 'platform_instance_id' => $platformInstance->id, - 'api_token' => $authResponse['jwt'] ?? null, // Store JWT in settings for now - ], - 'is_active' => true, - 'status' => 'active', - ]); - - return $this->sendResponse( - new PlatformAccountResource($platformAccount), - 'Platform account created successfully.' - ); - - } catch (\App\Exceptions\PlatformAuthException $e) { - // Check if it's a rate limit error - if (str_contains($e->getMessage(), 'Rate limited by')) { - return $this->sendError($e->getMessage(), [], 429); - } - - return $this->sendError('Invalid username or password. Please check your credentials and try again.', [], 422); - } catch (\Exception $e) { - - $message = 'Unable to connect to the Lemmy instance. Please check the URL and try again.'; - - // If it's a network/connection issue, provide a more specific message - if (str_contains(strtolower($e->getMessage()), 'connection') || - str_contains(strtolower($e->getMessage()), 'network') || - str_contains(strtolower($e->getMessage()), 'timeout')) { - $message = 'Connection failed. Please check the instance URL and your internet connection.'; - } - - return $this->sendError($message, [], 422); - } - } - - /** - * Create feed for onboarding - */ - public function createFeed(Request $request): JsonResponse - { - $validator = Validator::make($request->all(), [ - 'name' => 'required|string|max:255', - 'provider' => 'required|in:belga,vrt', - 'language_id' => 'required|exists:languages,id', - 'description' => 'nullable|string|max:1000', - ]); - - if ($validator->fails()) { - throw new ValidationException($validator); - } - - $validated = $validator->validated(); - - // Map provider to preset URL and type as required by onboarding tests - $provider = $validated['provider']; - $url = null; - $type = 'website'; - if ($provider === 'vrt') { - $url = 'https://www.vrt.be/vrtnws/en/'; - } elseif ($provider === 'belga') { - $url = 'https://www.belganewsagency.eu/'; - } - - $feed = Feed::firstOrCreate( - ['url' => $url], - [ - 'name' => $validated['name'], - 'type' => $type, - 'provider' => $provider, - 'language_id' => $validated['language_id'], - 'description' => $validated['description'] ?? null, - 'is_active' => true, - ] - ); - - return $this->sendResponse( - new FeedResource($feed->load('language')), - 'Feed created successfully.' - ); - } - - /** - * Create channel for onboarding - * @throws ValidationException - */ - public function createChannel(Request $request): JsonResponse - { - $validator = Validator::make($request->all(), [ - 'name' => 'required|string|max:255', - 'platform_instance_id' => 'required|exists:platform_instances,id', - 'language_id' => 'required|exists:languages,id', - 'description' => 'nullable|string|max:1000', - ]); - - if ($validator->fails()) { - throw new ValidationException($validator); - } - - $validated = $validator->validated(); - - // Get the platform instance to check for active accounts - $platformInstance = PlatformInstance::findOrFail($validated['platform_instance_id']); - - // Check if there are active platform accounts for this instance - $activeAccounts = PlatformAccount::where('instance_url', $platformInstance->url) - ->where('is_active', true) - ->get(); - - if ($activeAccounts->isEmpty()) { - return $this->sendError( - 'Cannot create channel: No active platform accounts found for this instance. Please create a platform account first.', - [], - 422 - ); - } - - $channel = PlatformChannel::create([ - 'platform_instance_id' => $validated['platform_instance_id'], - 'channel_id' => $validated['name'], // For Lemmy, this is the community name - 'name' => $validated['name'], - 'display_name' => ucfirst($validated['name']), - 'description' => $validated['description'] ?? null, - 'language_id' => $validated['language_id'], - 'is_active' => true, - ]); - - // Automatically attach the first active account to the channel - $firstAccount = $activeAccounts->first(); - $channel->platformAccounts()->attach($firstAccount->id, [ - 'is_active' => true, - 'priority' => 1, - 'created_at' => now(), - 'updated_at' => now(), - ]); - - return $this->sendResponse( - new PlatformChannelResource($channel->load(['platformInstance', 'language', 'platformAccounts'])), - 'Channel created successfully and linked to platform account.' - ); - } - - /** - * Create route for onboarding - * - * @throws ValidationException - */ - public function createRoute(Request $request): JsonResponse - { - $validator = Validator::make($request->all(), [ - 'feed_id' => 'required|exists:feeds,id', - 'platform_channel_id' => 'required|exists:platform_channels,id', - 'priority' => 'nullable|integer|min:1|max:100', - ]); - - if ($validator->fails()) { - throw new ValidationException($validator); - } - - $validated = $validator->validated(); - - $route = Route::create([ - 'feed_id' => $validated['feed_id'], - 'platform_channel_id' => $validated['platform_channel_id'], - 'priority' => $validated['priority'] ?? 50, - 'is_active' => true, - ]); - - // Trigger article discovery when the first route is created during onboarding - // This ensures articles start being fetched immediately after setup - ArticleDiscoveryJob::dispatch(); - - return $this->sendResponse( - new RouteResource($route->load(['feed', 'platformChannel'])), - 'Route created successfully.' - ); - } - /** * Mark onboarding as complete */ diff --git a/routes/api.php b/routes/api.php index b54f639..3142d04 100644 --- a/routes/api.php +++ b/routes/api.php @@ -38,10 +38,6 @@ // Onboarding Route::get('/onboarding/status', [OnboardingController::class, 'status'])->name('api.onboarding.status'); Route::get('/onboarding/options', [OnboardingController::class, 'options'])->name('api.onboarding.options'); - Route::post('/onboarding/platform', [OnboardingController::class, 'createPlatform'])->name('api.onboarding.platform'); - Route::post('/onboarding/feed', [OnboardingController::class, 'createFeed'])->name('api.onboarding.feed'); - Route::post('/onboarding/channel', [OnboardingController::class, 'createChannel'])->name('api.onboarding.channel'); - Route::post('/onboarding/route', [OnboardingController::class, 'createRoute'])->name('api.onboarding.route'); Route::post('/onboarding/complete', [OnboardingController::class, 'complete'])->name('api.onboarding.complete'); Route::post('/onboarding/skip', [OnboardingController::class, 'skip'])->name('api.onboarding.skip'); Route::post('/onboarding/reset-skip', [OnboardingController::class, 'resetSkip'])->name('api.onboarding.reset-skip'); diff --git a/tests/Feature/Http/Controllers/Api/V1/OnboardingControllerTest.php b/tests/Feature/Http/Controllers/Api/V1/OnboardingControllerTest.php index dfca1c1..67f4a66 100644 --- a/tests/Feature/Http/Controllers/Api/V1/OnboardingControllerTest.php +++ b/tests/Feature/Http/Controllers/Api/V1/OnboardingControllerTest.php @@ -9,7 +9,6 @@ use App\Models\PlatformInstance; use App\Models\Route; use App\Models\Setting; -use App\Services\Auth\LemmyAuthService; use Illuminate\Foundation\Testing\RefreshDatabase; use Tests\TestCase; @@ -189,180 +188,6 @@ public function test_options_returns_languages_and_platform_instances() ]); } - public function test_create_feed_validates_required_fields() - { - $response = $this->postJson('/api/v1/onboarding/feed', []); - - $response->assertStatus(422) - ->assertJsonValidationErrors(['name', 'provider', 'language_id']); - } - - public function test_create_feed_creates_vrt_feed_successfully() - { - $feedData = [ - 'name' => 'VRT Test Feed', - 'provider' => 'vrt', - 'language_id' => 1, - 'description' => 'Test description', - ]; - - $response = $this->postJson('/api/v1/onboarding/feed', $feedData); - - $response->assertStatus(200) - ->assertJson([ - 'success' => true, - 'data' => [ - 'name' => 'VRT Test Feed', - 'url' => 'https://www.vrt.be/vrtnws/en/', - 'type' => 'website', - 'is_active' => true, - ] - ]); - - $this->assertDatabaseHas('feeds', [ - 'name' => 'VRT Test Feed', - 'url' => 'https://www.vrt.be/vrtnws/en/', - 'type' => 'website', - 'language_id' => 1, - 'is_active' => true, - ]); - } - - public function test_create_feed_creates_belga_feed_successfully() - { - $feedData = [ - 'name' => 'Belga Test Feed', - 'provider' => 'belga', - 'language_id' => 1, - 'description' => 'Test description', - ]; - - $response = $this->postJson('/api/v1/onboarding/feed', $feedData); - - $response->assertStatus(200) - ->assertJson([ - 'success' => true, - 'data' => [ - 'name' => 'Belga Test Feed', - 'url' => 'https://www.belganewsagency.eu/', - 'type' => 'website', - 'is_active' => true, - ] - ]); - - $this->assertDatabaseHas('feeds', [ - 'name' => 'Belga Test Feed', - 'url' => 'https://www.belganewsagency.eu/', - 'type' => 'website', - 'language_id' => 1, - 'is_active' => true, - ]); - } - - public function test_create_feed_rejects_invalid_provider() - { - $feedData = [ - 'name' => 'Invalid Feed', - 'provider' => 'invalid', - 'language_id' => 1, - 'description' => 'Test description', - ]; - - $response = $this->postJson('/api/v1/onboarding/feed', $feedData); - - $response->assertStatus(422) - ->assertJsonValidationErrors(['provider']); - } - - public function test_create_channel_validates_required_fields() - { - $response = $this->postJson('/api/v1/onboarding/channel', []); - - $response->assertStatus(422) - ->assertJsonValidationErrors(['name', 'platform_instance_id', 'language_id']); - } - - public function test_create_channel_creates_channel_successfully() - { - $platformInstance = PlatformInstance::factory()->create(); - $language = Language::factory()->create(); - - // Create a platform account for this instance first - PlatformAccount::factory()->create([ - 'instance_url' => $platformInstance->url, - 'is_active' => true - ]); - - $channelData = [ - 'name' => 'test_community', - 'platform_instance_id' => $platformInstance->id, - 'language_id' => $language->id, - 'description' => 'Test community description', - ]; - - $response = $this->postJson('/api/v1/onboarding/channel', $channelData); - - $response->assertStatus(200) - ->assertJson([ - 'success' => true, - 'data' => [ - 'name' => 'test_community', - 'display_name' => 'Test_community', - 'channel_id' => 'test_community', - 'is_active' => true, - ] - ]); - - $this->assertDatabaseHas('platform_channels', [ - 'name' => 'test_community', - 'channel_id' => 'test_community', - 'platform_instance_id' => $platformInstance->id, - 'language_id' => $language->id, - 'is_active' => true, - ]); - } - - public function test_create_route_validates_required_fields() - { - $response = $this->postJson('/api/v1/onboarding/route', []); - - $response->assertStatus(422) - ->assertJsonValidationErrors(['feed_id', 'platform_channel_id']); - } - - public function test_create_route_creates_route_successfully() - { - $language = Language::first(); - $feed = Feed::factory()->language($language)->create(); - $platformChannel = PlatformChannel::factory()->create(); - - $routeData = [ - 'feed_id' => $feed->id, - 'platform_channel_id' => $platformChannel->id, - 'priority' => 75, - ]; - - $response = $this->postJson('/api/v1/onboarding/route', $routeData); - - $response->assertStatus(200) - ->assertJson([ - 'success' => true, - 'data' => [ - 'feed_id' => $feed->id, - 'platform_channel_id' => $platformChannel->id, - 'priority' => 75, - 'is_active' => true, - ] - ]); - - $this->assertDatabaseHas('routes', [ - 'feed_id' => $feed->id, - 'platform_channel_id' => $platformChannel->id, - 'priority' => 75, - 'is_active' => true, - ]); - } - public function test_complete_onboarding_returns_success() { $response = $this->postJson('/api/v1/onboarding/complete'); @@ -443,27 +268,6 @@ public function test_reset_skip_works_when_no_setting_exists() ]); } - public function test_create_platform_validates_instance_url_format() - { - $response = $this->postJson('/api/v1/onboarding/platform', [ - 'instance_url' => 'invalid.domain.with.spaces and symbols!', - 'username' => 'testuser', - 'password' => 'password123', - 'platform' => 'lemmy', - ]); - - $response->assertStatus(422) - ->assertJsonValidationErrors(['instance_url']); - } - - public function test_create_platform_validates_required_fields() - { - $response = $this->postJson('/api/v1/onboarding/platform', []); - - $response->assertStatus(422) - ->assertJsonValidationErrors(['instance_url', 'username', 'password', 'platform']); - } - public function test_onboarding_flow_integration() { // 1. Initial status - needs onboarding