Incorrect response to the REST call using node.js - node.js

I have the following api :
for (var index in workload.elements) {
(function(index) {
var arr = [];
var resourceIdentifiers = {};
var elementinfo = {};
var metadataModified = {};
elementinfo = workload.elements[index];
arr[index] = workload.elements[index].uri;
if (workload.elements[index].parameters.imageUri) {
arr.push(workload.elements[index].parameters.imageUri);
}
resourceIdentifiers = arr.join(',');
console.log('uri' + resourceIdentifiers);
mysql.elementlevelpricing(resourceIdentifiers, function(result) {
elementlevelpricingSummary = result;
metadataModified = workload.elements[index].metadata;
metadataModified.pricingsummary = elementlevelpricingSummary;
delete elementinfo.metadata;
elementinfo.metadata = metadataModified;
workloadinfo.elements = JSON.stringify(elementArray, null, 2);
elementArray[index] = elementinfo;
console.log(JSON.stringify(elementArray, null, 2));
res.send(JSON.stringify(elementArray, null, 2));
});
})(index);
}
console.log prints the correct result , but the response to the REST call is incorrect and getting the result of only one the value getting into the loop.

First off, use a forEach or map instead of a for loop with IIFEs.
Second, an HTTP request only has one response. You can't send multiple requests with res.send firing off a few times. If you want all of the information, just aggregate the results of JSON.stringify(elementArray, null, 2) and send the final aggregated data with res.send.

Related

Call multiple API in for loop and get one by one response using node js

I have to call multiple API using promise all in for loop but I am getting a response in unsequential format. e.g. I have 3 rows but getting a response in 1,2,3 sequence. First getting the first-row response than 3rd row and then getting 2nd row but I need to get a response in a sequential format like (1 row,2 rows,3 rows).
result = '{"error_code":0,"err_desc":null,"data":[{"":"","name":"OTT, KATHRYN M","address":"3110 Horseshoe Trl, Glenmoore, PA","email":"","phone1":"(410) 599-2212","phone2":"(610) 827-9107","phone3":"(610) 308-4566","phone4":"(610) 506-1121","phone5":"(610) 469-0737","phone6":"(610) 942-4347","phone7":"323-7898","phone8":"(814) 371-6133","phone9":""},{"":"","name":"BELTRANTE, SUSAN E","address":"3 Rhoads Ave, Moorestown, NJ\"","email":"SUSAN.BELTRANTE#AOL.COM, JOE.BARGER#YAHOO.COM,","phone1":"(856) 266-0381","phone2":"(856) 273-0869","phone3":"(609) 266-0381","phone4":"(856) 235-3933","phone5":"","phone6":"","phone7":"","phone8":"","phone9":""},{"":"","name":"Manish","address":"4895 E American Beauty Dr, Tucson, AZ 85756","email":"abc#gmail.com","phone1":"(857) 266-0381","phone2":"(857) 273-0869","phone3":"(610) 266-0381","phone4":"(857) 235-3933","phone5":"","phone6":"","phone7":"","phone8":"","phone9":""}]}';
var i;
for (i = 0; i < result.length; i++)
//for (i = 0; i <= 0; i++)
{
var phone = result[i].phone9;
var name = result[i].name;
var address = result[i].address;
var email = result[i].email;
var phone1 = result[i].phone1;
var phone2 = result[i].phone2;
var phone3 = result[i].phone3;
var phone4 = result[i].phone4;
var phone5 = result[i].phone5;
var phone6 = result[i].phone6;
var phone7 = result[i].phone7;
var phone8 = result[i].phone8;
var addressinfo = address.split(',');
var street = addressinfo[0];
var city = addressinfo[1];
var state = addressinfo[2];
var zip = addressinfo[3];
Promise.all([
fetch('https://backend.mioym.properties/api/247/eppraisal?street='+street+'&zip='+zip),
fetch('https://backend.mioym.properties/api/247/zillow?street='+street+'&zip='+zip),
fetch('https://backend.mioym.properties/api/247/pennymac?address='+address),
fetch('https://backend.mioym.properties/api/247/chase?address='+address),
fetch('https://backend.mioym.properties/api/247/realtor?address='+address)
]).then(function (responses) {
// Get a JSON object from each of the responses
return Promise.all(responses.map(function (response) {
console.log("here"+i);
//console.log(response.json());
console.log(response.url);
return response.json();
}));
}).then(function (data) {
console.log("success"+i);
// Log the data to the console
// You would do something with both sets of data here
console.log(data);
}).catch(function (error) {
console.log("error"+i);
// if there's an error, log it
console.log(error);
});
}
So please anyone suggest me solution.
The second Promise.all inside your then block is not necessary, as responses will already contain the resolved values. Note that Promise.all processes the requests in parallel but the resolved responses will be in order. So you can simply do:
Promise.all([
fetch('https://backend.mioym.properties/api/247/eppraisal?street=' + street + '&zip=' + zip),
fetch('https://backend.mioym.properties/api/247/zillow?street=' + street + '&zip=' + zip),
fetch('https://backend.mioym.properties/api/247/pennymac?address=' + address),
fetch('https://backend.mioym.properties/api/247/chase?address=' + address),
fetch('https://backend.mioym.properties/api/247/realtor?address=' + address)
]).then(function (responses) {
// Get a JSON object from each of the responses
return responses.map(function (response) {
console.log(response.url);
return response.json();
});
});

Using knex SELECT query results for another SELECT query

I am trying to run a PostgreSQL query with Knex and then use the results to run another query.
exports.buildBuoyFeaturesJSON = function (conditionA, conditionB) {
var query = null;
var selectedFields = knex.select
(
knex.raw('t_record.id AS id'),
...
knex.raw('t_record.latitude AS latitude'),
knex.raw('t_record.longitude AS longitude')
)
.from('t_record')
.then(function (response) {
var geometry_array = [];
var rows = response.rows;
var keys = [];
for (var key = 0; key <= rows.length - 1; key++) {
var geometry =
{
"id" : rows[key].id,
"type" : "Feature",
"geometry" : rows[key].geometry,
"properties" : {
...
"sensors" : []
}
};
keys.push(rows[key].id);
geometry_array.push(geometry);
}
getMeasurementsAndSensors(keys, geometry_array);
});
};
The latter function uses some of the results from the previous function. Due to asynchronous nature of Knex, I need to call the second function from inside the first function's .then() statement:
function getMeasurementsAndSensors (keys, geometry_array) {
var query = knex
.select
(
't_record_id',
'i_sensor_id',
'description',
'i_measurement_id',
't_sensor_name',
't_measurement_name',
'value',
'units'
)
.from('i_record')
...
.whereRaw('i_record.t_record_id IN (' + keys + ')')
.orderByRaw('t_record_id, i_sensor_id ASC')
.then(function (response) {
var rows = response.rows;
var t_record_id = 0;
var i_sensor_id = 0;
var record_counter = -1;
var sensor_counter = -1;
for (var records = 0; records <= rows.length -1; records++) {
if (t_record_id !== rows[records].t_record_id) {
t_record_id = rows[records].t_record_id;
record_counter++;
sensor_counter = -1;
}
if (i_sensor_id !== rows[records].i_sensor_id) {
i_sensor_id = rows[records].i_sensor_id;
geometry_array[record_counter].properties.sensors[++sensor_counter] =
{
'i_sensor_id' : rows[records].i_sensor_id,
't_sensor_name' : rows[records].t_sensor_name,
'description' : rows[records].description,
'measurements' : []
};
}
geometry_array[record_counter].properties.sensors[sensor_counter].measurements.push
({
'i_measurement_id': rows[records].i_measurement_id,
'measurement_name': rows[records].t_measurement_name,
'value': rows[records].value,
'units': rows[records].units
});
}
//wrapping features with metadata.
var feature_collection = GEOGRAPHY_METADATA;
feature_collection.features = geometry_array;
JSONToFile(feature_collection, 'buoy_features');
});
}
Currently I save end result to a JSON file because I couldn't get Promises to work. JSON is later used to power a small OpenLayers application, hence the JSON-ification after getting results.
I am quite sure that getting the data from a database, saving it to file, then accessing it from another process and using it for OpenLayers is a very redundant way to do it, but so far, it is the only one that works.
I know there are a lot of ways to make these functions work better, but I am new to promises and don't know how to work with them outside of most basic functions. Any suggestions how to make this code better are welcome.
All you appear to be missing is a bunch of returns.
Here are skeletonized versions of the two functions, including the necessary returns :
exports.buildBuoyFeaturesJSON = function(conditionA, conditionB) {
return knex.select (...)
^^^^^^
.from(...)
.then(function(response) {
// synchronous stuff
// synchronous stuff
return getMeasurementsAndSensors(keys, geometry_array);
^^^^^^
}).then(function(geometry_array) {
var feature_collection = GEOGRAPHY_METADATA;
feature_collection.features = geometry_array;
return feature_collection;
^^^^^^
});
};
function getMeasurementsAndSensors(keys, geometry_array) {
return knex.select(...)
^^^^^^
.from(...)
...
.whereRaw(...)
.orderByRaw(...)
.then(function(response) {
// heaps of synchronous stuff
// heaps of synchronous stuff
// heaps of synchronous stuff
return geometry_array;
^^^^^^^^^^^^^^^^^^^^^
});
}
I moved the feature_collection collection part into buildBuoyFeaturesJSON() on the basis that it seems to sit there more logically. If not, then it would be very simple to move it back into getMeasurementsAndSensors().
I have not tried to fix the additional issue highlighted by #vitaly-t.

Node.js dialling back API requests

I have a list of 125,000 + Id numbers.
I am making a request to an api to get more information for each one.
But my problem is that the api will stop giving me a response if I request more then 6 per second.
I need a way to control the speed of the requests.
Just use a function called by setInterval to do the actual API querying ?
Simple example:
var ids = [ /* big array here */ ];
function queryAllIds(ids, callback) {
var processedIds = [];
var lastProcessedId = 0;
var processedIdCount = 0;
var intervalId;
function queryApi() {
var idToProcess = lastProcessedId++;
doActualQuery(ids[idToProcess], function(result) {
processedIds[idToProcess] = result;
processedIdCount++;
if (processedIdCount === ids.length) {
nextTick(callback, processedIds);
}
});
}
if (intervalId && lastProcessedId === ids.length)
clearInterval(intervalId);
}
intervalId = setInterval(queryApi, 1000/6);
}
queryAllIds(ids, function (processedIds) {
// Do stuff here
});
We ended up using rate limiter which provided the rate limiting we needed right out of the box. https://www.npmjs.com/package/limiter

http call in backbone promise

Hi I have a backbone web app using Jquery and NodeJs/mongo as the server side framework. I'm having problems with making a http get call with a foreah loop and the results of the get call being iteratively added to each row of the loop.
var eventid = this.model.get("_id");
var inPromise = $.get("/registrants/list?eventid="+eventid,null,null,"json").then(
function (result){
var temp;
var finalVal = '';
var tempfinalVal = "";
var loop = 0
percentage = 0;
$.each(result.registrants,function(index,registrant){
temp = JSON.parse(registrant.fields);
for (var key in temp) {
if(key =="Email"){
if(temp[key] != ""){
$.get("/stats/registrant?userid="+temp[key]+"&eventid="+eventid,null,null,"json").then(function(result2){
percentage = (result2.Stats.type ===undefined || result2.Stats.type ==null) ? "0": result2.Stats.type;
finalVal +=percentage+"\n";
}).fail(function(){
percentage = "0";
});
}
}else if(key =="eventid"){
loop++;
finalVal = finalVal.slice(0, - 1);
finalVal +='\n';
}
finalVal +=temp[key] + ',';
}
});
//promises.push(inPromise);
}
).done(function(finalVal){
$("#webcast-download-registrants-tn").attr("href",'data:text/csv;charset=utf-8;filename=registration.csv",'+encodeURIComponent(finalVal));
console.log("DONE");
}).fail(function(){
console.log("fail");
});
// promise.done(function () {
// console.log(" PROMISE DONE");
// });
So I have the loop through a collection and the last item of the docuemnt gets a content froma nother http call and when all is fone it will create a CSV file. The problem is that THE "DONE" text echos firts then the "CALL" text is displayed
Rick, your problem is not the simplest due to :
the need for nested asynchronous gets
the need to build each CSV data row partly synchronously, partly asynchronously.
the need for a mechanism to handle the fulfilment of multiple promises generated in the inner loop.
From what you've tried, I guess you already know that much.
One important thing to note is that you can't rely on for (var key in temp) to deliver properties in any particular order. Only arrays have order.
You might try something like this :
var url = "/stats/registrant",
data = { 'eventid': this.model.get('_id') },
rowTerminator = "\n",
fieldNames = ['firstname','lastname','email','company','score'];
function getScore(email) {
return $.get(url, $.extend({}, data, {'userid':email}), null, "json").then(function(res) {
return res.Stats ? res.Stats.type || 0 : 0;
}, function() {
//ajax failure - assume score == 0
return $.when(0);
});
}
$.get("/registrants/list", data, null, "json").then(function(result) {
var promises = [];//An array in which to accumulate promises of CSV rows
promises.push($.when(fieldNames)); //promise of CSV header row
if(result.registrants) {
$.each(result.registrants, function(index, registrant) {
if(registrant.fields) {
// Synchronously initialize row with firstname, lastname, email and company
// (omitting score for now).
var row = fieldNames.slice(0,-1).map(function(fieldName, i) {
return registrant.fields[fieldName] || '';
});
//`row` remains available to inner functions due to closure
var promise;
if(registrant.fields.Email) {
// Fetch the registrant's score ...
promise = getScore(registrant.fields.Email).then(function(score) {
//... and asynchronously push the score onto row
row.push(score);
return row;
});
} else {
//or synchronously push zero onto row ...
row.push(0);
//... and create a resolved promise
promise = $.when(row);
}
promises.push(promise);//Accumulate promises of CSV data rows (still in array form), in the correct order.
}
});
}
return $.when.apply(null, promises).then(function() {
//Join all the pieces, in nested arrays, together into one long string.
return [].slice.apply(arguments).map(function(row) {
return row.join(); //default glue is ','
}).join(rowTerminator);
});
}).done(function(str) {
$("#webcast-download-registrants-tn").attr("href",'data:text/csv;charset=utf-8;filename=registration.csv",'+encodeURIComponent(str));
console.log("DONE");
}).fail(function() {
console.log("fail");
});
partially tested
See comments in code for explanation and please ask if there's anything you don't follow.

object stocking in node.js

How can I create resuable blank objects in node.js? For example :
In file1 :
var Result = {
error :false,
errorReport :[]
}
exports.Result = Result;
In file 2 :
var result = require('../file1');
exports.doSomething = function(){
var output = result.Result;
output.error = true;
output.errorReport = some_error_array;
}
Now, everytime I invoke doSomething(), it is obvious that the last result will be cached.
But, everytime I invoke doSomething(), i want a fresh object as it is declared in Result without any extra programming (i dont want to reset attributes everytime i do some thing with Result object.
Am I missing any minor detail here ? please help me out
You could make it a class:
exports.Result = function() {
this.error = false;
this.errorReport = [];
};
// later on:
var output = new result.Result();
...
Or even make it fancy so that you can pass arguments:
exports.Result = function(error, errorReport) {
this.error = error === undefined ? false : error;
this.errorReport = errorReport === undefined ? [] : errorReport;
};
// later
var output = new result.Result(true, some_error_array);

Resources