Skip to content

Commit

Permalink
FSR-1044 | Bug: 500 Error in station-csv Route Due to Invalid Telemet…
Browse files Browse the repository at this point in the history
…ry Timestamps (#554)

* Bug: 500 Error in station-csv Route Due to Invalid Telemetry Timestamps

https://eaflood.atlassian.net/browse/FSR-1044

* Fix issue where forecast station csv would error if no telemetry is in DB
Also fix issue where it would error if station doesn't exist, now will respond 404

---------

Co-authored-by: Max Bladen-Clark <[email protected]>
  • Loading branch information
maxcbc and Max Bladen-Clark authored Nov 16, 2023
1 parent 2ee30dd commit dc7a731
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 60 deletions.
105 changes: 45 additions & 60 deletions server/routes/station-csv.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const floodService = require('../services/flood')
const moment = require('moment-timezone')
const boom = require('@hapi/boom')

module.exports = {
method: 'GET',
Expand All @@ -12,85 +13,69 @@ module.exports = {

const station = await floodService.getStationById(id, direction)

if (!station) {
return boom.notFound('Station not found')
}

const stationName = station.external_name.replace(/[^a-zA-Z0-9]+/g, '-')

const [telemetry, thresholds] = await Promise.all([
const [rawTelemetry, thresholds] = await Promise.all([
floodService.getStationTelemetry(id, direction),
floodService.getStationForecastThresholds(id)
])

this.telemetry = telemetry

this.telemetry.forEach(function (item) {
item.type = 'observed'
item.ts = moment.utc(item.ts).format()
})
const telemetry = rawTelemetry.map(item => ({
...item,
type: 'observed',
ts: moment.utc(item.ts).format()
}))

// Forecast station
if (thresholds.length) {
const values = await floodService.getStationForecastData(station.wiski_id)

const forecast = values.SetofValues[0].Value
const includeForecast = !!thresholds.length
if (includeForecast && telemetry.length) {
const forecastStart = moment(telemetry[0].ts)
const truncateDate = moment(forecastStart).add(36, 'hours')
const { SetofValues: [{ Value: forecast } = { Value: [] }] = [] } = await floodService.getStationForecastData(station.wiski_id)

const forecastData = forecast.map(item => {
for (const item of forecast) {
const itemDate = item.$.date
const itemTime = item.$.time
const date = moment(`${itemDate} ${itemTime}`).format('YYYY-MM-DDTHH:mm') + 'Z'
return { ts: date, _: item._, type: 'forecast' }
})

// Truncate forecast data to be 36 hours from forecast creation
const forecastStart = moment(this.telemetry[0].ts)

this.truncateDate = moment(forecastStart).add(36, 'hours')

forecastData.forEach(function (value) {
value.ts = moment(value.ts)

if (value.ts.isBefore(forecastStart) || value.ts.isAfter(this.truncateDate)) {
return
const date = moment(`${itemDate}T${itemTime}Z`)

if (!date.isBefore(forecastStart) && !date.isAfter(truncateDate)) {
telemetry.push({
ts: moment.utc(date).format(),
_: item._,
type: 'forecast'
})
}
value.ts = moment.utc(value.ts).format()

this.telemetry.push(value)
}, this)
}
}

this.telemetry.sort(function (a, b) {
return new Date(a.ts) - new Date(b.ts)
})

if (thresholds.length) {
this.csvString = [
[
'Timestamp (UTC)',
'Height (m)',
'Type(observed/forecast)'
],
...this.telemetry.map(item => [
const csvString = [
[
'Timestamp (UTC)',
'Height (m)',
'Type(observed/forecast)'
],
...telemetry
.sort((a, b) => new Date(a.ts) - new Date(b.ts))
.map(item => [
item.ts,
item._,
item.type
])
]
.map(e => e.join(','))
.join('\n')
} else {
this.csvString = [
[
'Timestamp (UTC)',
'Height (m)'
],
...this.telemetry.map(item => [
item.ts,
item._
])
]
.map(e => e.join(','))
.join('\n')
}
]
.reduce((acc, [ts, height, type]) => {
acc += `${ts},${height}`
if (includeForecast) {
acc += `,${type}`
}
return `${acc}\n`
}, '')
.trim()

const response = h.response(this.csvString)
const response = h.response(csvString)
response.type('text/csv')
response.header('Content-disposition', `attachment; filename=${stationName}-height-data.csv`)
return response
Expand Down
30 changes: 30 additions & 0 deletions test/routes/station-csv.js
Original file line number Diff line number Diff line change
Expand Up @@ -310,4 +310,34 @@ lab.experiment('Routes test - station-csv', () => {
Code.expect(response.result).to.equal('Timestamp (UTC),Height (m)\n2020-03-13T01:30:00Z,1.354')
Code.expect(response.headers['content-type']).to.include('text/csv')
})

lab.test('GET /station-csv/1337 station not found', async () => {
const options = {
method: 'GET',
url: '/station-csv/1337'
}
const floodService = require('../../server/services/flood')

const fakeStationData = () => {}

const fakeTelemetryData = () => [
{
ts: '2020-03-13T01:30Z',
_: 1.354,
err: false
}
]

const fakeThresholdsData = () => []

sandbox.stub(floodService, 'getStationById').callsFake(fakeStationData)
sandbox.stub(floodService, 'getStationTelemetry').callsFake(fakeTelemetryData)
sandbox.stub(floodService, 'getStationForecastThresholds').callsFake(fakeThresholdsData)

const response = await server.inject(options)
Code.expect(response.statusCode).to.equal(404)
Code.expect(response.result.statusCode).to.equal(404)
Code.expect(response.result.error).to.equal('Not Found')
Code.expect(response.result.message).to.equal('Station not found')
})
})

0 comments on commit dc7a731

Please sign in to comment.