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

[2314] Add setup method in session service #2318

Merged
merged 8 commits into from
Oct 29, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 64 additions & 0 deletions guides/upgrade-to-v4.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
### Call session#setup method in your ApplicationRoute
ESA implemented setup method on session service for users to manually implement in their ApplicationRoutes.
Reason being that ESA wants to move away from relying on initializers which have been spinning up the session services for you by default.

***READ THIS if you are on ESA 4.1.0 and want to opt-in into this behavior***
In order to opt-in into this early, you'll need to add some configuration for ember-simple-auth.

Inside your `ember-cli-build.js` you'll need to add `useSessionSetupMethod` flag and set it to `true`:
```js
'ember-simple-auth': {
useSessionSetupMethod: true,
}
```
This will tell ESA to not use initializer to spin-up the session service.
And will exclude `routes/application.js` file from Ember Simple Auth addon which might've been causing some build issues while using with typescript.

With initializers gone, you'll need to call `session#setup` method directly in your application route.

```js
import Route from '@ember/routing/route';
import { inject as service } from '@ember/service';

export default class Application extends Route {
@service session;

async beforeModel() {
await this.session.setup();
}
}
```

If you had any custom setup in your `beforeModel` then you'll want to move this below the session setup to preserve timings.

***Old***
```
import Route from '@ember/routing/route';
import { inject as service } from '@ember/service';

export default class Application extends Route {
@service intl;

beforeModel() {
this.intl.setLocale('pl-PL');
}
}
```


***New***
```js
import Route from '@ember/routing/route';
import { inject as service } from '@ember/service';

export default class Application extends Route {
@service session;
@service intl;

async beforeModel() {
await this.session.setup();
this.intl.setLocale('pl-PL');
}
}
```

14 changes: 14 additions & 0 deletions packages/ember-simple-auth/addon/configuration.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import useSessionSetupMethod from 'ember-simple-auth/use-session-setup-method';

const DEFAULTS = {
rootURL: '',
routeAfterAuthentication: 'index',
Expand Down Expand Up @@ -36,6 +38,18 @@ export default {
*/
routeAfterAuthentication: DEFAULTS.routeAfterAuthentication,

/**
Flag used to determine whether users have decided to setup session service themselves.
This lets us to return early from initializer which would setup the service automatically.

This will be the default behavior in the future.

@property useSessionSetupMethod
@default false
@private
*/
useSessionSetupMethod,

load(config) {
this.rootURL = config.rootURL !== undefined ? config.rootURL : DEFAULTS.rootURL;
this.routeAfterAuthentication =
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
import { getOwner } from '@ember/application';
import Configuration from '../configuration';
import { deprecate } from '@ember/application/deprecations';

export default function setupSessionRestoration(registry) {
if (Configuration.useSessionSetupMethod) {
// return early in case users chose to opt out of initializer behavior.
return;
}
marcoow marked this conversation as resolved.
Show resolved Hide resolved

deprecate('Ember Simple Auth: The automatic session initialization is deprecated. Please inject session service in your application route and call the setup method manually.', false, {
id: 'ember-simple-auth.initializer.setup-session-restoration',
until: '5.0.0'
});
BobrImperator marked this conversation as resolved.
Show resolved Hide resolved

const ApplicationRoute = registry.resolveRegistration
? registry.resolveRegistration('route:application')
: registry.resolve('route:application');
Expand Down
30 changes: 29 additions & 1 deletion packages/ember-simple-auth/addon/services/session.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ function deprecateSessionEvents() {
}
}

function assertSetupHasBeenCalled(isSetupCalled) {
if (!isSetupCalled && Configuration.useSessionSetupMethod) {
assert("Ember Simple Auth: session#setup wasn't called. Make sure to call session#setup in your application route's beforeModel hook.", false);
}
}

/**
__The session service provides access to the current session as well as
methods to authenticate it, invalidate it, etc.__ It is the main interface for
Expand Down Expand Up @@ -293,6 +299,7 @@ export default Service.extend(Evented, {
@public
*/
requireAuthentication(transition, routeOrCallback) {
assertSetupHasBeenCalled(this._setupIsCalled);
let isAuthenticated = requireAuthentication(getOwner(this), transition);
if (!isAuthenticated) {
let argType = typeof routeOrCallback;
Expand All @@ -317,6 +324,7 @@ export default Service.extend(Evented, {
@public
*/
prohibitAuthentication(routeOrCallback) {
assertSetupHasBeenCalled(this._setupIsCalled);
let isAuthenticated = this.get('isAuthenticated');
if (isAuthenticated) {
let argType = typeof routeOrCallback;
Expand Down Expand Up @@ -367,5 +375,25 @@ export default Service.extend(Evented, {
*/
handleInvalidation(routeAfterInvalidation) {
handleSessionInvalidated(getOwner(this), routeAfterInvalidation);
}
},

/**
Sets up the session service.

This method must be called when the application starts up,
usually as the first thing in the `application` route's `beforeModel`
method.

@method setup
@public
*/
async setup() {
this._setupIsCalled = true;
marcoow marked this conversation as resolved.
Show resolved Hide resolved
try {
this._setupHandlers();
await this.session.restore();
} catch (error) {
// If it raises an error then it means that restore didn't find any restorable state.
}
},
});
28 changes: 27 additions & 1 deletion packages/ember-simple-auth/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

var path = require('path');
var Funnel = require('broccoli-funnel');
var writeFile = require('broccoli-file-creator');
var MergeTrees = require('broccoli-merge-trees');

module.exports = {
name: 'ember-simple-auth',
Expand All @@ -13,6 +15,10 @@ module.exports = {
this._super.included.apply(this, arguments);
this._ensureThisImport();

const app = this._findHost();
const config = app.options['ember-simple-auth'] || {};
this.addonConfig = config;

this.import('vendor/base64.js');
},

Expand All @@ -22,7 +28,26 @@ module.exports = {
});
},

_ensureThisImport() {
treeForAddon(tree) {
let useSessionSetupMethodConfig = writeFile(
'use-session-setup-method.js',
`export default ${Boolean(this.addonConfig.useSessionSetupMethod)};`
);

return this._super.treeForAddon.call(this, MergeTrees([tree, useSessionSetupMethodConfig]));
},

treeForApp(tree) {
if (this.addonConfig.useSessionSetupMethod) {
return new Funnel(tree, {
exclude: ['routes/application.js']
});
}

return this._super.treeForApp.apply(this, arguments);
},

_ensureThisImport: function() {
if (!this.import) {
this._findHost = function findHostShim() {
var current = this;
Expand All @@ -39,3 +64,4 @@ module.exports = {
}
}
};

2 changes: 2 additions & 0 deletions packages/ember-simple-auth/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
},
"dependencies": {
"base-64": "^0.1.0",
"broccoli-file-creator": "^2.1.1",
"broccoli-merge-trees": "^4.0.0",
"broccoli-funnel": "^1.2.0 || ^2.0.0",
"ember-cli-babel": "^7.20.5",
"ember-cli-is-package-missing": "^1.0.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import { describe, beforeEach, it } from 'mocha';
import { expect } from 'chai';
import sinonjs from 'sinon';
import setupSessionRestoration from 'ember-simple-auth/initializers/setup-session-restoration';
import Configuration from 'ember-simple-auth/configuration';
import { registerDeprecationHandler } from '@ember/debug';

describe('setupSessionRestoration', () => {
setupTest();
Expand All @@ -29,6 +31,64 @@ describe('setupSessionRestoration', () => {
expect(route).to.respondTo('beforeModel');
});

describe('useSessionSetupMethod', function() {
let useSessionSetupMethodDefault;

beforeEach(function() {
useSessionSetupMethodDefault = Configuration.useSessionSetupMethod;
Configuration.useSessionSetupMethod = false;
});

afterEach(function() {
Configuration.useSessionSetupMethod = useSessionSetupMethodDefault;
});

it("doesn't extend application route when is true", function() {
Configuration.useSessionSetupMethod = true;
const route = this.owner.resolveRegistration('route:application');
const reopenStub = sinon.stub(route, 'reopen');

setupSessionRestoration(this.owner);
expect(reopenStub).to.not.have.been.called;
});

it('extends application route when is false', function() {
const route = this.owner.resolveRegistration('route:application');
const reopenStub = sinon.stub(route, 'reopen');

setupSessionRestoration(this.owner);
expect(reopenStub).to.have.been.called;
});

it("doesn't show deprecation when is true", function() {
Configuration.useSessionSetupMethod = true;

let deprecations = [];
registerDeprecationHandler((message, options, next) => {
deprecations.push(message);

next(message, options);
});

setupSessionRestoration(this.owner);

expect(deprecations.filter(deprecation => deprecation.includes('Ember Simple Auth:'))).to.have.length(0);
});

it('shows deprecation when is false', function() {
let deprecations = [];
registerDeprecationHandler((message, options, next) => {
deprecations.push(message);

next(message, options);
});

setupSessionRestoration(this.owner);

expect(deprecations.filter(deprecation => deprecation.includes('Ember Simple Auth:'))).to.have.length(1);
});
});

describe('the beforeModel method', function() {
let session;
let route;
Expand Down
55 changes: 55 additions & 0 deletions packages/ember-simple-auth/tests/unit/services/session-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { setupTest } from 'ember-mocha';
import { expect } from 'chai';
import sinonjs from 'sinon';
import * as LocationUtil from 'ember-simple-auth/utils/location';
import Configuration from 'ember-simple-auth/configuration';

describe('SessionService', () => {
setupTest();
Expand Down Expand Up @@ -527,4 +528,58 @@ describe('SessionService', () => {
});
});
});

describe('setup', function() {
it('sets up handlers', async function() {
const setupHandlersStub = sinon.stub(sessionService, '_setupHandlers');

await sessionService.setup();
expect(setupHandlersStub).to.have.been.calledOnce;
});

it("doesn't raise an error when restore rejects", async function() {
sinon.stub(sessionService, '_setupHandlers');
sinon.stub(session, 'restore').throws();

await sessionService.setup();
});

describe('when using session methods', function() {
let useSessionSetupMethodDefault;

beforeEach(function() {
useSessionSetupMethodDefault = Configuration.useSessionSetupMethod;
Configuration.useSessionSetupMethod = false;
});

afterEach(function() {
Configuration.useSessionSetupMethod = useSessionSetupMethodDefault;
});

it('throws assertion when session methods are called before session#setup', async function() {
Configuration.useSessionSetupMethod = true;
let error;
try {
await sessionService.prohibitAuthentication();
} catch (assertion) {
error = assertion;
}
expect(error.message).to.eq("Assertion Failed: Ember Simple Auth: session#setup wasn't called. Make sure to call session#setup in your application route's beforeModel hook.");
});

it("doesn't throw assertion when session methods are called after session#setup", async function() {
Configuration.useSessionSetupMethod = true;
let error;

await sessionService.setup();
try {
await sessionService.prohibitAuthentication();
} catch (assertion) {
error = assertion;
}

expect(error).to.be.undefined;
});
});
});
});
3 changes: 2 additions & 1 deletion packages/test-app/app/routes/application.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ export default Route.extend({
session: service(),
sessionAccount: service(),

beforeModel() {
async beforeModel() {
await this.get('session').setup();
return this.get('sessionAccount').loadCurrentUser().catch(() => this.get('session').invalidate());
}
});
4 changes: 3 additions & 1 deletion packages/test-app/ember-cli-build.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ const EmberApp = require('ember-cli/lib/broccoli/ember-app');

module.exports = function(defaults) {
let app = new EmberApp(defaults, {
// Add options here
'ember-simple-auth': {
useSessionSetupMethod: true,
},
});

// Use `app.import` to add additional libraries to the generated
Expand Down
2 changes: 1 addition & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3753,7 +3753,7 @@ broccoli-merge-trees@^3.0.0, broccoli-merge-trees@^3.0.1, broccoli-merge-trees@^
broccoli-plugin "^1.3.0"
merge-trees "^2.0.0"

broccoli-merge-trees@^4.1.0, broccoli-merge-trees@^4.2.0:
broccoli-merge-trees@^4.0.0, broccoli-merge-trees@^4.1.0, broccoli-merge-trees@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/broccoli-merge-trees/-/broccoli-merge-trees-4.2.0.tgz#692d3c163ecea08c5714a9434d664e628919f47c"
integrity sha512-nTrQe5AQtCrW4enLRvbD/vTLHqyW2tz+vsLXQe4IEaUhepuMGVKJJr+I8n34Vu6fPjmPLwTjzNC8izMIDMtHPw==
Expand Down