Overhaul error handling
This commit is contained in:
@@ -71,7 +71,8 @@ async function resolveCoordinates( location: string ): Promise< GeoCoordinates >
|
|||||||
* Makes an HTTP/HTTPS GET request to the specified URL and parses the JSON response body.
|
* Makes an HTTP/HTTPS GET request to the specified URL and parses the JSON response body.
|
||||||
* @param url The URL to fetch.
|
* @param url The URL to fetch.
|
||||||
* @return A Promise that will be resolved the with parsed response body if the request succeeds, or will be rejected
|
* @return A Promise that will be resolved the with parsed response body if the request succeeds, or will be rejected
|
||||||
* with an Error if the request or JSON parsing fails.
|
* with an error if the request or JSON parsing fails. This error may contain information about the HTTP request or,
|
||||||
|
* response including API keys and other sensitive information.
|
||||||
*/
|
*/
|
||||||
export async function httpJSONRequest(url: string ): Promise< any > {
|
export async function httpJSONRequest(url: string ): Promise< any > {
|
||||||
try {
|
try {
|
||||||
@@ -110,6 +111,7 @@ function getTimeData( coordinates: GeoCoordinates ): TimeData {
|
|||||||
* @param adjustmentOptions Options to tweak the calculation, or undefined/null if no custom values are to be used.
|
* @param adjustmentOptions Options to tweak the calculation, or undefined/null if no custom values are to be used.
|
||||||
* @param wateringData The weather to use to calculate watering percentage.
|
* @param wateringData The weather to use to calculate watering percentage.
|
||||||
* @return The percentage that watering should be scaled by.
|
* @return The percentage that watering should be scaled by.
|
||||||
|
* @throws An error message will be thrown if the watering scale cannot be calculated.
|
||||||
*/
|
*/
|
||||||
function calculateZimmermanWateringScale( adjustmentOptions: AdjustmentOptions, wateringData: WateringData ): number {
|
function calculateZimmermanWateringScale( adjustmentOptions: AdjustmentOptions, wateringData: WateringData ): number {
|
||||||
|
|
||||||
@@ -117,7 +119,7 @@ function calculateZimmermanWateringScale( adjustmentOptions: AdjustmentOptions,
|
|||||||
|
|
||||||
// Check to make sure valid data exists for all factors
|
// Check to make sure valid data exists for all factors
|
||||||
if ( !validateValues( [ "temp", "humidity", "precip" ], wateringData ) ) {
|
if ( !validateValues( [ "temp", "humidity", "precip" ], wateringData ) ) {
|
||||||
return 100;
|
throw "Necessary field(s) were missing from WateringData.";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get baseline conditions for 100% water level, if provided
|
// Get baseline conditions for 100% water level, if provided
|
||||||
@@ -196,7 +198,13 @@ export const getWeatherData = async function( req: express.Request, res: express
|
|||||||
|
|
||||||
// Continue with the weather request
|
// Continue with the weather request
|
||||||
const timeData: TimeData = getTimeData( coordinates );
|
const timeData: TimeData = getTimeData( coordinates );
|
||||||
const weatherData: WeatherData = await weatherProvider.getWeatherData( coordinates );
|
let weatherData: WeatherData;
|
||||||
|
try {
|
||||||
|
weatherData = await weatherProvider.getWeatherData( coordinates );
|
||||||
|
} catch ( err ) {
|
||||||
|
res.send( "Error: " + err );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
res.json( {
|
res.json( {
|
||||||
...timeData,
|
...timeData,
|
||||||
@@ -259,7 +267,12 @@ export const getWateringData = async function( req: express.Request, res: expres
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
wateringData = await weatherProvider.getWateringData( coordinates );
|
try {
|
||||||
|
wateringData = await weatherProvider.getWateringData( coordinates );
|
||||||
|
} catch ( err ) {
|
||||||
|
res.send( "Error: " + err );
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let scale = -1, rainDelay = -1;
|
let scale = -1, rainDelay = -1;
|
||||||
@@ -328,7 +341,8 @@ export const getWateringData = async function( req: express.Request, res: expres
|
|||||||
* Makes an HTTP/HTTPS GET request to the specified URL and returns the response body.
|
* Makes an HTTP/HTTPS GET request to the specified URL and returns the response body.
|
||||||
* @param url The URL to fetch.
|
* @param url The URL to fetch.
|
||||||
* @return A Promise that will be resolved the with response body if the request succeeds, or will be rejected with an
|
* @return A Promise that will be resolved the with response body if the request succeeds, or will be rejected with an
|
||||||
* Error if the request fails.
|
* error if the request fails or returns a non-200 status code. This error may contain information about the HTTP
|
||||||
|
* request or, response including API keys and other sensitive information.
|
||||||
*/
|
*/
|
||||||
async function httpRequest( url: string ): Promise< string > {
|
async function httpRequest( url: string ): Promise< string > {
|
||||||
return new Promise< any >( ( resolve, reject ) => {
|
return new Promise< any >( ( resolve, reject ) => {
|
||||||
@@ -343,6 +357,11 @@ async function httpRequest( url: string ): Promise< string > {
|
|||||||
};
|
};
|
||||||
|
|
||||||
( isHttps ? https : http ).get( options, ( response ) => {
|
( isHttps ? https : http ).get( options, ( response ) => {
|
||||||
|
if ( response.statusCode !== 200 ) {
|
||||||
|
reject( `Received ${ response.statusCode } status code for URL '${ url }'.` );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let data = "";
|
let data = "";
|
||||||
|
|
||||||
// Reassemble the data as it comes in
|
// Reassemble the data as it comes in
|
||||||
|
|||||||
@@ -17,12 +17,12 @@ async function getDarkSkyWateringData( coordinates: GeoCoordinates ): Promise< W
|
|||||||
yesterdayData = await httpJSONRequest( yesterdayUrl );
|
yesterdayData = await httpJSONRequest( yesterdayUrl );
|
||||||
todayData = await httpJSONRequest( todayUrl );
|
todayData = await httpJSONRequest( todayUrl );
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// Indicate watering data could not be retrieved if an API error occurs.
|
console.error( "Error retrieving weather information from Dark Sky:", err );
|
||||||
return undefined;
|
throw "An error occurred while retrieving weather information from Dark Sky."
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( !todayData.hourly || !todayData.hourly.data || !yesterdayData.hourly || !yesterdayData.hourly.data ) {
|
if ( !todayData.hourly || !todayData.hourly.data || !yesterdayData.hourly || !yesterdayData.hourly.data ) {
|
||||||
return undefined;
|
throw "Necessary field(s) were missing from weather information returned by Dark Sky.";
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The number of hourly forecasts to use from today's data. This will only include elements that contain historic
|
/* The number of hourly forecasts to use from today's data. This will only include elements that contain historic
|
||||||
@@ -39,7 +39,7 @@ async function getDarkSkyWateringData( coordinates: GeoCoordinates ): Promise< W
|
|||||||
|
|
||||||
// Fail if not enough data is available.
|
// Fail if not enough data is available.
|
||||||
if ( samples.length !== 24 ) {
|
if ( samples.length !== 24 ) {
|
||||||
return undefined;
|
throw "Insufficient data was returned by Dark Sky.";
|
||||||
}
|
}
|
||||||
|
|
||||||
const totals = { temp: 0, humidity: 0, precip: 0 };
|
const totals = { temp: 0, humidity: 0, precip: 0 };
|
||||||
@@ -66,12 +66,12 @@ async function getDarkSkyWeatherData( coordinates: GeoCoordinates ): Promise< We
|
|||||||
try {
|
try {
|
||||||
forecast = await httpJSONRequest( forecastUrl );
|
forecast = await httpJSONRequest( forecastUrl );
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// Indicate weather data could not be retrieved if an API error occurs.
|
console.error( "Error retrieving weather information from Dark Sky:", err );
|
||||||
return undefined;
|
throw "An error occurred while retrieving weather information from Dark Sky."
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( !forecast.currently || !forecast.daily || !forecast.daily.data ) {
|
if ( !forecast.currently || !forecast.daily || !forecast.daily.data ) {
|
||||||
return undefined;
|
throw "Necessary field(s) were missing from weather information returned by Dark Sky.";
|
||||||
}
|
}
|
||||||
|
|
||||||
const weather: WeatherData = {
|
const weather: WeatherData = {
|
||||||
|
|||||||
@@ -10,13 +10,13 @@ async function getOWMWateringData( coordinates: GeoCoordinates ): Promise< Water
|
|||||||
try {
|
try {
|
||||||
forecast = await httpJSONRequest( forecastUrl );
|
forecast = await httpJSONRequest( forecastUrl );
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// Indicate watering data could not be retrieved if an API error occurs.
|
console.error( "Error retrieving weather information from OWM:", err );
|
||||||
return undefined;
|
throw "An error occurred while retrieving weather information from OWM."
|
||||||
}
|
}
|
||||||
|
|
||||||
// Indicate watering data could not be retrieved if the forecast data is incomplete.
|
// Indicate watering data could not be retrieved if the forecast data is incomplete.
|
||||||
if ( !forecast || !forecast.list ) {
|
if ( !forecast || !forecast.list ) {
|
||||||
return undefined;
|
throw "Necessary field(s) were missing from weather information returned by OWM.";
|
||||||
}
|
}
|
||||||
|
|
||||||
let totalTemp = 0,
|
let totalTemp = 0,
|
||||||
@@ -49,13 +49,13 @@ async function getOWMWeatherData( coordinates: GeoCoordinates ): Promise< Weathe
|
|||||||
current = await httpJSONRequest( currentUrl );
|
current = await httpJSONRequest( currentUrl );
|
||||||
forecast = await httpJSONRequest( forecastDailyUrl );
|
forecast = await httpJSONRequest( forecastDailyUrl );
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// Indicate watering data could not be retrieved if an API error occurs.
|
console.error( "Error retrieving weather information from OWM:", err );
|
||||||
return undefined;
|
throw "An error occurred while retrieving weather information from OWM."
|
||||||
}
|
}
|
||||||
|
|
||||||
// Indicate watering data could not be retrieved if the forecast data is incomplete.
|
// Indicate watering data could not be retrieved if the forecast data is incomplete.
|
||||||
if ( !current || !current.main || !current.wind || !current.weather || !forecast || !forecast.list ) {
|
if ( !current || !current.main || !current.wind || !current.weather || !forecast || !forecast.list ) {
|
||||||
return undefined;
|
throw "Necessary field(s) were missing from weather information returned by OWM.";
|
||||||
}
|
}
|
||||||
|
|
||||||
const weather: WeatherData = {
|
const weather: WeatherData = {
|
||||||
|
|||||||
4
types.ts
4
types.ts
@@ -90,7 +90,7 @@ export interface WeatherProvider {
|
|||||||
* Retrieves weather data necessary for watering level calculations.
|
* Retrieves weather data necessary for watering level calculations.
|
||||||
* @param coordinates The coordinates to retrieve the watering data for.
|
* @param coordinates The coordinates to retrieve the watering data for.
|
||||||
* @return A Promise that will be resolved with the WateringData if it is successfully retrieved,
|
* @return A Promise that will be resolved with the WateringData if it is successfully retrieved,
|
||||||
* or resolved with undefined if an error occurs while retrieving the WateringData.
|
* or rejected with an error message if an error occurs while retrieving the WateringData.
|
||||||
*/
|
*/
|
||||||
getWateringData?( coordinates : GeoCoordinates ): Promise< WateringData >;
|
getWateringData?( coordinates : GeoCoordinates ): Promise< WateringData >;
|
||||||
|
|
||||||
@@ -98,7 +98,7 @@ export interface WeatherProvider {
|
|||||||
* Retrieves the current weather data for usage in the mobile app.
|
* Retrieves the current weather data for usage in the mobile app.
|
||||||
* @param coordinates The coordinates to retrieve the weather for
|
* @param coordinates The coordinates to retrieve the weather for
|
||||||
* @return A Promise that will be resolved with the WeatherData if it is successfully retrieved,
|
* @return A Promise that will be resolved with the WeatherData if it is successfully retrieved,
|
||||||
* or resolved with undefined if an error occurs while retrieving the WeatherData.
|
* or rejected with an error message if an error occurs while retrieving the WeatherData.
|
||||||
*/
|
*/
|
||||||
getWeatherData?( coordinates : GeoCoordinates ): Promise< WeatherData >;
|
getWeatherData?( coordinates : GeoCoordinates ): Promise< WeatherData >;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user