I have Node.js package in Meteor app, how do I call the function? - javascript

I want to use this Node code https://arian.io/how-to-use-yelps-api-with-node/ in my Meteor app, to interface with Yelp's 2.0 API and return results that I can use.
I have installed meteorhacks:npm and added the appropriate node packages to packages.json:
{
"oauth-signature" : "1.3.0",
"nonce" : "1.0.3",
"request" : "2.58.0",
"lodash" : "3.10.0"
}
This is my .js file
//Start IsCLient
if (Meteor.isClient) {
//Call Yelp Helper
Template.yelper.events({
'click button': function () {
data = callYelp();
}
});
//Call Yelp Helper
//Call Yelp
callYelp = function callYelp(user) {
Meteor.call('callYelp', user);
}
//End Call Yelp
}
//End IsClient
if (Meteor.isServer) {
Meteor.startup(function () {
// code to run on server at startup
});
}
//Methods
Meteor.methods({
'callYelp': function callYelp(user) {
/* require the modules needed */
var oauthSignature = Meteor.npmRequire('oauth-signature');
var n = Meteor.npmRequire('nonce')();
var request = Meteor.npmRequire('request');
var qs = Meteor.npmRequire('querystring');
var _ = Meteor.npmRequire('lodash');
/* Function for yelp call
* ------------------------
* set_parameters: object with params to search
* callback: callback(error, response, body)
*/
request_yelp = function(set_parameters, callback) {
/* The type of request */
var httpMethod = 'GET';
/* The url we are using for the request */
var url = 'http://api.yelp.com/v2/search';
/* We can setup default parameters here */
var default_parameters = {
location: 'San+Francisco',
sort: '2'
};
/* We set the require parameters here */
var required_parameters = {
oauth_consumer_key : process.env.oauth_consumer_key,
oauth_token : process.env.oauth_token,
oauth_nonce : n(),
oauth_timestamp : n().toString().substr(0,10),
oauth_signature_method : 'HMAC-SHA1',
oauth_version : '1.0'
};
/* We combine all the parameters in order of importance */
var parameters = _.assign(default_parameters, set_parameters, required_parameters);
/* We set our secrets here */
var consumerSecret = process.env.consumerSecret;
var tokenSecret = process.env.tokenSecret;
/* Then we call Yelp's Oauth 1.0a server, and it returns a signature */
/* Note: This signature is only good for 300 seconds after the oauth_timestamp */
var signature = oauthSignature.generate(httpMethod, url, parameters, consumerSecret, tokenSecret, { encodeSignature: false});
/* We add the signature to the list of paramters */
parameters.oauth_signature = signature;
/* Then we turn the paramters object, to a query string */
var paramURL = qs.stringify(parameters);
/* Add the query string to the url */
var apiURL = url+'?'+paramURL;
/* Then we use request to send make the API Request */
request(apiURL, function(error, response, body){
return callback(error, response, body);
});
};
}
})
Everything seems to include properly, and I believe I have things set up right, adding the code to a Method and then calling it with the button press.
My problem is that within the callYelp method, there is a function created that makes the actual request to Yelp, and I'm not sure how to fire that.
You can see in the comments in my code where I've tried to make it work.
Is this is a scope issue? An issue with Node?
Also I know I don't have my Yelp API parameters (token, etc) set yet, I will add those, I'm just trying to get it to at least connect and throw an error first.
request(apiURL, function(error, response, body){
return callback(error, response, body);
});

I believe you can make use of http package http://docs.meteor.com/#/full/http to make a call, remember to add the package before using it:
meteor add http
And remember to unblock before making a call or your meteor process will be block by it. This is example from meteor documentation page:
Meteor.methods({checkTwitter: function (userId) {
check(userId, String);
this.unblock();
try {
var result = HTTP.call("GET", "http://api.twitter.com/xyz",
{params: {user: userId}});
return true;
} catch (e) {
// Got a network error, time-out or HTTP error in the 400 or 500 range.
return false;
}
}});

Related

File with variable that gets updated, and other files see that change?

I'm trying to build a simple tool that pings a bunch of url's to monitor their status and it updates a variable with each app's status.
I also have another file which i'd like to be able to execute at any time to get the current status of each app from that variable.
Here's my main file and you can see there are 2 exports - start, and getStatuses.
index.js
'use strict';
const rest = require('restler');
const time = require('simple-time');
const seconds = time.SECOND;
// The list of apps to check if are running
var apps = {
myApp: {
url: 'http://myUrl.com',
status: null,
lastUpdatedAt: new Date()
}
};
/**
* Loop through and check the status of every app
*/
function checkAllStatuses() {
for (var name in apps) {
if (apps.hasOwnProperty(name)) {
var app = apps[name];
console.log('app = ', app);
checkAppStatus(name, app);
}
}
}
/**
* Checks the status of an app
*
* #param name - The name of the app
* #param app - The app that we're checking the status of
*/
function checkAppStatus(name, app) {
var req = rest.get(app.url);
req.on('complete', function(result, response) {
if(response.statusCode !== app.status) {
updateStatus(name, response.statusCode);
}
});
req.on('error', function(e) {
console.log('ERROR: ' + e.message);
});
req.on('timeout', function(data, response) {
console.log('Request timed out');
});
}
/**
* Updates the status of an app
*
* #param app - The app to update the status of
* #param status - The status to update the app to
*/
function updateStatus(name, status) {
apps[name].status = status;
apps[name].lastUpdatedAt = new Date();
}
function getStatuses() {
return apps;
}
function start() {
// Check every 5 seconds
setInterval(checkAllStatuses, 5*seconds);
}
module.exports.start = start;
module.exports.getStatuses = getStatuses;
Then i have a file which starts the process:
start.js
'use strict';
const status = require('./index');
status.start();
Then i have a file that I want to execute to get the current status of the apps:
consume.js
'use strict';
const status = require('./index');
console.log(status.getStatuses());
The problem is that consume.js just displays exactly what's in the initial app variable in index.js which is:
{
myApp: {
url: 'http://myUrl.com',
status: null,
lastUpdatedAt: new Date()
}
};
while the process running the start() command is displaying an updated status that is NOT null.
How can I make it so consume.js can see the value of a variable that start.js is updating?
I'd like to not have to use a datastore if possible. Worst case scenario is i write to a file, run redis, mongo, or some other datastore but i'm trying to avoid that making this app as simple as possible.
You are using the same code, index.js in both start.js and consume.js, but creating two separate instances of it when you run each file.
That is, the apps variable is changing in the instance created by start.js, but nothing in consume.js tells your code to change the apps variable.
If you are not saving a history of statuses, or saving the data to a datastore, what's the point in the start routine? You could just call checkAllStatuses and then return the results when you wish to consume the data.
Edit
Here's an example of combining the two files (start.js and consume.js) into one file. It also adds in a sample socket.io implementation since you stated that providing the statuses via websockets to clients was the evntual goal.
var app = require('http').createServer(handler)
var io = require('socket.io')(app);
var fs = require('fs');
//// Your status library
var status = require('./index');
//// Start getting statuses
status.start();
app.listen(80);
//
// This is just the default handler
// in the socket.io example
//
function handler (req, res) {
fs.readFile(__dirname + '/index.html',
function (err, data) {
if (err) {
res.writeHead(500);
return res.end('Error loading index.html');
}
res.writeHead(200);
res.end(data);
});
}
io.on('connection', function (socket) {
// Someone wants the list of statuses
// This uses socket.io acknowledgements
// to return the data. You may prefer to use
// `socket.emit` instead or an altogether different socket library.
socket.on('status_fetch', function (data, callback_fn) {
callback_fn( status.getStatuses() );
});
});

pass file stream for model processing in Sails.js

I have custom Create method I am trying to add for my Founder model in my Sails.js Application. The Application includes files and normal form fields. Following the advice of this tutorial.
I am trying to offload a lot of controller code into the Founder model for processing. This makes something a little awkward and results in the following error.
TypeError: Cannot read property 'stream' of undefined
This is the result of this line in my model function:
createFromForm: function(opts, cb){
var id = opts.id;
var params = opts.params;
var newFilename = opts.avatartwo._files[0].stream.filename;
In the fourth line. Now in the controller the analogous code would be, following Sails syntax:
var newFilename = opts.req.file('avatartwo')_files[0].stream.filename;
I tred simply writing the result of req.file('avatartwo') into an option I passed into the model function via:
var opts = {
params: params,
id: id,
avatartwo: req.file('avatartwo')
};
Although this seems problematic, but because I am not the most informed on the subject, I do not know why. I could appreciate some elucidation here. Thanks!
To handle file upload in sailsjs use following code.
var uploadedFile = req.file('some_file').upload({
dirname: 'path to store the file',/* optional. defaults to assets/uploads I guess*/
saveAs: 'new file name', /* optional. default file name */
maxBytes: 5 * 1024 * 1024 //5 MB
}, function(err, uploadedFiles) {
if (err) {
return res.json(500, err);
} else if (uploadedFiles.length === 0) {
// handle if no files were uploaded
} else {
// do processing with file descriptor available at uploadedFiles[0].fd
// pass to model in your case
var opts = {
params: params,
id: id,
avatartwo: uploadedFiles[0].fd
};
}
});

Errors when using socket.io in AngularJs on user updates

Hi I'm trying to make an automated update on a list of articles whenever the user changes his preferred language.
The way I'm trying to do this is by having a IO socket update whenever the user changes in the database.
However I seem to be unsuccesfull in my endeavors, and I have no idea as to why.
Since I'm new to socket.io I thought I'll ask the coding gods in here for some help.
May the software be with you ^^
PS: the project is a Angular fullstack project, scaffolded with Yeoman
Code time!
client/ components/ articlebar/ articlebar.controller.js
'use strict';
angular.module('unityAcademyApp')
.controller('ArticlebarCtrl', function ($scope, $location, Auth, socket) {
$scope.articles = {};
function populateArticles(){
...
Some functionality where $scope.articles are set
...
};
socket.syncUpdates('user', $scope.articles, function() {
console.log('hit');
populateArticles();
});
});
client/ components/ socket/ socket.service.js
/* global io */
'use strict';
angular.module('unityAcademyApp')
.factory('socket', function(socketFactory) {
// socket.io now auto-configures its connection when we ommit a connection url
var ioSocket = io('', {
// Send auth token on connection, you will need to DI the Auth service above
// 'query': 'token=' + Auth.getToken()
path: '/socket.io-client'
});
var socket = socketFactory({
ioSocket: ioSocket
});
return {
socket: socket,
/**
* Register listeners to sync an array with updates on a model
*
* Takes the array we want to sync, the model name that socket updates are sent from,
* and an optional callback function after new items are updated.
*
* #param {String} modelName
* #param {Array} array
* #param {Function} cb
*/
syncUpdates: function (modelName, array, cb) {
cb = cb || angular.noop;
/**
* Syncs item creation/updates on 'model:save'
*/
socket.on(modelName + ':save', function (item) {
var oldItem = _.find(array, {_id: item._id});
var index = array.indexOf(oldItem); // this is line 39
var event = 'created';
// replace oldItem if it exists
// otherwise just add item to the collection
if (oldItem) {
array.splice(index, 1, item);
event = 'updated';
} else {
array.push(item);
}
cb(event, item, array);
});
/**
* Syncs removed items on 'model:remove'
*/
socket.on(modelName + ':remove', function (item) {
var event = 'deleted';
_.remove(array, {_id: item._id});
cb(event, item, array);
});
},
/**
* Removes listeners for a models updates on the socket
*
* #param modelName
*/
unsyncUpdates: function (modelName) {
socket.removeAllListeners(modelName + ':save');
socket.removeAllListeners(modelName + ':remove');
}
};
});
server/ config/ socketio.js
/**
* Socket.io configuration
*/
'use strict';
var config = require('./environment');
// When the user disconnects.. perform this
function onDisconnect(socket) {}
// When the user connects.. perform this
function onConnect(socket) {
// When the client emits 'info', this listens and executes
socket.on('info', function (data) {
console.info('[%s] %s', socket.address, JSON.stringify(data, null, 2));
});
// Insert sockets below
require('../api/translation/translation.socket').register(socket);
require('../api/comment/comment.socket').register(socket);
require('../api/article/article.socket').register(socket);
require('../api/language/language.socket').register(socket);
require('../api/thing/thing.socket').register(socket);
require('../api/user/user.socket').register(socket);
}
module.exports = function (socketio) {
// socket.io (v1.x.x) is powered by debug.
// In order to see all the debug output, set DEBUG (in server/config/local.env.js) to including the desired scope.
//
// ex: DEBUG: "http*,socket.io:socket"
// We can authenticate socket.io users and access their token through socket.handshake.decoded_token
//
// 1. You will need to send the token in `client/components/socket/socket.service.js`
//
// 2. Require authentication here:
// socketio.use(require('socketio-jwt').authorize({
// secret: config.secrets.session,
// handshake: true
// }));
socketio.on('connection', function (socket) {
socket.address = socket.handshake.address !== null ?
socket.handshake.address.address + ':' + socket.handshake.address.port :
process.env.DOMAIN;
socket.connectedAt = new Date();
// Call onDisconnect.
socket.on('disconnect', function () {
onDisconnect(socket);
console.info('[%s] DISCONNECTED', socket.address);
});
// Call onConnect.
onConnect(socket);
console.info('[%s] CONNECTED', socket.address);
});
};
server/ api/ user/ user.socket.js
/**
* Broadcast updates to client when the model changes
*/
'use strict';
var User = require('./user.model');
exports.register = function(socket) {
User.schema.post('save', function (doc) {
onSave(socket, doc);
});
User.schema.post('remove', function (doc) {
onRemove(socket, doc);
});
}
function onSave(socket, doc, cb) {
socket.emit('user:save', doc);
}
function onRemove(socket, doc, cb) {
socket.emit('user:remove', doc);
}
Errors encountered so far
So far I get the following error when running the code
TypeError: array.indexOf is not a function
at Socket.<anonymous> (socket.service.js:39)
at socket.js:24
at angular.js:17782
at completeOutstandingRequest (angular.js:5490)
at angular.js:5762
(anonymous function) # angular.js:12416
$get # angular.js:9203
(anonymous function) # angular.js:17785
completeOutstandingRequest # angular.js:5490
(anonymous function) # angular.js:5762
I'm not sure why you get that error, but I think I know why your data isn't updating.
You have to wrap your callback functions inside a $timeout function in order to trigger your changes. For example, you could do this:
$timeout(function(){
cb(event, item, array);
}, 0);
Remember to include $timeout directive in your socket factory.
what's 'undescore' mean? I'm not sure about the 'undescore', but I guess that's alias for 'this'. I think you should init var _ = this.
I just guessing.
I found the problem.
The error where due to it looking for a user in a list of articles, which returned undefined due to there not being any match. The solution was therefore to change the code in client/ components/ articlebar/ articlebar.controller.js
from
socket.syncUpdates('user', $scope.articles, function() {
console.log('hit');
populateArticles();
});
to
socket.syncUpdates('user', $scope.users, function() {
console.log('hit');
populateArticles();
});

Node.js respond with asynchronous data

Recently I started learning a little bit about Node.js and it's capabilities and tried to use it for some web services.
I wanted to create a web service which will serve as a proxy for web requests.
I wanted my service to work that way:
User will access my service -> http://myproxyservice.com/api/getuserinfo/tom
My service will perform request to -> http://targetsite.com/user?name=tom
Responded data would get reflected to the user.
To implement it I used the following code:
app.js:
var express = require('express');
var bodyParser = require('body-parser');
var app = express();
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
var proxy = require('./proxy_query.js')
function makeProxyApiRequest(name) {
return proxy.getUserData(name, parseProxyApiRequest);
}
function parseProxyApiRequest(data) {
returned_data = JSON.parse(data);
if (returned_data.error) {
console.log('An eror has occoured. details: ' + JSON.stringify(returned_data));
returned_data = '';
}
return JSON.stringify(returned_data);
}
app.post('/api/getuserinfo/tom', function(request, response) {
makeProxyApiRequest('tom', response);
//response.end(result);
});
var port = 7331;
proxy_query.js:
var https = require('https');
var callback = undefined;
var options = {
host: 'targetsite.com',
port: 443,
method: 'GET',
};
function resultHandlerCallback(result) {
var buffer = '';
result.setEncoding('utf8');
result.on('data', function(chunk){
buffer += chunk;
});
result.on('end', function(){
if (callback) {
callback(buffer);
}
});
}
exports.getUserData = function(name, user_callback) {
callback = user_callback
options['path'] = user + '?name=' + name;
var request = https.get(options, resultHandlerCallback);
request.on('error', function(e){
console.log('error from proxy_query:getUserData: ' + e.message)
});
request.end();
}
app.listen(port);
I wish I didn't screwed this code because I replaced some stuff to fit my example.
Anyway, the problem is that I want to post the response to the user when the HTTP request is done and I cant find how to do so because I use express and express uses asynchronous calls and so do the http request.
I know that if I want to do so, I should pass the makeProxyApiRequest the response object so he would be able to pass it to the callback but it is not possible because of asyn problems.
any suggestions?
help will be appreciated.
As you're using your functions to process requests inside your route handling, it's better to write them as express middleware functions, taking the specific request/response pair, and making use of express's next cascade model:
function makeProxyApiRequest(req, res, next) {
var name = parseProxyApiRequest(req.name);
res.locals.userdata = proxy.getUserData(name);
next();
}
function parseProxyApiRequest(req, res, next) {
try {
// remember that JSON.parse will throw if it fails!
data = JSON.parse(res.locals.userdata);
if (data .error) {
next('An eror has occoured. details: ' + JSON.stringify(data));
}
res.locals.proxyData = data;
next();
}
catch (e) { next("could not parse user data JSON."); }
}
app.post('/api/getuserinfo/tom',
makeProxyApiRequest,
parseProxyApiRequest,
function(req, res) {
// res.write or res.json or res.render or
// something, with this specific request's
// data that we stored in res.locals.proxyData
}
);
Even better would be to move those middleware functions into their own file now, so you can simply do:
var middleware = require("./lib/proxy_middleware");
app.post('/api/getuserinfo/tom',
middleware.makeProxyApiRequest,
middleware.parseProxyApiRequest,
function(req, res) {
// res.write or res.json or res.render or
// something, with this specific request's
// data that we stored in res.locals.proxyData
}
);
And keep your app.js as small as possible. Note that the client's browser will simply wait for a response by express, which happens once res.write, res.json or res.render etc is used. Until then the connection is simply kept open between the browser and the server, so if your middleware calls take a long time, that's fine - the browser will happily wait a long time for a response to get sent back, and will be doing other things in the mean time.
Now, in order to get the name, we can use express's parameter construct:
app.param("name", function(req, res, next, value) {
req.params.name = value;
// do something if we need to here, like verify it's a legal name, etc.
// for instance:
var isvalidname = validator.checkValidName(name);
if(!isvalidname) { return next("Username not valid"); }
next();
});
...
app.post("/api/getuserinfo/:name", ..., ..., ...);
Using this system, the :name part of any route will be treated based on the name parameter we defined using app.param. Note that we don't need to define this more than once: we can do the following and it'll all just work:
app.post("/api/getuserinfo/:name", ..., ..., ...);
app.post("/register/:name", ..., ..., ... );
app.get("/api/account/:name", ..., ..., ... );
and for every route with :name, the code for the "name" parameter handler will kick in.
As for the proxy_query.js file, rewriting this to a proper module is probably safer than using individual exports:
// let's not do more work than we need: http://npmjs.org/package/request
// is way easier than rolling our own URL fetcher. In Node.js the idea is
// to write as little as possible, relying on npmjs.org to find you all
// the components that you need to glue together. If you're writing more
// than just the glue, you're *probably* doing more than you need to.
var request = require("request");
module.exports = {
getURL: function(name, url, callback) {
request.get(url, function(err, result) {
if(err) return callback(err);
// do whatever processing you need to do to result:
var processedResult = ....
callback(false, processedResult);
});
}
};
and then we can use that as proxy = require("./lib/proxy_query"); in the middleware we need to actually do the URL data fetching.

Creating a Mockup REST API

I'm currently trying to create a NodeJS server or something similar to mockup a REST API that reads in a JSON file and responds to requests with that data. I really only need GET requests supported. What is the best way to go about this?
Here's what I have so far:
/**
* Sample items REST API
*/
function ItemsRepository() {
this.items = [];
}
ItemsRepository.prototype.find = function (id) {
var item = this.items.filter(function(item) {
return item.itemId == id;
})[0];
if (null == item) {
throw new Error('item not found');
}
return item;
}
/**
* Retrieve all items
* items: array of items
*/
ItemsRepository.prototype.findAll = function () {
return this.items;
}
/**
* API
*/
var express = require('express');
var app = express();
var itemRepository = new ItemsRepository();
app.configure(function () {
// used to parse JSON object given in the body request
app.use(express.bodyParser());
});
/**
* HTTP GET /items
* items: the list of items in JSON format
*/
app.get('/items', function (request, response) {
response.json({items: itemRepository.findAll()});
});
/**
* HTTP GET /items/:id
* Param: :id is the unique identifier of the item you want to retrieve
* items: the item with the specified :id in a JSON format
* Error: 404 HTTP code if the item doesn't exists
*/
app.get('/items/:id', function (request, response) {
var itemId = request.params.id;
try {
response.json(itemRepository.find(itemId));
} catch (exception) {
response.send(404);
}
});
app.listen(8080); //to port on which the express server listen
I know that I would use the following to include the file, I just don't know how to stuff the data into Items.
var responseItemsData = require('./items-list.json');
This is trivial in node. You can load the data by requiring the .json file directly
var responseData = require('./my-json-file'); //.json extension optional
//Do this during your startup code, not during the request handler
Then to send it:
res.write(JSON.stringify(responseData));
The rest of the code you need is easily available in almost every node.js tutorial on the web.
You can use jasmine+sinon:
var Episode = Backbone.Model.extend({
url: function() {
return "/episode/" + this.id;
}
});
beforeEach(function() {
this.server = sinon.fakeServer.create();
});
afterEach(function() {
this.server.restore();
});
it("should fire the change event", function() {
var callback = sinon.spy();
this.server.respondWith("GET", "/episode/123",
[200, {"Content-Type": "application/json"},'{"id":123,"title":"Hollywood - Part 2"}']);
var episode = new Episode({id: 123});
// Bind to the change event on the model
episode.bind('change', callback);
// makes an ajax request to the server
episode.fetch();
// Fake server responds to the request
this.server.respond();
// Expect that the spy was called with the new model
expect(callback.called).toBeTruthy();
expect(callback.getCall(0).args[0].attributes)
.toEqual({id: 123,
title: "Hollywood - Part 2"});
});
more details in: https://github.com/cld-santos/simplologia/tree/master/javascript-lessons/src/test/javascript/Sinon
The easiest way is to simply use the static middleware.
var express = require('express');
var app = express();
app.use('/api', express.static(__dirname + '/data'));
app.use('.*', express.static(__dirname + '/assets'));
This assumes that you are eventually going to put the REST api at /api but that while you're testing your data is going to be in the data directory and that your CSS/JS/HTML is in the assets folder. In reality you can put it whereever you want, but you now can put all your dev json separate from your code.
I created a tool for this purpose
https://github.com/homerquan/kakuen

Categories