UrlFetchApp query a spreadsheet with gviz - google-apps-script

I am trying to query a spreadsheet with gviz (Google Visualization), using UrlFetchApp, but no result so far.
Could you help me to fix this code?
(the query Url works fine in the browser)
function queryTest() {
var onlyforscope = SpreadsheetApp.getActiveSpreadsheet();
var template = "https://docs.google.com/spreadsheets/d/%s/gviz/tq?gid=%s&tq=select C,E,K,M,N,O where C contains '%s'";
var query = Utilities.formatString(template, docId, sheetId, value);
var param = {
method : "get",
headers : {"Authorization": "Bearer " + ScriptApp.getOAuthToken()},
muteHttpExceptions:true,
};
var r = UrlFetchApp.fetch(query, param).getContentText();
// var j = JSON.parse(r);
Logger.log(r);
return;
}
Thanks in advance, Fausto

it was trivial, though hard to find out for me
the required scope is Drive !!!
I just add this line and it worked
var onlyforscope = DriveApp.getRootFolder();

Related

Why is this Web App showing an error "Script function not found: events"?

The user clicks on a button, which has main() assigned to it.
It runs with no errors, but the protections are not removed or applied as it seems to get stuck because of the error mentioned in the question.
Here are the functions borrowed and adapted from Stackoverflow:
// This is the main function. Please set this function to the run button on Spreadsheet.
function main() {
//DriveApp.getFiles(); // This is a dummy method for detecting a scope by the script editor.
const activeSheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
const sheetName = activeSheet.getName();
if (sheetName === 'Itens_para_Costurar') {
var token = ScriptApp.getOAuthToken()
var url = ScriptApp.getService().getUrl();
var options = {
'method': 'post',
'headers': { 'Authorization': 'Bearer ' + token },
muteHttpExceptions: true
};
UrlFetchApp.fetch(url + "&key=removeprotectListaCortadas", options); // Remove protected range
listaPecasCortadas();//Atualiza a lista de peças cortadas
SpreadsheetApp.flush(); // This is required to be here.
UrlFetchApp.fetch(url + "&key=addprotectListaCortadas", options); // Add protected range
}
}
function doGet(e) {
if (e.parameter.key == "removeprotectListaCortadas") {
var listaPecasCortadasSht = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Itens_para_Costurar'); // Please set here.
// Remove protected range.
var protections = listaPecasCortadasSht.getProtections(SpreadsheetApp.ProtectionType.RANGE);
for (var i = 0; i < protections.length; i++) {
console.log('Protection Name: ' + protections[i].getDescription())
protections[i].remove();
}
} else {
// Add protected range.
var ownersEmail = Session.getActiveUser().getEmail();
var protection = listaPecasCortadasSht.getRange('A8:J100').protect();
var editors = protection.getEditors();
for (var i = 0; i < editors.length; i++) {
var email = editors[i].getEmail();
if (email != ownersEmail) protection.removeEditor(email);
}
}
return ContentService.createTextOutput("ok");
}
I would appreciate any light towards the solution.
Modification points:
If you were using my answer, when I posted it, ScriptApp.getService().getUrl() returned the endpoint of the developer mode. But, in the current stage, it doesn't return the endpoint of the developer mode. And, in this case, the invalid endpoint is returned. Ref
In your request body, 'method': 'post', is used. But, in your script, doGet is used. I thought that this might be the reason for your current issue.
In your script, var url = ScriptApp.getService().getUrl(); is used. But, using url, you are using UrlFetchApp.fetch(url + "&key=addprotectListaCortadas", options);. In this case, the query parameter is not correct.
When these points are reflected in your script, how about the following modification?
From:
var token = ScriptApp.getOAuthToken()
var url = ScriptApp.getService().getUrl();
var options = {
'method': 'post',
'headers': { 'Authorization': 'Bearer ' + token },
muteHttpExceptions: true
};
UrlFetchApp.fetch(url + "&key=removeprotectListaCortadas", options); // Remove protected range
To:
var url = "###"; // Please manually set your Web Apps URL here.
var options = { muteHttpExceptions: true };
UrlFetchApp.fetch(url + "?key=removeprotectListaCortadas", options);
If your Web Apps is deployed as Execute the app as: Me and Who has access to the app: Anyone, when the Web Apps URL is used, the access token is not required to be used. If you want to use the developer mode, please use the access token.
Note:
This answer is for your current issue. So, I cannot check your all script. Please be careful about this.
When you modified the Google Apps Script of Web Apps, please modify the deployment as a new version. By this, the modified script is reflected in Web Apps. Please be careful about this.
You can see the detail of this in my report "Redeploying Web Apps without Changing URL of Web Apps for new IDE (Author: me)".

How to escape "/" character in Google Script

I'm trying to bring Wordpress data to Google Sheets.
The script below worked for almost all columns but I can't bring the emails and if I try to scape the "-" my script won't run.
function getPage(offset,per_page) {
// gets posts in chunks of per_page
var ui = SpreadsheetApp.getUi(); // used for error messages
var ss = SpreadsheetApp.getActiveSpreadsheet();
var options = {
'method': 'get',
"contentType" : "application/json",
'muteHttpExceptions': true
}
var apiHost = 'https://domain.com.br/wp-json'; // set to your own domain
url = apiHost + '/acf/v3/cadastros?per_page='+per_page+'&offset='+offset;
try {
var response = UrlFetchApp.fetch(url, options);
var data = JSON.parse(response)
// loop through the map and output to sheet
for (i = 0; i < data.length; i++) {
row=offset+i+2; // set the row to make sure it is below header and takes into account the paging
ss.getRange('A'+row).setValue(data[i].id);
ss.getRange('B'+row).setValue(data[i].acf.contato);
ss.getRange('C'+row).setValue(data[i].acf.e-mail); //this line is breaking everything I've tried to use "\", put quotes but nothing seems to work
ss.getRange('D'+row).setValue(data[i].acf.telefone);
ss.getRange('E'+row).setValue(data[i].acf.cnpj);
ss.getRange('F'+row).setValue(data[i].acf.endereco);
ss.getRange('G'+row).setValue(data[i].acf.principais_produtos);
ss.getRange('H'+row).setValue(data[i].acf.volume_disponivel);
ss.getRange('I'+row).setValue(data[i].acf.estoque_disponivel);
ss.getRange('J'+row).setValue(data[i].acf.aceite);
}
return data.length;
} catch(error) {
var result = ui.alert( error.toString());
}
return 0;
}
How do I fix this?
Quick and easy will be to replace data[i].acf.e-mail with data[i]['acf']['e-mail']

Google app script Error could not parse text

I am trying to retrieve data by ID. Use the 3rd method in this link: How to speed ​up the search data in sheet
I run the function and err : Could not parse text.
I do not understand why I have used this method so many times and ran well, but this case is faulty.
This is my code:
function loadDataOfThread() {
var ss = SpreadsheetApp.openByUrl(url);
var ws = ss.getSheetByName("sheet1");
var ID = "12345";
var formatRange = ws.getRange(1, 1, ws.getLastRow() ,ws.getLastColumn()).setNumberFormat("#STRING#");
var query = "select * where A ='" + ID + "'";
var url = "https://docs.google.com/spreadsheets/d/" + ss.getId() + "/gviz/tq?gid=" + ws.getSheetId() + "&tqx=out:csv&tq=" + encodeURIComponent(query);
var options = {
headers: {
'Authorization': 'Bearer ' + ScriptApp.getOAuthToken()
}
};
var csv = UrlFetchApp.fetch(url, options);
var f = Utilities.parseCsv(csv); // err this line
var dataArr = [];
if (f.length > 0) {
for (var i = 0; i < f.length; i++) {
dataArr.push(f[i][1]);
}
}
}
I think in a spreadsheet whose data type is the date time column and it make err my function but i have convert to string !!! I do not understand why ?
How about this answer? Your issue might be able to be removed with "PasteDataRequest" because "PasteDataRequest" is better than parseCsv() as the parser of CSV data. In this answer, I would like to propose a method for using "PasteDataRequest" of Sheets API. Please think of this as just one of several answers. The flow of this method is as follows.
Insert a sheet as a temporal sheet.
Put the CSV data to the inserted sheet using "PasteDataRequest" of Sheets API.
Retrieve values from the temporal sheet.
Delete the temporal sheet.
Modified script:
When your script is modified, please modify as follows.
Before you use this script, please enable Sheets API at Advanced Google services.
From:
var f = Utilities.parseCsv(csv);
To:
var temp = ss.insertSheet("temp");
var sheetId = temp.getSheetId();
var resource = {requests: [{pasteData: {data: csv.getContentText(), coordinate: {sheetId: sheetId}, delimiter: ","}}]};
Sheets.Spreadsheets.batchUpdate(resource, ss.getId());
var f = temp.getDataRange().getValues();
ss.deleteSheet(temp);
Note:
Of course, I think that the issue can be also removed by modifying csv of var csv = UrlFetchApp.fetch(url, options);. But from your question, I cannot image the values of your issue. So I proposed above method. If you want to use other method, can you provide a sample Spreadsheet for replicating your issue? Of course, please remove your personal information. By this, I would like to think of the issue.
References:
Method: spreadsheets.batchUpdate
PasteDataRequest
If I misunderstood your question and this was not the direction you want, I apologize.

How to use Google Photos API Method: mediaItems.search in Google apps script for a spreadsheet

I really tried to figure this out on my own...
I am trying to load photo metadata from google photos into a sheet using the Google Photos API and google apps script.
I was able to make some progress after a lot of help on a previous question
Is it possible to load google photos metadata into google sheets?
I now have two functions.
function photoAPI_ListPhotos() - Uses Method: mediaItems.list and gives me all my photos that are not archived
function photoAPI_ListAlbums() - Uses Method: albums.list and gives me all my albums
What I want to do is retrieve all photos from a specific album. Method: mediaItems.search should do this but it uses the POST protocol and the previous working examples I found only use GET. Looking at the examples available on that page, there is a javascript portion but it does not work in apps script.
The documentation for UrlFetchApp tells me how to format a POST request but not how to add the parameters for authentication.
The external APIs also is not giving me the examples I am looking for.
I feel like I'm missing some essential tiny piece of info and I hope I'm not wasting everyone's time asking it here. Just a solid example of how to use POST with oauth in apps script should get me where I need to go.
Here is my working function for listing all non-archived photos.
function photoAPI_ListPhotos() {
/*
This function retrieves all photos from your personal google photos account and lists each one with the Filename, Caption, Create time (formatted for Sheet), Width, Height, and URL in a new sheet.
it will not include archived photos which can be confusing if you happen to have a large chunk of archived photos some pages may return only a next page token with no media items.
Requires Oauth scopes. Add the below line to appsscript.json
"oauthScopes": ["https://www.googleapis.com/auth/spreadsheets.currentonly", "https://www.googleapis.com/auth/photoslibrary", "https://www.googleapis.com/auth/photoslibrary.readonly", "https://www.googleapis.com/auth/script.external_request"]
Also requires a standard GCP project with the appropriate Photo APIs enabled.
https://developers.google.com/apps-script/guides/cloud-platform-projects
*/
//Get the spreadsheet object
var ss = SpreadsheetApp.getActiveSpreadsheet();
//Check for presence of target sheet, if it does not exist, create one.
var photos_sh = ss.getSheetByName("photos") || ss.insertSheet("photos", ss.getSheets().length);
//Make sure the target sheet is empty
photos_sh.clear();
var narray = [];
//Build the request string. Max page size is 100. set to max for speed.
var api = "https://photoslibrary.googleapis.com/v1/mediaItems?pageSize=100";
var headers = { "Authorization": "Bearer " + ScriptApp.getOAuthToken() };
var options = { "headers": headers, "method" : "GET", "muteHttpExceptions": true };
//This variable is used if you want to resume the scrape at some page other than the start. This is needed if you have more than 40,000 photos.
//Uncomment the line below and add the next page token for where you want to start in the quotes.
//var nexttoken="";
var param= "", nexttoken;
//Start counting how many pages have been processed.
var pagecount=0;
//Make the first row a title row
var data = [
"Filename",
"description",
"Create Time",
"Width",
"Height",
"ID",
"URL",
"NextPage"
];
narray.push(data);
//Loop through JSON results until a nextPageToken is not returned indicating end of data
do {
//If there is a nextpagetoken, add it to the end of the request string
if (nexttoken)
param = "&pageToken=" + nexttoken;
//Get data and load it into a JSON object
var response = UrlFetchApp.fetch(api + param, options);
var json = JSON.parse(response.getContentText());
//Check if there are mediaItems to process.
if (typeof json.mediaItems === 'undefined') {
//If there are no mediaItems, Add a blank line in the sheet with the returned nextpagetoken
//var data = ["","","","","","","",json.nextPageToken];
//narray.push(data);
} else {
//Loop through the JSON object adding desired data to the spreadsheet.
json.mediaItems.forEach(function (MediaItem) {
//Check if the mediaitem has a description (caption) and make that cell blank if it is not present.
if(typeof MediaItem.description === 'undefined') {
var description = "";
} else {
var description = MediaItem.description;
}
//Format the create date as appropriate for spreadsheets.
var d = new Date(MediaItem.mediaMetadata.creationTime);
var data = [
MediaItem.filename,
"'"+description, //The prepended apostrophe makes captions that are dates or numbers save in the sheet as a string.
d,
MediaItem.mediaMetadata.width,
MediaItem.mediaMetadata.height,
MediaItem.id,
MediaItem.productUrl,
json.nextPageToken
];
narray.push(data);
});
}
//Get the nextPageToken
nexttoken = json.nextPageToken;
pagecount++;
//Continue if the nextPageToaken is not null
//Also stop if you reach 400 pages processed, this prevents the script from timing out. You will need to resume manually using the nexttoken variable above.
} while (pagecount<4 && nexttoken);
//Continue if the nextPageToaken is not null (This is commented out as an alternative and can be used if you have a small enough collection it will not time out.)
//} while (nexttoken);
//Save all the data to the spreadsheet.
photos_sh.getRange(1, 1, narray.length, narray[0].length).setValues(narray);
}
You want to retrieve all photos of the specific album using Google Photo API.
You want to know how to use the method of mediaItems.search using Google Apps Script.
You have already been able to retrieve the data using Google Photo API.
If my understanding is correct, how about this sample script? Please think of this as just one of several answers.
Sample script 1:
var albumId = "###"; // Please set the album ID.
var headers = {"Authorization": "Bearer " + ScriptApp.getOAuthToken()};
var url = "https://photoslibrary.googleapis.com/v1/mediaItems:search";
var mediaItems = [];
var pageToken = "";
do {
var params = {
method: "post",
headers: headers,
contentType: "application/json",
payload: JSON.stringify({albumId: albumId, pageSize: 100, pageToken: pageToken}),
}
var res = UrlFetchApp.fetch(url, params);
var obj = JSON.parse(res.getContentText());
Array.prototype.push.apply(mediaItems, obj.mediaItems);
pageToken = obj.nextPageToken || "";
} while (pageToken);
Logger.log(mediaItems)
At the method of mediaItems.search, albumId, pageSize and pageToken are included in the payload, and the values are sent as the content type of application/json.
Sample script 2:
When your script is modified, how about the following modified script?
function photoAPI_ListPhotos() {
var albumId = "###"; // Please set the album ID.
var ss = SpreadsheetApp.getActiveSpreadsheet();
var photos_sh = ss.getSheetByName("photos") || ss.insertSheet("photos", ss.getSheets().length);
photos_sh.clear();
var narray = [];
var api = "https://photoslibrary.googleapis.com/v1/mediaItems:search";
var headers = { "Authorization": "Bearer " + ScriptApp.getOAuthToken() };
var nexttoken = "";
var pagecount = 0;
var data = ["Filename","description","Create Time","Width","Height","ID","URL","NextPage"];
narray.push(data);
do {
var options = {
method: "post",
headers: headers,
contentType: "application/json",
payload: JSON.stringify({albumId: albumId, pageSize: 100, pageToken: nexttoken}),
}
var response = UrlFetchApp.fetch(api, options);
var json = JSON.parse(response.getContentText());
if (typeof json.mediaItems === 'undefined') {
//If there are no mediaItems, Add a blank line in the sheet with the returned nextpagetoken
//var data = ["","","","","","","",json.nextPageToken];
//narray.push(data);
} else {
json.mediaItems.forEach(function (MediaItem) {
if(typeof MediaItem.description === 'undefined') {
var description = "";
} else {
var description = MediaItem.description;
}
var d = new Date(MediaItem.mediaMetadata.creationTime);
var data = [
MediaItem.filename,
"'"+description,
d,
MediaItem.mediaMetadata.width,
MediaItem.mediaMetadata.height,
MediaItem.id,
MediaItem.productUrl,
json.nextPageToken
];
narray.push(data);
});
}
nexttoken = json.nextPageToken || "";
pagecount++;
} while (pagecount<4 && nexttoken);
photos_sh.getRange(1, 1, narray.length, narray[0].length).setValues(narray);
}
Note:
This script supposes as follows.
Google Photo API is enabed.
The scope of https://www.googleapis.com/auth/photoslibrary.readonly or https://www.googleapis.com/auth/photoslibrary are included in the scopes.
Reference:
Method: mediaItems.search
If I misunderstood your question and this was not the result you want, I apologize.

Spotify API authorisation via Google Apps Script

I am using the following code to make requests to the Spotify API via Google Apps Script:
function search() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var artist = sheet.getRange(1,1).getValue();
artist = encodeURIComponent(artist.trim());
var result = searchSpotify(artist);
Logger.log(result);
}
function searchSpotify(artist) {
//searches spotify and returns artist ID
var response = UrlFetchApp.fetch("https://api.spotify.com/v1/search?q=" + artist + "&type=artist&limit=1",
{ method: "GET",
headers:{
"contentType": "application/json",
'Authorization': "Bearer BQBnpSUdaEweirImw23yh2DH8OGhTwh5a_VnY_fgb2BPML0KvFvYd04CaEdUhQN9N4ZUXMIVfJ1MjFe1_j0Gl0UoHDhcoC_dklluZyOkq8Bo6i2_wfxSbGzP3k5EUjUKuULAnmTwCdkdZQnl-SNU0Co"
},
});
json = response.getContentText();
var data = JSON.parse(json);
var uri = data.artists.items[0].uri.slice(15);
var getArtists = getRelatedArtists(uri);
Logger.log(getArtists);
return getArtists;
}
function getRelatedArtists(uri) {
//searches related artists with the returned ID
var response = UrlFetchApp.fetch("https://api.spotify.com/v1/artists/" + uri + "/related-artists",
{ method: "GET",
headers:{
"contentType": "application/json",
'Authorization': "Bearer BQBnpSUdaEweirImw23yh2DH8OGhTwh5a_VnY_fgb2BPML0KvFvYd04CaEdUhQN9N4ZUXMIVfJ1MjFe1_j0Gl0UoHDhcoC_dklluZyOkq8Bo6i2_wfxSbGzP3k5EUjUKuULAnmTwCdkdZQnl-SNU0Co"
},
});
json = response.getContentText();
var data = JSON.parse(json);
var listArtists = [];
for(var i = 0, len = data.artists.length; i < len; i++){
listArtists.push(data.artists[i].name);
}
return listArtists;
}
This works fine using the temporary Authorisation token from the Spotify website but this token refreshes every hour and so is obviously useless.
I am trying to use my own Authorisation token and ID which I have setup on Spotify however I'm struggling to make this work. As I understand it I may need to add an extra step at the beginning to start the authorisation process but I've tried all methods suggested but keep receiving server errors.
From the document, it seems that "Client Credentials Flow" uses the basic authorization.
In order to use this, at first, you are required to retrieve "client_id" and "client_secret".
Sample script:
var clientId = "### client id ###"; // Please set here.
var clientSecret = "### client secret ###"; // Please set here.
var url = "https://accounts.spotify.com/api/token";
var params = {
method: "post",
headers: {"Authorization" : "Basic " + Utilities.base64Encode(clientId + ":" + clientSecret)},
payload: {grant_type: "client_credentials"},
};
var res = UrlFetchApp.fetch(url, params);
Logger.log(res.getContentText())
From curl sample, grant_type is required to send as form.
Result:
The document says that the response is as follows.
{
"access_token": "NgCXRKc...MzYjw",
"token_type": "bearer",
"expires_in": 3600,
}
Note:
This is a simple sample script. So please modify this for your situation.
I prepared this sample script by the sample curl in the document.
Reference:
Client Credentials Flow
Edit:
As your next issue, you want to retrieve the access token from the returned value. If my understanding is correct, how about this modification? Please modify my script as follows.
From:
Logger.log(res.getContentText())
To:
var obj = JSON.parse(res.getContentText());
Logger.log(obj.access_token)
When the value is returned from API, it returns as a string. So it is required to parse it as an object using JSON.parse().