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

Hi @prithvi,

Do you have any suggestions as to how we can resolve this or if there is someone we can speak to further regarding the issue?

Thanks

Hi @jamesf,

With the first code example, the file length can be fetched from the stream and attached to the API request options in the header to avoid the issue that you have mentioned. Have you tried it?

Hello,

I am having a very similar issue, have you figured this out?

1 Like

Have a similar issue, cant find any repo on how to handle attachments in the ned request templates (2.3)

Am currenlty stuck due to this

Check this out

The referenced topic does not use the new request templates. May unirest post requests be made using platform version 2.3? It seems like the host must be whitelisted in manifest.json but that feature is scheduled for deprecation this month.

Hello Don,

I have not tested unirest out using the SDK- haven’t had a use case for it yet- however, it is a NodeJS library so I do not see why you wouldn’t be able to include it with your dependencies in your manifest.json file. When using the unirest library in a normal NodeJs file, I did not need to whitelist any hosts/domains.