Automatically updating google spreadsheet with data from bigquery - google-apps-script

Followed the instructions here and integrated a google spreadsheet with bigQuery. My query pulls data from the past hour, so it is important to keep the spreadsheet updated.
Is there a way for the sheet to automatically run the script and update the data on refreshing the page? Right now, I need to perform a few clicks to do it.

Addition to #Sandy Good's answer.
You need to use onOpen(e), it runs when a user opens a spreadsheet, document or form that has permission to edit.
function onOpen() {
// Add a custom menu to the spreadsheet.
SpreadsheetApp.getUi() // Or DocumentApp or FormApp.
.createMenu('Custom Menu')
.addItem('First item', 'menuItem1')
.addToUi();
}
You received authorization error since script needs authorization. Apps Script requires user authorization to access data.
If a script uses services that can access private data, you'll see one of the authorization dialog.
You may revoke scripts access by following these steps:
Visit the permissions page for your Google account. (To navigate to this page in the future, visit Google.com, then click your account picture in the top-right corner of the screen. Next, click Account, then Security, then View all in the account permissions section.)
Click the name of the script whose authorization you want to revoke, then click Revoke access on the right.
For more information regarding authorization, please follow the Official Google Documentation: https://developers.google.com/apps-script/add-ons/lifecycle

Since BigQuery has already been integrated to Google Spreadsheet,
here's what you have to do with your Script:
1. Send HTTP request using Datasets: update with added path parameters datasetId and projectId.
Sample HTTP request:
PUT https://www.googleapis.com/bigquery/v2/projects/projectId/datasets/datasetId
However, please note of the following when using Datasets: update:
It requires authorization as stated in Authenticating requests to the Google BigQuery API
The specified access list completely overwrites the existing access list. If you specify an empty access list, you will revoke access to everyone except yourself
This request requires authorization with at least one of the following scopes:
Scope
https://www.googleapis.com/auth/bigquery
https://www.googleapis.com/auth/cloud-platform
2. To update your data automatically, you can set up a Trigger function from inside your Script which executes based on your preferences with these reserved function names:
onOpen(e) runs when a user opens a spreadsheet, document, or form that he or she has permission to edit.
onEdit(e) runs when a user changes a value in a spreadsheet.
onInstall(e) runs when a user installs an add-on.
doGet(e) runs when a user visits a web app or a program sends an HTTP GET request to a web app.
doPost(e) runs when a program sends an HTTP POST request to a web app.
Again, you should also note that because simple triggers fire automatically, they are subject to several restrictions which you can go through in the documentation.

Related

GAS Ask again for permission and Add Permissions Description

I have a number of scripts running on Google Workspace. The is other users may blanket deny permissions to access sheets, email or other API calls which automatically generates an error message.
I believe this is because the permissions box does not say why it is requesting permissions only what permissions is requesting and none IT personnel will deny them for safety reasons.
So now I'm stuck with a bunch of errors and a bunch of users not correctly using the scripts i have set out messing up a lot of the forms.
I am looking for a way to ask for permissions every time a user opens a file if permissions have not been granted and add a description of why these permissions are needed for the file to work correctly.
I was hoping the UI app would allow me to insert a UI before the permissions option
example:
function onOpen(e){
try{ //or if (DriveApp.Permissions.Edit === true); if permissions can be queried.
var d = DriveApp;
}catch{
var ui = SpreadsheetApp.getUi();
ui.alert(
'This Sheet requires permissions for The menu options to work correctly.\nPlease allow.'
);
//DriveApp.request_new_permissions(); //psudocode
}
var dapp = DriveApp;
}
I have been trying to check the permissions through code but it doesn't seem part of the API.
Logger.log(DriveApp.Permission.EDIT === true); -> false
Current Behaviour.
User opens a Spreadsheet with onOpen trigger.
The App Scipt require use of Drive API and requests user permission.
User selects Deny
Later the user opens the same Spreadsheet with onOpen trigger.
Drive API has already been denied so the onOpen trigger fails.
Desired Behaviour:
User opens a Spreadsheet with onOpen trigger.
A box pops up saying "This Spreadsheet requires the use of Drive API to create folders for you. Please allow if you wish to use this feature"
The App Scipt require use of Drive API and requests user permission.
User selects Deny.
Later the user opens the same Spreadsheet with onOpen trigger.
The onOpen trigger checks if Drive API permissions have been Aloud.
if not:
A box pops up saying "This Spreadsheet requires the use of Drive API to create folders for you. Please allow if you wish to use this feature"
Drive API requests Permission again.
else:
The script executes normally

Differentiate User and Owner Info in Apps Script Installable Trigger

When I call Session.getActiveUser() from a function called by an installable trigger (e.g. an OnOpen Event trigger in Google Sheets - but not the Simple Trigger) in Apps Script, what user information am I getting?
I understand that installable triggers run under the owner/creator of the trigger regardless of who has opened the sheet. So would this always return that person's info?
Either way, how do I get the information of the other person? (e.g. if it gives owner info, how do I get the info of the user actually opening it - and vice-versa)
Update:
I got another user to test my script. I watched the logs while they were in the file, and it definitely reported THEM as the user, even when the installable OnOpen trigger was triggered.
This is good from the perspective that it showed them the correct menu options - he and I saw different menus per my OnOpen script, which is what I want.
However, this raises two issues for me:
This seems to go against the Google Documentation, which states: "Installable triggers always run under the account of the person who created them. For example, if you create an installable open trigger, it runs when your colleague opens the document (if your colleague has edit access), but it runs as your account. This means that if you create a trigger to send an email when a document is opened, the email is always sent from your account, not necessarily the account that opened the document."
In a future function, I will be calling an API from another App. This API will need my credentials (API ID and Secret). I was hoping / expecting that I could "sandbox" my credentials in an installable trigger - invisible to other users - that will allow them to use my credentials just for the specific functions which I would script into the API. If the installable trigger is in fact, NOT using my credentials, then how can I do this? I don't want to have to make every user go to the other App and generate their own set of API credentials, that will be unsustainable in this organization, and not everyone should need to do that.
It should return whomever triggered the script. But it depends if the security policy does allow you to access the user's identity. Seeing the documentation:
Gets information about the current user. If security policies do not allow access to the user's identity, User.getEmail() returns a blank string. The circumstances in which the email address is available vary: for example, the user's email address is not available in any context that allows a script to run without that user's authorization, like a simple onOpen(e) or onEdit(e) trigger, a custom function in Google Sheets, or a web app deployed to "execute as me" (that is, authorized by the developer instead of the user).
I have tested it and even an installable trigger won't return anything if it belongs to a different organization.
But you might be able to if other users belong to the same organization.
However, these restrictions generally do not apply if the developer runs the script themselves or belongs to the same Google Workspace domain as the user.
Workaround:
One thing I guess would be to assign the triggered function into a button and have the users click that upon opening the sheet. Via clicking the button, I have been able to show the User object using that method.
Or a webapp that will serve as a relay and will get the User details.

Allow script to edit locked cells

I have a complex spreadsheet where most of the sheet is locked and the user can only edit a handful of cells which triggers a bunch of calculations. This used to work fine but the problem now is I have added a drawing which I attached a script to so it acts as a button. Doing this forces the user to have to authorize and now the scripts run as that user so when the script tries to update cells that are locked to the user it fails.
How can I make it so a user can't type into cells, but my scripts can still update them. Basically I want the script to have full access to the sheet, not restricted by user permissions.
Workaround#1 -Service account:
Create a service account
Share your spreadsheet with edit permissions to the service account's email
Install and Use the Google oauth2 library to get Bearer token with necessary scopes(Drive/Sheets/Both). This token can be used to impersonate the service account.
Using the bearer token above, You can directly access the
google-sheets-api using urlfetch
OR use a published webapp(set to execute as "User accessing the app" and "Anyone") to use inbuilt services such as SpreadsheetApp. See Second related answer linked below.
In this case, PRIVATE_KEY of the service account acts as a password to access the spreadsheet with edit privileges. So, exposing it directly in the script editor will give access to any of the editors to access protected areas of the spreadsheet and all service account resources. So, in a way, protected areas are not protected in a absolute way. If protected areas need to be absolutely protected, You may be able to bypass this limitation
using two script projects: a bound one posting data to a unbound one, which is published as a web app and holds the private key. Here, editors can be supplied with passwords to access the unbound script.
Another way is to simply publish a addon, as a addon's source code is never visible to end users.
Workaround#2 - Installable triggers:
Use a installable edit trigger with a checkbox. Users click a checkbox in the unprotected area and script modifies the protected area.
Installable triggers run under the authority of the user who installed it and not as the current user.
They can bypass permission restrictions of the sheet. But this is a double edged sword. Anyone with edit permission will be able to trigger the script. Not only that, they may also be able to access the script editor and modify the script as they see fit. To limit foul usage,
Set the script to run only at a specified version: This can be done by setting the edit trigger manually in Tools > Script editor> Edit > Current project triggers > Add trigger > Select version. Script must have a saved version and be deployed as a webapp(doesn't need to be working).
Avoid providing unnecessary scopes to the script. Limit oauthScopes by editing manifest file. Preferably the only scope provided should be https://www.googleapis.com/auth/spreadsheets.currentonly
Related:
Is there a way to let a user edit another spreadsheet with a script and hide it from him at the same time?
Google App Script execute function when a user selects a cell in a range

Google App script: You do not have permission to call prompt

I have created a google script that show me a prompt dialog to write a comment when a column is edited. For some reason this only works with my email (script's creator), but with the other users that I have shared the spreadsheet don't work. When I open the script editor with other user accounts I can see the error in the View -> Execution Transcript: Execution failed: You do not have permission to call prompt.
My script function has a name "sendManualEmail" and I already have the trigger created when Event -> From spreadsheet -> On edit
I even created a new project for only that script and asked me the permissions to send emails with my account, but still not working for other users. I have read some other similar topic with same issue but I am still not able to fix mine. So thanks in advance for any comment!
You need your users to authorize your script. To do that, create a menu that activates on onOpen(). When clicked, send a message box, ensuring your users have to authorize your scripts to see the message.
Paste the following at the top of your script:
/** #OnlyCurrentDoc */
function onOpen() {
var ui = SpreadsheetApp.getUi();
ui.createMenu('myName') // your name
.addItem("Activate myName's script", 'activateMyStuff')
.addToUi();
}
function activateMyStuff() {
browser.msgBox('Script is activated! thanks.');
}
Important: when your users click the menu, they will also be prompted to authorize your scripts and all the permissions on the script's page. Make sure you clean up that script, otherwise your users may have to authorize weird things - and likely won't. Do test it with an alternate email address to see what others will see.
Lastly, consider publishing your script as an addon instead. It will make it that much easier for your users to authorize and use your work.
Are you logged into multiple Google accounts in the same browser?
Google Scripts insides may sometimes not work as expected when multiple accounts are authorized in the same browser session. Try logging out of all accounts except the one that owns the script.

How to run a spreadsheet bound script as the creator (not viewer/editor)

I made some script with my spreadsheet, which uses some Trigger and SendEmail functionalities. I have menu items in spreadsheet to control these triggers and sendemails.
Now when I share this spreadsheet with someone other, when he tried to access the trigger or sendemail functions from menu, the script asks for authorization as that user. IF authorized it will function as that user. e.g. send email as that user or make a new trigger as that user. This makes things double and useless
I want that any user accessing the script can use those functionalities, but won't require authorization to run as that user. The script should run as the creator of the sheet, so that no double triggering occurs. How should I do it?
You could create a web-app. Web-apps have the ability to run either as the user himself, or as the developer/publisher.
Under Execute the app as, select whose authorization the app should
run with: your account (the developer's) or the account of the user
who visits the app (see permissions).
https://developers.google.com/apps-script/guides/web
This web-app wouldn't have a sheet linked to it, but if you only use 1 sheet you can have the web-app access the sheet through the ID of the sheet. You could use your existing menu-items to trigger the webapp
Would this be a possible solution for your problem?