NodeJs async waterfall (callback method is not a function) - node.js

I am having a problem using async waterfall where I find that after calling the second callback (cbNumPages), the first parameter "pages" is the actual callback for the next function, instead of the last parameter "cbGetFiles" which it should be (as far as I know async waterfall says that last parameter should always be the callback, well in this case is apparently not).
The code is the following:
async.waterfall
([
function(cbNumPages)
{
request({
url: 'any-url',
qs: {},
method: 'GET',
headers: {
'Authorization' : 'any-auth'
}
}, (err, response, body) => {
if (!err && response.statusCode == 200)
{
var $ = cheerio.load(body);
var pagesList = $('ol.aui-nav').children();
if(pagesList.length<1)
{
var numPages = 1;
} else {
var numPages = pagesList.length-2;
}
console.log(numPages);
var pages = new Array(numPages),
total = numPages*20,
iterator = 0;
async.eachSeries(pages, function(page, cb)
{
if(page>1)
{
pages[iterator] = iterator;
}else {
pages[iterator] = iterator*20;
}
iterator++;
cb();
}, function(err){
if(err) cbNumPages(err);
cbNumPages(null, pages);
});
} else {
cbNumPages(err);
}
})
},
function(pages, cbGetFiles)
{
var files = [];
var limitDate = moment().tz('Europe/Madrid').subtract(330,'days').format();
async.eachSeries(pages, function(page, cb)
{
request({
url: 'any-url'+page,
qs: {},
method: 'GET',
headers: {
'Authorization' : 'any-auth'
}
}, (err, response, body) => {
if(!err && response.statusCode == 200)
{
var $ = cheerio.load(body);
var rows = $('tr[id^=\'attachment-\']');
async.eachLimit(rows, 1, function(row, cb)
{
var id = row.attribs['id'];
var file = row.attribs['data-attachment-filename'];
var author = $(row).children('.creator').text().trim();
var created = $(row).children('.created-date').text().trim();
created = moment.tz(created, 'MMM D, YYYY', 'Europe/Madrid').format();
var urlFile = 'simple-file' + $(row).children('.filename-column').children('.filename').attr('href');
var extension = row.attribs['data-attachment-filename'].split('.');
extension = extension[extension.length-1];
if(created<limitDate && validExtensions.indexOf(extension)>-1)
{
var f = '{ "id": "' + id + '",';
f += ' "file": "' + file + '",';
f += ' "url": "' + urlFile + '",';
f += ' "author": "' + author + '",';
f += ' "modified": "' + created + '" }';
files.push(JSON.parse(f));
}
cb();
}, (err) => {
if(err) cbGetFiles(err);
});
cb();
} else {
cb(err);
}
});
}, function(err){
if(err){
cbGetFiles(err);
} else {
cbGetFiles(null, files);
}
});
},
function(files, cbGetAutors)
{
var filesFinal = {};
for(var f in files)
{
if(!filesFinal[files[f].author])
{
var ff = {};
for(var i in files)
{
if(files[i].author === files[f].author)
{
ff[files[i].file] = files[i].url;
}
}
filesFinal[files[f].author] = ff;
}
}
cbGetAutors(null, JSON.parse(JSON.stringify(filesFinal)));
},
function(filesFinal, cbSendEmail)
{
var authors = Object.keys(filesFinal);
async.eachSeries(authors, function(author, cb)
{
var name = author.split(' ');
var email = 'simple-mail#gmail.com';
var msg = '<p>Hi ' + author + ',</p><p>how is it going:</p><p>';
for(var a in Object.keys(filesFinal[author]))
{
msg += '<p style="margin-left:20px"> '+ICON_DOC+' <a href="';
msg += filesFinal[author][Object.keys(filesFinal[author])[a]]+'">'+Object.keys(filesFinal[author])[a]+'</a></p>';
}
msg += '</p></p><p><b>NOTE: This is a no-reply address.</b></p><p>Have a nice day! '+ICON_MONKEY+'</p>';
var message = {
text: msg,
from: 'test#mail.com',
to: email,
bcc: '',
subject: 'Sample subject',
attachment: [{data: msg, alternative: true}]
};
serverEmail.send(message, function(err, message)
{
if(err)
{
cb(err);
} else {
console.log(message);
cb();
}
});
}, function(err){
if(err) cbSendEmail(err);
cbSendEmail();
});
}
], (err) => {
if(err) console.log(err);
});
I would like to know if there is a way to control this issue or at least if there are another options for what I want to do.
Thanks.

A better (neat) way to use async waterfall.
Make sure you use return before any callback function. I have added them in the code.
Also, if you are nesting eachSeries, it is better to give a different name to callback function than the parent callback function.
I have changed 'cb' of child async.series to 'inner_cb'
Updated Code:
async.waterfall
([
funcOne,
funcTwo,
funcThree,
funcFour
], (err) => {
if(err) console.log(err);
});
funciton funcOne(cbNumPages) {
request({
url: 'any-url',
qs: {},
method: 'GET',
headers: {
'Authorization' : 'any-auth'
}
}, (err, response, body) => {
if (!err && response.statusCode == 200)
{
var $ = cheerio.load(body);
var pagesList = $('ol.aui-nav').children();
if(pagesList.length<1)
{
var numPages = 1;
} else {
var numPages = pagesList.length-2;
}
console.log(numPages);
var pages = new Array(numPages),
total = numPages*20,
iterator = 0;
async.eachSeries(pages, function(page, cb)
{
if(page>1)
{
pages[iterator] = iterator;
}else {
pages[iterator] = iterator*20;
}
iterator++;
return cb();
}, function(err){
if(err) return cbNumPages(err);
return cbNumPages(null, pages);
});
} else {
return cbNumPages(err);
}
})
}
function funcTwo(pages, cbGetFiles) {
var files = [];
var limitDate = moment().tz('Europe/Madrid').subtract(330,'days').format();
async.eachSeries(pages, function(page, cb)
{
request({
url: 'any-url'+page,
qs: {},
method: 'GET',
headers: {
'Authorization' : 'any-auth'
}
}, (err, response, body) => {
if(!err && response.statusCode == 200)
{
var $ = cheerio.load(body);
var rows = $('tr[id^=\'attachment-\']');
async.eachLimit(rows, 1, function(row, inner_cb)
{
var id = row.attribs['id'];
var file = row.attribs['data-attachment-filename'];
var author = $(row).children('.creator').text().trim();
var created = $(row).children('.created-date').text().trim();
created = moment.tz(created, 'MMM D, YYYY', 'Europe/Madrid').format();
var urlFile = 'simple-file' + $(row).children('.filename-column').children('.filename').attr('href');
var extension = row.attribs['data-attachment-filename'].split('.');
extension = extension[extension.length-1];
if(created<limitDate && validExtensions.indexOf(extension)>-1)
{
var f = '{ "id": "' + id + '",';
f += ' "file": "' + file + '",';
f += ' "url": "' + urlFile + '",';
f += ' "author": "' + author + '",';
f += ' "modified": "' + created + '" }';
files.push(JSON.parse(f));
}
return inner_cb();
}, (err) => {
if(err) return cbGetFiles(err);
});
return cb();
} else {
return cb(err);
}
});
}, function(err){
if(err){
return cbGetFiles(err);
} else {
return cbGetFiles(null, files);
}
});
}
function funcThree(files, cbGetAutors) {
var filesFinal = {};
for(var f in files)
{
if(!filesFinal[files[f].author])
{
var ff = {};
for(var i in files)
{
if(files[i].author === files[f].author)
{
ff[files[i].file] = files[i].url;
}
}
filesFinal[files[f].author] = ff;
}
}
return cbGetAutors(null, JSON.parse(JSON.stringify(filesFinal)));
}
function funcFour(filesFinal, cbSendEmail) {
var authors = Object.keys(filesFinal);
async.eachSeries(authors, function(author, cb)
{
var name = author.split(' ');
var email = 'simple-mail#gmail.com';
var msg = '<p>Hi ' + author + ',</p><p>how is it going:</p><p>';
for(var a in Object.keys(filesFinal[author]))
{
msg += '<p style="margin-left:20px"> '+ICON_DOC+' <a href="';
msg += filesFinal[author][Object.keys(filesFinal[author])[a]]+'">'+Object.keys(filesFinal[author])[a]+'</a></p>';
}
msg += '</p></p><p><b>NOTE: This is a no-reply address.</b></p><p>Have a nice day! '+ICON_MONKEY+'</p>';
var message = {
text: msg,
from: 'test#mail.com',
to: email,
bcc: '',
subject: 'Sample subject',
attachment: [{data: msg, alternative: true}]
};
serverEmail.send(message, function(err, message)
{
if(err)
{
return cb(err);
} else {
console.log(message);
return cb();
}
});
}, function(err){
if(err) return cbSendEmail(err);
return cbSendEmail();
});
}

As #YSK said in a comment, I was obtaining a 401 from the response.statusCode and therefore it is being missleaded to the cbSendEmail(err) with err beying null. Making the next method in the waterfall's first parameter beying the callback instead of the second.

Related

How can write a column in tsv file using node js?

I have to write a json response in a column of tsv file how can I do that?
I am using the following code. Please find me a solution? I have to check it but its not working.
//npm init -y gen package.json file
var unProcessedItems = [];
var data = loadData('./Alabama_Pre_Final.tsv');
async function X(i) {
if (data[i] && data[i][7]) {
console.log(data[i][7]);
function address(address_details) {
request({
url: 'https://us-extract.api.smartystreets.com/?auth-id=xxx&auth-token=xxx',
method: 'POST',
headers: {
'content-type': 'application/json'
},
body: address_details,
},
(error, response, body) => {
if (!error && response.statusCode == 200) {
var res = JSON.parse(body);
let objectArray = [];
if (res.addresses[0].verified) {
objectArray.push(res.addresses[0].api_output[0].delivery_line_1, res.addresses[0].api_output[0].last_line, res.addresses[0].api_output[0].delivery_point_barcode)
}
var address_data = objectArray.join([separator = ','])
console.log(address_data)
}
});
}
address("1000 Greenhill Blvd NW, Fort Payne, 35967, AL, USA");
const data = 'Column 19\n1\t2\t3\n4\t5\t6';
require('fs').writeFileSync('./Alabama_Pre_Final.tsv', data);
// i++;
// X(i);
}
}
}
I've looked at your answer above I think it should be easy enough to modify so that it works as you expect, I've done this below. The other functions should be left unchanged. So the function X processes each row then returns a record variable which we append to the records array. After processing is complete we write the records array to the csv file.
async function loadData(filePath) {
if (fs.existsSync(filePath)) {
var tsvData = fs.readFileSync(filePath, 'utf-8');
var rowCount = 0;
var scenarios = [];
parse_tsv(tsvData, (row) => {
rowCount++;
if (rowCount > 1) {
scenarios.push(row);
}
});
return scenarios;
} else {
console.log("loadData: Returning empty..")
return [];
}
}
// Process a row of TSV data
function X(row) {
return new Promise((resolve, reject) => {
if (row && row[7]) {
console.log(row[7]);
request({
url: 'https://us-extract.api.smartystreets.com/?auth-id=e62698e8-c3fc-b929-0f5b-d3b54d0bcd0c&auth-token=cibfMexBdl3HrmwbWY6p',
method: 'POST',
headers: { 'content-type': 'application/json' },
body: row[7],
},
(error, response, body) => {
if (!error && response.statusCode == 200) {
var res = JSON.parse(body);
let objectArray = [];
if (res.addresses[0].verified) {
objectArray.push(res.addresses[0].api_output[0].delivery_line_1, res.addresses[0].api_output[0].components.city_name,
res.addresses[0].api_output[0].components.zipcode, res.addresses[0].api_output[0].components.state_abbreviation)
}
var address_data = objectArray.join([separator = ','])
resolve({ name: address_data.replace(/['"]+/g, '') });
} else if (error) {
reject(error);
} else {
reject( { statusCode: response.statusCode });
}
});
}
});
}
async function processData() {
let MAX_RECORDS = 5; // Change as appropriate
var data = await loadData('./Alabama_Pre_Final.tsv');
if (data.length > 0) {
unProcessedItems = [];
let records = [];
for(let row of data) {
// Process a row of data.
let record = await X(row);
records.push(record);
if (records.length === MAX_RECORDS) break;
}
console.log(records);
csvWriter.writeRecords(records)
.then(() => console.log('The CSV file was written successfully'));
} else {
console.log("No Data");
}
}
processData();
const fs = require('fs');
const createCsvWriter = require('csv-writer').createObjectCsvWriter;
var rp = require('request-promise');
var dataToWrite;
var http = require("http");
var request = require('request');
function loadData(filePath) {
if (fs.existsSync(filePath)) {
var tsvData = fs.readFileSync(filePath, 'utf-8');
var rowCount = 0;
var scenarios = [];
parse_tsv(tsvData, (row) => {
rowCount++;
if (rowCount > 1) {
scenarios.push(row);
}
});
return scenarios;
} else {
return [];
}
}
function parse_tsv(s, f) {
var ix_end = 0;
for (var ix = 0; ix < s.length; ix = ix_end + 1) {
ix_end = s.indexOf('\n', ix);
if (ix_end == -1) {
ix_end = s.length;
}
//var row = s.substring(ix, ix_end - 1).split('\t');
var row = s.substring(ix, ix_end).split('\t');
f(row);
}
}
var unProcessedItems = [];
var data = loadData('./Alabama_Pre_Final.tsv');
var records = [];
async function X(i) {
if (data[i] && data[i][7]) {
console.log(data[i][7]);
var options = {
method: 'POST',
url: 'https://us-extract.api.smartystreets.com/?auth-id=c64de073-9531-9444-35e6-7204d9d62c36&auth-token=e5FxYc1niUD7Cp0peixd',
headers: {
'content-type': 'application/json'
},
body: data[i][7],
json: true // Automatically stringifies the body to JSON
};
rp(options)
.then(function (parsedBody) {
// POST succeeded...
var res = parsedBody;
let objectArray = [];
if (res.addresses.length) {
if (res.addresses[0].verified) {
objectArray.push(res.addresses[0].api_output[0].delivery_line_1, res.addresses[0].api_output[0].components.city_name,
res.addresses[0].api_output[0].components.zipcode, res.addresses[0].api_output[0].components.state_abbreviation)
}
var address_data = objectArray.join([separator = ','])
console.log(address_data)
// records.push({ name: address_data.replace(/['"]+/g, '') });
const ADDRESS_COLUMN_INDEX = 7;
for(let row of address_data) {
row[ADDRESS_COLUMN_INDEX] = await X(row[ADDRESS_COLUMN_INDEX]);
}
records.push(address_data.replace(/['"]+/g, ''));
let output = records.map(row => row.join("\t")).join("\n");
i++;
if (i <= 7) {
X(i);
}
else {
fs.writeFileSync('out.tsv', output);
console.log('The TSV file was written successfully');
// return callback(records);
}
}
});
}
// console.log(records);
}
if (data.length > 0) {
unProcessedItems = [];
X(0);
} else {
console.log("No Data");
}
I've updated this answer based on your last revisions, I hope this helps you:
function loadData(filePath) {
if (fs.existsSync(filePath)) {
var tsvData = fs.readFileSync(filePath, 'utf-8');
var rowCount = 0;
var scenarios = [];
parse_tsv(tsvData, (row) => {
rowCount++;
if (rowCount > 1) {
scenarios.push(row);
}
});
return scenarios;
} else {
return [];
}
}
function parse_tsv(s, f) {
var ix_end = 0;
for (var ix = 0; ix < s.length; ix = ix_end + 1) {
ix_end = s.indexOf('\n', ix);
if (ix_end == -1) {
ix_end = s.length;
}
var row = s.substring(ix, ix_end).split('\t');
f(row);
}
}
var unProcessedItems = [];
var data = loadData('./Alabama_Pre_Final.tsv');
var records = [];
async function X(i) {
if (data[i] && data[i][7]) {
console.log(data[i][7]);
var options = {
method: 'POST',
url: 'https://us-extract.api.smartystreets.com/?auth-id=c64de073-9531-9444-35e6-7204d9d62c36&auth-token=e5FxYc1niUD7Cp0peixd',
headers: {
'content-type': 'application/json'
},
body: data[i][7],
json: true // Automatically stringifies the body to JSON
};
rp(options)
.then(async function (parsedBody) {
// POST succeeded...
var res = parsedBody;
let objectArray = [];
if (res.addresses.length) {
if (res.addresses[0].verified) {
objectArray.push(res.addresses[0].api_output[0].delivery_line_1, res.addresses[0].api_output[0].components.city_name,
res.addresses[0].api_output[0].components.zipcode, res.addresses[0].api_output[0].components.state_abbreviation)
}
var address_data = objectArray.join([separator = ','])
console.log("rp.then -> address_data:",address_data);
data[i][7] = address_data.replace(/['"]+/g, '');
records.push(data[i].join("\t"));
i++;
if (i <= 7) {
console.log("Looking up address #" + i);
X(i);
} else {
const output = records.join("\n");
// Remove the _test when you are happy with the result.
fs.writeFileSync('./Alabama_Pre_Final_test.tsv', output);
console.log('The TSV file was written successfully');
}
}
});
}
}
if (data.length > 0) {
unProcessedItems = [];
X(0);
} else {
console.log("No Data");
}

Parent function is not waiting for child to finish

Inside getMetadata(), Object.keys function is not waiting for httprequest to finish. How can I make object.keys function to wait till httprequest function manipluates the result variable?
I'm using node. I tried to make promise but failed.
function fetchAirportPageIDsListWithMetaJSON(faa, cb){
logger.info('[airportcms-data-processor] fetching airport pages list with Metadata');
var faa = faa
async.waterfall([
getAirportPageIDsList,
getMetadata,
], function (err, result) {
cb(null, result);
});
function getAirportPageIDsList(callback) {
httpRequests.fetchData('//s3 url to fetch data', function (err, data) {
var idsMap={};
data["page-ids"].forEach( (obj) => {
obj.list.forEach((item) => idsMap[item] = obj.id);
});
callback(null, idsMap);
})
}
function getMetadata(data,callback) {
var result=[];
Object.keys(data).sort().forEach( function (t) {
var tempJson={};
var urlcheck = verifySpecialPageId(t);
if (urlcheck){
var url = config.urls.s3_airports_base_url+'/'+faa+'/'+urlcheck;
}else{
var url = config.urls.s3_airports_base_url+'/'+faa+'/'+t;
}
tempJson["sectionId"]= t;
tempJson["page"]= data[t];
httpRequests.makeHeadRequestWithCallerId(url, function (err, metax) {
if (metax){
let z = metax.split('|')[0];
tempJson["SummaryRange"]= getSummaryRangeAirportPageList(z);
tempJson["timestamp"]= new Date(parseInt(z)).toLocaleDateString();
tempJson["callerId"]= metax.split('|')[1];
}else{
tempJson["timestamp"]='';
tempJson["callerId"]='';
tempJson["SummaryRange"]='';
}
})
result.push(tempJson);
});
logger.info("Final result: ", result);
callback(null, result);
}
}
http request function:
function makeHeadRequestWithCallerId (url, cb) {
httpRequest.head(url, function (err, res) {
if (err) {
logger.error('Error ' + err);
return cb(err, null);
}
if(res.code === 200) {
if (res.headers['x-amz-meta-x-amz-meta-lastmodified'] || res.headers['x-amz-meta-x-amz-meta-callerid']) {
var dateModified = res.headers['x-amz-meta-x-amz-meta-lastmodified'];
var timeStamp = Date.parse(dateModified);
var callerid = res.headers['x-amz-meta-x-amz-meta-callerid'];
if(timeStamp && callerid) {
return cb(null, timeStamp+'|'+callerid);
} else if (callerid){
return cb(null, '|'+callerid);
}else if(timeStamp){
return cb(null, timeStamp+'|');
}else{
return cb(null, null);
}
}else{
return cb(null, null);
}
}
});
}
Current log=> Final result:
[{ sectionId: 'internet-wifi', page: 'internet-wifi' },
{ sectionId: 'layover-ideas', page: 'layover-ideas' }]
Expected log=> Final result:
{ sectionId: 'internet-wifi',
page: 'internet-wifi',
SummaryRange: '12-99',
timestamp: '1/29/2018',
callerId: '' },
{ sectionId: 'layover-ideas',
page: 'layover-ideas',
SummaryRange: '12-99',
timestamp: '1/26/2017',
callerId: '' },
function getMetadata(data, callback) {
var result = [];
var count = Object.keys(data).length;
var i = 0;
Object.keys(data).sort().forEach(function (t) {
var tempJson = {};
var urlcheck = verifySpecialPageId(t);
if (urlcheck) {
var url = config.urls.s3_airports_base_url + '/' + faa + '/' + urlcheck;
} else {
var url = config.urls.s3_airports_base_url + '/' + faa + '/' + t;
}
tempJson["sectionId"] = t;
tempJson["page"] = data[t];
httpRequests.makeHeadRequestWithCallerId(url, function (err, metax) {
if (metax) {
let z = metax.split('|')[0];
tempJson["SummaryRange"] = getSummaryRangeAirportPageList(z);
tempJson["timestamp"] = new Date(parseInt(z)).toLocaleDateString();
tempJson["callerId"] = metax.split('|')[1];
} else {
tempJson["timestamp"] = '';
tempJson["callerId"] = '';
tempJson["SummaryRange"] = '';
}
result.push(tempJson);
i++;
if(count === i){
logger.info("Final result: ", result);
callback(null, result);
}
})
});
}

Request body sometimes null

The request body sometimes (less than 1% of the time) is null when pulled into lambda. I am processing on the order of 14,000 request bodies one at a time. Any request bodies erring out have to be handled manually. Why is the body randomly coming in null?
Sample code ran from prompt (node index):
const async = require('async');
const _ = require('lodash');
const moment = require('moment');
const Client = require('node-rest-client').Client;
const fs = require('fs');
const input = require('./TestFull.json');
module.exports = () => {
const filename = `./eventfulFails-${new moment().format("YYYY-MM-DD-HHmmss")}.json`;
console.log('Start Time: ', new moment().format("HH:mm:ss"));
let failedObjects = {
events: [],
venues: [],
performers: []
};
async.parallel([
async.apply(processVenues, input.venues, failedObjects),
async.apply(processPerformers, input.performers, failedObjects)
], (lookupErr) => {
if (lookupErr) {
return console.error('Error processing venues and performers.', lookupErr);
}
console.log('Start Events: ', new moment().format("HH:mm:ss"));
async.waterfall([
async.apply(processEvents, input.events, failedObjects)
], (eventErr) => {
if (eventErr) {
console.log('Time of Failure: ', new moment().format("HH:mm:ss"));
return console.error('Error processing events.', eventErr);
}
console.log('End Time: ', new moment().format("HH:mm:ss"));
if (failedObjects.events.length || failedObjects.venues.length || failedObjects.performers.length) {
const stream = fs.createWriteStream(filename);
stream.once('open', function(fd) {
stream.write(JSON.stringify(failedObjects));
stream.end();
});
}
});
});
};
function processVenues(venues, failedObjects, callback) {
const calls = [];
for (let i = 0; i < venues.length; i++) {
const v = venues[i];
calls.push(async.apply((venue, postCallback) => {
const client = new Client();
const args = {
data: venue,
headers: {"Content-Type": "application/json"}
};
client.post('https://hm1br4yo34.execute-api.us-west-2.amazonaws.com/dev/eventful-venue', args, (data, response) => {
if (response.statusCode !== 200 && response.statusCode !== 201) {
failedObjects.venues.push({
venue,
response
});
console.log('venue status code: ', response);
console.log('venue data: ', venue);
}
return postCallback(null);
});
}, v));
}
async.waterfall(calls, callback);
}
function processPerformers(performers, failedObjects, callback) {
const calls = [];
for (let i = 0; i < performers.length; i++) {
const v = performers[i];
calls.push(async.apply((performer, postCallback) => {
const client = new Client();
const args = {
data: performer,
headers: {"Content-Type": "application/json"}
};
client.post('https://hm1br4yo34.execute-api.us-west-2.amazonaws.com/dev/eventful-performer', args, (data, response) => {
if (response.statusCode !== 200 && response.statusCode !== 201) {
failedObjects.performers.push({
performer,
response
});
console.log('performer status code: ', response);
console.log('performer data: ', performer);
}
return postCallback(null);
});
}, v));
}
async.waterfall(calls, callback);
}
function processEvents(events, failedObjects, callback) {
const calls = [];
for (let i = 0; i < events.length; i++) {
const v = events[i];
calls.push(async.apply((event, postCallback) => {
const client = new Client();
const args = {
data: event,
headers: {"Content-Type": "application/json"}
};
client.post('https://hm1br4yo34.execute-api.us-west-2.amazonaws.com/dev/eventful', args, (data, response) => {
if (response.statusCode !== 200 && response.statusCode !== 201) {
failedObjects.events.push({
event,
response
});
console.log('event status code: ', response);
console.log('event data: ', event);
}
return postCallback(null);
});
}, v));
}
async.waterfall(calls, callback);
}
if (!module.parent) {
module.exports();
}
Code of function processVenues (eventful-venue-load) that is called:
const _ = require('lodash');
const AWS = require('aws-sdk');
const async = require('async');
const sdk = require('#consultwithmikellc/withify-sdk');
const host = process.env.aurora_host;
const user = process.env.aurora_user;
const database = process.env.aurora_database;
let decryptedPassword;
const lambda = new AWS.Lambda({
region: 'us-west-2' //your region
});
class WithifyEventCreate extends sdk.Lambda {
constructor(event, context, keysToDecrypt) {
super(event, context, keysToDecrypt);
this.getLocation = this.getLocation.bind(this);
this.insertLocations = this.insertLocations.bind(this);
this.insertLocationImages = this.insertLocationImages.bind(this);
}
decryptedKey(key, value) {
switch (key) {
case 'aurora_password':
decryptedPassword = value;
break;
}
}
initializeComplete() {
this.connect(host, user, decryptedPassword, database, true);
}
connectComplete() {
async.waterfall(
[
this.getLocation,
this.insertLocations,
this.insertLocationImages
]
);
}
getLocation(callback) {
const {id: eventfulLocationID} = this.body;
this.connection.query('SELECT * FROM `Location` WHERE `eventfulLocationID` = ?',
[eventfulLocationID],
(err, results) => {
if (err) {
// error call block
return this.sendResponse(err, this.createResponse(500));
} else if (results.length === 1) {
console.log('Invoking withify-eventful-venue-update...');
lambda.invoke({
FunctionName: 'withify-eventful-venue-update',
Payload: JSON.stringify(this.event)
}, (error, data) => {
return this.sendResponse(null, JSON.parse(data.Payload));
});
} else if (results.length > 1) {
return this.sendResponse(`The location lookup produced multiple results. event:${JSON.stringify(this.body)}`, this.createResponse(500));
} else {
return callback(null);
}
}
);
}
insertLocations(callback) {
const {name: locationName, address: street, city, region_abbr: state, postal_code,
description, id: eventfulLocationID, latitude: lat, longitude: lng, withdrawn: locationWithdrawn} = this.body;
let addresses = street.concat(', ', city, ', ', state, ', ', postal_code);
if (!description.length){
var phones = "";
}else{
var re = /(([\(][0-9]{3}[\)][\s][0-9]{3}[-][0-9]{4})|([0-9]{3}[-][0-9]{3}[-][0-9]{4})|([0-9]{3}[\.][0-9]{3}[\.][0-9]{4}))/i;
this.body.found = description.match(re);
if (!this.body.found){
var phone = "";
}else{
if (!this.body.found.length){
var phone = "";
}else{
var phone = this.body.found[0];
}
}
}
this.connection.query('INSERT IGNORE INTO `Location` (`locationName`, `address`, ' +
'`phone`, `lat`, `lng`, `eventfulLocationID`, `locationWithdrawn`) VALUES (?, ?, ?, ?, ?, ?, ?)',
[locationName, addresses, phone, lat, lng, eventfulLocationID, locationWithdrawn],
(err, results) => {
if (err) {
return this.sendResponse(err, this.createResponse(500));
}
this.body.locationID = results.insertId;
return callback(null);
}
);
}
insertLocationImages(callback) {
var altText = "";
const images = _.flatten(this.body.images.map(im => {
return _.map(im.sizes, (ims, idx) => {
const title = `Image ${idx}`;
return [
this.body.locationID,
this.body.name,
ims.url,
null,
null,
this.body.id,
ims.width,
ims.height
];
});
}));
if(!images[0]){
return this.sendResponse(null, this.createResponse(201, this.body));
}
this.connection.query('INSERT IGNORE INTO `LocationImage` (`locationID`, `imageTitle`, `imageUrl`, ' +
'`imageName`, `altText`, `eventfulLocationID`, `width`, `height`) VALUES ?',
[images],
(err, results) => {
if (err) {
return this.sendResponse(err, this.createResponse(500));
} else if (results.affectedRows !== images.length) {
return this.sendResponse('The image inserts did not affect the right number' +
' of rows.', this.createResponse(500));
}
return this.sendResponse(null, this.createResponse(201, this.body));
}
);
}
}
exports.handler = (event, context) => {
const withifyEventCreate = new WithifyEventCreate(event, context, ['aurora_password']);
withifyEventCreate.initialize([decryptedPassword]);
};

Paypal express checkout in Node.js (error 10410)

I have been unable to to successfully parse the returned token to authenticate final Paypal sandbox payment process. Any help or input would be greatly appreciated :)
REF:
https://github.com/petersirka/node-paypal-express-checkout
https://developer.paypal.com/docs/classic/express-checkout/in-context/integration/
Here is the error I am returned:
ACK: 'Failure',
VERSION: '52.0',
BUILD: '22708556'
L_ERRORCODE0: '10410',
L_SHORTMESSAGE0: 'Invalid token',
L_LONGMESSAGE0: 'Invalid token.',
L_SEVERITYCODE0: 'Error'
Client side:
<form id="myContainer" method="post" action="/api/payment/payone"></form>
<script>
window.paypalCheckoutReady = function () {
paypal.checkout.setup('XXX', {
environment: 'sandbox',
container: 'myContainer'
});
};
</script>
<script src="//www.paypalobjects.com/api/checkout.js" async></script>
API:
payone: function(req, res) {
var paypal = require('paypal-express-checkout').init('username', 'password', 'signature', 'return url', 'cancel url', 'debug');
paypal.pay('20130001', 10.00, 'XX', 'USD', false, function(err, url) {
if (err) {
console.log(err);
return;
}
res.redirect(url);
});
paypal.detail( "token", "PayerID", function(err, data, invoiceNumber, price) {
if (err) {
console.log(err);
return;
}
});
}
Finally, paypal-express doc:
var parser = require('url');
var https = require('https');
var qs = require('querystring');
function Paypal(username, password, signature, returnUrl, cancelUrl, debug) {
this.username = "XXX";
this.password = "XXX";
this.solutiontype = 'Mark';
this.signature = "XXX";
this.debug = debug || false;
this.returnUrl = 'XXX';
this.cancelUrl = 'XXX';
this.url = 'https://' + 'api-3t.sandbox.paypal.com' + '/nvp'; //'https://' + (debug ? 'api-3t.sandbox.paypal.com' : 'api-3t.paypal.com') + '/nvp';
this.redirect = 'https://' + 'www.sandbox.paypal.com/cgi-bin/webscr'; //https://' + (debug ? 'www.sandbox.paypal.com/cgi-bin/webscr' : 'www.paypal.com/cgi-bin/webscr');
};
Paypal.prototype.params = function() {
var self = this;
return {
USER: self.username,
PWD: self.password,
SIGNATURE: self.signature,
SOLUTIONTYPE: self.solutiontype,
VERSION: '52.0'
};
console.log(self);
};
Paypal.prototype.detail = function(token, payer, callback) {
if (token.get !== undefined && typeof(payer) === 'function') {
callback = payer;
payer = token.get.PayerID;
token = token.get.token;
}
console.log(token);
var self = this;
var params = self.params();
params.TOKEN = token;
params.METHOD = 'GetExpressCheckoutDetails';
self.request(self.url, 'POST', params, function(err, data) {
if (err) {
callback(err, data);
return;
}
if (typeof(data.CUSTOM) === 'undefined') {
callback(data, null);
return;
}
console.log('3.3');
var custom = data.CUSTOM.split('|');
var params = self.params();
params.PAYMENTACTION = 'Sale';
params.PAYERID = payer;
params.TOKEN = token;
params.AMT = custom[1];
params.CURRENCYCODE = custom[2];
params.METHOD = 'DoExpressCheckoutPayment';
self.request(self.url, 'POST', params, function(err, data) {
if (err) {
callback(err, data);
return;
}
console.log('3.4');
callback(null, data, custom[0], custom[1]);
});
});
return self;
};
Paypal.prototype.request = function(url, method, data, callback) {
var self = this;
var params = qs.stringify(data);
if (method === 'GET')
url += '?' + params;
var uri = parser.parse(url);
var headers = {};
headers['Content-Type'] = method === 'POST' ? 'application/x-www-form-urlencoded' : 'text/plain';
headers['Content-Length'] = params.length;
var location = '';
var options = { protocol: uri.protocol, auth: uri.auth, method: method || 'GET', hostname: uri.hostname, port: uri.port, path: uri.path, agent: false, headers: headers };
var response = function (res) {
var buffer = '';
res.on('data', function(chunk) {
buffer += chunk.toString('utf8');
})
req.setTimeout(exports.timeout, function() {
callback(new Error('timeout'), null);
});
res.on('end', function() {
var error = null;
var data = '';
if (res.statusCode > 200) {
error = new Error(res.statusCode);
data = buffer;
} else
data = qs.parse(buffer);
callback(error, data);
});
};
var req = https.request(options, response);
req.on('error', function(err) {
callback(err, null);
});
if (method === 'POST')
req.end(params);
else
req.end();
return self;
};
Paypal.prototype.pay = function(invoiceNumber, amount, description, currency, requireAddress, callback) {
// Backward compatibility
if (typeof(requireAddress) === 'function') {
callback = requireAddress;
requireAddress = false;
}
var self = this;
var params = self.params();
params.PAYMENTACTION = 'Sale';
params.AMT = prepareNumber(amount);
params.RETURNURL = self.returnUrl;
params.CANCELURL = self.cancelUrl;
params.DESC = description;
params.NOSHIPPING = requireAddress ? 0 : 1;
params.ALLOWNOTE = 1;
params.CURRENCYCODE = currency;
params.METHOD = 'SetExpressCheckout';
params.INVNUM = invoiceNumber;
params.CUSTOM = invoiceNumber + '|' + params.AMT + '|' + currency;
self.request(self.url, 'POST', params, function(err, data) {
if (err) {
callback(err, null);
return;
}
if (data.ACK === 'Success') {
callback(null, self.redirect + '?cmd=_express- checkout&useraction=commit&token=' + data.TOKEN);
return;
}
callback(new Error('ACK ' + data.ACK + ': ' + data.L_LONGMESSAGE0), null);
});
return self;
console.log(self);
};
function prepareNumber(num, doubleZero) {
var str = num.toString().replace(',', '.');
var index = str.indexOf('.');
if (index > -1) {
var len = str.substring(index + 1).length;
if (len === 1)
str += '0';
if (len > 2)
str = str.substring(0, index + 3);
} else {
if (doubleZero || true)
str += '.00';
}
return str;
};
exports.timeout = 10000;
exports.Paypal = Paypal;
exports.init = function(username, password, signature, returnUrl, cancelUrl, debug) {
return new Paypal(username, password, signature, returnUrl, cancelUrl, debug);
};
exports.create = function(username, password, signature, returnUrl, cancelUrl, debug) {
return exports.init(username, password, signature, returnUrl, cancelUrl, debug);
};
Usually we encounter Error 10410 if there is incorrect token or no token passed.
please make sure that you are passing the right PayPal Express token.
for information on error codes you may refer: https://developer.paypal.com/docs/classic/api/errorcodes/

TypeError: Cannot read property 'emails' of null

Here is line 54, where I am getting the error:
if (docr.emails) {
And here is the rest of my original code:
var MongoClient = require('mongodb').MongoClient;
var assert = require('assert');
var ObjectId = require('mongodb').ObjectID;
var config = require('./config'),
xlsx = require('./xlsx'),
utils = require('./utils'),
_ = require('lodash'),
url = config.DB_URL;
var meetings = [];
function findNumberOfNotesByMeeting(db, meeting, callback) {
var meetingId = meeting._id.toString(),
meetingName = meeting.name.displayValue,
attendees = meeting.attendees;
host = meeting.host;
var count = 1, pending = 0, accepted = 0;
console.log("==== Meeting: " + meetingName + '====');
_.each(attendees, function(item) {
console.log(count++ + ': ' + item.email + ' (' + item.invitationStatus + ')');
if (item.invitationStatus == 'pending') { pending++; }
else if (item.invitationStatus == 'accepted') { accepted++; }
});
console.log("*** " + attendees.length + ", " + pending + "," + accepted);
db.collection('users').findOne({'_id': new ObjectId(host)}, function(err, doc) {
var emails = [];
if (doc.emails) {
doc.emails.forEach(function(e) {
emails.push(e.email + (e.primary ? '(P)' : ''));
});
}
var email = emails.join(', ');
if (utils.toSkipEmail(email)) {
callback();
} else {
db.collection('notes').find({ 'meetingId': meetingId }).count(function(err, count) {
if (count != 0) {
console.log(meetingName + ': ' + count + ',' + attendees.length + ' (' + email + ')');
meetings.push([ meetingName, count, email, attendees.length, pending, accepted ]);
}
callback();
});
}
});
}
function findMeetings(db, meeting, callback) {
var host = meeting.host;
db.collection('users').findOne({'_id': new ObjectId(host)}, function(err, docr) {
var emails = [];
if (docr.emails) {
docr.emails.forEach(function(e) {
emails.push(e.email + (e.primary ? '(P)' : ''));
});
}
var email = emails.join(', ');
if (utils.toSkipEmail(email)) {
callback();
} else {
var cursor = db.collection('meetings').find({
'email': {'$regex': 'abc', '$options': 'i' }
});
}
cursor.count(function(err, count) {
console.log('count: ' + count);
var cnt = 0;
cursor.each(function(err, doc) {
assert.equal(err, null);
if (doc != null) {
findNumberOfNotesByMeeting(db, doc, function() {
cnt++;
if (cnt >= count) { callback(); }
});
}
});
});
});
};
MongoClient.connect(url, function(err, db) {
assert.equal(null, err);
findMeetings(db, function() {
var newMeetings = meetings.sort(function(m1, m2) { return m2[1] - m1[1]; });
newMeetings.splice(0, 0, [ 'Meeting Name', 'Number of Notes', 'Emails' ]);
xlsx.writeXLSX(newMeetings, config.xlsxFileNameMeetings);
db.close();
});
});
Try the following:
function findMeetings(db, meeting, callback) {
var host = meeting.host;
db.collection('users').findOne({'_id': new ObjectId(host)}, function(err, docr) {
var emails = [];
if (!err && docr && docr.emails) {
docr.emails.forEach(function(e) {
emails.push(e.email + (e.primary ? '(P)' : ''));
});
}
...

Resources