Google Apps Script XMLHttpRequest not showing parameters - google-apps-script

I am able to make the POST request to my google apps script web app, but I can't access my e.parameters when I log them.
HTML CODE:
<form onsubmit="submitForm(event)">
<input type="text" name="fname" required>
<button type="submit">Submit</button>
</form>
JS CODE:
function submitForm(e){
e.preventDefault()
var url = "https://xxxxxxxxxxxxxxxxxxxxxx/exec"
var params = "employeeStatus='Active'&name='Henry'";
var xhr = new XMLHttpRequest()
xhr.open("POST",url,true)
xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded")
xhr.onreadystatechange = ()=>{
var readyState = xhr.readyState
var status = xhr.status
if(readyState == 4 && status == 200){
var response = JSON.parse(xhr.responseText)
console.log(response)
}
}
xhr.send(params)
}
APPS SCRIPT CODE:
function doPost(e){
var values = e.parameters;
Logger.log(values)
return ContentService.createTextOutput(JSON.stringify({"a":5,"b":2}))
}
Can anyone please tell me what I'm doing wrong here? I've iterated the apps script code to try and log the e.parameters but I'm unable to get anything to work.
***NOTES:
I'm aware that the "params" value is NOT the same as the form input value
I return the JSON string just to ensure that the code is running all the way through and I can practice JSON.parse/JSON.stringify on the client-side.

When I saw your script, I think that your value of e.parameters is {"employeeStatus":["'Active'"],"name":["'Henry'"]}.
About I've iterated the apps script code to try and log the e.parameters but I'm unable to get anything to work., I think that the reason for your issue is due to that your request of "XMLHttpRequest" include no access token. From your request, I thought that the settings of Web Apps might be Execute as: Me and Who has access to the app: Anyone with V8 runtime. If my understanding is correct, the reason for your issue is due to that.
If you want to show Logger.log(values) in the log, please include the access token to the request header as follows.
Modified script:
function submitForm(e) {
e.preventDefault();
var url = "https://script.google.com/macros/s/###/exec"; // Your web apps URL.
url += "?access_token=###your access token###";
var params = "employeeStatus=Active&name=Henry";
var xhr = new XMLHttpRequest();
xhr.open("POST", url, true);
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xhr.onreadystatechange = () => {
var readyState = xhr.readyState;
var status = xhr.status;
if (readyState == 4 && status == 200) {
var response = JSON.parse(xhr.responseText);
console.log(response);
}
}
xhr.send(params);
}
In this case, as a test, you can simply retrieve your access token by the following Google Apps Script. // DriveApp.getFiles() is put for automatically detecting the scope of Drive API for accessing Web Apps. The expiration time of this access token is 1 hour. Please be careful about this. When the expiry time is over, please retrieve the access token again.
const sample = _ => {
console.log(ScriptApp.getOAuthToken());
// DriveApp.getFiles()
}
When the above script is run, the following value is shown in the log by Logger.log(values).
{access_token=[###], employeeStatus=[Active], name=[Henry]} : This is due to Logger.log.
When console.log(values) is used, { access_token: [ '###' ], employeeStatus: [ 'Active' ], name: [ 'Henry' ]} is shown.
Note:
As another approach, for example, when you want to check the value of e of doPost, I think that you can store the value in a Spreadsheet as a log as follows. By this, when doPost is run, the value of e is stored in the Spreadsheet as a log. In this case, the access token is not required to be used.
function doPost(e) {
SpreadsheetApp.openById("###spreadsheetId###").getSheets()[0].appendRow([new Date(), JSON.stringify(e)]);
return ContentService.createTextOutput(JSON.stringify({ "a": 5, "b": 2 }))
}
Note
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".
Reference:
Taking advantage of Web Apps with Google Apps Script

Related

Google sheets web app blocked by CORS when trying to send a get request on the front-end

I'm trying to make a google web app, but when I to send a get request, it gives me an error saying "_ has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource."
Javascript (front-end only):
fetch("https://script.google.com/macros/s/.../exec", {
method: "GET",
headers: {
"Content-Type": "application/json"
}
}).then(response => {
console.log(response);
});
App Script:
function doGet(e) {
// get spreadsheet
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheet = ss.getSheetByName('DataCollection');
const max = sheet.getRange("F1").getValue();
// get data and sort by amount
const data = [];
for (let i = 1; i <= max; i++) {data.push({name: sheet.getRange("B" + i).getValue(), data: sheet.getRange("A" + i).getValue()});}
data.sort((a, b) => (a.data > b.data) && -1 || 1);
return ContentService.createTextOutput(JSON.stringify(data)).setMimeType(ContentService.MimeType.JSON);
}
Currently, the deployment of the web app is set to execute as myself and anyone has access, and my webpage is static.
The web app works perfectly fine when I tested the code, but CORS blocks the request when I send it through my webpage. I've tried multiple solutions that worked for other people, but I kept getting the same result.
Solutions I've tried:
jQuery.ajax({
crossDomain: true,
url: "https://script.google.com/macros/s/.../exec",
method: "GET",
dataType: "jsonp",
success: response => console.log(response)
});
I tried adding redirect: "follow" to the fetch which did nothing
I tried adding mode: "no-cors" to the fetch which returned an empty response
Modification points:
I thought that in your Javascript, no request header is required to be used, because of the GET method. And, I thought that this might be the reason for your issue.
And, in order to retrieve the values from your Web Apps as a JSON object, how about modifying .then(response => {console.log(response);}) to then(res => res.json()).then(res => console.log(res))?
And, when I saw your Google Apps Script, I thought that the process cost will become high because getValue() is used in a loop.
When these points are reflected in your script, how about the following modification?
Modified script:
Google Apps Script side:
function doGet(e) {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheet = ss.getSheetByName('DataCollection');
const max = sheet.getRange("F1").getValue();
const data = sheet.getRange("A1:B" + sheet.getLastRow()).getValues().splice(0, max).map(([data, name]) => ({ name, data }));
data.sort((a, b) => (a.data > b.data) && -1 || 1);
return ContentService.createTextOutput(JSON.stringify(data)).setMimeType(ContentService.MimeType.JSON);
}
In your script, it seems that the event object e is not used.
Javascript side:
fetch("https://script.google.com/macros/s/###/exec") // Please set your Web Apps URL.
.then(res => res.json())
.then(res => console.log(res));
Note:
When I tested your showing script, I confirmed the same issue related to CORS. And, when my proposed script is used, I confirmed that the issue was removed.
When you modified the Google Apps Script of Web Apps, please modify the deployment as a new version. By this, the modified script is reflected in Web Apps. Please be careful about this.
You can see the detail of this in my report "Redeploying Web Apps without Changing URL of Web Apps for new IDE (Author: me)".
By the way, from your Javascript, in this case, it supposes that your Web Apps is deployed as "Execute as: Me" and "Who has access to the app: Anyone". Please be caraful about this.
References:
map()
Benchmark: Reading and Writing Spreadsheet using Google Apps Script (Author: me)
Taking advantage of Web Apps with Google Apps Script (Author: me)

doPost not running for other users in google appscript

I have deployed my appscript as a form addon and as a web app both.
Everything seems to be working fine in the container form. But now I'm facing this issue where doPost function is not running as I have to run the function as other user. I tried this code from this answer, but this is also giving same authorization error.
function merry2script() {
var url = 'https://script.google.com/macros/s/AKfycbzM97wKyc0en6UrqXnVZuR9KLCf-UZAEpzfzZogbYApD9KChnnM/exec';
var payload = {payloadToSend : 'string to send'};
var method = 'post'
var headers: {"Authorization": "Bearer " + ScriptApp.getOAuthToken()}
var response = UrlFetchApp.fetch(url, {method : method, payload: payload, headers: headers}).getContentText();
Logger.log(response);
return;
}
Is this the correct way to post to appscript with oauth token?
If not how can I send a post request ?
I deployed the web app with these settings
I'm getting this error
I've been stuck for 3 days any help is appreciated
Thank you
UPDATED QUESTION:
APPSCRIPT DOPOST
function doPost(e) {
var data = JSON.stringify(e);
var jsonData = JSON.parse(data);
let query = jsonData.queryString;
let params = query.split("&");
let destinationId = params[0].split("=")[1];
// code is breaking here saying "you don't have access to the document"
let ss = SpreadsheetApp.openById(destinationId);
let sheetName = ss.getActiveSheet().getSheetName();
let dataSheet = ss.getSheetByName(sheetName);
var uniqueIdCol = dataSheet.getRange("A1:A").getValues();
let rowToUpdate;
// code to update row...
}
BACKEND CODE
// call appscript to update status sheet
const data = {
comment
};
let scriptId = process.env.DEPLOYMENT_SCRIPT_ID;
const config = {
method: "post",
url: `https://script.google.com/macros/s/${scriptId}/exec?destinationId=${destinationId}&uniqueId=${uniqueId}&status=${status}`,
data,
headers: {
Authorization: `Bearer ${respondent.form.oAuthToken}`,
},
};
await axios(config);
These are the scopes which I requested to user
"https://www.googleapis.com/auth/script.container.ui",
"https://www.googleapis.com/auth/forms.currentonly",
"https://www.googleapis.com/auth/script.external_request",
"https://www.googleapis.com/auth/userinfo.email",
"https://www.googleapis.com/auth/spreadsheets",
"https://www.googleapis.com/auth/script.send_mail",
"https://www.googleapis.com/auth/forms",
"https://www.googleapis.com/auth/script.scriptapp",
"https://www.googleapis.com/auth/drive"
UPDATED QUESTION 2
I had made a script which can write to google sheet with some extra data which I send from my node backend.
So my script has doPost function which is invoked from backend. I send destinationId of the sheet to know in which sheet to write as in the code above.
I have deployed the webapp as Execute as: Me and Who has access to the app: Anyone.
I'm able to run the doPost function but not able to write to sheet.
Hope my question is clear
So after struggling for 4 days I was able to send email and write to spreadsheet with users OAuth token by directly interacting with Sheets API and Gmail API instead of doing it through ScriptApp doPost method

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 execute Google App script from external script

I am attempting to build a web app that will create a Google Doc from a template and populate it with user provided data. Using Google's quickstart example in the documentation, I can successfully authorize and access the Google Drive file system. Now I need to programmatically open a template Google Doc (or even create one from scratch) and add the data.
This is rather easily done using App Script's Document Service (the DocumentAppclass). So I can do something like:
function createDoc(contentArray) {
var doc = DocumentApp.create('Sample Document');
var body = doc.getBody();
var rowsData = contentArray; // data submitted with HTML form passed as arg
body.insertParagraph(0, doc.getName())
.setHeading(DocumentApp.ParagraphHeading.HEADING1);
table = body.appendTable(rowsData);
table.getRow(0).editAsText().setBold(true);
}
in a standalone App Script and successfully create the new Google Doc on Google Drive. I can't figure out how to execute this App Script from my external web app. Is there a way to do this or do I need to find a different way to create Google Docs (and add content) using just the Drive API?
EDIT:
here is the GET request from my web app:
var gurl = "https://script.google.com/macros/s/AKfycbwMHKzfZr1X06zP2iEB4E8Vh-U1vGahaLjXZA1tk49tBNf0xk4/exec";
$.get(
gurl,
{ name: "john", time: "2pm",},
function(data) {
console.log(data);
},
"jsonp"
)
and here is my doGet():
function doGet(e) {
var result = "";
var name = e.parameter.name;
Logger.log(name);
try {
result = "Hello " + name;
} catch (f) {
result = "Error: " + f.toString();
}
result = JSON.stringify({
"result": result
});
var doc = DocumentApp.create('ballz3');
var body = doc.getBody();
var rowsData = [['Plants', 'Animals'], ['Ficus', 'Goat'], ['Basil', 'Cat'], ['Moss', 'Frog']];
body.insertParagraph(0, doc.getName())
.setHeading(DocumentApp.ParagraphHeading.HEADING1);
table = body.appendTable(rowsData);
table.getRow(0).editAsText().setBold(true);
Logger.log('DOc Name: ' + doc.getName());
return ContentService
.createTextOutput(e.parameter.callback + "(" + result + ")")
.setMimeType(ContentService.MimeType.JAVASCRIPT);
}
In order to run a script from an external location like your web app you need to follow some set-up and use the Script API. The documentation provided below has a great example on how to run your scripts from outside.
Additionally, you can use the APIs directly, with services like OAuth to use the APIs directly in a way that can save you some time and make your code more simple. Using OAuth can provide you with simple API requests. To use it:
Go to the link provided below, select the desired scope (Drive for this example).
Exchange the authorization token for refresh/access tokens.
Proceed to configure the request. Here you can set all the parameters for the request, and even select from the existing operations the scope has available (“List possible operations” button).
The resulting request will look like the one below:
GET /drive/v3/files HTTP/1.1 Host: www.googleapis.com Content-length:
0 Authorization: Bearer [YOUR-TOKEN]
Beneath it you will see the server response to the request.
Documentation URL: https://developers.google.com/apps-script/api/how-tos/execute
OAuth Playground: https://developers.google.com/oauthplayground/