In sails.js: fetch model's data from remote server - sails.js

my sails.js app is embedded in php project where php generate some date for sails model. Can't find the way to fetch this date in beforeCreate callback or some custom method. I don't want use db to sync 2 models (model from sails & model from php). And i need to send some date to remote php app.
sails.js v.0.9.x
here is my controller:
module.exports = {
index: function (req, res) {},
create: function (req, res) {
if ( !req.param('login') )
throw new Error('Login is undefined');
if ( !req.param('message') )
throw new Error('Initial message is undefined');
var user, thread;
User.create({
id: 1,
name: req.param('login'),
ip: req.ip
}).done( function (err, model) {
user = model;
if (err) {
return res.redirect('/500', err);
}
user.fetch(); // my custom method
});
return res.view({ thread: thread });
}
};
and model:
module.exports = {
attributes: {
id: 'integer',
name: 'string',
ip: 'string',
fetch: function (url) {
var app = sails.express.app;
// suggest this but unlucky :)
app.get('/path/to/other/loc', function (req, res, next) {
console.log(req, res)
return next()
});
}
}
};
UPD My solution
Model:
beforeCreate: function (values, next) {
var options = 'http://localhost:1337/test',
get = require('http').get;
get(options, function (res) {
res.on('data', function (data) {
_.extend( values, JSON.parse( data.toString() ) );
next();
});
}).on('error', function () {
throw new Error('Unable to fetch remote data');
next();
});
}

Yikes--app.get is nowhere near what you need. That's binding a route handler inside of your app and has nothing to do with requesting remote data. Don't do this.
The easiest way to fetch remote data in Node is using the Request library. First install it in your project directory using npm install request. Then at the top of your model file:
var request = require('request');
and in your fetch method:
fetch: function(url, callback) {
request.get(url, function(error, response, body) {
return callback(error, body);
});
}
Note the added callback parameter for fetch; this is needed because the request is an asynchronous operation. That is, the call to fetch() will return immediately, but the request will take some time, and when it's done it will send the result back via the callback function. Seeing as fetch is at this point just a wrapper around request.get, I'm not sure why it's necessary to have it as a model method at all, but if the URL was based on something within the model instance then it would make sense.

Related

Meteor fetch server-side job without collection

I want to submit a request from my Meteor client to the server, that has the server make an HTTP request to a website, and then return the response to the client.
On a REST web server, I would make an HTTP GET from the client to the server, which would then make its own request and respond to the client.
I haven't added a REST interface to my Meteor app and don't want to add this overhead for just this one need. However, using collections to get this done is unweildy and not the right tool.
Is there any way for the Meteor client to securely ask the server to do something and get a response without using collections? I'm messing with meteor Methods such as:
Meteor.methods({
'/http/get'(name, cbk) {
cbk = cbk || function() {};
HTTP.get('http://www.google.com', {}, (err, data) => {
cbk(err, data);
});
},
});
However this isn't seeming to work. The call is being made on the Client side.
This is exactly what Meteor methods are for.
Meteor methods docs
Server
First define your method on your server:
Meteor.methods({
// Namespace for clarity
'make.rest_call'(callback) {
HTTP.get('http://www.google.com', {}, (err, data) => {
callback(err, data);
});
}
});
OR
If you need the client to do something with the data then return a promise here (promise docs)
Meteor.methods({
// Namespace for clarity
'make.rest_call'(callback) {
return new Promise((resolve, reject) => {
HTTP.get('http://www.google.com', {}, (err, data) => {
if (err) { reject(err); }
resolve(data);
});
}
}
});
Client
Then call it from your client:
// Simple call (just makes the call, does nothing on the client)
Meteor.call('make.rest_call');
OR
// Promise based call
Meteor.call('make.rest_call', (error, result) => {
if (error) { /* do something with error */ }
// result contains your promise.
result.then((data) => {
// do something with returned data
});
});

Unable to loadDatabase with lokiJs

I have trying to create a database with a collection added to this database and the changes saved to a IndexedDB.
Below is my code
Two controllers SaveController and LoadController.
myApp.controller('SaveController', ['$scope', 'Loki', function ($scope, Loki) {
// SAVE : will save App/Key/Val as 'finance'/'test'/{serializedDb}
// if appContect ('finance' in this example) is omitted, 'loki' will be used
var idbAdapter = new LokiIndexedAdapter('finance');
var db = new loki('ProjectDb', { adapter: idbAdapter });
var coll = db.addCollection('SampleCollection');
coll.insert({ SampleId: 'Sample text.....' });
db.saveDatabase(); // could pass callback if needed for async complete
}]);
and then in my LoadController i use the
myApp.controller('LoadController', ['$scope', 'Loki', function ($scope, Loki) {
var idbAdapter = new LokiIndexedAdapter('finance');
var db = new loki('ProjectDb', { adapter: idbAdapter, autoload: true });
db.loadDatabase({}, function (result) {
console.log(result);
});
alert(db.getCollection("SampleCollection"));
}]);
I get a null when i alert "alert(db.getCollection("SampleCollection"));" . It never enters the call back of the "loadDatabase" method.
Is there something that i am missing ?
IndexedDB in Browser
Here the page html
Edit for default localstorage implementation
I use the default implementation for loki js and i try to load the offline db is shows result as null every time even though the db exist
var offlineDb = new loki('DbOfflineNew');
offlineDb.loadDatabase({},function (result) {
console.log(result);
if (result == null) {
alert('loading for first time..');
}
else {
alert('existing load..');
}
});
Every time the alert "loading for first time.. " is fired.. Any thing i am missing here..?
Basically all your logic needs to be in the loadDatabase callback. If you try to console.log the collection before it's loaded it will be null. Many people fall in this trap.
In other words:
myApp.controller('LoadController', ['$scope', 'Loki', function ($scope, Loki) {
var idbAdapter = new LokiIndexedAdapter('finance');
var db = new loki('ProjectDb', { adapter: idbAdapter, autoload: true });
db.loadDatabase({}, function (result) {
console.log(result);
// put your log call here.
alert(db.getCollection("SampleCollection"));
});
}]);
Hope this helps.

sails.js model reference error

I am studying Sails.js and I have a question.
I made a model object using "sails generate model site" command.
and I made init.js file in config directory for my cronjob.
everytime system launched, cronjob starts from this init.js.
this is init.js
var fs = require('fs'),
sails = require('sails'),
async = require('async');
exports.initSite = function () {
'use strict';
sails.log.debug('init method start!');
async.waterfall([
function (callback) {
Site.find().exec(function (err, sites) {
if (err) {
callback(err);
}
if (sites) {
async.forEachSeries(sites,
function (siteData, callback2) {
siteData.remove(function (err) {
if (err) {
callback(err);
}
callback2();
});
}, function (err) {
if (err) {
callback(err);
}
callback();
});
} else {
callback();
}
});
},
function (callback) {
var jsonData = fs.readFile('./urls.json', 'utf8', function (err, datas) {
if (err) {
callback(err);
}
callback(null, datas);
});
},
function (datas, callback) {
var urls = JSON.parse(datas);
for (var key in urls) {
var value = urls[key];
var site = new Site({
siteId: value.id,
name: value.name,
url: value.url,
category: value.category
});
site.save(function(err){
if (err) {
callback(err);
}
});
}
callback();
}
],function(err){
if (err) {
sails.log.error('ERROR!');
}
sails.log.info('INIT OK');
});
};
and I read this init.js from app.js like this.
in app.js
require(__dirname + '/config/init').initSite();
but everytime I launch application, console messages says ReferenceError: Site is not defined.
I don't know why init.js can't read 'Site (model object)'.
your advice is very thankful for me.
(sorry for my bad english.... ;_;)
Sailsjs does not load your models until it has been "lifted". You need to run sails lift first from within the directory that your app.js file has been defined, then you can run this chron job. After sails loads, all your models will be exposed as globals. The code in init.js should be called from app.js after sails lifts.
This is based on the discussion about the above topic, so it's not exactly a "cron job" but it can be executed like one and also have access to all the nice features that Sails.js provides, including models.
You can use node-schedule.
So this is what i did.
Install node-schedule
npm install –save node-schedule
Create a crontab file in config/crontab.js
Paste this code in the crontab.js. What i am doing here is creating a method where i require my cronjob, finally i append the method to the jsonArray, so the config/bootstrap.js will execute the file.
module.exports.crontab = {
/*
* The asterisks in the key are equivalent to the
* schedule setting in crontab, i.e.
* minute hour day month day-of-week year
* so in the example below it will run every minute
*/
crons:function()
{
var jsonArray = [];
jsonArray.push({interval:’*/1 * * * * * ‘,method:’mytest’});
// add more cronjobs if you want like below
// but dont forget to add a new method…
//jsonArray.push({interval:’*/1 * * * * * ‘,method:’anothertest’});
return jsonArray;
},
// declare the method mytest
// and add it in the crons function
mytest: function(){
require(‘../crontab/mytest.js’).run();
}
/*
anothertest:function(){
require(‘../crontab/anothertest.js’).run();
}
*/
};
Open your config/bootstrap.js and add this code
module.exports.bootstrap = function(cb) {
_.extend(sails.hooks.http.app.locals, sails.config.http.locals);
// add the lines from here
// bootstrapping all the cronjobs in the crontab folder
var schedule = require(‘node-schedule’);
sails.config.crontab.crons().forEach(function(item){
schedule.scheduleJob(item.interval,sails.config.crontab[item.method]);
});
// It’s very important to trigger this callback method when you are finished
// with the bootstrap! (otherwise your server will never lift, since it’s waiting on the bootstrap)
cb();
};
Finally run sails l and you should see a message running every minute.

Sails.js Sockets - Best Practices 4/2014

I believe the approach to Sockets within sails.js has changed over the last several months. A little confused on what the best practice should be.
socket.get('/comment/subscribe', function () {
socket.on('comment', function(obj) {
if (obj.verb == 'created') {
var data = obj.data;
if (data.project == 1) {
// This adds it to a visual list.
addComment("data.content");
}
}
});
});
I believe this approach is deprecated. What is the best syntax socket subscription?
Also - am I able to subscribe to a particular criteria of the model, in this case a particular comment or other attribute?
By default when you request a model, you will be subscribed to it. That is the default if you are using blueprints. There is no need to explicitly subscribe.
However, you if you are making your own controller methods. You will need to subscribe socket requests to record updates manually on the server side.
Assuming you have a User model...
To have a socket be notified of all created events call User.watch(req)
For updated and destroyed call User.subscribe(req, records)
Here is a copy of my User controller for a project, I have put comments on the lines that handle subscriptions. hope it helps....
UserController = {
find: (req, res) ->
User.watch(req) // Subscription
if req.params.all().id
query = User.findOne(id: req.params('id'))
else
query = User.find()
query.exec((err, records) =>
return res.send(500, err) if (err)
User.subscribe(req, records) // Subscription
return res.json(records)
)
create: (req, res) ->
User.create(req.body).exec((err, record) =>
return res.send(500, err) if (err)
User.publishCreate(record, record.toJSON()) // Subscription
return res.json(record.toJSON())
)
update: (req, res) ->
User.update(req.params('id'), req.body).exec((err, records) =>
return res.send(500, err) if (err)
return res.notFound() if _.isEmpty(records)
record = _.first(records)
User.publishUpdate(record.id, record.toJSON()) // Subscription
return res.json(record.toJSON())
)
destroy: (req, res) ->
User.destroy(req.params('id')).exec((err, records) =>
return res.send(500, err) if (err)
return res.notFound() if _.isEmpty(records)
record = _.first(records)
User.publishDestroy(record.id) // Subscription
return res.send(204, null)
)
me: (req, res) ->
User.findOne(
id: req.session.user_id
).done((err, user) =>
User.subscribe(req, user) // Subscription
return res.json(user)
)
}
module.exports = UserController
Assuming you want to get some data from the server via socket the code should look something like this on frontend
socket.get('/comment/subscribe', function serverResponse(data) {
// use data
})
on server side in commentController
subscribe: function(req, res) {
//some logic
res.json({
data: 'some random data'
});
}
it depends on what exactly you want to do but this is the basic structure. Also note that sails uses blueprints which is a way to make some basic CRUD routes available without coding them in the controller More info here: http://irlnathan.github.io/sailscasts/blog/2014/01/17/sailscasts-answers-ep8-how-do-blueprint-actions-and-blueprint-routes-work-in-sails/
Regarding the part of the code in your question that starts with socket.on this is an event listener on the client side listening for server to send some data. For example
socket.on('room', function messageReceived(message) {
switch (message.verb) {
case 'created':
addRoom(message.data);
break;
case 'addedTo':
postStatusMessage('room-messages-'+message.id, $('#user-'+message.addedId).text()+' has joined');
increaseRoomCount(message.id);
break;
case 'removedFrom':
postStatusMessage('room-messages-'+message.id, $('#user-'+message.removedId).text()+' has left');
decreaseRoomCount(message.id);
break;
case 'destroyed':
removeRoom(message.id);
break;
case 'messaged':
receiveRoomMessage(message.data);
default:
break;
}
});

Backbone.js Model different url for create and update?

lets say I have a Backbone Model and I create an instance of a model like this:
var User = Backbone.Model.extend({ ... });
var John = new User({ name : 'John', age : 33 });
I wonder if it is possible when I use John.save() to target /user/create when I use John.save() on second time (update/PUT) to target /user/update when I use John.fetch() to target /user/get and when I use John.remove() to target /user/remove
I know that I could define John.url each time before I trigger any method but I'm wondering if it could be happen automatically some how without overriding any Backbone method.
I know that I could use one url like /user/handle and handle the request based on request method (GET/POST/PUT/DELETE) but I'm just wondering if there is a way to have different url per action in Backbone.
Thanks!
Methods .fetch(), .save() and .destroy() on Backbone.Model are checking if the model has .sync() defined and if yes it will get called otherwise Backbone.sync() will get called (see the last lines of the linked source code).
So one of the solutions is to implement .sync() method.
Example:
var User = Backbone.Model.extend({
// ...
methodToURL: {
'read': '/user/get',
'create': '/user/create',
'update': '/user/update',
'delete': '/user/remove'
},
sync: function(method, model, options) {
options = options || {};
options.url = model.methodToURL[method.toLowerCase()];
return Backbone.sync.apply(this, arguments);
}
}
To abstract dzejkej's solution one level further, you might wrap the Backbone.sync function to query the model for method-specific URLs.
function setDefaultUrlOptionByMethod(syncFunc)
return function sync (method, model, options) {
options = options || {};
if (!options.url)
options.url = _.result(model, method + 'Url'); // Let Backbone.sync handle model.url fallback value
return syncFunc.call(this, method, model, options);
}
}
Then you could define the model with:
var User = Backbone.Model.extend({
sync: setDefaultUrlOptionByMethod(Backbone.sync),
readUrl: '/user/get',
createUrl: '/user/create',
updateUrl: '/user/update',
deleteUrl: '/user/delete'
});
Are you dealing with a REST implementation that isn't to spec or needs some kind of workaround?
Instead, consider using the emulateHTTP option found here:
http://documentcloud.github.com/backbone/#Sync
Otherwise, you'll probably just need to override the default Backbone.sync method and you'll be good to go if you want to get real crazy with that... but I don't suggest that. It'd be best to just use a true RESTful interface.
No you can't do this by default with backbone. What you could to is to add to the model that will change the model url on every event the model trigger. But then you have always the problem that bckbone will use POST add the first time the model was saved and PUT for every call afterward. So you need to override the save() method or Backbone.sync as well.
After all it seems not a good idea to do this cause it break the REST pattern Backbone is build on.
I got inspired by this solution, where you just create your own ajax call for the methods that are not for fetching the model. Here is a trimmed down version of it:
var Backbone = require("backbone");
var $ = require("jquery");
var _ = require("underscore");
function _request(url, method, data, callback) {
$.ajax({
url: url,
contentType: "application/json",
dataType: "json",
type: method,
data: JSON.stringify( data ),
success: function (response) {
if ( !response.error ) {
if ( callback && _.isFunction(callback.success) ) {
callback.success(response);
}
} else {
if ( callback && _.isFunction(callback.error) ) {
callback.error(response);
}
}
},
error: function(mod, response){
if ( callback && _.isFunction(callback.error) ) {
callback.error(response);
}
}
});
}
var User = Backbone.Model.extend({
initialize: function() {
_.bindAll(this, "login", "logout", "signup");
},
login: function (data, callback) {
_request("api/auth/login", "POST", data, callback);
},
logout: function (callback) {
if (this.isLoggedIn()) {
_request("api/auth/logout", "GET", null, callback);
}
},
signup: function (data, callback) {
_request(url, "POST", data, callback);
},
url: "api/auth/user"
});
module.exports = User;
And then you can use it like this:
var user = new User();
// user signup
user.signup(data, {
success: function (response) {
// signup success
}
});
// user login
user.login(data, {
success: function (response) {
// login success
}
});
// user logout
user.login({
success: function (response) {
// logout success
}
});
// fetch user details
user.fetch({
success: function () {
// logged in, go to home
window.location.hash = "";
},
error: function () {
// logged out, go to signin
window.location.hash = "signin";
}
});