diff --git a/endpoints/road/documentation.md b/endpoints/road/documentation.md new file mode 100644 index 00000000..02e2f97e --- /dev/null +++ b/endpoints/road/documentation.md @@ -0,0 +1,17 @@ +# Road conditions in Iceland + +Source: [Vegagerdin](http://gagnaveita.vegagerdin.is/api/faerd2014_1) + +http://www.vegagerdin.is/upplysingar-og-utgafa/gagnaveita-vegagerdarinnar/ + +- GET [/road](https://apis.is/road) +- GET [/road/all](https://apis.is/road/all) + +Lookup road conditions in Iceland + +| Endpoints | Description | Example | +|------------|------------------------------------------------|---------------------------------------------| +| :endpoint | Which region in Iceland to get road conditions | [all](https://apis.is/road/all) | +| /reykjavik | | [reykjavik](https://apis.is/road/reykjavik) | + +--- diff --git a/endpoints/road/index.js b/endpoints/road/index.js new file mode 100644 index 00000000..5541061d --- /dev/null +++ b/endpoints/road/index.js @@ -0,0 +1,65 @@ +/* eslint-disable no-plusplus */ +/* eslint-disable no-prototype-builtins */ +/* eslint-disable prefer-destructuring */ +/* eslint-disable no-unneeded-ternary */ + +const request = require('request') +const xml2js = require('xml2js') +const h = require('apis-helpers') +const app = require('../../server') + +const parseString = xml2js.parseString + +const parseFeed = function (callback, data) { + parseString(data, { explicitRoot: false }, (err, result) => { + if (err) return callback(new Error(`Parsing of XML failed. Title ${err}`)) + const roads = [] + for (let i = 0; i < result.Faerd.length; ++i) { + const Road = result.Faerd[i] + roads.push({ + routeId: Road.IdLeid[0].length > 0 ? Road.IdLeid[0] : null, // Can be null + routeName: Road.LeidNafn[0].length > 0 ? Road.LeidNafn[0] : null, // Can be null + segmentId: parseInt(Road.IdButur[0], 10), + segmentSerial: Road.Rodun[0], + segmentName: Road.LangtNafn[0], + segmentShortName: Road.StuttNafn[0], + segmentSignal: Road.Skilti[0].length > 0 ? Road.Skilti[0] : null, + conditionId: Road.IdAstand[0], + conditionDescription: Road.FulltAstand[0], + conditionShortDescription: Road.StuttAstand[0], + priority: parseInt(Road.Forgangur[0], 10), + comment: Road.Aths[0].length > 0 ? Road.Aths[0] : null, + date: Road.DagsKeyrtUt[0], + isHighlands: parseInt(Road.ErHalendi[0], 2) === 1 ? true : false, + colorCode: Road.Linulitur[0].length > 0 ? Road.Linulitur[0] : null, + conditionUpdated: Road.DagsSkrad[0], + surfaceCondition: Road.AstandYfirbords[0], + }) + } + return callback(null, roads) + }) +} +const getFeed = function (url, callback) { + request.get({ + headers: { 'User-Agent': h.browser(), 'Content-Type': 'application/xml; charset=utf-8' }, + encoding: 'utf-8', + url, + }, (error, response, body) => { + console.log(body) + if (error) return callback(new Error(`${url} did not respond ${JSON.stringify(error)}`)) + parseFeed(callback, body) + }) +} +const serve = function (url, res, next) { + getFeed(url, (err, data) => { + if (err) { + console.error(err) + return next(502) + } + res.cache(1800).json({ results: data }) + }) +} +app.get('/road', (req, res, next) => { + const url = 'http://gagnaveita.vegagerdin.is/api/faerd2014_1' + serve(url, res, next) +}) diff --git a/endpoints/road/regions.js b/endpoints/road/regions.js new file mode 100644 index 00000000..a684d93f --- /dev/null +++ b/endpoints/road/regions.js @@ -0,0 +1,123 @@ +/* eslint-disable */ + +const request = require('request') +const xml2js = require('xml2js') +const h = require('apis-helpers') +const cheerio = require('cheerio') +const app = require('../../server') + +const parseString = xml2js.parseString + +const sourceUrl = 'http://gagnaveita.vegagerdin.is/api/faerd2014_1' + +const getRegionSegments = (url) => new Promise((resolve, reject) => { + request(url, (error, response, body) => { + if (error) { + reject(error) + } + + let $ + + try { + $ = cheerio.load(body) + } catch (e) { + return reject(error) + } + const hotspots = $('.vg-roadmap-hotspot') + + const segments = [] + // Loop through hotspots + hotspots.each(function () { + // This hotspot + const hotspot = $(this) + + // Find hotspot info + const hotspotinfo = hotspot.attr('data-hotspotinfo') + const obj = JSON.parse(hotspotinfo.replace(/'/g, '"')) + if (obj.idleid) + segments.push(parseInt(obj.idleid, 10)) + }) + resolve(segments) + }) +}) +const parseFeed = function (callback, data, regionUrl) { + parseString(data, { explicitRoot: false }, (err, result) => { + if (err) return callback(new Error(`Parsing of XML failed. Title ${err}`)) + const regionSegments = getRegionSegments(regionUrl) + regionSegments.then((segments) => { + const roads = [] + + for (let i = 0; i < result.Faerd.length; ++i) { + const Road = result.Faerd[i] + const shortSegmentId = parseInt(Road.IdButur[0].slice(0, 5), 10) + if (segments.includes(shortSegmentId)) + roads.push({ + routeId: Road.IdLeid[0].length > 0 ? Road.IdLeid[0] : null, // Can be null + routeName: Road.LeidNafn[0].length > 0 ? Road.LeidNafn[0] : null, // Can be null + segmentId: parseInt(Road.IdButur[0], 10), + segmentSerial: Road.Rodun[0], + segmentName: Road.LangtNafn[0], + segmentShortName: Road.StuttNafn[0], + segmentSignal: Road.Skilti[0].length > 0 ? Road.Skilti[0] : null, + conditionId: Road.IdAstand[0], + conditionDescription: Road.FulltAstand[0], + conditionShortDescription: Road.StuttAstand[0], + priority: parseInt(Road.Forgangur[0], 10), + comment: Road.Aths[0].length > 0 ? Road.Aths[0] : null, + date: Road.DagsKeyrtUt[0], + isHighlands: parseInt(Road.ErHalendi[0], 2) === 1 ? true : false, + colorCode: Road.Linulitur[0].length > 0 ? Road.Linulitur[0] : null, + conditionUpdated: Road.DagsSkrad[0], + surfaceCondition: Road.AstandYfirbords[0], + }) + } + return callback(null, roads) + }) + }) +} +const getFeed = function (url, regionUrl, callback) { + request.get({ + headers: { 'User-Agent': h.browser(), 'Content-Type': 'application/xml; charset=utf-8' }, + encoding: 'utf-8', + url, + }, (error, response, body) => { + if (error) return callback(new Error(`${url} did not respond ${JSON.stringify(error)}`)) + parseFeed(callback, body, regionUrl) + }) +} + +const serve = function (url, regionUrl, res, next) { + getFeed(url, regionUrl, (err, data) => { + if (err) { + console.error(err) + return next(502) + } + res.cache(1800).json({ results: data }) + }) +} + +app.get('/road/:region', (req, res, next) => { + const regionParam = req.params.region + let regionUrl = null + if (regionParam === 'reykjavik') + regionUrl = 'http://www.vegagerdin.is/ferdaupplysingar/faerd-og-vedur/reykjavik-og-nagrenni-faerd-kort/' + else if (regionParam === 'west') + regionUrl = 'http://www.vegagerdin.is/ferdaupplysingar/faerd-og-vedur/vesturland-faerd-kort/' + else if (regionParam === 'southwest') + regionUrl = 'http://vegagerdin.is/ferdaupplysingar/faerd-og-vedur/sudvesturland-faerd-kort/' + else if (regionParam === 'westfjords') + regionUrl = 'http://www.vegagerdin.is/ferdaupplysingar/faerd-og-vedur/vestfirdir-faerd-kort/' + else if (regionParam === 'south') + regionUrl = 'http://www.vegagerdin.is/ferdaupplysingar/faerd-og-vedur/sudurland-faerd-kort/' + else if (regionParam === 'north') + regionUrl = 'http://www.vegagerdin.is/ferdaupplysingar/faerd-og-vedur/nordurland-faerd-kort/' + else if (regionParam === 'east') + regionUrl = 'http://www.vegagerdin.is/ferdaupplysingar/faerd-og-vedur/austurland-faerd-kort/' + else if (regionParam === 'northeast') + regionUrl = 'http://www.vegagerdin.is/ferdaupplysingar/faerd-og-vedur/nordausturland-faerd-kort/' + else if (regionParam === 'southeast') + regionUrl = 'http://www.vegagerdin.is/ferdaupplysingar/faerd-og-vedur/sudausturland-faerd-kort/' + else if (regionParam === 'highlands') + regionUrl = 'http://www.vegagerdin.is/ferdaupplysingar/faerd-og-vedur/midhalendid-faerd-kort/' + serve(sourceUrl, regionUrl, res, next) +}) diff --git a/endpoints/road/tests/integration_test.js b/endpoints/road/tests/integration_test.js new file mode 100644 index 00000000..a25a9d71 --- /dev/null +++ b/endpoints/road/tests/integration_test.js @@ -0,0 +1,29 @@ +const request = require('request') +const helpers = require('../../../lib/test_helpers') + +describe('road', () => { + it('should return an array of objects containing correct fields', (done) => { + const fieldsToCheckFor = [ + 'routeId', + 'routeName', + 'segmentId', + 'segmentSerial', + 'segmentName', + 'segmentShortName', + 'segmentSignal', + 'conditionId', + 'conditionDescription', + 'conditionShortDescription', + 'priority', + 'comment', + 'date', + 'isHighlands', + 'colorCode', + 'conditionUpdated', + 'surfaceCondition', + ] + const params = helpers.testRequestParams('/road') + const resultHandler = helpers.testRequestHandlerForFields(done, fieldsToCheckFor) + request.get(params, resultHandler) + }) +})