Is it, at all, possible to deploy a Google Scripts as web app directly from Google Sheets?
I am trying to add a custom menu to Google Sheets where the user can programmatically deploy the file's script as a web app. And it looks like it is possible to do so by following instructions from this GitHub repo.
But for obvious reasons, I need to set projectId to equal ScriptApp.getScriptId(), so that when the Spreadsheet file is copied, this variable is dynamically set to equal to that script's unique id. However, when executing saveAndDeployNewVersion() from the custom menu I've set up (and even from inside the script itself) I always get deploymentId returned as undefined, before getting a 403 error from the final function in the chain (makeRequest_).
Is there any other approach, OR, can you point out what I'm doing wrong when implementing this script into my own project?
Request failed for https://script.googleapis.com returned code 403. Truncated server response: { "error": { "code": 403, "message": "Apps Script API has not been used in project 575477635395 before or it is disabled. Enable it by vi... (use muteHttpExceptions option to examine full response) (line 211, file "Code")
You need to
Switch to standard GCP.
Enable the apps script api for that GCP
Also see Related answer.
Related
I have a google apps script (that I use in one of my spreadsheets). I'm used to calling the DriveApp service to handle files (list, create, move, etc), but I wanted to get the user that last modified a file. Looking it up in several places, I understood that the only way to achieve that is to import the Drive API SDK (the Advanced Drive Service) into the Google Apps script. I added it using the official documentation. So, here is where I'm stuck. To simplify, look at the code below:
console.log(DriveApp.getFileById('1eMIUTEmpvzN5a3kK6PWbK4W8RCkbNOSxxxxxxxxxxx').getName())
console.log(Drive.Files.get('1eMIUTEmpvzN5a3kK6PWbK4W8RCkbNOSxxxxxxxxxxx').title)
The first one shows my file name just fine.
The second one fails with:
GoogleJsonResponseException: API call to drive.files.get failed with
error: File not found: 1eMIUTEmpvzN5a3kK6PWbK4W8RCkbNOSxxxxxxxxxxx
(anonyme) # List Files.gs:2
Any ideas why it wouldn't work?
From your script and your error of File not found, I thought that in your situation, the file might be put in the shared Drive. If my understanding is correct, such error occurs. On the other hand, when DriveApp.getFileById is used, the file in the shared Drive can be retrieved. So in your situation, how about the following modification?
From:
console.log(Drive.Files.get('1eMIUTEmpvzN5a3kK6PWbK4W8RCkbNOSxxxxxxxxxxx').title)
To:
console.log(Drive.Files.get('1eMIUTEmpvzN5a3kK6PWbK4W8RCkbNOSxxxxxxxxxxx', {supportsAllDrives: true}).title)
Reference:
Files: get
Quite new in the apps script world. Trying to create "google form generator" with dynamically created questions/attachment.
All worked well until I noticed that whenever I create the form, users have to "sign in by google account". This was not requested so I found setRequreLogin().
Whatever I set (false or true) I receive the error: "Script error message: Exception: This operation is not supported"
I did some googleing around and it seems the solution is to have the "google suite gmail account".
I use my personal #gmail.com email.
Do anybody have got the same error?
Or is there anybody who is not having the same error using the "regular" gmail?
How I do it (because it might be a bit different than default):
I use python to execute function that is created in appscript
The apps script had to be linked to "standard GCP project" otherwise I would not be able to execute from python (external API call)
I publish the apps script "deploy as API executable" so I can execute (and pass couple of parameters) the script from Python
Thanks for help
function createForm(ordernumber,surname, country, data_list) {
var form = FormApp.create(ordernumber);
form.setTitle(ordernumber +" | " + surname)
.setDescription('anything')
.setConfirmationMessage('you are welcome...');
// .setAllowResponseEdits(true)
// .setLimitOneResponsePerUser(true)
// .requiresLogin(false);
.setRequireLogin(true);
// .setAcceptingResponses(true);
}
According to the setRequireLogin() documentation:
setRequireLogin() - Sets whether the form requires respondents to log in to an account in the same domain or a subdomain before responding. The default for new forms is false unless a domain administrator changes the default.
This feature is available only for forms created by Google Workspace users. Users of other types of Google accounts can't be required to log in.
Therefore, if you posses a gmail account, you cannot use this method.
However, since your script is creating a new form, the users who use this API executable will have to authorize this operation, hence the login screen they are receiving.
Reference
Apps Script Form Class.
I'm creating a Google Docs add-on in Google Apps Script, and some of the functionality requires that I use the Google Drive advanced service as described in https://developers.google.com/apps-script/guides/services/advanced. After enabling the advanced service, my script is now requesting the https://www.googleapis.com/auth/drive scope, which is way overbroad for what I'm trying to do - I only want to touch the files that the user is actually using this add-on with, not their whole drive! I'd much rather be using https://www.googleapis.com/auth/drive.file, which is restricted to files the user is actively using with the script.
I've tried setting the #OnlyCurrentDoc JSDoc tag as mentioned in https://developers.google.com/gsuite/add-ons/concepts/scopes#editor_add-on_scopes, but that only changes the broad https://www.googleapis.com/auth/documents scope to https://www.googleapis.com/auth/documents.currentonly - it doesn't change the Drive API scope.
Also, I've verified that the script does actually need the auth/drive scope, because when I went into the project manifest and explicitly requested auth/drive.file, I got a 404 response with API call to drive.revisions.list failed with error: File not found: 1Sj_oq93ny5q9348ncyo8934nyc at getAuthors(Code:54) at showSidebar(Code:20). That's exactly what I'd expect for a file that hasn't been "tagged" for use with this script.
Here's a very minimal gdocs addon that shows the issue:
function onOpen(e) {
var menu = DocumentApp.getUi().createAddonMenu();
menu.addItem("Get revisions", "getRevisions");
menu.addToUi();
}
function getRevisions() {
var docId = DocumentApp.getActiveDocument().getId();
Logger.log("Document id: "+docId);
var revs = Drive.Revisions.list(docId);
Logger.log("Found revisions: "+revs.items.length);
}
Again, this works just fine with the default auth/drive scope, but not with auth/drive.file.
The documentation for https://www.googleapis.com/auth/drive.file specifies that it grants "Per-file access to files created or opened by the app. File authorization is granted on a per-user basis and is revoked when the user deauthorizes the app." according to the docs at https://developers.google.com/drive/api/v2/about-auth#OAuth2Authorizing. What isn't clear is: how does Google determine what files have been "created or opened by the app", especially when it comes to editor addons? I would think that any document that has had the add-on enabled would count as "opened by the app", but I guess not. Is there any way to make this scope work?
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.
I'd like to use a service account to access a Google Sheet via the Apps Script Execution API, but it's not clear from the documentation whether this is supported.
The steps I've tried (which result in a 403 status from the Execution API) are:
Create a new (unbound) Apps Script
Visit the linked Developer Console project
Enable the Execution API
Create a new service account within the same project (downloading
the generated JSON file)
Create a new Google Sheet and share it with the service account's
email address (this is the step I'm least sure about)
Write an apps script function that reads from the spreadsheet
Run the script manually from the Script Editor (to set the scopes
on the script correctly)
Publish the script ("Deploy as API executable"), making it accessible
to 'anyone'
Mint a new OAuth2 token using the service account and the scopes
linked to the script (in our case just
'https://www.googleapis.com/auth/spreadsheets')
Attempt to make a call to the Execution API using the token
This is the response I got:
{
"error": {
"code": 403,
"message": "The caller does not have permission",
"status": "PERMISSION_DENIED"
}
}
Does this not work because Service Accounts are never able to access the Execution API? Or is there something wrong with the steps above?
Your original 403 error indicates that you have incorrectly set up authentication for your service account. However, even if you get that working, as of now (10 Nov 2015) you cannot execute Apps Scripts via the Service Account.
It's a known bug, and is being tracked in the Apps Scripts Issue Tracker.
Currently(2020), Service accounts cannot work with Apps script API. As written in the documentation,
Warning: The Apps Script API does not work with service accounts.
Your problem is probably that the script is associated with the wrong project (i.e. its own project, instead of the project associated with your Service Account). Here is what you need to do:
From the Scripts editor select the following menu item: Resources > Developer Console Project.
On this screen enter the project number for your dev console.
cf this answer