Accessing URL Parameters in deployed webapp - google-apps-script

I can't seem to access URL parameters in my simple webapp. From the documentation it looks very simple:
function doGet(e) {
var foo = (e.parameters.test);
return ContentService.createTextOutput("Test: " + foo);
}
But foo keeps coming back as "undefined" when I run the webapp, either using the "my latest code" developer link, or a deployed version.
I've also noticed that when the page loads the URL Parameters I tack on at the end disappear.
The URL looks like:
https://script.googleusercontent.com/a/macros/mysite.com/echo?user_content_key=superlongstring&lib=otherstring&test=helloworld

You should tack on the parameter before the script URL redirects.
So it should look like -
https://script.google.com/macros/s/longstring/exec?test=helloworld
The redirected URL is really only for the output and its not re-usable and it wont trigger any new processing.

Related

Why is doGet() failing without posting logs?

I suppose my question is twofold: doGet() in the following context will just fail after 0.1~0.2 seconds without posting logs, so I have no idea how to troubleshoot it by myself. Additionally, if I'm having the script execute on my behalf, do I have to push a request with my authorization token to a more "pertinent" area than just the sheet name, such as within the iteration itself? Read further for more details:
I have a source spreadsheet where I am cross-referencing user inputted data to validate the information we have "on file". Most of our clients are over the age of 55, so I am trying to reduce end-user complexity by having the script run on my behalf whenever they need to use it (to bypass the Authorization screen, with the big scary "This application could be unsafe!" message). The way I've read to accomplish this seems to be with doGet(), so I set up a low-level HTTP Get request that just pushes a doGet() with my OAuth token, returning the sheet name. I also set up a masking function specifically to do this, and linked it to the button originally used for the iteration logic. The doGet() looks like this:
const doGet = e => {
Logger.log(`Recieved HTTP request.`);
const content = ContentService.createTextOutput(iterator(e));
Logger.log(content);
return content;
}
and the button that uses UrlFetchApp looks like:
const runMask = () => {
const active = SpreadsheetApp.getActiveSheet().getSheetName();
const v4 = 'https://script.google.com/macros/s/<scriptid>/dev' // ScriptApp.getService().getUrl() posts 404
UrlFetchApp.fetch(`${v4}?sheetName='${active}'`, {
headers: { Authorization: `Bearer ${ScriptApp.getOAuthToken()}` },
});
I have some logs set up within the real runMask() that proceed all the way to the end of the program, giving me real URLs and OAuth tokens, so I know it's making it through runMask() without an issue. However, the doGet() log doesn't post anything, even at the top of the function. I can see that it's executing the trigger in my execution log, but the log itself remains empty.
I've tried:
using ScriptApp.getService().getUrl() in place of v4: posts 404 in the log w/ truncated server response
replacing ${active} with the name of the sheet: same issue; logging ${active} also returns the correct name of the sheet.
Beyond this, I'm not even sure what to do. I have everything scoped correctly (auth/spreadsheets.currentonly, auth/script.external_request, and auth/userinfo.email), and I have no issues about operational security (as both the spreadsheet and script are written by me, the clients have no need to grant access to their entire drive). Before trying to implement doGet() and bypass the authorization screen, the iterator itself worked just fine. As such, I have chosen not to include it here, as it's hardly relevant (the function that executes the iteration function never makes it to that point).
I understand this has been quite the deluge of information; I'd be happy to provide more information or context as needed.
Getting ReferenceError: iterator is not defined (line 12, file "ag2")
With this:
const doGet = e => {
Logger.log(`Recieved HTTP request.`);
const content = ContentService.createTextOutput(iterator(e));
Logger.log(content);
return content;
}
Issued with url/exec?option=A
It runs with
const doGet = e => {
Logger.log(`Recieved HTTP request.`);
const content = ContentService.createTextOutput(JSON.stringify(e));
Logger.log(content);
return content;
}
and returns the appropriate stringified object
Only use the test URL (/dev) for testing the web app from a web browser.
Before doGet from a web browser using a versioned deployment (/exec) remember to publish a new version.
Assign a Google Cloud Project to your Google Apps Script project. For details see https://developers.google.com/apps-script/guides/cloud-platform-projects.
To make it easier to debug your avoid calling functions from a Google Apps Script method like createTextOutput, instead, assign the function result to a variable and use it as the method parameter, i.e. replace
const content = ContentService.createTextOutput(iterator(e));
by
const something = iterator(e);
const content = ContentService.createTextOutput(something);
For debugging purposes, create a function to call your doGet function, and check that it hasn't any problem to run, i.e.
function __test__doGet(){
const e = {
parameter: {}
}
doGet(e);
}
Related
Exception handling in google apps script web apps
Issue:
When I saw your question, I'm worried about I have everything scoped correctly (auth/spreadsheets.currentonly, auth/script.external_request, and auth/userinfo.email).
If you are using only the following scopes at oauthScopes of appsscript.json,
https://www.googleapis.com/auth/spreadsheets.currentonly
https://www.googleapis.com/auth/script.external_request
https://www.googleapis.com/auth/userinfo.email
Unfortunately, these scopes cannot be used for access to Web Apps. Although I'm not sure about the method for running your function of runMask, I thought that this might be the reason for your issue.
Solution:
If you want to access Web Apps of https://script.google.com/macros/s/<scriptid>/dev using the access token retrieved by ScriptApp.getOAuthToken(), please include the following scope.
https://www.googleapis.com/auth/drive.readonly
or
https://www.googleapis.com/auth/drive
After you include the above scope, please reauthorize the scopes, and test it again. When your function of iterator has already been declared and the script worked, by running runMask, you can see the log of Logger.log(Recieved HTTP request.) and Logger.log(content) at the log.
Reference:
Taking advantage of Web Apps with Google Apps Script

GetURL returns wrong URL

I have a Google App Script program that has a number of HTML pages. To generate the URL's for links to individual pages, I use this function on the server code:
function getScriptUrl() {
var url = ScriptApp.getService().getUrl();
return url;
}
to return a URL that I could then embed like this, on the client side:
<?var myURL = getScriptUrl();?><a class="btn btn-success" href='<?=myURL?>?page=CreateNew'>My Button/a>
This always returned the URL of the app. So, if am testing in the Dev version, it returns the Dev URL, if I am in the Exe version, it returns the Exe URL. But, now, if I am in the Dev version, it returns the Exe URL. This was never an issue in the past but started today. Does anyone know why this is happening or a better way to generate the URL to create links between pages?
EDIT:
I have tried to republish the app (in Legacy Editor and the New Editor), log out of G Suite account, clear my cache but these attempts did not work to address the issue.
This should a Google part issue, I use the same logic to auto direct to prod and test page. It works fine in the passed few month. But it can't work from last week.
I too have this problem. Until recently I had this code to help me distinguish whether I was testing the development version or the deployed version. It worked for months.
const url = ScriptApp.getService().getUrl();
if (url.endsWith('dev')) {
// more code here
}
Now getUrl() will always return the exec url.
(I know this isn't an answer, but I am a first time poster and do not have enough points to "Add a comment" and I don't want to ask a duplicate question)

Google Apps Script Async function execution on Server side

I have a GMail add-on which uses CardService for UI. Some of the callback functions for the Card actions take over than 30 sec. Thus, I'm getting the following error.
Gmail could not perform this add-on action.
Is there, any way to run Google Apps Script functions on the Server side asynchronous way, so I can return to a user some notification and continue work behind the scenes.
I have tried using some libraries like this one but with no luck, I'm able to use syntactically the Promises but functionally it's still synchronous.
As of now, there is no asynchronous execution for the Gmail add-on. Even if there is something, there is no way to refresh the UI without user action.
But, there is a hack. What you can do is, if there is a long running process is there, just create a "openlink" action (set link), which should open a url (https://yourhtmlpageurl?redirect_uri=) with html response. This html can have jquery ajax call, which can wait for some time. Once you get the response in the html window, redirect the page to the redirect_uri that is passed by passing the response data. So, our add on will get a callback to the function with parameter as json object with all the query parameters to the redirect_uri. Once you get the expected response, cache the response by using CacheService. return some html success template, which should automatically close the window by itself.
For Creating openlink action:
For generating redirect script URI with state token:
function generateNewStateToken(callbackName, user_info) {
return ScriptApp.newStateToken()
.withMethod(callbackName)
.withArgument("user_info", JSON.stringify(user_info))
.withTimeout(3600)
.createToken();
}
function getRedirectURI() {
return "https://script.google.com/macros/d/" + ScriptApp.getScriptId() + "/usercallback";
}
var htmlUrl = <your_html_url> + "?redirect_uri="+ getRedirectURI() + "&state=" + generateNewStateToken("asyncCallback", {});
CardService.newOpenLink().setUrl(htmlUrl).setOpenAs(CardService.OpenAs.OVERLAY).setOnClose(CardService.OnClose.RELOAD_ADD_ON);
function asyncCallback(data) {
data.response; // response which you can pass from script
CacheService.getUserCache().put("long_running_process_resp", data.response);
data.user_info; // user_info passed during state token creation
// cache the data using cache service
return HtmlService.createHtmlOutputFromFile("success");
}
success.html:
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<link rel="stylesheet" href="https://ssl.gstatic.com/docs/script/css/add-ons1.css">
</head>
<body>
<div class="sidebar">
<p>Long running Process completed.</p>
</div>
</body>
<script>
setTimeout(function() {
top.window.close();
}, 2000);
</script>
</html>
Once the success.html is closed by itself, there will be a refresh of the gmail add on. So, you can lookup for the long running response data from CacheService.
Let me know if you have any more questions on this process.
Sabbu's answer is pretty good. But, if you want to trigger the long running process without expecting the user to click on the openlink, you can render an image using:
CardService.newImage().setImageUrl('https://yourservice.com/pixel/action.png?name=value&_nocache='+new Date().getTime())
On the server side, you can map the path '/pixel/action.png' to the long running code which returns a tiny transparent pixel (1x1) on complete:
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg==

UrlFetch, No response

I'm trying to fetch URL using UrlFetchApp.fetch in google-apps-script.
This is my code:
function myFunction() {
var response = UrlFetchApp.fetch("https://www.fastlane.co.il/Mobile.aspx",
{muteHttpExceptions:true});
Logger.log(response.getContentText());
}
I have set a break point on every line of the code and started with debugger. But when I get to the second line, there is nothing in "response" var and the "continue" button just leaves me on this line like it have been loop line.
If I change the URL to "https://www.google.com", everything works ok.
If I change the URL to "https://www.google.com/not-a-real-page", I got an expected exception.
But with the needed URL there is no exception at all.
What am I doing wrong?
If the url is blocking an IP from outside of your home country then perhaps consider where the URL fetch is originating from. It most likely is originating from the Google's servers in Palo Alto California. I also cannot access that site from my web browser in Korea. I would say the site is blocking the call from America.

Parameter passing to webapp on doGet(e)

I'm trying to figure out how parameters in URL are passed to doGet() function.
I tried to do
https://script.google.com/a/macros/[my domain].com/s/[some random string generated by GAS] followed by "&variable=data"
but it only gave me this error:
"Sorry, the file you have requested does not exist."
FYI, https://script.google.com/a/macros/[my domain].com/s/[some random string generated by GAS] works well without "&variable=data".
To add GET parameters to the end of a URL, you need to use a ?.
So your URL should look something like this
http://script.[.......]/exec?variable=data&otherVariable=otherData
Also, be sure you publish your app to get this functionality. File > Manage Versions. Publish > Publish as Web App...