Invoking apps script within another apps script - google-apps-script

Is there any way to invoke a Google Apps Script which serves content and limited to a domain within another Google Apps Script which is invoked by a user who is in that domain? Basically the content serve script is something which runs on administrator of the domain and serves private information.
I think it should work when content serving script is made, available to anonymous usage, but I wanted the content serving script to be available only within domain.

You could publish the first script as a web service, and then just call the functions remotely.
This can be done because when you publish as a web application you set the permissions with which the script gets executed.
https://developers.google.com/apps-script/execution_web_apps

Unfortunately, this isn't possible, as the script request aren't executed in the name of the author neither the user executing it, setting anonymous usage for the script should work.
But you can pass an argument through post or get. so even if anyone can invoke the script, only the script invoked with a key argument will do something
The question that remain is what to use: get or post
I don't know if request made by the script are done in https, so it's maybe a better solution to use post.

is a library what you're looking for? Remember this may slow down executions (vs just binding the script to multiple files like normal)
https://support.google.com/docs/thread/13371261?hl=en
Yes, it is possible. The simplest solution is to create an Apps Script library. You can use either a standalone script or a bound script as the library. I prefer to use a standalone to make it easier to access, and you won't be able to use functions that are specific to bound scripts that could mess up your script.
HERE is a quick video demonstrating how to get a GAS library setup.
https://www.youtube.com/watch?v=TqWtSp4IJcg&feature=youtu.be

Related

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

Google Apps Script publishing questions

I have a few questions relating to publishing an add-on for Google Sheets.
I have a script right now set up with an on load trigger to run the script. Is it possible to have my script run the same way if it's published as an add-on? If not, I guess I can probably have the user click some sort of menu option to run it (which calls a specific function in my script)?
If I publish the script, can users see the code? The problem I have right now is that the script I'm using connects to an API that uses an API key to limit user requests. The key is meant to be private, and for now it's fine that I just have it in the code since I'm only using it privately. I don't have a way to hide the API key from the users if the code is public though, so it would really be a problem if it was public.
If you publish the Google Script as a web app or as an add-on, others cannot see the source code of the script.
"I have a script right now set up with an on load trigger to run the script."
If this means that you are trying to do anything apart from create a custom menu in the onOpen() it could fail as it may not have the authorisation to run; you are limited to what you can do in onOpen() in an add-on. Take a look at the add-on authorisation cycle.
The user can't see your add-on code. However if you were planning to open source your code at any point you can store private things like API keys using the Properties Service that can be manually set in the script editor (File>Project Properties>Script Properties) or by running a bit of code that you then delete.
Answer to question 1:
Yes, you can use the onOpen(e) simple trigger as outlined in the documentation.
(https://developers.google.com/apps-script/guides/triggers/
Answer to question 2:
As Amit Agarwal mentioned in their answer, the code for a published add-on will be private. Users will not be able to see your API key.

Calling apps script function from drive UI

I just implemented a function which operates on a File object in Google Drive(*). Long story short: the function doSomething(file) { ... } exists (in a standalone Google Apps Script) and is well tested.
I want to call this from the Drive UI, e.g. using context menu -> Open With ... (but really any sensible means would be OK).
Is there a straightforward way to achieve this? I prefer not to go through 'registering an app in the Chrome store' whole shebang when all I need is a glorified macro for myself.
I just finished listening to https://www.youtube.com/watch?v=0HVJMIeb3aE which comes closest so far but it seems to skip the technical details of exactly what I need.
(*) There is a rather frustrating repetitive per-file workflow within my organization whereby one needs to switch all write accesses to read-only, then assign ownership to a given user then change your own rights to read-only.
This can absolutely be done in Apps Script. There is a bit of set-up. That video you posted was the one I used to figure it out. When your app is attached to the drive ui your registered mime-types will have your app in the right-click menu.
I made a barebones framework that is highly commented about this process. It also shows how I handle the cases of being installed from the chrome store and called launched from chrome/chrome app launcher. You can make a copy of it here:
https://script.google.com/d/1nrwcxTjzysJl2DMSNB3BHyYfJLEbm02-ekusjUg4V9abzLZ3R_1Yqctj/edit?usp=sharing
*note: This assumes a bit of apps script familiarity. As the '13 IO videos were my first intro to apps script I'm fairly certain it is not that bad to get going.

Deploying container-bound Google Apps Script as Web App

I'm creating a spellchecker using the Google Docs API in an Apps Script (just a script that extends the functionality of a Google Doc), and I wan't to make this service available to users whom download it as a Web App. Problem is that when I've made my (Container-bound) script in the script editor, it is only available in the Google Document through which I created it - that is, if i open a new document, I cannot use the script.
If I "Deploy as Web App", make it available to everyone and paste the given URL, I get an error message saying that the script needs a function called doGet(), which is not in my script.
How do I go about to publish my script as a regular web app?
I would proceed by creating two scripts:
the core functionality would be deployed as a Web App and a simple container-bound script would offer an interface to call the Web App.
Since the Web App is not bound to a document you may want to follow this scheme:
function doGet(e){
if(e.parameter.docId){
doStuff(DocumentApp.openById(e.parameter.docId));
}
}
Now when you deploy the app you will get a link that gives you access to the functionality.
From the container-bound script you can add some UI (e.g. an Anchor element in a side-panel) that links to the web app with the appropriate parameters
ScriptApp.getService().getUrl() + "?docId=" + DocumentApp.getActiveDocument().getId()
or use UrlFetchApp to get the results and display them in the UI.
Unfortunately this is not the same as adding the functionality across all your documents automatically, but rather a way to install only a relatively lightweight hook in each document where you want to add the functionality, instead of the full script.
I am not aware of any method that can achieve that. Note that when you make a copy of a document, the copy will contain all its scripts so you can create a template for documents that need the additional functionality. This can get ackward though if you wish to mix and match multiple scripts.
The advantage of my method is that if you modify the core functionality, the change is immediately available to all your documents making use of it, with no need to update their scripts. On the other hand if the container-script needs to interact heavily with the UI it may get complicate and reduce the usefulness of separating it in two scripts.
The answer is in your question : the main function of your script (the one that builds the UI) must be called doGet() (this is the conventional entry point of any GAS webapp, the function that you implicitly call when opening the webapp url)... but I'm afraid this will not solve your problem...
Even if I don't know what is in your script and how you wrote it I guess that it refers to the doc in which you bound it as the "active document" and that will probably be the most important issue since a webapp has no document attached to it.
Beside that, how would a spell checker work as a standalone app ? could you explain more clearly what you want to do ?

Google Apps Script Authorization gets lost when changing Script - Upgrade path?

I have been working quite a lot with Google Apps Script lately, but there is one thing that still is very unclear to me and the docs do not hint about it at all:
When publishing a script as a WebApp (access: Anyone, as: User accessing the WebApp) and asking for permissions (e.g. GMailApp access, UserProperties and Trigger) and then afterwards changing that script (but not asking for any additional permissions, just changing code) and publishing it again, it seems as if triggers being run by Scripts priorly authorized by users lose their authorization (e.g. the user gets an email with a failure message: Authorization is required to perform that action. from that script).
I read about libraries being independent based on their version, but accessing GMailApp from within a library or a Trigger within a library is not possible as it needs the active user? Is there any way around this? What is the suggested upgrade path, e.g. how can I make (code) changes to the script without making it fail for existing users?
Some services - gmailApp and mailApp for sure- are considered as sensitive matters by Google and therefor any modification in the code, even a very minor change, implies a renewal of the authorization. I can't remember exactly right now where I read that info but I'm pretty sure I read it (!) and I saw it also as a Googler answer somewhere in this forum. Forgive me for not being accurate concerning references.
Anyway ... that explains why you have these authorization issues with your script and AFAIK there is no way to avoid this process.
That said, your users should get an authorization screen, not an error message for services that they use in your app.
If you use triggers in your app (that you set yourself of course) then you should run these functions yourself manually to pass the authorization since the triggers are executed under the authority of the one that creates them, no matter how your webapp is published.
I hope I'm clear enough, if not refer to the doc about installable triggers and this doc also.