handling xlsx response from an API call

100 views
Skip to first unread message

Robert Crooks

unread,
Aug 14, 2014, 9:57:57 AM8/14/14
to nod...@googlegroups.com
I have a proxy for several APIs (it handles OAuth authentication for the call) built with simple node.js and request. It just returns all the headers and response body from the API call to the client. One of the apis has an option to return xlsx data instead of JSON. When I return that response to the client, an xlsx file does get downloaded, but it's corrupted somehow, and can't be opened by Excel (or any other spreadsheet app). I think it's some kind of encoding mismatch, but can't figure out how to fix it. Any help appreciated. (I am a node.js newbie by the way.) Here's the proxy code, and I think it's in sendRequest() function that something's going wrong:

    var BCLSPROXY = (function () {
        "use strict";
        var util = require("util"),
            colors = require("colors"),
            http = require("http"),
            request = require("request"),
            aapiToken,
            pmapiToken,
            aapiExpires = 0,
            pmapiExpires = 0,
            aapiServer,
            pmapiServer,
            apiServer, // for other APIs
            // functions
            getFormValues,
            getAAPIAccessToken,
            getPMAPIAccessToken,
            getAccessToken,
            sendRequest;
        /*
         * extract form values from request body
         */
        getFormValues = function (body, callback) {
            // split the request body string into an array
            var valuesArray = body.split("&"),
                options = {},
                max = valuesArray.length,
                i,
                item,
                error = null;
            // initialize options to null except requestType to GET
            options.url = null;
            options.client_id = null;
            options.client_secret = null;
            options.requestBody = null;
            options.requestType = "GET";
            // now split each item into key and value and store in the object
            for (i = 0; i < max; i = i + 1) {
                item = valuesArray[i].split("=");
                options[item[0]] = item[1];
            }
            // data fixes
            // decode the URL
            options.url = decodeURIComponent(options.url);
            // check for required values
            if (options.client_id === null || options.client_secret === null || options.url === null) {
                error = "Error: client_id, client_secret, and url for API request are required!";
            }
            if (error === null) {
                callback(null, options);
            } else {
                callback(error);
            }
        };
        /*
         * get new Analytics API access_token
         */
        getAAPIAccessToken = function (options, callback) {
            // base64 encode the ciient_id:client_secret string for basic auth
            var auth_string = new Buffer(options.client_id + ":" + options.client_secret).toString("base64"),
                bodyObj,
                now = new Date().valueOf();
            if (aapiExpires < now) {
                request({
                    method: 'POST',
                    url: 'https://oauth.brightcove.com/v3/access_token',
                    headers: {
                        "Authorization": "Basic " + auth_string,
                        "Content-Type": "application/x-www-form-urlencoded"
                    },
                    body: 'grant_type=client_credentials'
                }, function (error, response, body) {
                    // check for errors
                    if (error === null) {
                        // return the access token to the callback
                        bodyObj = JSON.parse(body);
                        aapiToken = bodyObj.access_token;
                        aapiExpires = now + (bodyObj.expires_in * 1000);
                        callback(null, aapiToken);
                    } else {
                        callback(error);
                    }
                });
            } else {
                callback(null, aapiToken);
            }
        };
        /*
         * get new Player Management API access_token
         */
        getPMAPIAccessToken = function (options, callback) {
            // base64 encode the ciient_id:client_secret string for basic auth
            var auth_string = new Buffer(options.client_id + ":" + options.client_secret).toString("base64"),
                bodyObj,
                now = new Date().valueOf();
            if (pmapiExpires < now) {
                request({
                    method: 'POST',
                    url: 'https://oauth.brightcove.com/v3/access_token',
                    headers: {
                        "Authorization": "Basic " + auth_string,
                        "Content-Type": "application/x-www-form-urlencoded"
                    },
                    body: 'grant_type=client_credentials'
                }, function (error, response, body) {
                    // check for errors
                    if (error === null) {
                        // return the access token to the callback
                        bodyObj = JSON.parse(body);
                        pmapiToken = bodyObj.access_token;
                        pmapiExpires = now + (bodyObj.expires_in * 1000);
                        callback(null, pmapiToken);
                    } else {
                        callback(error);
                    }
                });
            } else {
                callback(null, pmapiToken);
            }
        };
        /*
         * get new access_token for other APIs
         */
        getAccessToken = function (options, callback) {
            // base64 encode the ciient_id:client_secret string for basic auth
            var auth_string = new Buffer(options.client_id + ":" + options.client_secret).toString("base64"),
                bodyObj;
            // don't know what API was requested, always get new token
            request({
                method: 'POST',
                url: 'https://oauth.brightcove.com/v3/access_token',
                headers: {
                    "Authorization": "Basic " + auth_string,
                    "Content-Type": "application/x-www-form-urlencoded"
                },
                body: 'grant_type=client_credentials'
            }, function (error, response, body) {
                // check for errors
                if (error === null) {
                    // return the access token to the callback
                    bodyObj = JSON.parse(body);
                    callback(null, bodyObj.access_token);
                } else {
                    callback(error);
                }
            });
        };
        /*
         * sends the request to the targeted API
         */
        sendRequest = function (token, options, callback) {
            var requestOptions = {
                    method: options.requestType,
                    url: options.url,
                    headers: {
                        "Authorization": "Bearer " + token,
                        "Content-Type": "application/json"
                    },
                    body: options.requestBody
                };
            request(requestOptions, function (error, response, body) {
                console.log("error", error);
                if (error === null) {
                    callback(null, response.headers, body);
                } else {
                    callback(error);
                }
            });
        };
    /*
     * sends the request to the Analytics API (special case)
     */
    sendRequest = function (token, options, callback) {
        var requestOptions = {
                method: options.requestType,
                url: options.url,
                headers: {
                    "Authorization": "Bearer " + token,
                    "Content-Type": "application/json"
                },
                body: options.requestBody
            };
        request(requestOptions, function (error, response, body) {
            console.log("error", error);
            if (error === null) {
                callback(null, response.headers, body);
            } else {
                callback(error);
            }
        });
    };
        /*
         * Http Server to handle Analytics API requests
         */
        aapiServer = http.createServer(function (req, res) {
            var body = "";
            /* the published version of this proxy accepts requests only from
             * domains that include "brightcove.com"
             * modify the following line to take requests from
             * other domains or remove the if block to
             * accept requests from any domain (not recommended!)
             */
            if (req.headers.origin.indexOf("brightcove.com") < 0) {
                res.writeHead(500);
                res.end("Your request cannot be processed; this proxy only handles requests originating from Brightcove servers. If you would like to build your own version of this proxy, see http://docs.brightcove.com/en/perform/oauth-api/guides/quick-start.html");
            }
            req.on("data", function (chunk) {
                body += chunk;
            });
            req.on("end", function () {
                getFormValues(body, function (error, options) {
                    if (error === null) {
                        getAAPIAccessToken(options, function (error, token) {
                            if (error === null) {
                                sendRequest(token, options, function (error, headers, body) {
                                    if (error === null) {
                                        console.log("headers", headers);
                                        var header;
                                        for (header in headers) {
                                            res.setHeader(header, headers[header]);
                                        }
                                        if (body.indexOf("{") === 0 || options.url.indexOf("format=json") > -1) {
                                            // prettify JSON
                                            body = JSON.stringify(JSON.parse(body), true, 2);
                                        }
                                            res.writeHead(200);
                                            res.end(body);
                                    } else {
                                        res.writeHead(500);
                                        res.end("Your API call was unsuccessful; here is what the server returned: " + error);
                                    }
                                });
                            } else {
                                res.writeHead(500);
                                res.end("There was a problem getting your access token: " + error);
                            }
                        });
                    } else {
                        res.writeHead(500);
                        res.end("There was a problem with your request: " + error);
                    }
                });
            });
        // change the following line to have the proxy listen for requests on a different port
        }).listen(8002);
        /*
         * Http Server to handle Player Management API requests
         */
        pmapiServer = http.createServer(function (req, res) {
            var body = "";
            // the published version of this proxy accepts requests only from domains that include "brightcove.com"
            // modify the following line to take requests from other domains
            // or remove the if block to accept requests from any domain (not recommended!)
            if (req.headers.origin.indexOf("brightcove.com") < 0) {
                res.writeHead(500);
                res.end("Your request cannot be processed; this proxy only handles requests originating from Brightcove servers. If you would like to build your own version of this proxy, see http://docs.brightcove.com/en/perform/oauth-api/guides/quick-start.html");
            }
            req.on("data", function (chunk) {
                body += chunk;
            });
            req.on("end", function () {
                getFormValues(body, function (error, options) {
                    if (error === null) {
                        getPMAPIAccessToken(options, function (error, token) {
                            if (error === null) {
                                sendRequest(token, options, function (error, headers, body) {
                                    if (error === null) {
                                        console.log("headers", headers);
                                        var header;
                                        for (header in headers) {
                                            res.setHeader(header, headers[header]);
                                        }
                                        if (body.indexOf("{") === 0 || options.url.indexOf("format=json") > -1) {
                                            // prettify JSON
                                            body = JSON.stringify(JSON.parse(body), true, 2);
                                        }
                                            res.writeHead(200);
                                            res.end(body);
                                    } else {
                                        res.writeHead(500);
                                        res.end("Your API call was unsuccessful; here is what the server returned: " + error);
                                    }
                                });
                            } else {
                                res.writeHead(500);
                                res.end("There was a problem getting your access token: " + error);
                            }
                        });
                    } else {
                        res.writeHead(500);
                        res.end("There was a problem with your request: " + error);
                    }
                });
            });
        // change the following line to have the proxy listen for requests on a different port
    }).listen(8003);
        /*
         * Http Server to handle other API requests
         */
        apiServer = http.createServer(function (req, res) {
            var body = "";
            // the published version of this proxy accepts requests only from domains that include "brightcove.com"
            // modify the following line to take requests from other domains
            // or remove the if block to accept requests from any domain (not recommended!)
            if (req.headers.origin.indexOf("brightcove.com") < 0) {
                res.writeHead(500);
                res.end("Your request cannot be processed; this proxy only handles requests originating from Brightcove servers. If you would like to build your own version of this proxy, see http://docs.brightcove.com/en/perform/oauth-api/guides/quick-start.html");
            }
            req.on("data", function (chunk) {
                body += chunk;
            });
            req.on("end", function () {
                getFormValues(body, function (error, options) {
                    if (error === null) {
                        getAccessToken(options, function (error, token) {
                            if (error === null) {
                                sendRequest(token, options, function (error, headers, body) {
                                    if (error === null) {
                                        console.log("headers", headers);
                                        var header;
                                        for (header in headers) {
                                            res.setHeader(header, headers[header]);
                                        }
                                        if (body.indexOf("{") === 0 || options.url.indexOf("format=json") > -1) {
                                            // prettify JSON
                                            body = JSON.stringify(JSON.parse(body), true, 2);
                                        }
                                            res.writeHead(200);
                                            res.end(body);
                                    } else {
                                        res.writeHead(500);
                                        res.end("Your API call was unsuccessful; here is what the server returned: " + error);
                                    }
                                });
                            } else {
                                res.writeHead(500);
                                res.end("There was a problem getting your access token: " + error);
                            }
                        });
                    } else {
                        res.writeHead(500);
                        res.end("There was a problem with your request: " + error);
                    }
                });
            });
        // change the following line to have the proxy listen for requests on a different port
    }).listen(8004);
        util.puts("http server for Analytics API ".blue + "started ".green.bold + "on port ".blue + "8002 ".yellow);
        util.puts("http server for Player Management API ".blue + "started ".green.bold + "on port ".blue + "8003 ".yellow);
        util.puts("http server for other APIs ".blue + "started ".green.bold + "on port ".blue + "8004 ".yellow);
    })();

klr...@gmail.com

unread,
Aug 15, 2014, 11:58:30 AM8/15/14
to nod...@googlegroups.com
not sure I understand the question correctly but if the file itself is corrupt try using try something like https://github.com/rubenv/node-xlsx-writer to build the xlsx from jason, I use express framework (or koa) so I download with something simple like (sorry for coffeescript)

...get "/blabladownload", (req,res) ->

 ....

   fs.exists 'blabla.txt', (exists) ->
      if exists
        res.download __dirname + '/blabla.txt', (err) ->   # doesn't care about file type
          throw err if err
          if err
            console.log 'error download eShopDownLoad ',err.stack
            res.send 'Error download eShopDownLoad: '+ err.code

          else  
            fs.unlink __dirname + '/blabla.txt',(err,data) ->
              if err
                console.log 'error unlink eShopDownLoad ',err.stack
                res.send 'Error download eShopDownLoad: '+ err.code
              else
                 do more stuff

seems to work. Hope it helps. K
                  &
...

Robert Crooks

unread,
Aug 17, 2014, 9:35:46 AM8/17/14
to nod...@googlegroups.com
thanks for the response -- I looked as xlsx-writer, but it's not what I need. I don't needed to build the xlsx data -- that's what the API is sending me -- I just need to pass it back to the client without garbling it.

Robert
Reply all
Reply to author
Forward
0 new messages