Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support returning promises on Session prototype methods #737

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
2 changes: 2 additions & 0 deletions .eslintrc.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
root: true
parserOptions:
ecmaVersion: 2017
rules:
eol-last: error
eqeqeq: ["error", "always", { "null": "ignore" }]
Expand Down
76 changes: 72 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,7 @@ app.get('/', function(req, res, next) {
})
```

#### Session.regenerate(callback)
#### Session.regenerate(callback) => Promise

To regenerate the session simply invoke the method. Once complete,
a new SID and `Session` instance will be initialized at `req.session`
Expand All @@ -324,9 +324,26 @@ and the `callback` will be invoked.
req.session.regenerate(function(err) {
// will have a new session here
})

// or promises
req.session.regenerate().then(function() {
// will have a new session here
}).catch(function(err) {
// a problem...
})

// or async/await
(async function() {
try {
await req.session.regenerate()
// will have a new session here
} catch(err) {
// a problem...
}
})()
```

#### Session.destroy(callback)
#### Session.destroy(callback) => Promise

Destroys the session and will unset the `req.session` property.
Once complete, the `callback` will be invoked.
Expand All @@ -335,9 +352,26 @@ Once complete, the `callback` will be invoked.
req.session.destroy(function(err) {
// cannot access session here
})

// or promises
req.session.destroy().then(function() {
// cannot access session here
}).catch(function(err) {
// a problem...
})

// or async/await
(async function() {
try {
await req.session.destroy()
// cannot access session here
} catch(err) {
// a problem...
}
})()
```

#### Session.reload(callback)
#### Session.reload(callback) => Promise

Reloads the session data from the store and re-populates the
`req.session` object. Once complete, the `callback` will be invoked.
Expand All @@ -346,9 +380,26 @@ Reloads the session data from the store and re-populates the
req.session.reload(function(err) {
// session updated
})

// or promises
req.session.reload().then(function() {
// session updated
}).catch(function(err) {
// a problem...
})

// or async/await
(async function() {
try {
await req.session.reload()
// session updated
} catch(err) {
// a problem...
}
})()
```

#### Session.save(callback)
#### Session.save(callback) => Promise

Save the session back to the store, replacing the contents on the store with the
contents in memory (though a store may do something else--consult the store's
Expand All @@ -366,6 +417,23 @@ redirects, long-lived requests or in WebSockets.
req.session.save(function(err) {
// session saved
})

// or promises
req.session.save().then(function() {
// session saved
}).catch(function(err) {
// a problem...
})

// or async/await
(async function() {
try {
await req.session.save()
// session saved
} catch(err) {
// a problem...
}
})()
```

#### Session.touch()
Expand Down
15 changes: 12 additions & 3 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -383,16 +383,25 @@ function session(options) {

function reload(callback) {
debug('reloading %s', this.id)
_reload.call(this, function () {
if (typeof callback === 'function') {
return _reload.call(this, function() {
wrapmethods(req.session)
callback.apply(this, arguments)
});
}

return _reload.call(this).then(function() {
wrapmethods(req.session)
}).catch(function(err) {
wrapmethods(req.session)
callback.apply(this, arguments)
throw err
})
}

function save() {
debug('saving %s', this.id);
savedHash = hash(this);
_save.apply(this, arguments);
return _save.apply(this, arguments);
}

Object.defineProperty(sess, 'reload', {
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"repository": "expressjs/session",
"license": "MIT",
"dependencies": {
"bluebird": "3.7.2",
"cookie": "0.4.0",
"cookie-signature": "1.0.6",
"debug": "2.6.9",
Expand Down
74 changes: 59 additions & 15 deletions session/session.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@

'use strict';

/**
* Module dependencies.
* @private
*/
var Promise = typeof Promise === 'undefined' ? require('bluebird') : Promise;

/**
* Expose Session.
*/
Expand Down Expand Up @@ -69,8 +75,12 @@ defineMethod(Session.prototype, 'resetMaxAge', function resetMaxAge() {
*/

defineMethod(Session.prototype, 'save', function save(fn) {
this.req.sessionStore.set(this.id, this, fn || function(){});
return this;
return stdCallbackOrPromiseWrapper.call(
this,
this.req.sessionStore,
'set',
[ this.id, this ],
fn);
});

/**
Expand All @@ -86,16 +96,28 @@ defineMethod(Session.prototype, 'save', function save(fn) {
*/

defineMethod(Session.prototype, 'reload', function reload(fn) {
var req = this.req
var store = this.req.sessionStore

store.get(this.id, function(err, sess){
if (err) return fn(err);
if (!sess) return fn(new Error('failed to load session'));
store.createSession(req, sess);
fn();
var self = this;
var req = this.req;
var store = this.req.sessionStore;

if (typeof fn === 'function') {
store.get(self.id, function(err, sess){
if (err) return fn(err);
if (!sess) return fn(new Error('failed to load session'));
store.createSession(req, sess);
fn();
});
return this;
}

return new Promise(function(resolve, reject){
store.get(self.id, function(err, sess){
if (err) return reject(err);
if (!sess) return reject(new Error('failed to load session'));
store.createSession(req, sess);
resolve();
});
});
return this;
});

/**
Expand All @@ -108,8 +130,12 @@ defineMethod(Session.prototype, 'reload', function reload(fn) {

defineMethod(Session.prototype, 'destroy', function destroy(fn) {
delete this.req.session;
this.req.sessionStore.destroy(this.id, fn);
return this;
return stdCallbackOrPromiseWrapper.call(
this,
this.req.sessionStore,
'destroy',
[ this.id ],
fn);
});

/**
Expand All @@ -121,8 +147,12 @@ defineMethod(Session.prototype, 'destroy', function destroy(fn) {
*/

defineMethod(Session.prototype, 'regenerate', function regenerate(fn) {
this.req.sessionStore.regenerate(this.req, fn);
return this;
return stdCallbackOrPromiseWrapper.call(
this,
this.req.sessionStore,
'regenerate',
[ this.req ],
fn);
});

/**
Expand All @@ -141,3 +171,17 @@ function defineMethod(obj, name, fn) {
writable: true
});
};

function stdCallbackOrPromiseWrapper(thisArg, method, args, callback) {
if (typeof callback === 'function') {
thisArg[method].apply(thisArg, args.concat([ callback ]))
return this
}

return new Promise(function(resolve, reject) {
thisArg[method].apply(thisArg, args.concat([function(err) {
if (err) return reject(err)
resolve()
}]))
})
}
Loading