Skip to content

Commit

Permalink
merge /stations/all into /stations
Browse files Browse the repository at this point in the history
  • Loading branch information
derhuerst committed May 3, 2020
1 parent 911a030 commit 8d2dcf3
Show file tree
Hide file tree
Showing 6 changed files with 96 additions and 53 deletions.
2 changes: 0 additions & 2 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ const createHealthCheck = require('hafas-client-health-check')

const pkg = require('./package.json')
const stations = require('./routes/stations')
const allStations = require('./routes/all-stations')
const station = require('./routes/station')
const lines = require('./routes/lines')
const line = require('./routes/line')
Expand All @@ -25,7 +24,6 @@ const hafas = createHafas('hafas-rest-api: ' + pkg.name)
const modifyRoutes = (routes) => ({
...routes,
'/stations': stations,
'/stations/all': allStations,
'/stations/:id': station,
'/lines': lines,
'/lines/:id': line,
Expand Down
15 changes: 15 additions & 0 deletions lib/to-ndjson-buf.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
'use strict'

const toNdjsonBuf = (items) => {
const chunks = []
let i = 0, bytes = 0
for (const item of items) {
const sep = i++ === 0 ? '' : '\n'
const buf = Buffer.from(sep + JSON.stringify(item), 'utf8')
chunks.push(buf)
bytes += buf.length
}
return Buffer.concat(chunks, bytes)
}

module.exports = toNdjsonBuf
16 changes: 16 additions & 0 deletions lib/vbb-stations.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
'use strict'

const {statSync} = require('fs')
const allStations = require('vbb-stations/full.json')

// We don't have access to the publish date+time of the npm package,
// so we use the ctime of vbb-stations/full.json as an approximation.
// todo: this is brittle, find a better way, e.g. a build script
const timeModified = statSync(require.resolve('vbb-stations/full.json')).ctime

// todo: station.stops?

module.exports = {
timeModified,
data: allStations,
}
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,13 @@
"dependencies": {
"cli-native": "^1.0.0",
"corser": "^2.0.1",
"etag": "^1.8.1",
"hafas-client-health-check": "^2.1.1",
"hafas-rest-api": "^3.3.1",
"lodash.omit": "^4.5.0",
"ndjson": "^1.5.0",
"nocache": "^2.0.0",
"serve-buffer": "^2.0.0",
"through2-map": "^3.0.0",
"vbb-hafas": "^7.0.2",
"vbb-lines": "^4.0.0",
Expand Down
11 changes: 0 additions & 11 deletions routes/all-stations.js

This file was deleted.

103 changes: 63 additions & 40 deletions routes/stations.js
Original file line number Diff line number Diff line change
@@ -1,64 +1,87 @@
'use strict'

const parse = require('cli-native').to
const computeEtag = require('etag')
const serveBuffer = require('serve-buffer')
const autocomplete = require('vbb-stations-autocomplete')
const stations = require('vbb-stations')
const map = require('through2-map')
const ndjson = require('ndjson')
const parse = require('cli-native').to
const {filterByKeys: createFilter} = require('vbb-stations')
const {data: stations, timeModified} = require('../lib/vbb-stations')
const toNdjsonBuf = require('../lib/to-ndjson-buf')

const JSON_MIME = 'application/json'
const NDJSON_MIME = 'application/x-ndjson'

const asJson = Buffer.from(JSON.stringify(stations), 'utf8')
const asJsonEtag = computeEtag(asJson)
const asNdjson = toNdjsonBuf(Object.entries(stations))
const asNdjsonEtag = computeEtag(asNdjson)

const err400 = (msg) => {
const err = (msg, statusCode = 500) => {
const err = new Error(msg)
err.statusCode = 400
err.statusCode = statusCode
return err
}

const allStations = stations()

// todo: merge this with db-rest#new-hafas-client/lib/stations
const complete = (req, res, next) => {
const limit = req.query.results && parseInt(req.query.results) || 3
const fuzzy = parse(req.query.fuzzy) === true
const completion = parse(req.query.completion) !== false
const results = autocomplete(req.query.query, limit, fuzzy, completion)
const complete = (req, res, next, q, onStation, onEnd) => {
const limit = q.results && parseInt(q.results) || 3
const fuzzy = parse(q.fuzzy) === true
const completion = parse(q.completion) !== false
const results = autocomplete(q.query, limit, fuzzy, completion)

const data = []
for (let result of results) {
// todo: make this more efficient
const [station] = stations(result.id)
for (const result of results) {
const station = stations[result.id]
if (!station) continue

data.push(Object.assign({}, result, station))
Object.assign(result, station)
onStation(result)
}

res.json(data)
next()
onEnd()
}

const filter = (req, res, next) => {
if (Object.keys(req.query).length === 0) {
return next(err400('Missing properties.'))
const filter = (req, res, next, q, onStation, onEnd) => {
const selector = Object.create(null)
for (const prop in q) selector[prop] = parse(q[prop])
const filter = createFilter(selector)

for (const station of Object.values(stations)) {
if (filter(station)) onStation(station)
}
onEnd()
}

const props = Object.create(null)
for (let prop in req.query) {
props[prop] = parse(req.query[prop])
const stationsRoute = (req, res, next) => {
const t = req.accepts([JSON_MIME, NDJSON_MIME])
if (t !== JSON_MIME && t !== NDJSON_MIME) {
return next(err(JSON + ' or ' + NDJSON_MIME, 406))
}
const results = stations(props)

res.type('application/x-ndjson')
const out = ndjson.stringify()
out
.once('error', next)
.pipe(res)
.once('finish', () => next())
res.setHeader('Last-Modified', timeModified.toUTCString())

for (const station of results) out.write(station)
out.end()
}
const head = t === JSON_MIME ? '{\n' : ''
const sep = t === JSON_MIME ? ',\n' : '\n'
const tail = t === JSON_MIME ? '\n}\n' : '\n'
let i = 0
const onStation = (s) => {
const j = JSON.stringify(s)
const field = t === JSON_MIME ? `"${s.id}":` : ''
res.write(`${i++ === 0 ? head : sep}${field}${j}`)
}
const onEnd = () => {
if (i > 0) res.end(tail)
else res.end(head + tail)
}

const stationsRoute = (req, res, next) => {
if (req.query.query) complete(req, res, next)
else filter(req, res, next)
const q = req.query
if (Object.keys(q).length === 0) {
const data = t === JSON_MIME ? asJson : asNdjson
const etag = t === JSON_MIME ? asJsonEtag : asNdjsonEtag
serveBuffer(req, res, data, {timeModified, etag})
} else if (q.query) {
complete(req, res, next, q, onStation, onEnd)
} else {
filter(req, res, next, q, onStation, onEnd)
}
}

stationsRoute.queryParameters = {
Expand Down

0 comments on commit 8d2dcf3

Please sign in to comment.