Handling errors with Ember Data using the JSON API standard [duplicate] - json

I am using Ember 1.13.7 and Ember Data 1.13.8, which by default use the JSON-API standard to format the payloads sent to and received from the API.
I would like to use Ember Data's built-in error handling in order to display red "error" form fields to the user. I have formatted my API error responses as per the JSON-API standard, e.g.
{"errors":[
{
"title":"The included.1.attributes.street name field is required.",
"code":"API_ERR",
"status":"400",
}
]}
and when I attempt to save my model the error callback is being correctly executed. If I look within the Ember Inspector I can see that the model's "isError" value is set to true but I can't see how Ember Data is supposed to know which field within the model is the one in an error state? I see from the official JSON-API pages (http://jsonapi.org/format/#errors) that you can include a "source" object within the error response:
source: an object containing references to the source of the error,
optionally including any of the following members:
pointer: a JSON Pointer [RFC6901] to the associated entity in the request document
[e.g. "/data" for a primary data object, or "/data/attributes/title"
for a specific attribute].
parameter: a string indicating which query
parameter caused the error.
but is this what I should be doing in order to tell Ember Data which fields it should mark as being in an error state?
If anyone can help shed some light on this I'd be grateful.
Thanks.

Note the answer below is based on the following versions:
DEBUG: -------------------------------
ember.debug.js:5442DEBUG: Ember : 1.13.8
ember.debug.js:5442DEBUG: Ember Data : 1.13.9
ember.debug.js:5442DEBUG: jQuery : 1.11.3
DEBUG: -------------------------------
The error handling documentation is unfortunately scattered around at the moment as the way you handle errors for the different adapters (Active, REST, JSON) are all a bit different.
In your case you want to handle validation errors for your form which probably means validation errors. The format for errors as specified by the JSON API can be found here: http://jsonapi.org/format/#error-objects
You'll notice that the API only specifies that errors are returned in a top level array keyed by errors and all other error attributes are optional. So seemingly all that JSON API requires is the following:
{
"errors": [
{}
]
}
Of course that won't really do anything so for errors to work out of the box with Ember Data and the JSONAPIAdapter you will need to include at a minimum the detail attribute and the source/pointer attribute. The detail attribute is what gets set as the error message and the source/pointer attribute lets Ember Data figure out which attribute in the model is causing the problem. So a valid JSON API error object as required by Ember Data (if you're using the JSONAPI which is now the default) is something like this:
{
"errors": [
{
"detail": "The attribute `is-admin` is required",
"source": {
"pointer": "data/attributes/is-admin"
}
}
]
}
Note that detail is not plural (a common mistake for me) and that the value for source/pointer should not include a leading forward slash and the attribute name should be dasherized.
Finally, you must return your validation error using the HTTP Code 422 which means "Unprocessable Entity". If you do not return a 422 code then by default Ember Data will return an AdapterError and will not set the error messages on the model's errors hash. This bit me for a while because I was using the HTTP Code 400 (Bad Request) to return validation errors to the client.
The way ember data differentiates the two types of errors is that a validation error returns an InvalidError object (http://emberjs.com/api/data/classes/DS.InvalidError.html). This will cause the errors hash on the model to be set but will not set the isError flag to true (not sure why this is the case but it is documented here: http://emberjs.com/api/data/classes/DS.Model.html#property_isError). By default an HTTP error code other than 422 will result in an AdapterError being returned and the isError flag set to true. In both cases, the promise's reject handler will be called.
model.save().then(function(){
// yay! it worked
}, function(){
// it failed for some reason possibly a Bad Request (400)
// possibly a validation error (422)
}
By default if the HTTP code returned is a 422 and you have the correct JSON API error format then you can access the error messages by accessing the model's errors hash where the hash keys are your attribute names. The hash is keyed on the attribute name in the camelcase format.
For example, in our above json-api error example, if there is an error on is-admin your would access that error like this:
model.get('errors.isAdmin');
This will return an array containing error objects where the format is like this:
[
{
"attribute": "isAdmin",
"message": "The attribute `is-admin` is required"
}
]
Essentially detail is mapped to message and source/pointer is mapped to attribute. An array is returned in case you have multiple validation errors on a single attribute (JSON API allows you to return multiple validation errors rather than returning just the first validation to fail). You can use the error values directly in a template like this:
{{#each model.errors.isAdmin as |error|}}
<div class="error">
{{error.message}}
</div>
{{/each}}
If there are no errors then the above won't display anything so it works nicely for doing form validation messages.
If you API does not use the HTTP 422 code for validation errors (e.g., if it uses 400) then you can change the default behavior of the JSONAPIAdapter by overriding the handleResponse method in your custom adapter. Here is an example that returns a new InvalidError object for any HTTP response status code that is 400.
import DS from "ember-data";
import Ember from "ember";
export default DS.JSONAPIAdapter.extend({
handleResponse: function(status, headers, payload){
if(status === 400 && payload.errors){
return new DS.InvalidError(payload.errors);
}
return this._super(...arguments);
}
});
In the above example I'm checking to see if the HTTP status is 400 and making sure an errors property exists. If it does, then I create a new DS.InvalidError and return that. This will result in the same behavior as the default behavior that expects a 422 HTTP status code (i.e., your JSON API error will be processed and the message put into the errors hash on the model).

Related

I am trying to get dynamic value of a response variable in an api using jmeter json extractor and using reponse assertion of substring to assert it

However, I'm unable to get the desired response in assertion. I have used json extractor and given
Name of created variable as : contenttype
Json path expression: $..contenttype
Default values: contenttype
And in the response assertion with pattern as substring I have given
"contenttype":${contenttype}
But in results I am getting assertion fail message like test expected to contain /"contenttype:":VOD/
We cannot help without seeing:
The response (at least partial)
Your contenttype variable value (can be visualized using Debug Sampler)
The placement of the Response Assertion (I'm talking about Scoping Rules)
So far I can only tell that:
The correct syntax for JMeter Variables is ${your-variable-name-here} so you might want to change your expression to "contenttype":${contenttype}
If the value of the contenttype variable is supposed to be a String - in JSON it needs to be surrounded with quotation marks
"contenttype":"${contenttype}"
If there are spaces around : like "contenttype" : "VOD" your response assertion will fail

Not able to find the property of an object in Angular9

I'm doing a full-stack project where I got an Object with two property "success" and "message", through API when I register the form.
But my Frontend which is in Angular is not being able to read the success property.
authService.registerUser returns an Observable, that's why you can subscribe to it. You should check, if the object passed in the returned Observable corresponds to the expected object (the one that contains the properties "success" and "message").
If this is the case, you could try to access the property in the following way:
if (!data['success']) {
this.submitting = false
}
Otherwise you have to correct the code in order to handle the data according to its type.

Fitnesse JSON expectation syntax

In reference to workflow of creating tests using RestFixture, I am asking myself, what kind of syntax the following statement represents:
jsonbody.name === 'Ted'
I need to know all possibilities of this kind of syntax to write down the expected value for much more complex JSON responses.
Is there a name or reference for the type of syntax that is used here?
The syntax is Javascript syntax. jsonbody is a variable containing the response.
See JavascriptExpectations in RestFixtureLiveDoc for more details. E.g.:
As of RestFixture Version 2, a javascript engine is embedded to allow expectations in Javascript on response body contents in JSON format.
!**** XPaths and JSON
For backward compatibility XPath expressions are maintained and executed
****!
After a successful response is received with content type "application/json" the expectation cell in a .RestFixture row is
interpreted as a string with Javascript and executed within the context of the response body.
An example:
| Table: Rest Fixture |http://${jettyHost}:${jettyPort}|
|GET | /resources/%id%.json | 200 |Content-Type : application/json |!-
jsonbody.resource.name=="test post" && jsonbody.resource.data=="some data"
-!|

Get content from anyContent as Json

I have case class with field AnyContent. I get it from DB as
AnyContentAsText( //some value)
Than when I get it in JSON like text
Json.obj("body"->content.asText)
it returns
[{"body":"AnyContentAsJson({\"ma\":\"some#email.com\"})"}]
When I want get it like JSON
Json.obj(content.asJson)
I get
[null]
How can I get it like JSON but not null of course?
The only way to go from AnyContentAsText to JSON would be to simply do Json.parse(content.asText).
However, it's odd that you're getting a value from your DB as AnyContentAsText. AnyContentAsText and all other subclasses of AnyContent are really intended for the request lifecycle. When you consume a request in a controller method, the first thing you should be doing is parsing your AnyContent into the expected underlying value (text, json, etc.) and then do any business logic/persistence with those underlying values.
If you're receiving AnyContentAsText on the backend, check the request headers that the client sends.
I had forgotten the "Content-Type": "application/json" header on my POST with JSON content. After adding the header, I received AnyContentAsJson on the backend side, as expected.

Wrapping JSON payload with key in EmberJS

Ok basically I am sending a POST request with some JSON payload in it to Codeigniter. I use RESTAdapater. JSON get sent there with no key, so I have no access to it.
Here is model:
App.Bookmark = DS.Model.extend({
title: DS.attr("string"),
url : DS.attr("string")
});
Here is controller:
App.BookmarksNewController = Ember.ObjectController.extend({
save: function(){
this.get("model.transaction").commit();
this.get("target").transitionTo("bookmarks");
}
});
In REST implementation in CI that I use standard way to access the post request is $this->input("key"). But when the above request is generated, only raw JSON data is sent. Therefore I don't seem to have a way to reference it in any way.
Take this example:
function post(){
$this->response(var_dump(file_get_contents("php://input")),200);
}
Gives me this output:
string(48) "{"bookmark":{"title":"sdffdfsd","url":"sdfsdf"}}"
what I would like to see is:
string(48) payload="{"bookmark":{"title":"sdffdfsd","url":"sdfsdf"}}"
Then is server I would be able to access this JSON with something like $this->post("payload").
So 1 of 2. Anyway to wrap the JSON payload with key? Or anyway to access raw JSON data in CI with no key available??
Ok figured it out myself (or rather read properly the examples).
When using Adam Whitney's fork of Phil Sturgeons REST controller for CodeIgniter the JSON payload will be available in $this->_post_args. And in the underlying implementation he uses my already mentioned file_get_contents("php://input").
EDIT: Same convention applies for other type of requests as well, for example DELETE request data will be available in $this->_delete_args. Or $this->_args is the "magic" place where all types of args can be found.