Two rest api response diff using rest assured - java

I am trying to compare the rest API response of two environments. I am using rest assured to get the response back. The problem I am facing is that I get a hashmap back which has a couple of hashmaps.
I have tried flattening the map into one map but keep on missing a hierarchy when the response differs slightly
here is a sample of the rest response
{
programmes:[
{
episodenumber:21,
seasonnumber:5,
seasontitle:"program Season 5",
seasonuuid:"7aabb30e-39a1-48bb-8dca-38f732c9c982",
seriestitle:"programm",
seriesuuid:"f95caa09-8cd7-4e23-a88a-444f7932714d",
sy:"blah blah blah. (S5, ep 21)",
t:"blah blah Fathers",
trailer:false,
type:"programme",
uuid:"69b91264-37c9-4b2f-a515-b45805a58867",
uuidtype:"programme",
waystowatch:{
svod:[
{
ad:false,
added:1495800164,
at:"DD",
availendtime:1497729599,
broadcasttime:1495137600,
cgid:10053,
cgname:"blah",
channelname:"blah1 HD",
d:2880,
downloadlink:"oig://FFC27E94",
hardofhearing:false,
is3d:false,
providerid:"ab_st_hd",
providername:"blah HD",
s:true,
sid:4061,
size:2352611,
sy:"blah blah. (S5, ep 21)",
videotype:"HD"
}
}
] ps:255,
tc:3
}
I am using the guava diff lib and for that, i need a simple map. I know this might not be the best of doing this comparison but this is for a short duration and this is what I can come up with. My question is how do I either flatten this response to make one map which i can pass to
MapDifference<String, ?> diff = Maps.difference(test, test2);
or is there a better way to compare two responses in rest assured. I have to get both the response at runtime.

If you are open to evaluating other tools take a look at Karate because it has been designed to:
allow you to specify expected payloads in (simplified) JSON itself
enable you to perform a "deep equals" comparison of two JSON payloads
be flexible so that you do a contains if you only want to check a sub-set of fields
handle dynamic values by letting you mark those fields as to be ignored or use a fuzzy match
even let you handle arrays where the elements are JSON (not just primitives), and ignore the order if needed
Disclaimer: am dev.

Related

How to combine JSON values in the response using Java

I am currently working on a school project. We have a series of response templates in JSON format that will take values from the request and then return it accordingly in the response when run in postman.
e.g
Request:
{
"Application_id":123456
}
Response:
{ "Application_id: 123456, TIMESTAMP: 20220501}
I am able to get these values in the response but the issue I am running accross now is figuring out how to combine 2 values in the request into one like so:
Request:
{
"Application_id":123456
"user_id_first_six": 456789
"user_id_last_four": 1234
}
Expected Response:
{ "Application_id: 123456, TIMESTAMP: 20220501, combined_id:456789****1234}
what I have tried is to put combined_id : "user_id_first_six"+******+"user_id_last_four" but this doesnt work.
Apologies if I cant be more specific as there are portions that I have left out due to confidentiality issues.
The easiest way to achieve this in Java would be to use JSONObject. In your Request-Handler, add two parameters of Type JSONObject and then merge them:
jsonObj.putAll(jsonObj1)
Thanks all for the guidance. I basically did what Knu8 suggested and extracted the values using Matcher+Regex (<(.*)>)(\W*)(<(.*)>) and converted them to strings and then used StringBuilder to append all the components together.

Rest Assured - comparing the json path with another

I have json for example below
{"TestJson":{
"Result":"Passed",
"description":"Passed."},
"Students":[{
"Class":{
"Primary":"Yes"
},
"Course":{
"Enrolled":"yes",
"AccountNumber":"2387287382"
},
"AccountNumber":"2387287382",
"Paid":"Yes"
}]}
I am wondering how can I find a good solution for this.
What I currently do
.body("Students[0].Course.AccountNumber",equalTo("2387287382"))
.body("Students[0].AccountNumber",equalTo("2387287382"))
My test criteria is to check key Students[0].AccountNumber matches Students[0].Course.AccountNumber
I want to do in this way, but i am not able to find a solution something like
.body("Students[0].Course.AccountNumber",equalTo("Students[0].AccountNumber"))
The above wont work obviously, but that is how I want to compare. basically comparing the key with another key and they should match.
Is this doable?
One way to do it is:
String A =
given ().
when().
get/put/post({api/name})
.extract()
.path("Students[0].Course.AccountNumber");
String B =
given ().
when().
get/put/post({api/name})
.extract()
.path("Students[0].AccountNumber");
Assert.assertEquals(A, B);
Seems like this workaround is the only way to go.
See the Use the response to verify other parts of the response section of the rest-assured docs. You basically want to create a lambda implementing ResponseAwareMatcher<Response>. Something like this:
get("/x").then().body("href", response -> equalTo("http://localhost:8080/" + response.path("userId"));

Proper json to back end post call

I'm having some troubles with different back-end processing of POST rest calls. I have two different objects which are updated through two different POST methods in my back-end. I catch the objects as a JsonNode, and in order to parse the attributes which I need to update, i create an iterator like so :
final Iterator<String> fieldNames = attributes.fieldNames();
The problem comes when I send my data from angular, in one case I need to explicitly send it like angular.toJson(data) in order to properly grab all the field names, and in the other case I just send the data (without the angular json conversion). Why is this behavior occurring ? Does this have to do with how I create the $http post call ? Here are the two different calls from angular:
$http.post(URL, angular.toJson(data)).success(function(data){
/*whatever*/ }).error(function(data) {
/*whatever*/ });
//Second call looks like this
var promise = $http({method: 'POST', url:URL, data:data, cache:'false'});
//this one i resolve using $q.all
I truncated the code to just the important stuff. My data is created like this currently(tried multiple ways in order to skip the need for toJson):
var data = "{\"Attribute1:\"+"\""+$scope.value1+"\","+
"\"Attribute2:\"+"\""+$scope.value2+"\"}";
How do I need to send the json data in order for it to correctly be converted to a JsonNode in my back-end, so I can properly iterate the fieldNames ?
I did manage to come to a common solution which consumes the json correctly in my back-end. I declared my json objects in angular like this :
$scope.dataToSend = {
"SomeAttribute" : "",
"SomeOtherAttribute" : ""
};
And then added my values like so :
$scope.dataTosend.SomeAttribute = someValue;
$scope.dataTosend.SomeOtherAttribute = someOtherValue;
No longer need to send the data with angular.toJson().

What are the best practices to add metadata to a RESTful JSON response?

Background
We are building a Restful API that should return data objects as JSON. In most of the cases it fine just to return the data object, but in some cases, f.ex. pagination or validation, we need to add some metadata to the response.
What we have so far
We have wrapped all json responses like this example:
{
"metadata" :{
"status": 200|500,
"msg": "Some message here",
"next": "http://api.domain.com/users/10/20"
...
},
"data" :{
"id": 1001,
"name": "Bob"
}
}
Pros
We can add helpful metadata to the response
Cons
In most cases we don't need the metadata field, and it adds complexity to the json format
Since it's not a data object any more, but more like a enveloped response, we can not use the response right away in f.ex backbone.js without extracting the data object.
Question
What is the best practices to add metadata to a json response?
UPDATE
What I've got so far from answers below:
Remove the metadata.status an return the http response code in the
http protocol instead (200, 500 ...)
Add error msg to body of an http 500 repsonse
For pagination i natural to have some metadata telling about the pagination structure, and the data nested in that structure
Small amount of meta data can be added to http header (X-something)
You have several means to pass metadata in a RESTful API:
Http Status Code
Headers
Response Body
For the metadata.status, use the Http Status Code, that's what's for!
If metadata is refers to the whole response you could add it as header fields.
If metadata refers only to part of the response, you will have to embed the metadata as part of the object.DON'T wrap the whole response in an artifical envelope and split the wrapper in data and metadata.
And finally, be consistent across your API with the choices you make.
A good example is a GET on a whole collection with pagination. GET /items
You could return the collection size, and current page in custom headers. And pagination links in standard Link Header:
Link: <https://api.mydomain.com/v1/items?limit=25&offset=25>; rel=next
The problem with this approach is when you need to add metadata referencing specific elements in the response. In that case just embed it in the object itself. And to have a consistent approach...add always all metadata to response. So coming back to the GET /items, imagine that each item has created and updated metadata:
{
items:[
{
"id":"w67e87898dnkwu4752igd",
"message" : "some content",
"_created": "2014-02-14T10:07:39.574Z",
"_updated": "2014-02-14T10:07:39.574Z"
},
......
{
"id":"asjdfiu3748hiuqdh",
"message" : "some other content",
"_created": "2014-02-14T10:07:39.574Z",
"_updated": "2014-02-14T10:07:39.574Z"
}
],
"_total" :133,
"_links" :[
{
"next" :{
href : "https://api.mydomain.com/v1/items?limit=25&offset=25"
}
]
}
Note that a collection response is an special case. If you add metadata to a collection, the collection can no longer be returned as an array, it must be an object with an array in it. Why an object? because you want to add some metadata attributes.
Compare with the metadata in the individual items. Nothing close to wrapping the entity. You just add some attributes to the resource.
One convention is to differentiate control or metadata fields. You could prefix those fields with an underscore.
Along the lines of #Charlie's comment: for the pagination part of your question you still need to bake the metadata into the response somhow, but the status and message attributes here are somewhat redundant, since they are already covered by the HTTP protocol itself (status 200 - model found, 404 - model not found, 403 - insufficient privs, you get the idea) (see spec). Even if your server returns an error condition you can still send the message part as the response body. These two fields will cover quite much of your metadata needs.
Personally, I have tended towards (ab)using custom HTTP headers for smaller pieces of metadata (with an X- prefix), but I guess the limit where that gets unpractical is pretty low.
I've expanded a bit about this in a question with a smaller scope, but I think the points are still valid for this question.
I suggest you to read this page https://www.odata.org/ You are not forced to use OData but the way they do the work is a good example of good practice with REST.
We had the same use case, in which we needed to add pagination metadata to a JSON response. We ended up creating a collection type in Backbone that could handle this data, and a lightweight wrapper on the Rails side. This example just adds the meta data to the collection object for reference by the view.
So we created a Backbone Collection class something like this
// Example response:
// { num_pages: 4, limit_value: 25, current_page: 1, total_count: 97
// records: [{...}, {...}] }
PageableCollection = Backbone.Collection.extend({
parse: function(resp, xhr) {
this.numPages = resp.num_pages;
this.limitValue = resp.limit_value;
this.currentPage = resp.current_page;
this.totalCount = resp.total_count;
return resp.records;
}
});
And then we created this simple class on the Rails side, to emit the meta data when paginated with Kaminari
class PageableCollection
def initialize (collection)
#collection = collection
end
def as_json(opts = {})
{
:num_pages => #collection.num_pages
:limit_value => #collection.limit_value
:current_page => #collection.current_page,
:total_count => #collection.total_count
:records => #collection.to_a.as_json(opts)
}
end
end
You use it in a controller like this
class ThingsController < ApplicationController
def index
#things = Thing.all.page params[:page]
render :json => PageableCollection.new(#things)
end
end
Enjoy. Hope you find it useful.
How about returning directly the object that you want in data, like return:
{
"id": 1001,
"name": "Bob"
}
And return in headers the metadata.
Option 1 (one header for all metadata JSON):
X-METADATA = '{"status": 200|500,"msg": "Some message here","next": "http://api.domain.com/users/10/20"...}'
Option 2 (one header per each metadata field):
X-METADATA-STATUS = 200|500
X-METADATA-MSG = "Some message here",
X-METADATA-NEXT = "http://api.domain.com/users/10/20"
...
Until now I was using like you, a complex JSON with two fields, one for data and one for metadata. But I'm thinking in starting using this way that I suggested, I think it will be more easy.
Remind that some server have size limit for HTTP headers, like this example: https://www.tutorialspoint.com/What-is-the-maximum-size-of-HTTP-header-values
JSON:API solves this by defining top-level meta and data properties.

Converting HTTP Response (Java "Properties" stream format) in to NSDictionary

I am working on iphone application which contains HTTP Request and Response.
The format of the response is a key/value format compatible with the Java "Properties" stream format.
I want to store the response into a NSDictionay. Could you suggest me any way to do this?
Thank you.
sangee
Edit:
Thanks guyz for the quick replies!!!
is their any other ways to store them in NSSdictionay?
I just want to store the album name and description in an array like this:
mutablearray = [wrwr, dsf, my album];
could you please let me know if this possible or not?
Thanks again!!!
This is the response i got it for my HTTP request...
GR2PROTO
debug_album= debug_gallery_version= debug_user=admin debug_user_type=Gallery_User debug_user_already_logged_in= server_version=2.12 status=0 status_text=Login successful.
#GR2PROTO debug_album= debug_gallery_version= debug_user=admin debug_user_type=Gallery_User debug_user_already_logged_in=1
album.name.1=wrwr album.title.1=wrwr album.summary.1= album.parent.1=0 album.resize_size.1=640 album.thumb_size.1=100 album.perms.add.1=true album.perms.write.1=true album.perms.del_item.1=true album.perms.del_alb.1=true album.perms.create_sub.1=true album.info.extrafields.1=Description
album.name.2=dsf album.title.2=dsf album.summary.2= album.parent.2=0 album.resize_size.2=640 album.thumb_size.2=100 album.perms.add.2=true album.perms.write.2=true album.perms.del_item.2=true album.perms.del_alb.2=true album.perms.create_sub.2=true album.info.extrafields.2=Description
album.name.3=my album album.title.3=my album album.summary.3= album.parent.3=0 album.resize_size.3=640 album.thumb_size.3=100 album.perms.add.3=true album.perms.write.3=true album.perms.del_item.3=true album.perms.del_alb.3=true album.perms.create_sub.3=true album.info.extrafields.3=Description
If you can, I would recommend serializing the data as JSON (or XML, if you have to) and parsing it using TouchJSON or a similar parser. If you really can't, then you'll have to implement your own parser--take a look at NSScanner.
Look at NSStream and the Stream Programming Guide for Cocoa.
Back in the day when Java was fully integrated into Cocoa, NSStream mapped onto Java streams. It still might. IIRC, (it's been a while) NSStream will return a properly populated NSDictionary from a Java stream.
Edit:
It looks like the text returned is just a space delimited hash which is the Java version of dictionary. It takes the form of key=value space key=value. The only tricky part is that some of the hashes are nested.
The first line for example is nested:
debug_album{
debug_gallery_version{
debug_user=admin
debug_user_type=Gallery_User
debug_user_already_logged_in{
server_version=2.12
status=0
status_text=Login successful.
}
}
}
You need a recursive scanner to parse that. The "key=space" pattern indicates a nested dictionary.

Categories

Resources