Reputation: 72
Ran into another road block today, on my path of writing a desktop chrome app for logging users project time.
What i am trying to do (and failing) is use the Apps Script API to access a google sheet that retains the information (project numbers) that i want to populate a drop down in my Chrome App UI.
Update: I have reworded this as to get to the point and be a little clear on what my issue is.
What i cant seem to achieve is calling the Apps script function from my chrome app. I have read this Execution API but still cant seem to make the coloration. for some reasons i keep getting the "Uncaught ReferenceError: gapi is not defined" in my console.
What i have managed to do is have both the Apps Script and the Chrome App under the same project name in the Developers Console. not sure if its needed but thought it might help with only 1 Oauth2 request.
Is there something my thick head is missing?
Any help or ideas would be much appreciated.
This is my manifest.json
{
"manifest_version": 2,
"name": "TimeSheet",
"description": "Small and easy desktop app for entering time spent on project files",
"version": "0.1.0",
"icons": {
"128": "icon_128.png"
},
"app": {
"background": {
"scripts": ["background.js"]
}
},
"permissions": [
"identity",
"app.window.alwaysOnTop"
],
"oauth2": {
"client_id": "clientid.apps.googleusercontent.com",
"scopes": [
"https://www.googleapis.com/auth/drive",
"https://www.googleapis.com/auth/spreadsheets"
]
},
"key": "very long string"
}
This is the bit of Oauth2 code running in my main.js
//This code confirms Oauth2 for access to google drive and related files
window.onload = function(){
document.querySelector("#Oauth2").addEventListener("click", function(){
chrome.identity.getAuthToken({"interactive": true}, function(token){
console.log(token);
});
});
};
// ID of the script to call. Acquire this from the Apps Script editor,
// under Publish > Deploy as API executable.
var scriptId = "blah";
// Create execution request.
var request = {
'function': 'getProjectNumbers',
};
// Make the request.
var op = gapi.client.request({
'root': 'https://script.googleapis.com',
'path': 'v1/scripts/' + scriptId + ':run',
'method': 'POST',
'body': request
});
// Log the results of the request.
op.execute(function(resp) {
if (resp.error && resp.error.status) {
// The API encountered a problem before the script started executing.
console.log('Error calling API: ' + JSON.stringify(resp, null, 2));
} else if (resp.error) {
// The API executed, but the script returned an error.
var error = resp.error.details[0];
console.log('Script error! Message: ' + error.errorMessage);
if (error.scriptStackTraceElements) {
// There may not be a stacktrace if the script didn't start executing.
console.log('Script error stacktrace:');
for (var i = 0; i < error.scriptStackTraceElements.length; i++) {
var trace = error.scriptStackTraceElements[i];
console.log('\t' + trace.function + ':' + trace.lineNumber);
}
}
} else {
// Here, the function returns an array of strings.
var projectNumbers = resp.response.result;
console.log('Project numbers in spreadsheet:');
projectNumbers.forEach(function(name){
console.log(name);
});
}
});
And this is the apps script code:
var projectDatabaseKey = 'blah'; //Project Database Sheet spreadsheet key
var pprojectDatabaseSheet = 'Project Database'; //Project Database Sheet spreadsheet sheet
//Function to revieve data of project numbers for drop down list
function getProjectNumbers() {
return SpreadsheetApp
.openById(projectDatabaseKey).getSheetByName(projectDatabaseSheet)
.getRange("A2:A" + (SpreadsheetApp.openById(projectDatabaseKey).getSheetByName(projectDatabaseSheet).getLastRow()))
.getValues();
}
I am just really unsure how to use the Oauth2 token and how to apply it to the apps script.
UPDATE
Ok i have tried to call an apps script in a different manor, What i am trying today is using the gapi-chrome-apps.js library to do the oauth2 work.
Now my problem is i get this error, that could be a range of things i am guessing:
POST https://www.googleapis.com/v1/scripts/blahblah:run 404 ()
gapi.client.request @ VM80 gapi-chrome-apps.js:105
getSheetsList @ gapiCallback.js:17
(anonymous function) @ gapiCallback.js:49
callbackWrapper @ VM80 gapi-chrome-apps.js:68
target.(anonymous function) @ extensions::SafeBuiltins:19
safeCallbackApply @ extensions::sendRequest:21
handleResponse @ extensions::sendRequest:72
And this Error, that comes from the gapi-chrome-apps.js script:
Uncaught SyntaxError: Unexpected token N in JSON at position 0
Really not sure what is causing this, here is my updated code:
//get listof sheets in spreadsheet
function getSheetsList(){
var scriptId = "blahblah";
// Initialize parameters for function call.
var sheetId = "blahblah";
// Create execution request.
var requests = {
'function': 'getSheetNames',
'parameters': [sheetId],
'devMode': true // Optional.
};
// Make the request.
gapi.client.request({
'root': 'https://script.googleapis.com',
'path': 'v1/scripts/' + scriptId + ':run',
'method': 'POST',
'body': requests,
'callback': printSheetsList
});
}
// Log the results of the request.
function printSheetsList(resp) {
if (resp.error && resp.error.status) {
// The API encountered a problem before the script started executing.
console.log('Error calling API: ' + JSON.stringify(resp, null, 2));
} else if (resp.error) {
// The API executed, but the script returned an error.
var error = resp.error.details[0];
console.log('Script error! Message: ' + error.errorMessage);
} else {
// Here, the function returns an array of strings.
var sheetNames = resp.response.result;
console.log('Sheet names in spreadsheet:');
sheetNames.forEach(function(name){
console.log(name);
});
}
}
//Prompts the user for authorization and then proceeds to
function authorize(params, callback) {
gapi.auth.authorize(params, function(accessToken) {
if (!accessToken) {
console.log("Error getting authorization");
} else {
callback();
}
});
}
function gapiIsLoaded() {
var params = { 'immediate': true };
if (!(chrome && chrome.app && chrome.app.runtime)) {
params.scope = "https://www.googleapis.com/auth/drive";
params.client_id = "blahblah";
gapi.auth.init(authorize.bind(null, params, getSheetsList));
} else {
authorize(params, getSheetsList);
}
}
Upvotes: 3
Views: 1852
Reputation: 77523
Using traditional GAPI will not work, since it dynamically loads more external scripts and Apps are not allowed to do that.
One possible solution is to run GAPI code in a sandboxed page, which can overcome the remote code restriction. This, however, is cumbersome as you'll need to pass data back and forth using postMessage
.
Another way is to try and use Google-provided library gapi-chrome-apps.js, that works in Chrome apps (and uses chrome.identity
to manage OAuth) - but please note this comment:
This library is likely not suitable for use without additional modifications.
Upvotes: 2
Reputation: 759
According to your post, you are simply not defining gapi
. You can load it like this
jQuery.getScript( "https://apis.google.com/js/api.js", onApiLoad );
where onApiLoad
is the function you would like to call when the gapi
is loaded.
For your code, I would wrap the following code in a function like this:
function onApiLoad() {
// Make the request.
var op = gapi.client.request({
'root': 'https://script.googleapis.com',
'path': 'v1/scripts/' + scriptId + ':run',
'method': 'POST',
'body': request
});
// Log the results of the request.
op.execute(function(resp) {
if (resp.error && resp.error.status) {
// The API encountered a problem before the script started executing.
console.log('Error calling API: ' + JSON.stringify(resp, null, 2));
} else if (resp.error) {
// The API executed, but the script returned an error.
var error = resp.error.details[0];
console.log('Script error! Message: ' + error.errorMessage);
if (error.scriptStackTraceElements) {
// There may not be a stacktrace if the script didn't start executing.
console.log('Script error stacktrace:');
for (var i = 0; i < error.scriptStackTraceElements.length; i++) {
var trace = error.scriptStackTraceElements[i];
console.log('\t' + trace.function + ':' + trace.lineNumber);
}
}
} else {
// Here, the function returns an array of strings.
var projectNumbers = resp.response.result;
console.log('Project numbers in spreadsheet:');
projectNumbers.forEach(function(name){
console.log(name);
});
}
});
}
Thanks to the second answer in this post, the below code is a pure JS implementation of $.getScript(). It includes a callback, so the code snippet below should work, assuming you wrap your code in a function as described above.
function getScript(source, callback) {
var script = document.createElement('script');
var prior = document.getElementsByTagName('script')[0];
script.async = 1;
prior.parentNode.insertBefore(script, prior);
script.onload = script.onreadystatechange = function( _, isAbort ) {
if(isAbort || !script.readyState || /loaded|complete/.test(script.readyState) ) {
script.onload = script.onreadystatechange = null;
script = undefined;
if(!isAbort) { if(callback) callback(); }
}
};
script.src = source;
}
getScript("https://apis.google.com/js/api.js", onApiLoad);
Upvotes: 0