The coordinates or dimensions of the range are invalid - google-apps-script

No matter what i do, i get the error in the title. If i replace my script with the standard
function doGet(e) {
var params = JSON.stringify(e);
return HtmlService.createHtmlOutput(params);
}
from Googles very own example: https://developers.google.com/apps-script/guides/web#url_parameters i have to run it once (inside the script editing thingy) to not show the error anymore, but when i put my code back in place it stays at the output of that dummy function. Probably because i cannot run my own function without errors (see below).
Settings for the web-app deployment:
version: 1
execute as: me
access: anyone, even anonymous
I think it has something to do with having to run a function once (or atleast that's what it looks like to me), but i cannot run my function since it's relying on url parameters. This already fails at e.parameter == undefined obviously.
function doGet(e) {
Logger.log(JSON.stringify(e));
var result = 'Ok';
if(e.parameter == undefined) {
result = 'no param';
}
else {
var id = 'id is normally here obviously not now';
var sheet = SpreadsheetApp.openById(id).getActiveSheet();
var newRow = sheet.getLastRow()+1;
var rowData = [];
for (var param in e.parameter) {
var value = stripQuotes(e.parameter[param]);
Logger.log(param + ': ' + e.parameter[param]);
switch (param) {
case 'timeStampBegin': //Parameter
rowData[0] = value; //Value in column B
break;
case 'timeStampEnd':
rowData[1] = value;
break;
default:
result = 'unsupported parameter';
}
}
Logger.log(JSON.stringify(rowData));
var newRange = sheet.getRange(newRow, 1, 1, rowData.length);
newRange.setValues([rowData]);
}
return ContentService.createTextOutput(result);
}
function stripQuotes( value ) {
return value.replace(/^["']|['"]$/g, "");
}
I appreciate any suggestions why this could be happening or how i could run my function with a test input.

You do need to run your script once in the IDE after you add any new services (e.g. HTML service, spreadsheet service, etc.) to approve those changes from a security standpoint.
To test your code without making tons of new versions and deploying each one (i.e. steps 2-3), you can either run it in the web IDE, or navigate to "Publish" > "Deploy as web app..." then click on "Test web app for your latest code." Unlike the live version, that link uses whatever code is currently saved, not the last deployed version.

I don't know of this is intentional by Google, but this is kinda ridiculous. Apparently a function has to be run once in the editor to be able to used. But here it is anyway:
Make an empty function which generates the needed input for your functions that you need to run, and call each. If they call each other, you only need to call the "parent"-function.
Make a new version under File->Manage versions.
Deploy your app again. Select the new version in the process.
I'm not sure 2+3 are really necessary, but for me it still ran the old dummy code (print back json) without doing that.

Related

If statement that does nothing if the first condition is not met

I'm busy writing a script that includes an If statement and I'm trying to make the else part of the statement do nothing if the first condition isn't met. The script checks a google drive folder for a file name and if it is present the first condition should run, else if it isn't present it should do nothing.
I've tried using else{ return false;} , else{ } and none are working. I've also tried swapping the conditions around but that didn't work either
I keep getting an error saying "Exception: Cannot retrieve the next object: iterator has reached the end. (line 7, file "calls")" because the file is deleted once the data is retrieved from it.
The first condition works perfectly but if the csv file isn't present in the folder the above message is displayed. The idea is that I set a trigger that runs every minute to check the folder.
Below is what I have so far. Any help would be greatly appreciated.
function calls(){
var hotFolder = DriveApp.getFolderById('idHere');
var targetSheet = SpreadsheetApp.openById('idHere').getSheetByName('sheetNameHere');
var callsSheet = SpreadsheetApp.openById('idHere').getSheetByName('sheetNameHere');
var callsCsv = hotFolder.getFilesByName('fileName.csv.Here').next();
if(callsCsv) {
var csvData = Utilities.parseCsv(callsCsv.getBlob().getDataAsString());
targetSheet.getRange(1,1,csvData.length,csvData[0].length).setValues(csvData);
callsCsv.setTrashed(true);
var targetSheetData = targetSheet.getRange(2,1,targetSheet.getLastRow()-1,targetSheet.getLastColumn()).getValues();
var handleTime = targetSheetData.map(function(row){
if([row[9]] != 'ABANDON') {
return [row[6] + row[7] +15];
} else {
return [['']];
}
});
targetSheet.getRange(2,12,handleTime.length).setValues(handleTime);
var newData = targetSheet.getRange(2,1,targetSheet.getLastRow(),targetSheet.getLastColumn()).getValues();
callsSheet.getRange(callsSheet.getLastRow,1,newData.length,newData[0].length).setValues(newData);
targetSheet.clear();
} else {
}
}
The "Exception: Cannot retrieve the next object: iterator has reached the end. (line 7, file "calls")" error message you are receiving is due to the fact that the it's not possible to get the next item in the collection of files or folders - probably because of the deletion from the previous run.
What you can do in this situation is to use the hasNext() method. According to its documentation :
hasNext() > Determines whether calling next() will return an item.
Return
Boolean — true if next() will return an item; false if not
Therefore, you can change your code to this:
var callsCsv = hotFolder.getFilesByName('fileName.csv.Here');
if (callsCsv.hasNext()) {
var callsCsvFile = callsCsv.next();
// your code here - callsCsv becomes callsCsvFile
}
Reference
Apps Script Class FileIterator.

Issue running an Installed Trigger in Google App Script

Fairly new to app script so bare with me.
Wrote this massive script, then went to set it up on a times trigger and it just refuses to run. I've gone ahead an back tracked as much as I could, to get at least something to work, yet I can't even get a basic toast to appear on a minute interval.
This is the script I've built, which I'm running directly to enable the trigger:
function createTriggers() {
ScriptApp.newTrigger('testTime')
.timeBased()
.everyMinutes(1)
.create();
};
The function it's calling is super simple, I've used it a lot and change it a lot too:
var gSS = SpreadsheetApp.openById("this_1s/the.1d")
function testTime() {
var d = new Date()
var Start = d.getTime();
gSS.toast(Start, "Testing", 30)
};
So how it should work, and it does if I just call the 'testTime' function directly, is a little toast pop-up appears on the spreadsheet in question, and stays visible for 30s.
When I run the trigger function 'createTriggers', nothing happens..
Please help! All the code I wrote is for nothing if I can't get it to run on its own.. :(
***** EDIT - 08/04/20 - based on comments *****
It's possible this was an XY example, I tried to run a small segment of the original code which works when I run it directly, and its not working here either.. this snippit does not have any UI facing functions in it, so it shouldn't be the issue..
All i did was take the above trigger function and change the name to 'testClear', which calls to the following functions:
function testClear(){
sheetVars(1)
clearSheetData(sheetSPChange)
};
function sheetVars(numSprints) {
// returns the global vars for this script
try {
sheetNameSprints = "Name of Sprint Sheet"
sheetNameSPChange = "Name of Story Point Change Sheet"
sheetSprints = gSS.getSheetByName(sheetNameSprints)
sheetSPChange = gSS.getSheetByName(sheetNameSPChange)
arraySprints = iterateColumn(sheetSprints,"sprintIDSorted", 1, numSprints)
}
catch(err) {
Logger.log(err)
};
};
function iterateColumn(sheet, header, columnNum, numRows) {
// Create an array of first column values to iterate through
// numRows is an int, except for the string "all"
var gData = sheet.getDataRange();
var gVals = gData.getValues();
var gLastR = ""
var gArray = []
// check how many rows to iterate
if (numRows == "all") {
gLastR = gData.getLastRow();
}
else {
gLastR = numRows
};
// Iterate through each row of columnNum column
for (i = 1; i < gLastR; i++){
// iterate through
if(gVals[i][columnNum] !== "" && gVals[i][columnNum] !== header){
// push to array
gArray.push(gVals[i][columnNum]);
}
else if (gVals[i][columnNum] == "") {
break
};
};
return gArray
};
function clearSheetData(sheet) {
// Delete all rows with data in them in a sheet
try {
if (!sheet.getRange(sheet.getLastRow(),1).isBlank()){
sheet.getRange(2, 1, sheet.getLastRow()-1, sheet.getLastColumn()-1).clearContent()
Logger.log("Sheet cleared from old data.")
}
else {
sheet.deleteRows(2, sheet.getLastRow()-1)
Logger.log("Sheet rows deleted from old data.")
};
}
catch(err){
Logger.log(err)
emailLogs()
};
};
The 'emailLogs' function is a basic MailApp so i get notified of an issue with the script:
function emailLogs() {
// Email Nikita the loggs of the script on error
var email = "my work email#jobbie"
var subject = "Error in Sheet: " + gSS.getName()
var message = Logger.getLog()
MailApp.sendEmail(email, subject, message)
};
Thanks to a comment I've now discovered the executions page!! :D This was the error for the edited script.
Aug 4, 2020, 10:48:18 AM Error Exception: Cannot call
SpreadsheetApp.getUi() from this context.
at unknown function
To show a toast every certain "time" (every n iterations) add this to the for loop
if (!((i+1) % n)) spreadsheet.toast("Working...")
From the question
Aug 4, 2020, 10:48:18 AM Error Exception: Cannot call SpreadsheetApp.getUi() from this context. at unknown function
The above error means that your time-drive script is calling a method that can only be executed when a user has opened the spreadsheet in the web browser.
In other words, toast can't be used in a time-driven trigger. The solution is to use client-side code to show that message every minute. To do this you could use a sidebar and a recursive function that executes a setTimeout
References
https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous/Timeouts_and_intervals
Based on all the comments, and new things I'd learned from that..:
I'd been calling to a global variable for my onOpen function:
var gUI = SpreadsheetApp.getUi();
even though I wasn't using it for the trigger, since it was global it tried to run and then failed.
I moved the actual definition of gUI into the onOpen function, and tried again and it worked.
Thank you all for the support!

Triggering GAS function via URL

Very new to this, but giving it a shot. I am trying to set up an Arduino motion sensor to trigger a script. At this point, my goal is to trigger a script via URL. I found this code below that I am working through, but I continue to get this error when running/debugging.
TypeError: Cannot read property "parameter" from undefined. (line 4, file "Code")
I have been looking into e.parameter object, but have not been able to make any headway
function doGet(e) {
Logger.log(e)
var passedString,whatToReturn;
passedString = e.parameter.searchStringName;
if (passedString === 'tylog') {
whatToReturn = tylog(); //Run function One
};
return ContentService.createTextOutput(whatToReturn);
};
var mns = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Monster")
var tyl = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("tyLog")
var tyd = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("tyData")
var twl = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("twLog")
var twd = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("twData")
var tym = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("tyMaster")
var twm = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("twMaster")
var test = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("test")
var tydate = tyd.getRange('A2');
var tydur = tyd.getRange(2, 2);
// Start functions
function start() {
tyl.getRange('A1').setValue(new Date());
twl.getRange('A1').setValue(new Date());
}
//Log Typhoon ride
function tylog() {
tyl.getRange(tyl.getLastRow() + 1, 1).setValue(new Date());
}
//Log Twister ride
function twlog() {
twl.getRange(twl.getLastRow() + 1, 1).setValue(new Date());
}
//Send Data to both logs and clear
function tyclear() {
tyd.getRange('A2:H2').copyTo(tym.getRange(tym.getLastRow() + 1, 1), {contentsOnly: true});
twd.getRange('A2:H2').copyTo(twm.getRange(twm.getLastRow() + 1, 1), {contentsOnly: true});
tyl.getRange('A1:A100').clearContent();
twl.getRange('A1:A100').clearContent();
}
URL Request:
https://script.google.com/macros/s/AKfycbxC5zYevR1IhfFcUMjmIqUaQ1dKNHTm4mhmWBq_Rc9HgemJQ6Q/exec?searchStringName=tylog
I put this into a new project by itself and it still returned undefined​.
function doGet(e) {
var passedString,whatToReturn;
passedString = e.parameter.searchStringName;
if (passedString === 'functionOne') {
whatToReturn = functionOne(); //Run function One
};
return ContentService.createTextOutput(whatToReturn);
};
function functionOne() {
var something;
return ContentService.createTextOutput("Hello, world!"); }
I believe that your URL should be https://script.google.com/macros/s/AKfycbxC5zYevR1IhfFcUMjmIqUaQ1dKNHTm4mhmWBq_Rc9HgemJQ6Q/exec?searchStringName=functionOne
After pondering this question for a while it makes no sense to require a return from functionOne. I was getting the client server communication mixed up with the Get request process. For most Get requests the request suggests some type of response since in general we're looking for some type of content to be displayed. In this situation that may not be required since the requestor is a machine.
The use of e.parameter.paramname; just enables us to send key/value pairs from within our querystring that we can recover to redirect our server actions.
2020 UPD:
Upon revisiting the question, I noticed that the OP runs the doGet trigger in the context of script editor, hence the e becoming undefined (as it is only constructed when a request hits the URL with an HTTP request with GET method).
Thus, the answer to the debugging part is:
When running a trigger manually from the script editor, event object will be unavailable
The answer to the running part is as a result of an extended discussion:
When assigning a result of the function, one has to put the return statement inside the function, and the tylog function did not return anything.
Also note that any change to a Web App code, unless accessing it via /dev endpoint (i.e. via /exec endpoint), won't be available until after redeployment.
References
Web Apps guide

Using google.visualization.Query in google web app

I have a functioning google web app that is identical to the app presented [here]
You'll note in code.gs that SpreadsheetApp.openByID......getRange().getValues() is used to retrieve an Array that is later converted into a DataTable in the Dashboard-JavaScript.html
Working Code.gs:
function doGet(e) {
var template = HtmlService.createTemplateFromFile('Index');
// Build and return HTML in IFRAME sandbox mode.
return template.evaluate()
.setTitle('Dashboard demo')
.setSandboxMode(HtmlService.SandboxMode.IFRAME);
}
/**
* Return all data from first spreadsheet as an array. Can be used
* via google.script.run to get data without requiring publication
* of spreadsheet.
* Returns null if spreadsheet does not contain more than one row.
*/
function getSpreadsheetData() {
var sheetId = '-key-';
var data = SpreadsheetApp.openById(sheetId).getSheets()[0].getRange("A1:D8").getValues();
return (data.length > 1) ? data : null;
}
I would like to use google.visualization.query instead of .getRange.
Does not work - currently returns "Google" not defined
function doGet(e) {
var template = HtmlService.createTemplateFromFile('Index');
// Build and return HTML in IFRAME sandbox mode.
return template.evaluate()
.setTitle('Dashboard demo')
.setSandboxMode(HtmlService.SandboxMode.IFRAME);
}
function getSpreadsheetData() {
var opts = {sendMethod: 'auto'};
var sheetId = '-key-';
var query = new google.visualization.Query('http://spreadsheets.google.com?key=' + sheetId, opts);
query.setQuery('select A, B, C, D');
query.send(draw)
}
function draw(response) {
if (response.isError()) {
alert('Error in query');
}
alert('No error')
}
I'm certain there are several issues - but I can't get any helpful errors returned to debug the issue.
My questions are:
Is is possible to use google.visualization.query in code.gs?
(I've read a post that leads me to believe that perhaps it cannot be used server side??/why)
If yes - how do I avoid "google not defined" errors
If no - is there an alternative method to "query" a google sheet from server side (the end goal is to have the flexibility to omit columns, perform aggregate functions, etc. when the datatable is retrieved). It is not possible to change the underlying spreadsheet (ie. sharing and publishing)
Finally- I apologize if any of this is formatted poorly or not clear. This is my first post here. In addition, I have limited experience and less expertise with javascript/apps script and google web apps.
No, it cannot be done server side, because google is a client side API.
Same reason as with google.script.run. (For a better understanding, you can check the whole code yourself, it resides here, a code that needs to be embedded within <script> tags, on the Html side).
As an alternative for the server side, you should be able to use the URLFetchApp.
The URL to compose should look something like:
var BASE_URL = "https://docs.google.com/a/google.com/spreadsheets/d/";
var SS_KEY = "stringSS_Key";
var BASE_QUERY = "/gviz/tq?tq=";
var partialUrl = BASE_URL + SS_KEY + BASE_QUERY;
var queryToRun = 'select dept, sum(salary) group by dept';
var finalUrl = partialUrl + encodeURIComponent(queryToRun);
And call URLFetchApp.fetch on it.

Url Parameter Error in Google Appscript with ContentService, MimeType XML

I am getting some issue when taking value form parameter here is my simple code in Google App script and deployed service. what is the issue ?
function doGet(e) {
var num = e.parameter.num;
var result=false;
result=(num%2==0);
if(result){
return ContentService.createTextOutput(result).setMimeType(ContentService.MimeType.XML);
}else{
return ContentService.createTextOutput(result).setMimeType(ContentService.MimeType.XML);
}
}
https://script.google.com/macros/s/AKfycbz86LRyPqowhg_ajj48oM13aESMPms30tbne-_p9sWwJVcaQzg/exec?num=20
Here is google appscript deployed url
This error I am getting when I am hitting this url
and code running error in App-script Environment
it seems that the issue might come from the modulo operation you are trying to apply to a string value, when I try this code it runs without error
function doGet(e) {
var num = Number(e.parameter.num);// make it a number before testing parity
var result=false;
result=(num%2==0);
var xmlContent = '<mydata>' + result+ num + '</mydata>';// added num value for test purpose
if(result){
return ContentService.createTextOutput(xmlContent).setMimeType(ContentService.MimeType.XML);
}else{
return ContentService.createTextOutput(xmlContent).setMimeType(ContentService.MimeType.XML);
}
}
That said, I suppose this is just a test code because I don't really see what it can be used for and the xml output is not valid but I'll leave you with that issue.