Getting the value from the response element using GPath and Rest Assured - java

I want to get the value of status from my response. So that i can assert it. I'm using rest assured with java & serenity BDD.
Response
{
"locationType": "STORE",
"locationId": "0003",
"events": {
"66e326db-fbfb-4f6e-9d2b-9425e7test5": {
"status": "BOOKING_OPEN"
}
}
}
So, here the event id (66e326db-fbfb-4f6e-9d2b-9425e7test5) is dynamic, which means for each run this UUID will get change.
Code
Response response = SerenityRest.lastResponse();
final ValidatableResponse validatableResponse = response.then();
validatableResponse.assertThat().body("events.*.status", containsString(expectedResponse));
When i run this, i'm getting Unrecognized Exception from serenity BDD. I think, that there is some issue in traversing in JSON.
Can someone please help me on getting the value of status here? So in this case, i'm looking for
BOOKING_OPEN

I think you should store UUID as a variable, and change your locator from your response.
response.getBody().jsonPath().get("events."+yourUUID+".status");

Groovy JsonSlurper does not support * breadthFirst() or ** depthFirst() tokens.
You could use below one to get the String result:
response.getBody().jsonPath().get("events.collect{it.value.status}.find()");
// would return "BOOKING_OPEN"
or below one to get a List result:
response.getBody().jsonPath().get("events.collect{it.value.status}");
//would return ["BOOKING_OPEN"]

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.

How to handle response with <?> type in spring-boot?

There is some third-party function with a method signature.
public ResponseEntity<?> uploadFile()
Without going into too much detail, this function uploads a file and returns a json response back, with a field containing the word ok.
I know the DTO structure that the function returns when the file is successfully uploaded.
But how do I handle a negative outcome. When does the error return? This example is abstract. But it can be understood that the structure of the erroneous DTO is not equal to the structure of the DTO on a positive outcome.
And I somehow need to check this field, let's call it "status", that it is not null. How can this be done better?
You can cast the response to String type first.
For example - ResponseEntity<String> resp = uploadfile();
Then check the status code for the response.
I am assuming that you will be getting 2xx series for a successful file upload and 4xx series if there is a failure.
Based on the status code you can conditionally map the string response body to whichever DTO you want to map it to.
For ex :
if(resp.getStatusCode()==200) {
SuccessDTO s= new
ObjectMapper().readValue(resp.getBody(),SuccessDTO.class);
}
else
{
ErrorDTO s= new
ObjectMapper().readValue(resp.getBody(),ErrorDTO.class);
}

How to fix "While parsing a protocol message, the input ended unexpectedly in the middle of a field" error

Getting InvalidProtocolBufferException: While parsing a protocol message, the input ended unexpectedly in the middle of a field. error when parsing Protobuf
Not sure why parsing failing ever though I can see the output if I call response.getContentAsString() method on response object.
my Proto file:
syntax = "proto2";
package com.test.protocol;
option java_package = "com.test.protocol.v1";
option java_outer_classname = "Test1";
message Test2 {
required int64 id = 1;
required string value = 2
}
I wrote API to return the Proto response which looks like below
final Test2.Builder builder = TEST2.newBuilder();
builder.setId(1);
builder.setValue("1");
return builder.build();
My API will return the below output
{"id": 1,"value": "1"}
I'm trying to call the API and parse it back to Test2 as below
import com.test.protocol.v1.Test1.Test2;
final Test2 result = Test2.parseFrom(response.getContentAsByteArray());
getting the error in the above step If I debug and try to print response.getContentAsString(), I'm getting correct output as below but parsing is failing.
{"id": 1,"value": "1"}
For your message, the serialized form should be the following 5 bytes 08-01-12-01-31.1 The parseFrom(byte[]) method expect such an encoding. As you give it a JSON, it misinterpret characters in the string and fails. For example, '{' means starting a group (this feature is deprecated) with tag 15.
I am going to assume you are using a web framework. You provide a protobuf message for the response in the server, and the framework sends the JSON representation to the client.
For the client to consume the JSON representation, you can use the following code.
JsonFormat.parser().merge(theJsonString, Test2.newBuilder())
Of course then you are not "using protobuf". Instead you are merely using protobuf generated classes.
For more the information of the encoding, see the documentation and try out the decoder.

Rest Assured Empty response body structure

I'd like to test a find rest service, if I find smth I want to delete from the database, otherwise do nothing
I use it like this (where rs is the Response from find)
JsonPath jsonPath = rs.getBody().jsonPath();
Object foundName= jsonPath.get("name");
if (foundName!= null) {
expect().statusCode(200).when().delete("..." + foundName);
}
So when nothing is found how to check the foundName for it , because I tried foundName!=null or foundName != "", and still it's not working.
So please explain what is the structure of an empty response body
Based on the debug info foundName is of type List , so the solution was to cast foundName to List and check if it's empty.
List foundName = (List)jsonPath.get("name");
foundName.isEmpty()
rs.body(blankOrNullString());
worked for me to verify the response body is null or blank.
You could call jsonPath.getString("name") which casts your (empty) response body to String and you could check it with equals("") (see RESTassured JavaDoc). I assumed that "name" is of type String.

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.

Categories

Resources