genkgo / ember-localforage-adapter

Offline usage for Ember Data, based on localstorage adapter, but now uses Mozilla's localforage as data source
Other
133 stars 26 forks source link

Assertion Failed: The response from a findQuery must be an Array, not undefined #22

Closed perlun closed 9 years ago

perlun commented 9 years ago

Hi,

Thanks for a nice piece of software. I get some errors when I try to query out data from my (offline) store using the LFAdapter:

Error: Assertion Failed: The response from a findQuery must be an Array, not undefined
    at new Error (native)
    at Error.EmberError (http://localhost:42000/for003/ui/vendor/ember-1.8.1.js:14251:23)
    at Object.Ember.assert (http://localhost:42000/for003/ui/vendor/ember-1.8.1.js:3865:15)
    at http://localhost:42000/for003/ui/vendor/ember-data-1.0.0-beta.16.1.js:4804:17
    at Object.Backburner.run (http://localhost:42000/for003/ui/vendor/ember-1.8.1.js:186:27)
    at ember$data$lib$system$store$$Service.extend._adapterRun (http://localhost:42000/for003/ui/vendor/ember-data-1.0.0-beta.16.1.js:10943:33)
    at http://localhost:42000/for003/ui/vendor/ember-data-1.0.0-beta.16.1.js:4801:15
    at tryCatch (http://localhost:42000/for003/ui/vendor/ember-1.8.1.js:47307:16)
    at invokeCallback (http://localhost:42000/for003/ui/vendor/ember-1.8.1.js:47319:17)
    at publish (http://localhost:42000/for003/ui/vendor/ember-1.8.1.js:47290:11)

The calling code basically looks like this:

  this.get('offlineStore').find('order-draft', { company: 1 })

The reason the error happens is because there are no order-draft items in the localforage (IndexedDB in my case, using Chrome Canary) storage. But it feels like the return value from the localforage adapter isn't really correct in this case...

(As you can see, we are still on Ember 1.8.1. Are looking towards upgrading to 1.10 but the Handlebars/HTMLBars migration isn't trivial for us so therefore it hasn't really taken place yet.)

frederikbosch commented 9 years ago

@perlun Are you able to replicate this in the form of a test?

// additional model: app/models/order-draft.js
import DS from 'ember-data';

var attr = DS.attr;

export default DS.Model.extend({
  name: attr('string'),
});

// test: integration/crud-test.js
test('return empty list when model not exists', function() {
  stop();

  run(function() {
    store.find('order-draft', { property: 'value'}).then(function(list) {
      equal(list.get('length'), 0, 'list is empty');
      start();
    });
    window.localforage.getItem('DS.LFAdapter').then(function (items) {
      equal(items['order-draft'], null, 'no items in localforage');
    });
  });
});

This test passes perfectly for me. Or do I misunderstand your issue?

perlun commented 9 years ago

Thanks for the quick reply. Yes, getting reproducibility on this would help in resolving the issue. Will check if I can make a (failing) test for this one.

frederikbosch commented 9 years ago

@perlun Thanks, let me know if you need some help.

perlun commented 9 years ago

I've tried updating our app now to newer Ember (1.11) but now I seem to be getting really, really weird errors:

TypeError: Cannot read property 'split' of undefined
    at Object.func (ember-1.11.1.debug.js:33691)
    at Object.Cache.get (ember-1.11.1.debug.js:9612)
    at Object.classify (ember-1.11.1.debug.js:33766)
    at exports.default.EmberObject.default.extend.resolveOther (ember-1.11.1.debug.js:5106)
    at superFunction [as _super] (ember-1.11.1.debug.js:13710)
    at resolveOther (ember-resolver-0.1.15.js:130)
    at superWrapper (ember-1.11.1.debug.js:17592)
    at exports.default.EmberObject.default.extend.resolve (ember-1.11.1.debug.js:4905)
    at Object.resolve [as resolver] (ember-1.11.1.debug.js:4668)
    at resolve (ember-1.11.1.debug.js:2243)

We are not using ember-cli but rather our own build infrastructure, and I am getting the above when doing a store.find like above. When poking around/debugging to try and see what could be causing this, I noticed that this is what's freaking out:

      extractArray: function(store, primaryType, rawPayload) {
        var payload = this.normalizePayload(rawPayload);
        var primaryTypeName = primaryType.typeKey;
        var primaryArray;

        for (var prop in payload) {
          var typeKey = prop;
          var forcedSecondary = false;

          if (prop.charAt(0) === '_') {
            forcedSecondary = true;
            typeKey = prop.substr(1);
          }

          var typeName = this.typeForRoot(typeKey);
          if (!store.modelFactoryFor(typeName)) {

Within this method, prop becomes @each which is then why it is failing. It essentially tries to resolve model:@each from the container. What on earth could be causing this? :smile:

I realize that it may be hard to guess from a subset of details like this. Sorry, I don't have a more reproducible case at the moment but will gladly share more details as needed.

frederikbosch commented 9 years ago

@perlun I will try to update this project to ember 1.11 in the next few days. Are you having this problem only with Chrome again? Or is this error applicable to any browser?

frederikbosch commented 9 years ago

@perlun If you are right, and prop becomes @each, then the error is originated in normalizePayload. The payload is not normalized correctly, @each should not be part of the normalized data.

frederikbosch commented 9 years ago

@perlun All tests pass with Ember 1.11. I'd love to help here, but advise you to checkout this repo, run tests with ember-cli (ember test) and finally add a test that replicates your problem. Best thing then is to issue a pull request, or if you cannot fix the bug yourself, give a clearer description of the problem. Also consider that is not a problem of this project, but a problem inside the dependencies: ember/ember-data/localforage.

frederikbosch commented 9 years ago

@perlun What happends if you upgrade ember-data to dev-master? A few days ago a normalization bug related to Canary was fixed.

perlun commented 9 years ago

Thanks for the very prompt reply. When I looked int he debugger, @each was not part of the payload. So it feels like it was maybe Ember's default enumerable mixin that inserted it or something.

You're right in that I tested w/ Chrome Canary (when I reported the issue). Also tested w/ standard Chrome now, got the same error. Tested with Firefox dev edition, also seemed to behave similarly (str was undefined in the innermost method).

Any idea on how to try and narrow it down? It could very well be my setup with the Localforage adapter for sure. I.e., is the localforage serializer the one I am supposed to use and could this be triggered if I am not using it correctly?

Like you say, reproducibility is key here, but also, just any pointer in how to debug this would be greatly appreciated... Thanks in advance. :smile:

frederikbosch commented 9 years ago

@perlun Could you create a very minimal script on jsbin that reproduces the error? There are some examples available that might help you. Kiitos!

perlun commented 9 years ago

Yeah, I will try to do something along those lines (narrow down my use case to a bare minimum that reproduces the error). Will keep you posted.

perlun commented 9 years ago

I think I have it now:

  1. When running store.find at an earlier stage in my app bootup, it worked fine.
  2. When running it at the regular place (inside one of the components that get rendered in one of my templates), I get the error.

So something was happening while my app was booting up that caused this weirdness. I've nailed it down to my custom serializer:

    app.OrderDraftSerializer = DS.RESTSerializer.extend(
      DS.EmbeddedRecordsMixin,
      attrs:
        customer: embedded: 'always'
        orderDraftLines: embedded: 'always'
        templateAssortmentItems: embedded: 'always'
    )

I think the problem here lies in the use of DS.RESTSerializer; I have to extend the localforage-adapter/serializers/localforage one instead. When I change my code to that, I don't get the error any more (but I'll then have to figure out something nifty since the model in question is used for both a regular REST-based DS.Store and a localforage-based store... so the serializer is very dependent on case-by-case here).

Btw, is embedded: 'always' style data supported with the ember-localforage-adapter? I ran the tests now locally but couldn't see any obvious support for it. Have you used it with the adapter?

perlun commented 9 years ago

Ah, I think serializerFor might help me... Something like this: https://github.com/kurko/ember-sync#initialization. That way, I can make it return different serializers for the online/offline scenarios.

frederikbosch commented 9 years ago

@perlun Great you found your solution! Can we close this issue then?

perlun commented 9 years ago

Yes. Huge thanks for supporting me on this. I have a PR coming up re. the embedded: always style data. It doesn't work out of the box, but you will get more details about it in that PR.

frederikbosch commented 9 years ago

@perlun Great, looking forward to that!