import axios, { AxiosError } from 'axios';
import {
BrregAPIError,
BrregRateLimitError,
BrregNotFoundError,
BrregValidationError,
BrregErrorResponse
} from './types.js';
export class BrregAPIClient {
private baseURL: string = 'https://data.brreg.no';
private timeout: number = 30000;
constructor() {
// Hardcoded configuration
}
private async handleResponse<T>(response: any): Promise<T> {
// Handle successful responses
if (response.status >= 200 && response.status < 300) {
return response.data;
}
// This should not be reached with axios interceptors, but just in case
throw new BrregAPIError(`Unexpected response status: ${response.status}`);
}
private handleError(error: AxiosError<BrregErrorResponse>): never {
if (!error.response) {
// Network error or timeout
if (error.code === 'ECONNABORTED') {
throw new BrregAPIError('Request timeout');
}
throw new BrregAPIError(`Network error: ${error.message}`);
}
const { status, data } = error.response;
const message = data?.feilmelding || error.message || 'Unknown error';
switch (status) {
case 400:
throw new BrregValidationError(message);
case 404:
throw new BrregNotFoundError(message);
case 429:
throw new BrregRateLimitError(message);
case 500:
case 502:
case 503:
case 504:
throw new BrregAPIError(`Server error: ${message}`);
default:
throw new BrregAPIError(message);
}
}
async get<T>(endpoint: string, params?: Record<string, any>): Promise<T> {
try {
const response = await axios.get(`${this.baseURL}${endpoint}`, {
params,
timeout: this.timeout,
headers: {
'Accept': 'application/json',
'User-Agent': 'brreg-mcp-server/1.0.0'
}
});
return await this.handleResponse<T>(response);
} catch (error) {
this.handleError(error as AxiosError<BrregErrorResponse>);
}
}
async downloadFile(endpoint: string, params?: Record<string, any>): Promise<ArrayBuffer> {
try {
const response = await axios.get(`${this.baseURL}${endpoint}`, {
params,
timeout: this.timeout * 2, // Longer timeout for downloads
responseType: 'arraybuffer',
headers: {
'Accept': 'application/gzip,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'User-Agent': 'brreg-mcp-server/1.0.0'
}
});
return response.data;
} catch (error) {
this.handleError(error as AxiosError<BrregErrorResponse>);
}
}
// Utility method to validate organization number
static validateOrganizationNumber(orgNr: string): boolean {
// Norwegian organization numbers are 9 digits
return /^\d{9}$/.test(orgNr);
}
// Utility method to validate municipality number
static validateMunicipalityNumber(municipalityNr: string): boolean {
// Norwegian municipality numbers are 4 digits
return /^\d{4}$/.test(municipalityNr);
}
// Utility method to format date for API
static formatDate(date: Date | string): string {
if (typeof date === 'string') {
return date; // Assume already in correct format
}
return date.toISOString().split('T')[0]; // YYYY-MM-DD format
}
// Utility method to build query parameters with proper formatting
buildParams(params: Record<string, any>): URLSearchParams {
const searchParams = new URLSearchParams();
for (const [key, value] of Object.entries(params)) {
if (value === undefined || value === null) {
continue;
}
if (Array.isArray(value)) {
if (value.length > 0) {
searchParams.append(key, value.join(','));
}
} else {
searchParams.append(key, String(value));
}
}
return searchParams;
}
}