Skip to content

Commit 74f07c3

Browse files
authored
refactor: convert TokenModel to an ES6 class and extract utils function for calculating lifetime
Merge pull request #223 from menewman/fix-convert-token-model-to-es6 thanks to @menewman
2 parents 320f947 + e4e2834 commit 74f07c3

File tree

4 files changed

+129
-42
lines changed

4 files changed

+129
-42
lines changed

lib/models/token-model.js

+57-42
Original file line numberDiff line numberDiff line change
@@ -3,63 +3,78 @@
33
/**
44
* Module dependencies.
55
*/
6-
76
const InvalidArgumentError = require('../errors/invalid-argument-error');
7+
const { getLifetimeFromExpiresAt } = require('../utils/date-util');
88

99
/**
10-
* Constructor.
10+
* The core model attributes allowed when allowExtendedTokenAttributes is false.
1111
*/
12+
const modelAttributes = new Set([
13+
'accessToken',
14+
'accessTokenExpiresAt',
15+
'refreshToken',
16+
'refreshTokenExpiresAt',
17+
'scope',
18+
'client',
19+
'user'
20+
]);
21+
22+
class TokenModel {
23+
constructor(data = {}, options = {}) {
24+
const {
25+
accessToken,
26+
accessTokenExpiresAt,
27+
refreshToken,
28+
refreshTokenExpiresAt,
29+
scope,
30+
client,
31+
user,
32+
} = data;
33+
34+
if (!accessToken) {
35+
throw new InvalidArgumentError('Missing parameter: `accessToken`');
36+
}
1237

13-
const modelAttributes = ['accessToken', 'accessTokenExpiresAt', 'refreshToken', 'refreshTokenExpiresAt', 'scope', 'client', 'user'];
14-
15-
function TokenModel(data, options) {
16-
data = data || {};
38+
if (!client) {
39+
throw new InvalidArgumentError('Missing parameter: `client`');
40+
}
1741

18-
if (!data.accessToken) {
19-
throw new InvalidArgumentError('Missing parameter: `accessToken`');
20-
}
42+
if (!user) {
43+
throw new InvalidArgumentError('Missing parameter: `user`');
44+
}
2145

22-
if (!data.client) {
23-
throw new InvalidArgumentError('Missing parameter: `client`');
24-
}
46+
if (accessTokenExpiresAt && !(accessTokenExpiresAt instanceof Date)) {
47+
throw new InvalidArgumentError('Invalid parameter: `accessTokenExpiresAt`');
48+
}
2549

26-
if (!data.user) {
27-
throw new InvalidArgumentError('Missing parameter: `user`');
28-
}
50+
if (refreshTokenExpiresAt && !(refreshTokenExpiresAt instanceof Date)) {
51+
throw new InvalidArgumentError('Invalid parameter: `refreshTokenExpiresAt`');
52+
}
2953

30-
if (data.accessTokenExpiresAt && !(data.accessTokenExpiresAt instanceof Date)) {
31-
throw new InvalidArgumentError('Invalid parameter: `accessTokenExpiresAt`');
32-
}
54+
this.accessToken = accessToken;
55+
this.accessTokenExpiresAt = accessTokenExpiresAt;
56+
this.client = client;
57+
this.refreshToken = refreshToken;
58+
this.refreshTokenExpiresAt = refreshTokenExpiresAt;
59+
this.scope = scope;
60+
this.user = user;
3361

34-
if (data.refreshTokenExpiresAt && !(data.refreshTokenExpiresAt instanceof Date)) {
35-
throw new InvalidArgumentError('Invalid parameter: `refreshTokenExpiresAt`');
36-
}
62+
if (accessTokenExpiresAt) {
63+
this.accessTokenLifetime = getLifetimeFromExpiresAt(accessTokenExpiresAt);
64+
}
3765

38-
this.accessToken = data.accessToken;
39-
this.accessTokenExpiresAt = data.accessTokenExpiresAt;
40-
this.client = data.client;
41-
this.refreshToken = data.refreshToken;
42-
this.refreshTokenExpiresAt = data.refreshTokenExpiresAt;
43-
this.scope = data.scope;
44-
this.user = data.user;
66+
const { allowExtendedTokenAttributes } = options;
4567

46-
if (options && options.allowExtendedTokenAttributes) {
47-
this.customAttributes = {};
68+
if (allowExtendedTokenAttributes) {
69+
this.customAttributes = {};
4870

49-
for (const key in data) {
50-
if ( Object.prototype.hasOwnProperty.call(data, key) && (modelAttributes.indexOf(key) < 0)) {
51-
this.customAttributes[key] = data[key];
52-
}
71+
Object.keys(data).forEach(key => {
72+
if (!modelAttributes.has(key)) {
73+
this.customAttributes[key] = data[key];
74+
}
75+
});
5376
}
5477
}
55-
56-
if(this.accessTokenExpiresAt) {
57-
this.accessTokenLifetime = Math.floor((this.accessTokenExpiresAt - new Date()) / 1000);
58-
}
5978
}
6079

61-
/**
62-
* Export constructor.
63-
*/
64-
6580
module.exports = TokenModel;

lib/utils/date-util.js

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
'use strict';
2+
3+
/**
4+
* @param expiresAt {Date} The date at which something (e.g. a token) expires.
5+
* @return {number} The number of seconds until the expiration date.
6+
*/
7+
function getLifetimeFromExpiresAt(expiresAt) {
8+
return Math.floor((expiresAt - new Date()) / 1000);
9+
}
10+
11+
module.exports = {
12+
getLifetimeFromExpiresAt,
13+
};

test/unit/models/token-model_test.js

+33
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,38 @@ describe('Model', function() {
2222
model.accessTokenLifetime.should.a('number');
2323
model.accessTokenLifetime.should.be.approximately(3600, 2);
2424
});
25+
26+
it('should throw if the required arguments are not provided', () => {
27+
should.throw(() => {
28+
new TokenModel({});
29+
});
30+
});
31+
32+
it('should ignore custom attributes if allowExtendedTokenAttributes is not specified as true', () => {
33+
const model = new TokenModel({
34+
accessToken: 'token',
35+
client: 'client',
36+
user: 'user',
37+
myCustomAttribute: 'myCustomValue'
38+
});
39+
40+
should.not.exist(model['myCustomAttribute']);
41+
should.not.exist(model['customAttributes']);
42+
});
43+
44+
it('should set custom attributes on the customAttributes field if allowExtendedTokenAttributes is specified as true', () => {
45+
const model = new TokenModel({
46+
accessToken: 'token',
47+
client: 'client',
48+
user: 'user',
49+
myCustomAttribute: 'myCustomValue'
50+
}, {
51+
allowExtendedTokenAttributes: true
52+
});
53+
54+
should.not.exist(model['myCustomAttribute']);
55+
model['customAttributes'].should.be.an('object');
56+
model['customAttributes']['myCustomAttribute'].should.equal('myCustomValue');
57+
});
2558
});
2659
});

test/unit/utils/date-util__test.js

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
const dateUtil = require('../../../lib/utils/date-util');
2+
3+
const sinon = require('sinon');
4+
require('chai').should();
5+
6+
describe('DateUtil', function() {
7+
describe('getLifetimeFromExpiresAt', () => {
8+
const now = new Date('2023-01-01T00:00:00.000Z');
9+
10+
beforeEach(() => {
11+
sinon.useFakeTimers(now);
12+
});
13+
14+
it('should convert a valid expiration date into seconds from now', () => {
15+
const expiresAt = new Date('2023-01-01T00:00:10.000Z');
16+
const lifetime = dateUtil.getLifetimeFromExpiresAt(expiresAt);
17+
18+
lifetime.should.be.a('number');
19+
lifetime.should.be.approximately(10, 2);
20+
});
21+
22+
afterEach(() => {
23+
sinon.restore();
24+
});
25+
});
26+
});

0 commit comments

Comments
 (0)