Parse JSON object with Node.js - json

I have a little node application that runs and gets stats from an httpprovider
Its returning the value every second in this format:
{ WOWZA_CONNECTIONS_CURRENT: 21,
WOWZA_CONNECTIONS_TOTAL: 4879,
WOWZA_CONNECTIONS_BYTES_IN: 303242,
WOWZA_CONNECTIONS_BYTES_OUT: 96372 }
but I need it to be returning in a different format, exactly like this:
WOWZA_CONNECTIONS_CURRENT 21 Rem-East-v4-Edge-1
WOWZA_CONNECTIONS_TOTAL 4879 Rem-East-v4-Edge-1
WOWZA_CONNECTIONS_BYTES_IN 303242 Rem-East-v4-Edge-1
WOWZA_CONNECTIONS_BYTES_OUT 96372 Rem-East-v4-Edge-1
How would I accomplish this?
Here is my Code:
#!/usr/bin/env node
// Requires.
// request, jsdom, optimist, all of which can be install with npm.
// NOTE: -----------------------------------------------------------------------
// Uses jsdom 0.2.13, 0.2.14 has a bug
// https://github.com/tmpvar/jsdom/issues/436
// Install w/ npm via `npm install jsdom#0.2.13`
// -----------------------------------------------------------------------------
// Require modules.
var request = require('request'),
jsdom = require('jsdom'),
argv = require('optimist').argv;
// Make sure that at least the --uri argument was passed.
if (argv.uri.length == 0) {
console.log('URI Required! Script should be called with one argument which is the URI of the connectioncounts HTTP provider to query.');
return;
}
/**
*
*/
var Collector = {
stats: {},
get_stats: function(uri, callback) {
request({ uri: uri }, function (error, response, body) {
if (error && response.statusCode !== 200) {
console.log('Error when contacting ' + uri);
}
jsdom.env({
html: body,
scripts: [
'http://code.jquery.com/jquery.min.js'
]
}, function (err, window) {
// User jQuery to Gather some stats from the connectioncounts HTTP
// provider.
var $ = window.jQuery;
Collector.stats['WOWZA_CONNECTIONS_CURRENT'] = parseInt($('ConnectionsCurrent').html());
Collector.stats['WOWZA_CONNECTIONS_TOTAL'] = parseInt($('ConnectionsTotal').html());
Collector.stats['WOWZA_CONNECTIONS_BYTES_IN'] = parseFloat($('MessagesInBytesRate').html());
Collector.stats['WOWZA_CONNECTIONS_BYTES_OUT'] = parseFloat($('MessagesOutBytesRate').html());
callback(window);
});
});
},
}
/**
* Wrap Collector.get_stats call in a closure it works better with setInterval.
*/
var callDelay = function() {
Collector.get_stats(argv.uri, function(response) {
// Print out collected stats.
console.log(Collector.stats);
});
}
// Get stats once right away.
callDelay();
// Allo repeating at specified interval if --repeat is set.
if (argv.repeat != undefined) {
// Default to every 30 seconds if no delay is specified.
if (argv.delay == undefined) {
argv.delay = 30000;
}
setInterval(callDelay, argv.delay);
}
EDIT:
I achieved the output i needed by changing the output section of the code link so.
// Print out collected stats.
//console.log(Collector.stats);
console.log('WOWZA_CONNECTIONS_CURRENT ' + Collector.stats.WOWZA_CONNECTIONS_CURRENT + ' Rem-East-v4-Edge-1');
console.log('WOWZA_CONNECTIONS_TOTAL ' + Collector.stats.WOWZA_CONNECTIONS_TOTAL + ' Rem-East-v4-Edge-1')
console.log('WOWZA_CONNECTIONS_BYTES_IN ' + Collector.stats.WOWZA_CONNECTIONS_BYTES_IN + ' Rem-East-v4-Edge-1')
console.log('WOWZA_CONNECTIONS_BYTES_OUT ' + Collector.stats.WOWZA_CONNECTIONS_BYTES_OUT + ' Rem-East-v4-Edge-1')

You don't need to parse this as JSON, it is already there in an object. Just construct a string the way you want it by accessing it via Collector.stats.

Related

NodeJS http.request end processing before data processes

Can some explain why the the http.request end function is running before any data is actually retrieved? And how would I debug this any further? Should I be checking an http status?
This is going to work with Google Home app, but I took that code out and getting same error running locally. The http.request is from what a teacher provided in a class.
You can paste: people/?search=Luke%20Skywalker
into http://swapi.com (SW = StarWars API) to see the expected result.
'use strict';
/*eslint no-undef: "error"*/
/*eslint-env node*/
/*eslint-disable no-console */
let http = require('http');
let starWarsAPI = `www.swapi.co`;
//function to get details of the Star Wars Characters
//exports.handler = function(event, context, callback) {
//console.log("event=" + JSON.stringify(event));
//console.log("context=" + JSON.stringify(context));
//let characterName = event.result.parameters.StarWarsCharacter;
let characterName = "Luke Skywalker";
console.log("**** characterName=" + characterName);
let options = searchPeopleRequestOptions(characterName);
console.log("options=" + JSON.stringify(options));
makeRequest(options, function( data, error) {
console.log(" Processing data.results");
let person = data.results[0];
if (person) {
let height = person.height;
let mass = person.mass;
let response = person.name + " is " + height + " centimeters tall, weighs " + mass + " kilograms";
console.log("**** response=" + response);
//callback(null, {"speech": response});
}
else {
console.log ("No person found");
//callback(null, {"speech": "I'm not sure that character exists!"});
}
});
//};
console.log("The end");
//create a function to read first and last names from the API.
function searchPeopleRequestOptions(argCharacterName) {
var pathValue = `/api/people/?search=`+
encodeURIComponent(argCharacterName);
return {
host: starWarsAPI,
path: pathValue
};
}
function makeRequest(options, callback) {
var responseString = "";
var request = http.request(options,
function(response) {
response.on('data', function(data) {
responseString += data;
console.log("responseString=" + responseString);
});
response.on('end', function() {
console.log("end: responseString=" + responseString);
// dies on next line because responseString is empty
var responseJSON = JSON.parse(responseString);
callback(responseJSON, null);
});
response.on('error', function (error) {
console.log('\n Error received: ' + error);
});
});
request.end();
}
This is what I see when I run it:
E:\GitHub\NealWalters\GoogleHomeTest
λ node indexTest.js
**** characterName=Luke Skywalker
options={"host":"www.swapi.co","path":"/api/people/?search=Luke%20Skywalker"}
The end
end: responseString=
undefined:1
I'm not sure what's writing out the "undefined: 1" line.
If you look at the server's response status code, it will be 301: Moved Permanently.
And value of location field of response is:
https://swapi.co/api/people/?search=Luke%20Skywalker
instead
http://swapi.co/api/people/?search=Luke%20Skywalker
As we can see, the protocol changed from http to https.
The problem is that the http client supplied with the node.js does not support redirection for permanently changed URL.
So, you can use https module instead http (just change the require('https')).
Or use packages that support redirection. For example axios or request.

Asynchronously working of for loop in protractor

when i execute the following code using protractor it works. I am passing nested json to for loop. Because of asynchronously working of for loop it print all values of variable i and reaches to last value because of this it always access last pair of username and password. How can i solve this issue?
var data = require('.../testdata.json');
describe('homepage Test', function() {
it('candidate login', function() {
browser.driver.get('https://abcxyz.com');
for (i in data.testdata) {
element(by.id('tool_btn3')).click();
console.log(i);
browser.getTitle().then(function(title) {
console.log("Title: " + title)
if (title === "<page title>") {
browser.driver.sleep(3000);
element(by.id('email_input')).sendKeys(data.testdata[i].username);
element(by.id('pwd_input')).sendKeys(data.testdata[i].password);
element(by.xpath('//*[#id="signIn_btn"]/div[2]')).click();
browser.sleep(3000);
element(by.id('setting_img')).click();
browser.sleep(2000);
element(by.id('logout_div')).click().then(function() {
console.log('success');
});
} else {
console.log("problem");
}
});
}
});
});
You need to keep in mind that you can't use a for-loop with promises. All is async so in the end it will bite you in the ass, meaning that the it is ready but the test isn't.
Based on you example it would suggest to make a method called for example logon (place it in a Page Object or something). It will do the logon and stuff for you. Add an empty promise-container (array) and push the promises in there.
When the for-loop is done you can resolve the complete promise-container at once and it will execute all the promises 1 after each other. It will look something like this.
var data = require('.../testdata.json');
describe('homepage Test', function() {
it('candidate login', function() {
var promises = [];
browser.driver.get('https://abcxyz.com');
for (i in data.testdata) {
promises.push(expect(logon(data.testdata[i].username, data.testdata[i].password)).to.equal(true));
promises.push(console.log(i));
}
Promise.all(promises);
});
});
/**
* Logon
* #params {string} username
* #params {string} password
* #return {boolean}
*/
function logon(username, password) {
element(by.id('tool_btn3')).click();\
return browser.getTitle().then(function(title) {
console.log("Title: " + title)
if (title === "<page title>") {
browser.driver.sleep(3000);
element(by.id('email_input')).sendKeys(username);
element(by.id('pwd_input')).sendKeys(password);
element(by.xpath('//*[#id="signIn_btn"]/div[2]')).click();
browser.sleep(3000);
element(by.id('setting_img')).click();
browser.sleep(2000);
return element(by.id('logout_div')).click()
.then(function() {
return Promise.resolve(true);
});
} else {
return Promise.resolve(false);
}
});
}
If you are using for example Node 7 you can use async/await, or use Babel to transpile the code. If you can write TypeScript you also get the async/await

aws lambda s3 function isn't called inside alexa skills kit

I am trying to create a skill for Amazon Echo that will call a JSON file from AWS S3. When I call the code from s3 basic get function it works. And the Amazon Alexa code works on its own.
But when I call them together the function gets skipped. So for the following code the console gets called before and after s3.getObject(). But the middle one gets skipped. I do not understand why.
I also checked whether s3 was being called, and it is.
let aws = require('aws-sdk');
let s3 = new aws.S3({ apiVersion: '2006-03-01'});
function callS3() {
console.log('loading S3 function');
var myData = [];
const params = {
Bucket: 'cvo-echo',
Key: 'data.json'
};
console.log("trying to get s3");
s3.getObject(params, (err, data) => {
if (err) {
console.log('error in s3 get: \n' + err);
//const message = `Error getting object ${key} from bucket ${bucket}.
// Make sure they exist and your bucket is in same region as this function.
//console.log(message);
} else {
console.log('CONTENT TYPE: ', data.ContentType);
console.log('Data body: \n' + data.Body.toString());
myData = JSON.parse(data.Body.toString());
console.log('myData.length = ' + myData.length);
}
console.log('myData >> ' + myData);
});
console.log('finished callS3() func');
return myData;
}
This might be a control flow issue, I've worked with amazons sdk before and was running into similar issues. Try implementing async within your code to have a better control of what happens when. This way methods won't skip.
UPDATE: adding some code examples of what you could do.
function callS3(callback) {
console.log('loading S3 function');
var myData = [];
const params = {
Bucket: 'cvo-echo',
Key: 'data.json'
};
console.log("trying to get s3");
s3.getObject(params, (err, data) => {
if (err) {
console.log('error in s3 get: \n' + err);
//const message = `Error getting object ${key} from bucket ${bucket}.
// Make sure they exist and your bucket is in same region as this function.
//console.log(message);
callback(err,null);//callback the error.
} else {
console.log('CONTENT TYPE: ', data.ContentType);
console.log('Data body: \n' + data.Body.toString());
myData = JSON.parse(data.Body.toString());
console.log('myData.length = ' + myData.length);
console.log('myData >> ' + myData);
console.log('finished callS3() func');
//Include the callback inside of the S3 call to make sure this function returns until the S3 call completes.
callback(null,myData); // first element is an error and second is your data, first element is null if no error ocurred.
}
});
}
/*
This MIGHT work without async but just in case you can read more about
async.waterfall where functions pass down values to the next function.
*/
async.waterfall([
callS3()//you can include more functions here, the callback from the last function will be available for the next.
//myNextFunction()
],function(err,myData){
//you can use myData here.
})
It's a timing issue. Here is an example of loading a JSON file from an S3 share when a session is started.
function onLaunch(launchRequest, session, callback) {
var sBucket = "your-bucket-name";
var sFile = "data.json";
var params = {Bucket: sBucket, Key: sFile};
var s3 = new AWS.S3();
var s3file = s3.getObject(params)
new AWS.S3().getObject(params, function(err, data) {
if (!err) {
var json = JSON.parse(new Buffer(data.Body).toString("utf8"));
for(var i = 0; i < json.length; i++) {
console.log("name:" + json[i].name + ", age:" + json[i].age);
}
getWelcomeResponse(callback);
} else {
console.log(err.toString());
}
});
}

Unable to send data in response to the view in nodejs

I am trying to create a simple web application which fires a http.request call, get the data and display it over to the html(ejs here). I am able to fire the request, get the data, massage it etc.. but unable to pass it to the view. Sample code is as below:
var searchData = [];
router.post('/',requesthandler);
function requesthandler(req,res){
var options = {
host: url,
port: 9999,
path: qstring,
method: 'GET'
};
var reqget = http.request(options,responsehandler);
reqget.end();
console.log('Rendering now:............................ ');
res.render('result',{title: 'Results Returned',searchdata : searchData});
}
function responsehandler(ress) {
console.log('STATUS: ' + ress.statusCode);
ress.on('data', function (chunk) {
output += chunk;
console.log('BODY: ' );
});
/* reqget.write(output); */
ress.on('end',parseresponse);
}
function parseresponse(){
var data = JSON.parse(output);
console.log(data.responseHeader);
// populate searchData here from data object
searchData.push({//some data});
}
function errorhandler(e) {
console.error(e);
}
module.exports = router;
Problem is I a unable to pass the objeect searchData to the view via res.render();
'Rendering now: ...........' gets executed before execution starts in parseresponse() and so the page is displayed without the data which seems to be in conjuction with using callbacks, So how can I pass the data object to the view once the searchData is loaded in parseresponse().
PS: I am able to print all console statements
define res variable globally:
var res;
function requesthandler(req,resObj){
res = resObj;//set it to the resObj
}
wrap res.render inside a function like this:
function renderPage(){
res.render('result',{title: 'Results Returned',searchdata : searchData});
}
then in parseresponse function do this:
function parseresponse(){
var data = JSON.parse(output);
searchData.push({some data});
renderPage();
}
Hope this solves your problem.

nodejs - parsing chunked twitter json

The nodejs server 'gets' this JSON stream from Twitter and sends it to the client:
stream.twitter.com/1/statuses/filter.json?track=gadget
The data returned to the client is 'chunked' JSON and both JSON.parse(chunk) and eval('(' + chunk + ')') on the client side result in parsing errors.
Concatenating the chucked pieces and waiting for the 'end' event isn't a solution either
I noticed previous samples used something like this on the client side that apparently worked before:
socket.onmessage = function(chunk) {
data = eval("(" + chunk.data + ")");
alert(data.user.screen_name);
I'm using this on the client side and it results in a parsing error:
var socket = new io.Socket();
socket.on('message', function(chunk) {
var data = eval('(' + chunk + ')'); // parsing error
alert(data.screen_name):
I know that its successfully returning a JSON chunk with:
var socket = new io.Socket();
socket.on('message', function(chunk) {
alert(chunk): // shows a JSON chunk
Server:
response.on('data', function (chunk) {
client.each(function(e) {
e.send(chunk);
});
Did something change or what else em I doing wrong?
UPDATE: The 'end' event does not fire because its streaming?
http.get({
headers: { 'content-type': 'application/json' },
host: 'stream.twitter.com',
path: '/1/statuses/filter.json?track...
}, function(res) {
res.setEncoding('utf8');
res.on('data', function (chunk) {
client.each(function(e) {
e.send(chunk);
});
});
// does not fire
res.on('end', function () {
});
...
I'm looking into the difference with http 1.0 and http 1.1 as far as sending chunked data.
Look at the section titled Parsing Responses in Twitter's documentation.
Parsing JSON responses from the Streaming API is simple every object is returned on its own line, and ends with a carriage return. Newline characters (\n) may occur in object elements (the text element of a status object, for example), but carriage returns (\r) should not.
On the server side, keep accumulating chunks until you see the carriage return "\r". Once the carriage return is found, extract the string up to the carriage return, and that gives us one tweet.
var message = ""; // variable that collects chunks
var tweetSeparator = "\r";
res.on('data', function(chunk) {
message += chunk;
var tweetSeparatorIndex = message.indexOf(tweetSeparator);
var didFindTweet = tweetSeparatorIndex != -1;
if (didFindTweet) {
var tweet = message.slice(0, tweetSeparatorIndex);
clients.forEach(function(client) {
client.send(tweet);
});
message = message.slice(tweetSeparatorIndex + 1);
}
});
The client becomes simple. Simply parse the socket message as JSON in its entirety.
socket.on('message', function(data) {
var tweet = JSON.parse(data);
});
#Anurag I'cant add comments, however instead of
if (chunk.substr("-1") == "\r")
it should be:
if ( chunk.charCodeAt(chunk.length-2) == 13 )
The carriage return isn't the last character.
I would recommend piping the response into a JSON parser. You can use this: https://github.com/dominictarr/JSONStream