Unable to make async rest request from a before business rule?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎03-18-2015 09:44 AM
I have a business rule that runs on insert and update on a app scoped table.The business rule calls a restful webservice and based on the response the operation should either be aborted or continue.
When I run this as a before rule I get an error stating Use an async business rule to perform outbound HTTP requests. I have tried switching the webservice to use executeasync and that has caused more errors with truncation of the endpoint. I have also tried switching the business rule to async but that doesn't allow for me to stop the current operation.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎07-31-2015 07:16 AM
Hey Bryan,
Here is the code I tweaked from above. This is on a business rule that runs on update on Incidents. It doesn't take the full 40s I am seeing when I point it to my web servers, but it still freezes the UI for 15-20 seconds.
Strangely enough, I get very similar behavior when using the waitForResponse. With the waitForResponse it seems to take 4-5 seconds longer, but both cases lock the UI for the duration.
function onBefore(current, previous) {
//This function will be automatically called when this rule is processed.
makeCall( "1", "put" );
makeCall( "2", "post" );
}
function makeCall( desc, m ) {
var doAsync = true;
var bin = 'http://requestb.in/vt3gi5vt'
var method = "post";
if( m )
method = m;
try{
var restMessage = new sn_ws.RESTMessageV2();
restMessage.setBasicAuth("admin", "admin" );
restMessage.setHttpMethod( method );
restMessage.setEndpoint( bin );
restMessage.setRequestBody("{\"short_description\" : \"Test incident: " + desc + "\"}");
if( !doAsync )
var response = restMessage.execute();
else {
response = restMessage.executeAsync(); //Might throw exception if http connection timed out or some issue with sending request itself because of encryption/decryption of password.
//response.waitForResponse(60);// In seconds. Wait at most 60 seconds to get response from ECC Queue/Mid Server //Might throw exception timing out waiting for response in ECC queue.
}
responseBody = response.haveError() ? response.getErrorMessage() : response.getBody();
status = response.getStatusCode();
} catch(ex) {
responseBody = '' + ex; //ex.getMessage();
status = '500';
} finally {
requestBody = restMessage? restMessage.getRequestBody():null;
}
gs.info("Request Body: " + requestBody);
gs.info("Response: " + responseBody);
gs.info("HTTP Status: " + status);
gs.addInfoMessage( 'Finished' );
}

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎07-31-2015 02:50 PM
Hey Travis,
Thanks for sending over details of your business rule script. To have this request execute asynchronously I would suggest you use events and script actions as Darrin Achtman mentioned above. Instead of making the request (request.execute() or request.executeAsync()) from within the Business Rule script I would suggest you fire an event using (gs.eventQueue()) and then have a Script Action run based on the firing of that event (pick it up from event queue). By passing the record that fired the business rule into the event as current you will have access to that record from within the Script Action and can then easily update any properties (i.e., add a comment or work note) based on the outcome of the RESTMessage request without blocking the UI. An alternate approach would be to use an Async Business rule.
TL;DR
When using RESTMessageV2.execute() the calling script will immediately block and wait for a response from the requested endpoint until either a response is received or the timeout as specified by the glide.http.connection_timeout property or by calling RESTMessageV2.setHttpTimeout().
When using RESTMessageV2.executeAsync() the calling script will not block and instead will move on to executing the next line in the script. However if your script references the RESTResponse object returned from calling executeAsync() and calls a method on that object (i.e., response.haveError()) then the script will block and wait either for a response to be returned or a timeout specified via either the property glide.rest.outbound.ecc_response.timeout or using RESTResponseV2.waitForResponse() to be exceeded.
Let's look at some sample scripts. The following two script samples will behave similarly. Both blocking and waiting for a response to the request or timeout before moving onto the gs.print('test test test'); line.
Execute()
var request = new sn_ws.RESTMessageV2();
request.setHttpMethod('get');
request.setRequestHeader('Content-Type', 'application/json');
request.setEndpoint('http://requestb.in/vt3gi5vt');
request.execute();
gs.log('test test test');
ExecuteAsync()
var request = new sn_ws.RESTMessageV2();
request.setHttpMethod('get');
request.setRequestHeader('Content-Type', 'application/json');
request.setEndpoint('http://requestb.in/vt3gi5vt');
var response = request.executeAsync();
gs.log('not blocked yet')
response.haveError();
gs.log('test test test');
The difference is that in the ExecuteAsync sample the script will not block and wait for a response or timeout before executing line #6 (gs.print('not blocked')). However once line #7 is executed the script will block and wait. I think there is room for improving documentation around this functionality as well as making the executeAsync functionality a bit easier to use.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎03-17-2017 09:22 AM
very helpful, thank you!
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎05-11-2018 12:19 PM
I know this is an old thread but this is an important topic and I have seen some misunderstanding about how it all works. Using the RESTMessageV2.executeAsync() method may or may not result in actual asynchronous behavior. Here's why:
If you use executeAsync() without using the waitForResponse(seconds) method [First diagram below] then the originating thread will continue to the next line immediately after starting the REST request. The drawback to this method is that there is no response handling unless you build something custom. The way to do this would be with a custom "probe" business rule on the ecc_queue to handle the "input" ECC record that brings back the response (see KB0563615 - RESTMessageV2 API EccTopic Support).
However, if you use the waitForResponse(seconds) method [Second diagram below], then the initiating thread will not be released immediately and thus, will not be asynchronous. It will continually poll the ECC Queue until either the specified number of seconds has elapsed or the REST request has completed and an "input" ecc_queue record has been created with the response of the REST operation - a RESTResponseV2 object.
Finally, one more important thing to understand about how RESTMessageV2.executeAsync() works is when there is no MID Server specified [Third diagram below]. In that case, a "Run Once" scheduled job will be created to send the request from the instance. You should NEVER use waitForResponse() without a MID Server because you will hang two threads instead of just one. In addition to unnecessarily using a scheduled job thread, this method will also introduce latency because the scheduled job will not execute immediately but rather must wait to be picked up by a scheduler worker - a process that usually takes around 10 seconds. For these reasons it is not best practice to use RESTMessageV2.executeAsync() with RESTResponseV2.waitForResponse() without a MID Server.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎05-18-2018 10:50 AM
Is there a way to use executeAsync() but not use a midserver? We have an outbound REST call that does not need to use the MID Server but using executeAsync() forces it to use the midserver which adds time since it sits in the ECC queue.