YouTube.Search.list ReferenceError - google-apps-script

YouTube.Search.list in Google Apps Scripts shows this error: ReferenceError: "YouTube" is not defined (line 22).
Dashboard shows that the request went through each time I ran the code. Youtube Data API is enabled in Apps Script and Dev Console.
Any help appreciated on why I'm getting this error.
/*
YouTube RSS Feeds
Written by #user1535152 http://stackoverflow.com/q/30486682/512127
Based on http://www.labnol.org/internet/twitter-rss-feed/28149/
*/
function doGet(e) {
var title = ("Youtube RSS Feed for " + e.parameter.search),
timez = Session.getScriptTimeZone(),
search = encodeURIComponent(e.parameter.search),
link = ("https://www.youtube.com/results?search_query=" + search),
self = ScriptApp.getService().getUrl() + "?" + search;
var rss='<?xml version="1.0"?>';
rss+='<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">';
rss+='<channel><title>'+title+'</title>';
rss+='<link>'+link+'</link>';
rss+='<atom:link href="'+self+'" rel="self" type="application/rss+xml" />';
rss+='<description>' + title + ' updated on ' + new Date() + '.</description>';
var results = YouTube.Search.list('id, snippet', {
q: search,
maxResults: 50,
order: 'date'
});
for (var i = 0; i < results.items.length; i++){
var item = results.items[i];
rss += "<item>";
rss += "<title>" + item.snippet.title + "</title>";
rss += "<link>http://www.youtube.com/watch?v=" + item.id.videoId + "</link>";
rss += "<description>" + item.snippet.description + "</description>";
rss += "<pubDate>" + Utilities.formatDate(new Date(item.snippet.publishedAt), timez, "EEE, dd MMM yyyy HH:mm:ss Z") + "</pubDate>";
rss += "<guid>http://www.youtube.com/watch?v=" + item.id.videoId + "</guid>";
rss += "</item>";
}
rss+="</channel></rss>";
return ContentService.createTextOutput(rss).setMimeType(ContentService.MimeType.RSS);
}

From your question, it was found that "Youtube Data API is enabled in Apps Script and Dev Console.". But an error of ReferenceError: "YouTube" is not defined occurs, when the script is run. So please confirm a following setting.
In order to use YouTube.Search.list(), it requires not only enabling API at API console, but also enabling at Advanced Google Services. I confirmed that when YouTube Data API is OFF at Advanced Google Services, the same error occurs. In order to enable YouTube Data API at Advanced Google Services, please confirm as follows.
Open script editor that there is the script.
Click Resources -> Advanced Google Services.
Turn on YouTube Data API.
Click OK.
If YouTube Data API is "off", please turn on by clicking. After this, please try to run, again.
If this is not useful for you, I'm sorry.

Related

How to obtain initial OAuth2.0 code from browser?

I have made a javascript script in Google Apps Script, attached to a google sheet. I ultimately want to connect the Google Sheet to the Google Fit API and have my fitness data automatically inputted to the Google Sheet. At step 0, I made my Google Console project and OAuth2.0 client ID & secret. I am at step 1, where I need to obtain the authentication code from the initial 'GET' request.
My request is correct and I can send the request correctly using the callback notation; the code below is run in a callback in a timed for loop such that the html object (html_objet) remains active for a certain amount of time, waiting to get the code. I can see the code in the browser url when I have finished approving with the Google popup, but I do not know how to import this code value from the client browser(popup) into my javascript program. I open the client browser(popup) using :
var html_texte = '<html><head><script>'
+ 'const winRef = window.open("'+js2html_data+'");'
+ 'winRef ? google.script.host.close() : window.alert("Allow popup to redirect to url");'
+ 'window.onload=function(){document.getElementById("'+js2html_data+'").href ="'+js2html_data+'";};'
+ '</script></head><body>'
+ '</body></html>';
var html_objet = HtmlService.createHtmlOutput(html_texte).setWidth(90).setHeight(1);
html_objet.js2html_data = js2html_data;
SpreadsheetApp.getUi().showModalDialog(html_objet, "Opening ...");
This works. But, I can not do anything with the window once it is open (ie: reload the window, get the current url).
I tried modifying the html_texte variable to the text below, such that I can return the url of the authenticated page. It does not work, how can I update the js2html_data variable such that it shows the url of the final Google user authenticated page? Or, pass the url to another html variable (html2js_data) out to my javascript program?
var html_texte = '<html><head><script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>'
+ '<script>'
+ 'const winRef = window.open("'+js2html_data+'");'
+ 'winRef ? google.script.host.close() : window.alert("Allow popup to redirect to url");'
+ 'window.onload=function(){document.getElementById("'+js2html_data+'").href ="'+js2html_data+'";};'
// + '$(document).ready(function getUrl(){ document.getElementById("'+html2js_data+'").innerHTML=window.location.href; });'
+ '$(document).ready(function getUrl(){ document.getElementById("'+html2js_data+'").href=window.location.href; });'
//+ '$(document).ready(function getUrl(){ document.getElementById(<?="'+html2js_data+'"?>).href=window.location.href; });'
+ '</script></head><body>'
// + '<h3 id="html2js_data" onclick="getUrl()">OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO</h3>'
+ '<div id="html2js_data"></div>'
+ '</body></html>';
Any suggestions would be great...I tried a lot of things...

GA4 | Google Analytics Admin API | Apps Script

I am trying to call the (new) alpha GA Admin API for the simple task of listing all the Accounts that I have access to... I am at a stage where I call the API but I do not get any error message nor I see the information on the google sheet. Can you please help?
function listGA4Accounts() {
var sheet = _setupListGA4AccountsSheet();
var accounts = AnalyticsAdmin.Accounts.list();
if (accounts.items && accounts.items.length) {
for (var i = 0; i < accounts.items.length; i++) {
var account = accounts.items[i];
var rowNum = i+2;
sheet.getRange("A" + rowNum).setNumberFormat('#')
.setValue(account.name).setBackground(AUTO_POP_CELL_COLOR);
sheet.getRange("B" + rowNum)
.setValue(account.displayName).setBackground(AUTO_POP_CELL_COLOR);
sheet.getRange("C" + rowNum)
.setValue(account.createTime).setBackground(AUTO_POP_CELL_COLOR);
}
}
}
The above was adapted from the old code being used for Universal Analytics/GA3 and used to work just fine. What I am missing? I also have a standard GCP project in place and the API is enabled for that GCP project.
Any help/thoughts on the above are highly appreciated.
Thanks.
You have been quite close to the solution.
TIPS for debugging the API response:
prompt the API response in the console with Logger.log(JSON.stringify(<API RESPONSE>))
copy the log and paste it on a JSON formatter website like this one: https://jsonformatter.curiousconcept.com/
check the actual structure
OPTIONAL: copy the formatted JSON from the page & save it in the script as a variable and use this instead of the API response to prepare the code. Once it's working properly with the saved data you can switch back to the data from the API request.
Following things that I changed:
removed var sheet = _setupListGA4AccountsSheet(); (was not relevant for testing the API response)
just changed the way the JSON object is accessed bc it's a nested one, to get an account item it's necessary to write <variable name>.accounts.item
Here is the code that can be copied as-is to App Script editor and can be tested:
function listGA4Accounts() {
var accounts = AnalyticsAdmin.Accounts.list();
if (accounts && !accounts.error) {
accounts = accounts.accounts; // <== this is why it didn't work is a nested JSON
Logger.log(accounts[0]);
for (var i = 0, account; account = accounts[i]; i++) {
Logger.log(account);
/**
* PLACE your code here
*/
}
}
}

Did Google Sheets stop allowing json access?

I have an app that opens the json version of a spreadsheet that I've published to the web. I used the instructions on this website: https://www.freecodecamp.org/news/cjn-google-sheets-as-json-endpoint/
It's been working fine for a couple months, but today I realized that the url of my json file is no longer working since yesterday. It gives the message, "Sorry, unable to open the file at this time. Please check the address and try again." The regular link to view the spreadsheet as a webpage still works though.
Did Google drop support for this feature? Is there another way to get the data of a spreadsheet in json format through a URL? I started looking into the Google Developer API, but it was really confusing.
You are using the JSON Alt Type variant of the Google Data protocol. This protocol is dated and appears to no longer work reliably. The GData API Directory tells:
Google Spreadsheets Data API: GData version is still live. Replaced by the Google Sheets API v4.
Google Sheets API v4 is a modern RESTful interface that is typically used with a client library to handle authentication and batch processing of data requests. If you do not want to do a full-blown client implementation, David Kutcher offers the following v4 analog for the GData JSON Alt Type, using jQuery:
GData (old version, not recommended):
var url = 'https://spreadsheets.google.com/feeds/list/' +
spreadsheet_id + '/' + tab_ordinal + '/public/values?alt=json';
($.getJSON(url, 'callback=?')).success(function(data) {
// ...
};
V4 (new version, recommended):
var url = 'https://sheets.googleapis.com/v4/spreadsheets/' +
spreadsheet_id + '/values/' + tab_name +
'?alt=json&key=' + api_key;
($.getJSON(url, 'callback=?')).success(function(data) {
// ...
};
...where:
spreadsheet_id is the long string of letters and numbers in the address of the spreadsheet — it is the bit between /d/ and /edit
tab_ordinal is number of the sheet — the first sheet that appears in the tab bar is sheet number 1, the second one is 2, and so on
tab_name is the name of the sheet, i.e., the name you see in the tab bar at the bottom of the window when you have the spreadsheet open for editing
api_key is the API key you get from from Google Cloud Platform console
Note that the JSON output format differs between the two versions.
With the GData pattern, the spreadsheet needs to be shared as File > Share > Publish to the web.
With the V4 pattern, the spreadsheet needs to be shared as File > Share > Share with others > anyone with the link can view.
As of March 2022:
If you dont want to create a key you can use this URL format:
https://docs.google.com/spreadsheets/d/{spreadsheetId}/gviz/tq
which downloads a json.txt file of the format
google.visualization.Query.setResponse({json});
From that you would have to slice out the json
-OR --
Just configure a key as per the Official docs.
Go to Google Console and create a project (or use an existing one)
Goto Credenetials page and create a API Key
Include Sheets API from library
And Voila!
You can now get json using URL Format:
https://sheets.googleapis.com/v4/spreadsheets/{spreadsheetId}/values/{sheetName}?alt=json&key={theKey}
Edit: The Sheet should be public and Anyone with link can view
Without jQuery ...
var url = 'https://docs.google.com/spreadsheets/d/'+id+'/gviz/tq?tqx=out:json&tq&gid='+gid;
with id of the spreadsheet and gid of the sheet
https://codepen.io/mikesteelson/pen/wvevppe
example :
var id = '______your_speadsheet_id________';
var gid = '0';
var url = 'https://docs.google.com/spreadsheets/d/'+id+'/gviz/tq?tqx=out:json&tq&gid='+gid;
fetch(url)
.then(response => response.text())
.then(data => document.getElementById("json").innerHTML=myItems(data.substring(47).slice(0, -2))
);
function myItems(jsonString){
var json = JSON.parse(jsonString);
var table = '<table><tr>'
json.table.cols.forEach(colonne => table += '<th>' + colonne.label + '</th>')
table += '</tr>'
json.table.rows.forEach(ligne => {
table += '<tr>'
ligne.c.forEach(cellule => {
try{var valeur = cellule.f ? cellule.f : cellule.v}
catch(e){var valeur = ''}
table += '<td>' + valeur + '</td>'
}
)
table += '</tr>'
}
)
table += '</table>'
return table
}
gdata is the older version of Sheets API and it's shut down. See Google's announcement here https://cloud.google.com/blog/products/g-suite/migrate-your-apps-use-latest-sheets-api

Google app scripts: email a spreadsheet as excel

How do you make an app script which attaches a spreadsheet as an excel file and emails it to a certain email address?
There are some older posts on Stackoverflow on how to do this however they seem to be outdated now and do not seem to work.
Thank you.
It looks like #Christiaan Westerbeek's answer is spot on but its been a year now since his post and I think there needs to be a bit of a modification in the script he has given above.
var url = file.exportLinks[MimeType.MICROSOFT_EXCEL];
There is something wrong with this line of code, maybe that exportLinks has now depreciated. When I executed his code it gave an error to the following effect:
TypeError: Cannot read property "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" from undefined.
The workaround is as follows:
The URL in the above line of code is basically the "download as xlsx" URL that can be used to directly download the spreadsheet as an xlsx file that you get from File> Download as > Microsoft Excel (.xlsx)
This is the format:
https://docs.google.com/spreadsheets/d/<<<ID>>>/export?format=xlsx&id=<<<ID>>>
where <<>> should be replaced by the ID of your file.
Check here to easily understand how to extract the ID from the URL of your google sheet.
Here's an up-to-date and working version. One prerequisite for this Google Apps script to work is that the Drive API v2 Advanced Google Service must be enabled. Enable it in your Google Apps script via Resources -> Advanced Google Services... -> Drive API v2 -> on. Then, that window will tell you that you must also enabled this service in the Google Developers Console. Follow the link and enable the service there too! When you're done, just use this script.
/**
* Thanks to a few answers that helped me build this script
* Explaining the Advanced Drive Service must be enabled: http://stackoverflow.com/a/27281729/1385429
* Explaining how to convert to a blob: http://ctrlq.org/code/20009-convert-google-documents
* Explaining how to convert to zip and to send the email: http://ctrlq.org/code/19869-email-google-spreadsheets-pdf
* New way to set the url to download from by #tera
*/
function emailAsExcel(config) {
if (!config || !config.to || !config.subject || !config.body) {
throw new Error('Configure "to", "subject" and "body" in an object as the first parameter');
}
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var spreadsheetId = spreadsheet.getId()
var file = Drive.Files.get(spreadsheetId);
var url = 'https://docs.google.com/spreadsheets/d/'+spreadsheetId+'/export?format=xlsx';
var token = ScriptApp.getOAuthToken();
var response = UrlFetchApp.fetch(url, {
headers: {
'Authorization': 'Bearer ' + token
}
});
var fileName = (config.fileName || spreadsheet.getName()) + '.xlsx';
var blobs = [response.getBlob().setName(fileName)];
if (config.zip) {
blobs = [Utilities.zip(blobs).setName(fileName + '.zip')];
}
GmailApp.sendEmail(
config.to,
config.subject,
config.body,
{
attachments: blobs
}
);
}
Update: I updated the way to set the url to download from. Doing it through the file.exportLinks collection is not working anymore. Thanks to #tera for pointing that out in his answer.

Is it possible to impersonate domain's users with Google Drive API using Google Apps Script?

I am a head of studies and school administrator of our Google Apps for Education.
I used Google Apps Script for a lot of applications (control of absences, sending emails, automatic reporting, ScriptDb databases and more) using gas services. It's fantastic.
Basically I need to create a folder structure (years, courses, teachers, ...) within the Google Drive of students.
With Google Apps Script services I can do it easily but then the folders belong to the creator (administrator) and I think then users spend the administrator storage quota. This does not interest me.
(Yes, I can make an application to be executed by the users and create the structure in its Google Drive, but I'd rather do it in an automated manner and without intervention)
To create this documents (and folders) in Google Drive users (teachers, students, ...) have adapted the code provided by Waqar Ahmad in this response [ Add an writer to a spreadsheet ... ]
That allows me to take possession of documents of other users to make updates using the Google Document List API (Google Apps administrative access to impersonate a user of the domain) and also have adapted to create folders and files on other Google Drive users. It works perfectly. I mention here:
How to add a Google Drive folder ...
But now, the version 3 of the Google Documents List AP, has been officially deprecated and encourage us to work with the Google API Drive.
I tried to do the same with this new Google API. Has anyone managed to do this? Is it possible? I have no idea where to start!
Thank you.
Sergi
Updated:
This is the code i'm working but I get an "invalid request" error:
(...)
var user = e.parameter.TB_email // I get user from a TextBox
//https://developers.google.com/accounts/docs/OAuth2ServiceAccount
//{Base64url encoded header}.{Base64url encoded claim set}.{Base64url encoded signature}
//{Base64url encoded header}
var header = '{"alg":"RS256","typ":"JWT"}'
var header_b64e = Utilities.base64Encode(header)
//{Base64url encoded claim set}
var t_ara = Math.round((new Date().getTime())/1000) // now
var t_fins = t_ara + 3600 // t + 3600 sec
var claim_set = '{"iss":"1111111111-xxxxxxxxxxxxxxxxxxxxxx#developer.gserviceaccount.com",'+
'"prn":"' + user + '",' +
'"scope":"https://www.googleapis.com/auth/prediction",'+
'"aud":"https://accounts.google.com/o/oauth2/token",'+
'"exp":'+t_fins+','+
'"iat":'+t_ara+'}'
// where '1111111111-xxxxxxxxxxx... is my CLIENT-ID (API Access -> Service Account)
var claim_set_b64e = Utilities.base64Encode(claim_set)
claim_set_b64e = claim_set_b64e.replace(/=/g,'')
var to_sign = header_b64e + '.' + claim_set_b64e
// [signature bytes] ??? // password 'isnotasecret???'
var key_secret = DocsList.getFileById('0Biiiiiiiiii-XXXXXXXXXXX').getBlob().getBytes()
// where '0Biiiiiiiiii-XXXXXXXXXXX'... is my p12 file (key_secret) uploaded to GDRive
// I don't know if this is correct !!!
var sign = Utilities.base64Encode(Utilities.computeHmacSha256Signature(to_sign, key_secret))
var JWT_signed = to_sign + '.' + sign
JWT_signed = JWT_signed.replace(/=/g,'')
// Token Request /////////////////////////////////////////////////////////////
var url = 'https://accounts.google.com/o/oauth2/token'
//var url = 'https%3A%2F%2Faccounts.google.com%2Fo%2Foauth2%2Ftoken' ???
//var url = 'https:' + '%2F%2Faccounts.google.com%2Fo%2Foauth2%2Ftoken' ???
var parameters = {
"method" : "POST",
"payload" : '"' + 'grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=' + JWT_signed + '"',
"contentType" : "application/x-www-form-urlencoded"
}
var content = UrlFetchApp.fetch(url,parameters) //.getContentText()
// Token Request end ////////////////////////////////////////////////////////
And I get an "Invalid Request" and not a JSON with the token
The 2 first parts ( header & claim set ) are OK. The result are equal to the result of Google OAuth page.
I don't know if the signature part are correct or if the error is in the token request.
The issue with your example above is that it it's computing the signature with hmacsha256. You need to use rsasha256. There are two service account libraries for apps script right now. One that I put together is:
https://gist.github.com/Spencer-Easton/f8dad65932bff4c9efc1
The issue with both libraries is they are derived from jsrsa which runs very slow on the server side.