146 lines
4.6 KiB
JavaScript
146 lines
4.6 KiB
JavaScript
const axios = require('axios');
|
|
|
|
class RepresentAPIService {
|
|
constructor() {
|
|
this.baseURL = process.env.REPRESENT_API_BASE || 'https://represent.opennorth.ca';
|
|
this.rateLimit = parseInt(process.env.REPRESENT_API_RATE_LIMIT) || 60;
|
|
this.lastRequestTime = 0;
|
|
this.requestCount = 0;
|
|
this.resetTime = Date.now() + 60000; // Reset every minute
|
|
}
|
|
|
|
async checkRateLimit() {
|
|
const now = Date.now();
|
|
|
|
// Reset counter if a minute has passed
|
|
if (now > this.resetTime) {
|
|
this.requestCount = 0;
|
|
this.resetTime = now + 60000;
|
|
}
|
|
|
|
// Check if we're at the rate limit
|
|
if (this.requestCount >= this.rateLimit) {
|
|
const waitTime = this.resetTime - now;
|
|
throw new Error(`Rate limit exceeded. Please wait ${Math.ceil(waitTime / 1000)} seconds.`);
|
|
}
|
|
|
|
this.requestCount++;
|
|
this.lastRequestTime = now;
|
|
}
|
|
|
|
async makeRequest(endpoint) {
|
|
await this.checkRateLimit();
|
|
|
|
try {
|
|
const response = await axios.get(`${this.baseURL}${endpoint}`, {
|
|
timeout: 10000,
|
|
headers: {
|
|
'User-Agent': 'Alberta-Influence-Campaign-Tool/1.0'
|
|
}
|
|
});
|
|
|
|
return response.data;
|
|
} catch (error) {
|
|
if (error.response) {
|
|
throw new Error(`API Error: ${error.response.status} - ${error.response.statusText}`);
|
|
} else if (error.request) {
|
|
throw new Error('Network error: Unable to reach Represent API');
|
|
} else {
|
|
throw new Error(`Request error: ${error.message}`);
|
|
}
|
|
}
|
|
}
|
|
|
|
async testConnection() {
|
|
try {
|
|
const data = await this.makeRequest('/boundary-sets/?limit=1');
|
|
return {
|
|
success: true,
|
|
message: 'Successfully connected to Represent API',
|
|
sampleData: data
|
|
};
|
|
} catch (error) {
|
|
return {
|
|
success: false,
|
|
message: 'Failed to connect to Represent API',
|
|
error: error.message
|
|
};
|
|
}
|
|
}
|
|
|
|
async getRepresentativesByPostalCode(postalCode) {
|
|
const formattedPostalCode = postalCode.replace(/\s/g, '').toUpperCase();
|
|
|
|
// Validate Alberta postal code (should start with T)
|
|
if (!formattedPostalCode.startsWith('T')) {
|
|
throw new Error('This tool is designed for Alberta postal codes only (starting with T)');
|
|
}
|
|
|
|
try {
|
|
const endpoint = `/postcodes/${formattedPostalCode}/`;
|
|
console.log(`Making Represent API request to: ${this.baseURL}${endpoint}`);
|
|
const data = await this.makeRequest(endpoint);
|
|
|
|
console.log('Represent API Response:', JSON.stringify(data, null, 2));
|
|
console.log(`Representatives concordance count: ${data.representatives_concordance?.length || 0}`);
|
|
console.log(`Representatives centroid count: ${data.representatives_centroid?.length || 0}`);
|
|
|
|
return {
|
|
postalCode: formattedPostalCode,
|
|
city: data.city,
|
|
province: data.province,
|
|
centroid: data.centroid,
|
|
representatives_concordance: data.representatives_concordance || [],
|
|
representatives_centroid: data.representatives_centroid || [],
|
|
boundaries_concordance: data.boundaries_concordance || [],
|
|
boundaries_centroid: data.boundaries_centroid || []
|
|
};
|
|
} catch (error) {
|
|
console.error(`Represent API error for ${formattedPostalCode}:`, error.message);
|
|
throw new Error(`Failed to fetch data for postal code ${formattedPostalCode}: ${error.message}`);
|
|
}
|
|
}
|
|
|
|
async getRepresentativeDetails(representativeUrl) {
|
|
try {
|
|
// Extract the path from the URL
|
|
const urlPath = representativeUrl.replace(this.baseURL, '');
|
|
const data = await this.makeRequest(urlPath);
|
|
return data;
|
|
} catch (error) {
|
|
throw new Error(`Failed to fetch representative details: ${error.message}`);
|
|
}
|
|
}
|
|
|
|
async getBoundaryDetails(boundaryUrl) {
|
|
try {
|
|
// Extract the path from the URL
|
|
const urlPath = boundaryUrl.replace(this.baseURL, '');
|
|
const data = await this.makeRequest(urlPath);
|
|
return data;
|
|
} catch (error) {
|
|
throw new Error(`Failed to fetch boundary details: ${error.message}`);
|
|
}
|
|
}
|
|
|
|
async searchRepresentatives(filters = {}) {
|
|
try {
|
|
const queryParams = new URLSearchParams();
|
|
|
|
// Add filters as query parameters
|
|
Object.keys(filters).forEach(key => {
|
|
if (filters[key]) {
|
|
queryParams.append(key, filters[key]);
|
|
}
|
|
});
|
|
|
|
const endpoint = `/representatives/?${queryParams.toString()}`;
|
|
const data = await this.makeRequest(endpoint);
|
|
return data;
|
|
} catch (error) {
|
|
throw new Error(`Failed to search representatives: ${error.message}`);
|
|
}
|
|
}
|
|
}
|
|
|
|
module.exports = new RepresentAPIService(); |