How to wait for a transaction to be finished


#1

I’m stick with a weird transaction racing when using ESA. I identify a User as follows in current-user service:

loadCurrentUser() {
    this.get('flashMessages').clearMessages();
    if (this.get('session.isAuthenticated')) {
      return this.get('store').queryRecord('user', { me: true }).then((user) => {
        this.get('flashMessages').success(this.get('i18n').t('flash.signed_in'));
        this.get('currentShop').setShop(user.get('shop'));
        this.set('user', user);
      });
    } else {
      this.get('flashMessages').info(this.get('i18n').t('flash.signed_off'));
      return RSVP.resolve();
    }
  }

In application route:

export default Route.extend(ApplicationRouteMixin, {
  currentUser: service('current-user'),

  beforeModel() {
    return this._loadCurrentUser();
  },

  sessionAuthenticated() {
    this._super(...arguments);
    this._loadCurrentUser();
  },

  _loadCurrentUser() {
    return this.get('currentUser').loadCurrentUser().catch(() => this.get('session').invalidate());
  }
});

After authentication I redirect to dashboard route:

#environment.js

ENV['ember-simple-auth'] = {
    routeAfterAuthentication: 'dashboard'
  };

In dashboard route I load shops:

export default Route.extend(AuthenticatedRouteMixin, {
  currentUser: service('current-user'),
  currentShop: service('current-shop'),

  model() {
    return this.store.findAll('shop');
  }
});

What is happening is that I have two end-point hit, both requiring a User to be found or created in the backend based on the data encoded in the token passed in from Ember. How is possible to wait the end of users/me authentication end-point to finish before hitting the dashboard route ? Thank you.


#2

I’m not sure if I understand your setup completely, but it feels like dashboard should actually be a nested route of users/me, so it becomes users/me/dashboard. If you move the user loading logic to users/me (which seems more appropriate) you can use this.modelFor in the dashboard route to get a reference to the user.


#3

@Bauke, Nope, users/me is just an end-point, as ESA docs suggested to manage a current user instance. So I have no nested routes and don’t think to need it:

Router.map(function() {
  this.route('auth-error');
  this.route('callback');
  this.route('dashboard');
  this.route('information');
  this.route('address');
  this.route('phone-fax');
  this.route('social');
  this.route('links');
  this.route('description');
  this.route('working-hours');
  this.route('holiday-hours');
  this.route('country-languages');
});

I belived that I had to use smth like async and await tips when calling the above end-point. Another idea was to remove routeAfterAuthentication: 'dashboard' declared in environment.js and use transitionToRoute('dashboard') after getting the user data from the backend.


#4

Ah, my bad.

I think it’s not necessarily a case of delaying the transition but about delaying the rendering of the dashboard route. In order to do that you need the queryRecord promise to be available in the model hook. Instead of setting the user on the service once the request is finished, you could set the actual request, which will resolve in the user record anyway:

const userRequest = this.get('store').queryRecord('user', { me: true });
userRequest.then((user) => {
  this.get('flashMessages').success(this.get('i18n').t('flash.signed_in'));
  this.get('currentShop').setShop(user.get('shop'));
});
return this.set('user', userRequest);

In the model hook of the dashboard route:

model() {
  return this.get('currentUser.user').then((user) => {
    // Load any additional data for this route.
  });
}

Using transitionToRoute('dashboard') seems like a bad idea. Imagine the user navigating to the information route. When it hits the refresh button it will go back to the dashboard route again.


#5

OK, I’m sorry, - I didn’t mention it, but as I’m using ESA (ember simple auth), I kept their way to authenticate a User used in their dummy app where loadCurrentUser function is already declared in beforeModel hook. That’s why I don’t think I need to check it in model hook.

The version of current-user.js service I’m going to test is as folows:

import RSVP from 'rsvp';
import Service, { inject as service } from '@ember/service';

export default Service.extend({
  session:        service(),
  store:          service(),
  flashMessages:  service('flash-messages'),
  i18n:           service('i18n'),
  currentShop:    service('current-shop'),

  async loadCurrentUser() {
    this.get('flashMessages').clearMessages();
    if (this.get('session.isAuthenticated')) {
      let user = await this.get('store').queryRecord('user', { me: true });
      if (user) {
        this.get('flashMessages').success(this.get('i18n').t('flash.signed_in'));
        this.get('currentShop').setShop(user.get('shop'));
        this.set('user', user);
        return user;
      } else {
        this._respondWithError();
      }

    } else {
      this._respondWithError();
    }
  },
  
  _respondWithError() {
    return RSVP.reject();
  }
});