Skip to content

Commit 2dd4736

Browse files
committed
[download][s]Support for Column Order by header parameter if given in the body of the download query
1 parent 1e48bb0 commit 2dd4736

2 files changed

Lines changed: 67 additions & 5 deletions

File tree

routes/index.js

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -121,12 +121,11 @@ DOWNLOAD_FORMATS_SUPPORTED = ['json', 'csv', 'tsv', 'xlsx', 'ods']
121121
router.post(`/${APP_VERSION}/download`, async function (req, res, next) {
122122
// get the graphql query from body
123123
const query = req.body.query ? req.body.query : req.body
124+
const xlsxHeader = req.body.header ? req.body.header : undefined
125+
// TODO header verification, else it will break when ordering fields
124126
// call GraphQL
125127
try {
126-
// TODO check graphql syntax BEFORE sending it
127-
const gqlRes = await request(`${process.env.HASURA_URL}/v1/graphql`, query)
128-
129-
// // capture graphql response
128+
// Some checks before the DB call
130129
const ext = (req.params.format || req.query.format || 'json')
131130
.toLowerCase()
132131
.trim()
@@ -137,12 +136,18 @@ router.post(`/${APP_VERSION}/download`, async function (req, res, next) {
137136
'Bad format. Supported Formats: ' +
138137
JSON.stringify(DOWNLOAD_FORMATS_SUPPORTED)
139138
)
139+
.end()
140+
return
140141
}
141142
const colSep = (req.query.field_separator || ',').trim()
142143
res.set(
143144
'Content-Disposition',
144145
'attachment; filename="download.' + ext + '";'
145146
)
147+
// TODO check graphql syntax BEFORE sending it
148+
const gqlRes = await request(`${process.env.HASURA_URL}/v1/graphql`, query)
149+
150+
// // capture graphql response
146151
if (ext != 'json') {
147152
// any spreadsheet supported by [js-xlsx](https://github.com/SheetJS/sheetjs)
148153
let wb = XLSX.utils.book_new()
@@ -153,7 +158,9 @@ router.post(`/${APP_VERSION}/download`, async function (req, res, next) {
153158

154159
//iterate over the result sets and create a work sheet to append to the book
155160
Object.keys(gqlRes).map((k) => {
156-
const ws = XLSX.utils.json_to_sheet(gqlRes[k])
161+
const ws = xlsxHeader
162+
? XLSX.utils.json_to_sheet(gqlRes[k], { header: xlsxHeader })
163+
: XLSX.utils.json_to_sheet(gqlRes[k])
157164
XLSX.utils.book_append_sheet(wb, ws, k)
158165
})
159166
if (ext === 'tsv' || (ext === 'csv' && colSep != ',')) {

test/integration/download.test.js

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ query MyQuery {
1313
}
1414
`
1515

16+
// const HEADER = `[float_column, int_column, text_column, time_column]`
17+
const HEADER = ['int_column', 'float_column', 'time_column', 'text_column']
18+
1619
describe('data-api-download', function () {
1720
it('should return JSON when no format parameter is passed', function (done) {
1821
request(app)
@@ -88,6 +91,12 @@ describe('data-api-download', function () {
8891
)
8992
assert(res.header['content-type'] == 'text/csv; charset=utf-8')
9093
// check that is a CSV
94+
const pipeSeparated = res.text
95+
.split('\n')
96+
.filter((line) => line.length > 0)
97+
.every((line) => line.includes(','))
98+
// console.log('pipe Separated = ', pipeSeparated)
99+
assert(pipeSeparated)
91100
done()
92101
})
93102
})
@@ -110,6 +119,52 @@ describe('data-api-download', function () {
110119
)
111120
assert(res.header['content-type'] == 'text/csv; charset=utf-8')
112121
// check that is a pipe-separated-CSV
122+
const pipeSeparated = res.text
123+
.split('\n')
124+
.filter((line) => line.length > 0)
125+
.every((line) => line.includes('|'))
126+
// console.log('pipe Separated = ', pipeSeparated)
127+
assert(pipeSeparated)
128+
129+
done()
130+
})
131+
})
132+
133+
it('should return columns in specified ORDER when adding headers to the call', function (done) {
134+
request(app)
135+
.post('/v1/download?format=csv')
136+
.send({
137+
query: QUERY,
138+
header: HEADER,
139+
})
140+
.expect(200)
141+
.end(function (err, res) {
142+
console.log('ERROR = ', err)
143+
// console.log('response TEXT: ', res.text)
144+
// console.log('response BODY: ', res.body)
145+
console.log(res.header)
146+
assert(
147+
res.header['content-disposition'] ==
148+
'attachment; filename="download.csv";'
149+
)
150+
assert(res.header['content-type'] == 'text/csv; charset=utf-8')
151+
// check that the header is ordered
152+
const headerLine = res.text.split('\n')[0].split(',')
153+
// console.log('header line = ', headerLine, HEADER)
154+
assert(headerLine.length === HEADER.length)
155+
const matchingHeader = headerLine
156+
.map(function (e, i) {
157+
// console.log(
158+
// 'matching header',
159+
// e.trim() == HEADER[i].trim(),
160+
// e,
161+
// HEADER[i]
162+
// )
163+
return e.trim() == HEADER[i].trim()
164+
})
165+
.every((e) => e == true)
166+
167+
assert(matchingHeader)
113168

114169
done()
115170
})

0 commit comments

Comments
 (0)