├── .gitignore ├── .editorconfig ├── .npmignore ├── .travis.yml ├── exampleRequests ├── getServiceDetails.xml ├── getNextArrival.xml ├── getNextDeparture.xml ├── getFastestDeparturesRequest.xml ├── getArrivalBoard.xml ├── ArrivalBoardOptions.xml ├── getDepartureBoard.xml ├── getNextDeparturesWithDetailsRequest.xml ├── getFastestDeparturesWithDetailsRequest.xml ├── getArrivalBoardWithDetails.xml ├── getArrivalDepartureBoard.xml ├── getDepartureBoardWithDetails.xml ├── getArrDepBoardWithDetailsRequest.xml └── generateRequests.js ├── CHANGELOG.md ├── .github └── ISSUE_TEMPLATE │ └── bug_report.md ├── package.json ├── LICENSE ├── test ├── templateStringTest.js ├── clientTest.js ├── requestTest.js └── test.js ├── exampleResponses ├── nextDeparture.xml ├── fastestDeparture.xml ├── arrivalBoardWithDets_bus.xml ├── nextDepartureWithDetails.xml ├── fastestDeparturesWithDetails.xml ├── arrivalBoardWithDets.xml ├── serviceDetails.xml ├── arrivalsBoard.xml ├── departureBoard.xml ├── arrivalDepartureBoard.xml └── departureBoardWithDetails.xml ├── requestBuilder.js ├── templates.js ├── bin └── darwin.js ├── index.js ├── README.md └── parsers.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | token.txt 3 | .nyc_output 4 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | [*] 2 | end_of_line = lf 3 | insert_final_newline = true 4 | indent_style = space 5 | indent_size = 4 6 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .editorconfig 2 | token.txt 3 | exampleResponses 4 | exampleRequests 5 | .nyc_output 6 | change.log 7 | test 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "lts/*" 4 | script: 5 | - mocha "./test/**/!(clientTest).js" 6 | -------------------------------------------------------------------------------- /exampleRequests/getServiceDetails.xml: -------------------------------------------------------------------------------- 1 | $$TOKEN$$123456789 2 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ### 1.0.6 [2018-01-11] 2 | 3 | 1. Upgrade to version 11 of the API wasn't completed fully in v1.0.5. Fixed in this release. 4 | 2. darwin command line tool for requests. 5 | 6 | ### 1.0.5 [2018-01-11] 7 | 8 | 1. Update National Rail base url to point to version 11 of the Darwin api. 9 | 2. Increase unit test coverage of requests and parsers. 10 | 3. Bug fix in apply options that caused incorrect defaults to be used if no options object passed to a function. 11 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 16 | **Expected behavior** 17 | A clear and concise description of what you expected to happen. 18 | 19 | **Additional context** 20 | Add any other context about the problem here. 21 | -------------------------------------------------------------------------------- /exampleRequests/getNextArrival.xml: -------------------------------------------------------------------------------- 1 | $$TOKEN$$ISL WAT0120 2 | -------------------------------------------------------------------------------- /exampleRequests/getNextDeparture.xml: -------------------------------------------------------------------------------- 1 | $$TOKEN$$ISL WAT0120 2 | -------------------------------------------------------------------------------- /exampleRequests/getFastestDeparturesRequest.xml: -------------------------------------------------------------------------------- 1 | $$TOKEN$$ISL WAT0120 2 | -------------------------------------------------------------------------------- /exampleRequests/getArrivalBoard.xml: -------------------------------------------------------------------------------- 1 | $$TOKEN$$15ISLto0120 2 | -------------------------------------------------------------------------------- /exampleRequests/ArrivalBoardOptions.xml: -------------------------------------------------------------------------------- 1 | $$TOKEN$$5ISLWATto560 2 | -------------------------------------------------------------------------------- /exampleRequests/getDepartureBoard.xml: -------------------------------------------------------------------------------- 1 | $$TOKEN$$15ISLto0120 2 | -------------------------------------------------------------------------------- /exampleRequests/getNextDeparturesWithDetailsRequest.xml: -------------------------------------------------------------------------------- 1 | $$TOKEN$$ISL WAT0120 2 | -------------------------------------------------------------------------------- /exampleRequests/getFastestDeparturesWithDetailsRequest.xml: -------------------------------------------------------------------------------- 1 | $$TOKEN$$ISL WAT0120 2 | -------------------------------------------------------------------------------- /exampleRequests/getArrivalBoardWithDetails.xml: -------------------------------------------------------------------------------- 1 | $$TOKEN$$15ISLto0120 2 | -------------------------------------------------------------------------------- /exampleRequests/getArrivalDepartureBoard.xml: -------------------------------------------------------------------------------- 1 | $$TOKEN$$15ISLto0120 2 | -------------------------------------------------------------------------------- /exampleRequests/getDepartureBoardWithDetails.xml: -------------------------------------------------------------------------------- 1 | $$TOKEN$$15ISLto0120 2 | -------------------------------------------------------------------------------- /exampleRequests/getArrDepBoardWithDetailsRequest.xml: -------------------------------------------------------------------------------- 1 | $$TOKEN$$15ISLto0120 2 | -------------------------------------------------------------------------------- /exampleRequests/generateRequests.js: -------------------------------------------------------------------------------- 1 | /** 2 | Creates a complete set of requests for se with testing 3 | */ 4 | 5 | var requestBuilder = require('../requestBuilder.js') 6 | 7 | console.log(requestBuilder.getDepartureBoardRequest('ISL', {})) 8 | console.log(requestBuilder.getDepartureBoardWithDetails('ISL', {})) 9 | console.log(requestBuilder.getArrivalsBoard('ISL', {})) 10 | console.log(requestBuilder.getArrivalsBoardWithDetails('ISL', {})) 11 | console.log(requestBuilder.getArrivalsDepartureBoard('ISL', {})) 12 | console.log(requestBuilder.getArrivalsDepartureBoardWithDetails('ISL', {})) 13 | console.log(requestBuilder.getServiceDetails('123456789', {})) 14 | console.log(requestBuilder.getNextDeparture('ISL', 'WAT', {})) 15 | console.log(requestBuilder.getNextDepartureWithDetails('ISL', 'WAT', {})) 16 | console.log(requestBuilder.getArrival('ISL', 'WAT', {})) 17 | console.log(requestBuilder.getFastestDeparture('ISL', 'WAT', {})) 18 | console.log(requestBuilder.getFastestDepartureWithDetails('ISL', 'WAT', {})) 19 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "national-rail-darwin", 3 | "version": "1.0.8", 4 | "description": " national rail's darwin soap based api", 5 | "main": "index.js", 6 | "bin": { 7 | "darwin": "bin/darwin.js" 8 | }, 9 | "scripts": { 10 | "test": "nyc mocha" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git+https://github.com/d5039m/national-rail-darwin.git" 15 | }, 16 | "keywords": [ 17 | "national-rail", 18 | "darwin", 19 | "trains", 20 | "rail" 21 | ], 22 | "author": "Matthew Salt", 23 | "license": "MIT", 24 | "bugs": { 25 | "url": "https://github.com/d5039m/national-rail-darwin/issues" 26 | }, 27 | "homepage": "https://github.com/d5039m/national-rail-darwin#readme", 28 | "dependencies": { 29 | "bluebird": "3.4.6", 30 | "commander": "^2.13.0", 31 | "request": "^2.85.0", 32 | "xmldoc": "0.5.1" 33 | }, 34 | "devDependencies": { 35 | "assert": "^1.4.1", 36 | "mocha": "^4.0.1", 37 | "nyc": "^14.1.1", 38 | "standard": "^8.6.0" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Matthew Salt 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /test/templateStringTest.js: -------------------------------------------------------------------------------- 1 | // var parsers = require('../parsers.js') 2 | // var fs = require('fs') 3 | // var fileContent 4 | // fileContent = fs.readFileSync('./exampleResponses/serviceDetails.xml', 'UTF-8') 5 | // parsers.parseServiceIdResponse(fileContent) 6 | 7 | // fileContent = fs.readFileSync('./exampleResponses/nextDepartureWithDetails.xml', 'UTF-8') 8 | // parsers.parseNextDepartureWithDetailsResponse(fileContent) 9 | 10 | // fileContent = fs.readFileSync('./exampleResponses/fastestDeparturesWithDetails.xml', 'UTF-8') 11 | // parsers.parseFastestDeparturesWithDetail(fileContent) 12 | 13 | // fileContent = fs.readFileSync('./exampleResponses/fastestDeparture.xml', 'UTF-8') 14 | // parsers.parseFastestDeparture(fileContent) 15 | 16 | // fileContent = fs.readFileSync('./exampleResponses/arrivalBoardWithDets.xml', 'UTF-8') 17 | // parsers.parseArrivalsBoardWithDetails(fileContent) 18 | 19 | // fileContent = fs.readFileSync('./exampleResponses/arrivalDepartureBoard.xml', 'UTF-8') 20 | // parsers.parseArrivalsDepartureBoard(fileContent) 21 | 22 | // fileContent = fs.readFileSync('./exampleResponses/arrivalDepartureBoardWithDetails.xml', 'UTF-8') 23 | // parsers.parseArrivalsDepartureBoardWithDetails(fileContent) 24 | 25 | // fileContent = fs.readFileSync('./exampleResponses/arrivalsBoard.xml', 'UTF-8') 26 | // parsers.parseArrivalsBoardResponse(fileContent) 27 | 28 | // fileContent = fs.readFileSync('./exampleResponses/departureBoard.xml', 'UTF-8') 29 | // parsers.parseDepartureBoardResponse(fileContent) 30 | 31 | // fileContent = fs.readFileSync('./exampleResponses/departureBoardWithDetails.xml', 'UTF-8') 32 | // parsers.parseDepartureBoardWithDetailsResponse(fileContent) 33 | 34 | // fileContent = fs.readFileSync('./exampleResponses/nextDeparture.xml', 'UTF-8') 35 | // parsers.parseNextDestinationResponse(fileContent) 36 | -------------------------------------------------------------------------------- /exampleResponses/nextDeparture.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 2018-01-11T18:34:01.9836252+00:00 6 | Isleworth 7 | ISL 8 | true 9 | 10 | 11 | 12 | 18:34 13 | On time 14 | 18:34 15 | On time 16 | 1 17 | South Western Railway 18 | SW 19 | train 20 | 10 21 | wfmF+NYLrCpeoJVHBxSxrQ== 22 | SW182100 23 | 24 | 25 | London Waterloo 26 | WAT 27 | 28 | 29 | 30 | 31 | London Waterloo 32 | WAT 33 | via Brentford 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /exampleResponses/fastestDeparture.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 2018-01-11T18:33:01.3148363+00:00 6 | Isleworth 7 | ISL 8 | true 9 | 10 | 11 | 12 | 18:34 13 | On time 14 | 18:34 15 | On time 16 | 1 17 | South Western Railway 18 | SW 19 | train 20 | 10 21 | wfmF+NYLrCpeoJVHBxSxrQ== 22 | SW182100 23 | 24 | 25 | London Waterloo 26 | WAT 27 | 28 | 29 | 30 | 31 | London Waterloo 32 | WAT 33 | via Brentford 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /test/clientTest.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs') 2 | var path = require('path') 3 | var token = fs.readFileSync(path.resolve('.', 'token.txt'), 'UTF-8').trim() 4 | var requestBuilder = require('../requestBuilder.js') 5 | 6 | var Rail = require('../index.js') 7 | var client = new Rail(token.trim()) 8 | 9 | client.thenablePOST(requestBuilder.getArrivalsBoardWithDetails('ISL', {'destination': 'WAT'})).then(function (result) { 10 | console.log('1) Success') 11 | }).catch(function (err) { 12 | console.log('1) Error: ' + err) 13 | }) 14 | client.thenablePOST(requestBuilder.getArrivalsBoard('ISL', {})).then(function (result) { 15 | console.log('2) Success') 16 | }).catch(function (err) { 17 | console.log('2) Error: ' + err) 18 | }) 19 | client.thenablePOST(requestBuilder.getArrivalsBoardWithDetails('ISL', {})).then(function (result) { 20 | console.log('3) Success') 21 | }).catch(function (err) { 22 | console.log('3) Error: ' + err) 23 | }) 24 | client.thenablePOST(requestBuilder.getArrivalsDepartureBoard('ISL', {})).then(function (result) { 25 | console.log('4) Success') 26 | }).catch(function (err) { 27 | console.log('4) Error: ' + err) 28 | }) 29 | client.thenablePOST(requestBuilder.getDepartureBoardRequest('ISL', {})).then(function (result) { 30 | console.log('5) Success') 31 | }).catch(function (err) { 32 | console.log('5) Error: ' + err) 33 | }) 34 | client.thenablePOST(requestBuilder.getDepartureBoardWithDetails('ISL', {})).then(function (result) { 35 | console.log('6) Success') 36 | }).catch(function (err) { 37 | console.log('6) Error: ' + err) 38 | }) 39 | client.thenablePOST(requestBuilder.getFastestDeparture('ISL', 'WAT', {})).then(function (result) { 40 | console.log('7) Success') 41 | }).catch(function (err) { 42 | console.log('7) Error: ' + err) 43 | }) 44 | client.thenablePOST(requestBuilder.getFastestDepartureWithDetails('ISL', 'WAT', {})).then(function (result) { 45 | console.log('8) Success') 46 | }).catch(function (err) { 47 | console.log('8) Error: ' + err) 48 | }) 49 | client.thenablePOST(requestBuilder.getNextDepartureWithDetails('ISL', 'WAT', {})).then(function (result) { 50 | console.log('9) Success') 51 | }).catch(function (err) { 52 | console.log('9) Error: ' + err) 53 | }) 54 | client.thenablePOST(requestBuilder.getNextDeparture('ISL', 'WAT', {})).then(function (result) { 55 | console.log('10) Success') 56 | }).catch(function (err) { 57 | console.log('10) Error: ' + err) 58 | }) 59 | client.thenablePOST(requestBuilder.getArrival('ISL', 'WAT', {})).then(function (result) { 60 | console.log('11) Success') 61 | }).catch(function (err) { 62 | console.log('11) Error: ' + err) 63 | }) 64 | client.thenablePOST(requestBuilder.getServiceDetails('123456789', {})).then(function (result) { 65 | console.log('12) Success') 66 | }).catch(function (err) { 67 | console.log('12) Error: ' + err.statusCode + ' ' + err.body) 68 | }) 69 | -------------------------------------------------------------------------------- /test/requestTest.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 3 | /* 4 | Test that request xmls build by the requestBuilder match what we're expecting. 5 | */ 6 | var assert = require('assert') 7 | var requestBuilder = require('../requestBuilder.js') 8 | var fs = require('fs') 9 | 10 | describe('Check request xmls build correctly', function () { 11 | it('Arrival Departure Board with Details', function () { 12 | var fileContent = fs.readFileSync('./exampleRequests/getArrDepBoardWithDetailsRequest.xml', 'UTF-8') 13 | var request = requestBuilder.getArrivalsDepartureBoardWithDetails('ISL') 14 | assert.equal(request, fileContent.trim('string')) 15 | }) 16 | 17 | it('Get Arrival Board', function () { 18 | var fileContent = fs.readFileSync('./exampleRequests/getArrivalBoard.xml', 'UTF-8') 19 | var request = requestBuilder.getArrivalsBoard('ISL', {}) 20 | assert.equal(request, fileContent.trim('string')) 21 | }) 22 | 23 | it('Get Arrival Board with options', function () { 24 | var fileContent = fs.readFileSync('./exampleRequests/ArrivalBoardOptions.xml', 'UTF-8') 25 | var request = requestBuilder.getArrivalsBoard('ISL', 26 | {'timeOffset': 5, 'timeWindow': 60, 'rows': 5, 'destination': 'WAT'}) 27 | assert.equal(request, fileContent.trim('string')) 28 | }) 29 | 30 | it('Get Arrival Board with Details', function () { 31 | var fileContent = fs.readFileSync('./exampleRequests/getArrivalBoardWithDetails.xml', 'UTF-8') 32 | var request = requestBuilder.getArrivalsBoardWithDetails('ISL', {}) 33 | assert.equal(request, fileContent.trim('string')) 34 | }) 35 | 36 | it('Get Arrival Departure Board', function () { 37 | var fileContent = fs.readFileSync('./exampleRequests/getArrivalDepartureBoard.xml', 'UTF-8') 38 | var request = requestBuilder.getArrivalsDepartureBoard('ISL', {}) 39 | assert.equal(request, fileContent.trim('string')) 40 | }) 41 | 42 | it('Get Departure Board', function () { 43 | var fileContent = fs.readFileSync('./exampleRequests/getDepartureBoard.xml', 'UTF-8') 44 | var request = requestBuilder.getDepartureBoardRequest('ISL', {}) 45 | assert.equal(request, fileContent.trim('string')) 46 | }) 47 | 48 | it('Get Departure Board with Details', function () { 49 | var fileContent = fs.readFileSync('./exampleRequests/getDepartureBoardWithDetails.xml', 'UTF-8') 50 | var request = requestBuilder.getDepartureBoardWithDetails('ISL', {}) 51 | assert.equal(request, fileContent.trim('string')) 52 | }) 53 | 54 | it('Get Fastest Departures Request', function () { 55 | var fileContent = fs.readFileSync('./exampleRequests/getFastestDeparturesRequest.xml', 'UTF-8') 56 | var request = requestBuilder.getFastestDeparture('ISL', 'WAT', {}) 57 | assert.equal(request, fileContent.trim('string')) 58 | }) 59 | 60 | it('Get Fastest Departures With Details Request', function () { 61 | var fileContent = fs.readFileSync('./exampleRequests/getFastestDeparturesWithDetailsRequest.xml', 'UTF-8') 62 | var request = requestBuilder.getFastestDepartureWithDetails('ISL', 'WAT', {}) 63 | assert.equal(request, fileContent.trim('string')) 64 | }) 65 | 66 | it('Get Next Departures with Details Request', function () { 67 | var fileContent = fs.readFileSync('./exampleRequests/getNextDeparturesWithDetailsRequest.xml', 'UTF-8') 68 | var request = requestBuilder.getNextDepartureWithDetails('ISL', 'WAT', {}) 69 | assert.equal(request, fileContent.trim('string')) 70 | }) 71 | 72 | it('Get Next Departures Request', function () { 73 | var fileContent = fs.readFileSync('./exampleRequests/getNextDeparture.xml', 'UTF-8') 74 | var request = requestBuilder.getNextDeparture('ISL', 'WAT', {}) 75 | assert.equal(request, fileContent.trim('string')) 76 | }) 77 | 78 | it('Get Arrival (getNextArrival) test', function () { 79 | var fileContent = fs.readFileSync('./exampleRequests/getNextArrival.xml', 'UTF-8') 80 | var request = requestBuilder.getArrival('ISL', 'WAT', {}) 81 | assert.equal(request, fileContent.trim('string')) 82 | }) 83 | 84 | it('Get Service Details', function () { 85 | var fileContent = fs.readFileSync('./exampleRequests/getServiceDetails.xml', 'UTF-8') 86 | var request = requestBuilder.getServiceDetails('123456789', {}) 87 | assert.equal(request, fileContent.trim('string')) 88 | }) 89 | }) 90 | -------------------------------------------------------------------------------- /requestBuilder.js: -------------------------------------------------------------------------------- 1 | var templates = require('./templates.js') 2 | 3 | function applyOptions (requestXML, options) { 4 | var optionDefaults = { 5 | timeOffset: [ 0, 'INT' ], 6 | timeWindow: [ 120, 'INT' ], 7 | rows: [ 15, 'INT' ], 8 | destination: [ '', 'TEXT' ] 9 | } 10 | for (var key in optionDefaults) { 11 | var value = optionDefaults[key][0] 12 | if (options && options[key]) { 13 | if (optionDefaults[key][1] === 'INT') value = parseInt(options[key]) 14 | else value = options[key] 15 | } 16 | requestXML = requestXML.replace('$$' + key.toUpperCase() + '$$', value) 17 | } 18 | return requestXML 19 | } 20 | 21 | var getDepartureBoardRequest = function (station, options) { 22 | var requestXML = applyOptions(templates.departureBoard, options) 23 | requestXML = requestXML.replace('$$FROM$$', station) 24 | return requestXML 25 | } 26 | 27 | var getDepartureBoardWithDetails = function (station, options) { 28 | var requestXML = applyOptions(templates.departureBoardWithDetails, options) 29 | requestXML = requestXML.replace('$$FROM$$', station) 30 | return requestXML 31 | } 32 | 33 | var getArrivalsBoard = function (station, options) { 34 | var requestXML = applyOptions(templates.arrivalsBoard, options) 35 | requestXML = requestXML.replace('$$FROM$$', station) 36 | return requestXML 37 | } 38 | 39 | var getArrivalsBoardWithDetails = function (station, options) { 40 | var requestXML = applyOptions(templates.arrivalsBoardWithDetails, options) 41 | requestXML = requestXML.replace('$$FROM$$', station) 42 | return requestXML 43 | } 44 | var getArrivalsDepartureBoard = function (station, options) { 45 | var requestXML = applyOptions(templates.arrivalsDepartureBoard, options) 46 | requestXML = requestXML.replace('$$FROM$$', station) 47 | return requestXML 48 | } 49 | var getArrivalsDepartureBoardWithDetails = function (station, options) { 50 | var requestXML = applyOptions(templates.arrivalsDepartureBoardWithDetails, options) 51 | requestXML = requestXML.replace('$$FROM$$', station) 52 | return requestXML 53 | } 54 | var getServiceDetails = function (serviceId) { 55 | var requestXML = templates.serviceDetails.replace('$$SERVICEID$$', serviceId) 56 | return requestXML 57 | } 58 | var getNextDeparture = function (station, destination, options) { 59 | options.destination = destination 60 | var requestXML = applyOptions(templates.nextDeparture, options) 61 | requestXML = requestXML.replace('$$FROM$$', station) 62 | return requestXML 63 | } 64 | var getNextDepartureWithDetails = function (station, destination, options) { 65 | options.destination = destination 66 | var requestXML = applyOptions(templates.nextDepartureWithDetails, options) 67 | requestXML = requestXML.replace('$$FROM$$', station) 68 | return requestXML 69 | } 70 | var getArrival = function (station, destination, options) { 71 | options.destination = destination 72 | var requestXML = applyOptions(templates.nextArrival, options) 73 | requestXML = requestXML.replace('$$FROM$$', station) 74 | return requestXML 75 | } 76 | var getFastestDeparture = function (station, destination, options) { 77 | options.destination = destination 78 | var requestXML = applyOptions(templates.fastestDeparture, options) 79 | requestXML = requestXML.replace('$$FROM$$', station) 80 | return requestXML 81 | } 82 | var getFastestDepartureWithDetails = function (station, destination, options) { 83 | options.destination = destination 84 | var requestXML = applyOptions(templates.fastestDepartureWithDetails, options) 85 | requestXML = requestXML.replace('$$FROM$$', station) 86 | return requestXML 87 | } 88 | module.exports.getDepartureBoardRequest = getDepartureBoardRequest 89 | module.exports.getDepartureBoardWithDetails = getDepartureBoardWithDetails 90 | module.exports.getArrivalsBoard = getArrivalsBoard 91 | module.exports.getArrivalsBoardWithDetails = getArrivalsBoardWithDetails 92 | module.exports.getArrivalsDepartureBoard = getArrivalsDepartureBoard 93 | module.exports.getArrivalsDepartureBoardWithDetails = getArrivalsDepartureBoardWithDetails 94 | module.exports.getServiceDetails = getServiceDetails 95 | module.exports.getNextDeparture = getNextDeparture 96 | module.exports.getNextDepartureWithDetails = getNextDepartureWithDetails 97 | module.exports.getArrival = getArrival 98 | module.exports.getFastestDeparture = getFastestDeparture 99 | module.exports.getFastestDepartureWithDetails = getFastestDepartureWithDetails 100 | -------------------------------------------------------------------------------- /exampleResponses/arrivalBoardWithDets_bus.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 9 | 16 | 2017-11-05T13:01:03.9634517+00:00 17 | Manchester Victoria 18 | MCV 19 | true 20 | 21 | 22 | 13:13 23 | On time 24 | BUS 25 | Northern 26 | NT 27 | bus 28 | 4K/pJ3foFNnVXFl6ON8t+A== 29 | 30 | 31 | Preston 32 | PRE 33 | 34 | 35 | 36 | 37 | Manchester Victoria 38 | MCV 39 | 40 | 41 | 42 | 43 | 44 | Preston 45 | PRE 46 | 11:00 47 | On time 48 | 49 | 50 | Leyland 51 | LEY 52 | 11:20 53 | On time 54 | 55 | 56 | Buckshaw Parkway 57 | BSV 58 | 11:34 59 | On time 60 | 61 | 62 | Chorley 63 | CRL 64 | 11:49 65 | On time 66 | 67 | 68 | Adlington (Lancashire) 69 | ADL 70 | 11:59 71 | On time 72 | 73 | 74 | Blackrod 75 | BLK 76 | 12:06 77 | On time 78 | 79 | 80 | Horwich Parkway 81 | HWI 82 | 12:12 83 | On time 84 | 85 | 86 | Lostock 87 | LOT 88 | 12:20 89 | On time 90 | 91 | 92 | Bolton 93 | BON 94 | 12:38 95 | On time 96 | 97 | 98 | Salford Crescent 99 | SLD 100 | 13:03 101 | On time 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | -------------------------------------------------------------------------------- /templates.js: -------------------------------------------------------------------------------- 1 | var soapHeader = 2 | '' + 3 | `` + 4 | `` + 5 | `$$TOKEN$$` + 6 | `` + 7 | `` 8 | 9 | var getServiceBoardStandardRequest = 10 | `$$ROWS$$` + 11 | `$$FROM$$` + 12 | `$$DESTINATION$$` + 13 | `to` + 14 | `$$TIMEOFFSET$$` + 15 | `$$TIMEWINDOW$$` 16 | 17 | var getServiceStandardRequest = 18 | `$$FROM$$` + 19 | `` + 20 | ` $$DESTINATION$$` + 21 | `` + 22 | `$$TIMEOFFSET$$` + 23 | `$$TIMEWINDOW$$` 24 | 25 | var arrivalsBoardWithDetails = 26 | soapHeader + 27 | `` + 28 | `` + 29 | getServiceBoardStandardRequest + 30 | `` + 31 | `` + 32 | `` 33 | 34 | var arrivalsDepartureBoard = 35 | soapHeader + 36 | `` + 37 | `` + 38 | getServiceBoardStandardRequest + 39 | `` + 40 | `` + 41 | `` 42 | 43 | var arrivalsDepartureBoardWithDetails = 44 | soapHeader + 45 | `` + 46 | `` + 47 | getServiceBoardStandardRequest + 48 | `` + 49 | `` + 50 | `` 51 | 52 | var departureBoardTemplate = 53 | soapHeader + 54 | `` + 55 | `` + 56 | getServiceBoardStandardRequest + 57 | `` + 58 | `` + 59 | `` 60 | 61 | var departureBoardWithDetails = 62 | soapHeader + 63 | `` + 64 | `` + 65 | getServiceBoardStandardRequest + 66 | `` + 67 | `` + 68 | `` 69 | 70 | var arrivalsBoardTemplate = 71 | soapHeader + 72 | `` + 73 | `` + 74 | getServiceBoardStandardRequest + 75 | `` + 76 | `` + 77 | `` 78 | 79 | var serviceDetailsTemplate = 80 | soapHeader + 81 | `` + 82 | `` + 83 | `$$SERVICEID$$` + 84 | `` + 85 | `` + 86 | `` 87 | 88 | var nextDeparture = 89 | soapHeader + 90 | `` + 91 | `` + 92 | getServiceStandardRequest + 93 | `` + 94 | `` + 95 | `` 96 | 97 | var nextDepartureWithDetails = 98 | soapHeader + 99 | `` + 100 | `` + 101 | getServiceStandardRequest + 102 | `` + 103 | `` + 104 | `` 105 | 106 | var nextArrival = 107 | soapHeader + 108 | `` + 109 | `` + 110 | getServiceStandardRequest + 111 | `` + 112 | `` + 113 | `` 114 | 115 | var fastestDeparture = 116 | soapHeader + 117 | `` + 118 | `` + 119 | getServiceStandardRequest + 120 | `` + 121 | `` + 122 | `` 123 | 124 | var fastestDepartureWithDetails = 125 | soapHeader + 126 | `` + 127 | `` + 128 | getServiceStandardRequest + 129 | `` + 130 | `` + 131 | `` 132 | 133 | module.exports.departureBoard = departureBoardTemplate 134 | module.exports.arrivalsBoard = arrivalsBoardTemplate 135 | module.exports.arrivalsBoardWithDetails = arrivalsBoardWithDetails 136 | module.exports.arrivalsDepartureBoard = arrivalsDepartureBoard 137 | module.exports.arrivalsDepartureBoardWithDetails = arrivalsDepartureBoardWithDetails 138 | module.exports.serviceDetails = serviceDetailsTemplate 139 | module.exports.nextDeparture = nextDeparture 140 | module.exports.nextDepartureWithDetails = nextDepartureWithDetails 141 | module.exports.departureBoardWithDetails = departureBoardWithDetails 142 | module.exports.fastestDeparture = fastestDeparture 143 | module.exports.fastestDepartureWithDetails = fastestDepartureWithDetails 144 | 145 | module.exports.nextArrival = nextArrival 146 | module.exports.header = soapHeader 147 | -------------------------------------------------------------------------------- /bin/darwin.js: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env node 2 | const program = require('commander') 3 | const util = require('util') 4 | var Rail = require('../index.js') 5 | 6 | var processOptions = function (parent) { 7 | var opts = {} 8 | if (parent.destination) { 9 | opts.destination = parent.destination 10 | } 11 | if (parent.rows) { 12 | opts.rows = parent.rows 13 | } 14 | if (parent.window) { 15 | opts.window = parent.window 16 | } 17 | return opts 18 | } 19 | 20 | function getClient () { 21 | var token = process.env.DARWIN_TOKEN 22 | if (program.token) { 23 | token = program.token 24 | } 25 | return new Rail(token) 26 | } 27 | 28 | var getPrinter = function (json) { 29 | return (err, result) => { 30 | if (err) { 31 | console.log(err.statusCode) 32 | console.log(err.body) 33 | console.log(err) 34 | } else { 35 | console.log(json ? JSON.stringify(result) : util.inspect(result, {depth: null})) 36 | } 37 | } 38 | } 39 | 40 | program 41 | .version('0.0.1') 42 | .description('Access the national-rail-darwin api. All 11 service calls from the Darwin public API are implemented here.') 43 | .option('-d, --destination ', 'Filter on a destination station. Must be a CRS code.') 44 | .option('-r, --rows ', 'Maximum number of rows to return. Defaults to 15.') 45 | .option('-w, --window ', 'Maxmimum number of minutes to search within. Defaults to 120.') 46 | .option('-t, --token ', 'Specify Darwin API token to use. Defaults to env.DARWIN_TOKEN') 47 | .option('-j, --json', 'Return raw JSON') 48 | 49 | program 50 | .command('arr-board ') 51 | .alias('ab') 52 | .description('Print Arrival Board') 53 | .action((station, options) => { 54 | getClient().getArrivalsBoard(station, processOptions(options.parent), getPrinter(options.parent.json)) 55 | }) 56 | 57 | program 58 | .command('arr-board-details ') 59 | .alias('ab-details') 60 | .description('Print Arrival Board with Details') 61 | .action((station, options) => { 62 | getClient().getArrivalsBoardWithDetails(station, processOptions(options.parent), getPrinter(options.parent.json)) 63 | }) 64 | 65 | program 66 | .command('arr-dep-board ') 67 | .alias('ad') 68 | .description('Print Arrival Departure Board') 69 | .action((station, options) => { 70 | getClient().getArrivalsDepartureBoard(station, processOptions(options.parent), getPrinter(options.parent.json)) 71 | }) 72 | 73 | program 74 | .command('arr-dep-board-details ') 75 | .alias('ad-details') 76 | .description('Print Arrival Departure Board with Details') 77 | .action((station, options) => { 78 | getClient().getArrivalsDepartureBoardWithDetails(station, processOptions(options.parent), getPrinter(options.parent.json)) 79 | }) 80 | 81 | program 82 | .command('departureBoard ') 83 | .alias('db') 84 | .description('Print Departure Board') 85 | .action((station, options) => { 86 | getClient().getDepartureBoard(station, processOptions(options.parent), getPrinter(options.parent.json)) 87 | }) 88 | 89 | program 90 | .command('departureBoardWithDetails ') 91 | .alias('db-details') 92 | .description('Print Departure Board with Details') 93 | .action((station, options) => { 94 | getClient().getDepartureBoardWithDetails(station, processOptions(options.parent), getPrinter(options.parent.json)) 95 | }) 96 | 97 | program 98 | .command('fastest-dep ') 99 | .alias('fd') 100 | .description('Print Fastest Departure') 101 | .action((station, destination, options) => { 102 | getClient().getFastestDeparture(station, destination, processOptions(options.parent), getPrinter(options.parent.json)) 103 | }) 104 | 105 | program 106 | .command('fastest-dep-details ') 107 | .alias('fd-details') 108 | .description('Print Fastest Departure with Details') 109 | .action((station, destination, options) => { 110 | getClient().getFastestDepartureWithDetails(station, destination, processOptions(options.parent), getPrinter(options.parent.json)) 111 | }) 112 | 113 | program 114 | .command('next-arrival ') 115 | .alias('na') 116 | .description('Print Next Arrival') 117 | .action((station, destination, options) => { 118 | getClient().getArrival(station, destination, processOptions(options.parent), getPrinter(options.parent.json)) 119 | }) 120 | 121 | program 122 | .command('next-departure ') 123 | .alias('nd') 124 | .description('Print Next Departure') 125 | .action((station, destination, options) => { 126 | getClient().getNextDeparture(station, destination, processOptions(options.parent), getPrinter(options.parent.json)) 127 | }) 128 | 129 | program 130 | .command('next-departure-details ') 131 | .alias('nd-details') 132 | .description('Print Next Departure with Details') 133 | .action((station, destination, options) => { 134 | getClient().getNextDepartureWithDetails(station, destination, processOptions(options.parent), getPrinter(options.parent.json)) 135 | }) 136 | 137 | program 138 | .command('service-details ') 139 | .alias('sd') 140 | .description('Print Service Details') 141 | .action((serviceId) => { 142 | getClient().getServiceDetails(serviceId, getPrinter(options.parent.json)) 143 | }) 144 | 145 | program.parse(process.argv) 146 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var request = require('request') 2 | var Bluebird = require('bluebird') 3 | 4 | var requestBuilder = require('./requestBuilder.js') 5 | var parser = require('./parsers.js') 6 | 7 | var baseUrl = 'https://lite.realtime.nationalrail.co.uk/OpenLDBWS/ldb11.asmx' 8 | 9 | var Darwin = function (apiKey, options) { 10 | this.key = apiKey || process.env.DARWIN_TOKEN 11 | if (options && options.baseUrl) { 12 | baseUrl = options.baseUrl 13 | } 14 | } 15 | 16 | Darwin.prototype.getBaseUrl = function () { 17 | return baseUrl 18 | } 19 | 20 | Darwin.prototype.thenablePOST = function (xml) { 21 | var xmlWithToken = xml.replace('$$TOKEN$$', this.key) 22 | return new Bluebird(function (resolve, reject) { 23 | request.post({ 24 | url: baseUrl, 25 | headers: { 26 | 'content-type': 'text/xml' 27 | }, 28 | body: xmlWithToken 29 | }, function (err, response, body) { 30 | if (err) { 31 | console.log(err) 32 | reject(err) 33 | } else if (response.statusCode > 300) { 34 | reject(response) 35 | } else { 36 | resolve(body) 37 | } 38 | }) 39 | }) 40 | } 41 | 42 | function thenableGET (url) { 43 | return new Bluebird(function (resolve, reject) { 44 | request.get({ 45 | url: url 46 | }, function (err, response, body) { 47 | if (err) { 48 | reject(err) 49 | } else if (response.statusCode > 300) { 50 | reject(response) 51 | } else { 52 | resolve(body) 53 | } 54 | }) 55 | }) 56 | } 57 | 58 | Darwin.prototype.getDepartureBoard = function (station, options, callback) { 59 | var requestXML = requestBuilder.getDepartureBoardRequest(station, options) 60 | this.thenablePOST(requestXML).then(function (result) { 61 | callback(null, parser.parseDepartureBoardResponse(result)) 62 | }).catch(function (err) { 63 | callback(err, null) 64 | }) 65 | } 66 | 67 | Darwin.prototype.getDepartureBoardWithDetails = function (station, options, callback) { 68 | var requestXML = requestBuilder.getDepartureBoardWithDetails(station, options) 69 | this.thenablePOST(requestXML).then(function (result) { 70 | callback(null, parser.parseDepartureBoardWithDetailsResponse(result)) 71 | }).catch(function (err) { 72 | callback(err, null) 73 | }) 74 | } 75 | 76 | Darwin.prototype.getArrivalsBoard = function (station, options, callback) { 77 | var requestXML = requestBuilder.getArrivalsBoard(station, options) 78 | this.thenablePOST(requestXML).then(function (result) { 79 | callback(null, parser.parseArrivalsBoardResponse(result)) 80 | }).catch(function (err) { 81 | callback(err, null) 82 | }) 83 | } 84 | 85 | Darwin.prototype.getArrivalsBoardWithDetails = function (station, options, callback) { 86 | var requestXML = requestBuilder.getArrivalsBoardWithDetails(station, options) 87 | this.thenablePOST(requestXML).then(function (result) { 88 | callback(null, parser.parseArrivalsBoardWithDetails(result)) 89 | }).catch(function (err) { 90 | callback(err, null) 91 | }) 92 | } 93 | 94 | Darwin.prototype.getArrivalsDepartureBoard = function (station, options, callback) { 95 | var requestXML = requestBuilder.getArrivalsDepartureBoard(station, options) 96 | this.thenablePOST(requestXML).then(function (result) { 97 | callback(null, parser.parseArrivalsDepartureBoard(result)) 98 | }).catch(function (err) { 99 | callback(err, null) 100 | }) 101 | } 102 | 103 | Darwin.prototype.getArrivalsDepartureBoardWithDetails = function (station, options, callback) { 104 | var requestXML = requestBuilder.getArrivalsDepartureBoardWithDetails(station, options) 105 | this.thenablePOST(requestXML).then(function (result) { 106 | callback(null, parser.parseArrivalsDepartureBoardWithDetails(result)) 107 | }).catch(function (err) { 108 | callback(err, null) 109 | }) 110 | } 111 | 112 | Darwin.prototype.getServiceDetails = function (serviceId, callback) { 113 | var requestXML = requestBuilder.getServiceDetails(serviceId) 114 | this.thenablePOST(requestXML).then(function (result) { 115 | callback(null, parser.parseServiceDetails(result)) 116 | }).catch(function (err) { 117 | callback(err, null) 118 | }) 119 | } 120 | 121 | Darwin.prototype.getNextDeparture = function (station, destination, options, callback) { 122 | var requestXML = requestBuilder.getNextDeparture(station, destination, options) 123 | this.thenablePOST(requestXML).then(function (result) { 124 | callback(null, parser.parseNextDepartureResponse(result)) 125 | }).catch(function (err) { 126 | callback(err, null) 127 | }) 128 | } 129 | 130 | Darwin.prototype.getNextDepartureWithDetails = function (station, destination, options, callback) { 131 | var requestXML = requestBuilder.getNextDepartureWithDetails(station, destination, options) 132 | this.thenablePOST(requestXML).then(function (result) { 133 | callback(null, parser.parseNextDepartureWithDetailsResponse(result)) 134 | }).catch(function (err) { 135 | callback(err, null) 136 | }) 137 | } 138 | 139 | Darwin.prototype.getArrival = function (station, destination, options, callback) { 140 | var requestXML = requestBuilder.getArrival(station, destination, options) 141 | this.thenablePOST(requestXML).then(function (result) { 142 | callback(null, parser.parseArrivalsBoardResponse(result)) 143 | }).catch(function (err) { 144 | callback(err, null) 145 | }) 146 | } 147 | 148 | Darwin.prototype.getFastestDeparture = function (station, destination, options, callback) { 149 | var requestXML = requestBuilder.getFastestDeparture(station, destination, options) 150 | this.thenablePOST(requestXML).then(function (result) { 151 | callback(null, parser.parseFastestDeparture(result)) 152 | }).catch(function (err) { 153 | callback(err, null) 154 | }) 155 | } 156 | 157 | Darwin.prototype.getFastestDepartureWithDetails = function (station, destination, options, callback) { 158 | var requestXML = requestBuilder.getFastestDepartureWithDetails(station, destination, options) 159 | this.thenablePOST(requestXML).then(function (result) { 160 | callback(null, parser.parseFastestDeparturesWithDetail(result)) 161 | }).catch(function (err) { 162 | callback(err, null) 163 | }) 164 | } 165 | 166 | Darwin.prototype.getStationDetails = function (stationName, callback) { 167 | var url = 'http://ojp.nationalrail.co.uk/find/stationsDLRLU/' + encodeURIComponent(stationName) 168 | thenableGET(url).then(function (body) { 169 | var results = JSON.parse(body) 170 | var output = results.map(function (result) { 171 | return { 172 | 'code': result[0], 173 | 'name': result[1], 174 | 'longitude': result[8], 175 | 'latitude': result[7], 176 | 'postcode': result[9], 177 | 'operator': result[10] 178 | } 179 | }) 180 | callback(null, output) 181 | }).catch(function (err) { 182 | callback(err, null) 183 | }) 184 | } 185 | 186 | module.exports = Darwin 187 | -------------------------------------------------------------------------------- /exampleResponses/nextDepartureWithDetails.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 2018-01-11T18:34:24.0265665+00:00 6 | Isleworth 7 | ISL 8 | true 9 | 10 | 11 | 12 | 18:34 13 | On time 14 | 18:34 15 | On time 16 | 1 17 | South Western Railway 18 | SW 19 | train 20 | 10 21 | wfmF+NYLrCpeoJVHBxSxrQ== 22 | SW182100 23 | 24 | 25 | London Waterloo 26 | WAT 27 | 28 | 29 | 30 | 31 | London Waterloo 32 | WAT 33 | via Brentford 34 | 35 | 36 | 37 | 38 | 39 | Syon Lane 40 | SYL 41 | 18:36 42 | On time 43 | 10 44 | 45 | 46 | Brentford 47 | BFD 48 | 18:38 49 | On time 50 | 10 51 | 52 | 53 | Kew Bridge 54 | KWB 55 | 18:41 56 | On time 57 | 10 58 | 59 | 60 | Chiswick 61 | CHK 62 | 18:44 63 | On time 64 | 10 65 | 66 | 67 | Barnes Bridge 68 | BNI 69 | 18:46 70 | On time 71 | 10 72 | 73 | 74 | Barnes 75 | BNS 76 | 18:49 77 | On time 78 | 10 79 | 80 | 81 | Putney 82 | PUT 83 | 18:52 84 | On time 85 | 10 86 | 87 | 88 | Wandsworth Town 89 | WNT 90 | 18:55 91 | On time 92 | 10 93 | 94 | 95 | Clapham Junction 96 | CLJ 97 | 18:58 98 | On time 99 | 10 100 | 101 | 102 | Queenstown Road Battersea 103 | QRB 104 | 19:01 105 | On time 106 | 10 107 | 108 | 109 | Vauxhall 110 | VXH 111 | 19:04 112 | On time 113 | 10 114 | 115 | 116 | London Waterloo 117 | WAT 118 | 19:11 119 | On time 120 | 10 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | -------------------------------------------------------------------------------- /exampleResponses/fastestDeparturesWithDetails.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 2018-01-11T18:33:24.6837861+00:00 6 | Isleworth 7 | ISL 8 | true 9 | 10 | 11 | 12 | 18:34 13 | On time 14 | 18:34 15 | On time 16 | 1 17 | South Western Railway 18 | SW 19 | train 20 | 10 21 | wfmF+NYLrCpeoJVHBxSxrQ== 22 | SW182100 23 | 24 | 25 | London Waterloo 26 | WAT 27 | 28 | 29 | 30 | 31 | London Waterloo 32 | WAT 33 | via Brentford 34 | 35 | 36 | 37 | 38 | 39 | Syon Lane 40 | SYL 41 | 18:36 42 | On time 43 | 10 44 | 45 | 46 | Brentford 47 | BFD 48 | 18:38 49 | On time 50 | 10 51 | 52 | 53 | Kew Bridge 54 | KWB 55 | 18:41 56 | On time 57 | 10 58 | 59 | 60 | Chiswick 61 | CHK 62 | 18:44 63 | On time 64 | 10 65 | 66 | 67 | Barnes Bridge 68 | BNI 69 | 18:46 70 | On time 71 | 10 72 | 73 | 74 | Barnes 75 | BNS 76 | 18:49 77 | On time 78 | 10 79 | 80 | 81 | Putney 82 | PUT 83 | 18:52 84 | On time 85 | 10 86 | 87 | 88 | Wandsworth Town 89 | WNT 90 | 18:55 91 | On time 92 | 10 93 | 94 | 95 | Clapham Junction 96 | CLJ 97 | 18:58 98 | On time 99 | 10 100 | 101 | 102 | Queenstown Road Battersea 103 | QRB 104 | 19:01 105 | On time 106 | 10 107 | 108 | 109 | Vauxhall 110 | VXH 111 | 19:04 112 | On time 113 | 10 114 | 115 | 116 | London Waterloo 117 | WAT 118 | 19:11 119 | On time 120 | 10 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # national-rail-darwin 2 | 3 | [![Standard - JavaScript Style Guide](https://img.shields.io/badge/code%20style-standard-brightgreen.svg)](http://standardjs.com/) 4 | [![Test Status](https://travis-ci.org/mattsalt/national-rail-darwin.svg?branch=master)](https://travis-ci.org/mattsalt/national-rail-darwin) 5 | 6 | ### Introduction 7 | 8 | `national-rail-darwin` aims to give you json object representations of the SOAP responses from National Rail's Darwin api. Details of the api can be found [here](http://lite.realtime.nationalrail.co.uk/openldbws/). You will need to get an API token to access Darwin, this requires registration. You can signup at [http://realtime.nationalrail.co.uk/OpenLDBWSRegistration/](http://realtime.nationalrail.co.uk/OpenLDBWSRegistration/). 9 | 10 | Currently only CRS codes are supported, a future update will allow full station names to be used. You can find a complete list of CRS codes on the [ National Rail website](http://www.nationalrail.co.uk/stations_destinations/48541.aspx). 11 | 12 | ### Installation 13 | 14 | ``` 15 | npm install national-rail-darwin 16 | ``` 17 | 18 | ### Usage 19 | 20 | All 11 requests exposed by the Darwin api are available in `national-rail-darwin` 21 | - getDepartureBoard(crsCode, options, callback) 22 | - getArrivalsBoard(crsCode, options, callback) 23 | - getArrivalsBoardWithDetails(crsCode, options, callback) 24 | - getArrivalsDepartureBoard(crsCode, options, callback) 25 | - getArrivalsDepartureBoardWithDetails(crsCode, options, callback) 26 | - getServiceDetails(serviceId, callback) 27 | - getNextDeparture(crsCode, destinationCrsCode, options, callback) 28 | - getNextDepartureWithDetails(crsCode, destinationCrsCode, options, callback) 29 | - getDepartureBoardWithDetails(crsCode, options, callback) 30 | - getFastestDeparture(crsCode, destinationCrsCode, options, callback) 31 | - getFastestDepartureWithDetails(crsCode, destinationCrsCode, options, callback) 32 | 33 | Additional functions 34 | - getStationDetails(Station, callback) 35 | 36 | 37 | Your api token can either be provided when the client is created or picked up from the environment variable `DARWIN_TOKEN`. 38 | 39 | ``` 40 | var Rail = require('national-rail-darwin') 41 | var rail = new Rail() // or -> new Rail(DARWIN_TOKEN) 42 | ``` 43 | 44 | ### Options 45 | 46 | Some functions take an options object. See the specific method definitions for details of these. 47 | 48 | ### Methods 49 | 50 | All methods return arrays of basic service objects of the form: 51 | ``` 52 | { 53 | sta: '23:57', 54 | eta: 'On time', 55 | std: '23:57', 56 | etd: 'On time', 57 | platform: '2', 58 | delayReason: null, 59 | origin: { 60 | name:, 61 | crs: 62 | }, 63 | destination: { 64 | name:, 65 | crs: 66 | }, 67 | length: '5', 68 | serviceId: 'xxxxxxxxxxxxxxxx+xx/xxx==' 69 | } 70 | ``` 71 | 72 | #### getDepartureBoard 73 | ```javascript 74 | rail.getDepartureBoard('WAT', {}, function(err,result){ 75 | //do stuff 76 | }) 77 | ``` 78 | 79 | Gets all public departures for the supplied station within 2 hours. 80 | Options: 81 | 'destination': Only show trains that call at the supplied station. 82 | 'rows': Maximum number of services to retrieve (1 - 149). 83 | 84 | #### getArrivalsBoard 85 | 86 | ```javascript 87 | rail.getArrivalsBoard('PUT', {}, function(err, result){ 88 | //do stuff 89 | }) 90 | ``` 91 | 92 | Similar to getDepartureBoard but shows arrivals within the next two hours. 93 | Options: 94 | 'destination': Only show trains that have called at the supplied station. 95 | 'rows': Maximum number of services to retrieve. 96 | 97 | #### getArrivalsBoardWithDetails 98 | 99 | ```javascript 100 | rail.getArrivalsBoardWithDetails('PUT', {}, function(err, result){ 101 | //do stuff 102 | }) 103 | ``` 104 | Adds service details including previous calling points to the getArrivalsBoardResult. 105 | Options: 106 | 'destination': Only show trains that have called at the supplied station. 107 | 'rows': Maximum number of services to retrieve. 108 | 109 | #### getArrivalsDepartureBoard 110 | 111 | ```javascript 112 | rail.getArrivalsDepartureBoard('PUT', {}, function(err, result){ 113 | //do stuff 114 | }) 115 | ``` 116 | Returns all public arrivals and departures for the supplied CRS code within 2 hours. 117 | Options: 118 | 'destination': Only show trains that have called at the supplied station. 119 | 'rows': Maximum number of services to retrieve. 120 | 121 | #### getArrivalsDepartureBoardWithDetails 122 | 123 | ```javascript 124 | rail.getArrivalsDepartureBoardWithDetails('PUT', {}, function(err, result){ 125 | //do stuff 126 | }) 127 | ``` 128 | Returns array of train services with both previous and subsequent calling points included for each service. 129 | Options: 130 | 'destination': Only show trains that have called at the supplied station. 131 | 'rows': Maximum number of services to retrieve. 132 | 133 | #### getServiceDetails 134 | ```javascript 135 | rail.getServiceDetails('SERVICE ID', function(err, result){ 136 | //do stuff 137 | }) 138 | ``` 139 | 140 | Gets detailed information about a particular service relative to the station that generated the serviceId. ServiceId is returned from other calls such as getDepartureBoard or getNextDeparture. The object returns includes all calling points of the service requested. The data is only available while the particular service is showing on the station departure board. This is normally for up to two minutes after the service is expected to depart. 141 | 142 | #### getNextDeparture 143 | ```javascript 144 | rail.getNextDeparture(crsCode, destinationCrsCode, {}, function(err, result){ 145 | //do stuff 146 | }) 147 | ``` 148 | Returns the next train leaving from supplied station calling at the destination CRS Code. 149 | 150 | #### getNextDepartureWithDetails 151 | ```javascript 152 | rail.getNextDepartureWithDetails(crsCode, destinationCrsCode, {}, function(err, result){ 153 | //do stuff 154 | }) 155 | ``` 156 | Returns the next train leaving from supplied station calling at the destination CRS Code within two hours including subsequent calling points. 157 | 158 | #### getDepartureBoardWithDetails 159 | ```javascript 160 | rail.getDepartureBoardWithDetails('WAT', {}, function(err,result){ 161 | //do stuff 162 | }) 163 | ``` 164 | Adds a list of future calling points to the standard departure board response. 165 | 'destination': Only show trains that call at the supplied station. 166 | 'rows': Maximum number of services to retrieve. 167 | 168 | #### getFastestDeparture 169 | ```javascript 170 | rail.getFastestDeparture('from', 'destination crs', {}, function(err,result){ 171 | //do stuff 172 | }) 173 | ``` 174 | Returns the service with the earliest arrival time at the destination station leaving from the supplied station. 175 | 176 | #### getFastestDepartureWithDetails 177 | ```javascript 178 | rail.getFastestDepartureWithDetails('from', 'destination crs', {}, function(err,result){ 179 | //do stuff 180 | }) 181 | ``` 182 | Same response as getFastestDeparture but includes service details such as previous and subsequent calling points. 183 | 184 | #### getStationDetails 185 | ```javascript 186 | rail.getStationDetails('Leeds', function(err,result){ 187 | //do stuff 188 | }) 189 | ``` 190 | Look up station details including CRSCode from the full station name 191 | 192 | 193 | ### Command Line 194 | 195 | national-rail-darwin now provides a command line tool, darwin, for querying the Darwin API. All Darwin requests are accessible via this tool. Install globally and run 196 | ``` 197 | darwin --help 198 | ``` 199 | for usage details. 200 | 201 | To authenticate using the command line you can either specify your API token using the `--token` flag or set `DARWIN_TOKEN` as an environment variable. 202 | 203 | You can print the result as raw JSON by using the `--json` option if you want 204 | to manipulate the output with something like [`jq`](https://stedolan.github.io/jq/). 205 | 206 | For example: 207 | ``` 208 | darwin --json -t XXX arr-board ANZ | jq '.trainServices[]' 209 | ``` 210 | -------------------------------------------------------------------------------- /exampleResponses/arrivalBoardWithDets.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 2018-01-11T18:27:35.8553243+00:00 6 | Isleworth 7 | ISL 8 | Putney 9 | PUT 10 | true 11 | 12 | 13 | 18:34 14 | On time 15 | 1 16 | South Western Railway 17 | SW 18 | train 19 | 10 20 | wfmF+NYLrCpeoJVHBxSxrQ== 21 | SW182100 22 | 23 | 24 | London Waterloo 25 | WAT 26 | 27 | 28 | 29 | 30 | London Waterloo 31 | WAT 32 | via Brentford 33 | 34 | 35 | 36 | 37 | 38 | London Waterloo 39 | WAT 40 | 17:45 41 | On time 42 | 10 43 | 44 | 45 | Vauxhall 46 | VXH 47 | 17:50 48 | On time 49 | 10 50 | 51 | 52 | Queenstown Road Battersea 53 | QRB 54 | 17:53 55 | On time 56 | 10 57 | 58 | 59 | Clapham Junction 60 | CLJ 61 | 17:56 62 | On time 63 | 10 64 | 65 | 66 | Wandsworth Town 67 | WNT 68 | 17:58 69 | On time 70 | 10 71 | 72 | 73 | Putney 74 | PUT 75 | 18:01 76 | On time 77 | 10 78 | 79 | 80 | Barnes 81 | BNS 82 | 18:05 83 | On time 84 | 10 85 | 86 | 87 | Mortlake 88 | MTL 89 | 18:07 90 | On time 91 | 10 92 | 93 | 94 | North Sheen 95 | NSH 96 | 18:09 97 | On time 98 | 10 99 | 100 | 101 | Richmond 102 | RMD 103 | 18:12 104 | On time 105 | 10 106 | 107 | 108 | St Margarets (London) 109 | SMG 110 | 18:14 111 | On time 112 | 10 113 | 114 | 115 | Twickenham 116 | TWI 117 | 18:17 118 | On time 119 | 10 120 | 121 | 122 | Whitton 123 | WTN 124 | 18:20 125 | On time 126 | 10 127 | 128 | 129 | Hounslow 130 | HOU 131 | 18:31 132 | On time 133 | 10 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | -------------------------------------------------------------------------------- /exampleResponses/serviceDetails.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 2018-01-11T18:34:53.3079542+00:00 6 | train 7 | Isleworth 8 | ISL 9 | South Western Railway 10 | SW 11 | SW182100 12 | 10 13 | 1 14 | 18:34 15 | On time 16 | 18:34 17 | On time 18 | 19 | 20 | 21 | London Waterloo 22 | WAT 23 | 17:45 24 | On time 25 | 10 26 | 27 | 28 | Vauxhall 29 | VXH 30 | 17:50 31 | On time 32 | 10 33 | 34 | 35 | Queenstown Road Battersea 36 | QRB 37 | 17:53 38 | On time 39 | 10 40 | 41 | 42 | Clapham Junction 43 | CLJ 44 | 17:56 45 | On time 46 | 10 47 | 48 | 49 | Wandsworth Town 50 | WNT 51 | 17:58 52 | On time 53 | 10 54 | 55 | 56 | Putney 57 | PUT 58 | 18:01 59 | On time 60 | 10 61 | 62 | 63 | Barnes 64 | BNS 65 | 18:05 66 | On time 67 | 10 68 | 69 | 70 | Mortlake 71 | MTL 72 | 18:07 73 | On time 74 | 10 75 | 76 | 77 | North Sheen 78 | NSH 79 | 18:09 80 | On time 81 | 10 82 | 83 | 84 | Richmond 85 | RMD 86 | 18:12 87 | On time 88 | 10 89 | 90 | 91 | St Margarets (London) 92 | SMG 93 | 18:14 94 | On time 95 | 10 96 | 97 | 98 | Twickenham 99 | TWI 100 | 18:17 101 | On time 102 | 10 103 | 104 | 105 | Whitton 106 | WTN 107 | 18:20 108 | On time 109 | 10 110 | 111 | 112 | Hounslow 113 | HOU 114 | 18:31 115 | On time 116 | 10 117 | 118 | 119 | 120 | 121 | 122 | 123 | Syon Lane 124 | SYL 125 | 18:36 126 | On time 127 | 10 128 | 129 | 130 | Brentford 131 | BFD 132 | 18:38 133 | On time 134 | 10 135 | 136 | 137 | Kew Bridge 138 | KWB 139 | 18:41 140 | On time 141 | 10 142 | 143 | 144 | Chiswick 145 | CHK 146 | 18:44 147 | On time 148 | 10 149 | 150 | 151 | Barnes Bridge 152 | BNI 153 | 18:46 154 | On time 155 | 10 156 | 157 | 158 | Barnes 159 | BNS 160 | 18:49 161 | On time 162 | 10 163 | 164 | 165 | Putney 166 | PUT 167 | 18:52 168 | On time 169 | 10 170 | 171 | 172 | Wandsworth Town 173 | WNT 174 | 18:55 175 | On time 176 | 10 177 | 178 | 179 | Clapham Junction 180 | CLJ 181 | 18:58 182 | On time 183 | 10 184 | 185 | 186 | Queenstown Road Battersea 187 | QRB 188 | 19:01 189 | On time 190 | 10 191 | 192 | 193 | Vauxhall 194 | VXH 195 | 19:04 196 | On time 197 | 10 198 | 199 | 200 | London Waterloo 201 | WAT 202 | 19:11 203 | On time 204 | 10 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | -------------------------------------------------------------------------------- /parsers.js: -------------------------------------------------------------------------------- 1 | var xmldoc = require('xmldoc') 2 | 3 | function parseArrivalsBoardResponse (soapResponse) { 4 | var board = getTrainServicesBoard(soapResponse, 'GetArrivalBoardResponse') 5 | var trains = [] 6 | 7 | try { 8 | board.eachChild(function (service) { 9 | trains.push(parseStandardService(service)) 10 | }) 11 | } catch (e) { } 12 | 13 | return { 'trainServices': trains } 14 | } 15 | 16 | function parseArrivalsBoardWithDetails (soapResponse) { 17 | var board = getTrainServicesBoard(soapResponse, 'GetArrBoardWithDetailsResponse') 18 | var trains = [] 19 | 20 | try { 21 | board.eachChild(function (service) { 22 | var train = parseStandardService(service) 23 | service.eachChild(function (element) { 24 | switch (element.name) { 25 | case 'lt7:previousCallingPoints': 26 | var previousCallingPoints = element.childNamed('lt7:callingPointList') 27 | train.previousCallingPoints = parseCallingPointList(previousCallingPoints) 28 | break 29 | } 30 | }) 31 | trains.push(train) 32 | }) 33 | } catch (e) { } 34 | 35 | return { 'trainServices': trains } 36 | } 37 | 38 | function parseArrivalsDepartureBoard (soapResponse) { 39 | var board = getTrainServicesBoard(soapResponse, 'GetArrivalDepartureBoardResponse') 40 | var trains = [] 41 | 42 | try { 43 | board.eachChild(function (service) { 44 | trains.push(parseStandardService(service)) 45 | }) 46 | } catch (e) { } 47 | 48 | return { 'trainServices': trains } 49 | } 50 | 51 | function parseArrivalsDepartureBoardWithDetails (soapResponse) { 52 | var board = getTrainServicesBoard(soapResponse, 'GetArrDepBoardWithDetailsResponse') 53 | var trains = [] 54 | 55 | try { 56 | board.eachChild(function (service) { 57 | var train = parseStandardService(service) 58 | service.eachChild(function (element) { 59 | switch (element.name) { 60 | case 'lt7:previousCallingPoints': 61 | var previousCallingPoints = element.childNamed('lt7:callingPointList') 62 | train.previousCallingPoints = parseCallingPointList(previousCallingPoints) 63 | break 64 | case 'lt7:subsequentCallingPoints': 65 | var subsequentCallingPoints = element.childNamed('lt7:callingPointList') 66 | train.subsequentCallingPoints = parseCallingPointList(subsequentCallingPoints) 67 | break 68 | } 69 | }) 70 | trains.push(train) 71 | }) 72 | } catch (e) { } 73 | 74 | return { 'trainServices': trains } 75 | } 76 | 77 | function parseServiceDetails (soapResponse) { 78 | var serviceXml = extractResponseObject(soapResponse, 'GetServiceDetailsResponse') 79 | .childNamed('GetServiceDetailsResult') 80 | var service = parseStandardService(serviceXml) 81 | 82 | serviceXml.eachChild(function (element) { 83 | switch (element.name) { 84 | case 'lt7:previousCallingPoints': 85 | var previousCallingPoints = element.childNamed('lt7:callingPointList') 86 | service.previousCallingPoints = parseCallingPointList(previousCallingPoints) 87 | break 88 | case 'lt7:subsequentCallingPoints': 89 | var subsequentCallingPoints = element.childNamed('lt7:callingPointList') 90 | service.subsequentCallingPoints = parseCallingPointList(subsequentCallingPoints) 91 | break 92 | } 93 | }) 94 | 95 | return { 'serviceDetails': service } 96 | } 97 | 98 | function parseDepartureBoardResponse (soapResponse) { 99 | var board = getTrainServicesBoard(soapResponse, 'GetDepartureBoardResponse') 100 | var trains = [] 101 | try { 102 | board.eachChild(function (service) { 103 | trains.push(parseStandardService(service)) 104 | }) 105 | } catch (e) { } 106 | 107 | return { 'trainServices': trains } 108 | } 109 | 110 | function parseDepartureBoardWithDetailsResponse (soapResponse) { 111 | var board = getTrainServicesBoard(soapResponse, 'GetDepBoardWithDetailsResponse') 112 | var trains = [] 113 | 114 | try { 115 | board.eachChild(function (service) { 116 | var train = parseStandardService(service) 117 | service.eachChild(function (element) { 118 | switch (element.name) { 119 | case 'lt7:subsequentCallingPoints': 120 | var subsequentCallingPoints = element.childNamed('lt7:callingPointList') 121 | train.subsequentCallingPoints = parseCallingPointList(subsequentCallingPoints) 122 | break 123 | } 124 | }) 125 | trains.push(train) 126 | }) 127 | } catch (e) { } 128 | 129 | return { 'trainServices': trains } 130 | } 131 | 132 | function parseNextDepartureResponse (response) { 133 | var board = getDepartureBoardDestination(response, 'GetNextDeparturesResponse') 134 | var trains = [] 135 | 136 | try { 137 | board.eachChild(function (service) { 138 | trains.push(parseStandardService(service)) 139 | }) 140 | } catch (e) { } 141 | 142 | return { 'trainServices': trains } 143 | } 144 | 145 | function parseNextDepartureWithDetailsResponse (response) { 146 | var board = getDepartureBoardDestination(response, 'GetNextDeparturesWithDetailsResponse') 147 | var trains = [] 148 | 149 | try { 150 | board.eachChild(function (service) { 151 | var train = parseStandardService(service) 152 | service.eachChild(function (element) { 153 | switch (element.name) { 154 | case 'lt7:subsequentCallingPoints': 155 | var subsequentCallingPoints = element.childNamed('lt7:callingPointList') 156 | train.subsequentCallingPoints = parseCallingPointList(subsequentCallingPoints) 157 | break 158 | } 159 | }) 160 | trains.push(train) 161 | }) 162 | } catch (e) { } 163 | 164 | return { 'trainServices': trains } 165 | } 166 | 167 | function parseFastestDeparture (response) { 168 | var board = getDepartureBoardDestination(response, 'GetFastestDeparturesResponse') 169 | var trains = [] 170 | 171 | try { 172 | board.eachChild(function (service) { 173 | trains.push(parseStandardService(service)) 174 | }) 175 | } catch (e) { } 176 | return { 'trainServices': trains } 177 | } 178 | 179 | function parseFastestDepartureWithDetails (response) { 180 | var board = getDepartureBoardDestination(response, 'GetFastestDeparturesWithDetailsResponse') 181 | var trains = [] 182 | try { 183 | board.eachChild(function (service) { 184 | var train = parseStandardService(service) 185 | service.eachChild(function (element) { 186 | switch (element.name) { 187 | case 'lt7:subsequentCallingPoints': 188 | var subsequentCallingPoints = element.childNamed('lt7:callingPointList') 189 | train.subsequentCallingPoints = parseCallingPointList(subsequentCallingPoints) 190 | break 191 | } 192 | }) 193 | trains.push(train) 194 | }) 195 | } catch (e) { } 196 | 197 | return { 'trainServices': trains } 198 | } 199 | 200 | function getTrainServicesBoard (response, responseType) { 201 | var board = extractResponseObject(response, responseType) 202 | .childNamed('GetStationBoardResult') 203 | .childNamed('lt7:trainServices') 204 | return board 205 | } 206 | 207 | function parseStandardService (service) { 208 | var train = {} 209 | service.eachChild(function (element) { 210 | switch (element.name) { 211 | case 'lt4:generatedAt': 212 | case 'lt7:generatedAt': 213 | service.generatedAt = element.val 214 | break 215 | case 'lt4:std': 216 | case 'lt7:std': 217 | train.std = element.val 218 | break 219 | case 'lt4:etd': 220 | case 'lt7:etd': 221 | train.etd = element.val 222 | break 223 | case 'lt4:sta': 224 | case 'lt7:sta': 225 | train.sta = element.val 226 | break 227 | case 'lt4:eta': 228 | case 'lt7:eta': 229 | train.eta = element.val 230 | break 231 | case 'lt4:platform': 232 | case 'lt7:platform': 233 | train.platform = element.val 234 | break 235 | case 'lt4:delayReason': 236 | case 'lt7:delayReason': 237 | train.delayReason = element.val 238 | break 239 | case 'lt4:serviceID': 240 | case 'lt7:serviceID': 241 | train.serviceId = element.val 242 | break 243 | case 'lt4:length': 244 | case 'lt7:length': 245 | train.length = element.val 246 | break 247 | case 'lt4:operator': 248 | case 'lt7:operator': 249 | train.operator = element.val 250 | break 251 | case 'lt4:operatorCode': 252 | case 'lt7:operatorCode': 253 | train.operatorCode = element.val 254 | break 255 | case 'lt5:rsid': 256 | case 'lt7:rsid': 257 | train.rsid = element.val 258 | break 259 | case 'lt5:origin': 260 | case 'lt7:origin': 261 | var origin = element.childNamed('lt4:location') 262 | train.origin = parseLocation(origin) 263 | break 264 | case 'lt5:destination': 265 | case 'lt7:destination': 266 | var destin = element.childNamed('lt4:location') 267 | train.destination = parseLocation(destin) 268 | break 269 | } 270 | }) 271 | return train 272 | } 273 | 274 | function parseCallingPointList (soapCallingPointList) { 275 | var callingPoints = [] 276 | soapCallingPointList.eachChild(function (child) { 277 | var callingPoint = {} 278 | child.eachChild(function (element) { 279 | switch (element.name) { 280 | case 'lt7:length': 281 | callingPoint.length = element.val 282 | break 283 | case 'lt7:crs': 284 | callingPoint.crs = element.val 285 | break 286 | case 'lt7:locationName': 287 | callingPoint.locationName = element.val 288 | break 289 | case 'lt7:st': 290 | callingPoint.st = element.val 291 | break 292 | case 'lt7:et': 293 | callingPoint.et = element.val 294 | break 295 | } 296 | }) 297 | callingPoints.push(callingPoint) 298 | }) 299 | return callingPoints 300 | } 301 | 302 | function extractResponseObject (soapMessage, response) { 303 | var parsed = new xmldoc.XmlDocument(soapMessage) 304 | return parsed.childNamed('soap:Body').childNamed(response) 305 | } 306 | 307 | function parseLocation (location) { 308 | return { 309 | name: location.childNamed('lt4:locationName').val, 310 | crs: location.childNamed('lt4:crs').val 311 | } 312 | } 313 | 314 | function getDepartureBoardDestination (response, responseType) { 315 | var board = extractResponseObject(response, responseType) 316 | .childNamed('DeparturesBoard') 317 | .childNamed('lt7:departures') 318 | .childNamed('lt7:destination') 319 | 320 | return board 321 | } 322 | 323 | module.exports.parseArrivalsBoardResponse = parseArrivalsBoardResponse 324 | module.exports.parseArrivalsBoardWithDetails = parseArrivalsBoardWithDetails 325 | module.exports.parseArrivalsDepartureBoard = parseArrivalsDepartureBoard 326 | module.exports.parseArrivalsDepartureBoardWithDetails = parseArrivalsDepartureBoardWithDetails 327 | 328 | module.exports.parseDepartureBoardResponse = parseDepartureBoardResponse 329 | module.exports.parseDepartureBoardWithDetailsResponse = parseDepartureBoardWithDetailsResponse 330 | 331 | module.exports.parseFastestDeparture = parseFastestDeparture 332 | module.exports.parseFastestDeparturesWithDetail = parseFastestDepartureWithDetails 333 | 334 | module.exports.parseNextDepartureResponse = parseNextDepartureResponse 335 | module.exports.parseNextDepartureWithDetailsResponse = parseNextDepartureWithDetailsResponse 336 | 337 | module.exports.parseServiceDetails = parseServiceDetails 338 | -------------------------------------------------------------------------------- /exampleResponses/arrivalsBoard.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 2018-01-11T18:31:09.4477859+00:00 6 | Isleworth 7 | ISL 8 | Putney 9 | PUT 10 | true 11 | 12 | 13 | 18:34 14 | On time 15 | 1 16 | South Western Railway 17 | SW 18 | train 19 | 10 20 | wfmF+NYLrCpeoJVHBxSxrQ== 21 | SW182100 22 | 23 | 24 | London Waterloo 25 | WAT 26 | 27 | 28 | 29 | 30 | London Waterloo 31 | WAT 32 | via Brentford 33 | 34 | 35 | 36 | 37 | 18:42 38 | On time 39 | 2 40 | South Western Railway 41 | SW 42 | train 43 | 10 44 | nh3WKFCFpkwRTwqpzEUc/A== 45 | SW211600 46 | 47 | 48 | London Waterloo 49 | WAT 50 | 51 | 52 | 53 | 54 | Mortlake 55 | MTL 56 | 57 | 58 | 59 | 60 | 18:49 61 | 18:51 62 | 1 63 | South Western Railway 64 | SW 65 | train 66 | 4 67 | KXIphaTeuVHS0/a4SWgoAg== 68 | SW188000 69 | 70 | 71 | Weybridge 72 | WYB 73 | 74 | 75 | 76 | 77 | London Waterloo 78 | WAT 79 | via Brentford 80 | 81 | 82 | 83 | 84 | 19:04 85 | On time 86 | 1 87 | South Western Railway 88 | SW 89 | train 90 | 8 91 | b5oGjwYNIrFOQCoeerXVsA== 92 | SW182200 93 | 94 | 95 | London Waterloo 96 | WAT 97 | 98 | 99 | 100 | 101 | London Waterloo 102 | WAT 103 | via Brentford 104 | 105 | 106 | 107 | 108 | 19:12 109 | On time 110 | 2 111 | South Western Railway 112 | SW 113 | train 114 | 10 115 | 5QgCfHmYd7JNvbV0YSyhVw== 116 | SW211700 117 | 118 | 119 | London Waterloo 120 | WAT 121 | 122 | 123 | 124 | 125 | Mortlake 126 | MTL 127 | 128 | 129 | 130 | 131 | 19:19 132 | On time 133 | 1 134 | South Western Railway 135 | SW 136 | train 137 | 4 138 | 2HTkZpwWG3h0h2h2gnYvPw== 139 | SW188200 140 | 141 | 142 | Weybridge 143 | WYB 144 | 145 | 146 | 147 | 148 | London Waterloo 149 | WAT 150 | via Brentford 151 | 152 | 153 | 154 | 155 | 19:34 156 | On time 157 | 1 158 | South Western Railway 159 | SW 160 | train 161 | 8 162 | +kJe9WIrueGPWUYhDzmRQg== 163 | SW182300 164 | 165 | 166 | London Waterloo 167 | WAT 168 | 169 | 170 | 171 | 172 | London Waterloo 173 | WAT 174 | via Brentford 175 | 176 | 177 | 178 | 179 | 19:42 180 | On time 181 | 2 182 | South Western Railway 183 | SW 184 | train 185 | 10 186 | cm080rz49AMkBqto0DmPCQ== 187 | SW211800 188 | 189 | 190 | London Waterloo 191 | WAT 192 | 193 | 194 | 195 | 196 | Mortlake 197 | MTL 198 | 199 | 200 | 201 | 202 | 19:49 203 | On time 204 | 1 205 | South Western Railway 206 | SW 207 | train 208 | 10 209 | 14nFdX55toGlajTRiRUE7g== 210 | SW188400 211 | 212 | 213 | Weybridge 214 | WYB 215 | 216 | 217 | 218 | 219 | London Waterloo 220 | WAT 221 | via Brentford 222 | 223 | 224 | 225 | 226 | 20:04 227 | On time 228 | 1 229 | South Western Railway 230 | SW 231 | train 232 | 10 233 | oUW4Wt3Vc+7BHVWXcrkWpQ== 234 | SW182400 235 | 236 | 237 | London Waterloo 238 | WAT 239 | 240 | 241 | 242 | 243 | London Waterloo 244 | WAT 245 | via Brentford 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | -------------------------------------------------------------------------------- /exampleResponses/departureBoard.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 2018-01-11T18:32:13.9061324+00:00 6 | Isleworth 7 | ISL 8 | Putney 9 | PUT 10 | true 11 | 12 | 13 | 18:34 14 | On time 15 | 1 16 | South Western Railway 17 | SW 18 | train 19 | 10 20 | wfmF+NYLrCpeoJVHBxSxrQ== 21 | SW182100 22 | 23 | 24 | London Waterloo 25 | WAT 26 | 27 | 28 | 29 | 30 | London Waterloo 31 | WAT 32 | via Brentford 33 | 34 | 35 | 36 | 37 | 18:42 38 | On time 39 | 2 40 | South Western Railway 41 | SW 42 | train 43 | 10 44 | nh3WKFCFpkwRTwqpzEUc/A== 45 | SW211600 46 | 47 | 48 | London Waterloo 49 | WAT 50 | 51 | 52 | 53 | 54 | Mortlake 55 | MTL 56 | 57 | 58 | 59 | 60 | 18:49 61 | 18:51 62 | 1 63 | South Western Railway 64 | SW 65 | train 66 | 4 67 | KXIphaTeuVHS0/a4SWgoAg== 68 | SW188000 69 | 70 | 71 | Weybridge 72 | WYB 73 | 74 | 75 | 76 | 77 | London Waterloo 78 | WAT 79 | via Brentford 80 | 81 | 82 | 83 | 84 | 19:04 85 | On time 86 | 1 87 | South Western Railway 88 | SW 89 | train 90 | 8 91 | b5oGjwYNIrFOQCoeerXVsA== 92 | SW182200 93 | 94 | 95 | London Waterloo 96 | WAT 97 | 98 | 99 | 100 | 101 | London Waterloo 102 | WAT 103 | via Brentford 104 | 105 | 106 | 107 | 108 | 19:12 109 | On time 110 | 2 111 | South Western Railway 112 | SW 113 | train 114 | 10 115 | 5QgCfHmYd7JNvbV0YSyhVw== 116 | SW211700 117 | 118 | 119 | London Waterloo 120 | WAT 121 | 122 | 123 | 124 | 125 | Mortlake 126 | MTL 127 | 128 | 129 | 130 | 131 | 19:19 132 | On time 133 | 1 134 | South Western Railway 135 | SW 136 | train 137 | 4 138 | 2HTkZpwWG3h0h2h2gnYvPw== 139 | SW188200 140 | 141 | 142 | Weybridge 143 | WYB 144 | 145 | 146 | 147 | 148 | London Waterloo 149 | WAT 150 | via Brentford 151 | 152 | 153 | 154 | 155 | 19:34 156 | On time 157 | 1 158 | South Western Railway 159 | SW 160 | train 161 | 8 162 | +kJe9WIrueGPWUYhDzmRQg== 163 | SW182300 164 | 165 | 166 | London Waterloo 167 | WAT 168 | 169 | 170 | 171 | 172 | London Waterloo 173 | WAT 174 | via Brentford 175 | 176 | 177 | 178 | 179 | 19:42 180 | On time 181 | 2 182 | South Western Railway 183 | SW 184 | train 185 | 10 186 | cm080rz49AMkBqto0DmPCQ== 187 | SW211800 188 | 189 | 190 | London Waterloo 191 | WAT 192 | 193 | 194 | 195 | 196 | Mortlake 197 | MTL 198 | 199 | 200 | 201 | 202 | 19:49 203 | On time 204 | 1 205 | South Western Railway 206 | SW 207 | train 208 | 10 209 | 14nFdX55toGlajTRiRUE7g== 210 | SW188400 211 | 212 | 213 | Weybridge 214 | WYB 215 | 216 | 217 | 218 | 219 | London Waterloo 220 | WAT 221 | via Brentford 222 | 223 | 224 | 225 | 226 | 20:04 227 | On time 228 | 1 229 | South Western Railway 230 | SW 231 | train 232 | 10 233 | oUW4Wt3Vc+7BHVWXcrkWpQ== 234 | SW182400 235 | 236 | 237 | London Waterloo 238 | WAT 239 | 240 | 241 | 242 | 243 | London Waterloo 244 | WAT 245 | via Brentford 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | -------------------------------------------------------------------------------- /exampleResponses/arrivalDepartureBoard.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 2018-01-11T18:31:44.5623443+00:00 6 | Isleworth 7 | ISL 8 | Putney 9 | PUT 10 | true 11 | 12 | 13 | 18:34 14 | On time 15 | 18:34 16 | On time 17 | 1 18 | South Western Railway 19 | SW 20 | train 21 | 10 22 | wfmF+NYLrCpeoJVHBxSxrQ== 23 | SW182100 24 | 25 | 26 | London Waterloo 27 | WAT 28 | 29 | 30 | 31 | 32 | London Waterloo 33 | WAT 34 | via Brentford 35 | 36 | 37 | 38 | 39 | 18:42 40 | On time 41 | 18:42 42 | On time 43 | 2 44 | South Western Railway 45 | SW 46 | train 47 | 10 48 | nh3WKFCFpkwRTwqpzEUc/A== 49 | SW211600 50 | 51 | 52 | London Waterloo 53 | WAT 54 | 55 | 56 | 57 | 58 | Mortlake 59 | MTL 60 | 61 | 62 | 63 | 64 | 18:49 65 | 18:51 66 | 18:49 67 | 18:51 68 | 1 69 | South Western Railway 70 | SW 71 | train 72 | 4 73 | KXIphaTeuVHS0/a4SWgoAg== 74 | SW188000 75 | 76 | 77 | Weybridge 78 | WYB 79 | 80 | 81 | 82 | 83 | London Waterloo 84 | WAT 85 | via Brentford 86 | 87 | 88 | 89 | 90 | 19:04 91 | On time 92 | 19:04 93 | On time 94 | 1 95 | South Western Railway 96 | SW 97 | train 98 | 8 99 | b5oGjwYNIrFOQCoeerXVsA== 100 | SW182200 101 | 102 | 103 | London Waterloo 104 | WAT 105 | 106 | 107 | 108 | 109 | London Waterloo 110 | WAT 111 | via Brentford 112 | 113 | 114 | 115 | 116 | 19:12 117 | On time 118 | 19:12 119 | On time 120 | 2 121 | South Western Railway 122 | SW 123 | train 124 | 10 125 | 5QgCfHmYd7JNvbV0YSyhVw== 126 | SW211700 127 | 128 | 129 | London Waterloo 130 | WAT 131 | 132 | 133 | 134 | 135 | Mortlake 136 | MTL 137 | 138 | 139 | 140 | 141 | 19:19 142 | On time 143 | 19:19 144 | On time 145 | 1 146 | South Western Railway 147 | SW 148 | train 149 | 4 150 | 2HTkZpwWG3h0h2h2gnYvPw== 151 | SW188200 152 | 153 | 154 | Weybridge 155 | WYB 156 | 157 | 158 | 159 | 160 | London Waterloo 161 | WAT 162 | via Brentford 163 | 164 | 165 | 166 | 167 | 19:34 168 | On time 169 | 19:34 170 | On time 171 | 1 172 | South Western Railway 173 | SW 174 | train 175 | 8 176 | +kJe9WIrueGPWUYhDzmRQg== 177 | SW182300 178 | 179 | 180 | London Waterloo 181 | WAT 182 | 183 | 184 | 185 | 186 | London Waterloo 187 | WAT 188 | via Brentford 189 | 190 | 191 | 192 | 193 | 19:42 194 | On time 195 | 19:42 196 | On time 197 | 2 198 | South Western Railway 199 | SW 200 | train 201 | 10 202 | cm080rz49AMkBqto0DmPCQ== 203 | SW211800 204 | 205 | 206 | London Waterloo 207 | WAT 208 | 209 | 210 | 211 | 212 | Mortlake 213 | MTL 214 | 215 | 216 | 217 | 218 | 19:49 219 | On time 220 | 19:49 221 | On time 222 | 1 223 | South Western Railway 224 | SW 225 | train 226 | 10 227 | 14nFdX55toGlajTRiRUE7g== 228 | SW188400 229 | 230 | 231 | Weybridge 232 | WYB 233 | 234 | 235 | 236 | 237 | London Waterloo 238 | WAT 239 | via Brentford 240 | 241 | 242 | 243 | 244 | 20:04 245 | On time 246 | 20:04 247 | On time 248 | 1 249 | South Western Railway 250 | SW 251 | train 252 | 10 253 | oUW4Wt3Vc+7BHVWXcrkWpQ== 254 | SW182400 255 | 256 | 257 | London Waterloo 258 | WAT 259 | 260 | 261 | 262 | 263 | London Waterloo 264 | WAT 265 | via Brentford 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | var assert = require('assert') 3 | var parsers = require('../parsers.js') 4 | var fs = require('fs') 5 | 6 | describe('Client options override correctly', function () { 7 | var Rail = require('../index.js') 8 | 9 | it('Original base url remains when not overriden', function () { 10 | const baseUrl = 'https://lite.realtime.nationalrail.co.uk/OpenLDBWS/ldb11.asmx' 11 | let client = new Rail('abc123') 12 | assert.equal(client.getBaseUrl(), baseUrl) 13 | }) 14 | 15 | it('Overrides the base url', function () { 16 | const newBaseUrl = 'http://somewhere' 17 | let client = new Rail('abc123', { baseUrl: newBaseUrl }) 18 | assert.equal(client.getBaseUrl(), newBaseUrl) 19 | }) 20 | }) 21 | 22 | /* arrivalBoardWithDets.xml */ 23 | describe('ArrivalBoardWithDetails parsed correctly', function () { 24 | var fileContent = fs.readFileSync('./exampleResponses/arrivalBoardWithDets.xml', 'UTF-8') 25 | var parsedResult = parsers.parseArrivalsBoardWithDetails(fileContent) 26 | 27 | it('5 train services returned', function () { 28 | assert.equal(1, parsedResult.trainServices.length) 29 | }) 30 | 31 | it('Service id of first service is resovled', function () { 32 | assert.equal('wfmF+NYLrCpeoJVHBxSxrQ==', parsedResult.trainServices[0].serviceId) 33 | }) 34 | 35 | it('STA of service is correct', function () { 36 | assert.equal('18:34', parsedResult.trainServices[0].sta) 37 | }) 38 | 39 | it('Operator of service is Southwestern trains', function () { 40 | assert.equal('South Western Railway', parsedResult.trainServices[0].operator) 41 | }) 42 | }) 43 | 44 | /* arrivalBoardWithDets_bus.xml */ 45 | describe('Arrival Departure Board with Details when no train services are available', function () { 46 | var fileContent = fs.readFileSync('./exampleResponses/arrivalBoardWithDets_bus.xml', 'UTF-8') 47 | var parsedResult = parsers.parseArrivalsBoardWithDetails(fileContent) 48 | 49 | it('does not throw an error', function () { 50 | assert.deepEqual([], parsedResult.trainServices) 51 | }) 52 | }) 53 | 54 | /* arrivalDepartureBoard.xml */ 55 | describe('Arrival Departure Board', function () { 56 | var fileContent = fs.readFileSync('./exampleResponses/arrivalDepartureBoard.xml', 'UTF-8') 57 | var parsedResult = parsers.parseArrivalsDepartureBoard(fileContent) 58 | 59 | it('10 Services returned', function () { 60 | assert.equal(10, parsedResult.trainServices.length) 61 | }) 62 | 63 | it('Service 1 on time', function () { 64 | assert.equal('On time', parsedResult.trainServices[0].eta) 65 | }) 66 | 67 | it('Service 1 platform ', function () { 68 | assert.equal(1, parsedResult.trainServices[0].platform) 69 | }) 70 | 71 | it('Service 1 length ', function () { 72 | assert.equal(10, parsedResult.trainServices[0].length) 73 | }) 74 | 75 | it('Service 1 destination ', function () { 76 | assert.equal('London Waterloo', parsedResult.trainServices[0].destination.name) 77 | }) 78 | }) 79 | 80 | /* arrivalDepartureBoardWithDetails.xml */ 81 | describe('Arrival Departure Board with Details parsing is correct', function () { 82 | var fileContent = fs.readFileSync('./exampleResponses/arrivalDepartureBoardWithDetails.xml', 'UTF-8') 83 | var parsedResult = parsers.parseArrivalsDepartureBoardWithDetails(fileContent) 84 | 85 | it('Arrival Departure Board sta = 21:34', function () { 86 | assert.equal('18:34', parsedResult.trainServices[0].sta) 87 | }) 88 | it('lArrival Departure Board eta = On time', function () { 89 | assert.equal('On time', parsedResult.trainServices[0].eta) 90 | }) 91 | it('Arrival Departure Board std = 18:34', function () { 92 | assert.equal('18:34', parsedResult.trainServices[0].std) 93 | }) 94 | it('Arrival Departure Board etd = On time', function () { 95 | assert.equal('On time', parsedResult.trainServices[0].etd) 96 | }) 97 | it('Arrival Departure Board platform = 1', function () { 98 | assert.equal('1', parsedResult.trainServices[0].platform) 99 | }) 100 | it('Arrival Departure Board operator = South Western Railway', function () { 101 | assert.equal('South Western Railway', parsedResult.trainServices[0].operator) 102 | }) 103 | it('Arrival Departure Board operatorCode = SW', function () { 104 | assert.equal('SW', parsedResult.trainServices[0].operatorCode) 105 | }) 106 | it('Arrival Departure Board length = 10', function () { 107 | assert.equal('10', parsedResult.trainServices[0].length) 108 | }) 109 | it('Arrival Departure BoardservicId = MrHNnCAHgnHGs5KtWtLkWw==', function () { 110 | assert.equal('wfmF+NYLrCpeoJVHBxSxrQ==', parsedResult.trainServices[0].serviceId) 111 | }) 112 | it('Arrival Departure Board origin = WAT and LondonWaterloo', function () { 113 | var origin = parsedResult.trainServices[0].origin 114 | assert.equal('London Waterloo', origin.name) 115 | assert.equal('WAT', origin.crs) 116 | }) 117 | it('Arrival Departure Board origin = WAT and LondonWaterloo', function () { 118 | var destination = parsedResult.trainServices[0].destination 119 | assert.equal('London Waterloo', destination.name) 120 | assert.equal('WAT', destination.crs) 121 | }) 122 | it('PreviousCallingPoints array is correct size 14', function () { 123 | var arr = parsedResult.trainServices[0].previousCallingPoints 124 | assert.equal(14, arr.length) 125 | }) 126 | it('Last previous calling point crs code = WAT', function () { 127 | var arr = parsedResult.trainServices[0].previousCallingPoints 128 | assert.equal('HOU', arr[13].crs) 129 | }) 130 | it('Last previous calling point locationName = London Waterloo', function () { 131 | var arr = parsedResult.trainServices[0].previousCallingPoints 132 | assert.equal('Hounslow', arr[13].locationName) 133 | }) 134 | it('Last previous calling point st = 09:45', function () { 135 | var arr = parsedResult.trainServices[0].previousCallingPoints 136 | assert.equal('18:31', arr[13].st) 137 | }) 138 | it('Last previous calling point et = On time', function () { 139 | var arr = parsedResult.trainServices[0].previousCallingPoints 140 | assert.equal('On time', arr[13].et) 141 | }) 142 | it('SubsequentCallingPoints array is correct size 14', function () { 143 | var arr = parsedResult.trainServices[0].subsequentCallingPoints 144 | assert.equal(12, arr.length) 145 | }) 146 | it('First subsequent calling point crs code = WAT', function () { 147 | var arr = parsedResult.trainServices[0].subsequentCallingPoints 148 | assert.equal('SYL', arr[0].crs) 149 | }) 150 | it('First subsequent calling point locationName = London Waterloo', function () { 151 | var arr = parsedResult.trainServices[0].subsequentCallingPoints 152 | assert.equal('Syon Lane', arr[0].locationName) 153 | }) 154 | it('First subsequent calling point st = 09:45', function () { 155 | var arr = parsedResult.trainServices[0].subsequentCallingPoints 156 | assert.equal('18:36', arr[0].st) 157 | }) 158 | it('First subsequent calling point et = On time', function () { 159 | var arr = parsedResult.trainServices[0].subsequentCallingPoints 160 | assert.equal('On time', arr[0].et) 161 | }) 162 | }) 163 | 164 | /* arrivalsBoard.xml */ 165 | describe('Arrival Departure Board', function () { 166 | var fileContent = fs.readFileSync('./exampleResponses/arrivalsBoard.xml', 'UTF-8') 167 | var parsedResult = parsers.parseArrivalsBoardResponse(fileContent) 168 | 169 | it('10 Services returned', function () { 170 | assert.equal(10, parsedResult.trainServices.length) 171 | }) 172 | 173 | it('Service 9 on time', function () { 174 | assert.equal('On time', parsedResult.trainServices[8].eta) 175 | }) 176 | 177 | it('Service 9 platform ', function () { 178 | assert.equal(1, parsedResult.trainServices[8].platform) 179 | }) 180 | 181 | it('Service 9 length ', function () { 182 | assert.equal(10, parsedResult.trainServices[8].length) 183 | }) 184 | 185 | it('Service 1 origin ', function () { 186 | assert.equal('Weybridge', parsedResult.trainServices[8].origin.name) 187 | }) 188 | }) 189 | 190 | /* departureBoard.xml */ 191 | describe('Departure Board', function () { 192 | var fileContent = fs.readFileSync('./exampleResponses/departureBoard.xml', 'UTF-8') 193 | var parsedResult = parsers.parseDepartureBoardResponse(fileContent) 194 | 195 | it('15 Services returned', function () { 196 | assert.equal(10, parsedResult.trainServices.length) 197 | }) 198 | 199 | it('Service 10 etd', function () { 200 | assert.equal('On time', parsedResult.trainServices[9].etd) 201 | }) 202 | it('Service 10 eta undefined', function () { 203 | assert.equal(undefined, parsedResult.trainServices[9].eta) 204 | }) 205 | 206 | it('Service 10 platform ', function () { 207 | assert.equal(1, parsedResult.trainServices[9].platform) 208 | }) 209 | 210 | it('Service 10 length ', function () { 211 | assert.equal(10, parsedResult.trainServices[9].length) 212 | }) 213 | 214 | it('Service 1 origin ', function () { 215 | assert.equal('London Waterloo', parsedResult.trainServices[0].origin.name) 216 | }) 217 | }) 218 | 219 | /* departureBoardWithDetails.xml */ 220 | describe('Departure Board With Details', function () { 221 | var fileContent = fs.readFileSync('./exampleResponses/departureBoardWithDetails.xml', 'UTF-8') 222 | var parsedResult = parsers.parseDepartureBoardWithDetailsResponse(fileContent) 223 | 224 | it('10 Services returned', function () { 225 | assert.equal(10, parsedResult.trainServices.length) 226 | }) 227 | 228 | it('Service 10 std', function () { 229 | assert.equal('20:04', parsedResult.trainServices[9].std) 230 | }) 231 | 232 | it('Service 10 sta undefined', function () { 233 | assert.equal(undefined, parsedResult.trainServices[9].sta) 234 | }) 235 | 236 | it('Service 10 platform ', function () { 237 | assert.equal(1, parsedResult.trainServices[9].platform) 238 | }) 239 | 240 | it('Service 10 operator ', function () { 241 | assert.equal('South Western Railway', parsedResult.trainServices[9].operator) 242 | }) 243 | 244 | it('Service 10 has 9 calling points ', function () { 245 | assert.equal(12, parsedResult.trainServices[0].subsequentCallingPoints.length) 246 | }) 247 | 248 | it('Service 10 calling point 9 is Clapham Junction ', function () { 249 | assert.equal('Clapham Junction', parsedResult.trainServices[0].subsequentCallingPoints[8].locationName) 250 | assert.equal('CLJ', parsedResult.trainServices[0].subsequentCallingPoints[8].crs) 251 | }) 252 | }) 253 | 254 | /* fastestDeparture.xml */ 255 | describe('FastestDeparture parsed correctly', function () { 256 | var fileContent = fs.readFileSync('./exampleResponses/fastestDeparture.xml', 'UTF-8') 257 | var parsedResult = parsers.parseFastestDeparture(fileContent) 258 | 259 | it('1 train service returned', function () { 260 | assert.equal(1, parsedResult.trainServices.length) 261 | }) 262 | 263 | it('Service id of service is resovled', function () { 264 | assert.equal('wfmF+NYLrCpeoJVHBxSxrQ==', parsedResult.trainServices[0].serviceId) 265 | }) 266 | 267 | it('STA of service is correct', function () { 268 | assert.equal('18:34', parsedResult.trainServices[0].sta) 269 | }) 270 | 271 | it('Operator of service is South Western Railway', function () { 272 | assert.equal('South Western Railway', parsedResult.trainServices[0].operator) 273 | }) 274 | 275 | it('Origin of service is London Waterloo', function () { 276 | assert.equal('London Waterloo', parsedResult.trainServices[0].origin.name) 277 | assert.equal('WAT', parsedResult.trainServices[0].origin.crs) 278 | }) 279 | }) 280 | 281 | /* fastestDepartureWithDetails.xml */ 282 | describe('FastestDeparture with Details parsed correctly', function () { 283 | var fileContent = fs.readFileSync('./exampleResponses/fastestDeparturesWithDetails.xml', 'UTF-8') 284 | var parsedResult = parsers.parseFastestDeparturesWithDetail(fileContent) 285 | 286 | it('1 train service returned', function () { 287 | assert.equal(1, parsedResult.trainServices.length) 288 | }) 289 | 290 | it('Service id of service is resovled', function () { 291 | assert.equal('wfmF+NYLrCpeoJVHBxSxrQ==', parsedResult.trainServices[0].serviceId) 292 | }) 293 | 294 | it('ETA of service is correct', function () { 295 | assert.equal('On time', parsedResult.trainServices[0].eta) 296 | }) 297 | 298 | it('Operator of service is South Western Railway', function () { 299 | assert.equal('South Western Railway', parsedResult.trainServices[0].operator) 300 | }) 301 | 302 | it('Origin of service is Weybridge', function () { 303 | assert.equal('London Waterloo', parsedResult.trainServices[0].destination.name) 304 | assert.equal('WAT', parsedResult.trainServices[0].destination.crs) 305 | }) 306 | 307 | it('Subsequent Calling Points is ok', function () { 308 | assert.equal(12, parsedResult.trainServices[0].subsequentCallingPoints.length) 309 | assert.equal('Chiswick', parsedResult.trainServices[0].subsequentCallingPoints[3].locationName) 310 | assert.equal(10, parsedResult.trainServices[0].subsequentCallingPoints[3].length) 311 | assert.equal('18:44', parsedResult.trainServices[0].subsequentCallingPoints[3].st) 312 | }) 313 | }) 314 | 315 | /* nextDeparture.xml */ 316 | describe('getNextDeparture parsed correctly', function () { 317 | var fileContent = fs.readFileSync('./exampleResponses/nextDeparture.xml', 'UTF-8') 318 | var parsedResult = parsers.parseNextDepartureResponse(fileContent) 319 | 320 | it('1 train service returned', function () { 321 | assert.equal(1, parsedResult.trainServices.length) 322 | }) 323 | 324 | it('RSID Parsed correctly', function () { 325 | assert.equal('SW182100', parsedResult.trainServices[0].rsid) 326 | }) 327 | 328 | it('Destination', function () { 329 | assert.equal('London Waterloo', parsedResult.trainServices[0].destination.name) 330 | }) 331 | 332 | it('STD is parsed', function () { 333 | assert.equal('18:34', parsedResult.trainServices[0].std) 334 | }) 335 | }) 336 | 337 | /* nextDepartureWithDetails.xml */ 338 | describe('getNextDepartureWithDetails parsed correctly', function () { 339 | var fileContent = fs.readFileSync('./exampleResponses/nextDepartureWithDetails.xml', 'UTF-8') 340 | var parsedResult = parsers.parseNextDepartureWithDetailsResponse(fileContent) 341 | 342 | it('1 train service returned', function () { 343 | assert.equal(1, parsedResult.trainServices.length) 344 | }) 345 | 346 | it('Service id of service is resovled', function () { 347 | assert.equal('wfmF+NYLrCpeoJVHBxSxrQ==', parsedResult.trainServices[0].serviceId) 348 | }) 349 | 350 | it('STA of service is correct', function () { 351 | assert.equal('18:34', parsedResult.trainServices[0].sta) 352 | }) 353 | 354 | it('Operator of service is South Western Railway', function () { 355 | assert.equal('South Western Railway', parsedResult.trainServices[0].operator) 356 | }) 357 | 358 | it('Origin of service is Weybridge', function () { 359 | assert.equal('London Waterloo', parsedResult.trainServices[0].origin.name) 360 | assert.equal('WAT', parsedResult.trainServices[0].origin.crs) 361 | }) 362 | }) 363 | /* serviceDetails.xml */ 364 | describe('Service Details parsing is correct', function () { 365 | var fileContent = fs.readFileSync('./exampleResponses/serviceDetails.xml', 'UTF-8') 366 | var parsedResult = parsers.parseServiceDetails(fileContent) 367 | it('returned object from example operator code = SW', function () { 368 | assert.equal('SW', parsedResult.serviceDetails.operatorCode) 369 | }) 370 | it('returned obejct from example operator = South Western Railway', function () { 371 | assert.equal('South Western Railway', parsedResult.serviceDetails.operator) 372 | }) 373 | it('returned object from example sta = 18:34', function () { 374 | assert.equal('18:34', parsedResult.serviceDetails.sta) 375 | }) 376 | it('returned object from example std = 18:34', function () { 377 | assert.equal('18:34', parsedResult.serviceDetails.std) 378 | }) 379 | it('returned object from example operatorCode = SW', function () { 380 | assert.equal('SW', parsedResult.serviceDetails.operatorCode) 381 | }) 382 | it('returned object from example operator = South Western Railway', function () { 383 | assert.equal('South Western Railway', parsedResult.serviceDetails.operator) 384 | }) 385 | 386 | it('returned object from example eta is undefined', function () { 387 | assert.equal(undefined, parsedResult.serviceDetails.etd) 388 | }) 389 | it('returned object from example length = 10', function () { 390 | assert.equal('10', parsedResult.serviceDetails.length) 391 | }) 392 | it('returned object from example platform = 1', function () { 393 | assert.equal('1', parsedResult.serviceDetails.platform) 394 | }) 395 | it('PreviousCallingPoints array is correct size 14', function () { 396 | var arr = parsedResult.serviceDetails.previousCallingPoints 397 | assert.equal(14, arr.length) 398 | }) 399 | it('First previous calling point crs code = WAT', function () { 400 | var arr = parsedResult.serviceDetails.previousCallingPoints 401 | assert.equal('WAT', arr[0].crs) 402 | }) 403 | it('First previous calling point locationName = London Waterloo', function () { 404 | var arr = parsedResult.serviceDetails.previousCallingPoints 405 | assert.equal('London Waterloo', arr[0].locationName) 406 | }) 407 | it('First previous calling point st = 09:45', function () { 408 | var arr = parsedResult.serviceDetails.previousCallingPoints 409 | assert.equal('17:45', arr[0].st) 410 | }) 411 | it('First previous calling point crs = SYL', function () { 412 | var arr = parsedResult.serviceDetails.previousCallingPoints 413 | assert.equal('WAT', arr[0].crs) 414 | }) 415 | it('subsequentCallingPoints array is correct size 12', function () { 416 | var arr = parsedResult.serviceDetails.subsequentCallingPoints 417 | assert.equal(12, arr.length) 418 | }) 419 | it('last subsequent calling point crs code = WAT', function () { 420 | var arr = parsedResult.serviceDetails.subsequentCallingPoints 421 | assert.equal('WAT', arr[11].crs) 422 | }) 423 | it('last subsequent calling point locationName = London Waterloo', function () { 424 | var arr = parsedResult.serviceDetails.subsequentCallingPoints 425 | assert.equal('London Waterloo', arr[11].locationName) 426 | }) 427 | it('last subsequent calling point st = 18:36', function () { 428 | var arr = parsedResult.serviceDetails.subsequentCallingPoints 429 | assert.equal('18:36', arr[0].st) 430 | }) 431 | it('last subsequent calling point et = On time', function () { 432 | var arr = parsedResult.serviceDetails.subsequentCallingPoints 433 | assert.equal('On time', arr[11].et) 434 | }) 435 | }) 436 | -------------------------------------------------------------------------------- /exampleResponses/departureBoardWithDetails.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 2018-01-11T18:32:37.1502814+00:00 6 | Isleworth 7 | ISL 8 | Putney 9 | PUT 10 | true 11 | 12 | 13 | 18:34 14 | On time 15 | 1 16 | South Western Railway 17 | SW 18 | train 19 | 10 20 | wfmF+NYLrCpeoJVHBxSxrQ== 21 | SW182100 22 | 23 | 24 | London Waterloo 25 | WAT 26 | 27 | 28 | 29 | 30 | London Waterloo 31 | WAT 32 | via Brentford 33 | 34 | 35 | 36 | 37 | 38 | Syon Lane 39 | SYL 40 | 18:36 41 | On time 42 | 10 43 | 44 | 45 | Brentford 46 | BFD 47 | 18:38 48 | On time 49 | 10 50 | 51 | 52 | Kew Bridge 53 | KWB 54 | 18:41 55 | On time 56 | 10 57 | 58 | 59 | Chiswick 60 | CHK 61 | 18:44 62 | On time 63 | 10 64 | 65 | 66 | Barnes Bridge 67 | BNI 68 | 18:46 69 | On time 70 | 10 71 | 72 | 73 | Barnes 74 | BNS 75 | 18:49 76 | On time 77 | 10 78 | 79 | 80 | Putney 81 | PUT 82 | 18:52 83 | On time 84 | 10 85 | 86 | 87 | Wandsworth Town 88 | WNT 89 | 18:55 90 | On time 91 | 10 92 | 93 | 94 | Clapham Junction 95 | CLJ 96 | 18:58 97 | On time 98 | 10 99 | 100 | 101 | Queenstown Road Battersea 102 | QRB 103 | 19:01 104 | On time 105 | 10 106 | 107 | 108 | Vauxhall 109 | VXH 110 | 19:04 111 | On time 112 | 10 113 | 114 | 115 | London Waterloo 116 | WAT 117 | 19:11 118 | On time 119 | 10 120 | 121 | 122 | 123 | 124 | 125 | 18:42 126 | On time 127 | 2 128 | South Western Railway 129 | SW 130 | train 131 | 10 132 | nh3WKFCFpkwRTwqpzEUc/A== 133 | SW211600 134 | 135 | 136 | London Waterloo 137 | WAT 138 | 139 | 140 | 141 | 142 | Mortlake 143 | MTL 144 | 145 | 146 | 147 | 148 | 149 | Hounslow 150 | HOU 151 | 18:45 152 | On time 153 | 10 154 | 155 | 156 | Whitton 157 | WTN 158 | 18:53 159 | On time 160 | 10 161 | 162 | 163 | Twickenham 164 | TWI 165 | 18:56 166 | On time 167 | 10 168 | 169 | 170 | St Margarets (London) 171 | SMG 172 | 19:00 173 | On time 174 | 10 175 | 176 | 177 | Richmond 178 | RMD 179 | 19:03 180 | On time 181 | 10 182 | 183 | 184 | North Sheen 185 | NSH 186 | 19:06 187 | On time 188 | 10 189 | 190 | 191 | Mortlake 192 | MTL 193 | 19:08 194 | On time 195 | 10 196 | 197 | 198 | 199 | 200 | 201 | 18:49 202 | 18:53 203 | 1 204 | South Western Railway 205 | SW 206 | train 207 | 4 208 | KXIphaTeuVHS0/a4SWgoAg== 209 | SW188000 210 | 211 | 212 | Weybridge 213 | WYB 214 | 215 | 216 | 217 | 218 | London Waterloo 219 | WAT 220 | via Brentford 221 | 222 | 223 | 224 | 225 | 226 | Syon Lane 227 | SYL 228 | 18:51 229 | 18:55 230 | 4 231 | 232 | 233 | Brentford 234 | BFD 235 | 18:53 236 | 18:57 237 | 4 238 | 239 | 240 | Kew Bridge 241 | KWB 242 | 18:56 243 | 19:00 244 | 4 245 | 246 | 247 | Chiswick 248 | CHK 249 | 18:59 250 | 19:02 251 | 4 252 | 253 | 254 | Barnes Bridge 255 | BNI 256 | 19:01 257 | 19:05 258 | 4 259 | 260 | 261 | Barnes 262 | BNS 263 | 19:04 264 | 19:08 265 | 4 266 | 267 | 268 | Putney 269 | PUT 270 | 19:07 271 | 19:10 272 | 4 273 | 274 | 275 | Wandsworth Town 276 | WNT 277 | 19:10 278 | 19:13 279 | 4 280 | 281 | 282 | Clapham Junction 283 | CLJ 284 | 19:13 285 | 19:16 286 | 4 287 | 288 | 289 | Queenstown Road Battersea 290 | QRB 291 | 19:16 292 | 19:19 293 | 4 294 | 295 | 296 | Vauxhall 297 | VXH 298 | 19:19 299 | 19:22 300 | 4 301 | 302 | 303 | London Waterloo 304 | WAT 305 | 19:26 306 | On time 307 | 4 308 | 309 | 310 | 311 | 312 | 313 | 19:04 314 | On time 315 | 1 316 | South Western Railway 317 | SW 318 | train 319 | 8 320 | b5oGjwYNIrFOQCoeerXVsA== 321 | SW182200 322 | 323 | 324 | London Waterloo 325 | WAT 326 | 327 | 328 | 329 | 330 | London Waterloo 331 | WAT 332 | via Brentford 333 | 334 | 335 | 336 | 337 | 338 | Syon Lane 339 | SYL 340 | 19:06 341 | On time 342 | 8 343 | 344 | 345 | Brentford 346 | BFD 347 | 19:08 348 | On time 349 | 8 350 | 351 | 352 | Kew Bridge 353 | KWB 354 | 19:11 355 | On time 356 | 8 357 | 358 | 359 | Chiswick 360 | CHK 361 | 19:14 362 | On time 363 | 8 364 | 365 | 366 | Barnes Bridge 367 | BNI 368 | 19:16 369 | On time 370 | 8 371 | 372 | 373 | Barnes 374 | BNS 375 | 19:19 376 | On time 377 | 8 378 | 379 | 380 | Putney 381 | PUT 382 | 19:22 383 | On time 384 | 8 385 | 386 | 387 | Wandsworth Town 388 | WNT 389 | 19:25 390 | On time 391 | 8 392 | 393 | 394 | Clapham Junction 395 | CLJ 396 | 19:28 397 | On time 398 | 8 399 | 400 | 401 | Queenstown Road Battersea 402 | QRB 403 | 19:31 404 | On time 405 | 8 406 | 407 | 408 | Vauxhall 409 | VXH 410 | 19:34 411 | On time 412 | 8 413 | 414 | 415 | London Waterloo 416 | WAT 417 | 19:41 418 | On time 419 | 8 420 | 421 | 422 | 423 | 424 | 425 | 19:12 426 | On time 427 | 2 428 | South Western Railway 429 | SW 430 | train 431 | 10 432 | 5QgCfHmYd7JNvbV0YSyhVw== 433 | SW211700 434 | 435 | 436 | London Waterloo 437 | WAT 438 | 439 | 440 | 441 | 442 | Mortlake 443 | MTL 444 | 445 | 446 | 447 | 448 | 449 | Hounslow 450 | HOU 451 | 19:15 452 | On time 453 | 10 454 | 455 | 456 | Whitton 457 | WTN 458 | 19:23 459 | On time 460 | 10 461 | 462 | 463 | Twickenham 464 | TWI 465 | 19:26 466 | On time 467 | 10 468 | 469 | 470 | St Margarets (London) 471 | SMG 472 | 19:30 473 | On time 474 | 10 475 | 476 | 477 | Richmond 478 | RMD 479 | 19:33 480 | On time 481 | 10 482 | 483 | 484 | North Sheen 485 | NSH 486 | 19:36 487 | On time 488 | 10 489 | 490 | 491 | Mortlake 492 | MTL 493 | 19:38 494 | On time 495 | 10 496 | 497 | 498 | 499 | 500 | 501 | 19:19 502 | On time 503 | 1 504 | South Western Railway 505 | SW 506 | train 507 | 4 508 | 2HTkZpwWG3h0h2h2gnYvPw== 509 | SW188200 510 | 511 | 512 | Weybridge 513 | WYB 514 | 515 | 516 | 517 | 518 | London Waterloo 519 | WAT 520 | via Brentford 521 | 522 | 523 | 524 | 525 | 526 | Syon Lane 527 | SYL 528 | 19:21 529 | On time 530 | 4 531 | 532 | 533 | Brentford 534 | BFD 535 | 19:23 536 | On time 537 | 4 538 | 539 | 540 | Kew Bridge 541 | KWB 542 | 19:26 543 | On time 544 | 4 545 | 546 | 547 | Chiswick 548 | CHK 549 | 19:29 550 | On time 551 | 4 552 | 553 | 554 | Barnes Bridge 555 | BNI 556 | 19:31 557 | On time 558 | 4 559 | 560 | 561 | Barnes 562 | BNS 563 | 19:34 564 | On time 565 | 4 566 | 567 | 568 | Putney 569 | PUT 570 | 19:37 571 | On time 572 | 4 573 | 574 | 575 | Wandsworth Town 576 | WNT 577 | 19:40 578 | On time 579 | 4 580 | 581 | 582 | Clapham Junction 583 | CLJ 584 | 19:43 585 | On time 586 | 4 587 | 588 | 589 | Queenstown Road Battersea 590 | QRB 591 | 19:46 592 | On time 593 | 4 594 | 595 | 596 | Vauxhall 597 | VXH 598 | 19:49 599 | On time 600 | 4 601 | 602 | 603 | London Waterloo 604 | WAT 605 | 19:56 606 | On time 607 | 4 608 | 609 | 610 | 611 | 612 | 613 | 19:34 614 | On time 615 | 1 616 | South Western Railway 617 | SW 618 | train 619 | 8 620 | +kJe9WIrueGPWUYhDzmRQg== 621 | SW182300 622 | 623 | 624 | London Waterloo 625 | WAT 626 | 627 | 628 | 629 | 630 | London Waterloo 631 | WAT 632 | via Brentford 633 | 634 | 635 | 636 | 637 | 638 | Syon Lane 639 | SYL 640 | 19:36 641 | On time 642 | 8 643 | 644 | 645 | Brentford 646 | BFD 647 | 19:38 648 | On time 649 | 8 650 | 651 | 652 | Kew Bridge 653 | KWB 654 | 19:41 655 | On time 656 | 8 657 | 658 | 659 | Chiswick 660 | CHK 661 | 19:44 662 | On time 663 | 8 664 | 665 | 666 | Barnes Bridge 667 | BNI 668 | 19:46 669 | On time 670 | 8 671 | 672 | 673 | Barnes 674 | BNS 675 | 19:49 676 | On time 677 | 8 678 | 679 | 680 | Putney 681 | PUT 682 | 19:52 683 | On time 684 | 8 685 | 686 | 687 | Wandsworth Town 688 | WNT 689 | 19:55 690 | On time 691 | 8 692 | 693 | 694 | Clapham Junction 695 | CLJ 696 | 19:58 697 | On time 698 | 8 699 | 700 | 701 | Queenstown Road Battersea 702 | QRB 703 | 20:01 704 | On time 705 | 8 706 | 707 | 708 | Vauxhall 709 | VXH 710 | 20:04 711 | On time 712 | 8 713 | 714 | 715 | London Waterloo 716 | WAT 717 | 20:11 718 | On time 719 | 8 720 | 721 | 722 | 723 | 724 | 725 | 19:42 726 | On time 727 | 2 728 | South Western Railway 729 | SW 730 | train 731 | 10 732 | cm080rz49AMkBqto0DmPCQ== 733 | SW211800 734 | 735 | 736 | London Waterloo 737 | WAT 738 | 739 | 740 | 741 | 742 | Mortlake 743 | MTL 744 | 745 | 746 | 747 | 748 | 749 | Hounslow 750 | HOU 751 | 19:45 752 | On time 753 | 10 754 | 755 | 756 | Whitton 757 | WTN 758 | 19:53 759 | On time 760 | 10 761 | 762 | 763 | Twickenham 764 | TWI 765 | 19:56 766 | On time 767 | 10 768 | 769 | 770 | St Margarets (London) 771 | SMG 772 | 20:00 773 | On time 774 | 10 775 | 776 | 777 | Richmond 778 | RMD 779 | 20:03 780 | On time 781 | 10 782 | 783 | 784 | North Sheen 785 | NSH 786 | 20:06 787 | On time 788 | 10 789 | 790 | 791 | Mortlake 792 | MTL 793 | 20:08 794 | On time 795 | 10 796 | 797 | 798 | 799 | 800 | 801 | 19:49 802 | On time 803 | 1 804 | South Western Railway 805 | SW 806 | train 807 | 10 808 | 14nFdX55toGlajTRiRUE7g== 809 | SW188400 810 | 811 | 812 | Weybridge 813 | WYB 814 | 815 | 816 | 817 | 818 | London Waterloo 819 | WAT 820 | via Brentford 821 | 822 | 823 | 824 | 825 | 826 | Syon Lane 827 | SYL 828 | 19:51 829 | On time 830 | 10 831 | 832 | 833 | Brentford 834 | BFD 835 | 19:53 836 | On time 837 | 10 838 | 839 | 840 | Kew Bridge 841 | KWB 842 | 19:56 843 | On time 844 | 10 845 | 846 | 847 | Chiswick 848 | CHK 849 | 19:59 850 | On time 851 | 10 852 | 853 | 854 | Barnes Bridge 855 | BNI 856 | 20:01 857 | On time 858 | 10 859 | 860 | 861 | Barnes 862 | BNS 863 | 20:04 864 | On time 865 | 10 866 | 867 | 868 | Putney 869 | PUT 870 | 20:07 871 | On time 872 | 10 873 | 874 | 875 | Wandsworth Town 876 | WNT 877 | 20:10 878 | On time 879 | 10 880 | 881 | 882 | Clapham Junction 883 | CLJ 884 | 20:13 885 | On time 886 | 10 887 | 888 | 889 | Queenstown Road Battersea 890 | QRB 891 | 20:16 892 | On time 893 | 10 894 | 895 | 896 | Vauxhall 897 | VXH 898 | 20:19 899 | On time 900 | 10 901 | 902 | 903 | London Waterloo 904 | WAT 905 | 20:27 906 | On time 907 | 10 908 | 909 | 910 | 911 | 912 | 913 | 20:04 914 | On time 915 | 1 916 | South Western Railway 917 | SW 918 | train 919 | 10 920 | oUW4Wt3Vc+7BHVWXcrkWpQ== 921 | SW182400 922 | 923 | 924 | London Waterloo 925 | WAT 926 | 927 | 928 | 929 | 930 | London Waterloo 931 | WAT 932 | via Brentford 933 | 934 | 935 | 936 | 937 | 938 | Syon Lane 939 | SYL 940 | 20:06 941 | On time 942 | 10 943 | 944 | 945 | Brentford 946 | BFD 947 | 20:08 948 | On time 949 | 10 950 | 951 | 952 | Kew Bridge 953 | KWB 954 | 20:11 955 | On time 956 | 10 957 | 958 | 959 | Chiswick 960 | CHK 961 | 20:14 962 | On time 963 | 10 964 | 965 | 966 | Barnes Bridge 967 | BNI 968 | 20:16 969 | On time 970 | 10 971 | 972 | 973 | Barnes 974 | BNS 975 | 20:19 976 | On time 977 | 10 978 | 979 | 980 | Putney 981 | PUT 982 | 20:22 983 | On time 984 | 10 985 | 986 | 987 | Wandsworth Town 988 | WNT 989 | 20:25 990 | On time 991 | 10 992 | 993 | 994 | Clapham Junction 995 | CLJ 996 | 20:28 997 | On time 998 | 10 999 | 1000 | 1001 | Queenstown Road Battersea 1002 | QRB 1003 | 20:31 1004 | On time 1005 | 10 1006 | 1007 | 1008 | Vauxhall 1009 | VXH 1010 | 20:34 1011 | On time 1012 | 10 1013 | 1014 | 1015 | London Waterloo 1016 | WAT 1017 | 20:41 1018 | On time 1019 | 10 1020 | 1021 | 1022 | 1023 | 1024 | 1025 | 1026 | 1027 | 1028 | 1029 | --------------------------------------------------------------------------------