Access Not Configured. The API (Drive API) is not enabled for your project. Please use the Google Developers Console to update your configuration - api

I got a bit of a problem.
i created a googlesheet and included a script, like this, to send it as a pdf to my email address:
function onOpen() {
var ui = SpreadsheetApp.getUi();
// Or DocumentApp or FormApp.
ui.createMenu('Custom Menu')
.addItem('Send summary', 'menuItem1')
.addSeparator()
.addItem('Send summary&Week', 'menuItem2')
.addToUi();
}
function menuItem1() {
var source = SpreadsheetApp.getActiveSpreadsheet();
var subject = source.getSheets()[9].getRange('G1').getValue(); //cell for subject in sheet
var body = source.getSheets()[9].getRange('F1').getValue();
var sheetNumSummary = 9; // first sheet(tab) is zero, second sheet is 1, etc..
var source = SpreadsheetApp.getActiveSpreadsheet();
var thema = source.getSheets()[9].getRange('f3').getValue(); //
var mailTo = ('youremailaddress#here.com'); //source.getSheets() [0].getRange('D1').getValue(); // 'D1' cell which consists an emailaddress.
var name = source.getSheets()[9].getRange('G1').getValue(); // Name of Attachement
var sheets = source.getSheets();
sheets.forEach(function (s, i) {
if (i !== sheetNumSummary) s.hideSheet();
});
var url = Drive.Files.get(source.getId())
.exportLinks['application/pdf'];
url = url + '&size=letter' + //paper size
'&portrait=false' + //orientation, false for landscape
'&fitw=true' + //fit to width, false for actual size
'&sheetnames=false&printtitle=false&pagenumbers=false' + //hide optional
'&gridlines=false' + //false = hide gridlines
'&fzr=false'; //do not repeat row headers (frozen rows) on each page
var token = ScriptApp.getOAuthToken();
var response = UrlFetchApp.fetch(url, {
headers: {
'Authorization': 'Bearer ' + token
}
now comes the weird thing. I'm able to use it with the Google sheet i used it with, but if i do a copy of it and use the script from the menue i get the message:
Access Not Configured. The API (Drive API) is not enabled for your project. Please use the Google Developers Console to update your configuration.
I checked everything in the admin and developer console, but couldn't find anything. I also activate:
Two-legged OAuth access control Allow access to all APIs
The key and secret above are able to access any user's data for all Google Data APIs. Learn more
If i leave the copy of the 1st sheet and deactivate the API access, the 1st sheet (the origin) doesn't work anymore with the script and i get the above alert message. I have to delete the copy, reactivate the API access to make it work again. So, somehow i guess, the copy is working with the a script that only allows to work with one Project Sheet and not with the copy of that sheet. Is there somehow a way to make it work? Or, how can i add/attach the script to the copy. I used this script:
function getSpreadsheetKey() {
return SpreadsheetApp.getActiveSpreadsheet().getId();
};
and saw that it gives me the google key from the original sheet. that might be the reason why i cant use the script with the copy of the google sheet. But i still don't know how wire it correctly.

This message [(403) Access Not Configured] means that when you set up your Google Drive Access, you missed out "Activate the Drive API". You did not configure the Drive API to enabled in your Google account.
To Activate the Drive API, go to Developers Console and Enable the Google Drive API.
Regarding 'Two -legged OAuth access control' it is not necessary to activate. As stated in the document: to bypass any access control checks in the Google Data API when using the domain key.
You have to Set up API access credentials [OAuth].
Here's a link to enable Drive API: https://developers.google.com/drive/v3/web/enable-sdk
For Two-legged OAuth access control document, here's the link: https://support.google.com/a/answer/162105?hl=en

I just had this problem and it solved it self automatically.
It looks like there is some delay in allowing the API to being used (after it being enabled)
For reference I had enabled the API and setup the two stage OAuth.
Basically what happened was that I had a unit test to validate the access and:
the test failed
googled the error code
saw this SO thread
spent some time trying to figure out what was wrong
run the test again (and it passed)

Related

Requesting a BigQuery API from Google Spreadsheet generates an Error

I am trying to fetch data from Bigquery and show them into my spreadsheet using the App script. First, I created a Spreadsheet file in my G-drive and then put my code into the script editor.
Here is the code I have used to get all the datasets from Bigquery:
function getAllDataSets(filter){
try{
let req_for_datasets = BigQuery.Datasets.list(PROJECT_ID);
let datasets = req_for_datasets.datasets;
let list = [];
datasets.forEach( obj => {
if(obj.datasetReference.datasetId.indexOf(filter)>-1)
list.push(obj.datasetReference.datasetId);
});
return list;
}catch(e){
return [];
}
}
this script works fine and I can see the result while running my script through Code Editor. I try to use this script in onOpen() or onEdit() to be able to get data when the spreadsheet gets opened.
but using the spreadsheet I receive this message:
GoogleJsonResponseException: API call to bigquery.tables.list failed with error: Request is missing required authentication credential. Expected OAuth 2 access token, login cookie or other valid authentication credential.
this is the code I have put in onOpen function:
function onOpen(){
let ui = SpreadsheetApp.getUi();
let method_list = ["SUM", "AVG", "COUNT"];
//Adding a Custom menu
ui.createMenu("Media Budget")
.addItem("Facebook", "makeQuery")
.addSeparator()
.addItem("Google Ads", "makeQuery")
.addToUi();
//Getting all datasets from the specified project
let dataset_list = getAllDataSets("dw_sandbox_");
Browser.msgBox(dataset_list);
//Creating dropdown list cell
let cell = SHEET.getRange("B1");
applyValidationToCell(dataset_list, cell);
}
other than that if I try to execute the function using the custom menu in the spreadsheet everything works fine as well.
It would be really appreciated if you can help me.
If you can perform a request when running code manualy, but not on simple onOpen or onEdit trigger - the reason is authorization issues
See restrictions:
Because simple triggers fire automatically, without asking the user
for authorization, they are subject to several restrictions:
...
They cannot access services that require authorization. For example, a
simple trigger cannot send an email because the Gmail service requires
authorization, but a simple trigger can translate a phrase with the
Language service, which is anonymous.
Workaround
Option a)
Rather than running the request onOpen, implement it into a separate function that will be called when chosen from custom menu
Sample:
function onOpen(){
let ui = SpreadsheetApp.getUi();
let method_list = ["SUM", "AVG", "COUNT"];
//Adding a Custom menu
ui.createMenu("Media Budget")
.addItem("Facebook", "makeQuery")
.addSeparator()
.addItem("Google Ads", "makeQuery")
.addSeparator()
.addItem("bigQuery", "makeBigQuery")
.addToUi();
}
function makeBigQuery(){
//Getting all datasets from the specified project
let dataset_list = getAllDataSets("dw_sandbox_");
Browser.msgBox(dataset_list);
//Creating dropdown list cell
let cell = SHEET.getRange("B1");
applyValidationToCell(dataset_list, cell);
}
Option b)
Run your exisitng code on installable instead on simple trigger.
Installable triggers can run funcitons that reuire auhtorization
To convert your simple trigger into an installable
rename the function from onOpen (otherisw you might run into conflicts from having both simple and installable triggers run simultaneously)
Go on Edit -> Current project's triggers - > New trigger - see also here

How to open popup with dropbox-api dropbox-js V2

I'm trying to migrate to dropbox-api v2 in my web application.
Currently I have implementation of opening popup window where user connects to his/her dropbox and I get token. Which I use to get access to files user selected in Dropbox.chooser in later steps.
But I'm having hard time to find the solution for this. I have link to all migration documents dropbox has, but there is not any word about what is the equivalent of client.authenticate() and Dropbox.AuthDriver.Popup() ?
Common Dropbox!!! I just found this issue posted in GitHub for dropbox-sdk-js, and the answer that they don't have this functionality in V2 :( it is really disappointing, i need to implement all staff myself:
https://github.com/dropbox/dropbox-sdk-js/issues/73#issuecomment-247382634
Updated
I have implemented my solution and would like to share if someone will need.
To open a popup window I use following code:
window.open(dropbox.getAuthenticationUrl("MY REDIRECT URL"), 'DropboxAuthPopup', 'dialog=yes,dependent=yes,scrollbars=yes,location=yes')
window.addEventListener('message',function(e) {
if (window.location.origin !== e.origin) {
// Throw error
} else {
// e.data Is what was sent from redirectUrl
// e.data.access_token is the token I needed from dropbox
}
},false);
Then on my page, which I specify dropbox to redirect, i put:
window.addEventListener('load', function() {
var message = parseQueryString(window.location.hash)
window.location.hash = '';
opener = window.opener
if (window.parent != window.top) {
opener = opener || window.parent
}
opener.postMessage(message, window.location.origin);
window.close();
})
Example of parseQueryString can be found from dropbox-sdk-js examples

Google Cloud Endpoints - Switch user / Require sign in again

When you authenticate with Google Cloud Endpoints if there is several Google Accounts signed in,that have already given your app permission, it simply chooses the default account, just like Gmail would do.
1) Gmail has the ability to switch Google Accounts in the top right corner. How would I achieve something similar?
2) Can you require a user to sign into their Google account again even if they are already signed in? Similar to when a user goes to change their account settings.
I've found a way, but it is a bit sad that this feature is not included (or not documented) in gapi.auth.authorize.
Anyway, if you open the authorization popup manually, and then process the resulting token, you can pass additional parameters such as prompt=select_account, which will allow the user to select their account.
Here's a code example. With popup blocking, you will have to call this function in an onclick event for the popup not to be blocked.
As such, the code is not really production-ready. We do not manage cases such as when the user refuses to give his consent, and we do not pass extra token information such as the expiration time.
var switchUserAccount = function (callback) {
var popup = window.open("https://accounts.google.com/o/oauth2/auth?client_id=102862643449-geb89aoann7dj6tsha4mtkhvos5mk01b.apps.googleusercontent.com"
+ "&prompt=select_account"
+ "&scope=https://www.googleapis.com/auth/userinfo.email"
+ "&redirect_uri=https://david-sandbox.appspot.com/autoclose.html"
+ "&access_type=online&response_type=token", "thewindow");
var waitForPopup = function () {
try {
var token = popup.location.hash.substring(14).split("&")[0];
console.log("FOund token !" + token);
if (token == "") {
console.log("Not ready yet")
setTimeout(waitForPopup, 500);
} else {
gapi.auth.setToken({access_token: token});
popup.close();
callback();
}
}
catch (e) {
console.log("Not ready yet, exception")
setTimeout(waitForPopup, 500);
}
};
waitForPopup();
}

Get tagged photo without access token

I have some problem on this. Can I get public tagged photo from Instagram api without getting any code or access token?
Please share any link for reading because I cannot found any. I feel it is less knowledge about Instagram api on web.
Thanks!
You can pull public media by tag without authentication.
Take a look at the API documentation for the get /tags/tag-name/media/recent endpoint. Here's the URL: http://instagram.com/developer/endpoints/tags/#get_tags_media_recent
The documentation can be confusing, it shows using an access_token for this endpoint in the example, but it is not required. You will need to register an application and get a client ID.
I use MeteorJS and call a method server side that returns essentially the 'view source' of the instagram page. So if you can run a server side scrape on the tag url you will be able to handle the response with what i have below and it will push all the images into an array.
//server side method
Meteor.methods({
'scrapeInst':function(tag){
return Scrape.url('https://www.instagram.com/explore/tags/'+tag+'/')
}})
//client side logic
Meteor.call('scrapeInst',Session.get('params').tag,function(err,resp){
var theInstResp = resp;
cleanOne = resp.replace(/>|window._sharedData = |;</|;|#47;|<|/g,'').split('script')
var splitter = cleanOne[22].split(',');
var theArr = [];
_.each(splitter,function(e){
var theFinal = {};
var theS = e.split(":");
if(theS[0].replace(/"| |/g,'') === "display_src"){
theFinal[theS[0].replace(/"| |/g,'')] = theS[2].replace(/%22/g,'');
theArr.push(theFinal)
}
});
Session.set('photos',theArr);
setTimeout(function(){
Session.set('loading',false)
},1000)
})

How do I get data from a background page to the content script in google chrome extensions

I've been trying to send data from my background page to a content script in my chrome extension. i can't seem to get it to work. I've read a few posts online but they're not really clear and seem quite high level. I've got managed to get the oauth working using the Oauth contacts example on the Chrome samples. The authentication works, i can get the data and display it in an html page by opening a new tab.
I want to send this data to a content script.
i'm having a lot of trouble with this and would really appreciate if someone could outline the explicit steps you need to follow to send data from a bg page to a content script or even better some code. Any takers?
the code for my background page is below (i've excluded the oauth paramaeters and other )
` function onContacts(text, xhr) {
contacts = [];
var data = JSON.parse(text);
var realdata = data.contacts;
for (var i = 0, person; person = realdata.person[i]; i++) {
var contact = {
'name' : person['name'],
'emails' : person['email']
};
contacts.push(contact); //this array "contacts" is read by the
contacts.html page when opened in a new tab
}
chrome.tabs.create({ 'url' : 'contacts.html'}); sending data to new tab
//chrome.tabs.executeScript(null,{file: "contentscript.js"});
may be this may work?
};
function getContacts() {
oauth.authorize(function() {
console.log("on authorize");
setIcon();
var url = "http://mydataurl/";
oauth.sendSignedRequest(url, onContacts);
});
};
chrome.browserAction.onClicked.addListener(getContacts);`
As i'm not quite sure how to get the data into the content script i wont bother posting the multiple versions of my failed content scripts. if I could just get a sample on how to request the "contacts" array from my content script, and how to send the data from the bg page, that would be great!
You have two options getting the data into the content script:
Using Tab API:
http://code.google.com/chrome/extensions/tabs.html#method-executeScript
Using Messaging:
http://code.google.com/chrome/extensions/messaging.html
Using Tab API
I usually use this approach when my extension will just be used once in a while, for example, setting the image as my desktop wallpaper. People don't set a wallpaper every second, or every minute. They usually do it once a week or even day. So I just inject a content script to that page. It is pretty easy to do so, you can either do it by file or code as explained in the documentation:
chrome.tabs.executeScript(tab.id, {file: 'inject_this.js'}, function() {
console.log('Successfully injected script into the page');
});
Using Messaging
If you are constantly need information from your websites, it would be better to use messaging. There are two types of messaging, Long-lived and Single-requests. Your content script (that you define in the manifest) can listen for extension requests:
chrome.extension.onRequest.addListener(function(request, sender, sendResponse) {
if (request.method == 'ping')
sendResponse({ data: 'pong' });
else
sendResponse({});
});
And your background page could send a message to that content script through messaging. As shown below, it will get the currently selected tab and send a request to that page.
chrome.tabs.getSelected(null, function(tab) {
chrome.tabs.sendRequest(tab.id, {method: 'ping'}, function(response) {
console.log(response.data);
});
});
Depends on your extension which method to use. I have used both. For an extension that will be used like every second, every time, I use Messaging (Long-Lived). For an extension that will not be used every time, then you don't need the content script in every single page, you can just use the Tab API executeScript because it will just inject a content script whenever you need to.
Hope that helps! Do a search on Stackoverflow, there are many answers to content scripts and background pages.
To follow on Mohamed's point.
If you want to pass data from the background script to the content script at initialisation, you can generate another simple script that contains only JSON and execute it beforehand.
Is that what you are looking for?
Otherwise, you will need to use the message passing interface
In the background page:
// Subscribe to onVisited event, so that injectSite() is called once at every pageload.
chrome.history.onVisited.addListener(injectSite);
function injectSite(data) {
// get custom configuration for this URL in the background page.
var site_conf = getSiteConfiguration(data.url);
if (site_conf)
{
chrome.tabs.executeScript({ code: 'PARAMS = ' + JSON.stringify(site_conf) + ';' });
chrome.tabs.executeScript({ file: 'site_injection.js' });
}
}
In the content script page (site_injection.js)
// read config directly from background
console.log(PARAM.whatever);
I thought I'd update this answer for current and future readers.
According to the Chrome API, chrome.extension.onRequest is "[d]eprecated since Chrome 33. Please use runtime.onMessage."
See this tutorial from the Chrome API for code examples on the messaging API.
Also, there are similar (newer) SO posts, such as this one, which are more relevant for the time being.