diff --git a/CHANGES_SUMMARY.md b/CHANGES_SUMMARY.md deleted file mode 100644 index a184bb0f..00000000 --- a/CHANGES_SUMMARY.md +++ /dev/null @@ -1,78 +0,0 @@ -# User Endpoint Refactoring - Changes Summary - -## Overview -This refactoring removes the redundant `/auth/me` endpoint and consolidates user profile retrieval through the proper `/users/{user_id}` endpoint, improving API consistency and reducing code duplication. - -## Problem Analysis -The system had two endpoints returning similar user information: -- `/auth/me` - Returned current user from JWT token -- `/users/{user_id}` - Returned user by ID - -This created redundancy and confusion in the API structure. - -## Changes Made - -### 1. Backend Changes -**File:** `services/auth/app/api/auth_operations.py` -- **Removed:** `/auth/me` endpoint (lines with `@router.get("/me")`) -- **Impact:** The endpoint no longer exists in the auth service -- **Reason:** Redundant with `/users/{user_id}` endpoint - -### 2. Frontend Changes -**File:** `frontend/src/api/services/user.ts` -- **Updated:** `getCurrentUser()` method -- **Before:** Called `/users/me` -- **After:** Gets current user ID from auth store and calls `/users/{user_id}` -- **Implementation:** - ```typescript - async getCurrentUser(): Promise { - // Get current user ID from auth store - const authStore = useAuthStore.getState(); - const userId = authStore.user?.id; - - if (!userId) { - throw new Error('No authenticated user found'); - } - - return apiClient.get(`${this.baseUrl}/${userId}`); - } - ``` - -### 3. API Client Changes -**File:** `frontend/src/api/client/apiClient.ts` -- **Updated:** Removed `/auth/me` from `noTenantEndpoints` array -- **Before:** `/auth/me` was listed as a user-level endpoint -- **After:** Removed since the endpoint no longer exists -- **Note:** `/auth/me/onboarding` remains as it's a different endpoint - -## API Gateway Behavior -The gateway routing remains unchanged and works correctly: -- Frontend calls `/users/{user_id}` -- Gateway forwards to `/api/v1/auth/users/{user_id}` in auth service -- Auth service returns user data via `get_user_by_id()` endpoint - -## Benefits -1. **Consistency:** Single source of truth for user data -2. **Simplicity:** Removes redundant endpoint -3. **Maintainability:** Clearer API structure -4. **Performance:** No duplicate data fetching logic - -## Testing -- Created verification script to ensure all changes are syntactically correct -- Verified that `/auth/me` endpoint has been removed -- Confirmed that UserService correctly uses user ID from auth store -- Validated that API client no longer references the removed endpoint - -## Migration Notes -- **Breaking Change:** Any direct calls to `/auth/me` will now return 404 -- **Replacement:** Use `/users/{user_id}` with the current user's ID -- **Frontend:** All existing frontend code using `useCurrentUser()` continues to work -- **Backend:** Other services should use `/users/{user_id}` for user data - -## Files Modified -1. `services/auth/app/api/auth_operations.py` - Removed endpoint -2. `frontend/src/api/services/user.ts` - Updated service method -3. `frontend/src/api/client/apiClient.ts` - Updated endpoint configuration - -## Verification -All changes have been verified with the verification script and pass syntax checks. \ No newline at end of file diff --git a/SUBSCRIPTION_ARCHITECTURE_REDESIGN.md b/SUBSCRIPTION_ARCHITECTURE_REDESIGN.md deleted file mode 100644 index 24a868cf..00000000 --- a/SUBSCRIPTION_ARCHITECTURE_REDESIGN.md +++ /dev/null @@ -1,361 +0,0 @@ -# Subscription System Architecture Redesign - IMPLEMENTED - -## Overview - -This document outlines the **completed implementation** of the comprehensive subscription system architecture redesign. The implementation improves organization, maintainability, and separation of concerns while respecting the existing service-based client architecture. - -## Current State - FULLY IMPLEMENTED ✅ - -### Issues Resolved - -1. ✅ **Mixed Concerns**: Subscription endpoints are now properly separated from tenant management -2. ✅ **Inconsistent URL Patterns**: All endpoints now use consistent `/tenants/{tenant_id}/subscription/*` structure -3. ✅ **Poor Organization**: Subscription operations are now centralized in one API file -4. ✅ **Middleware Dependencies**: All middleware now uses updated URL patterns -5. ✅ **Client Layer Confusion**: Tenant client contains only tenant-specific methods, subscription methods use proper patterns - -## New Architecture Design - FULLY IMPLEMENTED ✅ - -### API Structure & URL Patterns - -#### Public Endpoints (No Authentication) - ✅ IMPLEMENTED -``` -GET /api/v1/plans - Get all available plans -GET /api/v1/plans/{tier} - Get specific plan details -GET /api/v1/plans/{tier}/features - Get plan features -GET /api/v1/plans/{tier}/limits - Get plan limits -GET /api/v1/plans/compare - Compare all plans -``` - -#### Registration Flow (No Tenant Context) - ✅ IMPLEMENTED -``` -POST /api/v1/registration/payment-setup - Start registration with payment -POST /api/v1/registration/complete - Complete registration after 3DS -GET /api/v1/registration/state/{state_id} - Check registration state -``` - -#### Tenant-Dependent Subscription Endpoints - ✅ IMPLEMENTED -``` -GET /api/v1/tenants/{tenant_id}/subscription/status - Get subscription status -GET /api/v1/tenants/{tenant_id}/subscription/details - Get full subscription details -GET /api/v1/tenants/{tenant_id}/subscription/tier - Get subscription tier (cached) -GET /api/v1/tenants/{tenant_id}/subscription/limits - Get subscription limits -GET /api/v1/tenants/{tenant_id}/subscription/usage - Get usage summary -GET /api/v1/tenants/{tenant_id}/subscription/features/{feature} - Check feature access - -# Subscription Management -POST /api/v1/tenants/{tenant_id}/subscription/cancel - Cancel subscription -POST /api/v1/tenants/{tenant_id}/subscription/reactivate - Reactivate subscription -GET /api/v1/tenants/{tenant_id}/subscription/validate-upgrade/{new_plan} - Validate upgrade -POST /api/v1/tenants/{tenant_id}/subscription/upgrade - Upgrade subscription - -# Quota & Limit Checks -GET /api/v1/tenants/{tenant_id}/subscription/limits/locations - Check location limits -GET /api/v1/tenants/{tenant_id}/subscription/limits/products - Check product limits -GET /api/v1/tenants/{tenant_id}/subscription/limits/users - Check user limits -GET /api/v1/tenants/{tenant_id}/subscription/limits/recipes - Check recipe limits -GET /api/v1/tenants/{tenant_id}/subscription/limits/suppliers - Check supplier limits - -# Payment Management -GET /api/v1/tenants/{tenant_id}/subscription/payment-method - Get payment method -POST /api/v1/tenants/{tenant_id}/subscription/payment-method - Update payment method -GET /api/v1/tenants/{tenant_id}/subscription/invoices - Get invoices -``` - -### Service Layer Organization - ✅ IMPLEMENTED - -**`subscription.py`** - All subscription-related endpoints -- Registration flow endpoints -- Tenant-dependent subscription endpoints -- Subscription management endpoints -- Quota and limit checks -- Payment management endpoints - -**`tenant_operations.py`** - Only tenant-centric operations -- Tenant creation/management -- Tenant hierarchy operations -- Tenant settings management -- Tenant location management - -**`plans.py`** - Public plan information (unchanged) - -## Implementation Status - COMPLETE ✅ - -### Phase 1: Backend API Reorganization - ✅ COMPLETED - -#### Step 1: Move Subscription Endpoints - ✅ DONE -- ✅ Moved all subscription endpoints from `tenant_operations.py` to `subscription.py` -- ✅ Implemented new URL patterns with consistent structure -- ✅ Updated all endpoint implementations to use new paths - -**Files Modified:** -- `services/tenant/app/api/subscription.py` - ✅ All subscription endpoints added -- `services/tenant/app/api/tenant_operations.py` - ✅ Subscription endpoints removed - -#### Step 2: Update Tenant Service Main.py - ✅ DONE -- ✅ Ensured proper router inclusion for new subscription endpoints -- ✅ Removed old subscription endpoints from tenant operations router - -**Files Modified:** -- `services/tenant/app/main.py` - ✅ Router inclusion updated - -### Phase 2: Update Tenant Client - ✅ COMPLETED - -#### Step 3: Update Tenant Client URL Patterns - ✅ DONE - -All subscription-related methods in `shared/clients/tenant_client.py` updated: - -```python -# ✅ All methods already updated with new URL patterns -async def get_subscription_status(self, tenant_id: str) -> Optional[Dict[str, Any]]: - result = await self.get(f"tenants/{tenant_id}/subscription/status") - return result - -async def get_subscription_details(self, tenant_id: str) -> Optional[Dict[str, Any]]: - result = await self.get(f"tenants/{tenant_id}/subscription/details") - return result - -# ✅ All other subscription methods similarly updated -``` - -**Files Modified:** -- `shared/clients/tenant_client.py` - ✅ All subscription methods updated - -### Phase 3: Gateway Updates - ✅ COMPLETED - -#### Step 4: Update Gateway Routes - ✅ DONE - -```python -# ✅ All gateway routes updated with new patterns -@router.get("/plans") -@router.get("/plans/{tier}") -@router.get("/plans/{tier}/features") -@router.get("/plans/{tier}/limits") -@router.get("/plans/compare") - -# ✅ Registration flow -@router.post("/registration/payment-setup") -@router.post("/registration/complete") -@router.get("/registration/state/{state_id}") - -# ✅ Tenant subscription endpoints -@router.get("/tenants/{tenant_id}/subscription/status") -@router.get("/tenants/{tenant_id}/subscription/details") -@router.get("/tenants/{tenant_id}/subscription/tier") -@router.get("/tenants/{tenant_id}/subscription/limits") -@router.get("/tenants/{tenant_id}/subscription/usage") -@router.get("/tenants/{tenant_id}/subscription/features/{feature}") -@router.post("/tenants/{tenant_id}/subscription/cancel") -@router.post("/tenants/{tenant_id}/subscription/reactivate") -@router.get("/tenants/{tenant_id}/subscription/validate-upgrade/{new_plan}") -@router.post("/tenants/{tenant_id}/subscription/upgrade") -@router.get("/tenants/{tenant_id}/subscription/limits/locations") -@router.get("/tenants/{tenant_id}/subscription/limits/products") -@router.get("/tenants/{tenant_id}/subscription/limits/users") -@router.get("/tenants/{tenant_id}/subscription/limits/recipes") -@router.get("/tenants/{tenant_id}/subscription/limits/suppliers") -@router.get("/tenants/{tenant_id}/subscription/payment-method") -@router.post("/tenants/{tenant_id}/subscription/payment-method") -@router.get("/tenants/{tenant_id}/subscription/invoices") -``` - -**Files Modified:** -- `gateway/app/routes/subscription.py` - ✅ All gateway routes updated - -#### Step 5: Update Middleware URL Patterns - ✅ DONE - -```python -# ✅ In gateway/app/middleware/auth.py -async def _get_tenant_subscription_tier(self, tenant_id: str, request: Request) -> Optional[str]: - response = await self._make_request( - "GET", - f"{settings.TENANT_SERVICE_URL}/api/v1/tenants/{tenant_id}/subscription/tier" - ) -``` - -```python -# ✅ In gateway/app/middleware/read_only_mode.py -async def check_subscription_status(self, tenant_id: str, authorization: str) -> dict: - response = await self._make_request( - "GET", - f"{settings.TENANT_SERVICE_URL}/api/v1/tenants/{tenant_id}/subscription/status" - ) -``` - -**Files Modified:** -- `gateway/app/middleware/subscription.py` - ✅ URL patterns updated -- `gateway/app/middleware/auth.py` - ✅ Subscription tier lookup URL updated -- `gateway/app/middleware/read_only_mode.py` - ✅ Subscription status check URL updated -- `gateway/app/middleware/rate_limiting.py` - ✅ Subscription tier URL fixed -- `gateway/app/routes/tenant.py` - ✅ Removed conflicting wildcard route - -### Phase 4: Frontend Updates - ✅ COMPLETED - -#### Step 6: Update Frontend Subscription Service - ✅ DONE - -```typescript -// ✅ In frontend/src/api/services/subscription.ts -export class SubscriptionService { - // ✅ All methods updated to use new URL patterns - - async getSubscriptionStatus(tenantId: string): Promise { - return apiClient.get(`/tenants/${tenantId}/subscription/status`); - } - - async getSubscriptionDetails(tenantId: string): Promise { - return apiClient.get(`/tenants/${tenantId}/subscription/details`); - } - - async getSubscriptionTier(tenantId: string): Promise { - return apiClient.get(`/tenants/${tenantId}/subscription/tier`); - } - - async getUsageSummary(tenantId: string): Promise { - return apiClient.get(`/tenants/${tenantId}/subscription/usage`); - } - - async checkFeatureAccess(tenantId: string, featureName: string): Promise { - return apiClient.get(`/tenants/${tenantId}/subscription/features/${featureName}`); - } - - async cancelSubscription(tenantId: string, reason?: string): Promise { - return apiClient.post(`/tenants/${tenantId}/subscription/cancel`, { reason }); - } - - async reactivateSubscription(tenantId: string, plan: string = 'starter'): Promise { - return apiClient.post(`/tenants/${tenantId}/subscription/reactivate`, { plan }); - } - - async validatePlanUpgrade(tenantId: string, newPlan: string): Promise { - return apiClient.get(`/tenants/${tenantId}/subscription/validate-upgrade/${newPlan}`); - } - - async upgradePlan(tenantId: string, newPlan: string): Promise { - return apiClient.post(`/tenants/${tenantId}/subscription/upgrade`, { new_plan: newPlan }); - } - - async canAddLocation(tenantId: string): Promise { - return apiClient.get(`/tenants/${tenantId}/subscription/limits/locations`); - } - - async canAddProduct(tenantId: string): Promise { - return apiClient.get(`/tenants/${tenantId}/subscription/limits/products`); - } - - async canAddUser(tenantId: string): Promise { - return apiClient.get(`/tenants/${tenantId}/subscription/limits/users`); - } - - async canAddRecipe(tenantId: string): Promise { - return apiClient.get(`/tenants/${tenantId}/subscription/limits/recipes`); - } - - async canAddSupplier(tenantId: string): Promise { - return apiClient.get(`/tenants/${tenantId}/subscription/limits/suppliers`); - } - - async getPaymentMethod(tenantId: string): Promise { - return apiClient.get(`/tenants/${tenantId}/subscription/payment-method`); - } - - async updatePaymentMethod(tenantId: string, paymentMethodId: string): Promise { - return apiClient.post(`/tenants/${tenantId}/subscription/payment-method`, { payment_method_id: paymentMethodId }); - } - - async getInvoices(tenantId: string): Promise { - return apiClient.get(`/tenants/${tenantId}/subscription/invoices`); - } - - // ✅ Registration flow methods - async startRegistrationPaymentSetup(userData: any): Promise { - return apiClient.post('/registration/payment-setup', userData); - } - - async completeRegistration(setupIntentId: string, userData: any): Promise { - return apiClient.post('/registration/complete', { - setup_intent_id: setupIntentId, - user_data: userData - }); - } -} -``` - -**Files Modified:** -- `frontend/src/api/services/subscription.ts` - ✅ All URL patterns updated -- `frontend/src/api/services/tenant.ts` - ✅ Fixed subscription linking endpoint - -## Additional Fixes - ✅ COMPLETED - -### Service Integration Updates - -**Files Modified:** -- `services/auth/app/utils/subscription_fetcher.py` - ✅ Fixed subscription details URLs -- `services/auth/app/api/account_deletion.py` - ✅ Fixed subscription status URL -- `services/inventory/app/api/ingredients.py` - ✅ Fixed quota check URLs -- `services/tenant/tests/integration/test_subscription_creation_flow.py` - ✅ Fixed test URLs - -## Benefits Achieved ✅ - -1. **Clear Separation of Concerns**: Subscription operations are properly separated from tenant management operations -2. **Consistent URL Patterns**: All subscription endpoints follow a logical, standardized structure -3. **Better Organization**: Easier to find and maintain subscription-related code -4. **Service-Based Architecture**: Maintains the constraint of using tenant client only -5. **Improved Maintainability**: Changes to subscription logic are localized to one API file -6. **Better Performance**: Clear caching strategies for subscription data -7. **Easier Scaling**: Subscription endpoints can be scaled independently if needed -8. **Cleaner Codebase**: No mixed concerns between tenant and subscription operations - -## Implementation Summary - COMPLETE ✅ - -### Files Modified - -**Backend Services:** -- `services/tenant/app/api/subscription.py` - ✅ Add all subscription endpoints -- `services/tenant/app/api/tenant_operations.py` - ✅ Remove subscription endpoints -- `services/tenant/app/main.py` - ✅ Update router inclusion - -**Shared Client Layer:** -- `shared/clients/tenant_client.py` - ✅ Update all subscription methods with new URL patterns - -**Gateway Layer:** -- `gateway/app/routes/subscription.py` - ✅ Update all gateway routes with new patterns -- `gateway/app/routes/tenant.py` - ✅ Remove conflicting wildcard route -- `gateway/app/middleware/subscription.py` - ✅ Update URL patterns -- `gateway/app/middleware/auth.py` - ✅ Update subscription tier lookup URL -- `gateway/app/middleware/read_only_mode.py` - ✅ Update subscription status check URL -- `gateway/app/middleware/rate_limiting.py` - ✅ Fix subscription tier URL - -**Frontend Layer:** -- `frontend/src/api/services/subscription.ts` - ✅ Update all URL patterns -- `frontend/src/api/services/tenant.ts` - ✅ Fix subscription linking endpoint - -**Service Integration:** -- `services/auth/app/utils/subscription_fetcher.py` - ✅ Fix subscription details URLs -- `services/auth/app/api/account_deletion.py` - ✅ Fix subscription status URL -- `services/inventory/app/api/ingredients.py` - ✅ Fix quota check URLs -- `services/tenant/tests/integration/test_subscription_creation_flow.py` - ✅ Fix test URLs - -### Implementation Order - -1. **Backend API Reorganization** (Phase 1) - ✅ COMPLETED -2. **Update Tenant Client** (Phase 2) - ✅ COMPLETED -3. **Gateway Updates** (Phase 3) - ✅ COMPLETED -4. **Frontend Updates** (Phase 4) - ✅ COMPLETED -5. **Service Integration Fixes** - ✅ COMPLETED - -## Verification Status ✅ - -- ✅ All backend endpoints implemented and tested -- ✅ All client methods updated and verified -- ✅ All gateway routes updated and verified -- ✅ All frontend services updated and verified -- ✅ All middleware updated and verified -- ✅ All service integrations updated and verified -- ✅ No remaining old URL patterns in production code -- ✅ All tests updated to use new patterns - -## Migration Complete ✅ - -The subscription system architecture redesign has been **fully implemented** and is ready for production use. All components are using the new, consistent URL patterns as specified in the architecture redesign. The system maintains proper separation of concerns while respecting the existing service-based client architecture constraint. - -**Status: PRODUCTION READY 🚀** \ No newline at end of file diff --git a/docs/SUBSCRIPTION_ARCHITECTURE_REDESIGN.md b/docs/SUBSCRIPTION_ARCHITECTURE_REDESIGN.md deleted file mode 100644 index b4b40b9b..00000000 --- a/docs/SUBSCRIPTION_ARCHITECTURE_REDESIGN.md +++ /dev/null @@ -1,396 +0,0 @@ -# Subscription System Architecture Redesign - -## Overview - -This document outlines a comprehensive redesign of the subscription system architecture to improve organization, maintainability, and separation of concerns while respecting the existing service-based client architecture. - -## Current Issues - -1. **Mixed Concerns**: Subscription endpoints are scattered across different API files -2. **Inconsistent URL Patterns**: Mixed use of path vs query parameters for tenant IDs -3. **Poor Organization**: Subscription operations mixed with tenant management operations -4. **Middleware Dependencies**: Hardcoded URLs that need updating -5. **Client Layer Confusion**: Tenant client contains many subscription-specific methods - -## New Architecture Design - -### API Structure & URL Patterns - -#### Public Endpoints (No Authentication) -``` -GET /api/v1/plans - Get all available plans -GET /api/v1/plans/{tier} - Get specific plan details -GET /api/v1/plans/{tier}/features - Get plan features -GET /api/v1/plans/{tier}/limits - Get plan limits -GET /api/v1/plans/compare - Compare all plans -``` - -#### Registration Flow (No Tenant Context) -``` -POST /api/v1/registration/payment-setup - Start registration with payment -POST /api/v1/registration/complete - Complete registration after 3DS -GET /api/v1/registration/state/{state_id} - Check registration state -``` - -#### Tenant-Dependent Subscription Endpoints -``` -GET /api/v1/tenants/{tenant_id}/subscription/status - Get subscription status -GET /api/v1/tenants/{tenant_id}/subscription/details - Get full subscription details -GET /api/v1/tenants/{tenant_id}/subscription/tier - Get subscription tier (cached) -GET /api/v1/tenants/{tenant_id}/subscription/limits - Get subscription limits -GET /api/v1/tenants/{tenant_id}/subscription/usage - Get usage summary -GET /api/v1/tenants/{tenant_id}/subscription/features/{feature} - Check feature access - -# Subscription Management -POST /api/v1/tenants/{tenant_id}/subscription/cancel - Cancel subscription -POST /api/v1/tenants/{tenant_id}/subscription/reactivate - Reactivate subscription -GET /api/v1/tenants/{tenant_id}/subscription/validate-upgrade/{new_plan} - Validate upgrade -POST /api/v1/tenants/{tenant_id}/subscription/upgrade - Upgrade subscription - -# Quota & Limit Checks -GET /api/v1/tenants/{tenant_id}/subscription/limits/locations - Check location limits -GET /api/v1/tenants/{tenant_id}/subscription/limits/products - Check product limits -GET /api/v1/tenants/{tenant_id}/subscription/limits/users - Check user limits -GET /api/v1/tenants/{tenant_id}/subscription/limits/recipes - Check recipe limits -GET /api/v1/tenants/{tenant_id}/subscription/limits/suppliers - Check supplier limits - -# Payment Management -GET /api/v1/tenants/{tenant_id}/subscription/payment-method - Get payment method -POST /api/v1/tenants/{tenant_id}/subscription/payment-method - Update payment method -GET /api/v1/tenants/{tenant_id}/subscription/invoices - Get invoices -``` - -### Service Layer Organization - -**`subscription.py`** - All subscription-related endpoints -- Registration flow endpoints -- Tenant-dependent subscription endpoints -- Subscription management endpoints -- Quota and limit checks -- Payment management endpoints - -**`tenant_operations.py`** - Only tenant-centric operations -- Tenant creation/management -- Tenant hierarchy operations -- Tenant settings management -- Tenant location management - -**`plans.py`** - Public plan information (unchanged) - -## Implementation Plan - -### Phase 1: Backend API Reorganization - -#### Step 1: Move Subscription Endpoints -- Move all subscription endpoints from `tenant_operations.py` to `subscription.py` -- Implement new URL patterns with consistent structure -- Update all endpoint implementations to use new paths - -**Files to modify:** -- `services/tenant/app/api/subscription.py` - Add all subscription endpoints -- `services/tenant/app/api/tenant_operations.py` - Remove subscription endpoints - -#### Step 2: Update Tenant Service Main.py -- Ensure proper router inclusion for new subscription endpoints -- Remove old subscription endpoints from tenant operations router - -**Files to modify:** -- `services/tenant/app/main.py` - Update router inclusion - -### Phase 2: Update Tenant Client - -#### Step 3: Update Tenant Client URL Patterns - -Update all subscription-related methods in `shared/clients/tenant_client.py`: - -```python -async def get_subscription_status(self, tenant_id: str) -> Optional[Dict[str, Any]]: - # Updated URL pattern - result = await self.get(f"tenants/{tenant_id}/subscription/status") - return result - -async def get_subscription_details(self, tenant_id: str) -> Optional[Dict[str, Any]]: - # Updated URL pattern - result = await self.get(f"tenants/{tenant_id}/subscription/details") - return result - -# Update all other subscription methods similarly -async def get_subscription_tier(self, tenant_id: str) -> Optional[str]: - result = await self.get(f"tenants/{tenant_id}/subscription/tier") - return result.get('tier') if result else None - -async def get_subscription_limits(self, tenant_id: str) -> Optional[Dict[str, Any]]: - return await self.get(f"tenants/{tenant_id}/subscription/limits") - -async def get_usage_summary(self, tenant_id: str) -> Optional[Dict[str, Any]]: - return await self.get(f"tenants/{tenant_id}/subscription/usage") - -async def has_feature(self, tenant_id: str, feature: str) -> bool: - result = await self.get(f"tenants/{tenant_id}/subscription/features/{feature}") - return result.get('has_feature', False) if result else False - -# Registration flow methods -async def start_registration_payment_setup(self, user_data: Dict[str, Any]) -> Dict[str, Any]: - result = await self.post("registration/payment-setup", user_data) - return result - -async def complete_registration(self, setup_intent_id: str, user_data: Dict[str, Any]) -> Dict[str, Any]: - result = await self.post("registration/complete", { - "setup_intent_id": setup_intent_id, - "user_data": user_data - }) - return result - -# Update quota check methods -async def can_add_location(self, tenant_id: str) -> Dict[str, Any]: - return await self.get(f"tenants/{tenant_id}/subscription/limits/locations") - -async def can_add_product(self, tenant_id: str) -> Dict[str, Any]: - return await self.get(f"tenants/{tenant_id}/subscription/limits/products") - -async def can_add_user(self, tenant_id: str) -> Dict[str, Any]: - return await self.get(f"tenants/{tenant_id}/subscription/limits/users") - -async def can_add_recipe(self, tenant_id: str) -> Dict[str, Any]: - return await self.get(f"tenants/{tenant_id}/subscription/limits/recipes") - -async def can_add_supplier(self, tenant_id: str) -> Dict[str, Any]: - return await self.get(f"tenants/{tenant_id}/subscription/limits/suppliers") - -# Update subscription management methods -async def cancel_subscription(self, tenant_id: str, reason: str = "") -> Dict[str, Any]: - return await self.post(f"tenants/{tenant_id}/subscription/cancel", {"reason": reason}) - -async def reactivate_subscription(self, tenant_id: str, plan: str = "starter") -> Dict[str, Any]: - return await self.post(f"tenants/{tenant_id}/subscription/reactivate", {"plan": plan}) - -async def validate_plan_upgrade(self, tenant_id: str, new_plan: str) -> Dict[str, Any]: - return await self.get(f"tenants/{tenant_id}/subscription/validate-upgrade/{new_plan}") - -async def upgrade_subscription_plan(self, tenant_id: str, new_plan: str) -> Dict[str, Any]: - return await self.post(f"tenants/{tenant_id}/subscription/upgrade", {"new_plan": new_plan}) - -# Update payment management methods -async def get_payment_method(self, tenant_id: str) -> Optional[Dict[str, Any]]: - return await self.get(f"tenants/{tenant_id}/subscription/payment-method") - -async def update_payment_method(self, tenant_id: str, payment_method_id: str) -> Dict[str, Any]: - return await self.post(f"tenants/{tenant_id}/subscription/payment-method", { - "payment_method_id": payment_method_id - }) - -async def get_invoices(self, tenant_id: str) -> Optional[List[Dict[str, Any]]]: - return await self.get(f"tenants/{tenant_id}/subscription/invoices") -``` - -**Files to modify:** -- `shared/clients/tenant_client.py` - Update all subscription methods with new URL patterns - -### Phase 3: Gateway Updates - -#### Step 4: Update Gateway Routes - -```python -# In gateway/app/routes/subscription.py - -# Public endpoints -@router.get("/plans") -@router.get("/plans/{tier}") -@router.get("/plans/{tier}/features") -@router.get("/plans/{tier}/limits") -@router.get("/plans/compare") - -# Registration flow -@router.post("/registration/payment-setup") -@router.post("/registration/complete") -@router.get("/registration/state/{state_id}") - -# Tenant subscription endpoints -@router.get("/tenants/{tenant_id}/subscription/status") -@router.get("/tenants/{tenant_id}/subscription/details") -@router.get("/tenants/{tenant_id}/subscription/tier") -@router.get("/tenants/{tenant_id}/subscription/limits") -@router.get("/tenants/{tenant_id}/subscription/usage") -@router.get("/tenants/{tenant_id}/subscription/features/{feature}") -@router.post("/tenants/{tenant_id}/subscription/cancel") -@router.post("/tenants/{tenant_id}/subscription/reactivate") -@router.get("/tenants/{tenant_id}/subscription/validate-upgrade/{new_plan}") -@router.post("/tenants/{tenant_id}/subscription/upgrade") -@router.get("/tenants/{tenant_id}/subscription/limits/locations") -@router.get("/tenants/{tenant_id}/subscription/limits/products") -@router.get("/tenants/{tenant_id}/subscription/limits/users") -@router.get("/tenants/{tenant_id}/subscription/limits/recipes") -@router.get("/tenants/{tenant_id}/subscription/limits/suppliers") -@router.get("/tenants/{tenant_id}/subscription/payment-method") -@router.post("/tenants/{tenant_id}/subscription/payment-method") -@router.get("/tenants/{tenant_id}/subscription/invoices") -``` - -**Files to modify:** -- `gateway/app/routes/subscription.py` - Update all gateway routes with new patterns - -#### Step 5: Update Middleware URL Patterns - -```python -# In gateway/app/middleware/auth.py -async def _get_tenant_subscription_tier(self, tenant_id: str, request: Request) -> Optional[str]: - try: - # Updated URL pattern - response = await self._make_request( - "GET", - f"{settings.TENANT_SERVICE_URL}/api/v1/tenants/{tenant_id}/subscription/tier" - ) - # ... rest of method remains the same -``` - -```python -# In gateway/app/middleware/read_only_mode.py -async def check_subscription_status(self, tenant_id: str, authorization: str) -> dict: - try: - # Updated URL pattern - response = await self._make_request( - "GET", - f"{settings.TENANT_SERVICE_URL}/api/v1/tenants/{tenant_id}/subscription/status" - ) - # ... rest of method remains the same -``` - -**Files to modify:** -- `gateway/app/middleware/subscription.py` - Update URL patterns -- `gateway/app/middleware/auth.py` - Update subscription tier lookup URL -- `gateway/app/middleware/read_only_mode.py` - Update subscription status check URL - -### Phase 4: Frontend Updates - -#### Step 6: Update Frontend Subscription Service - -```typescript -// In frontend/src/api/services/subscription.ts -export class SubscriptionService { - // Update all methods to use new URL patterns - - async getSubscriptionStatus(tenantId: string): Promise { - return apiClient.get(`/tenants/${tenantId}/subscription/status`); - } - - async getSubscriptionDetails(tenantId: string): Promise { - return apiClient.get(`/tenants/${tenantId}/subscription/details`); - } - - async getSubscriptionTier(tenantId: string): Promise { - return apiClient.get(`/tenants/${tenantId}/subscription/tier`); - } - - async getUsageSummary(tenantId: string): Promise { - return apiClient.get(`/tenants/${tenantId}/subscription/usage`); - } - - async checkFeatureAccess(tenantId: string, featureName: string): Promise { - return apiClient.get(`/tenants/${tenantId}/subscription/features/${featureName}`); - } - - async cancelSubscription(tenantId: string, reason?: string): Promise { - return apiClient.post(`/tenants/${tenantId}/subscription/cancel`, { reason }); - } - - async reactivateSubscription(tenantId: string, plan: string = 'starter'): Promise { - return apiClient.post(`/tenants/${tenantId}/subscription/reactivate`, { plan }); - } - - async validatePlanUpgrade(tenantId: string, newPlan: string): Promise { - return apiClient.get(`/tenants/${tenantId}/subscription/validate-upgrade/${newPlan}`); - } - - async upgradePlan(tenantId: string, newPlan: string): Promise { - return apiClient.post(`/tenants/${tenantId}/subscription/upgrade`, { new_plan: newPlan }); - } - - async canAddLocation(tenantId: string): Promise { - return apiClient.get(`/tenants/${tenantId}/subscription/limits/locations`); - } - - async canAddProduct(tenantId: string): Promise { - return apiClient.get(`/tenants/${tenantId}/subscription/limits/products`); - } - - async canAddUser(tenantId: string): Promise { - return apiClient.get(`/tenants/${tenantId}/subscription/limits/users`); - } - - async canAddRecipe(tenantId: string): Promise { - return apiClient.get(`/tenants/${tenantId}/subscription/limits/recipes`); - } - - async canAddSupplier(tenantId: string): Promise { - return apiClient.get(`/tenants/${tenantId}/subscription/limits/suppliers`); - } - - async getPaymentMethod(tenantId: string): Promise { - return apiClient.get(`/tenants/${tenantId}/subscription/payment-method`); - } - - async updatePaymentMethod(tenantId: string, paymentMethodId: string): Promise { - return apiClient.post(`/tenants/${tenantId}/subscription/payment-method`, { payment_method_id: paymentMethodId }); - } - - async getInvoices(tenantId: string): Promise { - return apiClient.get(`/tenants/${tenantId}/subscription/invoices`); - } - - // Registration flow methods - async startRegistrationPaymentSetup(userData: any): Promise { - return apiClient.post('/registration/payment-setup', userData); - } - - async completeRegistration(setupIntentId: string, userData: any): Promise { - return apiClient.post('/registration/complete', { - setup_intent_id: setupIntentId, - user_data: userData - }); - } -} -``` - -**Files to modify:** -- `frontend/src/api/services/subscription.ts` - Update all URL patterns - -## Benefits of New Architecture - -1. **Clear Separation of Concerns**: Subscription operations are properly separated from tenant management operations -2. **Consistent URL Patterns**: All subscription endpoints follow a logical, standardized structure -3. **Better Organization**: Easier to find and maintain subscription-related code -4. **Service-Based Architecture**: Maintains the constraint of using tenant client only -5. **Improved Maintainability**: Changes to subscription logic are localized to one API file -6. **Better Performance**: Clear caching strategies for subscription data -7. **Easier Scaling**: Subscription endpoints can be scaled independently if needed -8. **Cleaner Codebase**: No mixed concerns between tenant and subscription operations - -## Implementation Summary - -### Files to Modify - -**Backend Services:** -- `services/tenant/app/api/subscription.py` - Add all subscription endpoints -- `services/tenant/app/api/tenant_operations.py` - Remove subscription endpoints -- `services/tenant/app/main.py` - Update router inclusion - -**Shared Client Layer:** -- `shared/clients/tenant_client.py` - Update all subscription methods with new URL patterns - -**Gateway Layer:** -- `gateway/app/routes/subscription.py` - Update all gateway routes with new patterns -- `gateway/app/middleware/subscription.py` - Update URL patterns -- `gateway/app/middleware/auth.py` - Update subscription tier lookup URL -- `gateway/app/middleware/read_only_mode.py` - Update subscription status check URL - -**Frontend Layer:** -- `frontend/src/api/services/subscription.ts` - Update all URL patterns - -### Implementation Order - -1. **Backend API Reorganization** (Phase 1) -2. **Update Tenant Client** (Phase 2) -3. **Gateway Updates** (Phase 3) -4. **Frontend Updates** (Phase 4) - -This comprehensive redesign creates a clean, modern subscription system with proper separation of concerns while respecting the existing service-based client architecture constraint. \ No newline at end of file diff --git a/test_user_endpoint_changes.py b/test_user_endpoint_changes.py deleted file mode 100755 index b1a7e62f..00000000 --- a/test_user_endpoint_changes.py +++ /dev/null @@ -1,170 +0,0 @@ -#!/usr/bin/env python3 -""" -Test script to verify that the user endpoint changes work correctly. -This script tests the new user endpoint structure after removing /auth/me. -""" - -import asyncio -import httpx -import json -from typing import Dict, Any - - -class UserEndpointTester: - """Test the user endpoint changes""" - - def __init__(self): - self.base_url = "http://localhost:8000" # Gateway URL - self.auth_service_url = "http://localhost:8001" # Auth service URL - self.timeout = 30.0 - - # Test data - self.test_user_id = "00000000-0000-0000-0000-000000000001" # Example UUID - self.test_auth_token = "test_token_12345" - - async def test_auth_service_user_endpoint(self): - """Test that the auth service user endpoint works correctly""" - print("🧪 Testing auth service user endpoint...") - - try: - async with httpx.AsyncClient(timeout=self.timeout) as client: - # Test GET /api/v1/auth/users/{user_id} - url = f"{self.auth_service_url}/api/v1/auth/users/{self.test_user_id}" - headers = { - "Authorization": f"Bearer {self.test_auth_token}", - "Content-Type": "application/json" - } - - print(f"📡 Requesting: GET {url}") - response = await client.get(url, headers=headers) - - print(f"📤 Response status: {response.status_code}") - print(f"📦 Response headers: {dict(response.headers)}") - - if response.status_code == 200: - data = response.json() - print(f"📋 Response data: {json.dumps(data, indent=2)}") - return True - elif response.status_code == 404: - print("⚠️ User not found (expected for test user)") - return True - else: - print(f"❌ Unexpected status code: {response.status_code}") - print(f"📋 Response: {response.text}") - return False - - except Exception as e: - print(f"❌ Error testing auth service: {e}") - return False - - async def test_gateway_user_endpoint(self): - """Test that the gateway user endpoint works correctly""" - print("\n🧪 Testing gateway user endpoint...") - - try: - async with httpx.AsyncClient(timeout=self.timeout) as client: - # Test GET /api/v1/users/{user_id} - url = f"{self.base_url}/api/v1/users/{self.test_user_id}" - headers = { - "Authorization": f"Bearer {self.test_auth_token}", - "Content-Type": "application/json" - } - - print(f"📡 Requesting: GET {url}") - response = await client.get(url, headers=headers) - - print(f"📤 Response status: {response.status_code}") - print(f"📦 Response headers: {dict(response.headers)}") - - if response.status_code == 200: - data = response.json() - print(f"📋 Response data: {json.dumps(data, indent=2)}") - return True - elif response.status_code == 404: - print("⚠️ User not found (expected for test user)") - return True - else: - print(f"❌ Unexpected status code: {response.status_code}") - print(f"📋 Response: {response.text}") - return False - - except Exception as e: - print(f"❌ Error testing gateway: {e}") - return False - - async def test_auth_me_endpoint_removed(self): - """Test that the /auth/me endpoint has been removed""" - print("\n🧪 Testing that /auth/me endpoint has been removed...") - - try: - async with httpx.AsyncClient(timeout=self.timeout) as client: - # Test GET /api/v1/auth/me (should return 404) - url = f"{self.auth_service_url}/api/v1/auth/me" - headers = { - "Authorization": f"Bearer {self.test_auth_token}", - "Content-Type": "application/json" - } - - print(f"📡 Requesting: GET {url}") - response = await client.get(url, headers=headers) - - print(f"📤 Response status: {response.status_code}") - - if response.status_code == 404: - print("✅ /auth/me endpoint correctly returns 404 (removed)") - return True - else: - print(f"❌ /auth/me endpoint still exists (status: {response.status_code})") - return False - - except Exception as e: - print(f"❌ Error testing /auth/me removal: {e}") - return False - - async def run_all_tests(self): - """Run all tests""" - print("🚀 Starting user endpoint change tests...\n") - - results = [] - - # Test 1: Auth service user endpoint - result1 = await self.test_auth_service_user_endpoint() - results.append(("Auth service user endpoint", result1)) - - # Test 2: Gateway user endpoint - result2 = await self.test_gateway_user_endpoint() - results.append(("Gateway user endpoint", result2)) - - # Test 3: /auth/me endpoint removed - result3 = await self.test_auth_me_endpoint_removed() - results.append(("/auth/me endpoint removed", result3)) - - # Print summary - print("\n" + "="*60) - print("📊 TEST SUMMARY") - print("="*60) - - all_passed = True - for test_name, passed in results: - status = "✅ PASS" if passed else "❌ FAIL" - print(f"{status} {test_name}") - if not passed: - all_passed = False - - print("="*60) - - if all_passed: - print("🎉 All tests passed! User endpoint changes are working correctly.") - else: - print("⚠️ Some tests failed. Please check the implementation.") - - return all_passed - - -if __name__ == "__main__": - tester = UserEndpointTester() - - # Run tests - success = asyncio.run(tester.run_all_tests()) - - exit(0 if success else 1) \ No newline at end of file diff --git a/verify_changes.py b/verify_changes.py deleted file mode 100755 index 81bc408d..00000000 --- a/verify_changes.py +++ /dev/null @@ -1,149 +0,0 @@ -#!/usr/bin/env python3 -""" -Verification script to check that the user endpoint changes are syntactically correct. -""" - -import os -import re -import sys -from pathlib import Path - - -def check_file_syntax(file_path: str) -> bool: - """Check if a file has valid syntax (Python or TypeScript)""" - try: - with open(file_path, 'r', encoding='utf-8') as f: - content = f.read() - - # Skip syntax check for TypeScript files - if file_path.endswith('.ts') or file_path.endswith('.tsx'): - print(f"✅ TypeScript file syntax check skipped for {file_path}") - return True - - # Basic syntax check for Python files - compile(content, file_path, 'exec') - return True - except SyntaxError as e: - print(f"❌ Syntax error in {file_path}: {e}") - return False - except Exception as e: - print(f"❌ Error reading {file_path}: {e}") - return False - - -def check_auth_me_removed(file_path: str) -> bool: - """Check that /auth/me endpoint has been removed""" - try: - with open(file_path, 'r', encoding='utf-8') as f: - content = f.read() - - # Look for the /auth/me endpoint definition - if '@router.get("/me"' in content or 'def get_current_user(' in content: - print(f"❌ /auth/me endpoint still exists in {file_path}") - return False - - return True - except Exception as e: - print(f"❌ Error checking {file_path}: {e}") - return False - - -def check_user_service_updated(file_path: str) -> bool: - """Check that UserService has been updated to use user ID""" - try: - with open(file_path, 'r', encoding='utf-8') as f: - content = f.read() - - # Check for the new implementation - if 'useAuthStore.getState()' in content and 'userId = authStore.user?.id' in content: - print(f"✅ UserService correctly updated in {file_path}") - return True - else: - print(f"❌ UserService not properly updated in {file_path}") - return False - except Exception as e: - print(f"❌ Error checking {file_path}: {e}") - return False - - -def check_api_client_updated(file_path: str) -> bool: - """Check that API client has been updated""" - try: - with open(file_path, 'r', encoding='utf-8') as f: - content = f.read() - - # Check that /auth/me is no longer in noTenantEndpoints (but allow /auth/me/onboarding) - lines = content.split('\n') - found_auth_me_alone = False - - for line in lines: - # Look for /auth/me but not /auth/me/onboarding - if line.strip() == "'/auth/me'," or line.strip().endswith("/auth/me',"): - found_auth_me_alone = True - break - - if found_auth_me_alone: - print(f"❌ /auth/me still in noTenantEndpoints in {file_path}") - return False - else: - print(f"✅ API client correctly updated in {file_path}") - return True - except Exception as e: - print(f"❌ Error checking {file_path}: {e}") - return False - - -def main(): - """Main verification function""" - print("🔍 Verifying user endpoint changes...") - print("=" * 60) - - # Files to check - files_to_check = [ - { - 'path': '/Users/urtzialfaro/Documents/bakery-ia/services/auth/app/api/auth_operations.py', - 'checks': [check_file_syntax, check_auth_me_removed] - }, - { - 'path': '/Users/urtzialfaro/Documents/bakery-ia/frontend/src/api/services/user.ts', - 'checks': [check_file_syntax, check_user_service_updated] - }, - { - 'path': '/Users/urtzialfaro/Documents/bakery-ia/frontend/src/api/client/apiClient.ts', - 'checks': [check_file_syntax, check_api_client_updated] - } - ] - - all_passed = True - - for file_info in files_to_check: - file_path = file_info['path'] - checks = file_info['checks'] - - print(f"\n📁 Checking {file_path}...") - - for check in checks: - if not check(file_path): - all_passed = False - - print("\n" + "=" * 60) - - if all_passed: - print("🎉 All verification checks passed!") - print("\n✅ Changes summary:") - print(" • Removed /auth/me endpoint from auth service") - print(" • Updated UserService to use /users/{user_id}") - print(" • Updated API client to remove /auth/me from noTenantEndpoints") - print(" • All files have valid syntax") - else: - print("❌ Some verification checks failed!") - print("\nPlease review the errors above and fix them.") - - print("=" * 60) - - return all_passed - - -if __name__ == "__main__": - success = main() - sys.exit(0 if success else 1) \ No newline at end of file