How to introduce sleep in the js callout ?

Not applicable

One of my usecases requires me to proceed with subsequent service callouts only when the response of a previous service callout has a certain state. I am making a GET Request using javascript callout policy and httpclient and then checking the response state in a loop . How can I introduce sleep / wait in between the looped GET Requests ?

Solved Solved
0 7 2,688
1 ACCEPTED SOLUTION

I'm not clear on what you want to do.

A ServiceCallout - the policy that can execute a call to a remote system - returns when the response has been received in its entirety. So if you are doing ServiceCallout, then there is no problem. The next policy will execute when the ServiceCallout response is received.

If you are using JavaScript to invoke remote systems, then you're using the httpclient. There's a Request object and you invoke a waitForComplete() method on that object.

var ex1 = context.session["ex1"]; // Get exchange objs from the session.
var ex2 = context.session["ex2"];

ex1.waitForComplete();      // pause until the response is returned, 
                            // or error occurs, or JS step time
                            // limit has been reached.
ex2.waitForComplete(100);   // pause for a maximum of 100 ms.

if (ex1.isSuccess() && ex2.isSuccess()) {
   response.content = ex1.getResponse().content + ex2.getResponse().content;
}

This is described in more detail in the relevant documentation page.

Also If you are using JavaScript, it's not clear why you want to check for the result in a loop. Can you explain that?

View solution in original post

7 REPLIES 7

I'm not clear on what you want to do.

A ServiceCallout - the policy that can execute a call to a remote system - returns when the response has been received in its entirety. So if you are doing ServiceCallout, then there is no problem. The next policy will execute when the ServiceCallout response is received.

If you are using JavaScript to invoke remote systems, then you're using the httpclient. There's a Request object and you invoke a waitForComplete() method on that object.

var ex1 = context.session["ex1"]; // Get exchange objs from the session.
var ex2 = context.session["ex2"];

ex1.waitForComplete();      // pause until the response is returned, 
                            // or error occurs, or JS step time
                            // limit has been reached.
ex2.waitForComplete(100);   // pause for a maximum of 100 ms.

if (ex1.isSuccess() && ex2.isSuccess()) {
   response.content = ex1.getResponse().content + ex2.getResponse().content;
}

This is described in more detail in the relevant documentation page.

Also If you are using JavaScript, it's not clear why you want to check for the result in a loop. Can you explain that?

Hey Dino ,

I am trying to orchestrate a process in the APIGEE API Tier. When I make a get request (using JS callout policy to a service#1 ), its response has a status ( Not 200 OK one but another custom one that says pending or complete) . If that status says that the previous step is complete , only then can I go to the next step ( which is a service callout to service#2 ).

Hence , till the status in the response reads Complete I want to periodically keep making get calls to service#1 with sleep in between.

oh! I see

And it may take 1 or 2 seconds, while you are waiting, is that correct?

if THAT is the case, then I would suggest that you use nodejs to orchestrate the set of steps you are describing. you can do it with the httpclient that is available in the JavaScript policy, but... it will probably be easier to do in nodejs using the very widely used request client available on npm.

But if you want to continue to use the JavaScript callout, you should be able to use a loop around the logic I showed above. (Just check for your "complete" status code).

yes I am using a loop , however I want to introduce sleep of 1 second between two subsequent calls to service#1 . I could not find a straight forward way to do that in javascript . I tried using the Promise framework to do that but Apigee didnt recognise promise. Is there a way i can introduce that sleep?

nodejs! Is there a reason that you do not want to use nodejs?

Nodejs runs as a target in an API Proxy in Apigee Edge. That means the nodejs logic would be the thing your proxy would stand in front of.

If you want this polling logic to run *in addition to* calling a regular target, in other words if you want the polling logic to happen in the request flow, prior to calling the target, or in the response flow, after calling the target.... then you could wrap all the polling (in nodejs)) into another API proxy and chain to it using ServiceCallout.

<LocalTargetConnection>
   <APIProxy>polling-proxy-that-uses-nodejs</APIProxy>
   <ProxyEndpoint>default</ProxyEndpoint>
</LocalTargetConnection><br>

To do this in nodejs, you would not use a "sleep" but rather setTimeout().

This is the Javascript way!

here's an example:

var request = require('request');
var util = require('util');


function extend(obj1, obj2) {
  var copy = {};
  [obj1, obj2].forEach(function(obj) {
    if (null !== obj && typeof obj == "object") {
      Object.keys(obj).forEach(function(attr){copy[attr] = obj[attr];});
    }
  });
  return copy;
}


function invokeTarget(options, cb) {
  var reqOptions = {
        timeout : options.timeout || 30000, // in ms
        uri: options.uri,
        method: options.method || 'get',
        body: options.body,
        headers: extend({
          'accept' : 'application/json',
          'user-agent' : 'wait-for-status-1'
        }, options.headers)
      };

  request(reqOptions, function(e, httpResp, body) {
    if (e) {
      return cb(e);
    }
    if (httpResp.statusCode == options.desiredStatus) {
      return cb(null, body);
    }
    if (options.maxLoops == 0) {
      return cb(new Error("exceeded loop count"));
    }
    // delay
    setTimeout(function() {
      invokeTarget(extend(options, {maxLoops:options.maxLoops - 1}), cb);
    },
               options.sleepTime || 1000);
  });
}


var token = 'whatever';
var headers = { authorization: 'Bearer ' + token, };
var uri = 'https://example.com/some-resource/12345';
var options = {
      uri:uri,
      method: 'get',
      headers:headers,
      desiredStatus: 429, // the status you are waiting for
      sleepTime:999, // time to sleep between requests
      maxLoops:10};  // max number of requests


invokeTarget(options, function(e, body) {
  if(e) {
    // error condition - never received desiredStatus
     ...
  }
  else {
    // the desiredStatus was returned
    if (body) {
        ...
    }
  }
});

This sounds like a very bad idea. If your backend goes down and you have 100 requests to Apigee, then Apigee will make 100request per second to a system that is already failing. Please consider refactoring this solution