CallBackery

 view release on metacpan or  search on metacpan

lib/CallBackery/qooxdoo/callbackery/source/class/callbackery/data/Server.js  view on Meta::CPAN

   Authors:   Tobi Oetiker <tobi@oetiker.ch>
   Utf8Check: äöü

************************************************************************ */

/**
 * JSON-RPC 2.0 client for the CallBackery backend. Wraps
 * {@link qx.io.jsonrpc.Client} while preserving the historic callback-style
 * API (callAsync/callAsyncSmart/callAsyncSmartBusy with a (ret, exc, id)
 * handler) so existing call sites keep working unchanged.
 *
 * @use(qx.io.transport.Xhr)
 */
qx.Class.define("callbackery.data.Server", {
    extend: qx.core.Object,
    type: "singleton",

    properties: {
        sessionCookie: {
            init: null,
            nullable: true
        },
        url: {
            init: 'QX-JSON-RPC/',
            check: 'String'
        },
        timeout: {
            init: 180000,
            check: 'Integer'
        }
    },

    members: {
        __client: null,

        /**
         * Lazily create the JSON-RPC client and wire the session-cookie header.
         * @return {qx.io.jsonrpc.Client}
         */
        _getClient: function() {
            if (!this.__client) {
                var self = this;
                var transport = new qx.io.transport.Xhr(this.getUrl());
                var client = new qx.io.jsonrpc.Client(transport);
                client.addListener('outgoingRequest', function() {
                    // getTransportImpl() creates a fresh impl that the
                    // immediately-following Xhr.send() will pick up and reuse.
                    var impl = transport.getTransportImpl();
                    impl.setTimeout(self.getTimeout());
                    var cookie = self.getSessionCookie();
                    if (cookie) {
                        impl.setRequestHeader('X-Session-Cookie', cookie);
                    }
                });
                this.__client = client;
            }
            return this.__client;
        },

        /**
         * Recursively replace non-finite numbers (NaN, Infinity) with null in
         * plain data so the outgoing JSON is valid. qx.io.jsonrpc serialises
         * via qx.util.Serializer.toJson, which emits a literal "NaN"/"Infinity"
         * (invalid JSON the server rejects), whereas the legacy transport used
         * JSON.stringify, which already mapped these to null. An empty numeric
         * form field is the common source of NaN. Dates and qooxdoo objects are
         * passed through untouched so the serialiser can handle them.
         *
         * @param v {var} value to sanitise
         * @return {var} json-safe value
         */
        _jsonSafe: function(v) {
            if (typeof v === 'number') {
                return isFinite(v) ? v : null;
            }
            if (v === null || typeof v !== 'object' || v instanceof Date) {
                return v;
            }
            if (qx.lang.Type.isArray(v)) {
                var arr = [];
                for (var i = 0; i < v.length; i++) {
                    arr[i] = this._jsonSafe(v[i]);
                }
                return arr;
            }
            // only descend into plain data objects; leave qooxdoo objects intact
            if (v.constructor === Object) {
                var out = {};
                for (var k in v) {
                    if (Object.prototype.hasOwnProperty.call(v, k)) {
                        out[k] = this._jsonSafe(v[k]);
                    }
                }
                return out;
            }
            return v;
        },

        /**
         * Perform the actual JSON-RPC call and adapt the promise to the
         * historic (ret, exc, id) callback contract.
         *
         * @param handler {Function} callback(ret, exc, id)
         * @param methodName {String} remote method
         * @param params {Array} positional parameters
         */
        _send: function(handler, methodName, params) {
            params = this._jsonSafe(params);
            var invoke = function(ret, exc, id) {
                try {
                    handler(ret, exc, id);
                }
                catch (e) {
                    if (window.console) {
                        window.console.error("Error while running CallAsync Handler", "ret:", ret, "exc", exc, "id", id, "e", e);
                    }
                }
            };
            this._getClient().sendRequest(methodName, params).then(
                function(result) {
                    invoke(result, null, null);
                },
                function(ex) {
                    // Only qx.io.exception.Protocol carries a server-supplied
                    // application code (e.g. 6 = login required, 7 = session
                    // expired). qx.io.exception.Transport/Cancel use their OWN



( run in 1.754 second using v1.01-cache-2.11-cpan-9581c071862 )