11var express = require ( 'express' )
22var router = express . Router ( )
3+ const { Readable, finished } = require ( 'stream' )
4+ const { once } = require ( 'events' )
5+
36const { request, gql } = require ( 'graphql-request' )
47
58const { queryForData } = require ( './queryGraphQL' )
9+ const XLSX = require ( 'xlsx' )
10+ const path = require ( 'path' )
611
712const APP_VERSION = 'v1'
813
@@ -54,7 +59,7 @@ function beautifyGQLSchema(gqlSchema) {
5459
5560/* GET home page. */
5661router . get ( '/' , function ( req , res , next ) {
57- res . send ( 'Home Page!!' )
62+ res . send ( 'DATA-API Home Page!!' )
5863} )
5964
6065router . get ( `/${ APP_VERSION } /datastore_search/help` , function ( req , res , next ) {
@@ -83,19 +88,12 @@ router.get(`/${APP_VERSION}/datastore_search`, async function (req, res, next) {
8388 if ( ! ( 'resource_id' in req . query ) ) {
8489 return res . redirect ( 303 , `/${ APP_VERSION } /datastore_search/help` )
8590 }
86- // console.log("Request: " + JSON.stringify(req))
87- // console.log('Query: ' + JSON.stringify(req.query))
88- // console.log('Params: ' + JSON.stringify(req.params))
89- // console.log("Headers: " + JSON.stringify(req.headers))
9091 const table = req . query . resource_id
9192 // query for schema -> this should be already in Frictionless format
9293 // const schema = await queryForSchema()
9394 const schema = await getGraphQLTableSchema ( table )
94- // console.log('SCHEMA: ' + JSON.stringify(schema))
9595 // query for data -> basically the call to queryGraphQL
9696 const data = await queryForData ( schema , req . query )
97-
98- // console.log('RESPONSE: ' + JSON.stringify(data))
9997 /*TODO*/
10098 /* Auth handling ... maybe JWT? */
10199 // Mandatory GET parameters check
@@ -110,4 +108,104 @@ router.get(`/${APP_VERSION}/datastore_search`, async function (req, res, next) {
110108 }
111109} )
112110
111+ //TODO finish this test function to manually check downloads
112+ // router.get(`/test/download`, async function (req, res, next) {
113+ // // res.sendFile('./test-download.html')
114+ // const ppath = __dirname.split(path.sep).slice(0, -1).join(path.sep)
115+ // res.sendFile(path.join(ppath + '/test/test-download.html'))
116+ // })
117+ /**
118+ *
119+ */
120+
121+ DOWNLOAD_FORMATS_SUPPORTED = [ 'json' , 'csv' , 'xlsx' , 'ods' ]
122+
123+ router . post ( `/${ APP_VERSION } /download` , async function ( req , res , next ) {
124+ console . log ( ' Download CALLED' )
125+ // get the graphql query from body
126+ const query = req . body . query ? req . body . query : req . body
127+ // call GraphQL
128+ try {
129+ // TODO check graphql syntax BEFORE sending it
130+ const gqlRes = await request ( `${ process . env . HASURA_URL } /v1/graphql` , query )
131+
132+ // // capture graphql response
133+ const ext = ( req . params . format || req . query . format || 'json' )
134+ . toLowerCase ( )
135+ . trim ( )
136+ if ( ! DOWNLOAD_FORMATS_SUPPORTED . includes ( ext ) ) {
137+ res
138+ . status ( 400 )
139+ . send (
140+ 'Bad format. Supported Formats: ' +
141+ JSON . stringify ( DOWNLOAD_FORMATS_SUPPORTED )
142+ )
143+ }
144+ const colSep = ( req . query . field_separator || ',' ) . trim ( )
145+ res . set (
146+ 'Content-Disposition' ,
147+ 'attachment; filename="download.' + ext + '";'
148+ )
149+ if ( ext != 'json' ) {
150+ // any spreadsheet supported by [js-xlsx](https://github.com/SheetJS/sheetjs)
151+ let wb = XLSX . utils . book_new ( )
152+ // TODO control the column/field order:
153+ // https://stackoverflow.com/questions/56854160/sort-and-filter-columns-with-xlsx-js-after-json-to-sheet
154+ // https://github.com/SheetJS/sheetjs/issues/738
155+ // it needs to receive the header parameter with the desired column order
156+
157+ //iterate over the result sets and create a work sheet to append to the book
158+ Object . keys ( gqlRes ) . map ( ( k ) => {
159+ const ws = XLSX . utils . json_to_sheet ( gqlRes [ k ] )
160+ XLSX . utils . book_append_sheet ( wb , ws , k )
161+ } )
162+ if ( ext === 'csv' && colSep != ',' ) {
163+ res . set ( 'Content-Type' , 'text/csv' )
164+ // only send the first sheet
165+ const sname = wb . SheetNames [ 0 ]
166+ const ws = wb . Sheets [ sname ]
167+ // TODO deal with record separator
168+ // const recSep = (req.query.record_separator || undefined).trim() // req.params.field_separator ||
169+ // const csv = XLSX.utils.sheet_to_csv(ws, { FS: colSep, RS: recSep })
170+ const csv = XLSX . utils . sheet_to_csv ( ws , { FS : colSep } )
171+ const readable = Readable . from ( csv , { encoding : 'utf8' } )
172+ for await ( const chunk of readable ) {
173+ if ( ! res . write ( chunk ) ) {
174+ // Handle backpressure
175+ await once ( res , 'drain' )
176+ }
177+ }
178+ res . end ( )
179+ } else {
180+ if ( ext === 'csv' ) {
181+ res . set ( 'Content-Type' , 'text/csv' )
182+ } else {
183+ res . set (
184+ 'Content-Type' ,
185+ 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
186+ )
187+ }
188+ res . end ( XLSX . write ( wb , { type : 'buffer' , bookType : ext } ) )
189+ }
190+ } else {
191+ // pure JSON, GraphQL already returns us that
192+ // Examples and docs here: https://nodesource.com/blog/understanding-streams-in-nodejs/
193+ // is json format, need to convert it to stream type it and stream it back to the client
194+ res . set ( 'Content-Type' , 'application/json' )
195+ const readable = Readable . from ( JSON . stringify ( gqlRes ) , {
196+ encoding : 'utf8' ,
197+ } )
198+ for await ( const chunk of readable ) {
199+ if ( ! res . write ( chunk ) ) {
200+ await once ( res , 'drain' )
201+ }
202+ }
203+ res . end ( )
204+ }
205+ } catch ( e ) {
206+ console . error ( 'Error during graphql call' , e )
207+ res . status ( 500 ) . end ( )
208+ }
209+ } )
210+
113211module . exports = router
0 commit comments