parent
b8f9ee0e76
commit
5aba64eb5f
@ -0,0 +1,26 @@ |
|||||||
|
import { createOpenmeteoClient } from './openmeteo'; |
||||||
|
|
||||||
|
describe('createOpenmeteoClient', () => { |
||||||
|
const client = createOpenmeteoClient(); |
||||||
|
it('returns some weather for the sample address', async () => { |
||||||
|
const result = await client.getCurrentWeather({ |
||||||
|
latitude: 52.28, |
||||||
|
longitude: 10.52, |
||||||
|
}); |
||||||
|
expect(result.apparentTemperature).toMatch(/^\d+(\.?\d+)?°C$/); |
||||||
|
expect(result.temperature).toMatch(/^\d+(\.?\d+)?°C$/); |
||||||
|
expect(result.relativeHumidity).toMatch(/^\d+%$/); |
||||||
|
|
||||||
|
// Just to make sure that the returned weather seems to be reasonable
|
||||||
|
expect(result).not.toEqual({ |
||||||
|
apparentTemperature: '0°C', |
||||||
|
temperature: '0°C', |
||||||
|
relativeHumidity: '0%', |
||||||
|
}); |
||||||
|
expect(result).not.toEqual({ |
||||||
|
apparentTemperature: '0.0°C', |
||||||
|
temperature: '0.0°C', |
||||||
|
relativeHumidity: '0%', |
||||||
|
}); |
||||||
|
}); |
||||||
|
}); |
@ -0,0 +1,68 @@ |
|||||||
|
import fetch from 'node-fetch'; |
||||||
|
import PQueue from 'p-queue'; |
||||||
|
import { WeatherProvider } from './types'; |
||||||
|
|
||||||
|
const requestedFields = [ |
||||||
|
'temperature_2m', |
||||||
|
'relativehumidity_2m', |
||||||
|
'apparent_temperature', |
||||||
|
'is_day', |
||||||
|
'precipitation', |
||||||
|
'rain', |
||||||
|
'showers', |
||||||
|
'snowfall', |
||||||
|
'weathercode', |
||||||
|
'cloudcover', |
||||||
|
'pressure_msl', |
||||||
|
'surface_pressure', |
||||||
|
'windspeed_10m', |
||||||
|
'winddirection_10m', |
||||||
|
'windgusts_10m', |
||||||
|
] as const; |
||||||
|
|
||||||
|
type OpenmeteoKey = (typeof requestedFields)[number]; |
||||||
|
type OpenmeteoResponse = |
||||||
|
| { |
||||||
|
current_units?: Record<OpenmeteoKey, string | undefined>; |
||||||
|
current?: Record<OpenmeteoKey, string | number | undefined>; |
||||||
|
} |
||||||
|
| undefined; |
||||||
|
|
||||||
|
export const createOpenmeteoClient = (): WeatherProvider => { |
||||||
|
// Rate limit (according to https://open-meteo.com/en/terms it's 10k per day;
|
||||||
|
// here we set it 1 per second, should be enough for the demo app goal)
|
||||||
|
const queue = new PQueue({ |
||||||
|
interval: 1000, |
||||||
|
intervalCap: 1, |
||||||
|
timeout: 2000, |
||||||
|
throwOnTimeout: true, |
||||||
|
}); |
||||||
|
|
||||||
|
return { |
||||||
|
getCurrentWeather: async ({ latitude, longitude }) => |
||||||
|
queue.add(async () => { |
||||||
|
const response = await fetch( |
||||||
|
`https://api.open-meteo.com/v1/forecast?latitude=${latitude}&longitude=${longitude}¤t=${requestedFields.join( |
||||||
|
',', |
||||||
|
)}`,
|
||||||
|
); |
||||||
|
const body = (await response.json()) as OpenmeteoResponse; |
||||||
|
if (!body || !body.current || !body.current_units) { |
||||||
|
throw new Error('cannot load weather'); |
||||||
|
} |
||||||
|
|
||||||
|
const { current, current_units } = body; |
||||||
|
const getReadableValue = (key: OpenmeteoKey) => |
||||||
|
`${current[key]}${current_units[key]}`; |
||||||
|
|
||||||
|
return { |
||||||
|
temperature: getReadableValue('temperature_2m'), |
||||||
|
apparentTemperature: getReadableValue( |
||||||
|
'apparent_temperature', |
||||||
|
), |
||||||
|
relativeHumidity: getReadableValue('relativehumidity_2m'), |
||||||
|
//...
|
||||||
|
}; |
||||||
|
}), |
||||||
|
}; |
||||||
|
}; |
@ -0,0 +1,10 @@ |
|||||||
|
export type WeatherProvider = { |
||||||
|
getCurrentWeather(params: { |
||||||
|
longitude: number; |
||||||
|
latitude: number; |
||||||
|
}): Promise<{ |
||||||
|
temperature: string; |
||||||
|
apparentTemperature: string; |
||||||
|
relativeHumidity: string; |
||||||
|
}>; |
||||||
|
}; |
Loading…
Reference in new issue