I want to get the Google apps script library details such as version number and note programmatically in a spreadsheet cell.
You can use the projects.versions.list method.
You need to specify the script ID of the library. If you have the link of the library you can find the script id in the url or in the project settings.
If they are not automatically added, you might have to add the scopes manually in the appsscript.json file. E.g. add this inside the brackets:
"oauthScopes": [
"https://www.googleapis.com/auth/script.projects",
"https://www.googleapis.com/auth/script.projects.readonly",
"https://www.googleapis.com/auth/script.external_request"
],
Example:
function getVersion(){
const AccessToken = ScriptApp.getOAuthToken();
const scriptID = "scriptID" // put the script id of the library
const url = "https://script.googleapis.com/v1/projects/" + scriptID + "/versions";
options = {
"method" : "GET",
"muteHttpExceptions": true,
"headers": {
'Authorization': 'Bearer ' + AccessToken
}
};
response = UrlFetchApp.fetch(url,options);
response = JSON.parse(response);
allVersions = response.versions; //get all versions
highestVersion = allVersions[0].versionNumber;
console.log(response);
console.log(highestVersion);
}
Related
I'm going to start by saying it's immensely frustrating half knowing how to do something but never quite being able to finish; this is another one of those projects for me.
Scenario: Using a Google Sheet and Apps Script I am attempting to update several User records in Zendesk using their API.
I think i probably have most if it right (i stand to be corrected of course) with the following script however I just cannot get it to update any records. I suspect it might be to do with how the array is presented (an area I sadly don't know enough about).
function updateManyUsers(){
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet1');
var [headers, ...rows] = sheet.getDataRange().getValues();
var data = {}
var items = []
rows.forEach(function(r) {
var obj={}
r.forEach(function (c, j) {
obj[headers[j]] = c
})
var data = {}//moved
data['users'] = obj // moved this inside your loop
items.push(data) // pushed the object into the items array
})
Logger.log("Log JSON Stringify Items: " + JSON.stringify(items))
items.forEach(function(i) { // added this to loop over objects in items
var url = 'https://itsupportdesk1611575857.zendesk.com/api/v2/users/update_many.json'; //https://developer.zendesk.com/api-reference/ticketing/users/users/#update-user
var user = 'myemailaddresshere/token';
var pwd = 'mytoken';
var options = {
'method' : 'PUT',
'headers': {
'Authorization': "Basic " + Utilities.base64Encode(user + ':' + pwd)
},
'payload' : JSON.stringify(i),
'contentType': 'application/json',
'muteHttpExceptions': true
};
UrlFetchApp.fetch(url, options);
Logger.log(i)
var response = UrlFetchApp.fetch(url, options);
Logger.log(response);
})
}
I've gone through as much as I can following the documentation, I know i had the end points incorrect and the method(?) too (set to Post instead of Push). I have gone through varying error messages that I have tried to act upon and this is my current one:
This is an image of the data in my sheet
Suplimental: In order to get better at this i would like to put myself on a learning path but am unsure what the path is; most of my automation work and scripting is done using Google Apps script so would people recommend a JavaScript course? I alter between that and Python not knowing what would suit me best to get a better understanding of this kind of issue.
Many thanks in advance.
From your endpoint in your script, I thought that you might have wanted to use "Batch update". Ref If my understanding is correct, the following sample curl in the official document can be used. Ref
curl https://{subdomain}.zendesk.com/api/v2/users/update_many.json \
-d '{"users": [{"id": 10071, "name": "New Name", "organization_id": 1}, {"external_id": "123", "verified": true}]}' \
-H "Content-Type: application/json" -X PUT \
-v -u {email_address}:{password}
If this sample curl command is converted to Google Apps Script using your script, how about the following modification?
Modified script:
function updateManyUsers2() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet1');
var [headers, ...rows] = sheet.getDataRange().getDisplayValues();
var users = rows.map(r => {
var temp = {};
headers.forEach((h, j) => {
if (r[j] != "") temp[h] = r[j];
});
return temp;
});
var url = 'https://itsupportdesk1611575857.zendesk.com/api/v2/users/update_many.json';
var user = 'myemailaddresshere/token';
var pwd = 'mytoken';
var options = {
'method': 'PUT',
'headers': {
'Authorization': "Basic " + Utilities.base64Encode(user + ':' + pwd)
},
'payload': JSON.stringify({ users }),
'contentType': 'application/json',
'muteHttpExceptions': true
};
var response = UrlFetchApp.fetch(url, options);
Logger.log(response.getContentText());
}
Note:
From the official document, it says Bulk or batch updates up to 100 users.. So, when you want to use more data, please modify the above script. Please be careful about this.
If an error occurs, please check the values of users, user and pwd, again.
Reference:
fetch(url, params)
I would like to create a custom function that pulls a Drive URL from a file name in Google Sheets.
So, using the code below:
If I have a valid file name in cell A1
The function =getFile(A1) would return the URL
When I run my script from within the script editor, the return value works.
When I run the function getFile() from within my sheet, I get the error below.
My code:
function getFile(cell) {
var filename = encodeURI(cell);
var url = "https://www.googleapis.com/drive/v3/files?fields=files(id,name)&q=name+contains+'" + filename + "' and trashed=false";
var params = {
method: "GET",
headers: {"Authorization": "Bearer " + ScriptApp.getOAuthToken()},
muteHttpExceptions: true
};
var res = UrlFetchApp.fetch(url, params).getContentText();
var json = JSON.parse(res);
return res; // outputs response below
if(json){
var objFiles = json.files[0];
var fileID = objFiles.id
var resURL = "https://docs.google.com/spreadsheets/d/" + fileID;
Logger.log(resURL);
//return resURL; // only works when run within script editor
}
}
Error:
"{
"error": {
"errors": [
{
"domain": "global",
"reason": "authError",
"message": "Invalid Credentials",
"locationType": "header",
"location": "Authorization"
}
],
"code": 401,
"message": "Invalid Credentials"
}
}
"
I'm guessing something's wrong with my Auth token. Can someone direct me to resolving this? Thanks in advance!
Custom functions runs as if run by a anonymous animal(user). ScriptApp.getOAuthToken will return a anonymous token without the required scopes. What you're attempting is not possible, unless the file in question is public.
References:
Custom functions permissions
Custom functions access services
This may be a solution for some needs.
My particular need was: loop through a column of file names and pull the Google Docs URL at a set interval. The code below just loops through filenames in "Column A" of "My Sheet" and returns the value into the adjacent cell of "Column B" (starting at row 2 because I had column headers). I'm not concerned about security because I'm only referencing internal organization files.
To get the code below to work you need to:
Google Sheet Doc Nav > Tools > Script Editor
Create a .gs file & input code below (Referencing The Respective Sheet
Within Script Editor > Edit > Current Project’s Triggers > Name Your Project
Within Script Editor > Edit > Current Project’s Triggers > Click on modal link “No triggers set up. Click here to add one now” > set your time-based trigger (reference replaceFileColumn in the select field within that modal)
My mistake was: thinking that I needed to use a custom function in each cell to do so. (I still don't fully understand the auth reasons why this wouldn't work, so if anyone could explain in lay-man's terms that would be fabulous; my solution is just a workaround for expediency's sake).
In my spreadsheet I have a time-driven trigger calling replaceFileColumn()
Hope this helps someone!
function getMyFile(cell) {
var filename = encodeURI(cell);
var files = DriveApp.getFilesByName(cell);
while (files.hasNext()) {
var file = files.next();
if(file){
var fileValue = file.getUrl();
return(fileValue);
};
};
}
function replaceFileColumn() {
var spreadsheet = SpreadsheetApp.getActive().getSheetByName('My Sheet');
var range = spreadsheet.getRange("A2:A");
var range_update = spreadsheet.getRange("B2:B");
var values = range.getValues();
for (var i = 0; i < values.length; i++) {
var fileName = values[i];
var getFileUrl = getMyFile(fileName);
values[i][0] = getFileUrl;
}
range_update.setValues(values);
}
#I'-'I's answer is correct. Although I'm not sure whether this is what you want, how about this workaround? I have also experienced the same issue. At that time, I had used the following workaround.
Set and get access token using PropertiesService.
The flow is as follows.
Flow:
Set access token every 1 hour by the time-driven trigger.
By this, the access token is updated every 1 hour. Because the expiration time of access token is 1 hour.
When the custom function is run, it gets the access token using PropertiesService.
By this, the access token can be used.
Modified script:
Please install this function as the time-driven trigger. Of course, you can run manually this function.
function setAccessToken() {
PropertiesService.getScriptProperties().setProperty("accessToken", ScriptApp.getOAuthToken());
}
In your script, please modify as follows.
From:
var params = {
method: "GET",
headers: {"Authorization": "Bearer " + ScriptApp.getOAuthToken()},
muteHttpExceptions: true
};
To:
var params = {
method: "GET",
headers: {"Authorization": "Bearer " + PropertiesService.getScriptProperties().getProperty("accessToken")},
muteHttpExceptions: true
};
Note:
In this case, the owner of access token is the owner of project.
I think that this can be also used by CacheService.
Reference:
PropertiesService
I need to change the permission of every uploaded file. But when I try to add this code,
printPermissionIdForEmail(email) {
var request = gapi.client.drive.permissions.getIdForEmail({
'email': email,
});
request.execute(function(resp) {
return ('ID: ' + resp.id);
});
}
I got an error of getIdForEmail is not a function.
gapi.client.init, gapi.auth2.getAuthInstance(),
are working. But why gapi.client.drive.permissions.getIdForEmail is not working? There is something I need to do? in Google Developers Page? in my Code?
getIdForEmail is a method only available in Google Drive v2.
With V3 you are going to have to go after it in another manner.
Do a files.list with the q parameter. In the q parameter supply the user whos permissions you wish to change. You can see here how to use search This would find all the files where someuser is the owner.
'someuser#gmail.com' in owners
Then you will get a list of file resources you can then check the permissions on each file using permissions.list and use that to change the ones you need.
I am not a JavaScript developer but I found this in the documentation it shows how to use search to list files.
/**
* Print files.
*/
function listFiles() {
gapi.client.drive.files.list({
'q': "'someuser#gmail.com' in owners",
'fields': "*"
}).then(function(response) {
appendPre('Files:');
var files = response.result.files;
if (files && files.length > 0) {
for (var i = 0; i < files.length; i++) {
var file = files[i];
appendPre(file.name + ' (' + file.id + ')');
}
} else {
appendPre('No files found.');
}
});
}
Update:
I just spotted this. About.get Gets information about the user, the user's Drive, and system capabilities
{
"user": {
"kind": "drive#user",
"displayName": "Linda Lawton",
"photoLink": "xxxx",
"me": true,
"permissionId": "060305882255734372",
"emailAddress": "xxxx#gmail.com"
}
}
Could that be the same permissionId you were looking for?
The method I use is based on the OAuth2 library published on script.google.com. This is written for Google Apps Script with domain-wide delegation. The key here is building a valid url and option for UrlFetchApp.fetch(url, options), then parsing the result to find the ID number.
function getIdForEmailv3(userEmail) {
var service = getService(userEmail);
if (service.hasAccess()) {
Logger.log('getIdForEmailv3(%s) has access', userEmail);
var url = 'https://www.googleapis.com/drive/v3/about' + '?fields=user/permissionId'
var options = {
'method': 'get',
'contentType': 'application/json',
'headers': { Authorization: 'Bearer ' + service.getAccessToken() },
'muteHttpExceptions': true
};
var response = UrlFetchApp.fetch(url, options);
var resultString = JSON.stringify(response.getContentText());
var regex = new RegExp(/\d+/g);
var id = regex.exec(resultString)[0];
Logger.log('getIdForEmailv3 returned %s for %s', id, userEmail);
return id
} else {
Logger.log('getIdForEmailv3 getLastError: %s', service.getLastError());
Logger.log('getIdForEmailv3 returned %s for %s', 0, userEmail);
return 0;
}
}
Regex idea from: Easiest way to get file ID from URL on Google Apps Script
Fields format from comment on solution: How to add 'field' property to a Google Drive API v3 call in JavaScript?
I don't know my JavaScript. So I am using someone else's:
var CONSUMER_KEY = 'xxxx'; // Register your app with Twitter.
var CONSUMER_SECRET = 'xxxx'; // Register your app with Twitter.
function getTwitterUserFollowers(id) {
// Encode consumer key and secret
var tokenUrl = "https://api.twitter.com/oauth2/token";
var tokenCredential = Utilities.base64EncodeWebSafe(
CONSUMER_KEY + ":" + CONSUMER_SECRET);
// Obtain a bearer token with HTTP POST request
var tokenOptions = {
headers: {
Authorization: "Basic " + tokenCredential,
"Content-Type": "application/x-www-form-urlencoded;charset=UTF-8"
},
method: "post",
payload: "grant_type=client_credentials"
};
var responseToken = UrlFetchApp.fetch(tokenUrl, tokenOptions);
var parsedToken = JSON.parse(responseToken);
var token = parsedToken.access_token;
// Authenticate Twitter API requests with the bearer token
var apiUrl = 'https://api.twitter.com/1.1/users/show.json?screen_name='+id;
var apiOptions = {
headers: {
Authorization: 'Bearer ' + token
},
"method": "get"
};
var responseApi = UrlFetchApp.fetch(apiUrl, apiOptions);
var result = "";
if (responseApi.getResponseCode() == 200) {
// Parse the JSON encoded Twitter API response
var tweets = JSON.parse(responseApi.getContentText());
return tweets.followers_count
}
Logger.log(result);
}
source: http://sarahmarshall.io/post/70812214349/how-to-add-twitter-follower-counts-to-a-google
Sarah Marshall provides a pretty awesome how-to on using a Google Sheets script with the Twitter API to get follower counts for a list of Twitter user names. But it times out after about 100 user names:
Service invoked too many times for one day: urlfetch. (line 21, file "Code")
I'm wondering how I can get around the rate limit, or account for it, and return follower counts for more than 100 user names. Any ideas?
The function you are using does too many things: it both obtains a token and uses it to retrieve followers. So, if you are invoking it 100 times, you obtain a token 100 times (and notably, time out on the line obtaining it); but you need it once. You should store the token somewhere, e.g., in the spreadsheet itself since you already have your private data in the script associated with it. Example:
function getToken() {
// Obtain a bearer token with HTTP POST request
var tokenOptions = {
headers: {
Authorization: "Basic " + tokenCredential,
"Content-Type": "application/x-www-form-urlencoded;charset=UTF-8"
},
method: "post",
payload: "grant_type=client_credentials"
};
var responseToken = UrlFetchApp.fetch(tokenUrl, tokenOptions);
var parsedToken = JSON.parse(responseToken);
var token = parsedToken.access_token;
SpreadsheetApp.getActiveSpreadsheet().getSheetByName('SheetWithToken').getRange('A1').setValue(token);
}
The last line stores the token string in cell A1 of the sheet named SheetWithToken. You'd invoke this function once, manually from the Script Editor.
The following function does the rest: it can be invoked as a custom function =getFollowers(A2) from the spreadsheet. Custom functions, like other spreadsheet functions, are re-evaluated only when the parameter changes. Thus, if the column with IDs (say, A) has a thousand of entries, you can paste the custom function gradually, for a handful at once.
function getFollowers(id) {
// Authenticate Twitter API requests with the bearer token
var token = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('SheetWithToken').getRange('A1').getValue();
var apiUrl = 'https://api.twitter.com/1.1/users/show.json?screen_name='+id;
var apiOptions = {
headers: {
Authorization: 'Bearer ' + token
},
"method": "get"
};
var responseApi = UrlFetchApp.fetch(apiUrl, apiOptions);
var result = "";
if (responseApi.getResponseCode() == 200) {
// Parse the JSON encoded Twitter API response
var tweets = JSON.parse(responseApi.getContentText());
return tweets.followers_count
}
}
You could even hardcode your access token into the second function, instead of fetching it from the spreadsheet. Twitter's tokens do not expire.
Another thing to consider is to recast the function getFollowers so that it accepts an array of IDs and loops through them, returning an array of follower counts. I don't think this would help, though: you would still have to worry about rate-limit on Twitter side, and at the same time be limited to 30 second execution time limit for custom functions.
I am developing a Google App Script to determine the size of a remote resource without downloading it. The code is as follows
function getRemoteFileSize()
{
var params = { "method" : "head" };
var resp = UrlFetchApp.fetch("https://www.google.com/images/srpr/logo11w.png", params);
Logger.log("Remote File Size: " + resp.getAllHeaders()["Content-Length"]);
}
However, Google App Script does not seem to support head requests and the code above cannot be executed.
What can be a viable alternative other than issuing a GET request ?
I am open to all suggestions including usage of a third-party service which has an API
You can try to override the method by setting the "headers" advanced parameter:
var params = {"method" : "GET", "headers": {"X-HTTP-Method-Override": "HEAD"}};
I've never used "HEAD", so I can't tell you for sure that this will work, but I have used the method override for "PATCH", and had that work.
I found that for my circumstance, I was able to use the Range header to request 0 bytes and then inspect the response headers which held the file size:
var params = {
method: "GET",
headers: {
Range: "bytes=0-0",
},
};
var response = UrlFetchApp.fetch(fileUrl,params);
var headers = response.getHeaders();
var fileSizeString = headers['Content-Range']
var fileSize = +headers['Content-Range'].split("/")[1];
The response headers had Content-Range=bytes 0-0/139046553 as a value that I could then use to convert to an integer (139046553) number of bytes.