Hey everyone,

I don't know how many people out here have ever heard of BroadSoft and the BroadWorks platform, but over the last few months I've been building toolkit (framework extension?) for working with the BroadWorks Extended Services Interface. BroadWorks is an enterprise VoIP / Unified Messaging platform that provides many of the features found on a traditional PBX and a lot more (TBH, all that call-switching and sip-signaling is all black magic voodoo to me).

The Extended Services Interface a set of REST services which allows third party application developers to managing / toggling the features available in BroadWorks (Think, call forwarding rules, do not disturb settings, business continuity, user directories, call histories, etc). See the API spec for Release 17 here: http://developer.broadsoft.com/node/11096

"SeXSI" (Sencha + XSI) is what I've code-named the project. If I had my way, it would be open source -- but it's not my call and I need to get permission before I release any major code stuffs. Right now it's only used in a few places internally by the Cloud PBX team... but it's coming along nicely. The end goal for a "1.0" release of SeXSI is to provide a complete set of components / widgets / data stores for the core "User Features" available in the Release 18 XSI-Actions package...

At any rate, I've run in to an issue that I can't seem to resolve. I've created a custom data proxy that is extended from the REST proxy. It is essentially identical to a REST proxy, except it allows for token replacement in the url, and (eventually) will assist with building headers, etc expected by the XSI.

I've also created 2 relatively simple data stores, and one slightly more complex store (with corresponding data models). None of these stores seem to be working correctly, and I'm wondering if my proxy implementation is wrong? When I try to attach the larger store to a grid, it renders an incorrect number rows, all of which are empty. Interestingly enough, the number of empty rows displayed on the grid is twice the number of actual rows returned by the XSI in xml. When I attempted to just load the simple store, it just says there are 0 records.

Here is my proxy:
Code:
/**
 * @docauthor Steve Hatherley <shatherley@broadsoft.com>
 *
 * The XSI proxy is a specialization of the {@link Ext.data.proxy.Rest RestProxy} which maps CRUD operations
 * to RESTful HTTP verbs as well as generating the standard set of HTTP Headers per the Xsi Interface Specification.
 *
 * Compatibility is targeted to version Release 18 Specicification
 */
Ext.define('SeXSI.data.proxy.XsiProxy', {
    extend: 'Ext.data.proxy.Rest',
    alias: 'proxy.xsi',
    /**
     * @cfg {String} HOST_URL
     * The base uri path to be prepended to all API calls (defaults to blank).
     * This can be the HOST url for the Xtended Services Interface, Both <host> and <host:port> are valid
     */

    config: {
        /**
         * @cfg {Boolean} batchActions
         * True to batch actions of a particular type when synchronizing the store. Defaults to false.
         */
        batchActions: false,


        /**
         * @cfg {String} defaultReaderType
         * The default registered reader type. Defaults to 'xml'.
         * @private
         */
        defaultReaderType: 'xml',


        /**
         * @cfg {String} defaultWriterType
         * The default registered writer type. Defaults to 'xml'.
         * @private
         */
        defaultWriterType: 'xml',
        
        /**
         * @cfg {String} userid
         * The BroadWorks UserID of the user initiating the call to XSI.
         */
        userid: undefined
    },
    constructor: function(cfg){


        cfg = cfg || {};
        console.log('*****************************************************');
        console.log('starting this.config in XsiProxy: ', this.config);
        console.log('starting cfg in XsiProxy: ', cfg);
        me = this;
        //me.
        Ext.apply(this, cfg);
        this.initConfig(cfg);
        console.log('this.config inside XsiProxy after initConfig',this.config);
        console.log('cfg inside XsiProxy after initConfig',cfg);


        console.log(this);
        me.buildHost();
        
        if(me.userid == undefined){
            me.userid = XSI.userid;
        }        


        me.headers = {
            HTTP_BW_USERID: me.userid || XSI.userid
        };
        
        console.log('me.serviceName',me.serviceName);
        if(me.serviceName === undefined ){
            // <debug>
            //console.error('No serviceName provided!');
            me.serviceName = this.config.serviceName;
        // </debug>
        }
        me.writer.root = me.serviceName;  
        
        console.log('*****************************************************');
        return me.callParent([cfg]);
    },
    setUserID: function(userid){
        me.userid = userid;
        me.buildHost();
    },    
    checkAuth: function(){
        if(!me.userid){
            console.error('No USERID Specified. Defaulting to redacted@redacted');
            me.userid = 'redacted@redacted';
        }
    },
    buildHost: function(){
        console.log('inside buildHost()');
        console.log('HTTP_BW_HOST',this.HTTP_BW_HOST);
        
        if(this.HTTP_BW_HOST === undefined){
            this.HTTP_BW_HOST= '';
            // <debug>
            console.log('HTTP_BW_HOST',this.HTTP_BW_HOST);
            // </debug>
        }
        return this.HTTP_BW_HOST + '/' + this._NAMESPACE + '/' + this.API_VER;
    },
    /**
         * Generates a url based on a given Ext.data.Request object. By default, ServerProxy's buildUrl will add the
         * cache-buster param to the end of the url.The XsiProxy implements dynamic token replacement on the proxy URL
         * 
         *  url: 'user/<userid>/services',
         *  token: 'userid'
         * 
         * 
         * @param {Ext.data.Request} request The request object
         * @return {String} The url
         */
    buildUrl: function(request){  
        var me        = this,
        operation = request.operation,
        records   = operation.records || [],
        record    = records[0],
        format    = me.format,
        url       = me.getUrl(request),
        id        = record ? record.getId() : operation.id;        
        
        url = me.buildHost() + '/' + url;        
        if (me.token !== undefined) {        
            url = url.replace("<" + me.token + ">", me[me.token]);        
        }        
        
        if (format) {        
            if (!url.match(/\?$/)) {        
                url += '?';        
            }        
        
            url += "format=" + format;        
        }        
        request.url = url;        
        return me.callParent(arguments);        
    },        
    /***        
    * Build HTTP Headers expected by XSI        
    */        
    buildHeaders: function(request){        
        
    },        
    authHeaders: function(){},        
    contentHeaders: function(){}   
},function(){
    Ext.apply(this.prototype, {
        /***
         * @property {String} API_VER
         * Broadsoft Xtended Services Interface API Version
         */
        API_VER:  "v2.0",
        _NAMESPACE: 'com.broadsoft.xsi-actions',
        userid: undefined,       
        /**
         * @property {Object} actionMethods
         * Mapping of action name to HTTP request method. These default to RESTful conventions for the 'create', 'read',
         * 'update' and 'destroy' actions (which map to 'POST', 'GET', 'PUT' and 'DELETE' respectively). This object
         * should not be changed except globally via {@link Ext#override Ext.override} - the {@link #getMethod} function
         * can be overridden instead.
         */
        actionMethods: {
            create : 'POST',
            read   : 'GET',
            update : 'PUT',
            destroy: 'DELETE'
        }
    });
    
});
and here is the simplest model:

Code:
Ext.define('SeXSI.data.model.user.services.AnonymousCallRejection', {
    extend: 'Ext.data.Model',
    alias: ['model.AnonymousCallRejectionModel'],
    config: {
        serviceName: 'AnonymousCallRejection'
    },
    fields: [
            {name: 'active', type: 'Boolean'}
    ]
});
and here is the corresponding store:

Code:
Ext.define('SeXSI.data.store.user.services.AnonymousCallRejection', {
    extend: 'Ext.data.Store',
    alias: ['store.AnonymousCallRejectionStore'],
    uses: ['SeXSI.data.proxy.XsiProxy'],
    requires  : ['SeXSI.data.model.user.services.AnonymousCallRejection'],
    config: {
        model: 'SeXSI.data.model.user.services.AnonymousCallRejection',
        proxy: {
            type: 'xsi',
            token: 'userid',
            url: 'user/<userid>/services/anonymouscallrejection',
            reader: {
                type: 'xml',
                record: 'AnonymousCallRejection'
            },
            writer: {
                header: '<?xml version="1.0" encoding="ISO-8859-1"?>'
            }
        }
    },
    constructor: function (config) {
        // <debug>
        console.log('inside SeXSI.data.store.user.services.AnonymousCallRejection constructor');
        console.log('starting this.config in AnonymousCallRejection: ', this.config);
        console.log('starting cfg in AnonymousCallRejection: ', config);
        // </debug>
        this.initConfig(config);


        // <debug>
        console.log('this.config after initConfig in ProfileStore: ', this.config);
        // </debug>
        return this.callParent(config);
    }
});
and here is the XML gets returned by the proxy and fed in to the store. The issue seems to be in parsing it, or extracting the values?

Code:
<?xml version="1.0" encoding="ISO-8859-1"?>
<AnonymousCallRejection xmlns="http://schema.broadsoft.com/xsi">
    <active>true</active>
</AnonymousCallRejection>
Edit: I had the 'record' config wrong for the AnonymousCallRejectionStore. Can't believe I didn't see that before. Adjusted the example. My bad.

Edit #2: Figured out the other issue with the more complex model. Accidentally put the Fields config inside of config: {..}