296 lines
8.4 KiB
TypeScript
296 lines
8.4 KiB
TypeScript
// frontend/src/api/services/external.service.ts
|
|
/**
|
|
* External Data Service
|
|
* Handles weather and traffic data operations for the external microservice
|
|
*/
|
|
|
|
import { apiClient } from '../client';
|
|
import { RequestTimeouts } from '../client/config';
|
|
|
|
// Align with backend WeatherDataResponse schema
|
|
export interface WeatherData {
|
|
date: string;
|
|
temperature?: number;
|
|
precipitation?: number;
|
|
humidity?: number;
|
|
wind_speed?: number;
|
|
pressure?: number;
|
|
description?: string;
|
|
source: string;
|
|
}
|
|
|
|
// Align with backend TrafficDataResponse schema
|
|
export interface TrafficData {
|
|
date: string;
|
|
traffic_volume?: number;
|
|
pedestrian_count?: number;
|
|
congestion_level?: string;
|
|
average_speed?: number;
|
|
source: string;
|
|
}
|
|
|
|
export interface WeatherForecast {
|
|
date: string;
|
|
temperature_min: number;
|
|
temperature_max: number;
|
|
temperature_avg: number;
|
|
precipitation: number;
|
|
description: string;
|
|
humidity?: number;
|
|
wind_speed?: number;
|
|
}
|
|
|
|
export interface HourlyForecast {
|
|
forecast_datetime: string;
|
|
generated_at: string;
|
|
temperature: number;
|
|
precipitation: number;
|
|
humidity: number;
|
|
wind_speed: number;
|
|
description: string;
|
|
source: string;
|
|
hour: number;
|
|
}
|
|
|
|
export class ExternalService {
|
|
/**
|
|
* Get Current Weather Data
|
|
*/
|
|
async getCurrentWeather(
|
|
tenantId: string,
|
|
lat: number,
|
|
lon: number
|
|
): Promise<WeatherData> {
|
|
try {
|
|
// ✅ FIX 1: Correct endpoint path with tenant ID
|
|
const endpoint = `/tenants/${tenantId}/weather/current`;
|
|
|
|
// ✅ FIX 2: Correct parameter names (latitude/longitude, not lat/lon)
|
|
const response = await apiClient.get(endpoint, {
|
|
params: {
|
|
latitude: lat, // Backend expects 'latitude'
|
|
longitude: lon // Backend expects 'longitude'
|
|
}
|
|
});
|
|
|
|
console.log('Weather API response:', response);
|
|
|
|
// Return backend response directly (matches WeatherData interface)
|
|
return response;
|
|
|
|
} catch (error) {
|
|
console.error('Failed to fetch weather from AEMET API via backend:', error);
|
|
throw new Error(`Weather data unavailable: ${error instanceof Error ? error.message : 'AEMET API connection failed'}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get Weather Forecast
|
|
*/
|
|
async getWeatherForecast(
|
|
tenantId: string,
|
|
lat: number,
|
|
lon: number,
|
|
days: number = 7
|
|
): Promise<WeatherForecast[]> {
|
|
try {
|
|
// Fix: Use POST with JSON body as expected by backend
|
|
const response = await apiClient.post(`/tenants/${tenantId}/weather/forecast`, {
|
|
latitude: lat,
|
|
longitude: lon,
|
|
days: days
|
|
});
|
|
|
|
// Handle response format
|
|
if (Array.isArray(response)) {
|
|
return response;
|
|
} else if (response && response.forecasts) {
|
|
return response.forecasts;
|
|
} else {
|
|
console.warn('Unexpected weather forecast response format:', response);
|
|
return [];
|
|
}
|
|
} catch (error) {
|
|
console.error('Failed to fetch weather forecast from AEMET API:', error);
|
|
throw new Error(`Weather forecast unavailable: ${error instanceof Error ? error.message : 'AEMET API connection failed'}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get Hourly Weather Forecast (NEW)
|
|
*/
|
|
async getHourlyWeatherForecast(
|
|
tenantId: string,
|
|
lat: number,
|
|
lon: number,
|
|
hours: number = 48
|
|
): Promise<HourlyForecast[]> {
|
|
try {
|
|
console.log(`🕒 Fetching hourly weather forecast from AEMET API for tenant ${tenantId}`, {
|
|
latitude: lat,
|
|
longitude: lon,
|
|
hours: hours
|
|
});
|
|
|
|
const response = await apiClient.post(`/tenants/${tenantId}/weather/hourly-forecast`, {
|
|
latitude: lat,
|
|
longitude: lon,
|
|
hours: hours
|
|
});
|
|
|
|
// Handle response format
|
|
if (Array.isArray(response)) {
|
|
return response;
|
|
} else if (response && response.data) {
|
|
return response.data;
|
|
} else {
|
|
console.warn('Unexpected hourly forecast response format:', response);
|
|
return [];
|
|
}
|
|
} catch (error) {
|
|
console.error('Failed to fetch hourly forecast from AEMET API:', error);
|
|
throw new Error(`Hourly forecast unavailable: ${error instanceof Error ? error.message : 'AEMET API connection failed'}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get Historical Weather Data
|
|
*/
|
|
async getHistoricalWeather(
|
|
tenantId: string,
|
|
lat: number,
|
|
lon: number,
|
|
startDate: string,
|
|
endDate: string
|
|
): Promise<WeatherData[]> {
|
|
try {
|
|
// Fix: Use POST with JSON body as expected by backend
|
|
const response = await apiClient.post(`/tenants/${tenantId}/weather/historical`, {
|
|
latitude: lat,
|
|
longitude: lon,
|
|
start_date: startDate,
|
|
end_date: endDate
|
|
});
|
|
|
|
// Return backend response directly (matches WeatherData interface)
|
|
return Array.isArray(response) ? response : response.data || [];
|
|
} catch (error) {
|
|
console.error('Failed to fetch historical weather from AEMET API:', error);
|
|
throw new Error(`Historical weather data unavailable: ${error instanceof Error ? error.message : 'AEMET API connection failed'}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get Current Traffic Data
|
|
*/
|
|
async getCurrentTraffic(
|
|
tenantId: string,
|
|
lat: number,
|
|
lon: number
|
|
): Promise<TrafficData> {
|
|
try {
|
|
const response = await apiClient.get(`/tenants/${tenantId}/traffic/current`, {
|
|
params: {
|
|
latitude: lat,
|
|
longitude: lon
|
|
}
|
|
});
|
|
|
|
// Return backend response directly (matches TrafficData interface)
|
|
return response;
|
|
} catch (error) {
|
|
console.error('Failed to fetch traffic data from external API:', error);
|
|
throw new Error(`Traffic data unavailable: ${error instanceof Error ? error.message : 'External API connection failed'}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get Traffic Forecast
|
|
*/
|
|
async getTrafficForecast(
|
|
tenantId: string,
|
|
lat: number,
|
|
lon: number,
|
|
hours: number = 24
|
|
): Promise<TrafficData[]> {
|
|
try {
|
|
// Fix: Use POST with JSON body as expected by backend
|
|
const response = await apiClient.post(`/tenants/${tenantId}/traffic/forecast`, {
|
|
latitude: lat,
|
|
longitude: lon,
|
|
hours: hours
|
|
});
|
|
|
|
// Return backend response directly (matches TrafficData interface)
|
|
return Array.isArray(response) ? response : response.data || [];
|
|
} catch (error) {
|
|
console.error('Failed to fetch traffic forecast from external API:', error);
|
|
throw new Error(`Traffic forecast unavailable: ${error instanceof Error ? error.message : 'External API connection failed'}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get Historical Traffic Data
|
|
*/
|
|
async getHistoricalTraffic(
|
|
tenantId: string,
|
|
lat: number,
|
|
lon: number,
|
|
startDate: string,
|
|
endDate: string
|
|
): Promise<TrafficData[]> {
|
|
try {
|
|
// Fix: Use POST with JSON body as expected by backend
|
|
const response = await apiClient.post(`/tenants/${tenantId}/traffic/historical`, {
|
|
latitude: lat,
|
|
longitude: lon,
|
|
start_date: startDate,
|
|
end_date: endDate
|
|
});
|
|
|
|
// Return backend response directly (matches TrafficData interface)
|
|
return Array.isArray(response) ? response : response.data || [];
|
|
} catch (error) {
|
|
console.error('Failed to fetch historical traffic from external API:', error);
|
|
throw new Error(`Historical traffic data unavailable: ${error instanceof Error ? error.message : 'External API connection failed'}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Test External Service Connectivity
|
|
*/
|
|
async testConnectivity(tenantId: string): Promise<{
|
|
weather: boolean;
|
|
traffic: boolean;
|
|
overall: boolean;
|
|
}> {
|
|
const results = {
|
|
weather: false,
|
|
traffic: false,
|
|
overall: false
|
|
};
|
|
|
|
try {
|
|
// Test weather service (AEMET API)
|
|
await this.getCurrentWeather(tenantId, 40.4168, -3.7038); // Madrid coordinates
|
|
results.weather = true;
|
|
} catch (error) {
|
|
console.warn('AEMET weather service connectivity test failed:', error);
|
|
results.weather = false;
|
|
}
|
|
|
|
try {
|
|
// Test traffic service
|
|
await this.getCurrentTraffic(tenantId, 40.4168, -3.7038); // Madrid coordinates
|
|
results.traffic = true;
|
|
} catch (error) {
|
|
console.warn('Traffic service connectivity test failed:', error);
|
|
results.traffic = false;
|
|
}
|
|
|
|
results.overall = results.weather && results.traffic;
|
|
return results;
|
|
}
|
|
}
|
|
|
|
export const externalService = new ExternalService(); |