Implementing request.postAsync promise with bluebird - node.js

I am using the bluebird promises framework to make a POST request and get the response to that POST request:
var Promise = require('bluebird');
var request = Promise.promisifyAll(require('request'));
// Set the headers
var headers = {
'User-Agent': 'Super Agent/0.0.1',
'Content-Type': 'application/x-www-form-urlencoded'
}
var options = [];
var scores = [];
// Configure the request
options[0] = {
url: 'https://api.havenondemand.com/1/api/sync/analyzesentiment/v1',
method: 'POST',
headers: headers,
form: {'apikey': 'XXXXXXXXXXX', 'text': 'I love dogs'}
}
// Start the request
request.postAsync(options[0]).spread(function(response, body) {
if (response.statusCode == 200) {
var answer = JSON.parse(body);
scores[0] = answer['aggregate']['score'];
}
}).then(function() { console.log(scores[0]) });
This is the error message that I am getting:
Unhandled rejection TypeError: expecting an array or an iterable object but got [object Null]
at apiRejection (/Users/vphuvan/demos/node_modules/bluebird/js/release/promise.js:10:27)
etc.
What do I have to do to resolve this error message?
Note: the version of bluebird I am currently using is 3.0.5

You need to set multiArgs: true in bluebird 3. This is one of the changes in the promisify API of bluebird 3.
Full solution below.
var Promise = require('bluebird');
var request = Promise.promisifyAll(require('request'), { multiArgs: true });
// Set the headers
var headers = {
'User-Agent': 'Super Agent/0.0.1',
'Content-Type': 'application/x-www-form-urlencoded'
}
var options = [];
var scores = [];
// Configure the request
options[0] = {
url: 'https://api.havenondemand.com/1/api/sync/analyzesentiment/v1',
method: 'POST',
headers: headers,
form: {'apikey': 'XXXXXXXXXXX', 'text': 'I love dogs'}
}
// Start the request
request.postAsync(options[0]).spread(function(response, body) {
if (response.statusCode == 200) {
var answer = JSON.parse(body);
scores[0] = answer['aggregate']['score'];
}
}).then(function() { console.log(scores[0]) });

Here is an answer that works: use the 'request-promise' framework. I am using request-promise#1.0.2, which is based on bluebird#2.10.2. Clearly, something undocumented happened between bluebird#2.10.2 and bluebird#3.0.5
var rp = require('request-promise');
// Set the headers
var headers = {
'User-Agent': 'Super Agent/0.0.1',
'Content-Type': 'application/x-www-form-urlencoded'
}
var options = [];
var scores = [];
var text = 'I love dogs';
// Configure the request
options[0] = {
url: 'https://api.havenondemand.com/1/api/sync/analyzesentiment/v1',
method: 'POST',
headers: headers,
form: {'apikey': 'XXXXXXXXXX', 'text': text}
}
// Start the request
rp(options[0])
.then(function (body) {
// POST succeeded...
console.log(body);
var answer = JSON.parse(body);
scores[0] = answer['aggregate']['score'];
})
.then(function() { console.log(scores[0]); })
.catch(function (err) {
throw err
});

Related

nodejs modio api "add modfile" failing to upload

Im trying to upload modfiles with the api but it keeps saying that Im not including filedata. Ive tried with fetch like in the docs but it just gives the same error. If I try http it just gives a list of the files as if it was a GET request.
var zip = `./mod.zip`; // it exists!
var body = {
//filedata: `#${zip}`,
filedata: fs.readFileSync(zip, `binary`),
//filehash: crypto.createHash('md5').update(fs.readFileSync(zip, `binary`)).digest('hex'),
//version: version,
//active: active,
//changelog: changelog,
//metadata_blob: meta,
};
var res = await fetch(`https://api.mod.io/v1/games/${config.modio.gameid}/mods/${config.modio.modid}/files`, { // actually HTTP
method: 'POST',
headers: {
'Authorization': `Bearer ${config.modio.token}`,
'Content-Type': 'multipart/form-data',
'Accept': 'application/json',
},
body: JSON.stringify(body),
});
//console.log(body.filedata);
res = await res.json();
if (res.error)
console.log(res.error);
else
console.log(res);
{
"error": {
"code": 422,
"error_ref": 13009,
"message": "Validation Failed. Please see below to fix invalid input:",
"errors": {
"filedata": "The filedata field is required when upload id is not present.",
"upload_id": "The upload id field is required when filedata is not present."
}
}
}
Yes, ive submitted a bug report to them already, twice. Now they are ghosting me. (I'll probably submit a link to this as well)
They replied!
Hi
As mentioned, you would need to send data using FormData, not JSON.
You can find details at https://developer.mozilla.org/en-US/docs/Web/API/FormData/Using_FormData_Objects
Thanks,
Danny King
Developer Relations Specialist
So, I remade my code
const https = require(`https`);
var crypto = require('crypto');
var FormData = require('form-data');
function ObjectToForm(obj = {}) {
var form = new FormData();
Object.keys(obj).forEach(key => {
var val = obj[key];
switch (typeof val) {
case `boolean`:
val = String(val);
break;
}
form.append(key, val);
});
return form;
}
if (fs.statSync(zip).size > 5368709120) return consolelog(`Zip bigger then 5gb`);
var body = {
filedata: fs.createReadStream(zip),
filehash: crypto.createHash('md5').update(fs.readFileSync(zip)).digest('hex'),
version: version,
active: active,
changelog: changelog,
metadata_blob: meta,
};
var form = ObjectToForm(body);
var options = {
hostname: 'api.mod.io',
port: 443,
path: `/v1/games/${config.modio.gameid}/mods/${config.modio.modid}/files`,
method: 'POST',
headers: {
'Authorization': `Bearer ${config.modio.token}`,
...form.getHeaders(),
'Accept': 'application/json',
},
};
var req = https.request(options, (res) => {
var data = [];
res.on('data', (d) => data.push(d));
req.on(`close`, () => {
var buffer = Buffer.concat(data);
var resp = JSON.parse(buffer.toString());
if (resp.error)
console.log(resp.error);
else if (res.statusCode == 201)
r(true);
});
});
req.on('error', (e) => {
console.log(`Error publishing:`);
console.log(e);
r(e);
});
form.pipe(req)
.on(`close`, () => req.end());
And it worked. Thanks King.

Node Js get request in for loop and collect responses in an object

nodejs / meteorjs newbie here,
I am trying to query an api in for loop and collect all responses in one object and return that object. here is my code set up
const req = require('request');
const getallJira = (namespace, services) => {
let allJiraResponses = {};
for (let i in services) {
let _serviceName = services[i];
const options = {
method: 'POST',
url: 'https://jira/jira/rest/api/2/search',
headers: {
'Content-Type': 'application/json',
Authorization: base64Auth,
Accept: 'application/json',
},
body: JSON.stringify({
jql: `NAMESPACE = ${namespace} AND labels = 'kind/promotion' AND (SERVICE_NAME = '${_serviceName}' OR 'Service Name/Package Name' = '${_serviceName}')`,
maxResults: 1000000,
startAt: 0,
fields,
}),
};
request(options, function(error, response) {
if (error) throw new Error(error);
const jiraResponse = JSON.parse(response.body);
allJiraResponses[_serviceName] = jiraResponse;
});
}
return allJiraResponses;
};
however the final allJiraResponses is returned empty because of the asynchronous nature of request. How do i record all the responses from jira API in the object and return it to the caller of getallJira
I think you should separate into function to make it clearer
the first one to get the result for a single service
const getJiraService = ( namespace, serviceName) => new Promise((resolve, reject) => {
const options = {
method: 'POST',
url: 'https://jira/jira/rest/api/2/search',
headers: {
'Content-Type': 'application/json',
Authorization: base64Auth,
Accept: 'application/json',
},
body: JSON.stringify({
jql: `NAMESPACE = ${namespace} AND labels = 'kind/promotion' AND (SERVICE_NAME = '${serviceName}' OR 'Service Name/Package Name' = '${serviceName}')`,
maxResults: 1000000,
startAt: 0,
fields,
}),
};
request(options, function(error, response) {
if (error) {
resolve({serviceName, error});
return;
}
const jiraResponse = JSON.parse(response.body);
resolve({serviceName, jiraResponse})
});
})
const getallJira = async (namespace, services) => {
const results = await Promise.all(services.map(s => getJiraService(namespace, s));
return results.reduce((res, {serviceName, error, jiraResponse}) => {
if(!!jiraResponse){
return {
...res,
serviceName: jiraResponse
};
}
return res;
}, {})

Node.js 3rd party REST API call

Im using nodejs to make a call to a 3rd party API. My code code returns the correct data for an id that I'm passing in my backend. When I run my app, to retrieve the data I go to localhost:5000/api/Dls.
My code
app.get("/api/Dls", (req, res) => {
const response = {
success: false
};
if (req.user && Authorized.myToken) {
response.success = true;
response.data = {};
response.data.user = req.user;
const id = response.data.user.sub;
var options = {
method: 'GET',
url: 'https://someApi/byId/' + 'id',
headers:
{
Accept: 'application/json',
Authorization: 'Bearer' + ' ' + Authorized.myToken
}
};
request(options, function (error, response, body){
if (error) {
console.log(error);
return;
}
const data = response.body;
const userDls = JSON.parse(data)
return res.json(userDls);
});
}
});
Now I'm trying to do something like this localhost:5000/api/Dls/1234 instead of using a hard coded id in the backend
I attempted doing the following but when I enter a valid id in the url (ex. localhost:5000/api/Dls/1234) I get this "", any idea to what I should be doing?
app.get("/api/Dls/:id", (req, res) => {
const response = {
success: false
};
if (Authorized.myToken) {
response.success = true;
var options = {
method: 'GET',
url: 'https://someApi/byId/',
headers:
{
Accept: 'application/json',
Authorization: 'Bearer' + ' ' + Authorized.myToken
}
};
request(options, function (error, response, body){
if (error) {
console.log(error);
return;
}
const data = response.body;
const userDls = JSON.parse(data)
return res.json(userDls);
});
}
});
Any feedback would be appreciated!
You are not passing the route id to the api.
response.success = true;
var options = {
method: 'GET',
url: 'https://someApi/byId/' + req.params.id,
headers:{
Accept: 'application/json',
Authorization: 'Bearer' + ' ' + Authorized.myToken
}
};

TypeError: Cannot read property 'name' of null NodeJs

I am learning and experimenting on NodeJs. I am using request-promise to call another api from NodeJs. I am using form-data to create a form and send it to another api.
My snippet:
const requestPromise = require('request-promise');
const FormData = require('form-data');
....
var sendToAPI = async (fileObjBuffer, myId, timestamp) => {
let formData = new FormData();
formData.append('fileData', fileObjBuffer);
formData.append('myId', myId);
formData.append('fileName', timestamp);
let options = {
method: 'POST',
uri: '<URL>',
formData: formData,
headers: {
'Access-Control-Allow-Origin': '*',
'enctype': 'multipart/form-data',
'Content-Type': 'application/json',
'Access-Control-Allow-Methods': 'GET,POST,OPTIONS,DELETE,PUT'
},
json: true
};
try {
let apiResult = await requestPromise(options).promise();
console.log('\n\napiResult: ', apiResult);
} catch (error) {
console.log('error in sending to api: ',error);
}
}
var fetchAllData = async () => {
let query = 'select * from demo_db.demo_table;';
let fileObject = "";
var result;
try {
//cassandra query
result = await client.execute(query, [], { prepare: true });
} catch (error) {
console.log('error in fetching data from Cassandra: ',error);
}
result.rows.forEach(resultObj => {
fileObject = fileObject +resultObj['room_id'] +":"+resultObj['message_id']+":"+resultObj['original_message'] +":"+resultObj['send_date'] +":"+ resultObj['sender'] +"%";
});
let fileObjBuffer = new Buffer(fileObject);
let myId = uuidv4();
let timestamp = date.format(new Date(), 'YYYYMMMDDhhmmss', false);
sendToAPI(fileObjBuffer,myId,timestamp);
}
My error:
error in sending to api: TypeError: Cannot read property 'name' of null
at FormData._getContentDisposition (/home/bhushan/NodeJS-Scheduler/node_modules/request/node_modules/form-data/lib/form_data.js:226:40)
at FormData._multiPartHeader (/home/bhushan/NodeJS-Scheduler/node_modules/request/node_modules/form-data/lib/form_data.js:177:33)
at FormData.append (/home/bhushan/NodeJS-Scheduler/node_modules/request/node_modules/form-data/lib/form_data.js:70:21)
at appendFormValue (/home/bhushan/NodeJS-Scheduler/node_modules/request/request.js:326:21)
at Request.init (/home/bhushan/NodeJS-Scheduler/node_modules/request/request.js:337:11)
at Request.RP$initInterceptor [as init] (/home/bhushan/NodeJS-Scheduler/node_modules/request-promise-core/configure/request2.js:45:29)
at new Request (/home/bhushan/NodeJS-Scheduler/node_modules/request/request.js:127:8)
at request (/home/bhushan/NodeJS-Scheduler/node_modules/request/index.js:53:10)
at sendToAPI (/home/bhushan/NodeJS-Scheduler/schedulerTest.js:52:25)
at fetchAllData (/home/bhushan/NodeJS-Scheduler/schedulerTest.js:95:2)
at process._tickCallback (internal/process/next_tick.js:68:7)
Please help me to solve this issue.
request-promise internally handles form-data. Therefore no need to use form-data explicitly. Instead I made normal object in following way:
var formData = {
fileData: {
value: fileObjBuffer,
filename: timestamp
},
fileName: timestamp,
myId: myId
}
This issue arises because in the formData key of requestPromise method you do not need to actually pass formData but an object.
request-promise internally creates formData from the object passed.
let formData = {
fileData: fileObjBuffer,
fileName: timestamp,
myId: myId
}
let options = {
method: 'POST',
uri: '<URL>',
formData: formData, // formData here is an object
headers: {
'Access-Control-Allow-Origin': '*',
'enctype': 'multipart/form-data',
'Content-Type': 'application/json',
'Access-Control-Allow-Methods': 'GET,POST,OPTIONS,DELETE,PUT'
},
json: true
};
I did not find any good solution. So, I have added a function which will test if the value is empty or not. if it is empty then don't add it into the formData object else add it.
const FormData = z.require('form-data');
const form = new FormData();
const addFormData = (key, value) => {
// If value is an array
if(Array.isArray(value) && value.length > 0) {
form.append(key, JSON.stringify(value));
return;
}
// If value is string or number
if(value) {
form.append(key, value);
}
// TODO: You can add aditional checks if you would like to verify if it is non-empty object etc
}
addFormData('name', 'Rahul')
addFormData('age', 28)

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);
});

Resources