Send attachment from in memory file

Hi,

We are trying to send a reply to a ticket that includes an attachment:

https://oursubdomain.freshdesk.com/api/v2/tickets/{ticketID}/reply

The example here: fresh-samples/create_ticket_custom_fields_and_attachements.js at master · freshworks/fresh-samples · GitHub shows the file being attached to the request using fs.createReadStream. Our file is stored in memory so we have tried to swap out the fs.createReadStream approach to use the equivalent for in memory files. This approach takes the file buffer and uses the Readable method from nodes stream api to create the stream:

    var tmp = require("tmp"); 
    const {Readable} = require('stream');

    function bufferToStream(myBuffer) {
        let stream = new Readable();
        stream.push(myBuffer);
        stream.push(null);
        return stream;
    }

    var unirest = require('unirest');
    var fs = require('fs');
    exports.handler = async (event, context, callback) => {
    var API_KEY = "ourkey";
    var FD_ENDPOINT = "ourdomain";

var PATH = "/api/v2/tickets";
var enocoding_method = "base64";
var auth = `Basic ${Buffer.from(`${API_KEY}`).toString('base64')}`;
const ticketId = '79';
var URL =  `https://${FD_ENDPOINT}.freshdesk.com/api/v2/tickets/${ticketId}/reply`;

const base64String = fs.readFileSync('base64.txt', 'utf8');
const binaryBuffer = Buffer.from(base64String, 'base64');

// create the stream from the buffer here
const stream = bufferToStream(binaryBuffer);

var fields = {
  body: 'test',
}

var headers = {
  'Authorization': auth
}
// fails with  [ERR_HTTP_INVALID_HEADER_VALUE]: Invalid value "undefined" for header "Content-Length"
//caused by  unirest are using the NPM module form-data, and the form-data module requires that if it received a stream that not fs.ReadStream
//https://github.com/Kong/unirest-nodejs/issues/126
  unirest.post(URL)
  .headers(headers)
  .field(fields)
  .attach('attachments[]', stream)
  .end(function(response){
    console.log(response.body)
    console.log("Response Status : " + response.status)
    if(response.status == 201){
      console.log("Location Header : "+ response.headers['location'])
    }
    else{
      console.log("X-Request-Id :" + response.headers['x-request-id']);
    }
  });
}

However executing this code results in an error: TypeError [ERR_HTTP_INVALID_HEADER_VALUE]: Invalid value "undefined" for header "Content-Length". This github issue explains why this happens. Passing a Buffer to .attach · Issue #126 · Kong/unirest-nodejs · GitHub

Because the example code could not be altered to work with our in memory files we wrote our own code using the FormData module. This code does create a multipart form and sends it to freshdesk but we receive an error: 502: Bad Gateway “Request failed with status code 502”.

const axios = require('axios');
const FormData = require('form-data');
const fs = require('fs');
const {Readable} = require('stream');

function bufferToStream(myBuffer) {
    let stream = new Readable();
    stream.push(myBuffer);
    stream.push(null);
    return stream;
}

const apiCall = async (url, method, headers, data = {}) => {
  const config = {
    method,
    url,
    headers,
    data,
  };

  const response = await axios(config);
  console.log(response);
  return response;
};

exports.handler = async (event, context, callback) => {
  const domain = 'ourdomain';
  const key = 'ourkey';
  const ticketId = '79';

  const base64String = fs.readFileSync('base64.txt', 'utf8');

  let success = false;

  const url = `https://${domain}.freshdesk.com/api/v2/tickets/${ticketId}/reply`;

  const headers = {
    Authorization: `Basic ${Buffer.from(`${key}`).toString('base64')}`,
  };

  var fields = {
    body: 'test',
  }

  const form = new FormData();
  const binaryBuffer = Buffer.from(base64String, 'base64');
  const stream = bufferToStream(binaryBuffer);

  form.append('attachments[]', stream,  {
    filename: 'base64.txt', 
  });
  
  // form.submit(url, function(err, res) { console.log(err); });
  try {
    // const test = await apiCall(showTicket, 'GET', testHeaders);
    const response = await apiCall(url, 'POST', headers, form);
    console.log(response.data);
  } catch (error) {
    console.log(error.response.data.errors);
    console.log({ success, error: error.message });
  }
};

Can anyone help us with this?

Hey @jamesf,

The code looks like a AWS lambda invocation. Did you try out the request from a local variation of the code? Also shouldn’t you add the multipart/form-data content type header to the request?

2 Likes

Hi,

Thanks for your reply. The second code snippet is indeed an aws lambda function. We are executing the lambda locally as a normal function so I don’t think this is relevant to the problem.

Regarding the multipart/form-data comment. The example uses FormData (FormData - Web APIs | MDN) which encodes the data as a multipart form. I have tried manually including the header and this does not work either. We see the same error.

1 Like