fix: Updates gapi to use new google identity service.
fix: Updates gapi to use new google identity service.
This commit is contained in:
parent
29b6ce7721
commit
bbb339d86f
|
@ -52,6 +52,7 @@ export function loadGoogleAPI() {
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
})
|
})
|
||||||
.then(() => dispatch(setGoogleAPIState(GOOGLE_API_STATES.LOADED)))
|
.then(() => dispatch(setGoogleAPIState(GOOGLE_API_STATES.LOADED)))
|
||||||
|
.then(() => googleApi.signInIfNotSignedIn())
|
||||||
.then(() => googleApi.isSignedIn())
|
.then(() => googleApi.isSignedIn())
|
||||||
.then(isSignedIn => {
|
.then(isSignedIn => {
|
||||||
if (isSignedIn) {
|
if (isSignedIn) {
|
||||||
|
@ -150,8 +151,7 @@ export function setGoogleAPIState(
|
||||||
* selectedBoundStreamID: *} | never>)}
|
* selectedBoundStreamID: *} | never>)}
|
||||||
*/
|
*/
|
||||||
export function showAccountSelection() {
|
export function showAccountSelection() {
|
||||||
return () =>
|
return () => googleApi.showAccountSelection(true);
|
||||||
googleApi.showAccountSelection();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -161,7 +161,7 @@ export function showAccountSelection() {
|
||||||
*/
|
*/
|
||||||
export function signIn() {
|
export function signIn() {
|
||||||
return (dispatch: Dispatch<any>) => googleApi.get()
|
return (dispatch: Dispatch<any>) => googleApi.get()
|
||||||
.then(() => googleApi.signInIfNotSignedIn())
|
.then(() => googleApi.signInIfNotSignedIn(true))
|
||||||
.then(() => dispatch({
|
.then(() => dispatch({
|
||||||
type: SET_GOOGLE_API_STATE,
|
type: SET_GOOGLE_API_STATE,
|
||||||
googleAPIState: GOOGLE_API_STATES.SIGNED_IN
|
googleAPIState: GOOGLE_API_STATES.SIGNED_IN
|
||||||
|
@ -205,10 +205,10 @@ export function updateProfile() {
|
||||||
.then(profile => {
|
.then(profile => {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: SET_GOOGLE_API_PROFILE,
|
type: SET_GOOGLE_API_PROFILE,
|
||||||
profileEmail: profile.getEmail()
|
profileEmail: profile.email
|
||||||
});
|
});
|
||||||
|
|
||||||
return profile.getEmail();
|
return profile.email;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,10 +20,9 @@ export const API_URL_LIVE_BROADCASTS = 'https://content.googleapis.com/youtube/v
|
||||||
/**
|
/**
|
||||||
* Array of API discovery doc URLs for APIs used by the googleApi.
|
* Array of API discovery doc URLs for APIs used by the googleApi.
|
||||||
*
|
*
|
||||||
* @type {string[]}
|
* @type {string}
|
||||||
*/
|
*/
|
||||||
export const DISCOVERY_DOCS
|
export const DISCOVERY_DOCS = 'https://www.googleapis.com/discovery/v1/apis/calendar/v3/rest';
|
||||||
= [ 'https://www.googleapis.com/discovery/v1/apis/calendar/v3/rest' ];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An enumeration of the different states the Google API can be in.
|
* An enumeration of the different states the Google API can be in.
|
||||||
|
@ -61,6 +60,13 @@ export const GOOGLE_API_STATES = {
|
||||||
*/
|
*/
|
||||||
export const GOOGLE_SCOPE_CALENDAR = 'https://www.googleapis.com/auth/calendar';
|
export const GOOGLE_SCOPE_CALENDAR = 'https://www.googleapis.com/auth/calendar';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Google API auth scope to access user email.
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
export const GOOGLE_SCOPE_USERINFO = 'https://www.googleapis.com/auth/userinfo.email';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Google API auth scope to access YouTube streams.
|
* Google API auth scope to access YouTube streams.
|
||||||
*
|
*
|
||||||
|
|
|
@ -3,10 +3,13 @@ import {
|
||||||
API_URL_LIVE_BROADCASTS,
|
API_URL_LIVE_BROADCASTS,
|
||||||
DISCOVERY_DOCS,
|
DISCOVERY_DOCS,
|
||||||
GOOGLE_SCOPE_CALENDAR,
|
GOOGLE_SCOPE_CALENDAR,
|
||||||
|
GOOGLE_SCOPE_USERINFO,
|
||||||
GOOGLE_SCOPE_YOUTUBE
|
GOOGLE_SCOPE_YOUTUBE
|
||||||
} from './constants';
|
} from './constants';
|
||||||
|
import logger from './logger';
|
||||||
|
|
||||||
const GOOGLE_API_CLIENT_LIBRARY_URL = 'https://apis.google.com/js/api.js';
|
const GOOGLE_API_CLIENT_LIBRARY_URL = 'https://apis.google.com/js/api.js';
|
||||||
|
const GOOGLE_GIS_LIBRARY_URL = 'https://accounts.google.com/gsi/client';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A promise for dynamically loading the Google API Client Library.
|
* A promise for dynamically loading the Google API Client Library.
|
||||||
|
@ -50,9 +53,9 @@ const googleApi = {
|
||||||
}
|
}
|
||||||
|
|
||||||
return this._getGoogleApiClient()
|
return this._getGoogleApiClient()
|
||||||
.auth2.getAuthInstance()
|
.client.oauth2
|
||||||
.currentUser.get()
|
.userinfo.get().getPromise()
|
||||||
.getBasicProfile();
|
.then(r => r.result);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -68,23 +71,40 @@ const googleApi = {
|
||||||
initializeClient(clientId, enableYoutube, enableCalendar) {
|
initializeClient(clientId, enableYoutube, enableCalendar) {
|
||||||
return this.get()
|
return this.get()
|
||||||
.then(api => new Promise((resolve, reject) => {
|
.then(api => new Promise((resolve, reject) => {
|
||||||
const scope
|
|
||||||
= `${enableYoutube ? GOOGLE_SCOPE_YOUTUBE : ''} ${enableCalendar ? GOOGLE_SCOPE_CALENDAR : ''}`
|
|
||||||
.trim();
|
|
||||||
|
|
||||||
// setTimeout is used as a workaround for api.client.init not
|
// setTimeout is used as a workaround for api.client.init not
|
||||||
// resolving consistently when the Google API Client Library is
|
// resolving consistently when the Google API Client Library is
|
||||||
// loaded asynchronously. See:
|
// loaded asynchronously. See:
|
||||||
// github.com/google/google-api-javascript-client/issues/399
|
// github.com/google/google-api-javascript-client/issues/399
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
api.client.init({
|
api.client.init({})
|
||||||
clientId,
|
.then(() => {
|
||||||
discoveryDocs: DISCOVERY_DOCS,
|
if (enableCalendar) {
|
||||||
scope
|
api.client.load(DISCOVERY_DOCS);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
api.client.load('https://www.googleapis.com/discovery/v1/apis/oauth2/v1/rest');
|
||||||
})
|
})
|
||||||
.then(resolve)
|
.then(resolve)
|
||||||
.catch(reject);
|
.catch(reject);
|
||||||
}, 500);
|
}, 500);
|
||||||
|
}))
|
||||||
|
.then(() => new Promise((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
const scope
|
||||||
|
= `${enableYoutube ? GOOGLE_SCOPE_YOUTUBE : ''} ${enableCalendar ? GOOGLE_SCOPE_CALENDAR : ''}`
|
||||||
|
.trim();
|
||||||
|
|
||||||
|
this.tokenClient = this._getGoogleGISApiClient().accounts.oauth2.initTokenClient({
|
||||||
|
// eslint-disable-next-line camelcase
|
||||||
|
client_id: clientId,
|
||||||
|
scope: `${scope} ${GOOGLE_SCOPE_USERINFO}`,
|
||||||
|
callback: '' // defined at request time in await/promise scope.
|
||||||
|
});
|
||||||
|
resolve();
|
||||||
|
} catch (err) {
|
||||||
|
reject(err);
|
||||||
|
}
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -95,13 +115,38 @@ const googleApi = {
|
||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
isSignedIn() {
|
isSignedIn() {
|
||||||
return this.get()
|
return new Promise((resolve, _) => {
|
||||||
.then(api => Boolean(api
|
const te = parseInt(this.tokenExpires, 10);
|
||||||
&& api.auth2
|
const isExpired = isNaN(this.tokenExpires) ? true : new Date().getTime() > te;
|
||||||
&& api.auth2.getAuthInstance
|
|
||||||
&& api.auth2.getAuthInstance()
|
resolve(Boolean(!isExpired));
|
||||||
&& api.auth2.getAuthInstance().isSignedIn
|
});
|
||||||
&& api.auth2.getAuthInstance().isSignedIn.get()));
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a script tag.
|
||||||
|
*
|
||||||
|
* @param {string} src - The source for the script tag.
|
||||||
|
* @returns {Promise<unknown>}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_loadScriptTag(src) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const scriptTag = document.createElement('script');
|
||||||
|
|
||||||
|
scriptTag.async = true;
|
||||||
|
scriptTag.addEventListener('error', () => {
|
||||||
|
scriptTag.remove();
|
||||||
|
|
||||||
|
reject();
|
||||||
|
});
|
||||||
|
scriptTag.addEventListener('load', resolve);
|
||||||
|
scriptTag.type = 'text/javascript';
|
||||||
|
|
||||||
|
scriptTag.src = src;
|
||||||
|
|
||||||
|
document.head.appendChild(scriptTag);
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -114,29 +159,19 @@ const googleApi = {
|
||||||
return googleClientLoadPromise;
|
return googleClientLoadPromise;
|
||||||
}
|
}
|
||||||
|
|
||||||
googleClientLoadPromise = new Promise((resolve, reject) => {
|
googleClientLoadPromise = this._loadScriptTag(GOOGLE_API_CLIENT_LIBRARY_URL)
|
||||||
const scriptTag = document.createElement('script');
|
.catch(() => {
|
||||||
|
|
||||||
scriptTag.async = true;
|
|
||||||
scriptTag.addEventListener('error', () => {
|
|
||||||
scriptTag.remove();
|
|
||||||
|
|
||||||
googleClientLoadPromise = null;
|
googleClientLoadPromise = null;
|
||||||
|
|
||||||
reject();
|
|
||||||
});
|
|
||||||
scriptTag.addEventListener('load', resolve);
|
|
||||||
scriptTag.type = 'text/javascript';
|
|
||||||
|
|
||||||
scriptTag.src = GOOGLE_API_CLIENT_LIBRARY_URL;
|
|
||||||
|
|
||||||
document.head.appendChild(scriptTag);
|
|
||||||
})
|
})
|
||||||
.then(() => new Promise((resolve, reject) =>
|
.then(() => new Promise((resolve, reject) =>
|
||||||
this._getGoogleApiClient().load('client:auth2', {
|
this._getGoogleApiClient().load('client', {
|
||||||
callback: resolve,
|
callback: resolve,
|
||||||
onerror: reject
|
onerror: reject
|
||||||
})))
|
})))
|
||||||
|
.then(this._loadScriptTag(GOOGLE_GIS_LIBRARY_URL))
|
||||||
|
.catch(() => {
|
||||||
|
googleClientLoadPromise = null;
|
||||||
|
})
|
||||||
.then(() => this._getGoogleApiClient());
|
.then(() => this._getGoogleApiClient());
|
||||||
|
|
||||||
return googleClientLoadPromise;
|
return googleClientLoadPromise;
|
||||||
|
@ -171,25 +206,50 @@ const googleApi = {
|
||||||
* Prompts the participant to sign in to the Google API Client Library, even
|
* Prompts the participant to sign in to the Google API Client Library, even
|
||||||
* if already signed in.
|
* if already signed in.
|
||||||
*
|
*
|
||||||
|
* @param {boolean} consent - Whether to show account selection dialog.
|
||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
showAccountSelection() {
|
showAccountSelection(consent: boolean) {
|
||||||
return this.get()
|
return this.get()
|
||||||
.then(api => api.auth2.getAuthInstance().signIn());
|
.then(api => new Promise((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
// Settle this promise in the response callback for requestAccessToken()
|
||||||
|
this.tokenClient.callback = resp => {
|
||||||
|
if (resp.error !== undefined) {
|
||||||
|
reject(resp);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the number of seconds the token is valid for, subtract 5 minutes
|
||||||
|
// to account for differences in clock settings and convert to ms.
|
||||||
|
const expiresIn = (parseInt(api.client.getToken().expires_in, 10) - 300) * 1000;
|
||||||
|
const now = new Date();
|
||||||
|
const expireDate = new Date(now.getTime() + expiresIn);
|
||||||
|
|
||||||
|
this.tokenExpires = expireDate.getTime().toString();
|
||||||
|
|
||||||
|
resolve(resp);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.tokenClient.requestAccessToken({ prompt: consent ? 'consent' : '' });
|
||||||
|
} catch (err) {
|
||||||
|
logger.error('Error requesting token', err);
|
||||||
|
}
|
||||||
|
}));
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prompts the participant to sign in to the Google API Client Library, if
|
* Prompts the participant to sign in to the Google API Client Library, if
|
||||||
* not already signed in.
|
* not already signed in.
|
||||||
*
|
*
|
||||||
|
* @param {boolean} consent - Whether to show account selection dialog.
|
||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
signInIfNotSignedIn() {
|
signInIfNotSignedIn(consent: boolean) {
|
||||||
return this.get()
|
return this.get()
|
||||||
.then(() => this.isSignedIn())
|
.then(() => this.isSignedIn())
|
||||||
.then(isSignedIn => {
|
.then(isSignedIn => {
|
||||||
if (!isSignedIn) {
|
if (!isSignedIn) {
|
||||||
return this.showAccountSelection();
|
return this.showAccountSelection(consent);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -201,11 +261,10 @@ const googleApi = {
|
||||||
*/
|
*/
|
||||||
signOut() {
|
signOut() {
|
||||||
return this.get()
|
return this.get()
|
||||||
.then(api =>
|
.then(() => {
|
||||||
api.auth2
|
this.tokenClient = undefined;
|
||||||
&& api.auth2.getAuthInstance
|
this.tokenExpires = undefined;
|
||||||
&& api.auth2.getAuthInstance()
|
});
|
||||||
&& api.auth2.getAuthInstance().signOut());
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -387,6 +446,17 @@ const googleApi = {
|
||||||
*/
|
*/
|
||||||
_getGoogleApiClient() {
|
_getGoogleApiClient() {
|
||||||
return window.gapi;
|
return window.gapi;
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the global Google Identity Services Library object.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @returns {Object|undefined}
|
||||||
|
*/
|
||||||
|
_getGoogleGISApiClient() {
|
||||||
|
return window.google;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
import { getLogger } from '../base/logging/functions';
|
||||||
|
|
||||||
|
export default getLogger('features/base/redux');
|
Loading…
Reference in New Issue