Getting data from a public readable Google Sheet in a script - google-apps-script

I'm trying to create a Google Script to access a range of cells from a Google Docs spreadsheet that I don't own but is shared. The spreadsheet is shared in a way that means it is publicly readable, but can't be written to. (Normal users can comment, but not edit)
When I try to run this script, I get an error: "You do not have permissions to access the requested document." I've authorized Google Scripts to my account, but that doesn't seem to be enough to do a getValues() call.
Here's my script so far:
function myFunction() {
var spreadsheet = SpreadsheetApp.openByUrl("https://docs.google.com/spreadsheets/d/1g24uo61ITTveZrUGx4ZY5nMoJp-n1NCxq5r4WMhAJU0/");
// I can access the name, this runs fine:
Logger.log(spreadsheet.getName());
SpreadsheetApp.setActiveSpreadsheet(spreadsheet);
SpreadsheetApp.setActiveSheet(spreadsheet.getSheets()[0]);
// fetch this sheet
sheet = spreadsheet.getActiveSheet();
var range = sheet.getRange(3,3,22,1);
// This next line fails:
var slicesAndCounts = range.getValues();
Logger.log(slicesAndCounts);
}
(The URL is the actual public spreadsheet, so this code should run as is)
Is there some way I can access the values in the spreadsheet from Google Scripts? I've considered trying to make a private copy of the spreadsheet, but I only need it for the duration of the script and I'm not sure if I can create a temporary copy easily. That solution also seems a bit heavy, considering I feel like I should be able to read the spreadsheet given I can access it via my browser or the Google Sheets app.

How about using Sheets API? At SpreadsheetApp, it seems that users cannot retrieve values from the shared spreadsheet. I think that the reason may be due to that the access token cannot use at SpreadsheetApp. In my environment, the same error also occurs. So I would like to propose to use Sheets API. When Sheets API is used, the access token is also used. By this, users can retrieve values from the shared spreadsheet.
In order to use this sample script, please enable Sheets API at Advanced Google Services and API console.
Enable Sheets API v4 at Advanced Google Services
On script editor
Resources -> Advanced Google Services
Turn on Google Sheets API v4
Enable Sheets API v4 at API console
On script editor
Resources -> Cloud Platform project
View API console
At Getting started, click Enable APIs and get credentials like keys.
At left side, click Library.
At Search for APIs & services, input "sheets". And click Google Sheets API.
Click Enable button.
If API has already been enabled, please don't turn off.
If now you are opening the script editor with the script for using Sheets API, you can enable Sheets API for the project by accessing this URL https://console.cloud.google.com/apis/library/sheets.googleapis.com/
About sample script :
The result of this sample is the same to that of your script. The values are retrieved from the range of 3,3,22,1 of 1st sheet in the shared spreadsheet.
In this sample script, it uses file ID of Spreadsheet. In your case, that is 1g24uo61ITTveZrUGx4ZY5nMoJp-n1NCxq5r4WMhAJU0 for https://docs.google.com/spreadsheets/d/1g24uo61ITTveZrUGx4ZY5nMoJp-n1NCxq5r4WMhAJU0/.
In this sample script, a1Notation is used for the range. In your case, getRange(3,3,22,1) is C3:C24.
For this script, when {ranges: "C3:C24"} is used, it means the 1st sheet of the spreadsheet. If you want to retrieve values from more than 2nd sheet, please use the sheet name like sheet2!C3:C24 and sheet3!C3:C24.
Script :
function myFunction() {
var spreadsheetId = "1g24uo61ITTveZrUGx4ZY5nMoJp-n1NCxq5r4WMhAJU0";
var slicesAndCounts = Sheets.Spreadsheets.Values.batchGet(spreadsheetId, {ranges: "C3:C24"}).valueRanges[0].values;
Logger.log(slicesAndCounts);
}
References :
Advanced Google Services : https://developers.google.com/apps-script/guides/services/advanced
Sheets API v4: https://developers.google.com/sheets/api/
If this was not useful for you, I'm sorry.

Related

How to access data on different Google Spreadsheet through Google Apps Script for user without Editor access?

I have 2 Spreadsheets, the 1st will Search on the 2nd spreadsheet with data using a google script function.
I want to keep the 2nd spreadsheet with data to be hidden (no editor access) from a user, but he/she will be able to Search on it via the google script function only.
I'm using google script Openbyurl to do it, but it won't let this user to run the Openbyurl unless he/she has editor access to the 2nd spreadsheet.
how should I deal with this?
Below function is in the 1st Spreadsheet, openByUrl links to 2nd Spreadsheet:
function onSearch(SN) {
var ss = SpreadsheetApp.openByUrl('docs.google.com/spreadsheets/...');
var sheets = ss.getSheets();
// search for data in ss sheets . . .
// return array of found data
}
I believe your goal is as follows.
You have 2 Google Spreadsheets "A" and "B".
You want to make the user retrieve values from Spreadsheet "B" with the script of Spreadsheet "A".
You don't want to share Spreadsheet "B" while Spreadsheet "A" is shared with the user.
In this case, as a workaround, how about accessing Spreadsheet "B" using Web Apps? By this, you can make the user access Spreadsheet "B" without sharing the Spreadsheet with the user. When this is reflected in your script, it becomes as follows.
Flow:
1. For Spreadsheet "B":
Please copy and paste the following script to the script editor of Spreadsheet "B".
function doGet(e) {
var SN = e.parameter.sn; // You can use the value of SN here.
var ss = SpreadsheetApp.getActiveSpreadsheet(); // Your 2nd Spreadsheet.
var sheets = ss.getSheets();
// search for data in ss sheets . . .
// return array of found data
var returnValues = ["sample"]; // Please replace this value for your actual script.
return ContentService.createTextOutput(JSON.stringify(returnValues));
}
2. Deploy Web Apps.
Please run this flow on the script editor of Spreadsheet "B". The detailed information can be seen at the official document.
On the script editor, at the top right of the script editor, please click "click Deploy" -> "New deployment".
Please click "Select type" -> "Web App".
Please input the information about the Web App in the fields under "Deployment configuration".
Please select "Me" for "Execute as".
This is the importance of this workaround.
Please select "Anyone" for "Who has access".
In this case, the user is not required to use the access token. So please use this as a test case.
Of course, you can also access your Web Apps using the access token. Please check this report.
Please click "Deploy" button.
Copy the URL of the Web App. It's like https://script.google.com/macros/s/###/exec.
When you modified the Google Apps Script, please modify the deployment as a new version. By this, the modified script is reflected in Web Apps. Please be careful this.
You can see the detail of this in the report of "Redeploying Web Apps without Changing URL of Web Apps for new IDE".
3. For Spreadsheet "A":
Please copy and paste the following script to the script editor of Spreadsheet "A". And, set your Web Apps URL. In this case, please give the value to SN.
function onSearch(SN) {
var url = "https://script.google.com/macros/s/###/exec"; // Please set your Web Apps URL here.
var res = UrlFetchApp.fetch(`${url}?sn=${SN}`);
var ar = JSON.parse(res.getContentText()); // This is the returned value from Spreadsheet "B".
// do something.
}
4. Testing:
When you run the script of onSearch, the value of SN is sent to Spreadsheet "B" and run the script of Spreadsheet "B", and the result values are returned. By this flow, you can retrieve the values from Spreadsheet "B" without sharing Spreadsheet "B" with the user.
Note:
In this sample script, when you directly run onSearch, the value of SN is not declared. So please be careful about this.
When you modified the Google Apps Script, please modify the deployment as a new version. By this, the modified script is reflected in Web Apps. Please be careful this.
You can see the detail of this in the report of "Redeploying Web Apps without Changing URL of Web Apps for new IDE".
My proposed script is a simple script. So please modify it for your actual situation.
References:
Web Apps
Taking advantage of Web Apps with Google Apps Script

Using private sheets with tabletop.js

I've used tabletop.js [1] in the past and is amazing! You can simply do anything you want seriously.
The only problem I saw is that you need to publish your spreadsheets to the web, which of course is really risky if you are working with sensitive data.
I'm in need now of using it in a project with sensitive data, so I was hoping someone can guide me on how to use it with spreadsheets that are not published to the web.
I've been searching for this for a long time without any success but seems that tabletop.js does support private sheets (here's the pull request that added this option [2]).
In fact, looking at the documentation they included it [1]:
authkey
authkey is the authorization key for private sheet support.
ASK: How am I suppose to use the authkey? can someone provide me with an example so I can try?
Thanks in advance!
[1] https://github.com/jsoma/tabletop
[2] https://github.com/jsoma/tabletop/pull/64
How about this answer?
Issue and workaround:
At "tabletop.js", from the endpoint (https://spreadsheets.google.com/feeds/list/###/###/private/values?alt=json) of request, it seems that "tabletop.js" uses Sheets API v3. And when authkey is used, oauth_token=authkey is added to the query parameter. In this case, unfortunately, it seems that the private Spreadsheet cannot be accessed with it. From this situation, unfortunately, I thought that in the current stage, "tabletop.js" might not be able to use the private Spreadsheet. But I'm not sure whether this might be resolved in the future update. Of course, it seems that the web-published Spreadsheet can be accessed using this library.
So, in this answer, I would like to propose the workaround for retrieving the values from Spreadsheet as the JSON object.
Pattern 1:
In this pattern, Google Apps Script is used. With Google Apps Script, the private Spreadsheet can be easily accessed.
Sample script:
When you use this script, please copy and paste it to the script editor and run the function myFunction.
function myFunction() {
const spreadsheetId = "###"; // Please set the Spreadsheet ID.
const sheetName = "Sheet1"; // Please set the sheet name.
const sheet = SpreadsheetApp.openById(spreadsheetId).getSheetByName(sheetName);
const values = sheet.getDataRange().getValues();
const header = values.shift();
const object = values.map(r => r.reduce((o, c, j) => Object.assign(o, {[header[j]]: c}), {}));
console.log(object) // Here, you can see the JSON object from Spreadsheet.
}
I thought that this might be the simple way.
Pattern 2:
In this pattern, the Web Apps created by Google Apps Script is used. When the Web Apps is used, the private Spreadsheet can be easily accessed. Because the Web Apps is created with Google Apps Script. In this case, you can access to the Web Apps from outside by logging in to Google account. And, the JSON object can be retrieved in HTML and Javascript.
Usage:
Please do the following flow.
1. Create new project of Google Apps Script.
Sample script of Web Apps is a Google Apps Script. So please create a project of Google Apps Script. In order to use Document service, in this case, Web Apps is used as the wrapper.
If you want to directly create it, please access to https://script.new/. In this case, if you are not logged in Google, the log in screen is opened. So please log in to Google. By this, the script editor of Google Apps Script is opened.
2. Prepare script.
Please copy and paste the following script (Google Apps Script) to the script editor. This script is for the Web Apps.
Google Apps Script side: Code.gs
function doGet() {
return HtmlService.createHtmlOutputFromFile("index");
}
function getObjectFromSpreadsheet(spreadsheetId, sheetName) {
const sheet = SpreadsheetApp.openById(spreadsheetId).getSheetByName(sheetName);
const values = sheet.getDataRange().getValues();
const header = values.shift();
const object = values.map(r => r.reduce((o, c, j) => Object.assign(o, {[header[j]]: c}), {}));
return object;
}
HTML&Javascript side: index.html
<script>
const spreadsheetId = "###"; // Please set the Spreadsheet ID.
const sheetName = "Sheet1"; // Please set the sheet name.
google.script.run.withSuccessHandler(sample).getObjectFromSpreadsheet(spreadsheetId, sheetName);
function sample(object) {
console.log(object);
}
</script>
spreadsheetId and sheetName are given from Javascript side to Google Apps Script side. From this situation, in this case, getObjectFromSpreadsheet might be instead of "tabletop.js".
3. Deploy Web Apps.
On the script editor, Open a dialog box by "Publish" -> "Deploy as web app".
Select "Me" for "Execute the app as:".
By this, the script is run as the owner.
Select "Only myself" for "Who has access to the app:".
In this case, in order to access to the Web Apps, it is required to login to Google account. From your situation, I thought that this might be useful.
Click "Deploy" button as new "Project version".
Automatically open a dialog box of "Authorization required".
Click "Review Permissions".
Select own account.
Click "Advanced" at "This app isn't verified".
Click "Go to ### project name ###(unsafe)"
Click "Allow" button.
Click "OK".
Copy the URL of Web Apps. It's like https://script.google.com/macros/s/###/exec.
When you modified the Google Apps Script, please redeploy as new version. By this, the modified script is reflected to Web Apps. Please be careful this.
4. Run the function using Web Apps.
You can test above scripts as follows.
Login to Google account.
Access to the URL of Web Apps like https://script.google.com/macros/s/###/exec using your browser.
By this, you can see the retrieved JSON object at the console.
Note:
When you modified the script of Web Apps, please redeploy the Web Apps as new version. By this, the latest script is reflected to the Web Apps. Please be careful this.
References:
Web Apps
Taking advantage of Web Apps with Google Apps Script

Google sheets Scripts not working in shared links

I am new to google sheets and scripts. I have created a searchable product list in google sheet and created a button clear the contents(not the formulas). Everything will work fine when I logged in. If i share the link with someone the script is not working and showing an error. The sheet is perfectly working.
function myFunction() {
var sheet = SpreadsheetApp.getActiveSheet();
var rangeList = sheet.getRangeList(['A8', 'E24']);
rangeList.clear({contentsOnly: true});
}
Is there any way to resolve this, please?
What you want to achieve is sadly not possible :( Please refer to Authorization for Google Services.
In line one of the documentation, you'll find -
Apps Script requires user authorization to access private data from built-in Google services or advanced Google services.
Given that you're using SpreadsheetApp service to access Sheets, a user needs to be logged in for the script functions to do as required.
Hope this clarifies.

Sheets function working in standalone Google Script but NOT when script is bound to a Google Sheet

I have this piece of code that is working perfectly when I use it in a standalone Google Script running as a web-app :
function getSubjectsRollNosFromGradeSheet() {
var spreadsheetId = '171hWWH8bV6kM1JJ2dvL80q3BoWC6eEc0U7l7wCTaXHE';
var rangeSubject = 'GradesT1!G1:AA1';
var values = Sheets.Spreadsheets.Values.get(spreadsheetId, rangeSubject).values;
However when I write this same code in a Script that is embedded inside another Google sheet (not the one being accessed for data) attached to a Google Form I get an error :
function updateForm(){
// call your form and connect to the drop-down item
var form = FormApp.openById("1VV_LeFJODZcIzfcYOh8vHEkhl033Lx4ZsXaY4Pm1ZUE");
var subList1 = form.getItemById("497738674").asListItem();
//pull data from another sheet
var spreadSheetID = "1EJSx62rZuLSKIgel7LH8hE7-68lupz4buutmVQBqi2I";
var rangeSubs = 'Sheet1!A1:A';
var subValues = Sheets.Spreadsheets.Values.get(spreadSheetID, rangeSubs).values;
I get this error
ReferenceError: "Sheets" is not defined. (line 16, file "Code")
Why is the Sheets object available in the first case but not in the second case and how should I change my code to overcome this error.
Sheets is one of methods of Advanced Google Services. So in order to use Sheets of Advanced Google Services, please enable Sheets API for Advanced Google Services and API console as follows.
Please do this at the bound script which included the script of your question.
Enable Sheets API v4 at Advanced Google Services
On script editor
Resources -> Advanced Google Services
Turn on Google Sheets API v4
Enable Sheets API v4 at API console
Enable Sheets API v4 at API console
On script editor
Resources -> Cloud Platform project
View API console
At Getting started, click Enable APIs and get credentials like keys.
At left side, click Library.
At Search for APIs & services, input "sheets". And click Google Sheets API.
Click Enable button.
References :
Advanced Google Services : https://developers.google.com/apps-script/guides/services/advanced
Sheets API v4: https://developers.google.com/sheets/api/
If I misunderstand your question, I'm sorry.

programmatically edit script in Google Drive document

I have a Google Drive document which writes values to a Google Drive spreadsheet using Google Apps Scripts.
The script associated with the document looks a lot like this:
// must change value to actual spreadsheet ID
RobProject.spreadsheetID = "spreadsheetID";
function onOpen()
{
// do stuff;
}
Each time I create a spreadsheet and its related documents, I manually change the value spreadsheetID to the spreadsheet's ID so the documents know to which spreadsheet they should write their values.
I would like a programmatic way to fill in the correct value for spreadsheetID into the Documents' scripts.
When I search for "edit scripts programmatically," I get tutorials for creating Google Apps Scripts, not editing scripts with scripts. Is there any way to edit Google Apps Scripts with a Google Apps Script?
If I understand correctly, you are working with a document and a spreadsheet. The document needs to know the Id of the spreadsheet.
There are some ways to access the Google Apps Script code through the API, but that is only for standalone projects, not for container-bound scripts (unfortunately).
You could consider using a naming convention for the document and spreadsheet so that you could use the Drive service to get from the document to the spreadsheet (DriveApp.getFilesByName()). Or possibly organize them by folder (DriveApp.getFoldersByName(), folder.getFiles()).
If you wanted to store the spreadsheet Id in a project property, you could build a UI in the document that let the user open up the list of files in Drive and pick the associated spreadsheet and then store the Id (ScriptProperties.setProperty('SpreadsheetId')).
Don't forget that onOpen has a parameter. You could use following code:
// Define global variable somewhere
RobProject = {};
function onOpen(e) {
RobProject.sSheet = e.source; // maybe the spreadsheet object is as useful as the ID
RobProject.spreadsheetID = e.source.getId();
// do stuff;
}
Please, for your own sake, don't try to write selfmodifying code.