I now have a Siesta unit test that is reproducing this exception bug. I've had a dig through the code and come up with a fix for Sencha Touch/ExtJS. It's not pretty but I don't see any other way.
The docs ...
Ext-direct-RemotingProvider.actions states that the callback from a direct RPC include an event argument (e) from which you can get the exception message via event.getMessage() and the success/failure status via event.getStatus(). This is only place that I can find that documents the schema of the event
object when an exception occurs.
Touch 2.1.1 (A slightly different but equivalent code sample is documented in ExtJS 4.1.3)
Code:
TestAction.multiply(
2, 4, // pass two arguments to server, so specify len=2
// callback function after the server is called
// result: the result returned by the server
// e: Ext.direct.RemotingEvent object
function(result, e) {
var t = e.getTransaction();
var action = t.action; // server side Class called
var method = t.method; // server side method called
if (e.getStatus()) {
var answer = Ext.encode(result); // 8
} else {
var msg = e.getMessage(); // failure message
}
}
);
The bug ...
Code:
Ext.define('Ext.data.proxy.Direct', {
extend: 'Ext.data.proxy.Server',
alternateClassName: 'Ext.data.DirectProxy',
alias: 'proxy.direct',
requires: ['Ext.direct.Manager'],
....
createRequestCallback: function(request, operation, callback, scope) {
var me = this;
return function(data, event) {
me.processResponse(
event.getStatus(), operation, request, event.getResult() /* => response */, callback, scope);
};
},
// @inheritdoc
setException: function(operation, response) {
/// BUG: message => event.getResult().getMessage() - but should be event.getMessage()
operation.setException(response.getMessage());
},
....
}
The fix ...
In Direct.createRequestCallback() we need replace ...
Code:
me.processResponse(event.getStatus(), operation, request, event.getResult(), callback, scope);
with ...
Code:
me.processResponse(event.getStatus(), operation, request, event, callback, scope);
... so we can later perform response.getMessage() and obtain event.getMessage().
This would also make it look much like the other calls to processResponse() ...
Code:
data\proxy\Ajax.js(329):
me.processResponse(success, operation, request, response, callback, scope);
data\proxy\JsonP.js(229):
me.processResponse(success, operation, request, response, callback, scope);
Looking at the existing code below for Server.processResponse() ...
We need to ensure that the behavior for other proxies is unchanged but that this call ...
Code:
resultSet = reader.process(response);
becomes ...
Code:
resultSet = reader.process(response.getResult());
... but only for the Direct proxy.
So I propose that it become ...
Code:
resultSet = reader.process(me.getResponseResult(response));
Where Server.getResponseResult(response) is a noop that just returns response and is inherited by other proxies while Direct.getResponseResult(reponse) returns response.getResult()
The only other behavioral issue is the calls to ...
Code:
me.fireEvent('exception', this, response, operation);
.. but I suspect that they were sending the wrong response value as well and that this change will fix them too.
Finally there also a call to
Code:
this.fireEvent('exception', this, response, operation);
that I suspect should be
Code:
me.fireEvent('exception', this, response, operation);
The Direct proxy also needs a missing a unit test case that would have picked this up.
Code:
data\proxy\Server.js(222):
processResponse: function(success, operation, request, response, callback, scope) {
var me = this,
action = operation.getAction(),
reader, resultSet;
if (success === true) {
reader = me.getReader();
try {
resultSet = reader.process(response);
} catch(e) {
operation.setException(e.message);
me.fireEvent('exception', this, response, operation);
return;
}
// This could happen if the model was configured using metaData
if (!operation.getModel()) {
operation.setModel(this.getModel());
}
if (operation.process(action, resultSet, request, response) === false) {
this.fireEvent('exception', this, response, operation);
}
} else {
me.setException(operation, response);
/**
* @event exception
* Fires when the server returns an exception
* @param {Ext.data.proxy.Proxy} this
* @param {Object} response The response from the AJAX request
* @param {Ext.data.Operation} operation The operation that triggered request
*/
me.fireEvent('exception', this, response, operation);
}
//this callback is the one that was passed to the 'read' or 'write' function above
if (typeof callback == 'function') {
callback.call(scope || me, operation);
}
me.afterRequest(request, success);
},