Skip to content

Commit 858942d

Browse files
author
Eric Koleda
committed
Add in-memory cache for tokens, to avoid CacheService replication issues within the same execution.
1 parent 08f1bf7 commit 858942d

File tree

6 files changed

+187
-17
lines changed

6 files changed

+187
-17
lines changed

gulpfile.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ gulp.task('clean', function() {
2727
});
2828

2929
gulp.task('lint', function() {
30-
return gulp.src('src/*.gs')
30+
return gulp.src(['src/*.gs', 'test/**/*.js'])
3131
.pipe(jshint())
3232
.pipe(jshint.reporter(stylish));
3333
});

package.json

+6-2
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@
1313
},
1414
"homepage": "https://github.com/googlesamples/apps-script-oauth2",
1515
"devDependencies": {
16+
"chai": "^4.1.2",
1617
"del": "^1.2.1",
18+
"gas-local": "^1.3.0",
1719
"gulp": "^3.9.0",
1820
"gulp-bump": "^0.3.1",
1921
"gulp-clean": "^0.3.1",
@@ -24,7 +26,8 @@
2426
"gulp-rename": "^1.2.2",
2527
"gulp-strip-line": "0.0.1",
2628
"jsdoc": "^3.5.5",
27-
"jshint-stylish": "^2.0.1"
29+
"jshint-stylish": "^2.0.1",
30+
"mocha": "^4.0.1"
2831
},
2932
"dependencies": {
3033
"underscore": "^1.8.3"
@@ -33,6 +36,7 @@
3336
"postversion": "gulp dist",
3437
"dist": "gulp dist",
3538
"lint": "gulp lint",
36-
"doc": "jsdoc -c jsdoc.json src/*.gs README.md"
39+
"doc": "jsdoc -c jsdoc.json src/*.gs README.md",
40+
"test": "mocha"
3741
}
3842
}

src/Service.gs

+28-14
Original file line numberDiff line numberDiff line change
@@ -373,11 +373,12 @@ Service_.prototype.reset = function() {
373373
validate_({
374374
'Property store': this.propertyStore_
375375
});
376-
var key = this.getPropertyKey_(this.serviceName_);
376+
var key = this.getPropertyKey_();
377377
this.propertyStore_.deleteProperty(key);
378378
if (this.cache_) {
379379
this.cache_.remove(key);
380380
}
381+
this.token_ = null;
381382
};
382383

383384
/**
@@ -504,12 +505,13 @@ Service_.prototype.saveToken_ = function(token) {
504505
validate_({
505506
'Property store': this.propertyStore_
506507
});
507-
var key = this.getPropertyKey_(this.serviceName_);
508+
var key = this.getPropertyKey_();
508509
var value = JSON.stringify(token);
509510
this.propertyStore_.setProperty(key, value);
510511
if (this.cache_) {
511512
this.cache_.put(key, value, 21600);
512513
}
514+
this.token_ = token;
513515
};
514516

515517
/**
@@ -520,22 +522,34 @@ Service_.prototype.getToken = function() {
520522
validate_({
521523
'Property store': this.propertyStore_
522524
});
523-
var key = this.getPropertyKey_(this.serviceName_);
524-
var token;
525-
if (this.cache_) {
526-
token = this.cache_.get(key);
525+
526+
// Check in-memory cache.
527+
if (this.token_) {
528+
return this.token_;
527529
}
528-
if (!token) {
529-
token = this.propertyStore_.getProperty(key);
530+
531+
var key = this.getPropertyKey_();
532+
var token;
533+
534+
// Check CacheService cache.
535+
if (this.cache_ && (token = this.cache_.get(key))) {
536+
token = JSON.parse(token);
537+
this.token_ = token;
538+
return token;
530539
}
531-
if (token) {
540+
541+
// Check PropertiesService store.
542+
if ((token = this.propertyStore_.getProperty(key))) {
532543
if (this.cache_) {
533544
this.cache_.put(key, token, 21600);
534545
}
535-
return JSON.parse(token);
536-
} else {
537-
return null;
546+
token = JSON.parse(token);
547+
this.token_ = token;
548+
return token;
538549
}
550+
551+
// Not found.
552+
return null;
539553
};
540554

541555
/**
@@ -544,8 +558,8 @@ Service_.prototype.getToken = function() {
544558
* @return {string} The property key.
545559
* @private
546560
*/
547-
Service_.prototype.getPropertyKey_ = function(serviceName) {
548-
return 'oauth2.' + serviceName;
561+
Service_.prototype.getPropertyKey_ = function() {
562+
return 'oauth2.' + this.serviceName_;
549563
};
550564

551565
/**

test/mocks/cache.js

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
var MockCache = function(opt_store) {
2+
this.store = opt_store || {};
3+
};
4+
5+
MockCache.prototype.get = function(key) {
6+
return this.store[key];
7+
};
8+
9+
MockCache.prototype.put = function(key, value) {
10+
this.store[key] = value;
11+
};
12+
13+
MockCache.prototype.remove = function(key) {
14+
delete this.store[key];
15+
};
16+
17+
module.exports = MockCache;

test/mocks/properties.js

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
var MockProperties = function() {
2+
this.store = {};
3+
};
4+
5+
MockProperties.prototype.getProperty = function(key) {
6+
return this.store[key];
7+
};
8+
9+
MockProperties.prototype.setProperty = function(key, value) {
10+
this.store[key] = value;
11+
};
12+
13+
MockProperties.prototype.deleteProperty = function(key) {
14+
delete this.store[key];
15+
};
16+
17+
module.exports = MockProperties;

test/test.js

+118
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
var assert = require('chai').assert;
2+
var _ = require('underscore');
3+
var gas = require('gas-local');
4+
var MockProperties = require('./mocks/properties');
5+
var MockCache = require('./mocks/cache');
6+
7+
var mocks = {
8+
Underscore: {
9+
load: function() {
10+
return _;
11+
}
12+
},
13+
ScriptApp: {
14+
getScriptId: function() {
15+
return '12345';
16+
}
17+
}
18+
};
19+
var options = {
20+
filter: function(f) {
21+
return true;
22+
}
23+
};
24+
var OAuth2 = gas.require('./src', mocks, options);
25+
26+
27+
describe('Service', function() {
28+
describe('#getToken()', function() {
29+
it('should load from the in-memory store', function() {
30+
var service = OAuth2.createService('test')
31+
.setPropertyStore(new MockProperties())
32+
.setCache(new MockCache());
33+
var token = {
34+
access_token: 'foo'
35+
};
36+
service.token_ = token;
37+
38+
assert.deepEqual(service.getToken(), token);
39+
});
40+
41+
it('should load from the cache and set the in-memory store', function() {
42+
var cache = new MockCache();
43+
var service = OAuth2.createService('test')
44+
.setPropertyStore(new MockProperties())
45+
.setCache(cache);
46+
var token = {
47+
access_token: 'foo'
48+
};
49+
cache.put(service.getPropertyKey_(), JSON.stringify(token));
50+
51+
assert.notExists(service.token_);
52+
assert.deepEqual(service.getToken(), token);
53+
assert.deepEqual(service.token_, token);
54+
});
55+
56+
it('should load from the properties and set the cache and in-memory store', function() {
57+
var cache = new MockCache();
58+
var properties = new MockProperties();
59+
var service = OAuth2.createService('test')
60+
.setPropertyStore(properties)
61+
.setCache(cache);
62+
var token = {
63+
access_token: 'foo'
64+
};
65+
var key = service.getPropertyKey_();
66+
properties.setProperty(key, JSON.stringify(token));
67+
68+
69+
assert.notExists(service.token_);
70+
assert.deepEqual(service.getToken(), token);
71+
assert.deepEqual(service.token_, token);
72+
assert.deepEqual(JSON.parse(cache.get(key)), token);
73+
});
74+
});
75+
76+
describe('#saveToken_()', function() {
77+
it('should save the token to the properties, cache, and in-memory store', function() {
78+
var cache = new MockCache();
79+
var properties = new MockProperties();
80+
var service = OAuth2.createService('test')
81+
.setPropertyStore(properties)
82+
.setCache(cache);
83+
var token = {
84+
access_token: 'foo'
85+
};
86+
87+
service.saveToken_(token);
88+
89+
var key = service.getPropertyKey_();
90+
assert.deepEqual(service.token_, token);
91+
assert.deepEqual(JSON.parse(cache.get(key)), token);
92+
assert.deepEqual(JSON.parse(properties.getProperty(key)), token);
93+
});
94+
});
95+
96+
describe('#reset()', function() {
97+
it('should delete the token from properties, cache, and in-memory store', function() {
98+
var cache = new MockCache();
99+
var properties = new MockProperties();
100+
var service = OAuth2.createService('test')
101+
.setPropertyStore(properties)
102+
.setCache(cache);
103+
var token = {
104+
access_token: 'foo'
105+
};
106+
var key = service.getPropertyKey_();
107+
properties.setProperty(key, JSON.stringify(token));
108+
cache.put(key, JSON.stringify(token));
109+
service.token_ = token;
110+
111+
service.reset();
112+
113+
assert.notExists(service.token_);
114+
assert.notExists(cache.get(key));
115+
assert.notExists(properties.getProperty(key));
116+
});
117+
});
118+
});

0 commit comments

Comments
 (0)