I use OAuth library from Google in order to set connection with Spotify. There is a problem. When createService() and authCallback() is part of auth object, raised error:
Couldn't find script function:
authCallback()
Why is the callback function not visible inside the auth object?
The code for this case:
function doGet() {
if (auth.hasAccess()) {
main();
} else {
return auth.createFlow();
}
}
const auth = (function () {
const CLIENT_ID = '...';
const CLIENT_SECRET = '...';
const _service = createService();
function createService() {
return OAuth2.createService('spotify')
.setAuthorizationBaseUrl('https://accounts.spotify.com/authorize')
.setTokenUrl('https://accounts.spotify.com/api/token')
.setClientId(CLIENT_ID)
.setClientSecret(CLIENT_SECRET)
.setCallbackFunction('authCallback') // set callback
.setPropertyStore(PropertiesService.getUserProperties())
.setScope('playlist-read-private playlist-modify-private playlist-modify-public user-library-read')
.setParam('response_type', 'code')
.setParam('redirect_uri', getRedirectUri());
}
function authCallback(request) {
let isAuthorized = _service.handleCallback(request);
if (isAuthorized) {
return HtmlService.createHtmlOutput('Success! You can close this tab.');
} else {
return HtmlService.createHtmlOutput('Denied. You can close this tab');
}
}
...
return {
hasAccess: hasAccess,
getAccessToken: getAccessToken,
createFlow: createFlow,
};
})();
But if do this without auth object, no error and success callback:
function createService() {
return OAuth2.createService('spotify')
.setCallbackFunction('authCallback')
// ...
}
function authCallback(request) {
// ...
}
I can do this, but then it makes no sense to hide implementation details in the auth object:
const auth = (function () {
function createService() {
return OAuth2.createService('spotify')
.setCallbackFunction('authCallback')
// ...
}
function authCallback(request) {
// ...
}
return {
// ...
authCallback: authCallback,
};
})();
function authCallback(request) {
return auth.authCallback(request);
}
function doGet() {
if (auth.hasAccess()) {
main();
} else {
return auth.createFlow();
}
}
Full code with error
function doGet() {
if (auth.hasAccess()) {
main();
} else {
return auth.createFlow();
}
}
const auth = (function () {
const CLIENT_ID = '...';
const CLIENT_SECRET = '...';
const _service = createService();
function createService() {
return OAuth2.createService('spotify')
.setAuthorizationBaseUrl('https://accounts.spotify.com/authorize')
.setTokenUrl('https://accounts.spotify.com/api/token')
.setClientId(CLIENT_ID)
.setClientSecret(CLIENT_SECRET)
.setCallbackFunction('authCallback')
.setPropertyStore(PropertiesService.getUserProperties())
.setScope('playlist-read-private playlist-modify-private playlist-modify-public user-library-read')
.setParam('response_type', 'code')
.setParam('redirect_uri', getRedirectUri());
}
function authCallback(request) {
let isAuthorized = _service.handleCallback(request);
if (isAuthorized) {
return HtmlService.createHtmlOutput('Success! You can close this tab.');
} else {
return HtmlService.createHtmlOutput('Denied. You can close this tab');
}
}
function getRedirectUri() {
let scriptId = encodeURIComponent(ScriptApp.getScriptId());
let template = 'https://script.google.com/macros/d/%s/usercallback';
return Utilities.formatString(template, scriptId);
}
function hasAccess() {
return _service.hasAccess();
}
function getAccessToken() {
return _service.getAccessToken();
}
function createFlow() {
let template = '<a href="%s" target="_blank">Authorize</a>';
let html = Utilities.formatString(template, _service.getAuthorizationUrl());
return HtmlService.createHtmlOutput(html);
}
return {
hasAccess: hasAccess,
getAccessToken: getAccessToken,
createFlow: createFlow,
};
})();
authCallbackin yourIIFE, how would the caller be able to invoke it?authCallbackinside an object instead of doing this at the global scope?