How to add a file attachment via NodeJS REST request - node.js

There's some documentation on the Jira site on how to add an attachment to an issue via a curl request here: https://confluence.atlassian.com/display/JIRAKB/How+to+attach+an+attachment+in+a+JIRA+issue+using+REST+API
This is the code that I used to successfully create an issue:
var request = require("request");
var auth = "Basic " + new Buffer("user:password").toString("base64");
var options = {
uri: 'http://domain.com/rest/api/2/issue/',
headers : {
"Authorization" : auth
},
method: 'POST',
json: {
"fields": {
"project": {
"id": "10000"
},
"summary": summary,
"description": description,
"issuetype": {
"name": "Bug"
},
"customfield_10003": {"value": value}
}
}
};
request(options, function (error, response, body) {
if (!error) {
console.log("Success");
}
});
So in order to add an attachment to a ticket with the ID of 1200, I would think I would do something like this:
var options = {
uri: 'http://domain.com/rest/api/2/issue/1200/attachment/',
headers : {
"Authorization" : auth,
"X-Atlassian-Token" : nocheck
},
method: 'POST',
json: {
"fields": {
"file" : "filename.txt"
}
}
};
But have had no luck.
Edit: Getting somewhere. Here's what I've got:
var request = require('request');
var fs = require("fs");
var auth = "Basic " + new Buffer("user:password").toString("base64");
var formData = {
file: {
value: fs.createReadStream('file.txt'),
options: {
filename: 'file.txt',
contentType: 'text/plain'
}
}
};
request.post({
url:'http://domain.com/rest/api/2/issue/14000/attachments/',
headers : {
"Authorization" : auth,
"X-Atlassian-Token" : "nocheck"
},
formData: formData
}, function optionalCallback(err, httpResponse, body) {
if (err) {
return console.error('upload failed:', err);
}
console.log('Upload successful! Server responded with:', body);
});
And it uploads a file called file.txt but when I look at the attachment it prints out a stack trace that starts out like this:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><status><status-code>500</status-code><stack-trace>java.lang.NullPointerException
at com.atlassian.plugins.rest.common.security.jersey.XsrfResourceFilter.mediaTypeToString(XsrfResourceFilter.java:91)
at com.atlassian.plugins.rest.common.security.jersey.XsrfResourceFilter.isXsrfable(XsrfResourceFilter.java:76)
at com.atlassian.plugins.rest.common.security.jersey.XsrfResourceFilter.filter(XsrfResourceFilter.java:54)
at com.sun.jersey.server.impl.uri.rules.HttpMethodRule.accept(HttpMethodRule.java:277)
at com.sun.jersey.server.impl.uri.rules.ResourceClassRule.accept(ResourceClassRule.java:108)
at com.sun.jersey.server.impl.uri.rules.RightHandPathRule.accept(RightHandPathRule.java:147)
at com.sun.jersey.server.impl.uri.rules.RootResourceClassesRule.accept(RootResourceClassesRule.java:84)
at com.sun.jersey.server.impl.application.WebApplicationImpl._handleRequest(WebApplicationImpl.java:1469)
at com.sun.jersey.server.impl.application.WebApplicationImpl._handleRequest(WebApplicationImpl.java:1400)
at com.sun.jersey.server.impl.application.WebApplicationImpl.handleRequest(WebApplicationImpl.java:1349)
at com.sun.jersey.server.impl.application.WebApplicationImpl.handleRequest(WebApplicationImpl.java:1339)
at com.sun.jersey.spi.container.servlet.WebComponent.service(WebComponent.java:416)
at com.sun.jersey.spi.container.servlet.ServletContainer.service(ServletContainer.java:537)

You first try:
var options = {
uri: 'http://domain.com/rest/api/2/issue/1200/attachment/',
headers : {
"Authorization" : auth,
"X-Atlassian-Token" : nocheck
},
method: 'POST',
json: {
"fields": {
"file" : "filename.txt"
}
}
};
will not work because as it's not supported. Check Jira attachment documentation
So, probably you need to restructure your formData object to be
var formData = {
file: fs.createReadStream('file.txt'),
};
No need for editing meta data if you don't need them. Also make sure you've got a valid stream of your file.
About stacktrace, I see stack frames related to XSRF despite adding "X-Atlassian-Token" header which doesn't make any sense for me.

Related

POST request with JSON payload with NodeJS

I'm trying to hit the Intercom API to retrieve a list of conversations and I can't figure out what's wrong. Here is the code:
const request=require('request')
const search_intercom=(admin_id, callback) => {
const options = {
url: 'https://api.intercom.io/conversations/search',
method: 'POST',
headers: {
Authorization: 'Bearer <token>'
},
json: {
query: JSON.stringify({
"field": "teammate_ids",
"operator": "=",
"value": admin_id
})
}
};
request(options, (error, {body} = {}) => {
if (error) {
callback('unable to connect to intercom API', undefined)
} else if (body.length === 0) {
callback('something went wrong', undefined)
} else {
callback(undefined, {
conversation_id: body.conversations[0].id,
client_name: body.conversations[0].source.author.name
})
console.log(body)
}
})
}
module.exports = search_intercom
I was able to wire it up correctly with the web server, so when I debug, options.json.query.admin_id does contain a valid id.
It breaks and says
conversation_id: body.conversations[0].id,
TypeError: Cannot read property '0' of undefined
Here is the content of the body response:
{
type: 'error.list',
request_id: '<some request_id>',
errors: [ { code: 'server_error', message: 'Server Error' } ]
}
Where should I look? I've tried a few different variations of options for sending the payload and I am guessing this is the issue, but I can't find the winning formula...
It looks like I got the body all wrong.
options should look like this instead:
const options = {
url: 'https://api.intercom.io/conversations/search',
method: 'POST',
headers: {
Authorization: 'Bearer <token>'
},
json: true,
body: {
query: {
"field": "teammate_ids",
"operator": "=",
"value": JSON.stringify(admin_id)
}
}
};

How can i assign the access token to the accessToken property in the constructor

I need help in assigning the accessToken property in MyApi class. it seems that the accessToken is not being assigned properly in my constructor. I want to store the access token in the accessToken property so that i use it in the other methods. unfortunately its not being stored properly.
If i place the statement for assigning outside of the request object, the accessToken property is being assigned and i can access it from other methids using this.accessToken. the access token comes in through the request's callback where i extract it from the body. the problem is that it is not being stored properly into the accessToken property.
var request = require("request");
class MyApi {
constructor() {
request({ url: "URL", headers: { Authorization: "Basic KEY" } }, function(
error,
response,
body
) {
if (error) {
console.log(error);
} else {
// this console log statement logs the access token
console.log(`Access Token : ${JSON.parse(body).access_token}`);
// i try to set it into the accessToken property
this.accessToken = JSON.parse(body).access_token;
// This statement logs undefined, meaning my accessToken property is not beinga ssiged successfully
console.log(`Access Token Two: ${this.accessToken}`);
}
});
}
newRequest(callback) {
request(
{
// Also if i try to use the accessToken property here, the request returns an error that the access token is invalid
method: "POST",
url: "URL",
headers: { Authorization: "Bearer " + this.accessToken },
json: { key: "value", key: "value" }
},
function(error, response, body) {
if (error) {
console.log(error);
} else {
callback(JSON.stringify(body));
}
}
);
}
}
module.exports = MyApi;
I want to use the access token in the other methods like the newRequest method to make more requests. someone help i'll appreciate
Addition:
Simplified version:
function request(f) {
f("Token");
}
class MyClass {
constructor() {
request(function(t) {
this.token = t;
}); //.bind(this));
}
getToken() {
return this.token;
}
}
const q = new MyClass();
console.log(q.getToken());
UPD: Try to run simplified version and then check comment.
Just use arrow functions, the problem will be solved. As you are using function, this refers to function reference, not class object reference
var request = require('request');
class MyApi {
constructor() {
request({
url: "URL",
headers: {
"Authorization": "Basic KEY"
}
},
(error, response, body) => {
if (error) {
console.log(error)
} else {
// this console log statement logs the access token
console.log(`Access Token : ${JSON.parse(body).access_token}`)
// i try to set it into the accessToken property
this.accessToken = JSON.parse(body).access_token
// This statement logs undefined, meaning my accessToken property is not beinga ssiged successfully
console.log(`Access Token Two: ${this.accessToken}`)
}
}
)
}
newRequest(callback) {
request({
// now it will work
method: 'POST',
url: "URL",
headers: {
"Authorization": "Bearer " + this.accessToken
},
json: {
"key": "value",
"key": "value"
}
},
(error, response, body) => {
if (error) {
console.log(error);
} else {
;
callback(JSON.stringify(body));
}
}
)
}
}
module.exports = MyApi
Refer this for a good explanation about this

Error 400 on trying to add a contact in mailchimp

I am getting this error when trying to call the mailchimp API V3 to add a new contact to my list
"type":"http://developer.mailchimp.com/documentation/mailchimp/guides/error-glossary/","title":"Invalid Resource","status":400,"detail":"The resource submitted could not be validated. For field-specific details, see the \'errors\' array.","instance":"f8b79d9b-1c02-4062-a0e4-847e31f7bd61","errors":[{"field":"","message":"Schema describes object, NULL found instead"}]}'
Here is the code used:
app.post("/", function(req,res){
var email = req.body.email;
var data = {
members : [
{
email_address: email,
status: "subscribed"
}
]
};
var jsonData = JSON.stringify(data);
var options = {
headers :{
'Authorization': 'thomas APIKEY',
'Content-Type': 'application/json',
'Content-Length': jsonData.length
},
url: "https://us20.api.mailchimp.com/3.0/lists",
method: "POST",
data:jsonData
};
console.log(options);
request(options, function(error, response, body){
if(error)
{
console.log(error);
}
else{
console.log(response);
}
})
I tried adding /Listkey/members to my url with no success
Do you have some tips? Thank you
I found the solution, the elements passed to request were not good.
Here is the functional code:
app.post("/", function(req,res){
var email = req.body.email;
var data = {
"email_address": email,
"status" : "subscribed"
};
var options = {
"url": "https://us20.api.mailchimp.com/3.0/lists/c9b9a6e109/members/",
"method": "POST",
"headers": {
"Authorization": "thomas APIKEY"
},
"body": data,
"json": true
};
})

Nodejs Post attachment to JIRA

I am receiving http POST response OK 200, but I see no file present on JIRA issue. From my research I can understand that it could be some problem with formData I am sending with request. Below is my code:
var newBuffer = new Buffer(req.Payload, 'base64');
var myReadableStreamBuffer = new streamBuffers.ReadableStreamBuffer({
frequency: 10, // in milliseconds.
chunkSize: 2048 // in bytes.
});
// With a buffer
myReadableStreamBuffer.put(newBuffer);
var formData = {
'file': {
'content': myReadableStreamBuffer,
'filename': req.FileName,
'mimeType': req.MimeType //mimeType from JSON
}
};
var options = {
url: 'https://comapny.atlassian.net/rest/api/2/issue/' + req.ReferenceId + '/attachments',
method: "POST",
json: true,
headers: {
'ContentType': 'multipart/form-data',
'Authorization': 'Basic ' + new Buffer(config.jira.jiraUser.userName + ':' + config.jira.jiraUser.password).toString('base64'),
'X-Atlassian-Token': 'nocheck'
},
formData: JSON.stringify(formData)
};
request(options,
function (error, response, body) {
if (error) {
errorlog.error(`Error Message : PostAttachmentToCSMS : ${error}`);
return response.statusCode;
}
else {
successlog.info(`Attachment posted for issue Key: ${req.ReferenceId} ${response.statusMessage}`);
return response.statusCode;
}
});
I can write file from myReadableStreamBuffer, so that seems ok. Please help me to identify the problem. Many thanks!
After spending some more time on it, I have found the correct format for formData:
var newBuffer = new Buffer(req.Payload, 'base64');
var formData = {
file: {
value: newBuffer,
options: {
filename: req.FileName,
contentType: req.MimeType
}
}
};
For whom like me getting errors with this API.
After struggling so many hrs on this thing, I finally found this works like a charm. I've got "XSRF check failed" 403/404 error message before writing this code.
// Don't change the structure of formData.
const formData = {
file: {
value: fs.createReadStream(filepath),
options: {
filename: filename,
contentType: "multipart/form-data"
}
}
};
const header = {
"Authentication": "Basic xxx",
// ** IMPORTANT **
// "Use of the 'nocheck' value for X-Atlassian-Token
// has been deprecated since rest 3.0.0.
// Please use a value of 'no-check' instead."
"X-Atlassian-Token": "no-check",
"Content-Type": "multipart/form-data"
}
const options = {
url: "http://[your_jira_server]/rest/api/2/issue/[issueId]/attachments",
headers: header,
method: "POST",
formData: formData
};
const req = request(options, function(err, httpResponse, body) {
whatever_you_want;
};
I was able to post attachments to JIRA using axios in the following way:
const axios = require('axios');
const FormData = require('form-data')
const fs = require('fs');
const url = 'http://[your_jira_server]/rest/api/2/issue/[issueId]/attachments';
let data = new FormData();
data.append('file', fs.createReadStream('put image path here'));
var config = {
method: 'post',
url: url,
headers: {
'X-Atlassian-Token': 'no-check',
'Authorization': 'Basic',
...data.getHeaders()
},
data: data,
auth: {
username: '',
password: ''
}
};
axios(config)
.then(function (response) {
res.send({
JSON.stringify(response.data, 0, 2)
});
})
.catch(function (error) {
console.log(error);
});

Passing array or nested objects in body of request npm

I have to send some parameters of String type along with one array in my body.
But it throws me an error message:
First argument must be String or buffer
Here is my code:
var tokenList = JSON.parse(req.body.tokenList);
var mobParams = {
"tokens": tokenList,
"profile": "<myprofile>",
"notification": {
"title": req.body.title,
"message": req.body.text
}
};
request({
method: "POST",
url: 'https://api.ionic.io/push/notifications',
headers: {
"content-type": "application/json",
"authorization": "Bearer ********"
},
body: (mobParams)
}, function(error, response, body){
console.log('Ionic push error', error);
console.log('IOnic push res', response);
console.log('IOnic push body', body);
if(!error){
return res.send({
code: 1,
message: "success"
});
}else{
return res.send({
code: 0,
message: error
});
}
How can I pass my array inside this object to request npm?
Also, I would like to add that this implementation works pretty fine through front-end but I have separate codebase which requires me to hit multiple FCM requests i.e. in a loop. So I would be happy to have a solution as neither ionic push nor FCM push works
For the FCM push I am trying the code below :
let desktopParams = {
"notification": {
"title": 'Merchant Portal Notifications',
"body": req.body.text
// "click_action" : action
},
"to": '/topics/' + topic
};
request({
method: "POST",
json: true,
url: 'https://fcm.googleapis.com/fcm/send',
headers: {
"content-type": "application/json",
"authorization": "key=****"
},
body: desktopParams
}, function(error, response, body){
console.log('error', error);
console.log('response', response);
console.log('body', body);
//return body;
});
you should try stringifying your tokenList & req.body.text before you join it with with the string (try to log it and post the outcome so that people will have a better idea about the objects...):
var cho = [{name:'john',lname:'cena'},{name:'mary',lname:'jane'}];
var che = {list:[{name:'john',lname:'cena'},{name:'mary',lname:'jane'}],group:'people'}
var mobParams = {
"tokens":JSON.parse(JSON.stringify(cho)),
"profile": "<myprofile>",
"notification": {
"title": "Some title",
"message":JSON.parse(JSON.stringify(che))
}
};
console.log(JSON.stringify(mobParams));//----->{"tokens":[{"name":"john","lname":"cena"},{"name":"mary","lname":"jane"}],"profile":"<myprofile>","notification":{"title":"Some title","message":{"list":[{"name":"john","lname":"cena"},{"name":"mary","lname":"jane"}],"group":"people"}}}
var arr = [{name:'john',lname:'cena'},{name:'mary',lname:'jane'}]
var js = JSON.stringify(arr);
var blabla = {'item':'something',js}
console.log(blabla); //-----> Object {item: "something", js: "[{"name":"john","lname":"cena"},{"name":"mary","lname":"jane"}]"}
var js = JSON.parse(JSON.stringify(arr));
var blabla = {'item':'something',js}
console.log(blabla); //-----> Object {item: "something", js: Array(2)}
var js = JSON.parse(arr);
var blabla = {'item':'something',js}
console.log(blabla); //-----> "SyntaxError: Unexpected identifier"

Resources