ANDRIY DACENKO
SOFTWARE ENGINEER
September 16, 2015
SOCKETS, MONGO, PASSPORT
Static
Dynamic
Immediate
Periodic Polling
Long Polling
Forever Frame
WebSockets
CLIENT
SERVER
Time Line
CLIENT
SERVER
Time Line
CLIENT
SERVER
Time Line
$ npm install socket.io
<script src="/socket.io/socket.io.js"></script>
<script>
var socket = io('http://localhost:8080');
socket.on('greet', function (data) {
console.log(data);
socket.emit('message', 'user', 'hello');
});
</script>
var io = require('socket.io')(8080);
io.on('connection', function (socket) {
socket.emit('greet', {
hello: 'world'
});
socket.on('message', function (from, msg) {
console.log('message by ', from, ' saying ', msg);
});
socket.on('disconnect', function () {
socket.emit('user disconnected');
});
});
<script>
var chat = io.connect('http://localhost/chat')
, news = io.connect('http://localhost/news');
chat.on('connect', function () {
chat.emit('hi!');
});
news.on('news', function () {
news.emit('woot');
});
</script>
var io = require('socket.io')(8080);
var chat = io
.of('/chat')
.on('connection', function (socket) {
socket.emit('a message', {
that: 'only'
, '/chat': 'will get'
});
chat.emit('a message', {
everyone: 'in'
, '/chat': 'will get'
});
});
var news = io
.of('/news')
.on('connection', function (socket) {
socket.emit('item', { news: 'item' });
});
<script>
var socket = io();
socket.on('connect', function () {
socket.emit('call me', 'andrew', function (data) {
console.log(data); // data will be 'holla'
});
});
</script>
var io = require('socket.io')(8080);
io.on('connection', function (socket) {
socket.on('call me', function (name, fn) {
fn('holla');
});
});
var io = require('socket.io')(8080);
io.on('connection', function (socket) {
var tweets = setInterval(function () {
getCatsTweets(function (tweet) {
socket.volatile.emit('cats tweet', tweet);
});
}, 100);
socket.on('disconnect', function () {
clearInterval(tweets);
});
});
var io = require('socket.io')(8080);
io.on('connection', function (socket) {
socket.broadcast.emit('new user connected');
});
Create simple server
Create input field
Create list of messages
Estimates: 10 min
Documents
Collections
Queries
{
"_id" : ObjectId("54c955492b7c8eb21818bd09"),
"address" : {
"street" : "2 Avenue",
"zipcode" : "10075",
"building" : "1480",
"coord" : [ -73.9557413, 40.7720266 ],
},
"grades" : [
{
"date" : ISODate("2015-09-16T00:00:00Z"),
"grade" : "A",
"score" : 11
}
]
}
[{
"_id" : ObjectId("55f89665db8b40a199e357fb"),
"name" : "Andrew"
},
{
"_id" : ObjectId("55f8966bdb8b40a199e357fc"),
"name" : "John"
}]
$ mongo
MongoDB shell version: 2.6.7
connecting to: test
>db
test
>use other
switched to db other
>load('show_databases.js')
{
"databases" : [{
"name" : "admin",
"sizeOnDisk" : 1,
"empty" : true
},{
"name" : "test",
"sizeOnDisk" : 1,
"empty" : true
}],
"totalSize" : 83886080,
"ok" : 1
}
// print_all_dos_in_collection.js
cursor = db.collection.find();
while ( cursor.hasNext() ) {
printjson( cursor.next() );
}
# eval method
mongo test --eval \
"printjson(db.collection.find().forEach(printjson))"
# execute for DB
mongo localhost:27017/test show_collection.js
# backup collection
mongodump --db test --collection collection
# backup db
mongodump --host mongodb1.example.net \
--port 37017 \
--username user \
--password pass \
--out /opt/backup/mongodump-2015-09-16
# import
mongoimport --db test \
--collection collecton \
--drop \
--file dataset.json
Import dataset*
Find last 5 records
with `Ave` in street
and score >= 10
Show only street and
matched grades
Estimates: 20 min
db.inventory.insert(
{
item: "ABC1",
details: {
model: "14Q3",
manufacturer: "XYZ Company"
},
stock: [
{ size: "S", qty: 25 },
{ size: "M", qty: 50 }
],
category: "clothing"
}
)
db.inventory.insert(
[{
item: "ABC1",
details: {
model: "14Q3",
manufacturer: "XYZ Company"
}
},
{
item: "ABC2",
details: {
model: "14Q3",
manufacturer: "XYZ Company"
}
}]
)
var bulk = db.inventory.initializeUnorderedBulkOp();
bulk.insert({
item: "ABC1",
details: {
model: "14Q3",
manufacturer: "XYZ Company"
}
}
);
bulk.insert({
item: "ABC2",
details: {
model: "14Q3",
manufacturer: "XYZ Company"
}
}
);
bulk.execute();
db.inventory.update(
{ item: "MNO2" },
{
$set: {
category: "apparel",
details: { model: "14Q3", manufacturer: "XYZ Company" }
},
$currentDate: { lastModified: true }
}
)
db.inventory.update(
{ category: "clothing" },
{
$set: { category: "apparel" },
$currentDate: { lastModified: true }
},
{ multi: true }
)
db.inventory.update(
{ item: "TBD1" },
{
item: "TBD1",
details: { "model" : "14Q4", "manufacturer" : "ABC Company" },
stock: [ { "size" : "S", "qty" : 25 } ],
category: "houseware"
},
{ upsert: true }
)
// remove all
db.inventory.remove({})
// remove match condition
db.inventory.remove( { type : "food" } )
// remove match condition only one
db.inventory.remove( { type : "food" }, 1 )
Schema
Types
Models
Validation
$ npm install mongoose
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/test');
var db = mongoose.connection;
db.on('error', function(err) {
console.error('connection error:', err));
});
db.once('open', function (callback) {
console.log('connected');
});
var animalSchema = new Schema({ name: String, type: String });
var Animal = mongoose.model('Animal', animalSchema);
String
Number
Date
Buffer
Boolean
Mixed
Objectid
Array
var schema = new Schema({
name: String,
binary: Buffer,
living: Boolean,
updated: { type: Date, default: Date.now },
age: { type: Number, min: 18, max: 65 },
mixed: Schema.Types.Mixed,
_someId: Schema.Types.ObjectId,
array: [],
ofString: [String],
ofNumber: [Number],
ofDates: [Date],
ofBuffer: [Buffer],
ofBoolean: [Boolean],
ofMixed: [Schema.Types.Mixed],
ofObjectId: [Schema.Types.ObjectId],
nested: {
stuff: { type: String, lowercase: true, trim: true }
}
})
// assign a function to the "methods" object of our animalSchema
animalSchema.methods.findSimilarTypes = function (cb) {
return this.model('Animal').find({ type: this.type }, cb);
}
var dog = new Animal({ type: 'dog' });
dog.findSimilarTypes(function (err, dogs) {
console.log(dogs);
});
// assign a function to the "statics" object of our animalSchema
animalSchema.statics.findByName = function (name, cb) {
return this.find({ name: new RegExp(name, 'i') }, cb);
}
var Animal = mongoose.model('Animal', animalSchema);
Animal.findByName('fido', function (err, animals) {
console.log(animals);
});
// define a schema
var personSchema = new Schema({
name: {
first: String,
last: String
}
});
personSchema.virtual('name.full').get(function () {
return this.name.first + ' ' + this.name.last;
});
// compile our model
var Person = mongoose.model('Person', personSchema);
// create a document
var bad = new Person({
name: { first: 'Walter', last: 'White' }
});
console.log('%s is insane', bad.name.full); // Walter White is insane
personSchema.virtual('name.full').set(function (name) {
var split = name.split(' ');
this.name.first = split[0];
this.name.last = split[1];
});
...
mad.name.full = 'Breaking Bad';
console.log(mad.name.first); // Breaking
console.log(mad.name.last); // Bad
bad.name.full = 'Breaking Bad';
var schema = new mongoose.Schema({ name: 'string', size: 'string' });
var Tank = mongoose.model('Tank', schema);
var small = new Tank({ size: 'small' });
small.save(function (err) {
if (err) return handleError(err);
// saved!
})
// or
Tank.create({ size: 'small' }, function (err, small) {
if (err) return handleError(err);
// saved!
})
Tank.find({ size: 'small' })
.where('createdDate')
.gt(oneYearAgo)
.exec(callback);
Tank.remove({ size: 'large' }, function (err) {
if (err) return handleError(err);
// removed!
});
Tank.update({name: 'Tiger'}, {
$set: {size: 'large'}
}, function (err) {
if (err) return handleError(err);
// updated!
});
All - Required
Numbers - Min & Max
Strings - Enum & Match
Dates - Min & Max
var toySchema = new Schema({
color: String,
name: String
});
var Toy = mongoose.model('Toy', toySchema);
Toy.schema.path('color').validate(function (value) {
return /blue|green|white|red|orange|periwinkle/i.test(value);
}, 'Invalid color');
var toy = new Toy({ color: 'grease'});
toy.save(function (err) {
console.log(err.errors.color.message)
// prints 'Validator "Invalid color" failed
// for path color with value `grease`'
console.log(err.name) // prints "ValidationError"
console.log(err.message) // prints "Validation failed"
});
Lighweight
Basic Auth
OAuth
OAuth2
var passport = require('passport')
, LocalStrategy = require('passport-local').Strategy;
passport.use(new LocalStrategy(
function(username, password, done) {
User.findOne({ username: username }, function (err, user) {
if (err) { return done(err); }
if (!user) {
return done(null, false, { message: 'Incorrect username.' });
}
if (!user.validPassword(password)) {
return done(null, false, { message: 'Incorrect password.' });
}
return done(null, user);
});
}
));
app.use(passport.initialize());
passport.serializeUser(function (user, done) {
// console.log('user', user);
done(null, user.username);
});
passport.deserializeUser(function (username, done) {
// console.log('username', username);
User.findOne({
username: username
}, function (err, user) {
return done(err, user);
});
})
app.post('/login', passport.authenticate('local', {
successRedirect: '/',
failureRedirect: '/login' }
));
var passport = require('passport')
, OpenIDStrategy = require('passport-openid').Strategy;
passport.use(new OpenIDStrategy({
returnURL: 'http://www.example.com/auth/openid/return',
realm: 'http://www.example.com/'
},
function(identifier, done) {
User.findOrCreate({ openId: identifier }, function(err, user) {
done(err, user);
});
}
));
// Accept the OpenID identifier and redirect the user to their OpenID
// provider for authentication. When complete, the provider will redirect
// the user back to the application at:
// /auth/openid/return
app.post('/auth/openid', passport.authenticate('openid'));
// The OpenID provider has redirected the user back to the application.
// Finish the authentication process by verifying the assertion. If valid,
// the user will be logged in. Otherwise, authentication has failed.
app.get('/auth/openid/return',
passport.authenticate('openid', { successRedirect: '/',
failureRedirect: '/login' }));
var passport = require('passport')
, OAuthStrategy = require('passport-oauth').OAuthStrategy;
passport.use('provider', new OAuthStrategy({
requestTokenURL: 'https://www.provider.com/oauth/request_token',
accessTokenURL: 'https://www.provider.com/oauth/access_token',
userAuthorizationURL: 'https://www.provider.com/oauth/authorize',
consumerKey: '123-456-789',
consumerSecret: 'shhh-its-a-secret'
callbackURL: 'https://www.example.com/auth/provider/callback'
},
function(token, tokenSecret, profile, done) {
User.findOrCreate(..., function(err, user) {
done(err, user);
});
}
));
var passport = require('passport')
, OAuth2Strategy = require('passport-oauth').OAuth2Strategy;
passport.use('provider', new OAuth2Strategy({
authorizationURL: 'https://www.provider.com/oauth2/authorize',
tokenURL: 'https://www.provider.com/oauth2/token',
clientID: '123-456-789',
clientSecret: 'shhh-its-a-secret'
callbackURL: 'https://www.example.com/auth/provider/callback'
},
function(accessToken, refreshToken, profile, done) {
User.findOrCreate(..., function(err, user) {
done(err, user);
});
}
));
// only one scope
app.get('/auth/provider',
passport.authenticate('provider', { scope: 'email' })
);
// multiple scopes
app.get('/auth/provider',
passport.authenticate('provider', { scope: ['email', 'sms'] })
);
Q&A Time
ANDRIY DACENKO
SOFTWARE ENGINEER
September 16, 2015
SOCKETS, MONGO, PASSPORT