From 51fe8403c2cbafe0f94751be467c75d139f62ca0 Mon Sep 17 00:00:00 2001 From: Nick Anderegg Date: Fri, 19 Apr 2024 17:26:14 -0400 Subject: [PATCH 1/4] fix: slight refactor to make the error handling more robust on index.js --- index.js | 39 ++++++++++++++++++++------ src/geoipAPI.js | 55 +++++++++++++++++++++++++++---------- src/weatherAPI.js | 6 ++-- views/currentConditions.pug | 2 +- 4 files changed, 75 insertions(+), 27 deletions(-) diff --git a/index.js b/index.js index 196b113..58f8420 100644 --- a/index.js +++ b/index.js @@ -1,6 +1,6 @@ const express = require('express') const weather = require('./src/weatherAPI.js') -const {requestCityFromIP, requestCoordsFromIP} = require('./src/geoipAPI.js') +const {geolocateFromIP} = require('./src/geoipAPI.js') const anonymizeip = require('./src/anonymizeip.js') var path = require('path'); @@ -28,15 +28,16 @@ api.get('/', (req, res) => { ipAddr: anonymizeip(clientIP), } - requestCityFromIP(clientIP).then((coords) => { - console.log('Coords obj:', coords) + const weatherResp = geolocateFromIP(clientIP).then((coords) => { + // If the geolocation is successful, format the name of the returned location, + // then call the weather API with the coordinates and timezone. if ("regionName" in coords && "city" in coords && "country" in coords) { renderValues.locationName = `${coords.city}, ${coords.regionName}, ${coords.country}` } else if ("country" in coords) { if ("city" in coords) { renderValues.locationName = `${coords.city}, ${coords.country}` - } else if ("region" in coords) { + } else if ("regionName" in coords) { renderValues.locationName = `${coords.regionName}, ${coords.country}` } else { renderValues.locationName = coords.country @@ -47,12 +48,27 @@ api.get('/', (req, res) => { renderValues.locationName = coords.regionName } - // By default, display the weather in Toronto. - return weather("43.7001", "-79.4163", "America/Toronto") - }).then((weatherData) => { - // renderValues.forecast = JSON.stringify(weatherData) + return weather(coords.lat, coords.lon, coords.timezone) + }).catch((coords) => { + // If the geolocation fails, default to Toronto, Ontario, Canada, then call + // the weather API with the coordinates and timezone. + + renderValues.locationName = "Toronto, Ontario, Canada" + return weather("43.7", "-79.42", "America/Toronto") + }) + + weatherResp.then((weatherData) => { + // Once the weather API call is successful, render the index page with the + // template values specified in `renderValues`. + renderValues.forecast = weatherData res.render('index', renderValues) + }).catch((e) => { + // If the weather API call fails, render the index page with the template + // and the limited values that are available. + + console.error("Error in main route:", e) + res.render('index', renderValues) }) }) @@ -61,8 +77,10 @@ api.get('/geolocate', (req, res) => { // will send a request to `/geolocate` to get the estimated coordinates // of the client's IP address. This will then return the coordinates to the // client, which will use them to call the weather API as it normally would. - geoip(req.ip).then((coords) => { + geolocateFromIP(req.ip).then((coords) => { res.json(coords) + }).catch((e) => { + res.json({status: 'error', code: 500, message: e.message}) }) }) @@ -70,10 +88,13 @@ api.get('/weather', (req, res) => { const queryParams = req.query if (!queryParams.lat || !queryParams.lon || !queryParams.timezone) { res.status(400).send('Missing query parameters. All of the following are required: lat, lon, timezone') + return } weather(queryParams.lat, queryParams.lon, queryParams.timezone).then((weatherData) => { res.json(weatherData) + }).catch((e) => { + res.json({status: 'error', code: 500, message: e.message}) }) }) diff --git a/src/geoipAPI.js b/src/geoipAPI.js index bf69bfd..a942a53 100644 --- a/src/geoipAPI.js +++ b/src/geoipAPI.js @@ -29,6 +29,16 @@ function buildAPIRequest(ipAddr, fieldsVal) { } +const defaultCoords = { + status: 'success', + country: 'Canada', + regionName: 'Ontario', + city: 'Toronto', + lat: '43.7', + lon: '-79.42', + timezone: 'America/Toronto', +} + function apiRequest(url) { return new Promise((resolve, reject) => { @@ -49,6 +59,7 @@ function apiRequest(url) { res.on('end', () => { try { coordsResponse = JSON.parse(responseData) + // console.log("coordsResponse: ", coordsResponse) // Because this sample app is used in live demos, we want to // anonymize the coordinates returned to the client side. @@ -58,13 +69,17 @@ function apiRequest(url) { } if (coordsResponse.status !== 'success') { - throw new Error('API request to `ip-api.com` failed.') + // If our query to the geolocation API endpoint fails, + // return the default coordinates object, which has the + // coordinates and timezone for Toronto, Ontario, Canada. + console.error('API request to `ip-api.com` did not succeed. Rejecting with default coordinates object (Toronto).') + reject(defaultCoords) } else { resolve(coordsResponse) } } catch (e) { console.error(e.message) - reject(e) + reject(defaultCoords) } }) }) @@ -78,31 +93,43 @@ function apiRequest(url) { }) } -function requestCoordsFromIP(ipAddr) { +// function requestCoordsFromIP(ipAddr) { - // Validate input and throw an error if the input string isn't a valid IP address. - if (!ipAddrRegex.test(ipAddr)) { - throw new Error('Invalid IP address format') - } +// // Validate input and throw an error if the input string isn't a valid IP address. +// if (!ipAddrRegex.test(ipAddr)) { +// throw new Error('Invalid IP address format') +// } - const url = buildAPIRequest(ipAddr, 'status,country,regionName,city,lat,lon,timezone') - return apiRequest(url) -} +// const url = buildAPIRequest(ipAddr, 'status,country,regionName,city,lat,lon,timezone') +// return apiRequest(url) +// } + +// function requestCityFromIP(ipAddr) { -function requestCityFromIP(ipAddr) { +// // Validate input and throw an error if the input string isn't a valid IP address. +// if (!ipAddrRegex.test(ipAddr)) { +// throw new Error('Invalid IP address format') +// } + +// const url = buildAPIRequest(ipAddr, 'status,country,regionName,city,lat,lon,timezone') +// return apiRequest(url) +// } + +function geolocateFromIP(ipAddr) { // Validate input and throw an error if the input string isn't a valid IP address. if (!ipAddrRegex.test(ipAddr)) { throw new Error('Invalid IP address format') } - const url = buildAPIRequest(ipAddr, 'status,country,regionName,city') + const url = buildAPIRequest(ipAddr, 'status,country,regionName,city,lat,lon,timezone') return apiRequest(url) } module.exports = { - requestCityFromIP, - requestCoordsFromIP, + // requestCityFromIP, + // requestCoordsFromIP, + geolocateFromIP, } diff --git a/src/weatherAPI.js b/src/weatherAPI.js index 24b321b..60f9e3b 100644 --- a/src/weatherAPI.js +++ b/src/weatherAPI.js @@ -145,7 +145,7 @@ function generateForecast(weatherData) { dailyForecasts[day] = singleDayForecast }) - console.log(dailyForecasts) + // console.log(dailyForecasts) return dailyForecasts } @@ -182,8 +182,8 @@ function getWeather(lat, lon, timezone) { timezone: timezone }) - console.log('------------------------------\nQuery parameters:') - console.log(queryParams) + // console.log('------------------------------\nQuery parameters:') + // console.log(queryParams) const queryString = Object.keys(queryParams).map(key => key + '=' + queryParams[key]).join('&') diff --git a/views/currentConditions.pug b/views/currentConditions.pug index 37316ef..b814928 100644 --- a/views/currentConditions.pug +++ b/views/currentConditions.pug @@ -2,7 +2,7 @@ h3 Weather Forecast button(id="geolocate") Get Local Forecast p(id="coords") #forecast - if forecast.current + if forecast && forecast.current if forecast.current.temperature_2m #currentTemperature span(id="currentTempVal") #{forecast.current.temperature_2m[0]} From 8c306a6c1c25fa0f000f26b08fb8efab1e617c95 Mon Sep 17 00:00:00 2001 From: Nick Anderegg Date: Fri, 19 Apr 2024 17:37:28 -0400 Subject: [PATCH 2/4] fix: add callbacks to render function calls --- index.js | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/index.js b/index.js index 58f8420..644657e 100644 --- a/index.js +++ b/index.js @@ -49,7 +49,7 @@ api.get('/', (req, res) => { } return weather(coords.lat, coords.lon, coords.timezone) - }).catch((coords) => { + }).catch(() => { // If the geolocation fails, default to Toronto, Ontario, Canada, then call // the weather API with the coordinates and timezone. @@ -62,13 +62,28 @@ api.get('/', (req, res) => { // template values specified in `renderValues`. renderValues.forecast = weatherData - res.render('index', renderValues) + return res.render('index', renderValues, function (err, html) { + if (err) { + console.error("Error rendering index page:", err) + return res.status(500).send('An error occurred while rendering the page.') + } else { + return res.send(html) + } + }) }).catch((e) => { // If the weather API call fails, render the index page with the template // and the limited values that are available. console.error("Error in main route:", e) - res.render('index', renderValues) + res.render('index', renderValues, function (err, html) { + if (err) { + console.error("Error rendering index page:", err) + return res.status(500).send('An error occurred while rendering the page.') + } else { + return res.send(html) + } + + }) }) }) From 8eb82943811585449f42d60ad27f05755d881442 Mon Sep 17 00:00:00 2001 From: Nick Anderegg Date: Fri, 19 Apr 2024 18:09:45 -0400 Subject: [PATCH 3/4] fix: slight refactor to handle data processing better --- index.js | 25 ++++--------------------- src/geoipAPI.js | 19 +++++++++++++++++++ 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/index.js b/index.js index 644657e..1c95378 100644 --- a/index.js +++ b/index.js @@ -32,22 +32,7 @@ api.get('/', (req, res) => { // If the geolocation is successful, format the name of the returned location, // then call the weather API with the coordinates and timezone. - if ("regionName" in coords && "city" in coords && "country" in coords) { - renderValues.locationName = `${coords.city}, ${coords.regionName}, ${coords.country}` - } else if ("country" in coords) { - if ("city" in coords) { - renderValues.locationName = `${coords.city}, ${coords.country}` - } else if ("regionName" in coords) { - renderValues.locationName = `${coords.regionName}, ${coords.country}` - } else { - renderValues.locationName = coords.country - } - } else if ("city" in coords) { - renderValues.locationName = coords.city - } else { - renderValues.locationName = coords.regionName - } - + renderValues.locationName = coords.locationName return weather(coords.lat, coords.lon, coords.timezone) }).catch(() => { // If the geolocation fails, default to Toronto, Ontario, Canada, then call @@ -92,11 +77,9 @@ api.get('/geolocate', (req, res) => { // will send a request to `/geolocate` to get the estimated coordinates // of the client's IP address. This will then return the coordinates to the // client, which will use them to call the weather API as it normally would. - geolocateFromIP(req.ip).then((coords) => { - res.json(coords) - }).catch((e) => { - res.json({status: 'error', code: 500, message: e.message}) - }) + geolocateFromIP(req.ip) + .then(coords => res.json(coords)) + .catch(e => res.json({status: 'error', code: 500, message: e.message})) }) api.get('/weather', (req, res) => { diff --git a/src/geoipAPI.js b/src/geoipAPI.js index a942a53..1c850a9 100644 --- a/src/geoipAPI.js +++ b/src/geoipAPI.js @@ -61,6 +61,25 @@ function apiRequest(url) { coordsResponse = JSON.parse(responseData) // console.log("coordsResponse: ", coordsResponse) + let locationName = '' + if ("regionName" in coordsResponse && "city" in coordsResponse && "country" in coordsResponse) { + locationName = `${coordsResponse.city}, ${coordsResponse.regionName}, ${coordsResponse.country}` + } else if ("country" in coordsResponse) { + if ("city" in coordsResponse) { + locationName = `${coordsResponse.city}, ${coordsResponse.country}` + } else if ("regionName" in coordsResponse) { + locationName = `${coordsResponse.regionName}, ${coordsResponse.country}` + } else { + locationName = coordsResponse.country + } + } else if ("city" in coordsResponse) { + locationName = coordsResponse.city + } else { + locationName = coordsResponse.regionName + } + + coordsResponse.locationName = locationName + // Because this sample app is used in live demos, we want to // anonymize the coordinates returned to the client side. if ("lat" in coordsResponse && "lon" in coordsResponse) { From 35aaeb41fde15596d08bbbbcbae0e4f0aea80a22 Mon Sep 17 00:00:00 2001 From: Nick Anderegg Date: Fri, 19 Apr 2024 18:19:01 -0400 Subject: [PATCH 4/4] fix: remove unused code --- src/geoipAPI.js | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/src/geoipAPI.js b/src/geoipAPI.js index 1c850a9..dd48de9 100644 --- a/src/geoipAPI.js +++ b/src/geoipAPI.js @@ -112,28 +112,6 @@ function apiRequest(url) { }) } -// function requestCoordsFromIP(ipAddr) { - -// // Validate input and throw an error if the input string isn't a valid IP address. -// if (!ipAddrRegex.test(ipAddr)) { -// throw new Error('Invalid IP address format') -// } - -// const url = buildAPIRequest(ipAddr, 'status,country,regionName,city,lat,lon,timezone') -// return apiRequest(url) -// } - -// function requestCityFromIP(ipAddr) { - -// // Validate input and throw an error if the input string isn't a valid IP address. -// if (!ipAddrRegex.test(ipAddr)) { -// throw new Error('Invalid IP address format') -// } - -// const url = buildAPIRequest(ipAddr, 'status,country,regionName,city,lat,lon,timezone') -// return apiRequest(url) -// } - function geolocateFromIP(ipAddr) { // Validate input and throw an error if the input string isn't a valid IP address. @@ -148,7 +126,5 @@ function geolocateFromIP(ipAddr) { module.exports = { - // requestCityFromIP, - // requestCoordsFromIP, geolocateFromIP, }