Ember data will define several type of errors :
DS.AdapterError < Ember.Error
DS.InvalidError < DS.AdapterError
DS.TimeoutError < DS.AdapterError
DS.AbortError < DS.AdapterError
An DS.AdapterError
has a errors
property that contains errors payload as defined in json-api
Adapter have to decide on the type of error to throw.
In case of error we going to expose throwed error on error
attribute of the record.
record.get('isError') // true
record.get('error') // DS.AdapterError
record.get('error.errors') // [{title: 'Server Error', status: 500}]
The store on commit rejection checks if an error is a DS.InvalidError
(or a subclass) and in this case will extract error messages by looking a source.pointer
attribute of each error in the json payload.
Adapter will implement a hook buildError
that will take a json-api errors document and decide on the type of error to throw. Main distinction is DS.InvalidError
for “user recoverable” errors with messages and other type of “system” errors. Default implementation is to check for 422. But different apis can have different strategies to distinguish the type of error.
We need to make clear the distinction between inValid
state and isError
state. The first is for “user recoverable” type of errors. The second is for “system is broken” type of errors. Maybe we need a better name then isInvalid
? Current name makes reference to validation errors and can be confusing. We feel that validations are a different problem and are a controller/component concern.
The mapping of attributes to error messages is exposed as DS.Model#errors
on the record.
record.get('errors').get('firstName') // ['title should not be empty']
record.get('error') // DS.InvalidError
record.get('error.errors') // [{title: "Invalid Attribute Error", details: 'First name should not be empty', status: '421', source: {pointer: 'data/attributes/first_name'}}]
On the adapter level we are going to use json-api error format to communicate with the store.
Main goal is to stop leaking jqXHR
objects to adapter hooks. It will make it easier to move to fetch
when the time comes.
RESTAdapter
implements a new hook :
/**
@method handleResponse
@param {String} status
@param {Object} headers
@param {Object} payload
@return {Object | DS.AdapterError} json response payload or an error
*/
handleResponse: function(status, headers, payload) {}
Default implementation will use two boolean hooks isSuccess
and isInvalid
. A response wich is not one of them or that thrown an exception is concidered an error
and will put the record in isError
state.
/**
@method handleResponse
@param {String} status
@param {Object} headers
@param {Object} payload
@return {Boolean}
*/
isSuccess: function(status, headers, payload) {}
/**
@method handleResponse
@param {String} status
@param {Object} headers
@param {Object} payload
@return {Boolean}
*/
isInvalid: function(status, headers, payload) {}
/**
@method ajaxSuccess
@deprecated
@param {jqXHR} jqXHR
@param {Object} payload
@return {Object} json response payload
*/
ajaxSuccess: function(jqXHR, payload) {}
/**
@method ajaxError
@deprecated
@param {jqXHR} jqXHR
@param {String} responseText
@param {String | Error} errorThrown
@return { jqXHR }
*/
ajaxError: function(jqXHR, responseText, errorThrown) {}
Not sure I understand the exact semantics of
ajaxSuccess
andajaxError
, can you expand on them a little bit?