Get attachment and send it to third party API

Hello, guys!

I’m building an integration with RTC using their OSLC-CM REST API and the auth is form based! :bomb::bomb::bomb:
When a ticket is created, I need to create a work item on RTC, and I need to send the attachments as well.

So there’s three points to consider:

  • Request from FDK can’t handle attachments and I am not sure about form based auth, since it’s needed to keep cookies to pass along in requests.
  • FDK blocks file stream.
  • RTC server is running on a Self Signed Certificate

Having it in mind, I’m trying to use the libs request and unirest. With both, setting the config to ignore invalid SSL, since self signed is considered invalid, I was able to log in, post and get work items, but I’m having issues with attachments in both.

Using request lib, I was able to get the file and send it to RTC, but when I download it back, it is corrupted. I think that’s because when I get the file, I’m not reading it on a file stream, I’m just sending the stream returned. Here’s the code: (I’ll omit the log in part, since it would too much code)

var request = require("request");
var async = require("async");
var cookies = request.jar();
var baseRequest = request.defaults({
    headers: {
        "X-Requested-With": "XMLHttpRequest",
        Accept: "application/json",
    },
    strictSSL: false,
    jar: cookies,
    followAllRedirects: true,

function attachFileToWorkItem(fileURL) {
    async.waterfall([
            (callback) => {
                // login...
            },
            (callback) => {
                // fileURL is a public link from freshdesk attachment
                request.get(fileURL, function(error, response, body) {
                    if (error || response.statusCode !== 200) {
                        console.log(error);
                        if (error) throw new Error(error);
                        return;
                    }
                    callback(null, body);
                });
            },
            (file, callback) => {
                var options = {
                    method: "POST",
                    jar: cookies,
                    url: this.serverURI +
                        "/service/com.ibm.team.workitem.service.internal.rest.IAttachmentRestService/?projectId=" +
                        this.projectAreaId +
                        "&multiple=true",
                    formData: {
                        attach: {
                            value: file,
                            options: {
                                filename: "nicolas.jpg",
                                contentType: "image/jpeg",
                            },
                        },
                    },
                };

                baseRequest(options, function(error, response, body) {
                    console.log(body);
                });
            ])
    }

Output:

{
    "files": [{
        "size": 144670,
        "name": "nicolas.jpg",
        "id": 8,
        "type": "image\/jpeg",
        "uuid": "_BS0WIVy8Eeu1YqJdjMiyfQ",
        "url": "https:\/\/localhost:9443\/ccm\/resource\/itemOid\/com.ibm.team.workitem.Attachment\/_BS0WIVy8Eeu1YqJdjMiyfQ"
    }]
}

The url returned is valid, I download the file, but can’t open, it’s corrupted!

Then I decided to give a try to unirest, since it has the method .attach() that I can just pass a URI and it will do all the magic. But the issue here is with the SSL. As request, it has as well the option to ignore invalid SSL. It works for auth, get and post work item, but when using .attach() it just does not work. Here’s the code:

var unirest = require("unirest");
cookies = unirest.jar();
// log in ommited

var req = unirest(
        "POST",
        "https://localhost:9443/ccm/service/com.ibm.team.workitem.service.internal.rest.IAttachmentRestService?projectId=_Jv0jwFXOEeuKc9Bmrq6cOA&multiple=true"
    )
    .headers({
        "Content-Type": "multipart/form-data"
    })
    .strictSSL(false)
    .jar(cookies)
    .attach(
        "file",
        "https://s3.amazonaws.com/cdn.freshdesk.com/data/helpdesk/attachments/production/8075345095/original/nicolas_cage.jpg?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAS6FNSMY2WD6T3JNC%2F20210120%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20210120T164143Z&X-Amz-Expires=86400&X-Amz-SignedHeaders=host&X-Amz-Signature=78fd8611c6a7c530937631d0f61d0c412c9a4f0d3d0a100eca919fa495e5c775"
    )
    .end(function(res) {
        if (res.error) {
            console.error(res.error);
        } else {
            console.log(res.raw_body);
        }
    });

Output:

{ Error: self signed certificate
    at TLSSocket.onConnectSecure (_tls_wrap.js:1058:34)
    at TLSSocket.emit (events.js:198:13)
    at TLSSocket._finishInit (_tls_wrap.js:636:8) code: 'DEPTH_ZERO_SELF_SIGNED_CERT' }

So do you guys know a way o fix the thing with request or unirest?

OBS: The endpoint for upload attachments is working fine, I was able to upload a file with Postman, everything worked fine!

After focusing on request.js for some time, I found out a solution. Call the download directly into the value attribute in the options object.

But since request.js is deprecated, it would be better make unirest work.
So if someone has some ideia, feel free to share :smiley:

Going to share all code here for request.js, perhaps helps someone in the future:

var async = require("async");
var request = require("request");

var cookies = request.jar();

var baseRequest = request.defaults({
    headers: {
        "X-Requested-With": "XMLHttpRequest",
        Accept: "application/json",
    },
    strictSSL: false,
    jar: cookies,
    followAllRedirects: true,
});

var loginOptions = {
    url: serverURI + "/authenticated/j_security_check",
    method: "POST",
    form: {
        j_username: userName,
        j_password: userPassword
    },
    resolveWithFullResponse: true,
    headers: {
        "Content-Type": "application/x-www-form-urlencoded",
        "OSLC-Core-Version": "2.0",
    },
}


function attachFileToWorkItem(fileURL) {
    async.waterfall([
        (callback) => {
            baseRequest(loginOptions, function(error, response, body) {
                if (error) throw new Error(error);
                callback();
            })
        },

        (callback) => {
            var options = {
                method: "POST",
                jar: cookies,
                url: this.serverURI +
                    "/service/com.ibm.team.workitem.service.internal.rest.IAttachmentRestService/?projectId=" +
                    this.projectAreaId +
                    "&multiple=true",
                formData: {
                    attach: {
                        value: request(fileURL),
                        options: {
                            filename: "nicolas.jpg",
                            contentType: "image/jpeg",
                        },
                    },
                },
            };

            baseRequest(options, function(error, response, body) {
                console.log(error);
                console.log(body);
            });
        }
    ])
}

Response:

null
{
    "files": [{
        "size": 80416,
        "name": "nicolas.jpg",
        "id": 9,
        "type": "image\/jpeg",
        "uuid": "_lmvdkVzwEeu1YqJdjMiyfQ",
        "url": "https:\/\/localhost:9443\/ccm\/resource\/itemOid\/com.ibm.team.workitem.Attachment\/_lmvdkVzwEeu1YqJdjMiyfQ"
    }]
}

Now opening this url response actually shows me the file.

2 Likes

hey @samuelpares,

Although request.js is deprecated there are multiple forks of request js like postman-request which are actively maintained might still be able to do what request.js does

2 Likes

I didn’t know this lib.
It did the job without having to change any code.
Gonna go with this one since it’s actively maintained.
Thanks!

1 Like