BigQuery: No OAuth token with Google Drive scope was found - google-apps-script

I'm trying to replace the contents of a Google Sheet with a BigQuery query.
I'm in the Google Sheet and using a script based on these: https://greenido.wordpress.com/2013/12/16/big-query-and-google-spreadsheet-intergration/
Accessing BigQuery with Google Spreadsheet
All the script does is pull the info from a BigQuery query and write it to a Google Sheet. But if I just run the script, I end up getting an error: BigQuery: No OAuth token with Google Drive scope was found
What do I need to do to get this to work?

After a lot of Googling, I found the answer. Apparently, even though I need to give my script the authority to manage not just my Google Sheet, but also the authority to manage my Google Drive.
All I have to do is add a function to make Google ask me for permission for the script to manage my Drive.
Here's the function:
function usingAppsScriptsToken ()}
// we need to provoke a drive dialog
// DriveApp.getFiles()
}
Note that key ingredient is DriveApp.getFiles(), and it still works even though it's commented. When I run the function, I'll get a pop-up window that says "(your script) would like to View and manage the files in your Google Drive". When I allow it, the data import function works.
Source

No need to add anything to your code.
You simply need to add an additional scope to the dependencies part of the appscript.json file:
"dependencies": {
"enabledAdvancedServices": [{
"userSymbol": "BigQuery",
"serviceId": "bigquery",
"version": "v2"
},
{
"userSymbol": "Drive",
"serviceId": "drive",
"version": "v2"
}]
}
In the View menu you could select to Show the manifest file:

Just add
DriveApp.getFiles()
to any function in your project and run it. After your running, you see a window that asks permission to the Drive.
Afterall you can delete this row and everything is will be ok.

Related

Manifest error: Google Apps script for multiple spreadsheets

I want to use 1 google apps script from multiple Google Spreadsheet files within my Google Drive.
I followed this answer: "you could use Libraries. The idea is that you create one script that you use as a library"
I deployed a library and tried to access it from the Google Apps Script editor. Indeed I can find the library when I enter the library ID (i.e. script ID from script settings), but it gives me a manifest error:
Die Manifestdatei "appsscript.json" enthält Fehler: Ungültige Dienst-ID: 20012023_sistrix_urlkeyword_standalone
Translated:
The appsscript.json manifest file contains errors: Invalid service ID: 20012023_sistrix_urlkeyword_standalone
The manifest file looks like this:
{
"timeZone": "Europe/Berlin",
"dependencies": {},
"exceptionLogging": "STACKDRIVER",
"runtimeVersion": "V8"
}
I was not able to find any hint on how to enter a valid service ID into the manifest file.
I'm writing this answer as a community wiki, since the issue was resolved from the comments section, in order to provide a proper response to the question.
According to s.Panse, the solution was to change the identifier of the script. It was changed from 30012023_sistrix_keywordabfrage to sisirix abfrage and after that it started to work correctly.

How to use spreadsheets.readonly instead of the full read/write auth scope

I am creating a web app for my organization using google apps script. The apps functionality boils down to reading data from different sheets and displaying it to the user.
The docs for using the SpreadsheetApp service seems to require that you provide the full r/w scope just to open a sheet; either by ID or URL i.e. openById() or openByURL() requires:
https://www.googleapis.com/auth/spreadsheets
However I would like to only use the following scope.
https://www.googleapis.com/auth/spreadsheets.readonly
I feel like it should be easy to do this in Apps Script but I cannot find anything in the documentation regarding it.
I have found something related to what I want here. However this seems very complicated requiring to manage API keys.
Is there a 'simple' read-only interface Google Apps Script provides or am I required to port my project over to a standard google cloud project, enable the Sheets API, figure out API keys, and understand how to apply oauth scopes separate from the appsscript manifest?
Issue and workaround:
Unfortunately, in the current stage, when the Spreadsheet service (SpreadsheetApp) is used, the scope of https://www.googleapis.com/auth/spreadsheets is required to be used. It seems that this is the current specification. For example, when DriveApp is used before, the scope of https://www.googleapis.com/auth/drive was required to be used. But, by the update on the Google side, when DriveApp.getFiles() is used, https://www.googleapis.com/auth/drive.readonly can be used. So, I think that the situation of the Spreadsheet service might be changed in the future update.
But, in the current stage, it is required to use the current specification. So, it is required to use a workaround. In this answer, I would like to propose a workaround for using Spreadsheet with the scope of https://www.googleapis.com/auth/spreadsheets.readonly.
Usage:
1. Prepare a Google Apps Script project.
Please create a new Google Apps Script project. In this case, you can use both the standalone type and the container-bound script type. In this answer, I would like to use the standalone type. If you want to directly create it, please access https://script.new. By this, you can see the script editor of Google Apps Script. Please set the filename.
2. Enable Sheets API.
In order to retrieve the values from Spreadsheet using the scope of https://www.googleapis.com/auth/spreadsheets.readonly, Sheets API is used. So, please enable Sheets API at Advanced Google services. Ref
3. Prepare manifest file.
Before the script is used, please set the manifest file (appsscript.json). So, please show the manifest file. Ref
And, please add your expected scope as follows. You can see that Sheets API has already been included in enabledAdvancedServices. Please add "oauthScopes": ["https://www.googleapis.com/auth/spreadsheets.readonly"] as follows.
{
"timeZone": "###",
"dependencies": {
"enabledAdvancedServices": [
{
"userSymbol": "Sheets",
"version": "v4",
"serviceId": "sheets"
}
]
},
"exceptionLogging": "STACKDRIVER",
"runtimeVersion": "V8",
"oauthScopes": ["https://www.googleapis.com/auth/spreadsheets.readonly"]
}
4. Prepare sample script.
Please copy and paste the following script to your script editor and please set spreadsheetId, and save the script.
function myFunction() {
const spreadsheetId = "###"; // Please set your Spreadsheet ID.
const res = Sheets.Spreadsheets.get(spreadsheetId);
console.log(res);
}
About "Method: spreadsheets.get", this is from your question.
5. Testing.
When you run myFunction, the script is run. And, the values of the Spreadsheet are returned. By this, you can retrieve the values from Spreadsheet using Sheets API with the scope of https://www.googleapis.com/auth/spreadsheets.readonly.
Note:
The scope of https://www.googleapis.com/auth/spreadsheets.readonly can be used for retrieving the values from Spreadsheet. When you want to update the Spreadsheet, it is required to use the scope of https://www.googleapis.com/auth/spreadsheets. Please be careful about this.
Reference:
Method: spreadsheets.get

How to resolve "This app is blocked" error for shared Google Apps Script library?

I'm trying to create a Google Apps Script (GAS) library that I can reuse across my Google Sheets spreadsheets. Following these instructions, here's what I've done so far:
Created a new project in Google Cloud Platform
Enabled Google Sheets API for project in step 1
Set up OAuth 2.0 authentication for project in step 1
Credential type = User data
Scopes = https://www.googleapis.com/auth/spreadsheets (I cannot use spreadsheets.currentonly because one method in this library requires the full spreadsheets scope)
OAuth Client ID = Web application
Configured OAuth consent screen
Publishing status = Testing
User type = External
Test users = Gmail email I used to write the script
In Apps Script editor:
Set the Google Cloud Platform (GCP) Project to Standard by associating it with the Project Number from step 1
Updated appsscript.json to look like this:
{
"timeZone": "America/Los_Angeles",
"dependencies": {
"enabledAdvancedServices": [
{
"userSymbol": "Sheets",
"version": "v4",
"serviceId": "sheets"
}
]
},
"exceptionLogging": "STACKDRIVER",
"runtimeVersion": "V8",
"oauthScopes": [
"https://www.googleapis.com/auth/spreadsheets"
]
}
Created a versioned deployment of my script following these instructions
Shared my GAS project as "Anyone on the internet with this link can view"
And here's the issue I'm facing:
Created a new Sheets using the same Google account that I used to create the GAS library
In the Scripts editor, I added my GAS library by entering the Script ID, then selected the HEAD version (it doesn't seem to matter -- all versions resulted in the same error)
In Code.js, wrote a test function that calls a function from my GAS library
Clicked the Run button to execute the test function; the following prompt appears:
Clicked "Review permissions" and selected the Google account I used to create the GAS library
ISSUE: This error page is displayed:
Now the strange part: if I repeat steps 1-5 above using a different Google account (i.e., not the same account I used to create the GAS library), then it works! I get the expected OAuth consent screen, and after granting permissions, I'm able to use my GAS library.
Has anyone figured out how to reuse a GAS library in a Sheets script that is under the same Google account as the one that was used to create the GAS library?
I just wrote about this here: https://aimanfikri.com/2022/05/09/this-app-is-blocked-error-on-google-apps-script-solution/
In summary:
Configure a 'standard project' on Google Cloud Platform.
Set user access under 'Scopes'
Add relevant user accounts (if you don't have an enterprise Google Workspace)
Change your Apps Script project to the newly configured project using the new 'project number'.
If the app is blocked for your account, there is an easy approach to allow it. Just keep in mind that you would need to be an admin in the domain in order to run these changes. Follow this guide on allowing apps and complete its steps. Feel free to leave a comment if you need more help.

Function from script editor works fine but not from sheets custom function

I wrote a function that adds an all-day event.
When the function running from script editor it's working fine, the all-day event is inserted to the calendar.
But when I'm running the function in the active spreadsheet =addevent(a1,b1,c1,d1)
I'm getting an error saying :
Exception: The script does not have permission to perform that action. Required permissions: (https://www.googleapis.com/auth/calendar || https://www.googleapis.com/auth/calendar.readonly || https://www.google.com/calendar/feeds) (line 11)
After searching the net I found how to add manually those permission by editing the oauthScopes in the JSON file:
{
"oauthScopes": [
"https://www.googleapis.com/auth/calendar",
"https://www.googleapis.com/auth/calendar.readonly",
"https://www.google.com/calendar/feeds",
"https://www.googleapis.com/auth/spreadsheets.currentonly",
"https://www.googleapis.com/auth/spreadsheets"
],
"timeZone": "censored",
"dependencies": {
"enabledAdvancedServices": [{
"userSymbol": "Calendar",
"serviceId": "calendar",
"version": "v3"
}]
},
"exceptionLogging": "STACKDRIVER",
"runtimeVersion": "V8"
}
But I am still getting the same error in the spreadsheet...
Answer:
While it is not possible to run a script which requires authorisation directly from a cell in a Sheet, you can set up the script to run on button click instead.
More Information & Workaround:
As a button does not have the same authorisation restrictions as custom functions, you can use them to run code with the scopes you need. To set this up:
Go to the Insert > Drawing menu item and create a shape; any shape will do, this will act as your button.
Press Save and Close to add this to your sheet.
Move the newly-added drawing to where you would like. In the top-right of the drawing, you will see the vertical ellipsis menu (⋮). Click this, and then click Assign script.
In the new window, type whatever the name of your function is, without parentheses (for example myFunction), and press OK.
References:
Custom Functions in Google Sheets | Apps Script | Google Developers
That is because you are not allowed to use a custom function to perform actions that require authorization such as adding an event to your calendar.
From the official documentation:
Unlike most other types of Apps Scripts, custom functions never ask
users to authorize access to personal data.
However, there are some workarounds that you could try. For example, you can create a custom menu option within your spreadsheet file or a time-driven trigger.

Google Classroom Apps Script - CourseWork.list permission error

I am trying to access course work from my Google Classroom in a Google Apps Script using the Classroom API v1. I followed the steps in the Quickstart to successfully retrieve my course list, but when I tried to access the coursework in one of my classes using the following:
var coursework = Classroom.Courses.CourseWork.list('valid courseId');
I get a 'The caller does not have permission' error. I can successfully retrieve the coursework list using the APIs Explorer, though.
From playing with the APIs Explorer, it looks like the "classroom.coursework.students.readonly" scope is needed for this command. However, that scope doesn't get added to my project when I hit the 'Allow' button in the permission dialog. Is there a way to add it to the scope list for the project? I've searched SO and have seen mention of setting scopes in other languages (python, for instance), but not in Apps Script. I've also seen mention of someone authorizing a scope manually in an Apps Script, but with no explanation on how to do that.
I've hit a wall on this, so if anyone has a suggestion, I'd really appreciate it. Thanks.
Originally addressed by me on this SO thread.
The appropriate Classroom API reference for this task is here.
Looks like even after enabling Advanced Google services..., you only get the following OAuth Scopes added -
https://www.googleapis.com/auth/classroom.courses
https://www.googleapis.com/auth/classroom.coursework.me.readonly
https://www.googleapis.com/auth/classroom.profile.emails
https://www.googleapis.com/auth/classroom.profile.photos
https://www.googleapis.com/auth/classroom.rosters
You can view these by navigating to File > Project properties > Scopes.
However, when you try the API from the documentation link, under the Credentials > Google OAuth 2.0 tab, it shows 4 more completely different OAuth scopes; those are as follows -
https://www.googleapis.com/auth/classroom.coursework.me
https://www.googleapis.com/auth/classroom.coursework.me.readonly
https://www.googleapis.com/auth/classroom.coursework.students
https://www.googleapis.com/auth/classroom.coursework.students.readonly
You need to add all 8 of these manually in your Apps script manifest file. To do that, navigate to View & check the Show manifest file. There you need to add this code, perhaps below dependencies -
"oauthScopes": [
"https://www.googleapis.com/auth/classroom.courses",
"https://www.googleapis.com/auth/classroom.coursework.me.readonly",
"https://www.googleapis.com/auth/classroom.profile.emails",
"https://www.googleapis.com/auth/classroom.profile.photos",
"https://www.googleapis.com/auth/classroom.rosters",
"https://www.googleapis.com/auth/classroom.coursework.me",
"https://www.googleapis.com/auth/classroom.coursework.me.readonly",
"https://www.googleapis.com/auth/classroom.coursework.students",
"https://www.googleapis.com/auth/classroom.coursework.students.readonly"
],
Note1: Only adding the newer 4 will not do the trick as the script would assume only these and not the original 5 there were auto-populated when your script ran for the first time.
Note2: The blank line is simply to differentiate between the scopes that get generated automatically vs. the ones you need to add manually (its redundant).
My appsscript.json file looks like this; yours might differ -
{
"timeZone": "Asia/Kolkata",
"dependencies": {
"enabledAdvancedServices": [{
"userSymbol": "Classroom",
"serviceId": "classroom",
"version": "v1"
}]
},
"oauthScopes": [
"https://www.googleapis.com/auth/classroom.courses",
"https://www.googleapis.com/auth/classroom.coursework.me.readonly",
"https://www.googleapis.com/auth/classroom.profile.emails",
"https://www.googleapis.com/auth/classroom.profile.photos",
"https://www.googleapis.com/auth/classroom.rosters",
"https://www.googleapis.com/auth/classroom.coursework.me",
"https://www.googleapis.com/auth/classroom.coursework.me.readonly",
"https://www.googleapis.com/auth/classroom.coursework.students",
"https://www.googleapis.com/auth/classroom.coursework.students.readonly"
],
"exceptionLogging": "STACKDRIVER"
}
I was getting this error repeatedly running the code as both a domain admin and as a teacher of the course I was testing with (i.e. I really should have had access).
In trying to ferret out the permissions issues, I tried making a call to Classroom.Courses.CourseWork.create, which triggered another authorization dialog that included additional permissions for accessing coursework. Even though my create call failed (I was still playing w/ the API and hadn't gotten the syntax right), the permissions it triggered were what I needed to get the course listing correct.
In short, here's the code that initially failed with the permission error you describe:
function getCoursework(id) {
var resp = Classroom.Courses.CourseWork.list(id);
work = resp.courseWork
if (work && work.length > 0) {
for (var i=0; i< work.length; i++) {
piece = work[i]
Logger.log('Work: %s (%s)',piece.title,JSON.stringify(piece));
}
}
}
That code did not trigger a permissions dialog, as it should have. However, once I ran the following (broken) code, I did get a permissions dialog, and then the above code worked:
function createCoursework (id) {
Classroom.Courses.CourseWork.create(id,
{ // doesn't work but triggers permissions correctly
"courseId": id,
"title": 'foo',
"description": 'desc',
});
}
As stated in this thread, make sure that the app script is associated with correct dev console project.
The script should be associated with the dev console project id that corresponds with OAuth 2.0 client ID used (this dev console project should also have "Apps Script Execution API" enabled).
To change the developer console project for an app script select the following menu item: Resources > Developer Console Project...
On this screen enter the project number for your dev console.
You must supply a valid OAuth token to use the API, and this requires a Developer Console Project.
I ahve the same issue - when running a Google Apps script add-on as a teacher of the project, the call to Courses.CourseWork.list works fine.
As soon as I switch to running the same script add-on as a student in the course, I get the 'The caller does not have permission' error.
This is not controllable by the developer of the add-on as beyond turning on the Classroom API, the scopes are not controllable by the developer.
The core issue is - code works for teachers of a course. Code fails for students of the course.
All this while the Classroom APi reference itself works fine. https://developers.google.com/classroom/reference/rest/v1/courses.courseWork/list
This is most likely a bug - at the very least, nothing to do with the generic catch-all answer given by #abielita above.
I see this bug is old so I have little hopes of an answer but here's hoping.