ServiceNow: Calling onSubmit in a callback function leads to infinite loop - datetime

Here I am comparing 2 variables for dates start_date and end_date and allowing to submit the form only in case end_date is bigger than start_date, else rejecting the form to be submitted, but while running this code, it goes into the infinite loop and if i make this asynchronous by using getXMLWait() instead of getXML(checkDateDiff) it's not supported with mobile api's.
Also there are lot of client script which help in comparing dates but none of them is supported with mobile apis.
Please have a look at the below code and help!!!!
function onSubmit() {
var requestType = g_form.getValue('request_type');
if (requestType == 'mifi') {
console.log("calling validateTravelEndDate()");
validateTravelEndDate();
return false;
} else
return true;
}
//Helper function which calls a AJAX script include called "ClientDateTimeUtils" which gives the response in a callback where i am deciding whether to submit the form or not based on the status of days result.
function validateTravelEndDate() {
var startDate = g_form.getValue('travel_start'); //First Date/Time field
var endDate = g_form.getValue('travel_end'); //Second Date/Time field
var dttype = 'day'; //this can be day, hour, minute, second. By default it will return seconds.
console.log("startDate :" + startDate + "endDate :" + endDate);
var ajax = new GlideAjax('ClientDateTimeUtils'); // This is the script include which can be used for date validation.
ajax.addParam('sysparm_name', 'getDateTimeDiff');
ajax.addParam('sysparm_fdt', startDate);
ajax.addParam('sysparm_sdt', endDate);
ajax.addParam('sysparm_difftype', dttype);
console.log("before " + g_form.getValue('travel_end'));
ajax.getXML(checkDateDiff);
}
// callback function where deciding to go ahead or not with form submission.
function checkDateDiff(response) {
var answer = response.responseXML.documentElement.getAttribute("answer");
console.log("difference in days:" + answer);
if (answer <= 0) {
alert("Travel End date must be after Travel Start date.");
g_form.setValue('travel_end', '');
g_form.showFieldMsg('travel_end', 'Please provide a future date', 'error');
return false;
} else {
console.log("%%%%%%%%%%%%%%% Calling g_form.submit()");
g_form.submit(); // This has some issue as it’s going in the infinite loop and if we just return true/false from here as it’s asynchronous call , it’s not handled by the onSubmit function
}
}

Your onSubmit() function always returns false for a mifi request. onSubmit() functions can execute a safer submit when they return a true. Also, g_form functions cannot be run in the callback function, since that is executed on the server.
Rather than have a g_form.submit() at the end of your checkDateDiff function, have onSubmit() function return true.
Something like this should work. I commented every line that I changed:
function onSubmit() {
var requestType = g_form.getValue('request_type');
if (requestType == 'mifi') {
console.log("calling validateTravelEndDate()");
// **CHANGED CODE: instead of g_form.submit(), this will return true
if(validateTravelEndDate()){
return true;
}
else{
return false;
}
} else
return true;
}
//Helper function which calls a AJAX script include called "ClientDateTimeUtils" which gives the response in a callback where i am deciding whether to submit the form or not based on the status of days result.
function validateTravelEndDate() {
var startDate = g_form.getValue('travel_start'); //First Date/Time field
var endDate = g_form.getValue('travel_end'); //Second Date/Time field
var dttype = 'day'; //this can be day, hour, minute, second. By default it will return seconds.
console.log("startDate :" + startDate + "endDate :" + endDate);
var ajax = new GlideAjax('ClientDateTimeUtils'); // This is the script include which can be used for date validation.
ajax.addParam('sysparm_name', 'getDateTimeDiff');
ajax.addParam('sysparm_fdt', startDate);
ajax.addParam('sysparm_sdt', endDate);
ajax.addParam('sysparm_difftype', dttype);
console.log("before " + g_form.getValue('travel_end'));
// **CHANGED CODE: validateTravelEndDate returns the callback value
return ajax.getXML(checkDateDiff);
}
// callback function where deciding to go ahead or not with form submission.
function checkDateDiff(response) {
var answer = response.responseXML.documentElement.getAttribute("answer");
console.log("difference in days:" + answer);
if (answer <= 0) {
alert("Travel End date must be after Travel Start date.");
g_form.setValue('travel_end', '');
g_form.showFieldMsg('travel_end', 'Please provide a future date', 'error');
return false;
}
else {
// **CHANGED CODE: checkDateDiff will return true
return true;
}
}

Related

MVC4 with Entity Framework Invalid entry for EnrollmentDate

I'm following a tutorial from here
For some reason when I try to create a new user with a date it won't accept it unless the month is January between dates ranging from 1-12ish.
I'm pretty sure it's because of the ValidationMessageFor(in the User.cs) method which forces me to enter a date which month must be January and I don't know where to alter it.
jquery.validate
jquery.validate.unobtrusive
Add code into script
$.validator.addMethod('date', function (value, element) {
if (this.optional(element)) {
return true;
}
var valid = true;
try {
$.datepicker.parseDate('dd/mm/yy', value);
}
catch (err) {
valid = false;
}
return valid;
});
$('#dt1').datepicker({ dateFormat: 'dd/mm/yy' });

"Meteor code must always run within a Fiber" when using Meteor.runAsync

Using cassandra with meteor.
let client = new cassandra.Client({contactPoints: [cassandraHost]});
var cassandraExecSync = Meteor.wrapAsync(client.execute, client);
MyProject.Feed.CassandraMeteorWrap = {
insertNewPost: function (userId, postContentJson, relevance) {
var insertCommand = insert(userId, postContentJson, relevance);
try {
return cassandraExecSync(insertCommand);
} catch (err) {
console.log("error inserting: " + insertCommand);
console.log(err);
}
}
So I wrapped Cassandra.client.execute (which has last arg as callback) with Meteor.wrapAsync
The first few inserts work, but after a few inserts (the insert is being called periodically) I get a:
[Error: Meteor code must always run within a Fiber. Try wrapping callbacks that you pass to non-Meteor libraries with Meteor.bindEnvironment.]
Update: Debug meteor showed the stack trace and the exception starts from the npm package I use "cassandra-driver" on the .onTimeout():
function listOnTimeout() {
var msecs = this.msecs;
var list = this;
debug('timeout callback ' + msecs);
var now = Timer.now();
debug('now: %d', now);
var first;
while (first = L.peek(list)) {
// If the previous iteration caused a timer to be added,
// update the value of "now" so that timing computations are
// done correctly. See test/simple/test-timers-blocking-callback.js
// for more information.
if (now < first._monotonicStartTime) {
now = Timer.now();
debug('now: %d', now);
}
var diff = now - first._monotonicStartTime;
if (diff < msecs) {
list.start(msecs - diff, 0);
debug(msecs + ' list wait because diff is ' + diff);
return;
} else {
L.remove(first);
assert(first !== L.peek(list));
if (!first._onTimeout) continue;
// v0.4 compatibility: if the timer callback throws and the
// domain or uncaughtException handler ignore the exception,
// other timers that expire on this tick should still run.
//
// https://github.com/joyent/node/issues/2631
var domain = first.domain;
if (domain && domain._disposed) continue;
try {
if (domain)
domain.enter();
var threw = true;
**first._onTimeout();**
if (domain)
domain.exit();
threw = false;
} finally {
if (threw) {
// We need to continue processing after domain error handling
// is complete, but not by using whatever domain was left over
// when the timeout threw its exception.
var oldDomain = process.domain;
process.domain = null;
process.nextTick(function() {
list.ontimeout();
});
process.domain = oldDomain;
}
}
}
}
debug(msecs + ' list empty');
assert(L.isEmpty(list));
list.close();
delete lists[msecs];
}

Network timeout using node.js client at about 5 seconds

Note, this question was previously very different. This is now the real issue. Which is...
When making a call to executeStoredProcedure() using the node.js client I get a 408 code, RequestTimeout and I get no data back from the sproc's "body". This seems to occur at about 5 seconds, but when I time bound things from inside the sproc itself, any value over say 700 milliseconds causes me to get a network timeout (although I don't see it until about 5 seconds have passed).
Note, I can have longer running sprocs with read operations. This only seems to occur when I have a lot of createDocument() operations, so I don't think it's on the client side. I think something is happening on the server side.
It's still possible that my original thought is true and I'm not getting a false back from a createDocument() call which causes my sproc to keep running past its timeout and that's what's causing the 408.
Here is the time limited version of my create documents sproc
generateData = function(memo) {
var collection, collectionLink, nowTime, row, startTime, timeout;
if ((memo != null ? memo.remaining : void 0) == null) {
throw new Error('generateData must be called with an object containing a `remaining` field.');
}
if (memo.totalCount == null) {
memo.totalCount = 0;
}
memo.countForThisRun = 0;
timeout = memo.timeout || 600; // Works at 600. Fails at 800.
startTime = new Date();
memo.stillTime = true;
collection = getContext().getCollection();
collectionLink = collection.getSelfLink();
memo.stillQueueing = true;
while (memo.remaining > 0 && memo.stillQueueing && memo.stillTime) {
row = {
a: 1,
b: 2
};
getContext().getResponse().setBody(memo);
memo.stillQueueing = collection.createDocument(collectionLink, row);
if (memo.stillQueueing) {
memo.remaining--;
memo.countForThisRun++;
memo.totalCount++;
}
nowTime = new Date();
memo.nowTime = nowTime;
memo.startTime = startTime;
memo.stillTime = (nowTime - startTime) < timeout;
if (memo.stillTime) {
memo.continuation = null;
} else {
memo.continuation = 'Value does not matter';
}
}
getContext().getResponse().setBody(memo);
return memo;
};
The stored procedure above queues document creates in a while loop until the API returns false.
Keep in mind that createDocument() is an asynchronous method. The boolean returned represents whether it is time to wrap up execution right there and then. The return value isn't "smart" enough to estimate and account for how much time the async call will take; so it can't be used for queueing a bunch of calls in a while() loop.
As a result, the stored procedure above doesn't terminate gracefully when the boolean returns false because it has a bunch of createDocument() calls that are still running. The end result is a timeout (which eventually leads to blacklisting on repeated attempts).
In short, avoid this pattern:
while (stillQueueing) {
stillQueueing = collection.createDocument(collectionLink, row);
}
Instead, you should use the callback for control flow. Here is the refactored code:
function(memo) {
var collection = getContext().getCollection();
var collectionLink = collection.getSelfLink();
var row = {
a: 1,
b: 2
};
if ((memo != null ? memo.remaining : void 0) == null) {
throw new Error('generateData must be called with an object containing a `remaining` field.');
}
if (memo.totalCount == null) {
memo.totalCount = 0;
}
memo.countForThisRun = 0;
createMemo();
function createMemo() {
var isAccepted = collection.createDocument(collectionLink, row, function(err, createdDoc) {
if (err) throw err;
memo.remaining--;
memo.countForThisRun++;
memo.totalCount++;
if (memo.remaining > 0) {
createMemo();
} else {
getContext().getResponse().setBody(memo);
}
});
if (!isAccepted) {
getContext().getResponse().setBody(memo);
}
}
};

jquery mobile function not returning value

I'm trying to run a check on a webdb running on a cordova/jQuery-mobile application:
This function will check whether the table exists in the DB and subsequently return a true or false. I have tried to change the return type from boolean to integer (0 and 1) but with no success.
function checkConfigTable(tablename) {
db.transaction
(
function (tx)
{
tx.executeSql
(
'SELECT name FROM sqlite_master WHERE type="table" AND name=?',
[tablename],
function(tx,results)
{
var len = results.rows.length;
if(len>0)
{
return true;
}
else {
return false;
}
}, errorCB
);
},errorCB,successCB
);
}
I want to then call checkConfigTable from this other one just to alert the return value (only for test purposes at the moment) but I am always getting "undefined" as return value.
// Function to retrieve current login config
function getSavedLogin(configid) {
var x = checkConfigTable("config");
alert('table: ' + x);
}
If I substitute the return with an alert directly in checkConfigTable() the correct values are shown in the alert box.

How to combine similar JavaScript methods to one

I have an ASP.NET code-behind page linking several checkboxes to JavaScript methods. I want to make only one JavaScript method to handle them all since they are the same logic, how would I do this?
Code behind page load:
checkBoxShowPrices.Attributes.Add("onclick", "return checkBoxShowPrices_click(event);");
checkBoxShowInventory.Attributes.Add("onclick", "return checkBoxShowInventory_click(event);");
ASPX page JavaScript; obviously they all do the same thing for their assigned checkbox, but I'm thinking this can be reduced to one method:
function checkBoxShowPrices_click(e) {
if (_hasChanged) {
confirm(
'All changes will be lost. Do you wish to continue?',
function(arg) {
if (arg.toUpperCase() == 'YES') {
var checkBox = document.getElementById('<%=checkBoxShowPrices.UniqueID%
>');
checkBox.checked = !checkBox.checked;
eval("<%=base.GetPostBackEventReference(checkBoxShowPrices)%>");
_hasChanged = false;
}
});
return false;
} else {
eval("<%=base.GetPostBackEventReference(checkBoxShowPrices)%>");
}
}
function checkBoxShowInventory_click(e) {
if (_hasChanged) {
confirm(
'All changes will be lost. Do you wish to continue?',
function(arg) {
if (arg.toUpperCase() == 'YES') {
var checkBox = document.getElementById('<%
=checkBoxShowInventory.UniqueID%>');
checkBox.checked = !checkBox.checked;
eval("<%=base.GetPostBackEventReference(checkBoxShowInventory)%>");
_hasChanged = false;
}
});
return false;
} else {
eval("<%=base.GetPostBackEventReference(checkBoxShowInventory)%>");
}
}
Add to the event the checkbox that is raising it:
checkBoxShoPrices.Attributes.Add("onclick", "return checkBox_click(this, event);");
Afterwards in the function you declare it like this:
function checkBoxShowPrices_click(checkbox, e){ ...}
and you have in checkbox the instance you need
You can always write a function that returns a function:
function genF(x, y) {
return function(z) { return x+y*z; };
};
var f1 = genF(1,2);
var f2 = genF(2,3);
f1(5);
f2(5);
That might help in your case, I think. (Your code-paste is hard to read..)

Resources