Apps Script libraries permission - google-apps-script

When I create an Apps Script library that accesses a spreadsheet and use it in a script then I get a permission error (for setValue in the following example).
If I call the same spreadsheet function in the script once (and then remove it) and then call the library function I will never get the permission error again (It's reproducible).
Have you ever experienced such behavior and if yes how did you solve this problem?
Thanks
The library
function addRecord(ss, sheetName) {
var sheet = ss.getSheetByName(sheetName);
sheet.getRange("A1:A1").setValue("Hello World!");
}
The script
function myFunction() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
MyLib.addRecord(ss, "Sheet1");
}
The error message
"You don’t have the required permission to call setValue"

Your script must have the same authorizations than the libraries need to have : if you know that the library reads/writes a spreadsheet or a calendar (or whatever) you have to authorize the script for these services.
You can do that quite easily with some (even dummy ) function that will tell the system to call the authorization process for the required services. From what you describe I guess the system doesn't check what is inside the library when you save you script so it's kind of a 'surprise' for the script to write to the spreadsheet without authorization. If you include the (dummy) calls I mentioned before the script will know what you are going to do and ask for authorization on the first run attempt.
Hoping I'm clear enough.

To close this - the issue reported by Stefan has been confirmed as an issue. We are looking into this. Permissions to a passed in spreadsheet are not being correctly absorbed by a library.
Stefan - please feel free to create a report on issue tracker if you prefer.

Related

Google Apps Script: MySQL function causes error in onEdit [duplicate]

I'm new to GAS and I struggle with the permission system.
I'm a normal Google drive user and I started a spreadsheet and tried to add some code to it. My code is working, but only if I'm in the code editor. I want to use the onEdit() function so it's important for me that it works within the sheet as well. When I ran my code in the editor for the first time it opened a new window where I needed to enter my credentials to allow the script, then it worked. If I do some changes to a cell in my sheet and the onEdit() function is triggered I receive an error message that says something like this(translated):
Exception: You are not permitted to call UrlFetchApp.fetch. Required permission: https://www.googleapis.com/auth/script.external_request
In the editor I displayed the manifest file and added the permission to the oauthScopes but within the sheet I still receive the message. This is how my code looks like (simplified):
function onEdit(e)
{
var data = {
'key1': 'value1',
'key2': 'value2'
};
var options = {
'method' : 'post',
'contentType': 'application/json',
'payload' : JSON.stringify(data)
};
try{
var response = UrlFetchApp.fetch('https://a-working-url.com', options); //error happening in this line
//some more data wizardry
}catch(error)
{
Browser.msgBox(error)
}
}
Any ideas how I can open this permission screen in my sheet or any hints how to solve it in a different way? I want to create a sheet with some code running in the back online. I want to share the sheet with some friends, tried it with Excel and VBA before until I realized that it's not working with Excel Online, so I switched to GAS.
onEdit(), like all simple triggers, is bound by the following restrictions (see official documentation):
The script must be bound to a Google Sheets, Slides, Docs, or Forms file, or else be an add-on that extends one of those
applications.
They do not run if a file is opened in read-only (view or comment) mode.
Script executions and API requests do not cause triggers to run. For example, calling Range.setValue() to edit a cell does not cause
the spreadsheet's onEdit trigger to run.
They cannot access services that require authorization. For example, a simple trigger cannot send an email because the Gmail
service requires authorization, but a simple trigger can translate
a phrase with the Language service, which is anonymous.
They can modify the file they are bound to, but cannot access other files because that would require authorization.
They may or may not be able to determine the identity of the current user, depending on a complex set of security restrictions.
They cannot run for longer than 30 seconds.
In certain circumstances, editor add-ons run their onOpen(e) and onEdit(e) simple triggers in a no-authorization mode that presents some additional complications. For more information, see the guide
to the add-on authorization lifecycle.
Simple triggers are subject to Apps Script trigger quota limits.
The ones highlighted in bold apply to your question.
Basically, it boils down to this - UrlFetchApp.fetch() is a service that requires authorization, so you won't be able to execute it from your onEdit(e) trigger, even if you have its associated scope set in your manifest file.
Use installable trigger instead and write your own "onEdit" function (with a different name) that you bind to your installable trigger.
https://developers.google.com/apps-script/guides/triggers/installable#g_suite_application_triggers
This solved the issue for me.

Identifying the spreadsheet to use in Google Script Editor

I am trying to automate a google spreadsheet on google script editor. However, when I try to identify the spreadsheet and select as the one that we are working on, it is written in the documentation cited below (openById) that scripts that use this method require authorization with one or more of the following scopes:
--> https://www.googleapis.com/auth/spreadsheets.currentonly
--> https://www.googleapis.com/auth/spreadsheets
When I add these scopes, my function doesn't run. It only runs when those scopes are not added. Either way, I am met with the error msg that reads: "We are sorry, a server error occurred. Please wait a bit and try again."
https://developers.google.com/apps-script/reference/spreadsheet/spreadsheet-app#openbyidid
I tried various methods include OpenByUrl and getActiveSpreadsheet...
function automatedInvoiceEmailing() {
var people = [];
// selecting the spreadsheet (without the bottom line, the function works just fine)
var ss = SpreadsheetApp.openById("1jdn3S1Iv2zDAqF6Hyy3fybKARZJYmg-LJVdUWJJS3LA");
}
Either way, I am met with the error msg that reads: "We are sorry, a server error occurred. Please wait a bit and try again."
I expected the sheet to have been selected
Edit: hmm, when I copied the google sheets and saved the new code, it runs properly!! :)
Since you are working with a bounded script (bound to the sheet you’re working with), the way to get a reference to the sheet is with var ss = SpreadsheetApp.getActiveSheet();, this will provide a reference to the bound document. You would use openById() to access another file.
As for scopes, they are added automatically when you run the script for the first time, it will ask for your permission to access your data, and when you accept, google adds them to the script project automatically.
Here are some quick-start examples on working with sheets and apps script

Google Sheets, "The request is missing a valid API key"

I am trying to use the Google Sheets API. The problem is, once I call my script function on the google spreadsheet, I get the following error:
API call to sheets.spreadsheets.values.get failed with error: The request is missing a valid API key. (line 5).
where line 5 in the script looks like this:
var values = Sheets.Spreadsheets.Values.get(spreadsheetId, rangeName).values;
and spreadsheetId and rangeName are defined in the first lines.
I think the problem might be that I did not copy the API key anywhere, but I really do not understand where and how I can do it.
I call the function just using = function().
When you use Sheets API by a custom function like =myFunction() put to a cell, such error occurs. When the custom function is run, ScriptApp.getOAuthToken() returns null. I think that this is the mainly reason of your issue. And unfortunately, I think that this is the specification. In order to avoid this issue, I would like to propose 2 workarounds.
Workaround 1:
A part of Spreadsheet Services can be used at the custom function. So using this, it obtains the same result with var values = Sheets.Spreadsheets.Values.get(spreadsheetId, rangeName).values;. In the case of your script, openById() cannot be used. So the script is as follows.
Sample script:
function customFunc() {
var rangeName = "#####"; // Please set this.
var sheet = SpreadsheetApp.getActiveSpreadsheet();
var values = sheet.getRange(rangeName).getValues();
return values;
}
Workaround 2:
If you want to use Sheets API, the access token is required. But it is required to directly request to the endpoint of Sheets API, because the access token is automatically used in internal at Advanced Google Services. As an issue of this case, there is that when ScriptApp.getOAuthToken() is run in the custom function, null is returned. In order to avoid this, as a method, the access token is directly given to the custom function using PropertiesService. The sample flow is as follows.
When the Spreadsheet is opened, it puts the access token to PropertiesService by the OnOpen trigger.
When you use this, please install the OnOpen trigger to onOpenFunc() in the sample script.
When the custom function is run, the access token retrieved by PropertiesService is used for using Sheets API.
By this, Sheets API can be used in the custom function.
Sample script:
// Please install OnOpen trigger to this function.
function onOpenFunc() {
PropertiesService.getScriptProperties().setProperty("accessToken", ScriptApp.getOAuthToken());
}
function customFunc() {
var spreadsheetId = "#####"; // Please set this.
var rangeName = "#####"; // Please set this.
var accessToken = PropertiesService.getScriptProperties().getProperty("accessToken");
var url = "https://sheets.googleapis.com/v4/spreadsheets/" + spreadsheetId + "/values/" + rangeName;
var res = UrlFetchApp.fetch(url, {headers: {"Authorization": "Bearer " + accessToken}});
var obj = JSON.parse(res.getContentText());
var values = obj.values;
return values;
}
The expilation time of access token is 1 hour. In this sample script, PropertiesService is used. In this case, when 1 hour is spent after the Spreadsheet was opened, the access token cannot be used. If you want to use continuously the access token, you can also update it using the time-driven trigger.
Note:
When you use Sheets API, please enable Sheets API at API console.
References:
Custom Functions in Google Sheets
spreadsheets.values.get
PropertiesService
If these workarounds were not what you want, I apologize.
I want to thank you, #Tanaike, for your response (I don't have enough 'points' to upvote or comment, so my only option is an 'Answer')
I know this thread is several years old, but I thought others might be interested in my personal experience.
First of all: "Workaround 1" worked for me!
The function/method "Sheets.Spreadsheets.Values.get(spreadsheetID, RangeName).values" was giving me an "missing a valid API key" error, so I swapped it for "sheet.getRange(RangeName).getValues()".
Most of the above was set as Global Variables, i.e. outside of any functions.
Weird thing was that this error occured only when running from within the [container] sheet, not from the "embedded" script.
For instance: If I had an active onOpen() function, and I opened/refreshed the Sheet, the script would log a "The request is missing a valid API key." error, and the UI/Menu I had built therein would not load.
I could, however run the onOpen() function from within the script itself, and the menu would appear, and function, within the Sheet. If I disabled/renamed the onOpen() function, and reloaded the Sheet, I would not get the error message.
Makes sense, as the simple loading of the Sheet does not appear to run the script, but when one does access it (the script), i.e. through the onOpen() function, then the initial global variables are read (and the error occurs).
However, when I ran the same function, or others, from within the script itself, they would run ok. This permissions conundrum is what has led me on a wild goose chase all over the Internet, ultimately landing here.
All this after numerous other issues, in the course of whose resolution I built a Google Cloud Project, added APIs (e.g. for Sheets), added scopes to the oauthScopes section of the manifest, and more.
It was only after I made the replacements described above that everything worked, both from the script, and its container spreadsheet! So, THANKS!
Anyway... Sorry for the long post, but I hope others may benefit from your solution, and in which context it helped me.
Cheers,
-Paul

Google apps script error: "You do not have permission to call protect" [duplicate]

This question already has an answer here:
No permission to call msgBox in Google Apps Scripting
(1 answer)
Closed 2 years ago.
I'm trying out my first Google Sheets Apps Script. I'm trying to make a custom function (via a bound script) that will check if the cell it's in is protected. If it is protected, it should change the cell's value to (for now at least) the protection type.
I can successfully run the simple demo script in the docs:
function DOUBLE(input) {
return input * 2;
}
But when calling Range::protect, I can an error
"You do not have permission to call protect"
Here's the function
function isProtected() {
var range = SpreadsheetApp.getActiveRange();
var protection = range.protect();
return protection.getProtectionType();
}
I also get the same permissions error with some other functions, e.g. Session.getEffectiveUser().
I figured that since this is a bound custom function and I'm the (only) sheet owner, I'd be able to call these methods. What am I missing? Thanks.
I've tried copy-pasting the script into the script editor of a new sheet, since some posts have had luck with that, but no luck for me.
Custom function run anonymously so the implicit consequence is that they can not do anything that requires authorization.
Changing a range protection requires an authorization , that's why you can't use it and get the message you get...
Same situation for getEffectiveUser(), it requires an authorization too.
Reference in docs :
Unlike most other types of Apps Scripts, custom functions never ask
users to authorize access to personal data. Consequently, they can
only call services that do not have access to personal data

You do not have permission to call openById

Problem: When I run the script, Google tells me,
You do not have permission to call openById
I had copied a script from another one of my Google spreadsheets and changed the target_ssKey variable's cell reference and created properly-sized Named Ranges in both the Source and Target spreadsheets.
Google Apps Script documentation says nothing about reasons why it might not be working:
https://developers.google.com/apps-script/reference/spreadsheet/spreadsheet-app#openById%28String%29
Another Google Apps Script documentation says that it should work for me because I invoke it from a custom menu:
https://developers.google.com/apps-script/guides/sheets/functions#using_apps_script_services
The second link above says:
If your custom function throws the error message You do not have
permission to call X service., the service requires user authorization
and thus cannot be used in a custom function.
To use a service other than those listed above, create a custom menu
that runs an Apps Script function instead of writing a custom
function. A function that is triggered from a menu will ask the user
for authorization if necessary and can consequently use all Apps
Script services.
I tried putting the function into a "Custom Functions" project and then into an "Add-on" project, but still got the same error message.
Any ideas on what I am doing wrong and how to make this work?
Here is my exact code:
function exportData_SStoSS() {
// Get the source data.
var source_ss = SpreadsheetApp.getActiveSpreadsheet();
var data = source_ss.getRangeByName("exportData").getValues();
// Identify the target.
var controls_sh = source_ss.getSheetByName("Controls");
var target_ssKey = controls_sh.getRange('C2').getValue();
var target_ss = SpreadsheetApp.openById(target_ssKey);
// Paste the data to the target.
target_ss.getRangeByName("importData").setValues(data);
};
I thought that I would throw in a similar issue that I had which brought me to this question, where I received the error You don't have permission to call by openById.
In my case I was trying to call functions from translate.gs which I copied from this example:
https://developers.google.com/apps-script/quickstart/docs
Note that at the top of translate.gs
/**
* #OnlyCurrentDoc
*
* The above comment directs Apps Script to limit the scope of file
* access for this add-on. It specifies that this add-on will only
* attempt to read or modify the files in which the add-on is used,
* and not all of the user's files. The authorization request message
* presented to users will reflect this limited scope.
*/
The culprit here is the #OnlyCurrentDoc comment. See here for reference:
https://developers.google.com/apps-script/guides/services/authorization
Removing #OnlyCurrentDoc fixed this issue for me
I could resolved this issue with this autorization guide of google developers.
https://developers.google.com/apps-script/concepts/scopes#setting_explicit_scopes
This entry It's necesary in json file.
"oauthScopes": [
"https://www.googleapis.com/auth/spreadsheets.readonly",
"https://www.googleapis.com/auth/userinfo.email",
"https://www.googleapis.com/auth/spreadsheets"
],
I found this official note which I believe clears up what caused the issue.
If your function is a custom function, that is one which can be used like a regular spreadsheet function in the sheet itself, then it has limited access to things and cannot open other spreadsheets.
The same script can however open other spreadsheets from a menu button or similar.
Link: Documentation at developers.google.com
The method openById can be called from a "Blank Project" but not a "Custom Functions in Sheets" nor a "Google Sheets Add-on" project.
I thought a "Blank Project" would create a project that was not connected to my spreadsheet, but I was wrong. The Blank Project is connected to my spreadsheet. The other types of projects that I tried to use seem to be limited-scope versions of script projects, not able to carry out some GAS methods.
Had this same issue and came to share my solution. In my case I had two spreadsheets, call them A and B. Both used scripts bound to each respective spreadsheet. Spreadsheet B was able to write data to a tab of spreadsheet A. But Spreadsheet A kept getting the "You do not have permission to call openById" error when trying to read from spreadsheet B. I then tried adding it as a custom menu item but still the same issue.
The solution in my case turned out to be really simple. I created a new unbound script in script.google.com that calls both spreadsheets using openById. The first time running put a smile on my face as it asked for authorization. Thereafter smooth sailing.