XmlHttpRequest readyState Always 1?

I have the following custom event code. I have been trying all sorts of variations to get it to work but I can’t seem to get any further than readyState 1. Any help would be appreciated:

Backendless.ServerCode.customEvent('chargeCustomerAndRecordDetails', function(req) {


  Backendless.Logging.setLogReportingPolicy( 1, 1 );
  var logger = Backendless.Logging.getLogger( "MyLogger");


  var config = {
    contentType: 'application/x-www-form-urlencoded',
    authorisationKey: '',
    authorisationValue: ''
  };


  var parse = function (req) {
    var result;
    try {
      result = JSON.parse(req.responseText);
    } catch (e) {
      result = req.responseText;
    }
    return [result, req];
  };


  var setContentType = function(value) {
    config.contentType = value;
  };


  var setAuthorisationHeaderKey = function(value) {
    config.authorisationKey = value;
  };


  var setAuthorisationHeaderValue = function(value) {
    config.authorisationValue = value;
  };


  var XMLHttpRequest = require("xmlhttprequest").XMLHttpRequest;


  var postData = encodeURI("username=test@email.com&api_key=12345&from=test@email.com&from_name=Test&to=test@email.com&subject=Test Email&body_html=This is the <h1>HTML</h1>");


  setContentType('application/x-www-form-urlencoded');
  setAuthorisationHeaderKey('');
  setAuthorisationHeaderValue('');


  var elasticrequest = new XMLHttpRequest();


  elasticrequest.open('POST', 'https://api.elasticemail.com/mailer/send', true);
  elasticrequest.setRequestHeader('Content-type', config.contentType);
  if (config.authorisationKey!='' && config.authorisationValue!=''){
     elasticrequest.setRequestHeader(config.authorisationKey, config.authorisationValue);
  }


  elasticrequest.onreadystatechange = function () {
logger.debug( "ready state changed" );
          var req;
logger.debug( "readystate="+elasticrequest.readyState);    
          if (elasticrequest.readyState === 4) {
logger.debug( "status="+elasticrequest.status);
            req = parse(elasticrequest);
            if (elasticrequest.status >= 200 && elasticrequest.status < 300) {
logger.debug( "success" );
            } else {
logger.debug( "error" );
            }
logger.debug( "always" );
          }
  };


  elasticrequest.send(postData);


});

Am I going about this the wrong way?

Gary

We will investigate this problem.
Inner task BKNDLSS-13506.

Gary,

Does the same code (where you create XHR and use it to communicate with api.elasticmail.com) work when you use it in a node.js environment (that is outside of Backendless)?

Regards,
Mark

Hi Garry

Does it work in debug mode ?
In production it will not work, since CodeRunner process will exit before your asynchronous job finished

Please read these two articles:

ServerCode works well in the DEBUG mode, but does not when deployed to the Cloud
Sync vs Async code

Thanks for the feedback guys. Yes, the code runs in debug mode.

I modified the code to be synchronous, but now I get an error:
“EACCES: permission denied, open ‘.node-xmlhttprequest-sync-21191’”

Would be grateful for any ideas… feels like this should work to me… here’s the code:

Backendless.ServerCode.customEvent('chargeCustomerAndRecordDetails', function(req) {
 
 Backendless.Logging.setLogReportingPolicy( 1, 1 );
 var logger = Backendless.Logging.getLogger( "MyLogger");
 
 var config = {
    contentType: 'application/x-www-form-urlencoded',
    authorisationKey: '',
    authorisationValue: ''
 };


 var setContentType = function(value) {
    config.contentType = value;
 };


 var setAuthorisationHeaderKey = function(value) {
    config.authorisationKey = value;
 };


 var setAuthorisationHeaderValue = function(value) {
    config.authorisationValue = value;
 };


 var XMLHttpRequest = require("xmlhttprequest").XMLHttpRequest;
 var request = new XMLHttpRequest();


 var postData = encodeURI("username=test@email.com&api_key=12345e&from=test@email.com&from_name=Test&to=test@email.com&subject=Test&body_html=This is the &lt;h1&gt;HTML&lt;/h1&gt;");


 setContentType('application/x-www-form-urlencoded');
 setAuthorisationHeaderKey('');
 setAuthorisationHeaderValue('');


 var elasticrequest = new XMLHttpRequest();


 elasticrequest.open('POST', 'https://api.elasticemail.com/mailer/send', false);
 elasticrequest.setRequestHeader('Content-type', config.contentType);
 if (config.authorisationKey!='' && config.authorisationValue!=''){
    elasticrequest.setRequestHeader(config.authorisationKey, config.authorisationValue);
 }


 elasticrequest.send(postData);
 
});

Gary

Tried a different approach… error: “Cannot find module ‘sntp’”

Backendless.Logging.setLogReportingPolicy( 1, 1 );
var logger = Backendless.Logging.getLogger( "MyLogger");


var request = require('request');


var myXMLText = 'username=email@test.com&api_key=12345&from=email@test.com&from_name=Test&to=email@test.com&subject=Test&body_html=This is the &lt;h1&gt;HTML&lt;/h1&gt;'
request({
    url: "https://api.elasticemail.com/mailer/send",
    method: "POST",
    headers: {
        "content-type": "application/x-www-form-urlencoded",  // <--Very important!!!
    },
    body: myXMLText
}, function (error, response, body){
    //console.log(response);
    logger.debug( response );
});

Almost ready to throw in the towel!

Gary

The code should not be synchronous hence this is impossible in nodejs env

Please re-read that articles I pointed.

The main idea, is that your event handler should return a Promise to the CodeRunner to make him waiting while it’s getting resolved

Something like this :

return new Promise(function(resolve, reject){
var request = new XMLHttpRequest();
//...


request.onreadystatechange = function() {


 //...


    if (this.readyState === 4) {
        //..


        if (this.status >= 200 && this.status < 300) {
            resolve(this.responseText)
        } else {
            reject(''Error. StatusCode:' + this.status)
        }
    }
};
})

Error in Cloud: Cannot find module ‘some_specific_third-party_module’

Thanks for the response. I finally got it to work… well sort of. At least I get an error result from the API provider!

Here’s the code for anyone interested.

return new Promise(function (resolve, reject) {


  var XMLHttpRequest = require("xmlhttprequest").XMLHttpRequest;


  var xhr = new XMLHttpRequest();


  xhr.open('POST', 'https://api.elasticemail.com/mailer/send');


  xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');


  xhr.onreadystatechange = function() {
    logger.debug( 'readyState='+this.readyState );
    if (this.readyState === 4) {
        logger.debug( 'status='+this.status );
      if (this.status >= 200 && this.status < 300) {
        logger.debug( 'hooray' );
        resolve(this.responseText);
      } else {
        logger.debug( 'neigh' );     
        reject(this.status);
      }
    }
  };
 
  var postData= 'username=email@email.com&api_key=12345&from=email@email.com&from_name=Test&to=email@email.com&subject=Test&body_html=This is the &lt;h1&gt;HTML&lt;/h1&gt;';


  logger.debug( 'send: '+postData );
  xhr.send(postData);


});

Thanks again for your response. What I don’t understand is, when I run:

npm install third-party-module --save

Then I run debug and deploy, why aren’t all the files deployed to the Backendless server?

Why is there files missing? When they are all available locally.

Deploy your code as this:

npm run deploy --keep-zip --verbose

and you will get more information about the deployment

When using the --keep-zip parameter, once the deployment is complete (or even after you attempt to run it), you will find the deploy.zip file in the {PROJECT_DIR} folder. You will be able to check its size and the content.

If you have your module declared in your package.json file in dependencies list, and you still see it excluded from the deployment, then it must be a bug.
We will need more information from you, like what node/npm version you use, which third-party module and so on…