onFormSubmit trigger removes when update G-Suite add-on - google-apps-script

I want create a standalone add-on for google form which should handle form-submit event with creation a new text file in google drive with content of the form response. And then distrubute this add-on for other users.
My goal is user can install the add-on on google form and it just should work, without additional setup from add-on menu.
While testing I figured out that there are Simple and Installable triggers are available. (onFormSubmit is an installable trigger)
Installable triggers clear each time when add-on is updated on G-Suite Marketplace. Therefore it removes my onFormSubmit trigger, and because of that the installed add-on stops working after update.
onFormSubmit trigger is added inside onInstall Simple trigger this way:
function onInstall() {
var form = FormApp.getActiveForm();
var trigger = ScriptApp.newTrigger('respondToFormSubmit')
.forForm(form)
.onFormSubmit()
.create();
}
I thought to bypass this issue by handling onFormSubmit inside Simple trigger onEdit of the linked spreadsheet. Actually it could check a spreadsheet for new rows and then create a new file. But Simple triggers are without autorization to private services (like google drive), so it's not possible to create a file on google drive inside a simple trigger.
Is there way to keep onFormSubmit trigger after publish a new version of add-on, without its manual re-install?
Well, these all reasonable, but then why onInstall event is not raised again? Or may be is there an event onUpdate to handle add-on update?
UPDATE
Here I read:
Add-on triggers will stop firing in any of the following situations:
If the add-on is uninstalled by the user
If the add-on is disabled in a document (if it is re-enabled, the trigger will become operational again)
If the developer unpublishes the add-on or submits a broken version to the add-on store
I just set next version number in the publish form, click "Update web store draft", and confirm update in new window. Am I doing everything right?
The trigger stops firing even without code modifications, just with new version of add-on. And there are no any log errors in stackdriver console. Could it be a bug?

Related

How do you run function in Google Workspace Addon when Google Spreadsheet is opened?

I have some trouble understanding this. In Editor Addons it is easy to run a function for example to create custom menu items by using onOpen trigger. But I cannot figure out how I would achieve the same result with Google Workspace addons.
There are no simple triggers available so I am left with 2 options which do not satisfy my needs:
I use a manifest homepageTrigger
"sheets": {
"homepageTrigger": {
"runFunction": "initializeMenu"
}
}
The problem with this is that this function doesn't run until you click the Addon icon in the panel on the right.
I create an installable trigger
ScriptApp.newTrigger("initializeMenu")
.forSpreadsheet("XYZ")
.onOpen()
.create()
The problem with this one is that I need to provide a specific Spreadsheet ID. But I want my addon to work on EVERY Google Spreadsheet after it is installed.
Am I missing something here?
After checking out the documentation, I believe this cannot be done the way you want with a Google Workspace Add-on.
As you mentioned in your post, you can use onOpen() triggers with editor add-ons to add the menu on all spreadsheets, however, Workspace add-ons have a different set of triggers. Among the list, the most relevant to us would be these:
homepageTriggers are a global trigger type that runs when the user clicks the add-on icon.
onFileScopeGranted triggers are specific to the editor apps (which includes Sheets), and they fire when the add-on gains access to the current file's scope, but it also requires user input to run.
Those are the only triggers available in Sheets if you check out the manifest file definition. Essentially the add-on cannot act by itself in the background with just manifest triggers, it needs to use installable triggers to do this, and to generate installable triggers you need the user to at least open the card once.
It is possible to create installable triggers, but the problem is that you still need one of the regular add-on triggers to fire to create these installable triggers. For example, you can create an onOpen trigger when the user opens the add-on card within a Sheet by defining it within the homepageTrigger function. Something like dobleunary's answer works for this:
Manifest:
"sheets": {
"homepageTrigger": {
"runFunction": "onEditorsHomepage"
},
Sheets.gs
function onEditorsHomepage() {
//probably some logic to check for duplicates
ScriptApp.newTrigger("initializeMenu")
.forSpreadsheet(SpreadsheetApp.getActive())
.onOpen()
.create()
}
In this situation the user clicks the add-on menu, the trigger is created for the sheet, and on subsequent runs the trigger creates the menu without needing to open the add-on again. This works and I was able to create triggers for over 30 different sheets so the 20 triggers/user/script quota may apply in a different way or maybe it counts each sheet as a different script file. This may warrant some testing to figure out the real limit since the docs do not specify.
I would recommend to just build an editor add-on instead and opt for simple triggers. You probably don't want to burden users with triggers for each of their files and even Google's comparison between add-on types emphasizes editor add-ons as the main tools to create custom menus. If you really must build a Google Workspace add-on, then you may have to just live with needing to have the users use the add-on card rather than a menu at the top.
Sources:
Add-on types
Building editor add-ons
Apps Script quotas
Google Workspace add-ons triggers
Editor add-on triggers
Overall add-on manifest
General and Editors manifest definitions.
I need to provide a specific Spreadsheet ID
Instead of using a hard-coded spreadsheet ID, use the ID of the active spreadsheet, or a direct reference to the active spreadsheet object, like this:
function installOnOpenTrigger() {
const functionNameToTrigger = 'initializeMenu';
const ss = SpreadsheetApp.getActive();
if (ss) {
try {
ScriptApp.newTrigger(functionNameToTrigger)
.forSpreadsheet(ss)
.onOpen()
.create();
} catch (error) {
;
}
}
}
SpreadsheetApp.getActive() will return the current spreadsheet whenever there is an active spreadsheet.
From the documentation:
Each add-on can only have one trigger of each type, per user, per document. For instance, in a given spreadsheet, a given user can only have one edit trigger, although the user could also have a form-submit trigger or a time-driven trigger in the same spreadsheet. A different user with access to the same spreadsheet could have their own separate set of triggers.
Add-ons can only create triggers for the file in which the add-on is used. That is, an add-on that is used in Google Doc A cannot create a trigger to monitor when Google Doc B is opened.

onSpecialEdit() for Google App Script on a sheet with many users executes for all, each time

This can't be right. What am I missing?
I have a Google Sheet with scripts. The scripts are using certain ui functions that require authentication and special permissions coming from an onEdit() event. Therefore, I had to create onSpecialEdit() and the custom trigger to run that function with event source of the spreadsheet and event type onEdit(). This all works fine. Script does what it should.
From what I've seen, each person who needs to have this ability and access the special edit will need their own trigger. That is created for them automatically. Triggers all work for each person.
Now, what I see is that when I do an action that triggers onEdit(), it appears to run the onSpecialEdit() 20 times...once for each person who has that trigger....I'm assuming.
This can't be right. What am I missing? Is there another way to create triggers where they are only executed by the person triggering? Is this approach wrong?
Issue:
You want to have an onEdit trigger that has access to services that require authorization.
These services should have access private of the user who has caused the trigger to run by editing the spreadsheet.
Since the services require authorization, it cannot be a simple but an installed trigger (Restrictions).
Since installed triggers act on behalf of the user who installed them, they can only access data of the user who installed them.
Therefore, each user whose data should be accessed needs to install the trigger, so multiple triggers should be installed.
A trigger runs whenever any user edits the spreadsheet, it doesn't have to be the user who installed the trigger. Because of this, all of the installed onEdit triggers will run whenever any user edits the spreadsheet.
Solution:
Use Session.getActiveUser() and Session.getEffectiveUser() to check whether the user editing the spreadsheet (getActiveUser) is the same as the one who installed the spreadsheet (getEffectiveUser). If it's not, stop the function execution. This way, only one of the multiple onEdit triggers will make the corresponding actions.
Code sample:
function onEditTrigger(e) {
const active = Session.getActiveUser().getEmail();
const effective = Session.getEffectiveUser().getEmail();
if (active === effective) {
// TRIGGER ACTIONS
}
}

Why Apps Script execution failed only when it is triggered by onEdit event on Google Spreadsheet?

I am working on Google Apps Script. I have apps script that I have wrote that is supposed to be executed by onEdit event on specific sheet.
onEdit.gs
function onEdit(e) {
var syokuninIchiran = new SyokuninIchiran();
syokuninIchiran.syncTable();
}
When the above is executed by edit on the sheet, this error occurs:
You do not have permission to call openById. Required privilege: https://www.googleapis.com/auth/spreadsheets
But, it works with no problem when the script is triggered from Google Apps Script development window. I call the same code by making a test function as follows:
function test(){
var syokuninIchiran = new SyokuninIchiran();
syokuninIchiran.syncTable();
}
It might be problem for this issue? The spreadsheet's owner is not mine. My account has right to edit as the image below.
Try1:
I tried to add the scope manually into the manifest file(appsscript.json). And I approved the authorization. But, nothing changed. It still have the same error message.
{
"oauthScopes": [
"https://www.googleapis.com/auth/sqlservice",
"https://www.googleapis.com/auth/spreadsheets",
"https://www.googleapis.com/auth/script.external_request"
]
}
Answer:
Simple Triggers can not open files aside from the file it is bound to. For this, you need to use an installable trigger.
More Information:
As per the documentation on Simple Trigger restrictions:
Because simple triggers fire automatically, without asking the user for authorization, they are subject to several restrictions:
They can modify the file they are bound to, but cannot access other files because that would require authorization.
So an installable trigger must be used.
Setting up the Installable Trigger:
Follow the Edit > Current project's triggers menu item, which will open a new page in the G Suite Developer Hub. Click the + Add Trigger button in the bottom right and set up the trigger settings as follows:
Choose which function to run: onEdit
Choose which deployment should run: Head
Select event source: From Spreadsheet
Select type of time based trigger: On Edit
And press save. This will increase the range of functionality of your edit triggers.
References:
Simple Triggers | Apps Script | Google Developers
Installable Triggers | Apps Script | Google Developers

Google Apps Script AddOn: onChange trigger programatically with misleading permission scope text

By Google documentation, an onChange trigger for a Spreadsheet addon has to be created programmatically via ScriptApp.newTrigger(...)....
newtrigger requires the scope :
https://www.googleapis.com/auth/script.scriptapp
When the user is asked for granting permissions, the text presented is:
Allow this application to run when you are not present
I understand this function can create time based trigger, but for a simple onChange in a Spreadsheet, I would understand if the user declines it due to that text.
Is there a way to have an onChange trigger without this scope for changes (not onEdit) in spreadsheets?
Answer:
The only way to create a trigger on behalf of a user programmatically is using ScriptApp.newTrigger(), and this method requires the https://www.googleapis.com/auth/script.scriptapp scope. No other scope will allow this to run.
You can however set up an onChange() trigger for the Spreadsheet, which will run regardless of who makes the change to the sheet.
More Information:
When you set up a trigger, you are allowing an app/script/function to run, on behalf of you, without you manually running the function. Asking a user to create a trigger will require them to authorise that they consent to the script doing something on their behalf.
You can however, set up the trigger yourself. As long as the function that will run on the trigger doesn't violate any of the installable trigger restrictions then this will run on the Sheet as long as it has been authorised to run by you.
Remember though, that if you set up the trigger yourself, the script will always execute as you, not as the user. So if the function, for example, sends an email, then the email will be sent on behalf of you - as you were the one that authorised the script.
Setting up an Installable Trigger manually:
If setting up the trigger to run as you is an acceptable solution, you can do so by following these steps:
Save the script with the save icon, press the run button (►), and confirm the authentication of running the script.
From here, following the Edit > Current project's triggers menu item, you will have a new page open in the G Suite Developer Hub. Click the + Add Trigger button in the bottom right and set up the trigger settings as follows:
Choose which function to run: <your-function-name>
Choose which deployment should run: Head
Select event source: From Spreadsheet
Select event type: On change
And press save.
This will now run on all changes made to the sheet - regardless of who made the change. Just remember that as far as execution goes, you're the script runner.
References:
Class ScriptApp | Apps Script - .newTrigger(functionName) method
Installable Triggers | Apps Script - Restrictions
Event Objects | Apps Script - Google Sheets events: Change

Triger a function on a change in doc

I want to run a Google App Script function every time a change is made to my Google Doc. I have found onChange and onEdit triggers which are only available for Google Spreadsheet. I am sure there must be an edit/change trigger for Google Doc as well.
Unfortunately there are no onEdit(), onChange() triggers for Google Docs.
The triggers available for Google Docs are the following:
onOpen() triggers both simple and installable;
time driven triggers;
onInstall() simple triggers.
What you can do instead is to use a time driven trigger so in this way even though the changes are not directly triggering the execution, the trigger will still run how often you want it to.
Last but not least, you can file a Feature Request on Issue Tracker where you specify the details needed.
Reference
Apps Script Trigger;
Google Issue Tracker.