68 lines
2.6 KiB
JavaScript

const axios = require('axios');
const logger = require('../utils/logger');
const SOCRATA_DOMAIN = 'https://data.edmonton.ca';
class SocrataService {
constructor() {
this.client = axios.create({
baseURL: SOCRATA_DOMAIN,
timeout: 30000 // 30 seconds for potentially large datasets
});
this.client.interceptors.response.use(
response => response,
error => {
logger.error('Socrata API Error:', {
message: error.message,
url: error.config?.url,
status: error.response?.status,
data: error.response?.data
});
throw error;
}
);
}
/**
* Fetches data from a Socrata dataset.
* @param {string} datasetId - The ID of the dataset (e.g., 'nggt-rwac').
* @param {object} params - SoQL query parameters.
* @returns {Promise<Array>} A promise that resolves to an array of records.
*/
async get(datasetId, params = {}) {
try {
logger.info(`Fetching Socrata dataset ${datasetId} with params:`, params);
// Socrata uses an app token for higher rate limits, but it's not required for public data.
// We can add an X-App-Token header here if one is obtained.
const response = await this.client.get(`/resource/${datasetId}.json`, { params });
logger.info(`Successfully fetched ${response.data.length} records from Socrata dataset ${datasetId}`);
return response.data;
} catch (error) {
logger.error(`Failed to fetch Socrata dataset ${datasetId}`, {
message: error.message,
status: error.response?.status,
statusText: error.response?.statusText,
data: error.response?.data,
url: error.config?.url,
params: params
});
// Provide more specific error messages
if (error.response?.status === 404) {
throw new Error(`Dataset ${datasetId} not found on Socrata API`);
} else if (error.response?.status === 400) {
throw new Error(`Invalid query parameters for dataset ${datasetId}`);
} else if (error.code === 'ENOTFOUND' || error.code === 'ECONNREFUSED') {
throw new Error('Unable to connect to Edmonton Open Data Portal');
}
throw new Error('Could not retrieve data from the external source.');
}
}
}
module.exports = new SocrataService();