How to generate a content-length - node.js

I'm using the node module form-data to send multipart/forms to a server, but i'm not sure how to calculate the content-length correctly for the request. The form-data github documentation seems a bit hand-wavy about it and the server i'm sending to requires the content-length header to be included.
const FormData = require('form-data');
const fs = require('fs');
const https = require('https');
class Platform {
_postKey(key, serviceName) {
const options = this._getOptions();
const keyName = (key.search(/(PUBLIC)/) !== -1) ? 'publicKey' : 'privateKey';
const extension = (keyName == 'publicKey') ? 'pub' : '';
const filePath = (extension.length > 0) ? __dirname + '/data/'+serviceName+'.'+extension : __dirname + '/data/'+serviceName;
const file = fs.createWriteStream(filePath, {flags: 'wx'});
file.write(key);
file.end();
const form = new FormData();
form.append('file', fs.createReadStream(filePath));
form.append('Name', keyName);
form.append('MimeMajor', 'application');
form.append('MimeMinor', 'x-pem-file');
form.append('Extension', extension);
form.append('FileClass', 'MFS::File');
options.headers = form.getHeaders();
options.headers.Authorization = 'Basic ' + Buffer.from(this.FILESTORE_USERNAME + ':' + this.FILESTORE_PASSWORD).toString('base64');
// options.headers['Content-Length'] = key.length;
console.log(options);
return new Promise((resolve, reject) => {
let post = https.request(options, (res) => {
let data = '';
if (res.statusCode < 200 || res.statusCode > 299) {
reject(new Error('File Storage API returned a status code outside of acceptable range: ' + res.statusCode));
} else {
res.setEncoding('utf8');
res.on('data', (chunk) => {
data += chunk;
});
res.on('error', (err) => {
reject(err);
});
res.on('end', () => {
console.log(data);
if (data) {
resolve(JSON.parse(data));
} else {
resolve();
}
});
}
});
post.on('error', reject);
form.pipe(post);
});
}
}

Related

How to send back the data got from response.on('end') to the client-side

I'm new to NodeJs and I'm having the problem with response.on('end') I still can't find out the method to send the data I got from the response to the client side.
exports.getCheckoutSession = catchAsync(async (req, res, next) => {
const uuidv1 = require('uuid/v1');
const https = require('https');
const tour = await Tour.findById(req.params.tourId);
console.log(tour);
//parameters send to MoMo get get payUrl
var endpoint = 'https://test-payment.momo.vn/gw_payment/transactionProcessor';
var hostname = 'https://test-payment.momo.vn';
var path = '/gw_payment/transactionProcessor';
var partnerCode = 'MOMO';
var accessKey = 'accessKey';
var serectkey = 'secretKey';
var orderInfo = 'pay with MoMo';
var returnUrl = 'https://momo.vn/return';
var notifyurl = 'https://callback.url/notify';
var amount = (tour.price * 23000).toString();
console.log(amount);
var orderId = req.params.tourId;
var requestId = req.params.tourId;
var requestType = 'captureMoMoWallet';
var extraData = 'merchantName=;merchantId='; //pass empty value if your merchant does not have stores else merchantName=[storeName]; merchantId=[storeId] to identify a transaction map with a physical store
//before sign HMAC SHA256 with format
//partnerCode=$partnerCode&accessKey=$accessKey&requestId=$requestId&amount=$amount&orderId=$oderId&orderInfo=$orderInfo&returnUrl=$returnUrl&notifyUrl=$notifyUrl&extraData=$extraData
var rawSignature =
'partnerCode=' +
partnerCode +
'&accessKey=' +
accessKey +
'&requestId=' +
requestId +
'&amount=' +
amount +
'&orderId=' +
orderId +
'&orderInfo=' +
orderInfo +
'&returnUrl=' +
returnUrl +
'&notifyUrl=' +
notifyurl +
'&extraData=' +
extraData;
//puts raw signature
console.log('--------------------RAW SIGNATURE----------------');
console.log(rawSignature);
//signature
const crypto = require('crypto');
var signature = crypto
.createHmac('sha256', serectkey)
.update(rawSignature)
.digest('hex');
console.log('--------------------SIGNATURE----------------');
console.log(signature);
//json object send to MoMo endpoint
var body = JSON.stringify({
partnerCode: partnerCode,
accessKey: accessKey,
requestId: requestId,
amount: amount,
orderId: orderId,
orderInfo: orderInfo,
returnUrl: returnUrl,
notifyUrl: notifyurl,
extraData: extraData,
requestType: requestType,
signature: signature
});
//Create the HTTPS objects
var options = {
hostname: 'test-payment.momo.vn',
port: 443,
path: '/gw_payment/transactionProcessor',
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Length': Buffer.byteLength(body)
}
};
//Send the request and get the response
console.log('Sending....');
var req = https.request(options, res => {
console.log(`Status: ${res.statusCode}`);
console.log(`Headers: ${JSON.stringify(res.headers)}`);
console.log('Type of body', JSON.stringify(res.body));
res.setEncoding('utf8');
let fullBody = '';
res.on('data', body => {
fullBody += body;
console.log(' Real Body');
console.log(fullBody);
//console.log('Type of body', body.payUrl);
// console.log(JSON.parse(body).payUrl);
// res.redirect(JSON.parse(body).payUrl);
});
res.on('end', () => {
const payURL = JSON.parse(fullBody).payUrl;
console.log('payUrl', payURL);
console.log('No more data in response.');
});
});
req.on('error', e => {
console.log(`problem with request: ${e.message}`);
});
// write data to request body
req.write(body);
req.end();
});
This is the url I got from response
payUrl https://test-payment.momo.vn/gw_payment/payment/qr?partnerCode=MOMO&accessKey=F8BBA842ECF85&requestId=5f38cc86954a6206211e2842&amount=23000&orderId=5f38cc86954a6206211e2842&signature=37ae247d56efd9ed6630b7d7d1435b88ffb8895956da5711a62ebbab8118aa7b&requestType=captureMoMoWallet
Can you please tell how could i send the data from res.on('end'), the "payURL" in the picture above, to client-side. I have tried some methods like res.writeHead, res.send, res.json( ) .... But they all returned error: res.send, res.writeHead, res.json... is not a function
This is my client-side, . If you guys don't mind , please also show me how to automatically redirect the payURL site above when the client click my button. Should I keep using window.location.replace like above ?
export const bookTour = async tourId => {
try {
const res = await fetch(
`http://localhost:3000/api/v1/bookings/checkout-session/${tourId}`,
{
method: 'POST',
body: 'a=1'
}
).then(res => window.location.replace(res.redirectURL));
console.log('The res', res);
} catch (err) {
showAlert('error', err);
}
};
This is my index.js
if (bookBtn) {
bookBtn.addEventListener('click', e => {
e.target.textContent = 'Processing...';
const tourId = e.target.dataset.tourId;
bookTour(tourId);
});
}
You're shadowing the req/res-variables from your getCheckoutSession-handler by using the same names for your http-request. If you change it to:
const request = https.request(options, response => {
// ...
let fullBody = '';
response.on('data', body => {
fullBody += body;
});
response.on('end', () => {
const payURL = JSON.parse(fullBody).payUrl;
// access the handler "res" object here
res.send(payURL);
// alternatively use res.json({payURL}) to send a json response
});
});
it should work fine.
Note: Nowadays you should definitely use const/let instead of var (see this for more information)
Simple,
res.on('end', () => {
const payURL = JSON.parse(fullBody).payUrl;
res.json({
payURL: payURL
})
});
or other way
res.on('end', () => {
const payURL = JSON.parse(fullBody).payUrl;
res.status(200).send({
payURL: payURL
});
});

Gettting error while fetching response from api in lambda function node js

While i am intent AMAZON ALEXA my lambda function didnt receive repsonse from api ...
and getting response - Sorry, an error occurred. Please say again.
function httpsGet(myData, callback)
{
var options = {
host: 'cp6gckjt97.execute-api.us-east-1.amazonaws.com',
port: 80,
path: '/prod/stateresource?usstate=' + encodeURIComponent(myData),
method: 'GET',
};
var req = https.request(options, res => {
res.setEncoding('utf8');
var returnData = "";
res.on('data', chunk => {
returnData = returnData + chunk;
});
res.on('end', () => {
console.log(JSON.stringify(returnData))
var pop = JSON.parse(returnData).population;
callback(pop); // this will execute whatever function the caller defined, with one argument
});
});
req.end();
}
const GetProductList_Handler = {
canHandle(handlerInput) {
const request = handlerInput.requestEnvelope.request;
return request.type === 'IntentRequest' && request.intent.name === 'GetProductList' ;
},
async handle(handlerInput) {
const request = handlerInput.requestEnvelope.request;
const responseBuilder = handlerInput.responseBuilder;
let sessionAttributes = handlerInput.attributesManager.getSessionAttributes();
var myRequest = 'Florida';
httpsGet(myRequest, (myResult) => {
say = "there2"+JSON.stringify(myResult);
});
return responseBuilder
.speak(say)
.reprompt('try again, ' + say)
.getResponse();
},
}

Adding Authorization Header

How would I add an authorization key to the header of this sample code?
I would like to add an authorization header to the getRemoteData url request. For example, Authorization = Bearer XXXXXXXXXXXX
How can I add this to my URL request?
async handle(handlerInput) {
let outputSpeech = 'This is the default message.';
await getRemoteData('http://api.open-notify.org/astros.json')
.then((response) => {
const data = JSON.parse(response);
outputSpeech = `There are currently ${data.people.length} astronauts in space. `;
for (let i = 0; i < data.people.length; i++) {
if (i === 0) {
//first record
outputSpeech = outputSpeech + 'Their names are: ' + data.people[i].name + ', '
} else if (i === data.people.length - 1) {
//last record
outputSpeech = outputSpeech + 'and ' + data.people[i].name + '.'
} else {
//middle record(s)
outputSpeech = outputSpeech + data.people[i].name + ', '
}
}
})
.catch((err) => {
//set an optional error message here
//outputSpeech = err.message;
});
return handlerInput.responseBuilder
.speak(outputSpeech)
.getResponse();
},
};
const getRemoteData = function (url) {
return new Promise((resolve, reject) => {
const client = url.startsWith('https') ? require('https') :
require('http');
const request = client.get(url, (response) => {
if (response.statusCode < 200 || response.statusCode > 299) {
reject(new Error('Failed with status code: ' + response.statusCode));
}
const body = [];
response.on('data', (chunk) => body.push(chunk));
response.on('end', () => resolve(body.join('')));
});
request.on('error', (err) => reject(err))
})
};
Change this client.get() call from:
const request = client.get(url, (response) => {
to:
const headers = {
Authorization: 'Bearer 1234'
};
const request = client.get(url, {headers}, (response) => {

Synchronous/sequential REST calls in loop

I'm trying to call a REST API in a "for" loop, however, the results aren't what I'm expecting.
I've attempted to wrap everything in a promise, but the order of operations is still off, executing it asynchronously rather than synchronously.
var https = require('https');
var zlib = require("zlib");
var axios = require('axios');
const cheerio = require('cheerio');
var page = 1;
var hasMore = "true";
function delay() {
return new Promise(resolve => setTimeout(resolve, 300));
}
async function getLocation(page) {
// notice that we can await a function
// that returns a promise
await delay();
var apiUrl = 'https://my.api.com/search/advanced?page=' + page +
'&pagesize=5';
https.get(apiUrl, function(response) {
console.log("headers: ", response.headers);
console.log(response.statusCode)
if (response.statusCode == 200) {
var gunzip = zlib.createGunzip();
var jsonString = '';
response.pipe(gunzip);
gunzip.on('data', function(chunk) {
jsonString += chunk;
});
gunzip.on('end', function() {
obj = JSON.parse(jsonString);
var url = obj.items[0].owner.link;
axios(url)
.then(response => {
const html = response.data;
const $ = cheerio.load(html);
//OUTPUT LOCATION
console.log($('h3.location').text().trim());
})
.catch(console.error);
});
gunzip.on('error', function(e) {
console.log(e);
});
} else {
console.log("Error");
}
});
}
async function startGetLocation() {
var page = 1;
var hasMore = "true";
do {
//OUTPUT PAGE NUMBER
console.log(page.toString());
await getLocation(page);
page = page + 1;
} while (page < 3);
}
startGetLocation();
Based on the sample code, I would have expected the below to output:
1
New York
2
However, it's outputting:
1
2
New York
The problem is that the callback function that you passed to the https.get() function gets executed asynchronously and that the getLocation function does not wait until this part resolves.
So you could simply wrap the https.get() call and the unzipping part in a promise, wait for it to resolve and then do the axios-part.
async function getLocation(page) {
await delay();
var apiUrl = 'https://my.api.com/search/advanced?page=' + page +
'&pagesize=5';
const fetchAndUnzipPromise = new Promise((resolve, reject) => {
https.get(apiUrl, function (response) {
console.log("headers: ", response.headers);
console.log(response.statusCode)
if (response.statusCode == 200) {
var gunzip = zlib.createGunzip();
var jsonString = '';
response.pipe(gunzip);
gunzip.on('data', function (chunk) {
jsonString += chunk;
});
gunzip.on('end', function () {
obj = JSON.parse(jsonString);
var url = obj.items[0].owner.link;
resolve(url);
});
gunzip.on('error', function (e) {
reject(e);
});
} else {
reject(new Error("Statuscode not as exepcted"));
}
});
});
return fetchAndUnzipPromise.then(url => {
return axios(url)
.then(response => {
const html = response.data;
const $ = cheerio.load(html);
//OUTPUT LOCATION
console.log($('h3.location').text().trim());
})
.catch(console.error);
})
}

Trouble with asynchronous functions in node.js

I'm very new to node, and I'm trying to pull a list of IDs from an API, iterate through that list for each ID saving the output, and ultimately rename each file generated. The code below is the closest I've come, and while it works sometimes, it frequently fails as I believe one function isn't waiting for the other to complete (e.g. tries to read before a write), but I'm sure I have other issues going on.
const apiKey = inputData.apiKey
var https = require('https');
var sync = require('sync');
var fs = require('fs');
var JSONfileloc = "./pdfs/file.json"
var queryurl = 'https://intakeq.com/api/v1/intakes/summary?startDate=2018-01-01'
var authHeaders = { 'X-Auth-Key': apiKey }
var queryOpts = { method: 'GET', headers: authHeaders}
function handleFile (error, file)
{
if (error) return console.error('Ran into a problem here', error)
}
fetch(queryurl, queryOpts)
.then
(function findAPI(res, err)
{
if( err )
{ console.log('I cant find the API '+err) }
return res.json()
{console.log('found the API!')}
}
)
.then (function itID(res, err)
{
if( err )
{ console.log('I cant iterate the API '+err) }
for(var i = 0; i < res.length; i++)
{
var intakeID=res[i].Id;
var APIoptions={ host:"intakeq.com", path:"/api/v1/intakes/"+ intakeID, headers: authHeaders };
var PDFoptions={ host:"intakeq.com", path:"/api/v1/intakes/"+ intakeID+'/pdf', headers: authHeaders };
console.log('Working on ID:'+intakeID)
var JSONrequest = https.get(APIoptions, writeJSON)
}})
//READ JSON FUNCTION
function readJSON (err, data)
{
if (err) throw err;
if(data.indexOf('New Patient Forms') >= 0)
var contents = fs.readFileSync(JSONfileloc, handleFile);
var jsonContent = JSON.parse(contents)
//pull PT Name
pName = (jsonContent.ClientName);
console.log('The Patient Name Is ' + jsonContent.ClientName)
//pull PT DOB
pDob = (jsonContent.Questions[3].Answer)
console.log('Patient DOB Is ' + jsonContent.Questions[3].Answer)
//pull Form Type
pForm = (jsonContent.QuestionnaireName)
console.log('The Form Submitted is ' + jsonContent.QuestionnaireName)
//rename and move JSON
fs.rename("./pdfs/file.json", './JSONLogs/'+pName+' '+pForm+' '+Date.now()+'.json', function(err) {
if ( err ) console.log('Problem renaming! ' + err)
else console.log('Copying & Renaming JSON File!');
})
};
//WRITE JSON FUNCTION
function writeJSON(response, err)
{
var JSONfile = fs.createWriteStream(JSONfileloc, handleFile);
if (err) throw err;
response.pipe(JSONfile);
console.log('JSON Created')
fs.readFile(JSONfileloc, readJSON)
}
The research I've done leads me to believe that async.forEach is probably the right approach here, but I've been having a hard time getting that to work properly. Thanks in advance and any suggestions are much appreciated.
const apiKey = inputData.apiKey
var https = require('https');
var sync = require('sync');
var fs = require('fs');
var JSONfileloc = "./pdfs/file.json"
var queryurl = 'https://intakeq.com/api/v1/intakes/summary?startDate=2018-01-01'
var authHeaders = {
'X-Auth-Key': apiKey
}
var queryOpts = {
method: 'GET',
headers: authHeaders
}
function handleFile(error, file) {
if (error) return console.error('Ran into a problem here', error)
}
fetch(queryurl, queryOpts)
.then(function findAPI(res) {
return res.json();
})
.then(function itID(res) {
const JSONRequests = [];
for (var i = 0; i < res.length; i++) {
var intakeID = res[i].Id;
var APIoptions = {
host: "intakeq.com",
path: "/api/v1/intakes/" + intakeID,
headers: authHeaders
};
var PDFoptions = {
host: "intakeq.com",
path: "/api/v1/intakes/" + intakeID + '/pdf',
headers: authHeaders
};
// https.get has response as a stream and not a promise
// This `httpsGet` function converts it to a promise
JSONRequests.push(httpsGet(APIoptions, i));
}
return Promise.all(JSONRequests);
})
function httpsGet(options, filename) {
return new Promise((resolve, reject) => {
https.get(options, (response) => {
// The WriteJSON function, just for brewity
// Otherwise pass resolve to the seperate writeJSON and call it in there
var JSONfile = fs.createWriteStream(filename + ".json");
response.pipe(JSONfile);
JSONfile.on('close', () => {
readJSON(filename + ".json").then(() => {
resolve();
})
})
})
})
}
//READ JSON FUNCTION
function readJSON(filename) {
// if (err) throw err;
var contents = fs.readFileSync(filename, 'utf-8'); // removed handleFile as readFileSync does not allow callbacks, added format
var jsonContent = JSON.parse(contents)
// Make your conditional checks here with the jsonContents
//pull PT Name
pName = (jsonContent.ClientName);
console.log('The Patient Name Is ' + jsonContent.ClientName)
//pull PT DOB
pDob = (jsonContent.Questions[3].Answer)
console.log('Patient DOB Is ' + jsonContent.Questions[3].Answer)
//pull Form Type
pForm = (jsonContent.QuestionnaireName)
console.log('The Form Submitted is ' + jsonContent.QuestionnaireName)
//rename and move JSON
return new Promise((resolve, reject) => {
fs.rename("./pdfs/file.json", './JSONLogs/' + pName + ' ' + pForm + ' ' + Date.now() + '.json', function (err) {
if (err) {
console.log('Problem renaming! ' + err);
reject(err);
} else {
console.log('Copying & Renaming JSON File!');
resolve();
}
})
})
};
Updated to convert https.get response stream to return a Promise which can be handled much better.

Resources