diff --git a/packages/activemodel-adapter/lib/system/active_model_adapter.js b/packages/activemodel-adapter/lib/system/active_model_adapter.js index d1a5d33f6d8..0046c5d197b 100644 --- a/packages/activemodel-adapter/lib/system/active_model_adapter.js +++ b/packages/activemodel-adapter/lib/system/active_model_adapter.js @@ -6,7 +6,6 @@ import {pluralize} from "ember-inflector"; @module ember-data */ -var forEach = Ember.EnumerableUtils.forEach; var decamelize = Ember.String.decamelize, underscore = Ember.String.underscore; @@ -142,18 +141,7 @@ var ActiveModelAdapter = RESTAdapter.extend({ var error = this._super(jqXHR); if (jqXHR && jqXHR.status === 422) { - var response = Ember.$.parseJSON(jqXHR.responseText), - errors = {}; - - if (response.errors !== undefined) { - var jsonErrors = response.errors; - - forEach(Ember.keys(jsonErrors), function(key) { - errors[Ember.String.camelize(key)] = jsonErrors[key]; - }); - } - - return new InvalidError(errors); + return new InvalidError(Ember.$.parseJSON(jqXHR.responseText)); } else { return error; } diff --git a/packages/activemodel-adapter/tests/integration/active_model_adapter_test.js b/packages/activemodel-adapter/tests/integration/active_model_adapter_test.js index 14d7c2116ce..c2724325083 100644 --- a/packages/activemodel-adapter/tests/integration/active_model_adapter_test.js +++ b/packages/activemodel-adapter/tests/integration/active_model_adapter_test.js @@ -21,7 +21,7 @@ test('buildURL - decamelizes names', function() { }); test('ajaxError - returns invalid error if 422 response', function() { - var error = new DS.InvalidError({ name: "can't be blank" }); + var error = new DS.InvalidError({ errors: { name: "can't be blank" } }); var jqXHR = { status: 422, @@ -32,7 +32,7 @@ test('ajaxError - returns invalid error if 422 response', function() { }); test('ajaxError - invalid error has camelized keys', function() { - var error = new DS.InvalidError({ firstName: "can't be blank" }); + var error = new DS.InvalidError({ errors: { firstName: "can't be blank" } }); var jqXHR = { status: 422, diff --git a/packages/ember-data/lib/adapters/rest_adapter.js b/packages/ember-data/lib/adapters/rest_adapter.js index 175377e5abf..c020c9aefb2 100644 --- a/packages/ember-data/lib/adapters/rest_adapter.js +++ b/packages/ember-data/lib/adapters/rest_adapter.js @@ -697,19 +697,25 @@ export default Adapter.extend({ }, /** - Takes an ajax response, and returns a relevant error. + Takes an ajax response, and returns an error payload. Returning a `DS.InvalidError` from this method will cause the record to transition into the `invalid` state and make the `errors` object available on the record. + This function should return the entire payload as received from the + server. Error object extraction and normalization of model errors + should be performed by `extractErrors` on the serializer. + + Example + ```javascript App.ApplicationAdapter = DS.RESTAdapter.extend({ ajaxError: function(jqXHR) { var error = this._super(jqXHR); if (jqXHR && jqXHR.status === 422) { - var jsonErrors = Ember.$.parseJSON(jqXHR.responseText)["errors"]; + var jsonErrors = Ember.$.parseJSON(jqXHR.responseText); return new DS.InvalidError(jsonErrors); } else { diff --git a/packages/ember-data/lib/serializers/json_serializer.js b/packages/ember-data/lib/serializers/json_serializer.js index 552b100a7bc..ab479634332 100644 --- a/packages/ember-data/lib/serializers/json_serializer.js +++ b/packages/ember-data/lib/serializers/json_serializer.js @@ -260,6 +260,16 @@ export default Ember.Object.extend({ delete hash[primaryKey]; }, + /** + @method normalizeErrors + @private + */ + normalizeErrors: function(type, hash) { + this.normalizeId(hash); + this.normalizeAttributes(type, hash); + this.normalizeRelationships(type, hash); + }, + /** Looks up the property key that was set by the custom `attr` mapping passed to the serializer. @@ -970,6 +980,41 @@ export default Ember.Object.extend({ } }, + /** + `extractErrors` is used to extract model errors when a call is made + to `DS.Model#save` which fails with an InvalidError`. By default + Ember Data expects error information to be located on the `errors` + property of the payload object. + + Example + + ```javascript + App.PostSerializer = DS.JSONSerializer.extend({ + extractErrors: function(store, type, payload, id) { + if (payload && typeof payload === 'object' && payload._problems) { + payload = payload._problems; + this.normalizeErrors(type, payload); + } + return payload; + } + }); + ``` + + @method extractErrors + @param {DS.Store} store + @param {subclass of DS.Model} type + @param {Object} payload + @param {String or Number} id + @return {Object} json The deserialized errors + */ + extractErrors: function(store, type, payload, id) { + if (payload && typeof payload === 'object' && payload.errors) { + payload = payload.errors; + this.normalizeErrors(type, payload); + } + return payload; + }, + /** `keyForAttribute` can be used to define rules for how to convert an attribute name in your model to a key in your JSON. diff --git a/packages/ember-data/lib/system/adapter.js b/packages/ember-data/lib/system/adapter.js index bc65550a500..8aabb4ffb28 100644 --- a/packages/ember-data/lib/system/adapter.js +++ b/packages/ember-data/lib/system/adapter.js @@ -23,6 +23,10 @@ var errorProps = [ transition to the `invalid` state and the errors will be set to the `errors` property on the record. + This function should return the entire payload as received from the + server. Error object extraction and normalization of model errors + should be performed by `extractErrors` on the serializer. + Example ```javascript @@ -31,7 +35,7 @@ var errorProps = [ var error = this._super(jqXHR); if (jqXHR && jqXHR.status === 422) { - var jsonErrors = Ember.$.parseJSON(jqXHR.responseText)["errors"]; + var jsonErrors = Ember.$.parseJSON(jqXHR.responseText); return new DS.InvalidError(jsonErrors); } else { return error; diff --git a/packages/ember-data/lib/system/store.js b/packages/ember-data/lib/system/store.js index 63b5739d731..c1514a86d1f 100644 --- a/packages/ember-data/lib/system/store.js +++ b/packages/ember-data/lib/system/store.js @@ -1869,7 +1869,9 @@ function _commit(adapter, store, operation, record) { return record; }, function(reason) { if (reason instanceof InvalidError) { - store.recordWasInvalid(record, reason.errors); + var errors = serializer.extractErrors(store, type, reason.errors, get(record, 'id')); + store.recordWasInvalid(record, errors); + reason = new InvalidError(errors); } else { store.recordWasError(record, reason); }