-
-
Notifications
You must be signed in to change notification settings - Fork 927
Conditionally promisify all model functions #272
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
Conditionally promisify all model functions #272
Conversation
lib/handlers/authorize-handler.js
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be return Promise.try(promisify(this.model.getClient, 2), [clientId, null]), as the signature of getClient is (clientId, clientSecret[, callback]). Currently promisify's callback is passed as clientSecret.
model.getClient = function getClient(clientId, clientSecret, callback) {
let client = /* get client using clientId */;
if (clientSecret) {
// oops..
}
// ...
}
This could be prevented in the model by performing a check like typeof clientSecret === 'function' but callback support should really be optional. If I decide to use promises (or throw) I shouldn't have to be aware of this edge case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This problem persists.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point, will fix.
@mjsalinger can you provide a concrete example of how this is not working with promises and callbacks?
Closing for now, but I'll surely give it a second look if something is broken.
This is critical for my app. Reopening. Will update with an example shortly.
@nunofgs For example, prior to this pull request, if I create a model function as follows:
model.validateScope(user, client, scope callback) {
// do some work
callback(null, scope);
}
and the code in the library before this PR is
return Promise.try(this.model.validateScope, [user, client, scope]);
it would not work. Because Promise.try in the example above is expecting the function this.model.validateScope to return a Promise, and the model uses a callback, bluebird won't process it and the then will never execute.
So to make this model function work with the Promise chain, it needs to be Promisified, becoming
return Promise.try(promisify(this.model.validateScope, 3), [user, client, scope])
This PR still breaks getClient on models using promises (see this comment).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Previously model#validateScope was a required model function. (Promise.try throws a TypeError if the first argument isn't a function. ) This change makes validateScope optional.
If this is the desired effect there should probably be an else accepting any scope. Something like this should work:
} else { return scope; }
Without this addition all scopes are lost, resulting in a call to model#saveToken with token.scope === undefined (see for example ClientCredentialsGrantType#saveToken).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adding additional thens and Promise.try is unnecessary.
Simply replacing the old
return this.model.saveToken(token, client, user);
with
return promisify(this.model.saveToken, 3)(token, client, user)
should be fine.
Promise.try should just be used to start a new promise chain, because it catches synchronous exceptions and rejects the returned promise. Down here we're already inside a promise handler, so synchronous exceptions are caught and passed down the chain anyways.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adding additional thens and Promise.try is unnecessary. See previous comments.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adding additional thens and Promise.try is unnecessary. See previous comments.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adding additional thens and Promise.try is unnecessary. See previous comments.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changing getClient to check that the callback is always passed as third argument would catch the problem described in this comment.
getClient: function(clientId, clientSecret, callback) { should(clientSecret).equal(null); callback(null, { grants: ['authorization_code'], redirectUris: ['http://example.com/cb'] }); }
I was reading the documentation of promisify-any again and noticed two things:
- When using
promisify-anywith a non-native promise library, it's cleaner to tellpromisify-anyto actually use it. Without the call touse()promisify-anydefaults to using it's own copy of bluebird (currentlybluebird@2.10.0).
var Promise = require('bluebird'); var promisify = require('promisify-any').use(Promise);
This shouldn't be a huge issue since bluebird accepts any thenable, but this brings me to the second point.
promisify()already catches synchronous exceptions, which makes the use of bluebird'sPromise.tryredundant.
function fn() { throw new Error('test'); }
Using promisify-any:
promisify(fn)().catch(console.log.bind(console));
Using bluebird:
Promise.try(fn).catch(console.log.bind(console));
Using promisify-any without bluebird's Promise.try comes with the added benefit that we don't rely on passing arguments to Promise.try, which is deprecated as of bluebird@3.0.0.
So
return Promise.try(this.model.getUserFromClient, client);
becomes
return promisify(this.model.getUserFromClient, 1)(client);
instead of
return Promise.try(promisify(this.model.getUserFromClient, 1), client);
I like this approach, I'll make the necessary changes.
Added fixes, @maxtruxa.
Looking great. I don't see any problems with merging this.
* Set promisify-any to use the declared Bluebird promise handler * Removed deprecated Promise.try and replaed with just using promisify * Changed getClient to always pass three parameters, with clientSecret being `null` when not needed * Simplified some of the promise chains for saveToken
6e91ad7 to
ade9b15
Compare
Thanks!
The current library does not run well with node-style callback functions, which require promisification. Bluebird's native promisify will only promisify node-style callback functions, but will break existing promises.
.then.