Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(222)

Side by Side Diff: remoting/webapp/crd/js/identity.js

Issue 937593002: Changed identity API to use promises instead of callbacks. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 /** 5 /**
6 * @fileoverview 6 * @fileoverview
7 * Wrapper class for Chrome's identity API. 7 * Wrapper class for Chrome's identity API.
8 */ 8 */
9 9
10 'use strict'; 10 'use strict';
(...skipping 13 matching lines...) Expand all
24 * @param {remoting.Identity.ConsentDialog=} opt_consentDialog 24 * @param {remoting.Identity.ConsentDialog=} opt_consentDialog
25 * @constructor 25 * @constructor
26 */ 26 */
27 remoting.Identity = function(opt_consentDialog) { 27 remoting.Identity = function(opt_consentDialog) {
28 /** @private */ 28 /** @private */
29 this.consentDialog_ = opt_consentDialog; 29 this.consentDialog_ = opt_consentDialog;
30 /** @type {string} @private */ 30 /** @type {string} @private */
31 this.email_ = ''; 31 this.email_ = '';
32 /** @type {string} @private */ 32 /** @type {string} @private */
33 this.fullName_ = ''; 33 this.fullName_ = '';
34 /** @type {Array.<remoting.Identity.Callbacks>} */ 34 /** @type {Promise<string>} */
35 this.pendingCallbacks_ = []; 35 this.pendingPromise_ = null;
36 }; 36 };
37 37
38 /** 38 /**
39 * chrome.identity.getAuthToken should be initiated from user interactions if 39 * chrome.identity.getAuthToken should be initiated from user interactions if
40 * called with interactive equals true. This interface prompts a dialog for 40 * called with interactive equals true. This interface prompts a dialog for
41 * the user's consent. 41 * the user's consent.
42 * 42 *
43 * @interface 43 * @interface
44 */ 44 */
45 remoting.Identity.ConsentDialog = function() {}; 45 remoting.Identity.ConsentDialog = function() {};
46 46
47 /** 47 /**
48 * @return {Promise} A Promise that resolves when permission to start an 48 * @return {Promise} A Promise that resolves when permission to start an
49 * interactive flow is granted. 49 * interactive flow is granted.
50 */ 50 */
51 remoting.Identity.ConsentDialog.prototype.show = function() {}; 51 remoting.Identity.ConsentDialog.prototype.show = function() {};
52 52
53 /** 53 /**
54 * Call a function with an access token. 54 * Get an access token.
55 * 55 *
56 * @param {function(string):void} onOk Function to invoke with access token if 56 * @return {!Promise<string>} A promise resolved the an access token or
57 * an access token was successfully retrieved. 57 * rejected with a remoting.Error.
58 * @param {function(remoting.Error):void} onError Function to invoke with an
59 * error code on failure.
60 * @return {void} Nothing.
61 */ 58 */
62 remoting.Identity.prototype.callWithToken = function(onOk, onError) { 59 remoting.Identity.prototype.getToken = function() {
63 this.pendingCallbacks_.push(new remoting.Identity.Callbacks(onOk, onError)); 60 /** @const */
64 if (this.pendingCallbacks_.length == 1) { 61 var that = this;
65 chrome.identity.getAuthToken( 62
66 { 'interactive': false }, 63 if (this.pendingPromise_ == null) {
67 this.onAuthComplete_.bind(this, false)); 64 this.pendingPromise_ = new Promise(function(
kelvinp 2015/02/18 19:45:24 I think base.Deferred will work better as you want
John Williams 2015/02/18 20:28:22 Done.
65 /** function(string) */ resolve,
kelvinp 2015/02/18 19:45:24 Is this a new syntax of annotating parameters? I l
John Williams 2015/02/18 20:28:22 It's a recent addition to the syntax. I like it f
66 /** function(remoting.Error) */ reject) {
67 chrome.identity.getAuthToken(
68 { 'interactive': false },
69 that.onAuthComplete_.bind(that, resolve, reject, false));
70 });
68 } 71 }
72 return this.pendingPromise_;
69 }; 73 };
70 74
71 /** 75 /**
72 * Call a function with a fresh access token. 76 * Get a fresh access token.
kelvinp 2015/02/18 19:45:24 Nit: Gets
John Williams 2015/02/18 20:28:22 Done.
73 * 77 *
74 * @param {function(string):void} onOk Function to invoke with access token if 78 * @return {!Promise<string>} A promise resolved the an access token or
kelvinp 2015/02/18 19:45:24 s/resolved the an/resolved with an/
75 * an access token was successfully retrieved. 79 * rejected with a remoting.Error.
76 * @param {function(remoting.Error):void} onError Function to invoke with an
77 * error code on failure.
78 * @return {void} Nothing.
79 */ 80 */
80 remoting.Identity.prototype.callWithNewToken = function(onOk, onError) { 81 remoting.Identity.prototype.getNewToken = function() {
81 /** @type {remoting.Identity} */ 82 /** @type {remoting.Identity} */
82 var that = this; 83 var that = this;
83 84
84 /** 85 return this.getToken().then(function(/** string */ token) {
85 * @param {string} token 86 return new Promise(function(resolve, reject) {
86 */ 87 chrome.identity.removeCachedAuthToken({'token': token }, function() {
87 function revokeToken(token) { 88 resolve(that.getToken());
88 chrome.identity.removeCachedAuthToken( 89 });
89 {'token': token }, 90 });
90 that.callWithToken.bind(that, onOk, onError)); 91 });
91 }
92
93 this.callWithToken(revokeToken, onError);
94 }; 92 };
95 93
96 /** 94 /**
97 * Remove the cached auth token, if any. 95 * Remove the cached auth token, if any.
98 * 96 *
99 * @param {function():void=} opt_onDone Completion callback. 97 * @return {!Promise<null>} A promise resolved with the operation completes.
100 * @return {void} Nothing.
101 */ 98 */
102 remoting.Identity.prototype.removeCachedAuthToken = function(opt_onDone) { 99 remoting.Identity.prototype.removeCachedAuthToken = function() {
103 var onDone = (opt_onDone) ? opt_onDone : base.doNothing; 100 return new Promise(function(resolve, reject) {
104 101 /** @param {string} token */
105 /** @param {string} token */ 102 var onToken = function(token) {
106 var onToken = function(token) { 103 if (token) {
107 if (token) { 104 chrome.identity.removeCachedAuthToken(
108 chrome.identity.removeCachedAuthToken({'token': token}, onDone); 105 {'token': token}, resolve.bind(null, null));
109 } else { 106 } else {
110 onDone(); 107 resolve(null);
111 } 108 }
112 }; 109 };
113 chrome.identity.getAuthToken({'interactive': false}, onToken); 110 chrome.identity.getAuthToken({'interactive': false}, onToken);
111 });
114 }; 112 };
115 113
116 /** 114 /**
117 * Get the user's email address and full name. 115 * Get the user's email address and full name. The full name will be
118 * The full name will be null unless the webapp has requested and been 116 * null unless the webapp has requested and been granted the
119 * granted the userinfo.profile permission. 117 * userinfo.profile permission.
120 * 118 *
121 * @param {function(string,string):void} onOk Callback invoked when the user's 119 * TODO(jrw): Type declarations say the name can't be null. Are the
122 * email address and full name are available. 120 * types wrong, or is the documentation wrong?
123 * @param {function(remoting.Error):void} onError Callback invoked if an 121 *
124 * error occurs. 122 * @return {!Promise<{email: string, name: string}>} Promise
kelvinp 2015/02/18 19:45:24 no space between : and type name
John Williams 2015/02/18 20:28:22 Done.
125 * @return {void} Nothing. 123 * resolved with the user's email address and full name, or rejected
124 * with a remoting.Error.
126 */ 125 */
127 remoting.Identity.prototype.getUserInfo = function(onOk, onError) { 126 remoting.Identity.prototype.getUserInfo = function() {
128 if (this.isAuthenticated()) { 127 if (this.isAuthenticated()) {
129 onOk(this.email_, this.fullName_); 128 /**
130 return; 129 * The temp variable is needed to work around a compiler bug.
130 * @type {{email: string, name: string}}
131 */
132 var result = {email: this.email_, name: this.fullName_};
133 return Promise.resolve(result);
131 } 134 }
132 135
133 /** @type {remoting.Identity} */ 136 /** @type {remoting.Identity} */
134 var that = this; 137 var that = this;
135 138
136 /** 139 return this.getToken().then(function(token) {
137 * @param {string} email 140 return new Promise(function(resolve, reject) {
138 * @param {string} name 141 /**
139 */ 142 * @param {string} email
140 var onResponse = function(email, name) { 143 * @param {string} name
141 that.email_ = email; 144 */
142 that.fullName_ = name; 145 var onResponse = function(email, name) {
143 onOk(email, name); 146 that.email_ = email;
144 }; 147 that.fullName_ = name;
148 resolve({email: email, name: name});
149 };
145 150
146 this.callWithToken( 151 remoting.oauth2Api.getUserInfo(onResponse, reject, token);
147 remoting.oauth2Api.getUserInfo.bind( 152 });
148 remoting.oauth2Api, onResponse, onError), 153 });
149 onError);
150 }; 154 };
151 155
152 /** 156 /**
153 * Get the user's email address. 157 * Get the user's email address.
154 * 158 *
155 * @param {function(string):void} onOk Callback invoked when the email 159 * @return {!Promise<string>} Promise resolved with the user's email
156 * address is available. 160 * address or rejected with a remoting.Error.
157 * @param {function(remoting.Error):void} onError Callback invoked if an
158 * error occurs.
159 * @return {void} Nothing.
160 */ 161 */
161 remoting.Identity.prototype.getEmail = function(onOk, onError) { 162 remoting.Identity.prototype.getEmail = function() {
162 this.getUserInfo(function(email, name) { 163 this.getUserInfo().then(function(userInfo) {
163 onOk(email); 164 return userInfo.email;
164 }, onError); 165 });
165 }; 166 };
166 167
167 /** 168 /**
168 * Get the user's email address, or null if no successful call to getUserInfo 169 * Get the user's email address, or null if no successful call to getUserInfo
169 * has been made. 170 * has been made.
170 * 171 *
171 * @return {?string} The cached email address, if available. 172 * @return {?string} The cached email address, if available.
172 */ 173 */
173 remoting.Identity.prototype.getCachedEmail = function() { 174 remoting.Identity.prototype.getCachedEmail = function() {
174 return this.email_; 175 return this.email_;
175 }; 176 };
176 177
177 /** 178 /**
178 * Get the user's full name. 179 * Get the user's full name.
179 * This will return null if either: 180 * This will return null if either:
180 * No successful call to getUserInfo has been made, or 181 * No successful call to getUserInfo has been made, or
181 * The webapp doesn't have permission to access this value. 182 * The webapp doesn't have permission to access this value.
182 * 183 *
183 * @return {?string} The cached user's full name, if available. 184 * @return {?string} The cached user's full name, if available.
184 */ 185 */
185 remoting.Identity.prototype.getCachedUserFullName = function() { 186 remoting.Identity.prototype.getCachedUserFullName = function() {
186 return this.fullName_; 187 return this.fullName_;
187 }; 188 };
188 189
189 /** 190 /**
190 * Callback for the getAuthToken API. 191 * Callback for the getAuthToken API.
191 * 192 *
193 * @param {function(string):void} resolve
194 * @param {function(remoting.Error):void} reject
192 * @param {boolean} interactive The value of the "interactive" parameter to 195 * @param {boolean} interactive The value of the "interactive" parameter to
193 * getAuthToken. 196 * getAuthToken.
194 * @param {?string} token The auth token, or null if the request failed. 197 * @param {?string} token The auth token, or null if the request failed.
195 * @private 198 * @private
196 */ 199 */
197 remoting.Identity.prototype.onAuthComplete_ = function(interactive, token) { 200 remoting.Identity.prototype.onAuthComplete_ = function(
201 resolve, reject, interactive, token) {
kelvinp 2015/02/18 19:45:24 As commented earlier, use base.Deferred instead
John Williams 2015/02/18 20:28:22 Done.
198 // Pass the token to the callback(s) if it was retrieved successfully. 202 // Pass the token to the callback(s) if it was retrieved successfully.
199 if (token) { 203 if (token) {
200 while (this.pendingCallbacks_.length > 0) { 204 resolve(token);
201 var callback = /** @type {remoting.Identity.Callbacks} */ 205 this.pendingPromise_ = null;
202 (this.pendingCallbacks_.shift());
203 callback.onOk(token);
204 }
205 return; 206 return;
206 } 207 }
207 208
208 // If not, pass an error back to the callback(s) if we've already prompted the 209 // If not, pass an error back to the callback(s) if we've already prompted the
209 // user for permission. 210 // user for permission.
210 if (interactive) { 211 if (interactive) {
211 var error_message = 212 var error_message =
212 chrome.runtime.lastError ? chrome.runtime.lastError.message 213 chrome.runtime.lastError ? chrome.runtime.lastError.message
213 : 'Unknown error.'; 214 : 'Unknown error.';
214 console.error(error_message); 215 console.error(error_message);
215 while (this.pendingCallbacks_.length > 0) { 216 reject(remoting.Error.NOT_AUTHENTICATED);
216 var callback = /** @type {remoting.Identity.Callbacks} */
217 (this.pendingCallbacks_.shift());
218 callback.onError(remoting.Error.NOT_AUTHENTICATED);
219 }
220 return; 217 return;
221 } 218 }
222 219
223 // If there's no token, but we haven't yet prompted for permission, do so 220 // If there's no token, but we haven't yet prompted for permission, do so
224 // now. 221 // now.
225 var that = this; 222 var that = this;
226 var showConsentDialog = 223 var showConsentDialog =
227 (this.consentDialog_) ? this.consentDialog_.show() : Promise.resolve(); 224 (this.consentDialog_) ? this.consentDialog_.show() : Promise.resolve();
228 showConsentDialog.then(function() { 225 showConsentDialog.then(function() {
229 chrome.identity.getAuthToken({'interactive': true}, 226 chrome.identity.getAuthToken({'interactive': true},
230 that.onAuthComplete_.bind(that, true)); 227 that.onAuthComplete_.bind(
228 that, resolve, reject, true));
kelvinp 2015/02/18 19:45:24 indentation
John Williams 2015/02/18 20:28:22 Done.
231 }); 229 });
232 }; 230 };
233 231
234 /** 232 /**
235 * Internal representation for pair of callWithToken callbacks.
236 *
237 * @param {function(string):void} onOk
238 * @param {function(remoting.Error):void} onError
239 * @constructor
240 * @private
241 */
242 remoting.Identity.Callbacks = function(onOk, onError) {
243 /** @type {function(string):void} */
244 this.onOk = onOk;
245 /** @type {function(remoting.Error):void} */
246 this.onError = onError;
247 };
248
249 /**
250 * Returns whether the web app has authenticated with the Google services. 233 * Returns whether the web app has authenticated with the Google services.
251 * 234 *
252 * @return {boolean} 235 * @return {boolean}
253 */ 236 */
254 remoting.Identity.prototype.isAuthenticated = function() { 237 remoting.Identity.prototype.isAuthenticated = function() {
255 return remoting.identity.email_ !== ''; 238 return remoting.identity.email_ !== '';
256 }; 239 };
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698