URLfetch in an event failing - google-apps-script

I have two combo boxes in cells that are populated by ranges in the sheet. When the first one changes, I build a URL and call:
var resp = UrlFetchApp.fetch(url, {"method": "get","muteHttpExceptions":false});
And it populates a 2nd range which controls the 2nd combo box. This works just fine in debug mode. But my goal is for this to work using the onEdit mode. If the first combo box is changed, the method runs and populates the second. Using this statement:
mydoc.getActiveSheet().getRange(15, 1).setValue("Some debug message");
I have it placed throughout the method to see where it dies. I have a statement right after the UrlFetchApp.fetch() method that never writes, so I know that's where the problem is. Can you make these types of calls during events?
Again, it works just fine running it manually via the script editor, but not when called from onEdit.
I see this question where they do not allow it, but the last comment in the thread said he got it to work by attaching another custom method to onEdit. I call URLFetchApp from another method, but I tried creating a myOnEdit function, and called URLFetchApp from there, but it still fails. Not sure what he meant by manually attaching to the event...

Using a simple trigger:
function onEdit(e) {
var response = UrlFetchApp.fetch('http://www.google.com/');
Logger.log(response.getContentText());
}
I get the following error (View -> Execution transcript):
Execution failed: You do not have permission to call fetch (line 2, file "Code") [0.008 seconds total runtime]
One possible workaround is to create a installable trigger:
function myOnEdit(e) {
var response = UrlFetchApp.fetch('http://www.google.com/');
Logger.log(response.getContentText());
}
/*
In the Spreadsheet, create new trigger:
Run: myOnEdit
Events: From spreadsheet
On edit
*/

Related

Looking for an onEdit script that can be used to change the color of a google sheets cell when it is edited?

I am using google sheets to monitor a project that includes multiple users. I would like to use an onEdit command to change a cell's color to yellow when the content is edited. Below is the code that I am working with. However, I am unable to get it to function. Coding is still new to me, so any advice would be greatly appreciated.
function onEdit1(e)
{
var range=e.range;
var column=range.getColumn();
if(column>3 && column<27)
{
range.setBackground('#ffff00')'
}
}
function onEdit(e){
if(e.range.getSheet().getName()!="Sheet1")return;
if(e.range.columnStart>3 && e.range.columnStart<27) {
e.range.setBackground('#ffff00');
}
}
I also added a line to limit it to only one sheet. You may wish to change the name of that sheet or even remove it entirely if you want it to run on your entire spreadsheet.
A lot of new programmers try to run these onEdit(e) functions from the script editor. Unfortunately, that doesn't work because the e parameter is expecting to be populated by the event trigger. Without the event object you'll normally get an error like Cannot read property range from undefined because e has not been populated by the event trigger.
I test them by making sure I'm editing the correct sheet and correct range and I use the e.source.toast() function to provide me with feed back sort of like the console.log() does.
If you want to learn more about the event object then try adding a Logger.log(JSON.stringify(e)); to the first line after the function declaration. And then get it to run by editing the appropriate sheet in the appropriate way and go to view log to see the results.

Try Catch for if script is already running in sheets, prevent a 2nd instance at same time?

I have a script that does some basic copy and pasting of data, creates a sheet as a pdf and attaches to an email.I have a button to start it for my less-tech-experienced colleagues embedded in a sheet:
We share the spreadsheet doc and I'd like to keep it that way for version control. What's the best way to set the script up so that if an instance of the script is already running in the spreadsheet doc it rejects a 2nd attempt until the first one is completed? Some sort of try catch?
I don't think the code is as relevant for this question, here's the beginning of the function in question:
function failedSettlementsEmailz() {
var contacts;
var toastMembers="";
....more code...
It calls two other functions in my script document that is bound to the Google Sheet spreadsheet.
Thanks
Take a look at the LockService.
For example in your code have:
var lock = LockService.getScriptLock();
if (lock.tryLock(1000)) { // Wait for 1s
// Do stuff ...
lock.releaseLock()
} else {
// This script is already running, try again later ...
}
I understood that when the script is run by clicking a button, you don't want to run the script more while the script is already running. If my understanding is correct, how about this workaround? Please think of this as one of several answers.
In this workaround, I selected to use CacheService. "CacheService allows you to access a cache for short term storage of data." The default expiration is 10 minutes. This is over the maximum execution time of GAS (6 minutes). So I selected this.
Flow :
Retrieve the key of "script" from CacheService.
If there is the key, it means that the script is running.
If there is no key, it means that the script is not running.
At this time, put the key of "script" with a value.
The process you want is run.
After the process was finished, the key is removed. This means the script is not running.
Sample script :
function failedSettlementsEmailz(){
var cache = CacheService.getScriptCache();
if (!cache.get("script")) {
cache.put("script", "running"); // you can use various value for the value of "running".
// do something
cache.remove("script");
}
}
Reference :
CacheService
If I misunderstand your question, I'm sorry.

Creating a programmatic trigger fails silently

Can anybody see anything wrong in this code
// make new ssheet
var payablesNewDoc=SpreadsheetApp.getActiveSpreadsheet().copy(newName); // NB undocumented function
// Create onEdit trigger for the new Spreadsheet
var onEditTrigger = ScriptApp.newTrigger("onChange")
.forSpreadsheet(payablesNewDoc)
.onEdit()
.create();
Logger.log(onEditTrigger.getHandlerFunction()); // logs "onChange"
Logger.log(onEditTrigger.getEventType()); // logs "ON_EDIT"
It runs without error, and the two log messages seem to indicate that the trigger was created. However when I open the new spreadsheet and make an edit, nothing is fired and the script editor doesn't show any declared triggers.
because it seems in this instance you are making a copy of the host Sheet and script, the onEdit can be added as a simple trigger within the template script, i.e. a function so defined: function onEdit() { // do stuff }.
This function will be copied along with the Spreadsheet itself.
function onEdit() {
// This function being present will automatically add an onEdit trigger
// It can act as a parent for calling any other functions
// e.g. onChange() as per your example
}
One improvement you could make if you are likely to have many copies of the same script floating about you can call a handler function hosted in a library elsewhere to keep your triggered code in one place. If you attach the external script as a Library with development mode turned on, this Library will also be attached to any copies you make to the original Spreadsheet. This will allow you amend the triggered code without having to open all the copies
Make a standalone GS script with a handler function that will replicate the functions you originally wished to trigger, you will need to pass the active sheet by Id thusly:
function editFunction(event) {
try {
var range = event.source.getActiveSheet().getActiveSelection();
return "cells " + range.getA1Notation() + \
" changed to " + range.getValues().toString() + \
" in \"" + event.source.getName() + \
"\" by " + Session.getEffectiveUser();
} catch (err) {
return err;
}
}
then in your template Spreadsheet add the external script as a library (e.g. named EditLibrary).
within your simple onEdit() function simply call the Library function instead of anything inline; passing event details as a parameter.
function onEdit() {
Browser.msgBox(EditLibrary.editFunction(eventObject));
}
that should do what you need with no need to use the triggerBuilder and allowing you amend your code later.
On this thread Is there any way to install an "on edit trigger" in another google docs spreadsheet?
I read
"While it is possible to add an onEdit trigger for another spreadsheet, the trigger will always belong to the script that created it, and can only run functions within the script that created it. – Eric Koleda Jun 30 at 0:05"
Perhaps this is the explanation?

function onOpen() is not running

My function includes adding a menu and toast to the document. I have verified that the trigger (onOpen) is set as well. It only works when a user goes into Tools, Script Manager, Run. We have too many users with too many backgrounds to expect then to know how to do this. Why isn't it working? (Using Chrome)
function onOpen()
{
var menus = [{name: "Advance in Workflow", functionName:"sendEmail"}];
SpreadsheetApp.getActiveSpreadsheet().addMenu("Auto Advance FG Workflow", menus);
//sheet.toast(Notify/Remind users);
sheet.toast("While you are here we kindly ask that you do not add, modify or remove any columns.","Welcome - " + username,8);
}
Thanks,
I was having the same issue.
I realized, sometimes Google create some kind of cache of the scripts (I'm used to have a "test" script and I usually alter it's content, and, sometimes, the script runs as if I didn't).
So, what I did that solved the onOpen() not working was changing the function name and ading a trigger manually.
Go to "Resources -> Current script's triggers…"
Choose the function to run on open
It worked like a charm here!
Updated Location Information:
or
Then
This is an old post but I just had this problem and find out why it was not working correctly in my case:
I had, at the top of my script file a variable that required some authorisations and that prevented the script to correctly run. I saw that OP called var username = Session.getActiveUser().getUsername(); (that requires authorisations, and it's may be the cause).
eg:
this code won't work:
function onOpen(){
SpreadsheetApp.getUi()
.createMenu("Exportation")
.addItem("Lancer l'exportation", "exportationMenu")
.addToUi();
}
var stConsCons= SpreadsheetApp.openById(sgcid).getSheetByName("Consultant");
but this one will work:
function onOpen(){
SpreadsheetApp.getUi()
.createMenu("Exportation")
.addItem("Lancer l'exportation", "exportationMenu")
.addToUi();
}
function whatever(){
var stConsCons= SpreadsheetApp.openById(sgcid).getSheetByName("Consultant");
...}
It turns out that you need to add the onOpen(e) function to Triggers!
In my case, onOpen wasn't working because I had a variable, outside of a function, opening a sheet with SpreadsheetApp.openById() rather than SpreadsheetApp.getActiveSpreadsheet(). I guess onOpen doesn't work with openById() even if the sheet you are opening is bound to the script. onOpen() won't work with this kind of a variable outside of a function:
var sheet = SpreadsheetApp.openById("1b_PQD...").getSheetByName("demos")
If your script is bound to the sheet, you can solve this problem by using the getActiveSpreadsheet() function. Otherwise, you can solve it by putting your openById() call into a function.
In my case there was a reference error, that while did not stop the script entirely, it did stop the menu from appearing.
I was only able to detect that error after I run a debug on the script.
It looks like the problem may be that "sheet" isn't defined, which is why the toast is failing.
I know this is a really old question, but for any one finding this now, I may have a solution. The onOpen function often runs with the authorization mode none instead of limited when being used as an event trigger. This may cause errors in things that are related to the specific file or user data. For example:
function onOpen(e) {
SpreadsheetApp.getActivePresentation(); //Will error out if permissions are not set to limited.
SpreadsheetApp.getUi(); //This will always run even if the AuthMode is set to NONE
}
Additionally it is worth noting that if you have any variable used or initialized before onOpen(e), basically any global variables that access sensitive info fail if the AuthMode is set to NONE.
var ss = SpreadsheetApp.getActivePresentation(); //bad
var ss;
...
function init() {
ss = SpreadsheetApp.getActivePresentation(); //good because now that the function is already run we should have full permissions
}
Simple triggers silently fail if they lack permission. I ran into this with an onOpen() in a script that initialized File objects of non-bound files. I moved all File object instantiation to menu functions that do have permission.
In other words, this will not work
function onOpen(e) {
...
let file = DriveApp.getFileById(nonBoundFileId);
...
}
but this will work
function menuFunction() {
...
let file = DriveApp.getFileById(nonBoundFileId);
...
}
because menuFunction can be given permission(s) that simple triggers lack.

How to Access ScriptDB from Spreadsheet Custom Function?

In this post, I suggested to use the ScriptDB as an intermediate storage for global data of a Container Extension code. I wrote a sample code for my answer but the sample throws the error: You do not have permission to call query (line X) exception in a ScriptDb.getMyDb().query(...); line. I created the following simpler example demonstrating the problem. The code, both getDBSize and getSource functions, is permitted to use the ScriptDB by running it in the editor. The getDBSize function is executed without any problem by pressing the Run button in the Spreadsheet Script Manager Dialog. The getSource function works everywhere.
I published the Spreadsheet for the example - link. It is impossible to share the code for view, but it is possible to output it in a cell, the cell B3 contains exactly bellow code.
How is possible to permit the Spreadsheet Code to have access to the ScriptDB?
function getDBSize() {
var db = ScriptDb.getMyDb();
var result = db.query({});
var count = result.getSize();
return count;
}
function getSource() {
return this.toSource();
}
The problem is that you're trying to run this function as a spreadsheet custom function, and custom functions are way more limited than all examples on the Container Extension page you linked.
But, from a theoretical point of view, custom functions as well as simple event handlers (e.g. onEdit, onOpen), can not access anything that requires the user account or is associated with the user, only "generic" methods. For example, UrlFetchApp, open the current spreadsheet, or read ScriptProperties.
So, in thesis, querying a ScriptDb should be possible, since it's a generic call and has nothing to do with the active user, it's analogous to ScriptProperties. I don't see a workaround that would actually let you query the db, but you could use ScriptProperties instead. You can easily save and retrieve any object you would save on ScriptDb by using JSON.stringify and .parse. Of course, I'm not comparing ScriptDb capabilites with ScriptProperties, it's just a workaround.
Anyway, this seems like a good candidate for an enhancement request on our issue tracker.