diff --git a/package.json b/package.json index 85d3447..a05d635 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "weather-plus", - "version": "0.0.16", + "version": "0.0.17", "description": "Weather Plus is a powerful wrapper around various Weather APIs that simplifies adding weather data to your application", "main": "./dist/cjs/index.js", "module": "./dist/esm/index.js", diff --git a/src/interfaces.ts b/src/interfaces.ts index 58fc707..a9ac838 100644 --- a/src/interfaces.ts +++ b/src/interfaces.ts @@ -1,26 +1,29 @@ -export interface IWeatherData { - dewPoint: IDewPoint; - humidity: IRelativeHumidity; - temperature: ITemperature; - conditions: IConditions; +export enum IWeatherUnits { + C = 'C', + F = 'F', + percent = 'percent', + string = 'string', } -export interface ITemperature { - value: number; - unit: 'C' | 'F'; +export enum IWeatherKey { + dewPoint = 'dewPoint', + humidity = 'humidity', + temperature = 'temperature', + conditions = 'conditions', } -export interface IRelativeHumidity { - value: number; - unit: 'percent'; +export interface IWeatherData { + [IWeatherKey.dewPoint]: IDewPoint; + [IWeatherKey.humidity]: IRelativeHumidity; + [IWeatherKey.temperature]: ITemperature; + [IWeatherKey.conditions]: IConditions; } - -export interface IDewPoint { - value: number; - unit: 'C' | 'F'; +export type IBaseWeatherProperty = { + value: T; + unit: U | keyof typeof IWeatherUnits; } -export interface IConditions { - value: string; - unit: 'string'; -} \ No newline at end of file +export type IRelativeHumidity = IBaseWeatherProperty; +export type IDewPoint = IBaseWeatherProperty; +export type IConditions = IBaseWeatherProperty; +export type ITemperature = IBaseWeatherProperty; \ No newline at end of file diff --git a/src/providers/nws/client.test.ts b/src/providers/nws/client.test.ts index 7f62e1b..ba1ccf2 100644 --- a/src/providers/nws/client.test.ts +++ b/src/providers/nws/client.test.ts @@ -49,7 +49,7 @@ describe('fetchNearbyStations', () => { mock.onGet(observationStations).reply(200, mockResponse); const result = await fetchNearbyStations(observationStations); - expect(result).toBe(mockResponse.features[0].id); + expect(result).toEqual(mockResponse.features); }); }); diff --git a/src/providers/nws/client.ts b/src/providers/nws/client.ts index ff6cfb2..417996e 100644 --- a/src/providers/nws/client.ts +++ b/src/providers/nws/client.ts @@ -1,19 +1,55 @@ import axios from 'axios'; import debug from 'debug'; -import { IWeatherData } from '../../interfaces'; +import { IWeatherData, IWeatherKey, IWeatherUnits } from '../../interfaces'; import { IGridpointsStations, IPointsLatLngResponse, IObservationsLatest, + IFeature, } from './interfaces'; const log = debug('weather-plus:nws:client'); +export const WEATHER_KEYS = Object.values(IWeatherKey); + export async function getWeather(lat: number, lng: number) { + const data: Partial = {}; + const weatherData: IWeatherData[] = []; + const observationStations = await fetchObservationStationUrl(lat, lng); - const stationId = await fetchNearbyStations(observationStations); - const observation = await fetchLatestObservation(stationId); - return convertToWeatherData(observation); + const stations = await fetchNearbyStations(observationStations); + + if (!stations.length) { + throw new Error('No stations found'); + } + + do { + try { + const stationId = stations.pop()?.id; + + if (!stationId) { + break; + } + + const observation = await fetchLatestObservation(stationId); + const weather = convertToWeatherData(observation); + + weatherData.push(weather); + } catch (error) { + break; + } + } + while (!WEATHER_KEYS.reduce((acc, key) => acc && weatherData.some((data) => data[key]), true) || stations.length > 0); + + for (const key of WEATHER_KEYS) { + const value = weatherData.find((data) => data[key]); + + if (value && value[key]?.value) { + data[key] = value[key] as never; + } + } + + return data; } // Fetch the observation station URL from the Weather.gov API @@ -32,12 +68,12 @@ export async function fetchObservationStationUrl( // https://api.weather.gov/gridpoints/OKX/33,35/stations export async function fetchNearbyStations( observationStations: string -): Promise { - const stationResponse = await axios.get( +): Promise { + const { data: { features } } = await axios.get( observationStations ); - const stationUrl = stationResponse.data.features[0].id; - return stationUrl; + + return features; } // Fetch the latest observation from the Weather.gov API @@ -57,19 +93,19 @@ export function convertToWeatherData(observation: any): IWeatherData { return { dewPoint: { value: properties.dewpoint.value, - unit: properties.dewpoint.unitCode === 'wmoUnit:degC' ? 'C' : 'F', + unit: properties.dewpoint.unitCode === 'wmoUnit:degC' ? IWeatherUnits.C : IWeatherUnits.F, }, humidity: { value: properties.relativeHumidity.value, - unit: 'percent', + unit: IWeatherUnits.percent, }, temperature: { value: properties.temperature.value, - unit: properties.temperature.unitCode === 'wmoUnit:degC' ? 'C' : 'F', + unit: properties.temperature.unitCode === 'wmoUnit:degC' ? IWeatherUnits.C : IWeatherUnits.F, }, conditions: { value: properties.textDescription, - unit: 'string', + unit: IWeatherUnits.string, } }; }