Calling Google Apps Script in another Project File - google-apps-script

I am trying to call a Google Apps Script file that is in another project file following the sample here using UrlFetchApp.fetch.
I'm getting the same error that the original poster mentions but I am not having an success with my sample.
Did Google change something in the last 4 years that prevents me from calling the other script file?
See script below.
Below is the function that I am using to call the other project file
function makeRequest()
{
var webAppUrl = "https://script.google.com/macros/s/***/exec";
var auth = ScriptApp.getOAuthToken();
var header = { 'Authorization': 'Bearer ' + auth };
var options = { 'method':'post', 'headers':header };
var resp = UrlFetchApp.fetch(webAppUrl, options);
Logger.log(resp);
}
Below is the function that I am trying to call. Additionally, I have ran the authorizeDrive function and published as a webapp.
function authorizeDrive()
{
var forScope = DriveApp.getRootFolder();
}
function doPost()
{
var ss = SpreadsheetApp.openById('ssID');
var name = ss.getName();
Logger.log('called');
return ContentService.createTextOutput(name);
}

You want to run the Google Apps Script in the GAS project A by accessing to Web Apps from the GAS project B.
In your case, Web Apps is deployed by Who has access to the app: of Only myself or Anyone.
You want to access to Web Apps using the access token.
The GAS project A and B are in your Google Drive.
If my understanding is correct, how about this answer? Please think of this as just one of several possible answers.
I think that in your case, the scope is required to be added to the project including makeRequest(). So in order to add the scope for accessing to Web Apps using the access token, how about the following modification?
Modified script:
function makeRequest()
{
var webAppUrl = "https://script.google.com/macros/s/***/exec";
var auth = ScriptApp.getOAuthToken();
var header = { 'Authorization': 'Bearer ' + auth };
var options = { 'method':'post', 'headers':header };
var resp = UrlFetchApp.fetch(webAppUrl, options);
Logger.log(resp);
}
// DriveApp.getFiles() // This comment line is used for automatically detecting the scope.
Please add the // DriveApp.getFiles() of the comment line. This comment line is used for automatically detecting the scope.
In this case, https://www.googleapis.com/auth/drive.readonly is added to the scopes. If this didn't resolve your issue, please add the comment line of // DriveApp.createFile(blob). In this case, https://www.googleapis.com/auth/drive is added.
Note:
When the script of Web Apps side is modified, please redeploy it as new version. By this, the latest script is reflected to Web Apps. Please be careful this.
If the owner of GAS project of Web Apps is not your account which has the script of makeRequest(), at first, please share the GAS project file of Web Apps with your account. Then, please test it. This specification has added at April 11, 2018. Also, please be careful this.
References:
Web Apps
Taking advantage of Web Apps with Google Apps Script
If I misunderstood your question and this was not the result you want, I apologize.

Related

If LockService works for a common script for multiple users, is there an option when dealing with multiple scripts sending data to the same column?

I have 4 different scripts linked to 4 different accounts, which do their functions and in the end send a value from a specific cell to a final spreadsheet and in that final spreadsheet , to the same column of data:
Script Spreadsheet 1:
function Spreadsheet1() {
HIDDEN CODE LINES → THEY ARE COLLECT DATA FROM AN API
var first_sheet_id = SpreadsheetApp.openById('SPREADSHEET 1');
var first_sheet_page = first_sheet.getSheetByName('Sheet86');
var second_sheet_id = SpreadsheetApp.openById('SAME DESTINATION AS FOUR SCRIPTS');
var second_sheet_page = second_sheet.getSheetByName('Sheet86');
var r=1;
while(second_sheet_page.getRange(r,1).getValue()) {
r++;
}
second_sheet_page.getRange(r,1).setValue(first_sheet_page.getRange(1,1).getValue());
}
Script Spreadsheet 2:
function Spreadsheet2() {
HIDDEN CODE LINES → THEY ARE COLLECT DATA FROM AN API
var first_sheet_id = SpreadsheetApp.openById('SPREADSHEET 2');
var first_sheet_page = first_sheet.getSheetByName('Sheet86');
var second_sheet_id = SpreadsheetApp.openById('SAME DESTINATION AS FOUR SCRIPTS');
var second_sheet_page = second_sheet.getSheetByName('Sheet86');
var r=1;
while(second_sheet_page.getRange(r,1).getValue()) {
r++;
}
second_sheet_page.getRange(r,1).setValue(first_sheet_page.getRange(1,1).getValue());
}
Script Spreadsheet 3:
function Spreadsheet3() {
HIDDEN CODE LINES → THEY ARE COLLECT DATA FROM AN API
var first_sheet_id = SpreadsheetApp.openById('SPREADSHEET 3');
var first_sheet_page = first_sheet.getSheetByName('Sheet86');
var second_sheet_id = SpreadsheetApp.openById('SAME DESTINATION AS FOUR SCRIPTS');
var second_sheet_page = second_sheet.getSheetByName('Sheet86');
var r=1;
while(second_sheet_page.getRange(r,1).getValue()) {
r++;
}
second_sheet_page.getRange(r,1).setValue(first_sheet_page.getRange(1,1).getValue());
}
Script Spreadsheet 4:
function Spreadsheet4() {
HIDDEN CODE LINES → THEY ARE COLLECT DATA FROM AN API
var first_sheet_id = SpreadsheetApp.openById('SPREADSHEET 4');
var first_sheet_page = first_sheet.getSheetByName('Sheet86');
var second_sheet_id = SpreadsheetApp.openById('SAME DESTINATION AS FOUR SCRIPTS');
var second_sheet_page = second_sheet.getSheetByName('Sheet86');
var r=1;
while(second_sheet_page.getRange(r,1).getValue()) {
r++;
}
second_sheet_page.getRange(r,1).setValue(first_sheet_page.getRange(1,1).getValue());
}
LockService would work if it was the same script with multiple users trying to use it at the same time.
But in my case, there are four scripts with auto trigger (every 5 minutes) running and sending to the same column of the same spreadsheet.
Is there any way to be able to avoid having the risk of them meeting and putting values on the same lines?
If there is any way, please create a visual example of how to use it
so that I understand the method as I believe it is not as simple as my
knowledge limit.
From your updated question, in your situation, how about using Web Apps with LockService? From your question, I confirmed that 4 accesses are run simultaneously. In this case, in my benchmark for writing a Spreadsheet using Web Apps, it has already been found that 4 workers can be used. Ref From this result, I proposed to use Web Apps as a workaround for achieving your goal.
Usage:
1. Prepare script for Web Apps.
As a sample, please copy and paste the following script to a new Google Apps Script project. This script is used as Web Apps. In this case, you can also put this script in one of 4 scripts. But as a sample, I separated 4 clients and a server of Web Apps.
Please set your destination Spreadsheet ID.
function doGet(e) {
const lock = LockService.getDocumentLock();
if (lock.tryLock(350000)) {
try {
var ssId = e.parameter.spreadsheetId;
if (!ssId) return ContentService.createTextOutput("No spreadsheet ID.");
var first_sheet = SpreadsheetApp.openById(ssId);
var first_sheet_page = first_sheet.getSheetByName('Sheet86');
var second_sheet = SpreadsheetApp.openById('SAME DESTINATION AS FOUR SCRIPTS'); // <--- Please set your destination Spreadsheet ID.
var second_sheet_page = second_sheet.getSheetByName('Sheet86');
var r = 1;
while (second_sheet_page.getRange(r, 1).getValue()) {
r++;
}
second_sheet_page.getRange(r, 1).setValue(first_sheet_page.getRange(1, 1).getValue());
} catch (e) {
return ContentService.createTextOutput(e.message);
} finally {
lock.releaseLock();
return ContentService.createTextOutput("Done");
}
} else {
return ContentService.createTextOutput("Timeout");
}
}
2. Deploy Web Apps.
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 your situation, I thought that this setting might be suitable.
Of course, you can use the access token. If you want to use the access token, please set it as Anyone with Google account and use the access token at the client side.
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. Prepare script of 4 clients.
Spreadsheet1
Please copy and paste the following script to the script editor of Spreadsheet 1. And, please reinstall the trigger. Because the scope is authorized.
function Spreadsheet1() {
const srcSpreadsheetId = 'SPREADSHEET 1'; // Please set spreadsheet ID.
const webAppsUrl = "https://script.google.com/macros/s/###/exec"; // Please set your Web Apps URL.
const res = UrlFetchApp.fetch(webAppsUrl + "?spreadsheetId=" + srcSpreadsheetId);
console.log(res.getContentText());
}
Spreadsheet2
Please copy and paste the following script to the script editor of Spreadsheet 2. And, please reinstall the trigger. Because the scope is authorized.
function Spreadsheet2() {
const srcSpreadsheetId = 'SPREADSHEET 2'; // Please set spreadsheet ID.
const webAppsUrl = "https://script.google.com/macros/s/###/exec"; // Please set your Web Apps URL.
const res = UrlFetchApp.fetch(webAppsUrl + "?spreadsheetId=" + srcSpreadsheetId);
console.log(res.getContentText());
}
Spreadsheet3
Please copy and paste the following script to the script editor of Spreadsheet 3. And, please reinstall the trigger. Because the scope is authorized.
function Spreadsheet3() {
const srcSpreadsheetId = 'SPREADSHEET 3'; // Please set spreadsheet ID.
const webAppsUrl = "https://script.google.com/macros/s/###/exec"; // Please set your Web Apps URL.
const res = UrlFetchApp.fetch(webAppsUrl + "?spreadsheetId=" + srcSpreadsheetId);
console.log(res.getContentText());
}
Spreadsheet4
Please copy and paste the following script to the script editor of Spreadsheet 4. And, please reinstall the trigger. Because the scope is authorized.
function Spreadsheet4() {
const srcSpreadsheetId = 'SPREADSHEET 4'; // Please set spreadsheet ID.
const webAppsUrl = "https://script.google.com/macros/s/###/exec"; // Please set your Web Apps URL.
const res = UrlFetchApp.fetch(webAppsUrl + "?spreadsheetId=" + srcSpreadsheetId);
console.log(res.getContentText());
}
4. Testing.
After the script of Web Apps and the scripts of 4 clients were prepared, please run those functions of clients. By this, the script of Web Apps can be run with LockService. In this case, your 4 clients can be run simultaneously.
Note:
In this case, the order of the functions Spreadsheet1 to Spreadsheet4 cannot be controlled. 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".
References:
Benchmark: Concurrent Writing to Google Spreadsheet using Form
Web Apps
Taking advantage of Web Apps with Google Apps Script

ScriptApp.getOAuthToken not getting the right permissions for drive through url fetch app?

Trying to explore this with a very simple script but I'm getting an insufficient permissions error:
function mini(){
var gdriveId = "1hp8ncIG4Ww7FH8wi7HjJzzzzzzz";
var options = {
method: "GET",
headers: {
'Authorization': 'Bearer ' + ScriptApp.getOAuthToken()
},
}
var url = "https://www.googleapis.com/drive/v2/files/"+gdriveId+"/children";
var response = JSON.parse(UrlFetchApp.fetch( url, options).getContentText());
}
I tried enabling the v2 drive api in the advanced google services dropdown but that didn't work.
I believe your situation and goal as follows.
From gdriveId in your script, I thought that you want to retrieve the folder list in the root folder of gdriveId using the method of "Children: list" in Drive API v2.
You have already enabled Drive API at Advanced Google Services.
For this, how about this answer?
Modification points:
When your script is put to new GAS project and Drive API is enabled at Advanced Google Services, the scopes of the project is only https://www.googleapis.com/auth/script.external_request. The required scope can be automatically detected by the script editor. But, even when Drive API is only enabled, it seems that no scopes are added. I think that the reason of your issue is this.
Under above situation, if you want to retrieve the access token including the required scopes, in order to make the script editor automatically detect the scope of https://www.googleapis.com/auth/drive.readonly, for example, please put // DriveApp.getFiles() to the script as a comment line.
In this case, when you use the methods for other scopes in your script, those scopes can be automatically detected and added by the script editor.
Modified script 1:
When your script is modified, it becomes as follows.
function mini(){
var gdriveId = "1hp8ncIG4Ww7FH8wi7HjJzzzzzzz";
var options = {
method: "GET",
headers: {
'Authorization': 'Bearer ' + ScriptApp.getOAuthToken()
},
}
var url = "https://www.googleapis.com/drive/v2/files/"+gdriveId+"/children";
var response = JSON.parse(UrlFetchApp.fetch( url, options).getContentText());
}
// DriveApp.getFiles() // <--- Added this comment line. By this, the scope of https://www.googleapis.com/auth/drive.readonly is added.
Modified script 2:
When the method of Advanced Google service is used, the scope of https://www.googleapis.com/auth/drive is automatically added. By this, the following script works.
function test() {
var gdriveId = "1hp8ncIG4Ww7FH8wi7HjJzzzzzzz";
var res = Drive.Children.list(gdriveId);
console.log(res)
}
Other pattern:
From June 1, 2020, the files and folders in the shared Drive can be retrieved by Drive service. So you can also use the following script.
function myFunction() {
const getFolderList = (id, folders = []) => {
const f = DriveApp.getFolderById(id);
const fols = f.getFolders();
let temp = [];
while (fols.hasNext()) {
const fol = fols.next();
temp.push({name: fol.getName(), id: fol.getId(), parent: f.getName()});
}
if (temp.length > 0) {
folders.push(temp);
temp.forEach((e) => getFolderList(e.id, folders));
}
return folders.flat();
};
var gdriveId = "###"; // Please set the Drive ID.
const res = getFolderList(gdriveId);
console.log(res);
}
References:
Advanced Google services
Children: list of Drive API v2
Authorization Scopes
If you want to give permission to write with ScriptApp.getOAuthToken(), just add the following code in a commented out form and authorize it at runtime. If you don't do this, you'll only be able to download and browse.
//DriveApp.addFile("test");
Reference URL:https://00m.in/UeeOB

How to handle authorization for Apps Script from Chrome extension

I try to execute a Google Apps Script from my Chrome extension. My Google Apps Script uses Spreadsheet Service - it reads data from the user's spreadsheet, url of which is provided by user to my extension and then sent by extension to GAS. So, I've deployed Google Apps Script as web app, which is executed by a user accessing the web app and can be accessed by anyone. The problem is that when the script is run for the first time by a new user, the user can't see the authorization request so the script can't access the spreadsheet and send data to the extension. Everything works fine only if a user runs the script manually in the browser - only then the authorization request appears.
My question is how extension can trigger the authorization request to appear automatically?
I have read this question with answers Running Apps Script from Chrome extension requires authorization but there are only instructions on how to avoid authorization, which in my case can't be avoided. I would appreciate any help with this.
Update: I'm trying to use getAuthorizationUrl() as advised in the comments and now I'm stuck again. Here is the code from the Google Apps Script:
function doGet(e) {
var url=e.parameter.url;
var ss = SpreadsheetApp.openByUrl(url);
var authInfo = ScriptApp.getAuthorizationInfo(ScriptApp.AuthMode.FULL);
var status=authInfo.getAuthorizationStatus();
if (status=="NOT_REQUIRED") {
var sheet = ss.getSheets()[0];
var data = getData(sheet);
if(!data) {
data = '';
}
return ContentService.createTextOutput(JSON.stringify({'result': data})).setMimeType(ContentService.MimeType.JSON);
} else {
var authUrl=authInfo.getAuthorizationUrl();
return authUrl;
}
}
In both cases responseType of my XMLHttpRequest will be "", so how can my extension distinguish between 2 possible responses? I need something like this in the content script (don't know what to put inside if):
xhr.onreadystatechange = function(response) {
if (xhr.readyState === 4 && xhr.status == 200) {
try {
if (??response is authUrl) {
//do redirect;
}
else {
//process the data from the spreadsheet
}

Pushing Slack data to Google Sheets w/ Event Subscription

I'm trying to create a bot on Slack that sends the data of new messages sent to a private channel (that the bot is in) to a Google Sheet. I was successfully able to do this with data following a Slack slash command, by using this script:
function doPost(e) {
if (typeof e !== 'undefined') {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Sheet1');
sheet.getRange(1,1).setValue(JSON.stringify(e));
}
}
I tried using the same script with the Events API, but it needs to pass a request with a challenge parameter, and the endpoint must respond with the challenge value. Using the GScript web app's URL, I keep getting a failed response. How do I have the URL Verification Handshake work with Google Sheets and respond with the correct challenge string?
HTTP Post Fail
How about this modification?
The official document says as follows. This has already been mentioned in your question.
challenge: a randomly generated string produced by Slack. The point of this little game of cat and mouse is that you're going to respond to this request with a response body containing this value.
So please modify your script for returning the value of challenge from doPost as follows.
Modified script:
function doPost(e) {
if (typeof e !== 'undefined') {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Sheet1');
sheet.getRange(1,1).setValue(JSON.stringify(e));
}
var v = JSON.parse(e.postData.contents); // Added
return ContentService.createTextOutput(v.challenge); // Added
}
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 Web Apps. Please be careful this.
References:
Subscribing to event types
createTextOutput(content)
Added:
From your replying of I'm getting the same error., I thought that the latest script might not be reflected to Web Apps. And as a simple script, please copy and paste the following script instead of your script. And please redeploy the Web Apps as new version. Then, please test it again.
Sample script:
function doPost(e) {
var v = JSON.parse(e.postData.contents);
return ContentService.createTextOutput(v.challenge);
}

How to download a file using a url in Google Apps Script

I am fairly new to Google Apps Script. I am using Google's functionality to access the DFA/DCM Trafficking and Reporting API through App Scripts without having to use OAuth.
When I run the DCM Report to then convert into google sheets, I am not able to figure out how to use either urls i'm supplied with to download the CSV.
Here is the code i'm using.
var file = DCM.Reports.run(profile.profileId,30792432);
var file2 = DCM.Files.get(30792432, file.id);
//wait till running of the report is complete.
file2 = DCM.Files.get(30792432, file.id);
var response = UrlFetchApp.fetch(file2.urls.browserUrl);
I also try using:
file2.urls.apiUrl();
for the UrlFetchApp service, but that didn't work either.
Any help on how to execute the url to download the file as an object where I can paste into google sheets would be greatly appreciated.
Add the ScriptApp authorization bearer as a header in the parameters while using the apiurl call. Something like:
var token = ScriptApp.getOAuthToken();
var headersOptions = {
Authorization : 'Bearer ' + token
};
var options = {
headers : headersOptions
};
var csvDoc = UrlFetchApp.fetch(file2.url.apiurl, options);