Cannot read property 'init' of undefined gapi.client - javascript

I am trying to integrate Google Calendar API with Js inside an application. I tried to follow Google Quickstart. If I paste the example code in an HTML doc, change the clientID and the API key, I get the error attached when I open the file in Chrome.
Also, with console.log(gapi.client) I get back null.
The first block of code is where gapi functions are called (calendar_model.js).
The second block is where I link the api script to the document (I need to do that by js, in a file called dependency.js).
The third block is my index.js, from where I call the function in dependency.js.
console.log("first");
// Client ID and API key from the Developer Console
var CLIENT_ID = 'xxxxxxxxxxxxxxxxx.apps.googleusercontent.com';
var API_KEY = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxx';
// Array of API discovery doc URLs for APIs used by the quickstart
var DISCOVERY_DOCS = ["https://www.googleapis.com/discovery/v1/apis/calendar/v3/rest"];
// Authorization scopes required by the API; multiple scopes can be
// included, separated by spaces.
var SCOPES = "https://www.googleapis.com/auth/calendar.readonly";
var authorizeButton;
var signoutButton;
/**
* On load, called to load the auth2 library and API client library.
*/
function handleClientLoad() {
console.log("gapi type: " + typeof gapi + '\n');
console.log("gapi client type: " + typeof gapi.client + '\n');
gapi.load('client:auth2', initClient);
}
/**
* Initializes the API client library and sets up sign-in state
* listeners.
*/
function initClient()
{
console.log("initClient callback start");
gapi.client.init({
apiKey: API_KEY,
clientId: CLIENT_ID,
discoveryDocs: DISCOVERY_DOCS,
scope: SCOPES
}).then(function () {
// Listen for sign-in state changes.
gapi.auth2.getAuthInstance().isSignedIn.listen(updateSigninStatus);
console.log(gapi);
// Handle the initial sign-in state.
updateSigninStatus(gapi.auth2.getAuthInstance().isSignedIn.get());
authorizeButton.onclick = handleAuthClick;
signoutButton.onclick = handleSignoutClick;
}, function(error) {
appendPre(JSON.stringify(error, null, 2));
});
}
window.testMicroAppDependency = {
calendarInit: function(){
var calendar_root = document.getElementById("gcalendar-root");
//create authorize button
var x = document.createElement("button");
x.setAttribute("id", "authorize_button");
//x.style = "display: none";
calendar_root.appendChild(x);
//create signout button
x = document.createElement("button");
x.setAttribute("id", "signout_button");
//x.style = "display: none";
calendar_root.appendChild(x);
authorizeButton = document.getElementById('authorize_button');
signoutButton = document.getElementById('signout_button');
x = document.createElement("script");
x.src = "https://apis.google.com/js/api.js";
x.async = true;
x.defer = true;
x.setAttribute("onload", "handleClientLoad()");
x.setAttribute("onreadystatechange", "if (this.readyState === 'complete') this.onload()");
calendar_root.appendChild(x);
}
}
registry.add("microapp", "googlecalendar", {
init: function (parentNode, options) {
this._waitForDependency(function (error, dependency) {
if (error) {
console.error("Unable to start Google Calendar MicroApp: " + error);
} else {
var content = document.createElement("div");
content.setAttribute("id", "gcalendar-root");
parentNode.appendChild(content);
dependency.calendarInit();
}
});
},
_waitForDependency: function(callback) {
var timeout = 5000;
var startTime = new Date().getTime();
var interval = setInterval(function() {
if (window.hasOwnProperty("testMicroAppDependency")) {
clearInterval(interval);
callback(null, window.testMicroAppDependency);
}
if (new Date().getTime() - startTime > timeout) {
clearInterval(interval);
callback("Loading of HelloWorld MicroApp scripts timed out.", null);
}
console.log("... (waiting for testMicroAppDependency)");
}, 100);
}
});
Here is the console where I log the type of gapi and of gapi.client. gapi is an object, so the library is loaded, but gapi.client is undefined.
I am pretty a newbie in web dev, maybe it's a silly issue or I didn't comprehend something about Google API, but I can't get resolve it.
Any help is appreciated :)

The main issue you are having is you are calling initClient function as an explicit function here:
gapi.load('client:auth2', initClient())
you have to pass it as an implicit function because gapi.load will use it as a callback (I recommend you to check more about them). Therefore, it must be passed like this:
gapi.load('client:auth2', initClient)
Notice the subtle, but really important difference between the parenthesis in initClient() and initClient.
Also, you have these variables in the air without assigning them values for what I could see:
var authorizeButton;
var signoutButton;
They should be assigned the button elements to eventually handle them in your code:
var authorizeButton = document.getElementById('authorize_button');
var signoutButton = document.getElementById('signout_button');

Related

Multiple user Auth Simultaneously for Gmail API

I want to show the total email count in my multiple accounts on a single page. Currently I auth one by one for all email account and then store the email count in array.
But, I need to refresh the count after 10s and I can't auth every 10s. So is there any method where I can keep multiple Gmail accounts logged in and fetch email count periodically.
This Method gets email count:
function listLabels() {
gapi.client.gmail.users.labels.get({
'userId': 'me',
'id': 'INBOX'
}).then(function(response) {
ct.push(response.result.messagesTotal)
});
}
This is used for auth:
var ct = [];
// Client ID and API key from the Developer Console
var CLIENT_ID = '<CLIENT ID>';
// Array of API discovery doc URLs for APIs used by the quickstart
var DISCOVERY_DOCS = ["https://www.googleapis.com/discovery/v1/apis/gmail/v1/rest"];
// Authorization scopes required by the API; multiple scopes can be
// included, separated by spaces.
var SCOPES = 'https://www.googleapis.com/auth/gmail.readonly';
var authorizeButton = document.getElementById('authorize_button');
var signoutButton = document.getElementById('signout_button');
/**
* On load, called to load the auth2 library and API client library.
*/
function handleClientLoad() {
gapi.load('client:auth2', initClient);
}
/**
* Initializes the API client library and sets up sign-in state
* listeners.
*/
function initClient() {
gapi.client.init({
discoveryDocs: DISCOVERY_DOCS,
clientId: CLIENT_ID,
scope: SCOPES
}).then(function() {
// Listen for sign-in state changes.
gapi.auth2.getAuthInstance().isSignedIn.listen(updateSigninStatus);
// Handle the initial sign-in state.
updateSigninStatus(gapi.auth2.getAuthInstance().isSignedIn.get());
authorizeButton.onclick = handleAuthClick;
signoutButton.onclick = handleSignoutClick;
});
}
/**
* Called when the signed in status changes, to update the UI
* appropriately. After a sign-in, the API is called.
*/
function updateSigninStatus(isSignedIn) {
if (isSignedIn) {
authorizeButton.style.display = 'none';
signoutButton.style.display = 'block';
listLabels();
} else {
authorizeButton.style.display = 'block';
signoutButton.style.display = 'none';
}
}
/**
* Sign in the user upon button click.
*/
function handleAuthClick(event) {
gapi.auth2.getAuthInstance().signIn();
}
/**
* Sign out the user upon button click.
*/
function handleSignoutClick(event) {
gapi.auth2.getAuthInstance().signOut();
}

Using google calender inside greasemonkey script

I am trying to use the google calender api inside a greasemonkey script. This is my code inspired from the google calender api tutorial:
// ==UserScript==
// #name Google Calender
// #namespace xyz
// #include *
// #version 1
// ==/UserScript==
var $ = unsafeWindow.jQuery;
API_js_callback = "https://apis.google.com/js/api.js";
var script = document.createElement('script');
script.src = API_js_callback;
var head = document.getElementsByTagName("head")[0];
(head || document.body).appendChild(script);
//this is not working
var gapi = unsafeWindow.gapi;
$(document).ready(function()
{
// Client ID and API key from the Developer Console
var CLIENT_ID = 'XXX';
// Array of API discovery doc URLs for APIs used by the quickstart
var DISCOVERY_DOCS = ["https://www.googleapis.com/discovery/v1/apis/calendar/v3/rest"];
// Authorization scopes required by the API; multiple scopes can be
// included, separated by spaces.
var SCOPES = "https://www.googleapis.com/auth/calendar.readonly";
handleClientLoad();
/**
* On load, called to load the auth2 library and API client library.
*/
function handleClientLoad() {
gapi.load('client:auth2', initClient);
}
/**
* Initializes the API client library and sets up sign-in state
* listeners.
*/
function initClient() {
.........
}
});
Inside the handleClientLoad method, I got the following exception:
Cannot read property 'load' of undefined
Any idea?
The gapi library is injected after the document loads, therefore it hasn't loaded yet when the document is ready. The gapi-related code need to be run on script load, for example:
GAPI_js_src = "https://apis.google.com/js/api.js";
var script = document.createElement('script');
script.src = GAPI_js_src;
var head = document.getElementsByTagName("head")[0];
(head || document.body).appendChild(script);
script.onload = (function() {
var gapi = unsafeWindow.gapi;
// Client ID and API key from the Developer Console
var CLIENT_ID = 'XXX';
// Array of API discovery doc URLs for APIs used by the quickstart
var DISCOVERY_DOCS = ["https://www.googleapis.com/discovery/v1/apis/calendar/v3/rest"];
// Authorization scopes required by the API; multiple scopes can be
// included, separated by spaces.
var SCOPES = "https://www.googleapis.com/auth/calendar.readonly";
handleClientLoad();
/**
* On load, called to load the auth2 library and API client library.
*/
function handleClientLoad() {
gapi.load('client:auth2', initClient);
}
/**
* Initializes the API client library and sets up sign-in state
* listeners.
*/
function initClient() {
.........
}
});

DocumentApp causes App Script executed from Javascript client to 401

Currently attempting to execute a GAS from a Javascript client. The GAS script is as follows:
function findAndReplace() {
var doc = DocumentApp.openById('ID_OMITTED');
}
This executes fine by pressing Play in the GAS editor, however when I try to execute from my Javascript client, I click authorise.
Then I receive a 401.
Error calling API: {
"error": {
"code": 401,
"message": "ScriptError",
"status": "UNAUTHENTICATED",
"details": [{
"#type": "type.googleapis.com/google.apps.script.v1.ExecutionError",
"errorMessage": "Authorization is required to perform that action.",
"errorType": "ScriptError"
}]
}
}
And now when I press Play in the GAS editor, I get a Action not allowed error. I can then create a new script by copying it, press Play and it works fine. Executing the from the Javascript causes the whole the 401 and then the whole cycle repeats.
However if I execute a simple:
function test() {
return 'hello';
}
It works, and returns
Folders under your root folder:
h (0)
e (1)
l (2)
l (3)
o (4)
(The output looks like this because the program expects an array, as you can see below).
This is the Javascript client, basically copy and paste of the Javascript quickstart.
var CLIENT_ID = 'CLIENT_ID_OMITTED';
var SCOPES = ['https://www.googleapis.com/auth/drive'];
function checkAuth() {
gapi.auth.authorize(
{
'client_id': CLIENT_ID,
'scope': SCOPES.join(' '),
'immediate': true
}, handleAuthResult
);
}
function handleAuthResult(authResult) {
var authorizeDiv = document.getElementById('authorize-div');
if (authResult && !authResult.error) {
authorizeDiv.style.display = 'none';
callScriptFunction();
} else {
authorizeDiv.style.display = 'inline';
}
}
function handleAuthClick(event) {
gapi.auth.authorize(
{client_id: CLIENT_ID, scope: SCOPES, immediate: false},
handleAuthResult
);
return false;
}
function callScriptFunction() {
var scriptId = "SCRIPT_ID_OMITTED";
var request = {
'function': 'findAndReplace'
};
var op = gapi.client.request({
'root': 'https://script.googleapis.com',
'path': 'v1/scripts/' + scriptId + ':run',
'method': 'POST',
'body': request
});
op.execute(function(resp) {
if (resp.error && resp.error.status) {
appendPre('Error calling API:');
appendPre(JSON.stringify(resp, null, 2));
} else if (resp.error) {
var error = resp.error.details[0];
appendPre('Script error message: ' + error.errorMessage);
if (error.scriptStackTraceElements) {
appendPre('Script error stacktrace:');
for (var i = 0; i < error.scriptStackTraceElements.length; i++) {
var trace = error.scriptStackTraceElements[i];
appendPre('\t' + trace.function + ':' + trace.lineNumber);
}
}
} else {
var folderSet = resp.response.result;
if (Object.keys(folderSet).length === 0) {
appendPre('No folders returned!');
} else {
appendPre('Folders under your root folder:');
Object.keys(folderSet).forEach(function(id){
appendPre('\t' + folderSet[id] + ' (' + id + ')');
});
}
}
});
}
function appendPre(message) {
var pre = document.getElementById('output');
var textContent = document.createTextNode(message + '\n');
pre.appendChild(textContent);
}
I made sure to create the permissions for DocumentApp, by copying it. Which when the copy is ran it gives me this prompt:
Which I obviously accept.
I create a new project on the script each time, enabling the Scripts API
and then creating an OAuth2 client,
copying the client id into the script. I also Deploy as API Executable on the script, and take the script id.
What am I missing? Why is the code producing a 401? And why is the GAS script then mysteriously returning Action now allowed in the editor only to work once its copied?
Thanks in advance.
After trying to do this for a couple nights I solved it straight after posted to SO... typical!
This post solved it
Basically, go to File > Project Properties and click the Scopes tab. From here you can see all the scopes required by your project.
This then must be added to the scopes array in your client-side javascript.
So my scopes now looks like this:
var SCOPES = ['https://www.googleapis.com/auth/drive', 'https://www.googleapis.com/auth/documents'];
Why the GAS script then decide to return Action not allowed I'll never know, it really threw me off the trail.
Hopefully this helps some body else.

Firefox add-on get the DOM window which made an HTTP request

I'm capturing the HTTP requests in a Firefox Add-on SDK extension. I need to get the DOM window associated with the request. However, I'm getting an NS_NOINTERFACE error.
Here is my code:
var httpRequestObserver = {
observe: function (subject, topic, data) {
var httpRequest = subject.QueryInterface(Ci.nsIHttpChannel);
var requestUrl = subject.URI.host;
var domWin;
var assWindow;
console.log('URL: ', requestUrl);
try {
domWin = httpRequest.notificationCallbacks.getInterface(Ci.nsIDOMWindow);
assWindow = httpChannel.notificationCallbacks.getInterface(Ci.nsILoadContext)
.associatedWindow;
console.log(domWin);
} catch (e) {
console.log(e);
}
// console.log('TAB: ', tabsLib.getTabForWindow(domWin.top));
var hostName = wn.domWindow.getBrowser().selectedBrowser.contentWindow.location.host;
console.log('HOST: ', hostName);
},
get observerService() {
return Cc['#mozilla.org/observer-service;1'].getService(Ci.nsIObserverService);
},
register: function () {
this.observerService.addObserver(this, 'http-on-modify-request', false);
},
unregister: function () {
this.observerService.removeObserver(this, 'http-on-modify-request');
}
};
httpRequestObserver.register();
I've tried both nsIDOMWindow and nsILoadContext, but NS_NOINTERFACE error always appears on an attempt to get the window object.
I have finally managed to get the data I need via
httpRequest.notificationCallbacks.getInterface(Ci.nsILoadContext).topFrameElement
For example, to get url of the document which started the request, I used
httpRequest.notificationCallbacks.getInterface(Ci.nsILoadContext).topFrameElement._documentURI.href
Since you already found how to get the <browser> from the request you can do the following to get back to SDK APIs:
let browser = ....topFrameElement
let xulTab = browser.ownerDocument.defaultView.gBrowser.getTabForBrowser(browser)
let sdkTab = modelFor(xulTab)
modelFor() is documented in the tabs module.

IndexedDB open DB request weird behavior

I have an app (questionnaire) that uses indexedDB.
We have one database and several stores in it.
Stores have data already stored in them.
At some point a dashboard html file is loaded. In this file I am calling couple of functions:
function init(){
adjustUsedScreenHeight();
db_init();
setInstitutionInstRow();
loadRecommendations();
loadResultsFromDB();
fillEvaluations();
document.addEventListener("deviceready", onDeviceReady, function(e) {console.log(e);});
}
The init() function is called on body onLoad.
setInstitutionInstRow() looks like these:
function setInstitutionInstRow(localId){
//localId = 10;
if (localId == undefined){
console.log("Localid underfined: ");
//open db, open objectstore;
var request = indexedDB.open("kcapp_db", "1.0");
request.onsuccess = function() {
var db = request.result;
var tx = db.transaction ("LOCALINSTITUTIONS", "readonly");
var store = tx.objectStore("LOCALINSTITUTIONS");
tx.oncomplete = function(){
db.close();
}
tx.onerror = function(){
console.log("Transaction error on setInstInstRow");
}
var cursor = store.openCursor();
cursor.onsuccess= function () {
var match = cursor.result;
console.log ("Retrieved item: " + match.value.instid);
// alert("Added new data");
if (match){
setInstituionInstRow(match.value.instid);
console.log("Got localid: " + math.value.instid);
}
else
console.log("localinsid: it is empty " );
};
cursor.onerror = function () {
console.log("Error: " + item.result.errorCode);
}
}
request.onerror = function () {
console.log("Error: " + request.result.errorCode );
}
request.oncomplete = function (){
console.log("The transaction is done: setInstitutionRow()");
}
request.onupgradeneeded = function (){
console.log("Upgrade needed ...");
}
request.onblocked = function(){
console.log("DB is Blocked ...");
}
} else {
instid = localId;
var now = new Date();
//console.log("["+now.getTime()+"]setInstituionInstRow - instid set to "+localId);
//open db, open objectstore;
var request = indexedDB.open("kcapp_db", "1.0");
request.onsuccess = function() {
var db = this.result;
var tx = db.transaction ("INSTITUTIONS", "readonly");
var store = tx.objectStore("INSTITUTIONS");
var item = store.get(localId);
console.log(item);
item.onsuccess= function () {
console.log ("Retrieved item: ");
if (item.length > 0)
var lInstitution = item.result.value;
kitaDisplayValue = lInstitution.krippe;
};
item.onerror = function () {
console.log("Error: " + item.result.errorCode);
}
}
request.onerror = function () {
console.log("Error: " + request.result.errorCode );
}
}
Now the problem is,
var request = indexedDB.open("kcapp_db", "1.0");
the above request is never getting into any onsuccess, oncomplete, onerror states. I debugged with Chrome tools, it never getting into any above states.
Accordingly I am not getting any data from transactions.
And there are no errors in Chrome console.
And here is the request value from Chrome dev:
From above image the readyState: done , which means it should fire an event (success, error, blocked etc). But it is not going into any of them.
I am looking into it, and still can not figure out why it is not working.
Have to mention that the other functions from init() is behaving the same way.
Looking forward to get some help.
You may be using an invalid version parameter to the open function. Try indexedDB.open('kcapp_db', 1); instead.
Like Josh said, your version parameter should be an integer, not a string.
Your request object can get 4 events in response to the open request: success, error, upgradeneeded, or blocked. Add event listeners for all of those (e.g. request.onblocked = ...) and see which one is getting fired.
I had that problem but only with the "onupgradeneeded" event. I fixed it changing the name of the "open" function. At the begining I had a very long name; I changed it for a short one and start working. I don't know if this is the real problem but it was solved at that moment.
My code:
if (this.isSupported) {
this.openRequest = indexedDB.open("OrdenesMant", 1);
/**
* Creación de la base de datos con tablas y claves primarias
*/
this.openRequest.onupgradeneeded = function(oEvent) {
...
Hope it works for you as well.

Categories

Resources