Skip to content

Commit 2c974bb

Browse files
authored
Fix crash if weather API returns empty items object (#3129)
1 parent 04e58d8 commit 2c974bb

File tree

1 file changed

+11
-12
lines changed

1 file changed

+11
-12
lines changed

website/src/apis/weather.ts

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
import axios, { AxiosResponse } from 'axios';
22
import { isSameDay, addDays } from 'date-fns';
33

4+
/** An empty object, i.e. `{}` */
5+
type EmptyItem = Record<string, undefined>;
6+
47
export type WeatherResponse<T> = {
58
api_info: {
69
status: string;
710
};
8-
items: T[];
11+
// API sometimes returns `[{}]` instead of an array of Ts, no idea why
12+
items: (T | EmptyItem)[];
913
};
1014

1115
type ValidPeriod = {
@@ -71,44 +75,39 @@ export type Forecast = Weather & {
7175
const API_PREFIX = 'https://api.data.gov.sg/v1/environment';
7276
const NOWCAST_AREA = 'Queenstown';
7377

74-
function getResponseData<T>(response: AxiosResponse<WeatherResponse<T>>): T {
78+
function getResponseData<T>(response: AxiosResponse<WeatherResponse<T>>): T | EmptyItem {
7579
const { data } = response;
7680
if (data.api_info.status !== 'healthy') {
77-
throw new Error(`API returned non-healthy status ${data.api_info.status}`);
81+
throw new Error(`Weather API returned non-healthy status ${data.api_info.status}`);
7882
}
79-
8083
return data.items[0];
8184
}
8285

8386
export function twoHour(): Promise<string | null> {
8487
return axios
8588
.get<WeatherResponse<NowCastItem>>(`${API_PREFIX}/2-hour-weather-forecast`)
8689
.then((response) => {
87-
// 2-hour forecast may return an empty object sometimes, no idea why
8890
const areaForecast = getResponseData(response).forecasts?.find(
8991
(forecast) => forecast.area === NOWCAST_AREA,
9092
);
91-
92-
if (!areaForecast) return null;
93-
return areaForecast.forecast;
93+
return areaForecast?.forecast ?? null;
9494
});
9595
}
9696

9797
export function tomorrow(): Promise<string | null> {
9898
return axios
9999
.get<WeatherResponse<DayCastItem>>(`${API_PREFIX}/24-hour-weather-forecast`)
100100
.then((response) => {
101-
const tomorrowForecast = getResponseData(response).periods.find((period) =>
101+
const tomorrowForecast = getResponseData(response).periods?.find((period) =>
102102
isSameDay(new Date(period.time.start), addDays(new Date(), 1)),
103103
);
104-
105104
// The forecast for tomorrow may not be available, so this can return null
106-
return tomorrowForecast ? tomorrowForecast.regions.west : null;
105+
return tomorrowForecast?.regions?.west ?? null;
107106
});
108107
}
109108

110109
export function fourDay(): Promise<Forecast[]> {
111110
return axios
112111
.get<WeatherResponse<FourDayCastItem>>(`${API_PREFIX}/4-day-weather-forecast`)
113-
.then((response) => getResponseData(response).forecasts);
112+
.then((response) => getResponseData(response).forecasts ?? []);
114113
}

0 commit comments

Comments
 (0)