Can you do something on a Google-Apps-Script fail? - google-apps-script

Is there a way to have an automatic 'back-up' plan if my Google Apps Script fails? I'm thinking something along the lines of a python Try / Except.
For example, I am building a function to create a calendar event from a form (script is attached to the spreadsheet the form is linked to). The script creates an event number for the new form submission (storing it in the spreadsheet) and then makes the calendar event. If my function fails in making the event, there is a new event number in the spreadsheet, but no event in the calendar.
Does Google Scripts have a pre-built way to fail gracefully?
Or, is there a way to build an outer function that runs the original and has the graceful fail as (e.g.) the else of an if-then-else?

Apps Script is based on Javascript, so you can use the try...catch functionality.
If you are calling functions on your Apps Script back-end from your front-end you can also use the withFailureHandler to handle a failed execution.

Related

My OnFormSubmit trigger needs to complete before add-on's OnFormSubmit trigger fires

I'm using the Form Publisher add-on with Google Forms in order to turn each Forms response into a Google Doc. It's a great add-on.
I've written my own OnFormSubmit trigger (Head deployment; part of an app-script library) that renames the file(s) the form user may have uploaded as part of their form response. (The filenames are only available from the Drive.App file object, not from the form response itself.)
Based on the upload filenames that Form Publisher puts into the Google doc it generates, I can tell that sometimes the doc is being generated BEFORE my OnFormSubmit renames the files, and sometimes it is being generated AFTER my OnFormSubmit fires.
So my question is this: Given that I have no real knowledge of how Form Publisher works (though I would think OnFormSubmit would be at the heart of it), is there any way that I can assure that my OnFormSubmit will fire and finish before Form Publisher begins to generate the Google Doc?
Thanks, iansedano! I've continued to test and research, and everything I've seen points to your comment being correct.
I'm afraid that if you have two triggers that fire from the same event there is no way to ensure one starts/finishes before the other. They go off to the server asynchronously so the order will never be dependable. The way you normally deal with this is to make them part of the same trigger, but since you are mixing and matching your own script with closed source add-on script, that is not possible. If you wanted your one to fire after then you could just use a delay, but to make it fire before, you will probably be best off writing your own script.
For the record, there appears to be one small glimmer of hope here. I read that post's answer as saying that an add-on could use #customfunction to expose a function that my OnFormSubmit trigger could call; that custom function could then, for example, put the add-on to sleep for a bit (or even better: sleep until I call a second custom function to signal that I'm done) in order for my OnFormSubmit trigger to go first.
But other than that possibility, I've come up with nothing.

How do you use Logger.log(e) with Google Forms? (Using the NEW Apps Script Editor)

In the legacy Apps Script Editor you could log e from Google Form submissions.
How is this done in the NEW Apps Script Editor?
function myFunction(e) {
Logger.log(e);
}
To make sure we are on the same page:
If you are going to run a function like this in any editor you are going to get null:
because simply e is not defined and it is only returning data upon trigger executions of this function. But this function is executed by some events depending on the type of trigger you are using. Therefore, you are not going to see anything (that is not null) in the console if you manually execute this function.
After the function is triggered by specific events:
In any editor again, you can go to the execution page to see the details of the execution. In the new editor, you go to Executions:
and you can see a list of all the executions of this particular function. For example, if your function is a simple onEdit trigger e.g. onEdit(e), you will see this upon editing a cell in the spreadsheet:
You can also see the type of the execution, whether it was executed by the script (Editor) or by a trigger (Simple Trigger).
But anyway, trigger functions are not supposed to be executed manually. As the name suggests, trigger functions are triggered upon events. It wouldn't make sense to use a trigger function and need to manually execute it. It would be a regular function then.

Centralized script for multiple spreadsheets

I have created a Notification script and would like to use it for multiple spreadsheets (produced by Forms). I would like to have a centralized script and referencing it on my multiple spreadsheets in order to simplify maintenance.
I have tried to use the SpreadsheetApp.openById() like below but looks like this feature has been disabled for security reasons and is not supported anymore.
function append()
{
SpreadsheetApp.openById("1xdePF..........................");
}
Any idea on how to use the same script for multiple spreadsheets?
The documentation specifies that openById opens the spreadsheet with the given ID.
You cannot use this method to open a script.
To open an Apps Script, go to https://script.google.com/home/my and clock on the script of your choice.
For bound scripts, you open the document to which the script is bound and go to Tools->Script Editor
Preface
This answer is supplementary to ziganotschka's, since you indeed cannot access the script bound to a document by opening said document with openById(). The answer, instead, covers other issues you had and suggests additional ways to solve your task.
Problem
When trying to use openById() you receive an error message of the following structure:
Exception: Document [doc id here] is missing (perhaps it was deleted, or maybe you don't have read access?)
You mentioned in comments that the id is 58 chars (I bit it is 57) and obtained from "properties", which explains the error - there is no spreadsheet with such an id, because file id and script id you extracted are not the same thing. If you ever need to extract current id programmatically (here it is assumed to be called from a script bound to spreadsheet, but other services have similar methods), you can call getActiveSpreadsheet() -> getId().
Solutions
You stated that you need a maintenance script, so how about creating a standalone script project that is deployed in a way any document can access:
As a library
Any script that has a saved version can be a library that can be used by other scripts by adding its id to the list accessible from Resources->Libraries menu.
As a Web App
Any script that has a doGet, doPost function (or both) can be deployed as a Web App, essentially exposing it to the net. Since you said that the spreadsheets are "produced" by Forms, and you created a "notification" script, I assume you are interested in a FormSubmit event. When you deploy as a Web App, you get a url (don't forget to choose an appropriate permission).
After that, it is only a matter of making sure that:
each Form has an installable onFormSubmit trigger (you can instlall it via [please see refs for docs] ScriptApp.newTrigger('callback name').forForm('form ref').onFormSubmit()).
The callback for the trigger calls the Web App url via UrlFetchApp.fetch() with necessary data (like spreadsheet id or any other info) as query (if using doGet) or request body (if using doPost).
The data you need will be available in the event object constructed on hit to doGet or doPost [note to avoid common misconception: you can't debug event objects in the editor, it can only be done live].
Reference
getActiveSpreadsheet() docs
getId() docs
getFileById() docs
Standalone scripts guide
Bound scripts guide
Libraries guide
Form submit event reference
new Trigger() docs
UrlFetchApp.fetch() docs
doGet / doPost event object docs

Why Isn't My Spreadsheet Changing From Input Values of An Outside Source?

So I am trying to use an outside source (Zapier) to input values into my spreadsheet. These input values are then "transposed (formula wise)" into my spreadsheet to fit the cell coordinates with which they are to align.
I have the spreadsheet set to run 'onEdit' and when these incoming values arrive, it is supposed to cause the rest of the spreadsheet to change, but the function is not running.
However, if I were to edit the spreadsheet 'manually,' the onEdit function runs perfectly.
So why then would the spreadsheet not be running the function, when the outside source brings its input values?
UPDATE:
So I discovered that if I manually authorize an 'onChange' installable trigger, it will work. But if I create a copy of the same exact spreadsheet, the installable trigger will not exist in the copy. The copy needs to have the trigger without me having to do it manually. So I am trying to create a code inside of Google Script Editor that will either allow me to use the onChange function or install the onChange function in the Developer Hub. Any thoughts? Here is the code I tried but did not work:
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("My
Sheet")
var ssid = "My SpreadSheet ID";
ScriptApp.newTrigger('My Sheet)
.forSpreadsheet(ss.getId())
.onChange()
.create();
myFunction()
{
If there is an alternative for the onChange function, then I'm all ears. I just need a function that can run itself in my copies.
As a part of a collaborative effort, let me clarify the Zapier part (this answer does not concern the copy part).
Part 1. Zapier setup
Assuming you have a third-party application that you pull data from (btw, since you decided to use apps script, isn't it easier to drop the middleman like Zapier and connect to the 3P app API – if it has one, ofc – directly?), you created a Catch Hook and a POST Action.
The POST Action setup contains several fields:
URL field - this is where your /exec URL goes (WebApp is deployed via Publish->Deploy as a WebApp). After you deploy your script as a WebApp, you will get a URL that users and scripts can make requests to (it is always of this format https://script.google.com/macros/s/{yourProjectId}/exec - with some slight diff. due to access permissions). To avoid permissions issue, set the Who has access to the app option to anyone or anyone, even anonymous (otherwise, you'll have to devise auth handling).
Payload Type field is irrelevant here, but I suggest using JSON.
Data field is required if you chose the POST Action and should contain key-value pairs of data you would like to transmit via Zapier (the data will be available in parameter/parameters property of the event object).
Part 2. WebApp setup
Published WebApps should have either a doGet() or doPost() function to be able to receive and process requests (or both). Each of them accepts one special argument, which is constructed each time a request to the WebApp is made – an event object.
The event object will contain all the data that you sent from Zapier. You can then use this data to conditionally trigger different functions, pass data to handlers, etc. So, instead of relying on triggers, you can create a function that is called inside the doGet / doPost that will a) populate your target sheet with new values; b) do anything else after that, thus acting as an analogue of onEdit / onChange.
Useful links
Event object structure;
Passing event objects around;
Creating triggers on other documents;

Cache custom function result between spreadsheet opens

I have a Google spreadsheet that has it's data loaded using a custom function.
The custom function calls a REST API, transforms the data and displays it.
The problem is that every time I open the doc, the function is called and all the REST API calls go out.
I need to be able to manually trigger service calls to refresh data, and in between show the data from last time.
How can I best achieve that?
Short answer
Instead of calling your function from a formula (custom function) call it from:
The script editor
A custom menu
Explanation
At this time custom functions are recalculated when the spreadsheet is open and when any of its arguments changes its values. To run the function at will use the script editor play or debug buttons or a custom menu. Also you could use other triggers. Choose the one that best suit your needs.
If you still want to cache the result of calling the corresponding REST API use the Class Cache.
References
Overview of Google Apps Script
Custom functions in Google Sheets
Cache Service