I'm trying to use Google Apps Script to retrieve data from Google Apps Reporting API which specify me to send XML request
My goal is to retrieve disk_space_report an put that data into spreadsheet so I can monitor the disk space in that spreadsheet and also process some data.
Anyone can give me an example on how to do that kind of stuff?
Thanks in advanced.
Here is a little code which I have written and using to fetch Google Apps users account report.
function startHere(){
var domain = UserManager.getDomain();
var fDate = '2012-12-18';//Utilities.formatDate(new Date(), Session.getTimeZone(), 'yyyy-MM-dd');
var url = 'https://www.google.com/hosted/services/v1.0/reports/ReportingData';
//Build API request parameters
var fetchArgs = googleOAuth_('Reporting', url);
fetchArgs.method = 'POST';
var rawXML = '<?xml version="1.0" encoding="UTF-8"?>'
+'<rest xmlns="google:accounts:rest:protocol" xmlns:xsi=" http://www.w3.org/2001/XMLSchema-instance ">'
+'<type>Report</type>'
+'<domain>'+domain+'</domain>'
+'<date>'+fDate+'</date>'
+'<page>1</page>'
+'<reportType>daily</reportType>'
+'<reportName>accounts</reportName>'
+'</rest>';
fetchArgs.payload = rawXML;
fetchArgs.contentType = "application/xml";
fetchArgs.headers = {"Content-type": "application/atom+xml charset=UTF-8"};
//Fetch CSV data
var csvData = UrlFetchApp.fetch(url, fetchArgs).getContentText();
//Parse CSV data and make a 2D array
var recs = csvData.split('\n');
var data = []; //this is actual 2D data
for(var i=0; i<recs.length-1; i++){
var temp = recs[i].split(',');
if(i==0) temp.push('percent_disk_usage');
else{
var usage = (parseInt(temp[5])*100)/(parseInt(temp[4])*1024*1024);
temp.push(usage);
}
data.push(temp);
}
//Write data to spreadsheet
}
function googleOAuth_(name,scope) {
var oAuthConfig = UrlFetchApp.addOAuthService(name);
oAuthConfig.setRequestTokenUrl("https://www.google.com/accounts/OAuthGetRequestToken?scope="+scope);
oAuthConfig.setAuthorizationUrl("https://www.google.com/accounts/OAuthAuthorizeToken");
oAuthConfig.setAccessTokenUrl("https://www.google.com/accounts/OAuthGetAccessToken");
oAuthConfig.setConsumerKey("anonymous");
oAuthConfig.setConsumerSecret("anonymous");
return {oAuthServiceName:name, oAuthUseToken:"always"};
}
Just a quick sample. Inside your script create a new HTML file called: xmlRequest with the following content:
<?xml version="1.0" encoding="UTF-8"?>
<rest xmlns="google:accounts:rest:protocol"
xmlns:xsi=" http://www.w3.org/2001/XMLSchema-instance ">
<type>Report</type>
<domain>YOURDOMAIN.COM</domain>
<date>2012-12-01</date>
<page>1</page>
<reportType>daily</reportType>
<reportName>accounts</reportName>
</rest>
Change YOURDOMAIN.COM with your Google Apps domain. Inside Code.gs paste the following code:
/**
* Script configuration
*/
var SCOPE = 'https://www.google.com/hosted/services/v1.0/reports/ReportingData';
var APPNAME = "disk_space_report";
var URL = 'https://www.google.com/hosted/services/v1.0/reports/ReportingData';
function testit() {
// Generate the new entry from a template
var template = HtmlService.createHtmlOutputFromFile("xmlRequest").getContent();
var response = UrlFetchApp.fetch(URL,googleOAuth_('POST', template));
Logger.log(response.getContentText());
}
/**
* Google authentication loader
* #param {String} method the HTTP method to use for the UrlFetch operation, possible values are: GET, POST, PUT, DELETE
* #param {String} payload the payload to use if needed
* #return {Object} configuration options for UrlFetch, including oAuth parameters
*/
function googleOAuth_(method, payload) {
// Shared configuration for all methods
var oAuthConfig = UrlFetchApp.addOAuthService(APPNAME);
oAuthConfig.setRequestTokenUrl('https://www.google.com/accounts/OAuthGetRequestToken?scope='+encodeURIComponent(SCOPE));
oAuthConfig.setAuthorizationUrl('https://www.google.com/accounts/OAuthAuthorizeToken');
oAuthConfig.setAccessTokenUrl('https://www.google.com/accounts/OAuthGetAccessToken');
oAuthConfig.setConsumerKey('anonymous');
oAuthConfig.setConsumerSecret('anonymous');
// Detect the required method
switch(method) {
case "GET":
return {oAuthServiceName:APPNAME, oAuthUseToken:'always'};
break;
case "POST":
return {oAuthServiceName:APPNAME, oAuthUseToken:'always', payload: payload, contentType: 'application/atom+xml', method: "POST"};
break;
case "PUT":
return {oAuthServiceName:APPNAME, oAuthUseToken:'always', payload: payload, contentType: 'application/atom+xml', method: "PUT"};
break;
case "DELETE":
return {oAuthServiceName:APPNAME, oAuthUseToken:'always', method: "DELETE"};
break;
default:
return {oAuthServiceName:APPNAME, oAuthUseToken:'always'};
break;
}
}
Now run the testit function and inside the logger you should get the raw disk usage stats to get parsed.
Related
I got a bit stuck.
Goal:
To have a chatbot that domain users can use to confirm shoppinglist that is kept in a google spreadsheets.
The sheet that holds the data is generated based on forms input and manual input.
I am looking for a chatbot because I wanted an interface that shows some basic in/output without sharing the whole spreadsheet.
What have I done:
I looked into service accounts and I am able to read data. I wanted a sheet object but I read that this is not possible. I get a JSON object. I will manage I think.
How do I write data to the sheet?
The code under here only reads it. What function sends the data to sheets?
/**
* Configures the spreadsheet service.
*/
function getSpreasheetService() {
return OAuth2.createService("spreadsheet")
// Set the endpoint URL.
.setTokenUrl("https://accounts.google.com/o/oauth2/token")
// Set the private key and issuer.
.setPrivateKey(SERVICE_ACCOUNT_PRIVATE_KEY)
.setIssuer(SERVICE_ACCOUNT_EMAIL)
// Set the property store where authorized tokens should be persisted.
.setPropertyStore(PropertiesService.getScriptProperties())
// Set the scope.
.setScope("https://www.googleapis.com/auth/spreadsheets");
}
function readSheet(){
var service = getSpreasheetService();
var ssId = 'sheetsid' ;
var range = 'Voorraad!A1:50';
var url = 'https://sheets.googleapis.com/v4/spreadsheets/' + ssId +'/values/' + range;
var response = UrlFetchApp.fetch(url, { headers: {Authorization: 'Bearer ' + service.getAccessToken() } }); //json data
var rep = JSON.parse(response.getContentText());
var values = rep.values;
console.log(response);
console.log(rep);
// Logger.log(values);
//now i can use these data to reply back to end user as message
}
Here we go I found a solution :)
/**
* Configures the spreadsheet service.
*/
function getSpreasheetService() {
return OAuth2.createService("spreadsheet")
// Set the endpoint URL.
.setTokenUrl("https://accounts.google.com/o/oauth2/token")
// Set the private key and issuer.
.setPrivateKey(SERVICE_ACCOUNT_PRIVATE_KEY)
.setIssuer(SERVICE_ACCOUNT_EMAIL)
// Set the property store where authorized tokens should be persisted.
.setPropertyStore(PropertiesService.getScriptProperties())
// Set the scope.
.setScope("https://www.googleapis.com/auth/spreadsheets");
}
function writeSheet(){
var service = getSpreasheetService();
var spreadsheetId = ' your spreadsheet id' ;
var range = 'Blad1!A3'; // range = '[sheetname]![starting cell]:[ending cell]';
var waardes = {values:[ ['A3 data' , 'B3 data', 'C3 data'] , [4, '', "C4"]] } ;
//no matter if it is one value or rows with multiple values, it must be a nested json object. {values: [['value']]}
// "" or '' work the same
// Method: spreadsheets.values.update
var url = "https://sheets.googleapis.com/v4/spreadsheets/" + spreadsheetId + "/values/" + range + "?valueInputOption=USER_ENTERED"; //valueInputOption is required
var params = {
method: "put",
headers: {Authorization: "Bearer " + service.getAccessToken()},
contentType: "application/json",
payload: JSON.stringify(waardes),
};
UrlFetchApp.fetch(url, params);
}
I used these pages to find my answers:
https://gist.github.com/tanaikech/d102c9600ba12a162c667287d2f20fe4
https://developers.google.com/google-ads/scripts/docs/features/third-party-apis
https://developers.google.com/sheets/api/reference/rest
I`m trying to get URL parameter passed to google form via apps script, but don't know exactly how does it does. Case:
https://docs.google.com/forms/d/e/XXXX/viewform?a=12
(Passed XXXX, cause this form is for clients use)
and I need to get answers and this parameter after submit.
function onSubmit(e) {
var POST_URL = "www.myserver.com?id=a";//this parametr
var form = FormApp.getActiveForm();
var allResponses = form.getResponses();
var latestResponse = allResponses[allResponses.length - 1];
var response = latestResponse.getItemResponses();
var payload = {};
for (var i = 0; i < response.length; i++) {
var question = response[i].getItem().getTitle();
var answer = response[i].getResponse();
payload[question] = answer;
}
var options = {
"method": "post",
"contentType": "application/json",
"payload": JSON.stringify(payload)
};
UrlFetchApp.fetch(POST_URL, options);
};
This code made for sending POST request with answers of form to my server and how can I pass the a=12 or only 12 to this request?
You need to append the values to POST_URL instead of passing via payload. There's a nice bit of code provided by Google in their OAuth2 library that will do this for you:
/**
* Builds a complete URL from a base URL and a map of URL parameters.
* #param {string} url The base URL.
* #param {Object.<string, string>} params The URL parameters and values.
* #return {string} The complete URL.
* #private
*/
function buildUrl_(url, params) {
var paramString = Object.keys(params).map(function(key) {
return encodeURIComponent(key) + '=' + encodeURIComponent(params[key]);
}).join('&');
return url + (url.indexOf('?') >= 0 ? '&' : '?') + paramString;
}
Which means you can do something like:
var POST_URL = "www.myserver.com?id=a";
var payload = {a: 12};
console.log(buildUrl_(POST_URL, payload)); // www.myserver.com?id=a&a=12
function buildUrl_(url, params) {
var paramString = Object.keys(params).map(function(key) {
return encodeURIComponent(key) + '=' + encodeURIComponent(params[key]);
}).join('&');
return url + (url.indexOf('?') >= 0 ? '&' : '?') + paramString;
}
Ultimately, the final line in your code would be:
UrlFetchApp.fetch(buildUrl_(POST_URL, payload), options);
I am trying to follow documentation but never get the 0Auth2 to connect.
Btw I'm running the script manually from the google sheets scripting page, where should I get prompting for allowing access?
(I don't understand all this 0Auth2 scheme and I have already gave authorization to run the script and got client id and secret)...
See below my log and script routines (the first get to photo is still minimalistic as I didn't yet get through the 0Auth2 step ;-) .
Thanks in advance for any hint. I thought it would be trivial as it is my own sheet and google photo account...
Log:
[19-01-06 17:50:05:809 CET] starting
[19-01-06 17:50:05:810 CET] getPhotoService
[19-01-06 17:50:05:849 CET] false
[19-01-06 17:50:05:850 CET] redirectURI=https://script.google.com/macros/d/[REMOVED]/usercallback
[19-01-06 17:50:05:864 CET] Open the following URL and re-run the script: https://accounts.google.com/o/oauth2/auth?client_id=[removed].apps.googleusercontent.com&response_type=code&redirect_uri=https%3A%2F%2Fscript.google.com%2Fmacros%2Fd%2F[removed]%2Fusercallback&state=[removed]&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fphotoslibrary.readonly&login_hint=[removed]&access_type=offline&approval_prompt=force
Script:
function getPhotoService() {
// Create a new service with the given name. The name will be used when
// persisting the authorized token, so ensure it is unique within the
// scope of the property store.
Logger.log('getPhotoService');
return OAuth2.createService('gPHOTOfj')
// enable caching on the service, so as to not exhaust script's PropertiesService quotas
.setPropertyStore(PropertiesService.getUserProperties())
.setCache(CacheService.getUserCache())
// Set the endpoint URLs, which are the same for all Google services.
.setAuthorizationBaseUrl('https://accounts.google.com/o/oauth2/auth')
.setTokenUrl('https://accounts.google.com/o/oauth2/token')
// Set the client ID and secret, from the Google Developers Console.
.setClientId(CLIENT_ID)
.setClientSecret(CLIENT_SECRET)
// Set the name of the callback function in the script referenced
// above that should be invoked to complete the OAuth flow.
.setCallbackFunction('authCallback')
//.setCallbackFunction('https://script.google.com/macros/d/'+SCRIPT_ID+'/authCallback')
// Set the property store where authorized tokens should be persisted.
.setPropertyStore(PropertiesService.getUserProperties())
// Set the scopes to request (space-separated for Google services).
.setScope('https://www.googleapis.com/auth/photoslibrary.readonly')
// Below are Google-specific OAuth2 parameters.
// Sets the login hint, which will prevent the account chooser screen
// from being shown to users logged in with multiple accounts.
.setParam('login_hint', Session.getActiveUser().getEmail())
// Requests offline access.
.setParam('access_type', 'offline')
// Forces the approval prompt every time. This is useful for testing,
// but not desirable in a production application.
.setParam('approval_prompt', 'force');
}
function authCallback(request) {
Logger.log('Called back!');
var photoService = getPhotoService();
var isAuthorized = photoService.handleCallback(request);
if (isAuthorized) {
Logger.log('Authorisation Success!');
} else {
Logger.log('Authorisation Denied...!');
}
}
// Modified from http://ctrlq.org/code/20068-blogger-api-with-google-apps-script
function photoAPI() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var albums_sh = ss.getSheetByName("albums") || ss.insertSheet("albums", ss.getSheets().length);
albums_sh.clear(); var nrow = new Array(); var narray = new Array();
Logger.log("starting");
var service = getPhotoService();
Logger.log(service.hasAccess());
Logger.log('redirectURI='+service.getRedirectUri());
if (service.hasAccess()) {
var api = "https://photoslibrary.googleapis.com/v1/albums";
var headers = {
"Authorization": "Bearer " + service.getAccessToken()
};
var options = {
"headers": headers,
"method" : "GET",
"muteHttpExceptions": true
};
var response = UrlFetchApp.fetch(api, options);
var json = JSON.parse(response.getContentText());
for (var i in json.items) {
nrow = []; nrow.push(json.items[i].id); nrow.push(json.items[i].name); nrow.push(json.items[i].url); narray.push(nrow);
}
albums_sh.getRange(1,1,narray.length,narray[0].length).setValues(narray);
} else {
var authorizationUrl = service.getAuthorizationUrl();
Logger.log("Open the following URL and re-run the script: " + authorizationUrl);
}
}
So it works, if others want to use it. But it is quite slow (I have 500 albums...):
function photoAPI() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var albums_sh = ss.getSheetByName("albums") || ss.insertSheet("albums", ss.getSheets().length);
albums_sh.clear();
var narray = [];
var api = "https://photoslibrary.googleapis.com/v1/albums";
var headers = { "Authorization": "Bearer " + ScriptApp.getOAuthToken() };
var options = { "headers": headers, "method" : "GET", "muteHttpExceptions": true };
var param= "", nexttoken;
do {
if (nexttoken)
param = "?pageToken=" + nexttoken;
var response = UrlFetchApp.fetch(api + param, options);
var json = JSON.parse(response.getContentText());
json.albums.forEach(function (album) {
var data = [
album.title,
album.mediaItemsCount,
album.productUrl
];
narray.push(data);
});
nexttoken = json.nextPageToken;
} while (nexttoken);
albums_sh.getRange(1, 1, narray.length, narray[0].length).setValues(narray);
}
The image shows the code who is updated.
The var "xlsFile" is undefined, why? How can I convert the Google Sheets file to an Excel file with (Google Sheets) Script Editor
function googleOAuth_ (name, scope) {
var oAuthConfig = UrlFetchApp.addOAuthService(name);
oAuthConfig.setRequestTokenUrl("https://www.google.com/accounts/OAuthGetRequestToken? scope="+scope);
oAuthConfig.setAuthorizationUrl("https://www.google.com/accounts/OAuthAuthorizeToken");
oAuthConfig.setAccessTokenUrl("https://www.google.com/accounts/OAuthGetAccessToken");
oAuthConfig.setConsumerKey('anonymous');
oAuthConfig.setConsumerSecret('anonymous');
return {oAuthServiceName:name, oAuthUseToken:"always"};
}
function test(){
var id = '#'
exportToXls(id)
}
function exportToXls(id){
var mute = {muteHttpExceptions: true };
var name = DriveApp.getFileById(id).getName()
var url = 'https://docs.google.com/feeds/';
var doc = UrlFetchApp.fetch(url+'download/spreadsheets/Export?key='+id+'&exportFormat=xls', mute).getBlob()
var xlsfile = DocsList.createFile(doc).rename(name+'.xlsx')
}
Using the Drive API, we can get more information about files than is available through the DriveApp methods. Check out the file data, especially exportLinks. Those links contain the magic that will let us get an XLS file. (For fun, put a breakpoint after file is assigned, and check what information you have to play with.)
This script uses the Advanced Drive Service, which must be enabled. A more complete version, with error checking, is available in this gist.
/**
* Downloads spreadsheet with given file id as an Excel file.
* Uses Advanced Drive Service, which must be enabled. * Throws if error encountered.
*
* #param {String} fileId File ID of Sheets file on Drive.
*/
function downloadXLS(fileId) {
var file = Drive.Files.get(fileId);
var url = file.exportLinks[MimeType.MICROSOFT_EXCEL];
var options = {
headers: {
Authorization:"Bearer "+ScriptApp.getOAuthToken()
},
muteHttpExceptions : true /// Get failure results
}
var response = UrlFetchApp.fetch(url, options);
var status = response.getResponseCode();
var result = response.getContentText();
if (status != 200) {
// Get additional error message info, depending on format
if (result.toUpperCase().indexOf("<HTML") !== -1) {
var message = strip_tags(result);
}
else if (result.indexOf('errors') != -1) {
message = JSON.parse(result).error.message;
}
throw new Error('Error (' + status + ") " + message );
}
var doc = response.getBlob();
//DocsList.createFile(doc).rename(file.title + '.xlsx') // Deprecated
DriveApp.createFile(doc).setName(file.title + '.xlsx');
}
The code below uses oAuthConfig which is now deprecated. Use Mogsdad answer instead. The importXLS function uses the drive API and still works.
You'll find many post saying this is not possible and (a few) others saying that you can...and obviously you can !
Mogsdad's answer here (simultaneously) brings an elegant solution using drive service, here is another one so you have a choice ;-)
As a bonus, I added the reverse process, if ever you need it.
Use a function call similar to what I use in the test function to make it work.
function googleOAuth_(name,scope) {
var oAuthConfig = UrlFetchApp.addOAuthService(name);
oAuthConfig.setRequestTokenUrl("https://www.google.com/accounts/OAuthGetRequestToken?scope="+scope);
oAuthConfig.setAuthorizationUrl("https://www.google.com/accounts/OAuthAuthorizeToken");
oAuthConfig.setAccessTokenUrl("https://www.google.com/accounts/OAuthGetAccessToken");
oAuthConfig.setConsumerKey('anonymous');
oAuthConfig.setConsumerSecret('anonymous');
return {oAuthServiceName:name, oAuthUseToken:"always"};
}
function test(){
var id = 'spreadsheet_ID'
exportToXls(id)
}
function exportToXls(id){
var name = DriveApp.getFileById(id).getName()
var url = 'https://docs.google.com/feeds/';
var doc = UrlFetchApp.fetch(url+'download/spreadsheets/Export?key='+id+'&exportFormat=xls',
googleOAuth_('docs',url)).getBlob()
var xlsfile = DocsList.createFile(doc).rename(name+'.xls')
}
function importXLS(){
var files = DriveApp.searchFiles('title contains ".xls"');
while(files.hasNext()){
var xFile = files.next();
var name = xFile.getName();
if (name.indexOf('.xls')>-1){
var ID = xFile.getId();
var xBlob = xFile.getBlob();
var newFile = { title : name+'_converted',
key : ID
}
file = Drive.Files.insert(newFile, xBlob, {
convert: true
});
}
}
}
I using this code in google script from request to google fusion tables
https://developers.google.com/fusiontables/docs/samples/apps_script
function getGAauthenticationToken(email, password) {
password = encodeURIComponent(password);
var response = UrlFetchApp.fetch("https://www.google.com/accounts/ClientLogin", {
method: "post",
payload: "accountType=GOOGLE&Email=" + email + "&Passwd=" + password + "&service=fusiontables&Source=testing"
});
var responseStr = response.getContentText();
responseStr = responseStr.slice(responseStr.search("Auth=") + 5, responseStr.length);
responseStr = responseStr.replace(/\n/g, "");
return responseStr;
}
function getdata(authToken) {
query = encodeURIComponent("SHOW TABLES");
var URL = "http://www.google.com/fusiontables/api/query?sql=" + query;
var response = UrlFetchApp.fetch(URL, {
method: "get",
headers: {
"Authorization": "GoogleLogin auth=" + authToken,
}
});
return response.getContentText();
}
function test(){
var email = "xyz#gmail.com";
var pass = "xyz";
var token = getGAauthenticationToken(email,pass);
Logger.log(getdata(token));
}
but, how I can make the connection with OAuth 2.0 without user/pass ?
Not oAuth 2.0, Apps Script only supports 1.0 yet. But you can use 1.0 normally with the Fusion Tables API. There's no need for user-pass info. Here's a function I wrote a long time ago to set oAuth om my UrlFetch requests to Google APIs.
/**
* Set up Google's oAuth authentication for UrlFetch
* #retuns {Object} args to be used UrlFetchApp
* #param {string} name oAuth service name, can be anything
* but must not repeat between different scopes
* recommend to use a meaninful name related to the scope
* #param {string} scope google scope for oAuth
*/
function googleOAuth(name,scope) {
var oAuthConfig = UrlFetchApp.addOAuthService(name);
oAuthConfig.setRequestTokenUrl("https://www.google.com/accounts/OAuthGetRequestToken?scope="+scope);
oAuthConfig.setAuthorizationUrl("https://www.google.com/accounts/OAuthAuthorizeToken");
oAuthConfig.setAccessTokenUrl("https://www.google.com/accounts/OAuthGetAccessToken");
oAuthConfig.setConsumerKey('anonymous');
oAuthConfig.setConsumerSecret('anonymous');
return {oAuthServiceName:name, oAuthUseToken:"always"};
}
There's also a notable library now, linked on Google Apps Script site, that will automate even more things to you. Please take a look.