Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: improved the nws client algorithm to receive data from differen… #4

Merged
merged 1 commit into from
Aug 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down
41 changes: 22 additions & 19 deletions src/interfaces.ts
Original file line number Diff line number Diff line change
@@ -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<T, U extends IWeatherUnits> = {
value: T;
unit: U | keyof typeof IWeatherUnits;
}

export interface IConditions {
value: string;
unit: 'string';
}
export type IRelativeHumidity = IBaseWeatherProperty<number, IWeatherUnits.percent>;
export type IDewPoint = IBaseWeatherProperty<number, IWeatherUnits.C | IWeatherUnits.F>;
export type IConditions = IBaseWeatherProperty<string, IWeatherUnits.string>;
export type ITemperature = IBaseWeatherProperty<number, IWeatherUnits.C | IWeatherUnits.F>;
2 changes: 1 addition & 1 deletion src/providers/nws/client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
});
});

Expand Down
60 changes: 48 additions & 12 deletions src/providers/nws/client.ts
Original file line number Diff line number Diff line change
@@ -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<IWeatherData> = {};
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
Expand All @@ -32,12 +68,12 @@ export async function fetchObservationStationUrl(
// https://api.weather.gov/gridpoints/OKX/33,35/stations
export async function fetchNearbyStations(
observationStations: string
): Promise<string> {
const stationResponse = await axios.get<IGridpointsStations>(
): Promise<IFeature[]> {
const { data: { features } } = await axios.get<IGridpointsStations>(
observationStations
);
const stationUrl = stationResponse.data.features[0].id;
return stationUrl;

return features;
}

// Fetch the latest observation from the Weather.gov API
Expand All @@ -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,
}
};
}
Loading