Implement IUCN API v4 support with fallback to v3. Improved error handling and optimized species search using genus and species names.
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/f9ca419d-567c-41d0-a14d-f345868599e8.jpg
This commit is contained in:
@ -32,6 +32,7 @@ export interface ApiResponse<T> {
|
|||||||
|
|
||||||
export interface TokenResponse {
|
export interface TokenResponse {
|
||||||
token: string | null;
|
token: string | null;
|
||||||
|
iucnToken: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ApiStatusResponse {
|
export interface ApiStatusResponse {
|
||||||
|
202
server/routes.ts
202
server/routes.ts
@ -307,7 +307,7 @@ export async function registerRoutes(app: Express): Promise<Server> {
|
|||||||
apiVersion: "v4",
|
apiVersion: "v4",
|
||||||
message: "IUCN API v4 is connected and responding"
|
message: "IUCN API v4 is connected and responding"
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error: any) {
|
||||||
// If V4 fails, fall back to checking V3 with environment API key
|
// 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, falling back to V3:", error.message);
|
||||||
}
|
}
|
||||||
@ -371,6 +371,39 @@ export async function registerRoutes(app: Express): Promise<Server> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// To avoid 414 errors, we'll limit the name parameter to just the genus and species
|
||||||
|
// This will extract first two parts of scientific name (genus and species)
|
||||||
|
const nameParts = String(name).split(' ');
|
||||||
|
const simplifiedName = nameParts.slice(0, 2).join(' ');
|
||||||
|
const [genusName, speciesName] = nameParts;
|
||||||
|
|
||||||
|
// Try with v4 API first if we have a token
|
||||||
|
const activeToken = await storage.getActiveToken();
|
||||||
|
if (activeToken?.iucnToken) {
|
||||||
|
try {
|
||||||
|
// Use the v4 API with scientific name endpoint
|
||||||
|
const response = await axios.get(`${IUCN_V4_BASE_URL}/taxa/scientific_name`, {
|
||||||
|
headers: {
|
||||||
|
"Authorization": `Bearer ${activeToken.iucnToken}`
|
||||||
|
},
|
||||||
|
params: {
|
||||||
|
genus_name: genusName,
|
||||||
|
species_name: speciesName || ""
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.json({
|
||||||
|
success: true,
|
||||||
|
data: response.data,
|
||||||
|
apiVersion: "v4"
|
||||||
|
});
|
||||||
|
} catch (error: any) {
|
||||||
|
console.log("IUCN V4 API species lookup failed, falling back to V3:", error.message);
|
||||||
|
// Continue to v3 fallback
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback to v3 API
|
||||||
const apiKey = process.env.IUCN_API_KEY;
|
const apiKey = process.env.IUCN_API_KEY;
|
||||||
if (!apiKey) {
|
if (!apiKey) {
|
||||||
return res.status(401).json({
|
return res.status(401).json({
|
||||||
@ -380,11 +413,6 @@ export async function registerRoutes(app: Express): Promise<Server> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// To avoid 414 errors, we'll limit the name parameter to just the genus and species
|
|
||||||
// This will extract first two parts of scientific name (genus and species)
|
|
||||||
const nameParts = String(name).split(' ');
|
|
||||||
const simplifiedName = nameParts.slice(0, 2).join(' ');
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await axios.get(`${IUCN_V3_BASE_URL}/species/name/${encodeURIComponent(simplifiedName)}`, {
|
const response = await axios.get(`${IUCN_V3_BASE_URL}/species/name/${encodeURIComponent(simplifiedName)}`, {
|
||||||
params: { token: apiKey }
|
params: { token: apiKey }
|
||||||
@ -392,7 +420,8 @@ export async function registerRoutes(app: Express): Promise<Server> {
|
|||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
success: true,
|
success: true,
|
||||||
data: response.data
|
data: response.data,
|
||||||
|
apiVersion: "v3"
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (axios.isAxiosError(error) && error.response) {
|
if (axios.isAxiosError(error) && error.response) {
|
||||||
@ -423,6 +452,52 @@ export async function registerRoutes(app: Express): Promise<Server> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// To avoid 414 errors, we'll limit the name parameter to just the genus and species
|
||||||
|
const nameParts = String(name).split(' ');
|
||||||
|
const simplifiedName = nameParts.slice(0, 2).join(' ');
|
||||||
|
const [genusName, speciesName] = nameParts;
|
||||||
|
|
||||||
|
// Try with v4 API first if we have a token
|
||||||
|
const activeToken = await storage.getActiveToken();
|
||||||
|
if (activeToken?.iucnToken) {
|
||||||
|
try {
|
||||||
|
// First, we need to find the species taxon ID using scientific name lookup
|
||||||
|
const taxaResponse = await axios.get(`${IUCN_V4_BASE_URL}/taxa/scientific_name`, {
|
||||||
|
headers: {
|
||||||
|
"Authorization": `Bearer ${activeToken.iucnToken}`
|
||||||
|
},
|
||||||
|
params: {
|
||||||
|
genus_name: genusName,
|
||||||
|
species_name: speciesName || ""
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Check if we found the species
|
||||||
|
if (taxaResponse.data && taxaResponse.data.result && taxaResponse.data.result.length > 0) {
|
||||||
|
const taxonId = taxaResponse.data.result[0].taxonid;
|
||||||
|
|
||||||
|
// Now retrieve the threats using the taxon ID
|
||||||
|
const threatsResponse = await axios.get(`${IUCN_V4_BASE_URL}/threats/species/id/${taxonId}`, {
|
||||||
|
headers: {
|
||||||
|
"Authorization": `Bearer ${activeToken.iucnToken}`
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.json({
|
||||||
|
success: true,
|
||||||
|
data: threatsResponse.data,
|
||||||
|
apiVersion: "v4"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// If no species found, fall back to v3
|
||||||
|
console.log("IUCN V4 API: No species found with the given scientific name, falling back to V3");
|
||||||
|
} catch (error: any) {
|
||||||
|
console.log("IUCN V4 API threats lookup failed, falling back to V3:", error.message);
|
||||||
|
// Continue to v3 fallback
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback to v3 API
|
||||||
const apiKey = process.env.IUCN_API_KEY;
|
const apiKey = process.env.IUCN_API_KEY;
|
||||||
if (!apiKey) {
|
if (!apiKey) {
|
||||||
return res.status(401).json({
|
return res.status(401).json({
|
||||||
@ -432,10 +507,6 @@ export async function registerRoutes(app: Express): Promise<Server> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// To avoid 414 errors, we'll limit the name parameter to just the genus and species
|
|
||||||
const nameParts = String(name).split(' ');
|
|
||||||
const simplifiedName = nameParts.slice(0, 2).join(' ');
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await axios.get(`${IUCN_V3_BASE_URL}/threats/species/name/${encodeURIComponent(simplifiedName)}`, {
|
const response = await axios.get(`${IUCN_V3_BASE_URL}/threats/species/name/${encodeURIComponent(simplifiedName)}`, {
|
||||||
params: { token: apiKey }
|
params: { token: apiKey }
|
||||||
@ -443,7 +514,8 @@ export async function registerRoutes(app: Express): Promise<Server> {
|
|||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
success: true,
|
success: true,
|
||||||
data: response.data
|
data: response.data,
|
||||||
|
apiVersion: "v3"
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (axios.isAxiosError(error) && error.response) {
|
if (axios.isAxiosError(error) && error.response) {
|
||||||
@ -474,6 +546,52 @@ export async function registerRoutes(app: Express): Promise<Server> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// To avoid 414 errors, we'll limit the name parameter to just the genus and species
|
||||||
|
const nameParts = String(name).split(' ');
|
||||||
|
const simplifiedName = nameParts.slice(0, 2).join(' ');
|
||||||
|
const [genusName, speciesName] = nameParts;
|
||||||
|
|
||||||
|
// Try with v4 API first if we have a token
|
||||||
|
const activeToken = await storage.getActiveToken();
|
||||||
|
if (activeToken?.iucnToken) {
|
||||||
|
try {
|
||||||
|
// First, we need to find the species taxon ID using scientific name lookup
|
||||||
|
const taxaResponse = await axios.get(`${IUCN_V4_BASE_URL}/taxa/scientific_name`, {
|
||||||
|
headers: {
|
||||||
|
"Authorization": `Bearer ${activeToken.iucnToken}`
|
||||||
|
},
|
||||||
|
params: {
|
||||||
|
genus_name: genusName,
|
||||||
|
species_name: speciesName || ""
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Check if we found the species
|
||||||
|
if (taxaResponse.data && taxaResponse.data.result && taxaResponse.data.result.length > 0) {
|
||||||
|
const taxonId = taxaResponse.data.result[0].taxonid;
|
||||||
|
|
||||||
|
// Now retrieve the habitats using the taxon ID
|
||||||
|
const habitatsResponse = await axios.get(`${IUCN_V4_BASE_URL}/habitats/species/id/${taxonId}`, {
|
||||||
|
headers: {
|
||||||
|
"Authorization": `Bearer ${activeToken.iucnToken}`
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.json({
|
||||||
|
success: true,
|
||||||
|
data: habitatsResponse.data,
|
||||||
|
apiVersion: "v4"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// If no species found, fall back to v3
|
||||||
|
console.log("IUCN V4 API: No species found with the given scientific name, falling back to V3");
|
||||||
|
} catch (error: any) {
|
||||||
|
console.log("IUCN V4 API habitats lookup failed, falling back to V3:", error.message);
|
||||||
|
// Continue to v3 fallback
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback to v3 API
|
||||||
const apiKey = process.env.IUCN_API_KEY;
|
const apiKey = process.env.IUCN_API_KEY;
|
||||||
if (!apiKey) {
|
if (!apiKey) {
|
||||||
return res.status(401).json({
|
return res.status(401).json({
|
||||||
@ -483,10 +601,6 @@ export async function registerRoutes(app: Express): Promise<Server> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// To avoid 414 errors, we'll limit the name parameter to just the genus and species
|
|
||||||
const nameParts = String(name).split(' ');
|
|
||||||
const simplifiedName = nameParts.slice(0, 2).join(' ');
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await axios.get(`${IUCN_V3_BASE_URL}/habitats/species/name/${encodeURIComponent(simplifiedName)}`, {
|
const response = await axios.get(`${IUCN_V3_BASE_URL}/habitats/species/name/${encodeURIComponent(simplifiedName)}`, {
|
||||||
params: { token: apiKey }
|
params: { token: apiKey }
|
||||||
@ -494,7 +608,8 @@ export async function registerRoutes(app: Express): Promise<Server> {
|
|||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
success: true,
|
success: true,
|
||||||
data: response.data
|
data: response.data,
|
||||||
|
apiVersion: "v3"
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (axios.isAxiosError(error) && error.response) {
|
if (axios.isAxiosError(error) && error.response) {
|
||||||
@ -525,6 +640,52 @@ export async function registerRoutes(app: Express): Promise<Server> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// To avoid 414 errors, we'll limit the name parameter to just the genus and species
|
||||||
|
const nameParts = String(name).split(' ');
|
||||||
|
const simplifiedName = nameParts.slice(0, 2).join(' ');
|
||||||
|
const [genusName, speciesName] = nameParts;
|
||||||
|
|
||||||
|
// Try with v4 API first if we have a token
|
||||||
|
const activeToken = await storage.getActiveToken();
|
||||||
|
if (activeToken?.iucnToken) {
|
||||||
|
try {
|
||||||
|
// First, we need to find the species taxon ID using scientific name lookup
|
||||||
|
const taxaResponse = await axios.get(`${IUCN_V4_BASE_URL}/taxa/scientific_name`, {
|
||||||
|
headers: {
|
||||||
|
"Authorization": `Bearer ${activeToken.iucnToken}`
|
||||||
|
},
|
||||||
|
params: {
|
||||||
|
genus_name: genusName,
|
||||||
|
species_name: speciesName || ""
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Check if we found the species
|
||||||
|
if (taxaResponse.data && taxaResponse.data.result && taxaResponse.data.result.length > 0) {
|
||||||
|
const taxonId = taxaResponse.data.result[0].taxonid;
|
||||||
|
|
||||||
|
// Now retrieve the conservation measures using the taxon ID
|
||||||
|
const measuresResponse = await axios.get(`${IUCN_V4_BASE_URL}/measures/species/id/${taxonId}`, {
|
||||||
|
headers: {
|
||||||
|
"Authorization": `Bearer ${activeToken.iucnToken}`
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.json({
|
||||||
|
success: true,
|
||||||
|
data: measuresResponse.data,
|
||||||
|
apiVersion: "v4"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// If no species found, fall back to v3
|
||||||
|
console.log("IUCN V4 API: No species found with the given scientific name, falling back to V3");
|
||||||
|
} catch (error: any) {
|
||||||
|
console.log("IUCN V4 API conservation measures lookup failed, falling back to V3:", error.message);
|
||||||
|
// Continue to v3 fallback
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback to v3 API
|
||||||
const apiKey = process.env.IUCN_API_KEY;
|
const apiKey = process.env.IUCN_API_KEY;
|
||||||
if (!apiKey) {
|
if (!apiKey) {
|
||||||
return res.status(401).json({
|
return res.status(401).json({
|
||||||
@ -534,10 +695,6 @@ export async function registerRoutes(app: Express): Promise<Server> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// To avoid 414 errors, we'll limit the name parameter to just the genus and species
|
|
||||||
const nameParts = String(name).split(' ');
|
|
||||||
const simplifiedName = nameParts.slice(0, 2).join(' ');
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await axios.get(`${IUCN_V3_BASE_URL}/measures/species/name/${encodeURIComponent(simplifiedName)}`, {
|
const response = await axios.get(`${IUCN_V3_BASE_URL}/measures/species/name/${encodeURIComponent(simplifiedName)}`, {
|
||||||
params: { token: apiKey }
|
params: { token: apiKey }
|
||||||
@ -545,7 +702,8 @@ export async function registerRoutes(app: Express): Promise<Server> {
|
|||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
success: true,
|
success: true,
|
||||||
data: response.data
|
data: response.data,
|
||||||
|
apiVersion: "v3"
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (axios.isAxiosError(error) && error.response) {
|
if (axios.isAxiosError(error) && error.response) {
|
||||||
|
Reference in New Issue
Block a user