google.script.run is NOT running every time it is called. Some times the function runs, other times it does nothing - html

In Google apps script when using a client sided .HTML file you can call a server sided script using google.script.run.(Function name).
You can see the related documentation here: https://developers.google.com/apps-script/guides/html/reference/run
Now this script has been working with no problems over the first 6 months of its lifetime or so. I have not touched the program and I have not been notified or have located any newly deprecated code.
Over the course of the last couple months however, my users have been reporting that when they finish interacting with the HTML document, nothing happens when they close it and they have to repeat the entire process 3 or sometimes even 4 times before they will get it to go through.,
This means that when the user closes the client sided HTML window, the server sided function should be called to handle the remaining tasks but in some cases is not. This issue is completely random, and does not seem to be caused by anything specific.
I have taken some steps myself to attempt to solve the issue. I have wrapped the entirety of the code in try catch blocks, including the .HTML and .GS files. This means that if literally ANYTHING goes wrong in ANY script, I will be notified of it immediately. However, despite this being the case I am yet to receive any emails of it failing even though I watch it fail with my own eyes. I have added log commands before and after this function to see if it stops working all together or continues. In every case regardless of whether the function call is successful or not the log commands go through.
To me this can only mean that for some reason the function google.script.run is not working properly, and is failing to run the associated function, but is not returning an error message or stopping the script.
I am at an absolute loss since I have no error message, no reproducible steps, and no history of this being a problem before while suddenly starting to get worse and worse over time. I have checked Google's issue tracker to no results. If anyone else is using this function and is having problems I would love you to share your experiences here. If you have a solution please let me know as soon as possible. If I can't fix this issue I am going to have to use a new platform entirely.
Edit 10/2:
After looking further into this issue I have discovered a list of all executions on this project. I can see what functions were executed, when, and how long they took to execute. I can see that when the function that opens the HTML service is ran, the next function that should run does not always appear in the list. And when it doesn't, I can see that the user repeated their steps until it did run. This supports my theory that the function just isn't running when it should be after being called my script.run

Tl;dr: The affected computers are running so slowly that google.script.host.close would run before google.script.run.functionName() is able to be called and the information passed from the client to server, causing the function to never run but also not return an error. Adding Utilities.sleep(1000) fixes the issue.
I'm answering here in the situation that someone stumbles upon this thread in the future because they're having similar problems.
I was able to fix the issue by adding two lines of code between
google.script.run and google.script.host.close.
I added Google's Utilities.sleep(1000) to force the computer to wait one second between executing the function and closing the HTML window. I also added an HTML alert that shows that the function was called and didn't suffer from a runtime error.
I don't know exactly why this seems to have fixed the issue but I have a theory.
I have about 20 computers this spreadsheet runs on. Only about 6 of them were having the issue, and this wasn't brought to my attention until recently. As it turns out the 6 computers that were having the issue were the slowest computers of the bunch.
My theory is that the computers were so slow, and the internet bandwidth was fluctuating so much that the computer simply didn't have time to call google.script.run and pass off the information from the client sided HTML window that it simply got closed and cut off when google.script.host.close was run. This means that the function will not exist in the execution transcripts or history, nor will there be any sort of runtime error. All of those things were true in my situation. This also explains why I never had the issue on any of my own equipment in a testing environment since it didn't suffer from any slowdowns the other computers were having.
By adding both Utilities.sleep(1000) and the UI alert this forces the javascript to not continue to google.script.host.close until the user interacts with the UI alert (Which is just a confirmation window with an OK button) and afterwards waits a full second. This sacrifices a tiny bit of user friendly-ness for a more functional script. Since I have implemented this "fix" none of my users are reporting any issues and all of my execution history looks just fine.
Hopefully this helps any future passerbys.

In the comments you posted this function snippet:
Here is a basic copy of the script that utilizes google.script.run:
function onFailure(error) {
MailApp.sendEmail("sparkycbass#gmail.com", "Order book eror", "ERROR: " + error.message);
google.script.host.close();
}
function handleFormSubmit(formObject) {
google.script.run.withFailureHandler(onFailure).processForm(formObject)
google.script.host.close();
}
The problem here is that google.script.run is asynchronous - the call to your server-side function processForm is not guaranteed to be even initiated before the call to google.script.host.close() is made:
Client-side calls to server-side functions are asynchronous: after the browser requests that the server run the function doSomething(), the browser continues immediately to the next line of code without waiting for a response. This means that server function calls may not execute in the order you expect. If you make two function calls at the same time, there is no way to know which function will run first; the result may differ each time you load the page. In this situation, success handlers and failure handlers help control the flow of your code.
A proper pattern is to only call "destructive" commands - such as closing the host and therefore unloading all the relevant Apps Script instances - after the server has indicated the async operation completed. This is within the success handler of the google.script.run call:
.html
function onFailure(error) { // server function threw an unhandled exception
google.script.run.sendMeAnEmail("Order book error", "ERROR: " + error.message);
console.log(error);
document.getElementById("some element id").textContent = "There was an error processing that form. Perhaps try again?"
}
function onSuccess(serverFunctionOutput, userObj) {
// do stuff with `serverFunctionOutput` and `userObj`
// ...
google.script.host.close();
}
function handleFormSubmit(formObject) {
google.script.run
.withFailureHandler(onFailure)
.withSuccessHandler(onSuccess)
.processForm(formObject);
}
.gs
function processForm(formData) {
console.log({message: "Processing form data", input: formData});
// ...
}
function sendMeAnEmail(subject, message) {
console.log({message: "There was a boo-boo", email: {message: message, subject: subject}});
MailApp.sendEmail("some email", subject, message);
}

Related

Googlesheets check if last execution failed

I have a google sheet with a trigger that kicks off some code at 2 am each night.
sometimes the code fails due to various reason like network issue, or timing out because the site is busy and responses are slow (make numerous api calls)
I was thinking of putting in a code than runs based on a trigger every 15 minutes, to see if the last execution failed, then run my code again.
Been googling and search stackoverflow, but can't find a starting point on how to achieve this.
Basically I want to know if the last execution (which I can see manually in the executions screen) has a failed or time-out status, if it does I'll kick off my code again.
Use exponential backoff. You may want to try the exponentialBackoff_() function, like this:
function myFunctionThatRunsOnATrigger() {
exponentialBackoff_(myFunctionThatSometimesFails);
}
function myFunctionThatSometimesFails() {
//...
}

My messages are delivered out of flow sequence order and how do I compensate?

I wish to use Twilio in the context of an adventure game. As the gamer (Geocacher) progresses on an actual treasure (cache) hunt, clues are given by text when certain code words or numbers are entered as part of the thread. I am very comfortable creating the flow in Studio and have a working flow that is debugged and ready to go.
Not so fast, grasshopper! Strange things started to happen when beta testing the flow. Basically texts that show as being sent arrive to the user out of sequence in the thread. The SM logs show everything is working correctly (message sent) but, what I call Zombie messages arrive to the user after a previous message has arrived. The Zombies are legitimate messages from the Flow but out of the correct sequence and that makes the thread unusable for my purposes.
I learned too late in my "programming" that Twilio states, "If you send multiple SMS messages to the same user in a short time, Twilio cannot guarantee that those messages will arrive in the order that you sent them." Ugh!
So , I started with the Help Techs at Twillio and one solution is to create a subflow that basically is inserted after a Send Message Widget. This sub flow basically Fetches the message via the SMS SID to check for SMS status. If status is "delivered", we can safely say the message has been received by the recipient and then permit the next message in the flow.
That sound great but I am not a programmer and will never be able to integrate the suggested code much less debug it when things don't work. There might be many other approaches that you guys can suggest. The task is 1.) Send a message, 2.) Run a subflow that checks for message delivery, 3.) send the next message in the sequence.
I need to move on to implementation and this type of sub flow is out of my wheelhouse. I am willing to pay for programming help.
I can post the JSON code that was created as a straw man but have no idea how to use it and if it is the optimum solution if that is of help. It would seem that a lot of folks experience this issue and would like a solution. A nice tight JSON subflow with directions on how to insert would seem to be a necessary part of the Widget toolkit provided by Twillio in Studio.
Please Help Me! =)
As you stated, the delivery of the message cannot be guaranteed. Checking the status of the sent message is the most reliable, using a subflow, a Twilio Function, or a combination. Just keep in mind that Twilio Functions have a 10s execution time limit. I don't expect delivering the SMS will take longer than 10s is most cases. If you're worried about edge cases, you'd have to loop the check for the status multiple times. I'll share a proof of concept later for this.
An easier way, but it still doesn't guarantee delivery order, would be to add some delay between each message. There's no built-in delay widget, but here's code on how to create a Twilio Function to add delays, up to 10s.
A more hacky way to implement delays without having to use this Twilio Function, is to use the Send & Wait For Reply Widget and configure the "Stop Gathering After" property to the amount of delay you'd like to add. If the user responds, connect to the next widget, if they don't also connect to the widget.
As mentioned earlier, here's th Subflow + Function proof of concept I hacked together:
First, create a Twilio Functions Service, in the service create two functions:
/delay:
// Helper function for quickly adding await-able "pauses" to JavaScript
const sleep = (delay) => new Promise((resolve) => setTimeout(resolve, delay));
exports.handler = async (context, event, callback) => {
// A custom delay value could be passed to the Function, either via
// request parameters or by the Run Function Widget
// Default to a 5 second delay
const delay = event.delay || 5000;
// Pause Function for the specified number of ms
await sleep(delay);
// Once the delay has passed, return a success message, TwiML, or
// any other content to whatever invoked this Function.
return callback(null, `Timer up: ${delay}ms`);
};
/get-message:
exports.handler = function(context, event, callback) {
const messageSid = event.message_sid,
client = context.getTwilioClient();
if(!event.message_sid) throw "message_sid parameter is required.";
client.messages(messageSid)
.fetch()
.then(message => callback(null, message))
.catch((error) => {
console.error(error);
return callback(error);
});
};
Then, create a Studio Flow named something like "Send and Wait until Delivered".
In this flow, you send the message, grabbing the message body passed in from the parent flow, {{trigger.parent.parameters.message_body}}.
Then, you run the /get-message Function, and check the message status.
If delivered, set status variable to delivered. This variable will be passed back to the parent flow. If any of these accepted,queued,sending,sent, then the message is still in route, so wait a second using the /delay function, then loop back to the /get-message function.
If any other status, it is assumed there's something wrong and status is set to error.
Now you can create your parent flow where you call the subflow, specifying the message_body parameter. Then you can check the status variable for the subflow, whether it is 'delivered' or 'error'.
You can find the export for the subflow and the parent flow in this GitHub Gist. You can import it and it could be useful as a reference.
Personally, I'd add the /delay function, and use that after every message, adding a couple of seconds delay. I'd personally assume the delay adds enough buffer for no zombie messages to appear.
Note: The code, proof of concept, and advice is offered as is without liability to me or Twilio. It is not tested against a production workload, so make sure you test this thoroughly for your use case!

Random permission/auhorization errors with Session.getActiveUser()

During the past week I have faced a nasty issue with my google apps script, which has been working just fine for several years already, with at least 500 users.
When I'm trying to get the login information of the current user this way:
var email_act = Session.getActiveUser().getEmail();
the server throws an exception:
Exception: You do not have permission to call Session.getActiveUser.
Required permissions: https://www.googleapis.com/auth/userinfo.email
at getUserInfo(UserFunctions:46:27)
In my case the effective user is different than active user, but I got the same error also when trying to call Session.getEffectiveUser().
This happens randomly, approx 30% of calls and so far my solution was simply to retry until it succeeds. Note that when I did the retries in the backend-code, it did not solve the problem, maybe because there's something wrong with the "session" itself. If it failed once, it failed also all the consequent calls. Therefore, I implemented the retry-loop in front-end code (assuming that each call to back-end is then considered as a new session) and that helped - after a few (worst case 5) retries it works. The Back-end has now a simple try-catch error handler and it returns status (-1) to FE in case of failure.
Any ideas what might be the root cause? My solution is way too uggly to live with...
Thanks in advance!
I just came across the same error today. For me, the Session.getActiveUser().getEmail() was in a scriplet on a dynamic webpage I create with a script using the function "include" that is defined in the code.gs like this:
function include(filename) {
return HtmlService.createHtmlOutputFromFile(filename).getContent();
}
It seems changing this to
function include(filename) {
return HtmlService.createTemplateFromFile(filename).evaluate().getContent();
}
has cleared things right up. I believe the error started occuring when I switched the code to the new V8 engine in GAS.
EDIT: oops, it worked the first time, but then failed after that. So I had to move the Session.getActiveUser().getEmail() inside code.gs
function getUserEmail() {
return Session.getActiveUser().getEmail()
}
and in the javascript on the webpage I had to use a callback :
google.script.run.withSuccessHandler(function (email) {
document.getElementById("yourIDhere").innerHTML = email;
}).getUserEmail();
Not very pretty at all, but it's working reliably now.

Google Cloud Function: lazy loading not working

I deploy a google cloud function with lazy loading that loads data from google datastore. The last update time of my function is 7/25/18, 11:35 PM. It works well last week.
Normally, if the function is called less than about 30 minutes since last called. The function does not need to load data loaded from google datastore again. But I found that the lazy loading is not working since yesterday. Even the time between two function is less than 1 minute.
Does anyone meet the same problem? Thanks!
The Cloud Functions can fail due to several reasons such as uncaught exception and internal process crashes, therefore, it is required to check the logs files / HTTP responses error messages to verify the issue root cause and determine if the function is being restarted and generating Function execution timeouts that could explain why your function is not working.
I suggest you take a look on the Reporting Errors documentation that explains the process required to return a function error in order to validate the exact error message thrown by the service and return the error at the recommended way. Keep in mind that when the errors are returned correctly, then the function instance that returned the error is labelled as behaving normally, avoiding cold starts that leads higher latency issues, and making the function available to serve future requests if need be.

remaking code from in the same function doesn't work

On the client side I have a form that is submitted to a 'processFrom' function on the google app script server side when submitted. However the code got long and spaghetti like so I decided to just scratch it and start over. Essentially erased everything between the curly brackets. Now nothing will run inside the processForm function even just a simple Logger.log('in the function'); Won't log. I'm pretty baffled.
the client side button code that works when the old code is there:
<input type="button" class="btn" value="Adding to list" onclick="google.script.run.processForm(this.parentNode);formSubmitted();">
This is the server side that doesn't seem to process:
function processForm(formdata){
Logger.log(formdata);
Logger.log('in the loop');
}
Again I'd like to reiterate that all I did is delete spaghetti code from inside the curly brackets for processForm and all of the sudden it doesn't work. If I copy paste the spaghetti code back it miraculously works. By work I mean any code inside processForm starts to run and logg etc.
Update:
I went through the code in the functions processFrom and deleted line by line and check if the function is still working. The code is -
function processForm(formdata){
Logger.log(formdata);
var cSpreadsheet = DriveApp.getFilesByName(formdata.Name);
var spreadsheet = getListing();
}
This logs about one out of every four submits. If I take out the two variables I can't get formdata to log at all.
Update 2:
After more review I think I found the problem. The line is the problem (more specifically the formSubmitted() function):
onclick="google.script.run.processForm(this.parentNode);formSubmitted();"
In formSubmitted I have another call to google.script.run for something else, which overrides the google logs with nothing since in that function I do not have anything logging. The log only came up once in a while for processForm because I must have switched fast enough to the other tab and looked at the logs quickly before the formSubmitted function completed or ran.
After more review I think I found the problem. The line is the problem (more specifically the formSubmitted() function):
onclick="google.script.run.processForm(this.parentNode);formSubmitted();"
In formSubmitted I have another call to google.script.run for something else, which overrides the google logs with nothing since in that function I do not have anything logging. The log only came up once in a while for processForm because I must have switched fast enough to the other tab and looked at the logs quickly before the formSubmitted function completed or ran.