I am building a site using Ember Simple Auth.
I followed these instructions to try and add the current user object to the session and it worked, using this slightly adapted code:
import Ember from 'ember';
import Session from 'simple-auth/session';
export default {
name: "current-user",
before: "simple-auth",
initialize: function(container) {
Session.reopen({
setCurrentUser: function() {
var accessToken = this.get('secure.token');
var _this = this;
if (!Ember.isEmpty(accessToken)) {
return container.lookup('store:main').find('user', 'me').then(function(user) {
_this.set('content.currentUser', user);
});
}
}.observes('secure.token'),
setAccount: function() {
var _this = this;
return container.lookup('store:main').find('account', this.get('content.currentUser.account.content.id')).then(function(account) {
_this.set('content.account', account);
});
}.observes('content.currentUser'),
});
}
};
However, using the latest version of Ember I'm getting the following:
DEPRECATION: lookup was called on a Registry. The initializer API no longer receives a container, and you should use an instanceInitializer to look up objects from the container. See http://emberjs.com/guides/deprecations#toc_deprecate-access-to-instances-in-initializers for more details.
I know that I need to split the above into /app/initializers and /app/instance-initializers (as per the notes here) but I'm not quite sure how to go about it.
Of course, if there is an easier/cleaner way to make the user and account objects available to every route/template I'd love to hear them :)
Thanks
This works for me on:
ember-cli: 0.2.7 (ember: 1.12.0, ember-data: 1.0.0-beta.18)
ember-cli-simple-auth: 0.8.0-beta.3
Note:
ember-data: 1.13. Store is registered in an initializer, should work as is
ember-data: 1.0.0-beta.19. Store is registered in an instance-initializer, some adjustments needed
1) Customize session
//config/environment.js
ENV['simple-auth'] = {
session: 'session:custom',
...
}
//app/sessions/custom.js
import Session from 'simple-auth/session';
export default Session.extend({
// here _store is ember-data store injected by initializer
// why "_store"? because "store" is already used by simple-auth as localStorage
// why initializer? I tried
// _store: Ember.inject.service('store') and got error
currentUser: function() {
var userId = this.get('secure.userId');
if (userId && this.get('isAuthenticated')) {
return this._store.find('user', userId);
}
}.property('secure.userId', 'isAuthenticated')
});
2) Inject store to session by initializer (otherwise find() wouldn't work)
//app/initializers/session-store
export function initialize(container, application) {
application.inject('session:custom', '_store', 'store:main')
// "store:main" is highly dynamic depepeding on ember-data version
// in 1.0.0-beta.19 (June 5, 2015) => "store:application"
// in 1.13 (June 16, 2015) => "service:store"
}
export default {
name: 'session-store',
after: 'ember-data',
initialize: initialize
}
3) In template
{{#if session.isAuthenticated}}
{{session.currentUser.name}}
{{/if}}
Note: this does not relieve you from deprecations generated by ember-simple-auth itself.
First of all you shouldn't reopen the session but use a custom session instead (see this example: https://github.com/simplabs/ember-simple-auth/blob/master/examples/4-authenticated-account.html#L132). Also you you shouldn't only load the current user when the access token is set but when the session is authenticated ('session.get('isAuthenticated')') which makes your code not dependent on the authenticator.
The deprecation warnings regarding the use of the registry in the initializer will go away in ESA 0.9.0 hopefully.
Here's a before and after of an initializer/instance-initializer that I did the other day.
Before
export function initialize( container, application ) {
var session = Ember.Object.create({
user:null,
authorization:null
});
application.register('session:main', session, { instantiate: false });
application.inject('route', 'session', 'session:main');
application.inject('controller', 'session', 'session:main');
application.inject('adapter', 'session', 'session:main');
}
After
export function initialize( instance) {
var session = Ember.Object.create({
user:null,
authorization:null
});
instance.registry.register('session:main', session, { instantiate: false });
instance.registry.injection('route', 'session', 'session:main');
instance.registry.injection('controller', 'session', 'session:main');
instance.registry.injection('adapter', 'session', 'session:main');
}
Ember Data Stuff
Ember Data in the latest iterations should be fetched using store:application
export function initialize(instance) {
var store = instance.container.lookup('store:application');
....
}
export default {
name: 'socket',
initialize: initialize,
after:['ember-data']
};
Related
I'm trying to setup a PromiseProxy Service that returns an Ember Data model, but the result doesn't seem to set the content property.
My service looks like this:
import Ember from 'ember';
const { computed, inject, ObjectProxy, PromiseProxyMixin } = Ember;
export default ObjectProxy.extend(PromiseProxyMixin, {
isServiceFactory: true,
store: inject.service(),
promise: computed({
get() {
var store = this.get('store');
return store.findRecord('community', window.community.id);
}
})
});
I then inject this service into the following locations:
export function initialize(container, application) {
application.inject('controller', 'community', 'service:community');
application.inject('route', 'community', 'service:community');
application.inject('model', 'community', 'service:community');
application.inject('component', 'community', 'service:community');
}
export default {
name: 'community',
after: 'store',
initialize: initialize
};
And then I use it as a model in my application route as a sort of deferReadiness workaround, since my whole app depends on this one model
which is used throughout and expected to be there.
export default Ember.Route.extend({
model() {
return this.get('community');
}
});
The issue is that it goes on to other routes, and properties on the community object are not there, i.e. content isn't set. Also community.isPending is true. The CP does get hit and the data comes back (I tested with a then in the CP).
Here is a full gist example: https://gist.github.com/knownasilya/8c9f78d910ed50ec8d84
Edit
So I found a workaround:
promise: computed({
get() {
var store = this.get('store');
return store.findRecord('community', window.community.id)
.then(data => {
this.set('content', data);
return data;
})
}
})
Seems like it doesn't set the content because model is proxied already?
Ember Data already wraps its objects in an ObjectProxy, you could just set the object as your service.
Additionally, this syntax is deprecated in future versions syntax for initializers, since it's moved to instance initializers, but no big deal.
initialize: function (container, application) {
// the store will be available from the container,
// and the name of the store changes depending on which version you are using.
var store = container.lookup('service:store'),
community= store.find('community', id);
application.register("service:community", community, { instantiate: false });
application.inject("controller", "community", "service:community");
application.inject("route", "community", "service:community");
application.inject("component", "community", "service:community");
}
And then you can still return community from the model, beforeModel hook etc.
I have a initializer like this:
import Ember from 'ember';
import Session from 'simple-auth/session';
var SessionWithCurrentUser = Session.extend({
store: Ember.inject.service(),
currentUser: function() {
console.log(this.get('store'));
console.log(this.store);
console.log(this.container.lookup('service:store'));
}.property('secure.access_token')
});
export default {
name: 'custom-session',
after: 'ember-data',
initialize(registry) {
registry.register('session:withCurrentUser', SessionWithCurrentUser);
}
};
currentUser gets called on user interaction, long after my app has finished loading. Only the last container lookup gives the store, the other 2 is an object:
{
_lastData: Object,
key: "ember_simple_auth:session"
[..]
}
What's going on? Why can't I inject the store?
It's because store in the current version of simple-auth is being overridden by an instance-initializer with the session storage. The next major version of simple-auth will turn the session storage into a service and we'll be able to do:
import Ember from 'ember';
const { service } = Ember.inject;
export default Ember.Service.extend({
session: service('session'),
store: Ember.inject.service(),
account: Ember.computed('session.content.secure.account_id', function() {
const accountId = this.get('session.content.secure.account_id');
if (!Ember.isEmpty(accountId)) {
return DS.PromiseObject.create({
promise: this.get('store').find('account', accountId)
});
}
})
});
From the dummy app, once https://github.com/simplabs/ember-simple-auth/pull/602 is merged.
I've seen other questions about this (like this one), and I believe this should be working
import Ember from 'ember';
import Session from 'simple-auth/session';
export default {
name: 'session-with-me',
before: 'simple-auth',
initialize: function() {
Session.reopen({
me: function() {
if (this.get('isAuthenticated')) {
return this.container.lookup('service:store').find('me', { singleton: true });
}
}.property('isAuthenticated')
});
}
};
the find('me', { singleton: true }) is a working patch of ember-jsonapi-resources. While debugging I can see the request being sent, and the payload comes through. I use the same find call elsewhere in the app, and can confirm a model gets instantiated fine.
On the inspector, under container > simple-auth-session I can see me as a session property, but it shows as { _id: 68, _label: undefined ...}
Has the way to set a session property changed? I may have seen a mention about this somewhere, but I can't find it anymore.
This is in the same domain of another question I asked earlier, but I'm giving up on that approach and trying simply to fetch the user independently of the authentication process.
Set up a custom session like that:
export default Session.extend({
me: function() {
var accessToken = this.get('secure.access_token');
if (!Ember.isEmpty(accessToken)) {
return DS.PromiseObject.create({
promise: this.container.lookup('service:me').find({});
});
}
}.property('secure.access_token')
});
// app/config/environment.js
ENV['simple-auth'] = {
session: 'session:me'
}
DS.PromiseObject is actually part of Ember Data which you're not using - I don't know whether there's an equivalent in the library you chose.
This is most likely an issue with ember-jsonapi-resources, not with Ember Simple Auth.
Instead of reopening the session though you should define your own one that extends the default one that Ember Simple Auth provides - see e.g. this answer: How to store the user in a session
We ended up making it work like this:
// app/sessions/me.js
export default Session.extend({
me: function() {
var accessToken = this.get('secure.access_token');
if (!Ember.isEmpty(accessToken)) {
let self = this;
return this.container.lookup('service:me').find({}).then((me) => {
self.set('me', me);
});
}
}.property('secure.access_token')
});
// app/config/environment.js
ENV['simple-auth'] = {
session: 'session:me'
}
Partly this was due to the way resource services are initialized in EJR (so #marcoow's hunch on this was correct), the other part was just bad coding on my part.
Interestingly we didn't have to explicitly register the session in the container
currently I'm changing my project from ember to ember-cli and run in a issue I can't get rid of. I am really not sure where the problem is, since I also updated ember and ember-data.
I get the Error:
Uncaught Error: Cannot re-register: `store:main`, as it has already been resolved.
I'm trying to load a user via the simple-auth Session.
import UserSession from '../session/user';
export default {
name: 'user-session',
before: 'simple-auth',
after: 'store',
initialize: function(container, application) {
container.register('session:user', UserSession);
}
};
and the session:
import Session from 'simple-auth/session';
export default Session.extend({
setup: function(authenticator, content, trigger) {
// Do not let setup trigger
this._super.call(this, authenticator, content, false);
// Lookup user and trigger events ourselves
var store = this.container.lookup('store:main');
console.log(store.find);
var self = this;
store.find('user', content.userId)
.then(function(user) {
self.set('user', user);
self.trigger('sessionAuthenticationSucceeded');
}, function() {
console.log('ERROR: Could not resolve user of session!');
});
}
});
the store.find is there but then the error breaks it.
I also tired to inject the store like this:
Ember-Simple-Auth currentUser example help required but had the same result.
Further I tried to make it via the instance-initalizer for ember-data beta.19.
I do stuff like this:
Session from '../session/user';
export default {
name: 'user-session1',
after: 'ember-data',
initialize: function(container, application) {
var store = container.lookup('store:main');
}
};
but this ends up in:
Uncaught TypeError: container.lookup is not a function
Using:
DEBUG: -------------------------------
ember.debug.js:4874DEBUG: Ember : 1.12.1
ember.debug.js:4874DEBUG: Ember Data : 1.0.0-beta.19.2
ember.debug.js:4874DEBUG: jQuery : 1.11.3
ember.debug.js:4874DEBUG: Ember Simple Auth : 0.8.0
ember.debug.js:4874DEBUG: -------------------------------
Thx for the help
------------------ EDIT --------------------------------------------
I updated my instance-initializers based on #Artych comment where I get the store.
I removed the custom session from simple-auth and tried it with reopening
ENV['simple-auth'] = {
//session: 'session:user', ...
My function:
initialize: function(application) {
var store = application.container.lookup('store:main');
Session.reopen({
setCurrentUser: function() {
console.log('never get here');
var accessToken = this.get('access_token');
var self = this;
if (!Ember.isEmpty(accessToken)) {
//never gets here, doesn't matter if I take other variables
}
}.observes('access_token', 'id', 'userId', 'user_id')
});
}
the problem now is that it never goes into "setCurrentUser". I still can logout and in. my autenticator:
authenticate: function(credentials) {
return new Ember.RSVP.Promise(function(resolve, reject) {
var ttl = 30*60*1000; // Request login for 30 minutes
var data = _.extend(credentials, {ttl: ttl});
Ember.$.ajax({
type: 'POST',
url: ENV.api + '/users/login',
data: data,
dataType: 'json'
})
.then(function(response) {
console.log(response);
Ember.run(null, resolve, response);
}, function(xhr, status, error) {
Ember.run(null, reject, error);
});
});
},
returns:
Object {id: "xI3sPSsgdOiHLd8DcFyuOE42KhbuO8gi8BjWBJRrgHgeCESWoma99C2RtvC6tnxG", ttl: 1800000, created: "2015-07-02T14:00:06.600Z", userId: 1}
As you can see, I added a bunch of observed variables: observes('access_token', 'id', 'userId', 'user_id') which I saw in different other questions but nothings helps.
Any idea on this?
Thx
I Solved it.
Simple don't use Ember Data 1.0.0-beta.19.2. I upgraded to 1.13.4 and I was able to get the store like before.
I'm trying to test my registration and login processes, the integration tests were passing perfectly prior to creating an initializer to extend the Ember-Simple-Auth Session object with the currentUser property.
It all works correctly in the browser, its just the tests now fail all in the sessionAuthenticationSucceeded action in the application route on the following line:
this.get('session.currentUser').then(function(user) {
with : TypeError: Cannot read property 'then' of undefined
/routes/application.js
import Ember from 'ember';
import ApplicationRouteMixin from 'simple-auth/mixins/application-route-mixin';
export default Ember.Route.extend(ApplicationRouteMixin, {
actions: {
sessionAuthenticationSucceeded: function () {
var self = this;
this.get('session.currentUser').then(function(user) {
if (user.get('account') && user.get('status') === 'complete'){
self.transtionTo('home');
} else {
console.log('Need to complete Registration');
self.transitionTo('me');
}
});
}
}
}
/initializers/custom-session.js
import Ember from 'ember';
import Session from 'simple-auth/session';
export default {
name: 'custom-session',
before: 'simple-auth',
initialize: function(container) {
// application.deferReadiness();
Session.reopen({
currentUser: function() {
var id = this.get('user_id');
if (!Ember.isEmpty(id)) {
console.log('getting the current user');
return container.lookup('store:main').find('user', id);
}
}.property('user_id')
});
// application.advanceReadiness();
}
};
/tests/integration/visitor-signs-up-test.js
test('As a user with valid email and password', function(){
var email = faker.internet.email();
signUpUser(email, 'correctpassword', 'correctpassword');
andThen(function(){
equal(find('#logged-in-user').text(), email, 'User registered successfully as ' + email);
equal(sessionIsAuthenticated(App), true, 'The session is Authenticated');
});
});
test/helpers/registration-login.js
export function signUpUser(email, password, passwordConfirmation) {
visit('/register').then(function(){
fillIn('input.email', email);
fillIn('input.password', password);
fillIn('input.password-confirmation', passwordConfirmation);
click('button.submit');
});
}
I have tried using
application.deferReadiness()
as you can see commented out in the initializer (also pass in application in that instance) to ensure the async request has completed and user is available but that hasn't worked either.
I am using Pretender to intercept the api requests, but the call to api/v1/users/:id isn't being made at all during the tests.
The strange part is it works perfectly in the browser.
I'm trying to understand why this won't this work? Any guidance would be appreciated!
NB: I have also tried solution listed here and here with same outcome as above.
I have figured out the problem, turns out I wasn't returning a user_id from the api/v1/users/sign_in request Pretender was intercepting hence when sessionAuthenticationSucceeded fired, there was no user_id available and thus currentUser was never being updated/triggered.
I'll leave all the code up there in case it helps somebody else. Comments or improvements to it are still very welcome!