From 7ebec064864073ab88606c379fe8af37a657e169 Mon Sep 17 00:00:00 2001 From: Magnus-SmariSma <20734986-Magnus-SmariSma@users.noreply.replit.com> Date: Thu, 20 Mar 2025 23:04:52 +0000 Subject: [PATCH] Enhance IUCN API integration: Add support for v4 API, improve error handling, and clarify authentication instructions in README. Replit-Commit-Author: Agent Replit-Commit-Session-Id: e931b5ab-041b-42e7-baf1-50017869cef6 Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/e19c6a51-7e4c-4bb8-a6a6-46dc00f0ec99/fe2c55e5-ba3a-4d5e-84f0-b14bbe529292.jpg --- README.md | 39 +++++++++++++++++--- client/src/components/api-status.tsx | 8 ++--- server/routes.ts | 53 ++++++++++++++++++---------- 3 files changed, 72 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index e5c0c92..b5acda1 100644 --- a/README.md +++ b/README.md @@ -61,10 +61,22 @@ The application requires a CITES+ API token for authentication. To obtain a toke 4. Enter the token in the application's authentication panel ### IUCN Red List API -The application requires an IUCN Red List API key for accessing conservation data. To obtain a key: +The application supports both IUCN Red List API v3 and v4 versions: + +#### IUCN API v3 (Legacy) +To use the v3 API: 1. Visit [https://apiv3.iucnredlist.org/api/v3/docs](https://apiv3.iucnredlist.org/api/v3/docs) 2. Register for an account and request an API key -3. The key will be automatically configured in the application's environment variables +3. The key will be automatically configured in the application's environment variables (`IUCN_API_KEY`) + +#### IUCN API v4 (Recommended) +For enhanced functionality with the v4 API: +1. Visit [https://apiv3.iucnredlist.org/api/v4/docs](https://apiv3.iucnredlist.org/api/v4/docs) +2. Register for an account and request access to the v4 API +3. Generate a bearer token for the v4 API +4. Enter the token in the application's authentication panel (IUCN tab) + +The application will intelligently use v4 if available, with automatic fallback to v3 when needed. ## Getting Started @@ -101,8 +113,10 @@ The application requires an IUCN Red List API key for accessing conservation dat ### Using the Application 1. **API Authentication**: - - Enter your CITES+ API token in the authentication panel - - The API status indicators will show if both APIs are successfully connected + - Click the "API Token" button to open the authentication panel + - In the CITES tab, enter your CITES+ API token + - In the IUCN tab, enter your IUCN v4 bearer token (if available) + - The API status indicators will show if both APIs are successfully connected, including which version of the IUCN API is active 2. **Searching for Species**: - Enter a scientific name (e.g., "Panthera tigris") in the search box @@ -180,9 +194,24 @@ npm test ## Limitations and Known Issues -- IUCN API may return 414 errors for very long scientific names - Some species may not have data in all API sources - API rate limits may apply (refer to API documentation for details) +- IUCN API v3 may return 414 errors for very long scientific names (this is minimized in the implementation) +- IUCN API v4 requires a separate bearer token authentication + +## API Version Handling + +The application intelligently manages API versions with the following approach: + +- **IUCN API Version Selection**: + - The system first attempts to use IUCN API v4 if a bearer token is available + - If v4 returns an error or isn't available, the system automatically falls back to v3 + - All API responses include an `apiVersion` field indicating which version was used + +- **Version-Specific Authentication**: + - CITES+ API: Uses a token-based authentication via query parameter + - IUCN v3: Uses an API key via environment variable and query parameter + - IUCN v4: Uses OAuth 2.0 Bearer token authentication via headers ## Future Enhancements diff --git a/client/src/components/api-status.tsx b/client/src/components/api-status.tsx index 41a8e07..6860600 100644 --- a/client/src/components/api-status.tsx +++ b/client/src/components/api-status.tsx @@ -75,12 +75,12 @@ export default function ApiStatus({ citesToken }: ApiStatusProps) { - + {citesStatus === 'connected' ? 'CITES+ API is connected and working' : citesStatus === 'checking' ? 'Checking CITES+ API connection...' - : citesApiData?.message || 'CITES+ API is not connected. Please add your API token.'} + : citesApiData?.message || 'CITES+ API is not connected. Click "API Token" and add your CITES+ API token.'} @@ -101,12 +101,12 @@ export default function ApiStatus({ citesToken }: ApiStatusProps) { - + {iucnStatus === 'connected' ? `IUCN Red List API ${iucnApiVersion} is connected and working` : iucnStatus === 'checking' ? 'Checking IUCN Red List API connection...' - : iucnApiData?.message || 'IUCN Red List API connection issue. Check the API key.'} + : iucnApiData?.message || 'IUCN Red List API connection issue. To use IUCN API features, click "API Token" and add your API key on the IUCN tab.'} diff --git a/server/routes.ts b/server/routes.ts index 9a0fbd6..1406e5a 100644 --- a/server/routes.ts +++ b/server/routes.ts @@ -291,10 +291,10 @@ export async function registerRoutes(app: Express): Promise { // Try V4 API first (with bearer token) const activeToken = await storage.getActiveToken(); if (activeToken?.iucnToken) { - // Use the version endpoint which is the simplest endpoint - const versionUrl = `${IUCN_V4_BASE_URL}/version`; - try { + // Use a simpler endpoint to check if the API is working - we'll just ping the version endpoint + const versionUrl = `${IUCN_V4_BASE_URL}/version`; + const response = await axios.get(versionUrl, { headers: { "Authorization": `Bearer ${activeToken.iucnToken}` @@ -308,46 +308,61 @@ export async function registerRoutes(app: Express): Promise { message: "IUCN API v4 is connected and responding" }); } catch (error: any) { - // If V4 fails, fall back to checking V3 with environment API key - console.log("IUCN V4 API check failed, falling back to V3:", error.message); + console.log("IUCN V4 API check failed:", error.message); + + // If token is invalid, return a useful message + if (error.response?.status === 401) { + return res.status(401).json({ + success: false, + connected: false, + message: "IUCN API v4 token is invalid. Please check your token and try again." + }); + } + + // For other errors, continue to try v3 + console.log("Falling back to IUCN v3 API check"); } } - // Fallback to V3 API (with query parameter token) + // If no v4 token or v4 check failed, try v3 const apiKey = process.env.IUCN_API_KEY; if (!apiKey) { return res.status(401).json({ success: false, connected: false, - message: "IUCN API key is not configured" + message: "No IUCN API credentials found. Please add your IUCN API key." }); } - // Use the version endpoint for V3 - const versionUrl = `${IUCN_V3_BASE_URL}/version`; - try { - // Make sure we're not including any parameters in the URL itself - only in params object - const response = await axios.get(versionUrl, { + // For v3, use a really simple request to avoid the 414 error + const response = await axios.get(`${IUCN_V3_BASE_URL}/version`, { params: { token: apiKey } }); - res.json({ + return res.json({ success: true, connected: true, apiVersion: "v3", message: "IUCN API v3 is connected and responding" }); - } catch (error) { - if (axios.isAxiosError(error) && error.response) { - return res.status(error.response.status).json({ + } catch (error: any) { + // Handle v3 API errors + console.log("IUCN V3 API check failed:", error.message); + + if (error.response?.status === 401) { + return res.status(401).json({ success: false, connected: false, - message: "Failed to connect to IUCN API: " + (error.response?.data?.message || error.message), - status: error.response.status + message: "IUCN API v3 key is invalid. Please check your environment variables." }); } - throw error; + + return res.status(error.response?.status || 500).json({ + success: false, + connected: false, + message: "Failed to connect to IUCN API: " + (error.response?.data?.message || error.message) + }); } } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error);