Call Custom Function from cell that calls YouTube API - google-apps-script

I have searched a lot for this already, and I honestly can't believe that I haven't been able to find the answer, even if it's "You can't do that".
I have created a bound Google Apps Script Project inside of a Google Sheet. That project contains a function called getVidViews source:
function getVidViews(id){
//id = k3y1hJPVavY
let res = YouTube.Videos.list('statistics',{'id':id});
return res.items[0].statistics.viewCount;
}
If I run this code from the Project Editor IDE and un-comment the line where I set id. I'm redirected to authorize oAuth scopes, and the function executes exactly how I want.
However, I want to have a spreadsheet where I have the id in cell A1 and I want to paste the result of the call in B1.
I should be able to accomplish this by pasting an id in A1 and using the formula =getVidViews(A1) in B1
When I do this, I get the error:
GoogleJsonResponseException: API call to youtube.videos.list failed
with error: The request is missing a valid API key.
I cannot figure why it is requesting an API key here, but not in the code editor. Also, I know that Google Apps Scripts create a hidden GCP project, but don't know if I can add an API key to that project for use in this instance.
Please point me in the right direction.

Custom functions only support certain services and YouTube isn't one of them.
Here's a deep dive into custom functions if you are interested.

Related

How to create a Message-Box in Google Apps Script

I am creating an new Google Apps Script about GDrive/New/More/Google Apps Script.
The script is running perfect and on the end my intention is to send a Info on the Desk.
When I am using this line:
Browser.msgBox("TEST");
I get the error back:
Exception: Cannot call Browser.msgBox() from this context; have you tried Logger.log() instead?
Logger.log can't be the solution, because I don't see the result on the desktop, but only in the log files.
Is there another solution or is it possible to get a simply example what I can do?
In this case Browser.msgBox is a method for the Browser class, this class is specifically meant to be used in Spreadsheets as stated in the documentation:
Class Browser
This class provides access to dialog boxes specific to Google Sheets.
The methods in this class are only available for use in the context of a Google Spreadsheet.
If you want a notification you could use either the Browser.msgBox method or the Toast method, either way they both need to be applied in a Spreadsheet in order to work.

Share a custom google sheet function with another email address without showing the source code

Iv'e written a custom google sheet function using Google Apps Script, and i would like to share it with another spreadsheet user, but i do not want to share the functions source code.
I've converted the Google Apps Script to a project, but now i have no idea how to link the project back to my sheet, so the function will work again.
I also do not want to publish the AddIn to the marketplace.
Test As Addin also doesn't work, the sheet is opened in a new tab, but the cells with the custom function says #NAME?.
What am i missing?
Explanation / issues:
The error #NAME? indicates that you are trying to use a function
that does not exist. As you also mentioned, this function does not
belong to the active spreadsheet but on a different project.
Unfortunately, it is not possible to share a custom function with other spreadsheets directly.
Possible Workarounds:
You can create an add-on.
Another workaround solution would be to create a library. That is a great alternative and the documentation is quite straightforward.
Related:
Creating add ons in Google Apps Script
How to call library function as custom function from spreadsheet formula?

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

Correct scope for Google App Script Execution API?

I'm hoping to automate some HR work by running a Google App Script via the Execution API. Without getting too much into the details, I'd like to pass employee evaluation data as a parameter into the App Script. The script will then use this data to compile an "Employee Review" GDoc.
So far, I have ran a simple test App Script using the Execution API. For example, I can successfully run a simple function which logs a string or interacts with spreadsheets. So far so good.
But I run into problems when trying to write to a GDoc (which is unfortunately integral to my task). Here's my paired down script:
// TODO: Eventually, we'll pass these variables as arguments
var docId = "MY-DOC-ID";
// Find the team member review doc
var doc = DocumentApp.openById(docId);
// Replace placeholder text
var docBody = doc.getActiveSection();
docBody.replaceText('{{DATE}}', "Date set by App Script!!!");
doc.saveAndClose();
This script works when I press the "Run" button in the App Scripts web UI. But when I try to run via the Execution API, I get:
{
"error": "unauthorized_client",
"error_description": "Unauthorized client or scope in request."
}
So apparently I haven't provided the correct scope? Following the docs, I can find the necessary scope(s) in Project Properties > Scopes which says:
But when I try adding that scope, it wont work. As I said other scopes (e.g. https://www.googleapis.com/auth/spreadsheets) work just fine. Perhaps the auth/documents scope is no longer supported or there's a bug in their API?
Questions
What is the correct scope? I can see a big list here but I don't see https://www.googleapis.com/auth/documents, so?
Any other suggestions? For example, is it possible to write to a Google Doc using the Google Client API directly (i.e. without using App Scripts)?
Doh. I figured out the solution to my problem. While it was a dumb mistake, it's nevertheless worth posting as it may save others confusion in the future.
First, a little context about my setup. I'm authenticating to the Google Client API using a Service Account. Furthermore, as is common when using a service account setup, I am impersonating a user within our organization (specifically my own account).
My missing step (obvious in hindsight)...
Log into the App Script web UI as the person you are impersonating.
Manually run the script by pressing the play button
If the impersonated user has not already granted permissions to access the required scopes, you will be prompted to do so.
After granting access (specifically for the https://www.googleapis.com/auth/documents scope), my authorization error disappeared.
So the lesson: Make sure the account you are impersonating has granted access for all the scopes which your script requires.

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.