freealberta/influence/app/services/represent-api.js

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();