[#124] Cross Origin Error

Migrated from Redmine #124 | Author: Anonymous
Status: Closed | Priority: Normal | Created: 2018-03-08


Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://user-api.simplybook.me/. (Reason: CORS header ‘Access-Control-Allow-Origin’ missing).

I keep getting this error when trying to access the API. It is random. Sometimes it comes up and sometimes not. Other times I get a 503 “Service Temporarily Unavailable.” with this message.

Below is my code to access the API:

import { sbm, JSONRpcClient } from ‘./sbmConfig’;

let SBM = {
client: null
}

const companyLogin = ‘cyno’;
const sbmApiKey = sbm.apiKey;
let tokenExpired = null;

const getToken = () => {
let loginClient = getLoginClient();
return loginClient.getToken(companyLogin, sbmApiKey);
}

const getSbmClient = () => {
console.log(‘Getting SBM client.’);
SBM.client = getJSONRpcClient();

if (tokenExpired) {
    // This doesn't work
    SBM.client = getJSONRpcClient();
    console.log('Token Expired: Getting new token.', );
}
return SBM.client;

}

// LOGIN TO SBM
const getLoginClient = () => {
return new JSONRpcClient({
url: ‘//user-api.simplybook.me/login’,
onerror: function (error) {
alert(error);
console.log(‘Client Login Error @Calendar Actions’);
}
});
}

const getJSONRpcClient = () => {
let token = getToken();
return new JSONRpcClient({
‘url’: ‘//user-api.simplybook.me’,
‘headers’: {
‘X-Company-Login’: companyLogin,
‘X-Token’: token
},
‘onerror’: function (error) {
alert(‘Error: ‘+ error + ’ Message: ’ + error.message + ’ Code: ’ + error.code + ’ Token: ’ + token);
console.log(’%cGETTING CLIENT ERROR @SBM Calendar Actions’,‘color: greenyellow’);
if (error.code == -32600){
tokenExpired = true;
let newLoginClient = new JSONRpcClient({
url: ‘//user-api.simplybook.me/login’,
onerror: function (error) {
alert(error);
console.log(‘Client Login Error @Calendar Actions’);
}
});
let newToken = getToken();
SBM.client = new JSONRpcClient({
‘url’: ‘//user-api.simplybook.me’,
‘headers’: {
‘X-Company-Login’: companyLogin,
‘X-Token’: newToken
},
‘onerror’: function (error) {
alert('Error: ‘+ error + ’ Message: ’ + error.message + ’ was fired from repeat call to sbmLogin ’ + ‘Code: ’ + error.code + ’ Token: ’ + newToken);
console.log(’%cGETTING CLIENT ERROR @SBM Calendar Actions’,‘color: greenyellow’);
}
});
}
}
});
}

export {getSbmClient as default, getAsyncSbmClient};

THIS IS THE JSONRPC SETUP CODE

declare var jQuery: any;
declare var Exception: any;

export const sbm = {
apiKey: ‘cc92801171ff01c1d45d7675ef77300c4dd92909a795fbd81165b95b9284b4eb’
}

/**

  • JSON-RPC Client Exception class

  • @param String code

  • @param String message
    */
    var JSONRpcClientException = function (code, message) {
    this.code = code;
    this.message = message;
    }
    JSONRpcClientException.prototype = jQuery.extend(JSONRpcClientException.prototype, {

    /**

    • Magic method. COnvert object to string.
    • @return String
      */
      toString: function () {
      return ‘[’ + this.code + '] ’ + this.message;
      }

});

/**

  • JSON-RPC Client

  • @param Object options
    */
    export const JSONRpcClient = function (options) {
    this.setOptions(options);
    this.init();
    }
    JSONRpcClient.prototype = jQuery.extend(JSONRpcClient.prototype, {

    /**

    • Default options
      */
      options: {
      ‘onerror’: function () {},
      ‘onsuccess’: function () {},
      ‘url’: ‘’,
      ‘headers’: {}
      },
      current: 1,
      onerror: null,
      onsuccess: null,
      onstart: null,

    /**

    • Init client
      */
      init: function () {
      this.onerror = this.getParam(‘onerror’);
      this.onsuccess = this.getParam(‘onsuccess’);

      this.initMethods();
      },

    /**

    • Init API methiods by url
      */
      initMethods: function () {
      var instance = this;
      // get all methods
      jQuery.ajax(this.getParam(‘url’), {
      ‘async’: false,
      ‘success’: function (data) {
      if (data.methods) {
      // create method
      jQuery.each(data.methods, function(methodName, methodParams) {
      var method = function () {
      var params = new Array();
      for(var i = 0; i < arguments.length; i++){
      params.push(arguments[i]);
      }
      var id = (instance.current++);
      var callback = params[params.length - 1];
      var request = {jsonrpc: ‘2.0’, method: methodName, params: params, id: id};

                       var async = false;
                       if (jQuery.type(callback) == 'function') {
                       	async = true;
                       	params.pop();
                       }
                       
                       var res = null;
                       // API request
                       jQuery.ajax(instance.getParam('url'), {
                           'contentType': 'application/json',
                           'type': methodParams.transport,
                           'processData': false,
                           'dataType': 'json',
                           'cache': false,
                           'data': JSON.stringify(request),
                           'headers': instance.getParam('headers'),
                           'async': async,
                           'success': function (result) {
                               if (jQuery.type(result.error) == 'object') {
                                   res = new JSONRpcClientException(result.error.code, result.error.message);
                                   instance.onerror(res);
                               } else {
                                   res = result.result;
                                   if (jQuery.type(callback) == 'function') {
                               	    callback(res);
                                   }
                               }
                               instance.onsuccess(res, id, methodName);
                           }
           	        });
                       if (!async) {
                       	return res;
                       }
           	    }
      
           	    instance[methodName] = method;
           	});
           } else {
               throw Exception("Methods could not be found");
           }
       }
      

      });
      },

    /**

    • Set client options
    • @param Object options
      */
      setOptions: function (options) {
      this.options = jQuery.extend({}, this.options, options);
      },

    /**

    • Get client param, if param is not available in this.options return defaultValue
    • @param String key
    • @param mixed defaultValue
    • @return mixed
      */
      getParam: function (key, defaultValue) {
      if (jQuery.type(this.options[key]) != ‘undefined’) {
      return this.options[key];
      }
      return defaultValue;
      }

});

var SimplybookWidget = function(options) {
this.options = {
// common options
‘widget_type’: ‘iframe’,
‘theme’: ‘default’,
‘theme_settings’: {},
‘app_config’: {},
‘timeline’: null,
‘datepicker’: null,
‘url’: ‘’,
// iframe widget options
‘iframe_postpone’: false,
// button widget options
‘button_position’: ‘right’,
‘button_position_offset’: ‘auto’,
‘button_background_color’: ‘#06acee’,
‘button_text_color’: ‘#ffffff’,
‘button_title’: ‘Book Now’,
‘button_preload’: true,
‘button_custom_id’: null,
‘navigate’: ‘book’
};
this.name = ‘widget_’ + Math.random();
this.frame = null;
for (var name in options) {
if (!options.hasOwnProperty(name)) {
continue;
}
this.options[name] = options[name];
}
this.init();
};
SimplybookWidget.prototype.init = function() {
if (!this.options.postpone) {
switch (this.options.widget_type) {
case ‘iframe’:
this.displayIframe();
break;
case ‘reviews’:
this.options.navigate = ‘reviews’;
this.displayIframe();
break;
case ‘button’:
this.addButton();
break;
case ‘contact-button’:
this.addContactButton();
break;
}
}
};
SimplybookWidget.prototype.onReceiveMessage = function(message) {
if (typeof message.data === ‘object’) {
if (!this.frame || message.source !== this.frame.contentWindow) {
return;
}
switch (message.data.event) {
case ‘appReady’:
this.setSettings(message.data);
break;
case ‘updateWidgetSize’:
this.updateWidgetSize(message.data);
break;
case ‘closeWidget’:
this.closePopup();
break;
case ‘modalShown’:
if (this.options.widget_type === ‘iframe’) {
this.changeModalPosition(message.data.listener_id);
}
break;
case ‘stepChanged’:
if (this.options.widget_type === ‘iframe’) {
this.scrollToContent(message.data.content_position);
}
break;
}
}
};
SimplybookWidget.prototype.setSettings = function() {
var win = this.frame.contentWindow || this.frame;
var postData = {
‘update_config’: true,
‘update_theme_vars’: true,
‘theme_vars’: this.options.theme_settings,
‘config_vars’: this.options.app_config,
};
if (this.options.navigate) {
postData[‘navigate’] = this.options.navigate;
this.options.navigate = null;
}
if (this.options.reviews_count || this.options.hide_add_reviews) {
postData[‘reviews_vars’] = {
“reviews_count”: this.options.reviews_count,
“hide_add_reviews”: parseInt(this.options.hide_add_reviews)
};
}
win.postMessage(postData, ‘');
};
SimplybookWidget.prototype.navigate = function(to) {
var win = this.frame.contentWindow || this.frame;
win.postMessage({
‘navigate’: to
}, '
’);
};
SimplybookWidget.prototype.scrollToContent = function(contentPos) {
var scrollTop = window.pageYOffset || document.documentElement.scrollTop;
var iframePosition = this.frame.getBoundingClientRect();
var top = scrollTop + iframePosition.top + contentPos;
if (scrollTop > top) {
window.scrollTo(0, top);
}
};
SimplybookWidget.prototype.changeModalPosition = function(listenerId) {
var win = this.frame.contentWindow || this.frame;
var scrollTop = window.pageYOffset || document.documentElement.scrollTop;
var iframePosition = this.frame.getBoundingClientRect();
var top = scrollTop + iframePosition.top;
var scrollTo = scrollTop - top;
win.postMessage({
‘update_modal_position’: true,
‘listener_id’: listenerId,
‘top’: Math.max(scrollTo, 0)
}, ‘*’);
};
SimplybookWidget.prototype.getUrl = function() {
var widget_type = this.options.widget_type;
if (widget_type == ‘contact-button’) {
widget_type = ‘button’;
}
var url = this.options.url + ‘/v2/?widget-type=’ + widget_type + ‘&theme=’ + this.options.theme;
if (this.options.theme) {
url += ‘&theme=’ + this.options.theme;
}
if (this.options.theme_id) {
url += ‘&theme_id=’ + this.options.theme_id;
}
if (this.options.timeline) {
url += ‘&timeline=’ + this.options.timeline;
}
if (this.options.datepicker) {
url += ‘&datepicker=’ + this.options.datepicker;
}
if (this.options.is_rtl) {
url += ‘&is_rtl=1’;
}
return url;
};
SimplybookWidget.prototype.subscribeMessages = function() {
var instance = this;
window.addEventListener(“message”, function(data) {
instance.onReceiveMessage(data);
}, false);
};
// iframe widget methods
SimplybookWidget.prototype.updateWidgetSize = function(data) {
if (this.options.widget_type !== ‘iframe’ && this.options.widget_type !== ‘reviews’) {
return;
}
this.frame.height = data.height;
};
SimplybookWidget.prototype.displayIframe = function() {
document.write(
‘’
);
this.frame = document.getElementById(this.name);
this.subscribeMessages();
};
// button widget methods
SimplybookWidget.prototype.addButton = function() {
this.addButtonWidgetStyles();
var btn;
if (this.options.button_custom_id) {
btn = document.getElementById(this.options.button_custom_id);
} else {
btn = this.getButtonNode();
}
var instance = this;
btn.addEventListener(‘click’, function() {
instance.showPopupFrame(‘book’);
});
document.body.appendChild(btn);
};
SimplybookWidget.prototype.addContactButton = function() {
this.addButtonWidgetStyles();
var btn;
if (this.options.button_custom_id) {
btn = document.getElementById(this.options.button_custom_id);
} else {
btn = this.getButtonNode();
}
var instance = this;
btn.addEventListener(‘click’, function() {
instance.showPopupFrame(‘contact-widget’);
});
document.body.appendChild(btn);
};
SimplybookWidget.prototype.getButtonNode = function() {
this.btn = document.createElement(‘div’);
this.btnLabel = document.createElement(‘div’);
this.btnLabel.innerText = this.options.button_title;
this.btn.appendChild(this.btnLabel);
this.updateButtonStyles();
return this.btn;
};
SimplybookWidget.prototype.updateButtonStyles = function(options) {
if (!this.btn) {
return;
}
if (options) {
this.options.button_position = options.button_position;
this.options.button_background_color = options.button_background_color;
this.options.button_text_color = options.button_text_color;
this.options.button_position_offset = options.button_position_offset;
this.options.button_title = options.button_title;
}
this.btn.className = ‘simplybook-widget-button ’ + this.options.button_position;
this.btn.style.backgroundColor = this.options.button_background_color;
this.btn.style.color = this.options.button_text_color;
if (this.options.button_position === ‘top’ || this.options.button_position === ‘bottom’) {
this.btn.style.right = this.options.button_position_offset;
this.btn.style.bottom = ‘’;
} else {
this.btn.style.bottom = this.options.button_position_offset;
this.btn.style.right = ‘’;
}
this.btnLabel.innerText = this.options.button_title;
};
SimplybookWidget.prototype.resetWidget = function(options) {
this.options = options;
this.updateButtonStyles(options);
this.container = null;
this.frame = null;
};
SimplybookWidget.prototype.showPopupFrame = function(navigateTo) {
if (navigateTo === undefined) {
navigateTo = ‘book’;
}
if (!this.container) {
this.container = document.createElement(‘div’);
this.container.className = ‘simplybook-widget-container active’;
this.options.navigate = navigateTo;
this.container.appendChild(this.getIframeNode());
document.body.appendChild(this.container);
} else {
this.container.className = ‘simplybook-widget-container active’;
this.navigate(navigateTo);
}
this.showOverlay();
};
SimplybookWidget.prototype.closePopup = function() {
this.hideOverlay();
this.container.className = ‘simplybook-widget-container’;
};
SimplybookWidget.prototype.showOverlay = function() {
if (!this.overlay) {
this.overlay = document.createElement(‘div’);
this.overlay.className = ‘simplybook-widget-overlay’;
var instance = this;
this.overlay.addEventListener(‘click’, function() {
instance.closePopup();
});
}
document.body.appendChild(this.overlay);
var instance = this;
// to show animated appear
setTimeout(function() {
instance.overlay.className = instance.overlay.className + ’ active’;
}, 10);
};
SimplybookWidget.prototype.hideOverlay = function() {
if (this.overlay) {
this.overlay.className = ‘simplybook-widget-overlay’;
var instance = this;
setTimeout(function() {
document.body.removeChild(instance.overlay);
}, 300);
}
};
SimplybookWidget.prototype.addButtonWidgetStyles = function() {
var link = document.createElement(‘link’);
link.setAttribute(‘rel’, ‘stylesheet’);
link.setAttribute(‘type’, ‘text/css’);
link.setAttribute(‘href’, this.options.url + ‘/v2/widget/widget.css’);
document.getElementsByTagName(‘head’)[0].appendChild(link);
};
SimplybookWidget.prototype.getIframeNode = function() {
if (!this.frame) {
this.frame = document.createElement(‘iframe’);
this.frame.border = ‘0’;
this.frame.frameBorder = ‘0’;
this.frame.name = this.name;
this.frame.id = this.name;
this.frame.src = this.getUrl();
this.subscribeMessages();
}
return this.frame;
};

Anonymous wrote:

Here’s a screenshot of the error again combined with the cross origin and 503 errors. Some browsers only show the x-origin error others show both the x-origin and 503 together.

Dmytro Bondarev wrote:

You are getting 503 error because you are trying to make a lot of calls at the same time.
You have to cache data and try to minimize API calls.

We do not send origin headers on 503 error.

Anonymous wrote:

Thanks!! That helps!

Anonymous wrote:

This was the problem. I had a component that was calling the API multiple times in fast succession. Thanks again!