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

Related Models Promisification #452

Closed
wants to merge 3 commits into from

Conversation

partap
Copy link
Contributor

@partap partap commented Feb 18, 2015

Rebased and isolated related models promise code from #447

The tests currently rely on promise-capable DAO from #451, but the code itself should work separately.

Connects strongloop/loopback#418

@slnode
Copy link

slnode commented Feb 18, 2015

Can one of the admins verify this patch? To accept patch and trigger a build add comment ".ok\W+to\W+test."

@partap
Copy link
Contributor Author

partap commented Feb 18, 2015

@bajtos
I've updated the relations to use, e.g., customer.orders.get() for promise-based retrieval to avoid the awkwardness of customer.orders(true)

@partap
Copy link
Contributor Author

partap commented Feb 18, 2015

@bajtos
Rather than rewrite ~70 tests in relations.test.js to only use promises for relations and not dao, I suggest copying the dao.js file from the promises-dao into the working tree here to get the tests to run...

@partap
Copy link
Contributor Author

partap commented Feb 21, 2015

@bajtos
There is a ton of trailing whitespace in relation-definition.js.
I've been dancing around it with my commits... Could you take care of it in the master branch?

@bajtos
Copy link
Member

bajtos commented Feb 24, 2015

There is a ton of trailing whitespace in relation-definition.js. I've been dancing around it with my commits... Could you take care of it in the master branch?

Done.

@@ -1276,8 +1313,12 @@ BelongsTo.prototype.related = function (refresh, params) {
var scopeQuery = null;

if (arguments.length === 1) {
if (typeof refresh === 'boolean') {
params = utils.createPromiseCallback();
} else {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes Product.categories(true) return a promise, correct? I thought we agreed that Product.categories() will not be promise-enabled at all. I am proposing to revert all changes made in the related() method.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops.
Yeah, I added the get() methods but didn't remove promises from the related() methods.
...although it doesn't really hurt anything, I guess...

@partap
Copy link
Contributor Author

partap commented Feb 25, 2015

Oh snap... removing all promise support from related() breaks get()

@partap
Copy link
Contributor Author

partap commented Feb 25, 2015

...and removing the refresh arg from get also breaks it... it could be a filter condition.

@partap partap force-pushed the promises-related branch 2 times, most recently from aaf8850 to f200f6b Compare March 2, 2015 23:31
@bajtos
Copy link
Member

bajtos commented Mar 4, 2015

@partap what's the status of this PR, is it ready for final review?

@partap
Copy link
Contributor Author

partap commented Mar 4, 2015

@bajtos I believe so.

@partap
Copy link
Contributor Author

partap commented Mar 4, 2015

@bajtos
Rebased on latest master with DAO Promise support now.
I redid the get methods to work the same way as related, rather than forcing refresh=true...it's a little messy with the argument resolution because of the arguments checks, but I think it would probably be better to change the default for refresh in related than in get, and that would be beyond the scope of this PR.

@bajtos
Copy link
Member

bajtos commented Mar 9, 2015

I redid the get methods to work the same way as related, rather than forcing refresh=true...it's a little messy with the argument resolution because of the arguments checks, but I think it would probably be better to change the default for refresh in related than in get, and that would be beyond the scope of this PR.

The "related()" API is messy, confusing and broken, I have intentionally asked to use a better approach in the new "get()" method. See the discussion in strongloop/loopback#256 for more details.

Also boolean-based APIs are really bad, it's very difficult to tell what is the code Product.categories(true) doing - is it saying refresh=true, useCache=true, or something completely else?

@bajtos
Copy link
Member

bajtos commented Mar 9, 2015

If we want to provide a function for getting the data from the cache first, then let's can add getCached to make the distinction clear. IMO it can be left out of scope of this PR.

@bajtos
Copy link
Member

bajtos commented Mar 9, 2015

@slnode ok to test

When a callback is omitted from a method on a model relation that
supports promises, return that promise.  This includes all the standard
DAO methods, as well as any user-defined methods that return promises.

e.g.:
mylist.todos.create({name: 'Item 1'}) // returns Promise

This API will use native ES6 promises if available.  If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.

e.g.:
global.Promise = require('bluebird')

Relations affected:

- BelongsTo
- HasOne
- HasMany
- HasManyThrough
- HasAndBelongsToMany
- ReferencesMany
- EmbedsOne

Exceptions:

The EmbedsMany relation has not been promisified, because most of the
methods return synchronous values.

The base relation getter method [e.g.: mylist.todos()] has not been
promisified, due to its default caching behavior.

New Methods:

- getAsync(condition, cb)

A new method "getAsync()" has been added to all relations except
EmbedsMany, which always fetches from the datasource rather than from
the cache.  It takes an optional "where" condition (except for HasOne
and BelongsTo) and an optional callback.  If the callback is omitted,
a Promise is returned.
@partap
Copy link
Contributor Author

partap commented Mar 30, 2015

@bajtos It looks ok now.

} else if (cb === undefined && typeof cond !== 'function') {
cb = utils.createPromiseCallback();
}
definition.related(self, f._scope, cond, cb);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this code does not correctly handle the variant getAsync(mycb).

It will call related(self, f._scope, mycb, undefined), and because related is checking arguments.length, it will parse the arguments incorrectly.

I think this is the correct implementation:

  if (cb === undefined) {
    if (cond === undefined) {
      // getAsync()
      cb = utils.createPromiseCallback();
      cond = true;
    } else if (typeof cond !== 'function') {
      // getAsync({where:{}})
      cb = utils.createPromiseCallback();
    } else {
      // getAsync(function(){})
      cb = cond;
      cond = true;
    }
  }

Thoughts?

@bajtos
Copy link
Member

bajtos commented Apr 1, 2015

@partap I found an issue the proposed implementation of scope.getAsync, see my comment above. I can apply the proposed fix myself to save you time, if you like.

@partap
Copy link
Contributor Author

partap commented Apr 3, 2015

@bajtos
Good catch, thanks!

That case wasn't covered by the tests so I added one... your fix works.

@partap
Copy link
Contributor Author

partap commented Apr 3, 2015

@bajtos
Not sure why is says failing check now... All tests pass on my end.

Hmm. It appears there is a request from "Jenkins" to allow read-write access to all my public and private repos. I can't imagine how that would be necessary.

(edit: sorry for the delay, my macbook has been in the shop the last few days)

@bajtos
Copy link
Member

bajtos commented Apr 7, 2015

@slnode test please

@bajtos
Copy link
Member

bajtos commented Apr 7, 2015

Not sure why is says failing check now... All tests pass on my end.

The build slaves were not working correctly. I have restarted the build.

Hmm. It appears there is a request from "Jenkins" to allow read-write access to all my public and private repos. I can't imagine how that would be necessary.

Yeah, I agree that should not be required. Can you @rmg comment on this please?

@bajtos
Copy link
Member

bajtos commented Apr 7, 2015

@slnode test please

@bajtos
Copy link
Member

bajtos commented Apr 7, 2015

The CI is broken, I have verified the implementation locally on my machine.

bajtos pushed a commit that referenced this pull request Apr 7, 2015
Promisify model relation methods
@bajtos
Copy link
Member

bajtos commented Apr 7, 2015

Landed via 2bdcce0, thank you for this valuable contribution!

@esco
Copy link

esco commented Apr 7, 2015

👍

@rmg
Copy link
Contributor

rmg commented Apr 7, 2015

Jenkins requires that level of access so that it can verify that you are part of the strongloop org and that you have commit access to the repos it is validating you against -- It is not a public CI.

@michael-lumley
Copy link

Is there any documentation on this, and the correct syntax for implementing it?

@superkhau superkhau assigned crandmck and unassigned bajtos Apr 5, 2016
@superkhau superkhau added the doc label Apr 5, 2016
@superkhau
Copy link
Contributor

Is there any documentation on this, and the correct syntax for implementing it?

@crandmck ^

@crandmck
Copy link
Contributor

crandmck commented Apr 7, 2016

No, unfortunately I haven't had time to document use of the promise API at all. There are lots of open issues for the existing docs, so it would have to be prioritized relative to those.... Before I can start on that, though, it would be very helpful to have a few "promise-ized" sample apps. @superkhau perhaps you could help in that area? For example (maybe) loopback-getting-started-promise, etc.

@superkhau
Copy link
Contributor

@jannyHou Could you create an issue for yourself to create a sample app for this? I believe you did implemented this feature right?

@jannyHou
Copy link
Contributor

jannyHou commented Apr 7, 2016

@superkhau yeah, thanks for reminding me. I create a story in TOB for it: https://github.com/strongloop-internal/scrum-loopback/issues/827

bajtos referenced this pull request Jul 10, 2017
When a callback is omitted from a method on a model relation that
supports promises, return that promise.  This includes all the standard
DAO methods, as well as any user-defined methods that return promises.

e.g.:
mylist.todos.create({name: 'Item 1'}) // returns Promise

This API will use native ES6 promises if available.  If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.

e.g.:
global.Promise = require('bluebird')

Relations affected:

- BelongsTo
- HasOne
- HasMany
- HasManyThrough
- HasAndBelongsToMany
- ReferencesMany
- EmbedsOne

Exceptions:

The EmbedsMany relation has not been promisified, because most of the
methods return synchronous values.

The base relation getter method [e.g.: mylist.todos()] has not been
promisified, due to its default caching behavior.

New Methods:

- getAsync(condition, cb)

A new method "getAsync()" has been added to all relations except
EmbedsMany, which always fetches from the datasource rather than from
the cache.  It takes an optional "where" condition (except for HasOne
and BelongsTo) and an optional callback.  If the callback is omitted,
a Promise is returned.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.