Integrating opbeat with sails - sails.js

I'm trying to integrate opbeat with sails.js. They have a node.js client which includes middleware support for Connect and Express.
I've tried to create a custom middleware in http.js
module.exports.http = {
middleware: {
opbeat : require('opbeat')({
organizationId: '...',
appId: '...',
secretToken: '...'
}),
order: [
'opbeat',
'startRequestTimer',
'cookieParser',
'session',
'myRequestLogger',
'bodyParser',
'handleBodyParserError',
'compress',
'methodOverride',
'poweredBy',
'$custom',
'router',
'www',
'favicon',
'404',
'500'
],
}
};
Unfortunately it doesn't work. If you could please help point me in the right direction.

The value returned from the initializer function isn't a proper middleware function (it's just an Opbeat client). To get the middleware function, call middleware.connect() on the client:
var opbeat = require('opbeat')({
organizationId: '...',
appId: '...',
secretToken: '...'
})
module.exports.http = {
middleware: {
opbeat: opbeat.middleware.connect(), // get the Opbeat middleware function
order: [
... // put the bulk of your middleware here
'opbeat'
]
}
}
P.S. The function is called middleware.connect() because it was the connect module that set the standard of having a middleware function that expects the 3 arguments; Request, Response and Callback. An alias exists that is called middleware.express() - but it's just that: An alias.
Update:
I reversed the order of the middleware in the example above so that Opbeat is placed at the end. This is important to that that it can catch errors tickling down the middleware-chain.

Related

Integration of Ltijs into existing sails app

I want to use the Ltijs library (https://cvmcosta.me/ltijs) in our Sails application.
The way to deploay Ltijs as part of another express server is this (from Ltijs documentation):
const app = express()
lti.setup('EXAMPLEKEY', { url: 'mongodb://localhost/database' })
// Start LTI provider in serverless mode
await lti.deploy({ serverless: true })
// Mount Ltijs express app into preexisting express app with /lti prefix
app.use('/lti', lti.app)
The way to put middleware into Sails is something like that (without app.use(...)!):
// config/http.js
const lti = require('ltijs').Provider;
lti.setup(
'8swieleivBef',
{url: 'mongodb://localhost:27017/mysailsapp'},
);
lti.deploy({serverless: true});
module.exports.http = {
middleware: {
order: [
'cookieParser',
'session',
'bodyParser',
'ltiExpressAdapter', //<-------- my middleware adapter -----------------
'compress',
'poweredBy',
'router',
'www',
'favicon',
],
ltiExpressAdapter: lti.app, //<-------- my middleware adapter -----------------
.
.
.
The latter works, but it works to "good", because now every request is caught by Ltijs and the application doesn't work anymore.
My Question is, how do I bring the path '/lti' from app.use('/lti', lti.app) into the sails game?
I tried lots of things like this that didn't work:
ltiExpressAdapter: (function () {
return async function (req, res, next) {
if (req.path.match(/^\/lti.*$/)) {
return lti.app;
}
return next();
};
})(),
Thanks in advance for help!
Seems I found a solution using app.use(...):
// config/http.js
const express = require('express'); //<------ NEW -----
const app = express(); //<------ NEW -----
const lti = require('ltijs').Provider;
lti.setup(
'8swieleivBef',
{url: 'mongodb://localhost:27017/ltijsdb'}, //<------ (NEW) -----
);
lti.deploy({serverless: true});
module.exports.http = {
middleware: {
order: [
'cookieParser',
'session',
'bodyParser',
'ltiExpressAdapter', //<-------- my middleware adapter -----------------
'compress',
'poweredBy',
'router',
'www',
'favicon',
],
ltiExpressAdapter: app.use('/lti', lti.app), //<------ NEW -----
.
.
.
Now I get the expexted error message from Ltijs only when I call the /lti path (http://localhost:1337/lti) and the rest of the application runs like before.
Now I hopefully can go on setting up Ltijs and try to connect from a test consumer.
(I also changed the Ltijs DB so that it isn't mixed up with my App's DB.)

using connect-flash with sails.js

I am trying to use connect-flash with sails.js, according to middleware documentation of sailsjs http://sailsjs.org/documentation/concepts/middleware , this is what I did
passportInit : require('passport').initialize(),
passportSession : require('passport').session(),
flash : require('connect-flash'),
order: [
'startRequestTimer',
'cookieParser',
'session',
'passportInit',
'passportSession',
'flash',
'myRequestLogger',
'bodyParser',
'handleBodyParserError',
'compress',
'methodOverride',
'poweredBy',
'$custom',
'router',
'www',
'favicon',
'404',
'500'
]
and this is how my AuthController.js looks ( as I am using connect-flash with passport )
module.exports = {
_config: {
actions: false,
shortcuts: false,
rest: false
},
'login': function(req, res,next) {
passport.authenticate('local', { successRedirect: '/user/dashboard',
failureRedirect: '/login',
failureFlash: true })(res,req,next);
},
'logout': function(req, res) {
req.logout();
res.redirect('/');
}
};
But after adding flash in the queue pages never finish loading and it never renders. Please can anybody assist me with this? I am not sure how to make this work. Thanks in advance.
I've had more success with Express' flash module.
In my config/http.js:
middleware : {
flash : require('flash')(),
...
order : [
...
'passportInit',
'passportSession',
'flash',
'bodyParser',
...
],
...
}
I spent quite some time and was not able to get this package working with sales, I am sure I must be missing something, However I found another package sails-hook-flash that is plug and play for sails app.

Sails.js change bodyParser for specific paths

In Sails.js 0.10.5, I want to replace bodyParser for specific paths. For example, use a different body parser for path '/app/upload' and for the rest use the default. How do I do this?
You can do this by overriding config/http.js. Add your custom parser to the middleware, and replace bodyParser in the order with your custom parser.
Something like this should work
module.exports.http = {
middleware: {
superBodyParser: function (req, res, next) {
if (req.path === '/app/upload') {
// your custom parser
}
else {
require('skipper')(req, res, next);
}
},
order: [
'startRequestTimer',
'cookieParser',
'session',
'myRequestLogger',
// 'bodyParser', <-- not required anymore
'superBodyParser'
'handleBodyParserError',
'compress',
'methodOverride',
'poweredBy',
'$custom',
'router',
'www',
'favicon',
'404',
'500'
]
}
};

SailsJS: add a middleware before the defined express middlewares for debugging

General Problem
Sails JS is built upon express JS, for debugging reasons I would like to inject a middleware or a function in Sails JS before cookieParser/bodyParser, I expect that Sails JS wrapped the following:
app.use(cookieParser());
app.use(bodyParser());
etc...
I would like to inject my own middleware function before these injections, how can I do that? for example in order to track the initial request.
Specific problem:
I integrated with Passport JS, sensitive users info are in request (see below), I am pretty sure this is due to request parsing middleware like cookieParser/bodyParser, but I would like to know how to confirm this myself.
(I would also be happy for a confirmation from you)
When I print the request, the user information is there, specifically, the password ("password: '$2a$10$rfRptIm7o1BKD1Qdr7yPUeWVisEHyZciCdD0ebivLAm8PPVRUicES',")
Here is the partial request:
_passport:
{ instance:
{ _key: 'passport',
_strategies: [Object],
_serializers: [Object],
_deserializers: [Object],
_infoTransformers: [],
_framework: [Object],
_userProperty: 'user',
Authenticator: [Function: Authenticator],
Passport: [Function: Authenticator],
Strategy: [Object],
strategies: [Object] },
session: { user: '532ea818e6221c90251e9342' } },
user:
{ username: 'nizar',
password: '$2a$10$rfRptIm7o1BKD1Qdr7yPUeWVisEHyZciCdD0ebivLAm8PPVRUicES',
createdAt: Sun Mar 23 2014 11:23:36 GMT+0200 (Jerusalem Standard Time),
updatedAt: Sun Mar 23 2014 11:23:36 GMT+0200 (Jerusalem Standard Time),
id: '532ea818e6221c90251e9342' },
while in the model I toJSON and deleted the password:
toJSON: function() {
var obj = this.toObject();
delete obj.password;
return obj;
}
1.General Problem
For Sails 0.9.x, the middlwares loaded by the sails server are defined in sails/lib/express/index.js. As you can see, the custom middlware defined in config/express.js is used after cookieParser, session, bodyParser and methodOverride. To inject a custom middleware before cookieParser and bodyParser, you can override the cookieParser or modify sails/lib/express/index.js directly.
For Sails 0.10.x, you can define a custom loadMiddleware function (default implementation is in sails/lib/hooks/http/loadMiddleware.js) in config/express.js. Scott Gress has explained this in detail.
2.Specific Problem
If you don't want the request object to include the password information, call toJSON() in the callback function of deserializeUser:
passport.deserializeUser(function(id, done) {
User.findOneById(id).done(function(err, user) {
done(err, user.toJSON());
});
});
and in the callback function of the strategy definition:
passport.use('local',
new LocalStrategy({
usernameField: 'userename',
passwordField: 'password'
},
function(username, password, done) {
User.findOne({ name: username}).done(function(err, user) {
if (err) return done(err);
if (!user) {
return done(null, false, {message: 'Unknown user'+username});
};
if (!user.validatePassword(password)) {
return done(null, false, {message: 'Invalid password!'});
};
return done(null, user.toJSON());
});
}
));
For Sails v0.10, you can follow the procedure in this answer to insert custom middleware before the body parser. In v0.9.x, you'd have to create your own bodyParser callback and add the middleware in there; see this answer for more info.
However in your case I'm not sure any of this is necessary. Passport is adding user data to the request object on the server as a convenience, but it's not actually being transmitted in the request. So, there's no sensitive data out in the open--it's just in memory. Your only concern is to keep it from being sent back to the client, which you're already doing with your toJSON method on the User model.

Using Grunt to Mock Endpoints

I'm using Yeoman, Grunt, and Bower, to construct a platform for building a frontend independently of a a backend. The idea would be that all of my (AngularJS) controller, services, factories, etc live in this project, and get injected afterwards into my serverside codebase based off the result of grunt build.
My question is:
How can I mock endpoints so that the Grunt server responds to the same endpoints as my (Rails) App will?
At the moment I am using:
angular.module('myApp', ['ngResource'])
.run(['$rootScope', function ($rootScope) {
$rootScope.testState = 'test';
}]);
And then in each of my individual services:
mockJSON = {'foo': 'myMockJSON'}
And on every method:
if($rootScope.testState == 'test'){
return mockJSON;
}
else {
real service logic with $q/$http goes here
}
Then after grunt build, testState = 'test' gets removed.
This is clearly a relatively janky architecture. How can I avoid it? How can I have Grunt respond to the same endpoints as my app (some of which have dynamic params) apply some logic (if necessary), and serve out a json file (possibly dependent on path params)?
I've fixed this issue by using express to write a server that responds with static json.
First I created a directory in my project called 'api'. Within that directory I have the following files:
package.json:
{
"name": "mockAPI",
"version": "0.0.0",
"dependencies": {
"express": "~3.3.4"
}
}
Then I run npm install in this directory.
index.js:
module.exports = require('./lib/server');
lib/server.js:
express = require('express');
var app = express();
app.get('/my/endpoint', function(req, res){
res.json({'foo': 'myMockJSON'});
});
module.exports = app
and finally in my global Gruntfile.js:
connect: {
options: {
port: 9000,
hostname: 'localhost',
},
livereload: {
options: {
middleware: function (connect, options) {
return [
lrSnippet,
mountFolder(connect, '.tmp'),
mountFolder(connect, yeomanConfig.app),
require('./api')
];
}
}
},
Then the services make the requests, and the express server serves the correct JSON.
After grunt build, the express server is simply replaced by a rails server.
As of grunt-contrib-connect v.0.7.0 you can also just add your custom middleware to the existing middleware stack without having to manually rebuild the existing middleware stack.
livereload: {
options: {
open: true,
base: [
'.tmp',
'<%= config.app %>'
],
middleware: function(connect, options, middlewares) {
// inject a custom middleware into the array of default middlewares
middlewares.push(function(req, res, next) {
if (req.url !== '/my/endpoint') {
return next();
}
res.writeHead(200, {'Content-Type': 'application/json' });
res.end("{'foo': 'myMockJSON'}");
});
return middlewares;
}
}
},
See https://github.com/gruntjs/grunt-contrib-connect#middleware for the official documentation.
Alternatively you can use the grunt-connect-proxy to proxy everything that is missing in your test server to an actual backend.
It's quite easy to install, just one thing to remember when adding proxy to your livereload connect middleware is to add it last, like this:
middleware: function (connect) {
return [
lrSnippet,
mountFolder(connect, '.tmp'),
mountFolder(connect, yeomanConfig.app),
proxySnippet
];
}
grunt-connect-prism is similar to the Ruby project VCR. It provides an easy way for front end developers to record HTTP responses returned by their API (or some other remote source) and replay them later. It's basically an HTTP cache, but for developers working on a Single Page Application (SPA). You can also generate stubs for API calls that don't exist, and populate them the way you want.
It's useful for mocking complex & high latency API calls during development. It's also useful when writing e2e tests for your SPA only, removing the server from the equation. This results in much faster execution of your e2e test suite.
Prism works by adding a custom connect middleware to the connect server provided by the grunt-contrib-connect plugin. While in 'record' mode it will generate a file per response on the filesystem with content like the following:
{
"requestUrl": "/api/ponies",
"contentType": "application/json",
"statusCode": 200,
"data": {
"text": "my little ponies"
}
}
DISCLAIMER: I'm the author of this project.
You can use Apache proxy and connect your REST server with gruntjs.
Apache would do this:
proxy / -> gruntjs
proxy /service -> REST server
you would use your application hitting Apache and angular.js application would think that is talking with itself so no cross domain problem.
Here is a great tutorial on how to set this up:
http://alfrescoblog.com/2014/06/14/angular-js-activiti-webapp-with-activiti-rest/
Just my alternative way that based on Abraham P's answer. It does not need to install express within 'api' folder. I can separate the mock services for certain files. For example, my 'api' folder contains 3 files:
api\
index.js // assign all the "modules" and then simply require that.
user.js // all mocking for user
product.js // all mocking for product
file user.js
var user = function(req, res, next) {
if (req.method === 'POST' && req.url.indexOf('/user') === 0) {
res.end(
JSON.stringify({
'id' : '5463c277-87c4-4f1d-8f95-7d895304de12',
'role' : 'admin'
})
);
}
else {
next();
}
}
module.exports = user;
file product.js
var product = function(req, res, next) {
if (req.method === 'POST' && req.url.indexOf('/product') === 0) {
res.end(
JSON.stringify({
'id' : '5463c277-87c4-4f1d-8f95-7d895304de12',
'name' : 'test',
'category': 'test'
})
);
}
else {
next();
}
}
module.exports = product;
index.js just assigns all the "modules" and we simply require that.
module.exports = {
product: require('./product.js'),
user: require('./user.js')
};
My Gruntfile.js file
connect: {
options: {
port: 9000,
// Change this to '0.0.0.0' to access the server from outside.
hostname: 'localhost',
livereload: 35729
},
livereload: {
options: {
open: true,
middleware: function (connect) {
return [
connect.static('.tmp'),
connect().use(
'/bower_components',
connect.static('./bower_components')
),
connect.static(appConfig.app),
require('./api').user,
require('./api').product,
];
}
}
}