Passing body in restassured using Generics doesn't work for File and FileInputStream - rest-assured

In Restassured we can pass the request payload in the body method by different ways like
String
POJO Object
Map Object
JsonObject (from GSON library)
File and
FileInputStream
So, I created following one method using generics to accommodate all these types: -
public <T> Response postAMember(T body) {
return given().spec(this.spec).body(body).when().post(EndPoints.GET_ALL_POST_A_MEMBER).andReturn();
}
Now, this is how I'm consuming it for respective Type (Not all in one go...one at a time): -
#Test
public void postMember() throws IOException {
// Using Hashmap
Map<String, String> body = new HashMap<>();
body.put("name", "Rocky");
body.put("gender", "Male");
// Using Model and GSON
Member imember = new Member("Rocky", "Male");
Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
String body = gson.toJson(imember);
// Using JsonObject (GSON)
JsonObject body = new JsonObject();
body.addProperty("name", "Rocky");
body.addProperty("gender", "Male");
// Using Payload JSON File
File body = new File("src/test/resources/Payloads/postmemberpayload.json");
// Using Raw String
String body = "{\r\n" +
" \"name\": \"Rocky\",\r\n" +
" \"gender\": \"Male\"\r\n" +
"}";
// Using FileInputStream
FileInputStream fis = new FileInputStream(body); // in this case I would pass fis to body method
Response resp = MemberService.getMemberServiceInstance().postAMember(body);
Assert.assertEquals(resp.getStatusCode(), StatusCode.CREATED_201);
Member omember = resp.getBody().as(Member.class);
System.out.println(omember.toString());
}
postAMember method works fine only with : -
String
POJO Object
Map Object
JsonObject (from GSON library)
But fails with remaining two: -
File - Output is bad request 400
FileInputStream - Output is java.lang.IllegalArgumentException: jdk.internal.ref.PhantomCleanable<?> declares multiple JSON fields named next
And for now I've to make following two more overloaded version of postAMember: -
public Response postAMember(File body) {
return given().spec(this.spec).body(body).when().post(EndPoints.GET_ALL_POST_A_MEMBER).andReturn();
}
public Response postAMember(FileInputStream body) {
return given().spec(this.spec).body(body).when().post(EndPoints.GET_ALL_POST_A_MEMBER).andReturn();
}
Now above two methods generate the response. Any clue what's wrong here? Why the method with generics is not able to take File and FileInputStream?
I've fetched the latest Restassured libraries from maven central repo.

As far as I understand, your generics method will map to body(Object object) of RequestSpecification, then this object will be serialized.
class RequestSpecificationImpl
...
RequestSpecification body(Object object) {
...
this.requestBody = ObjectMapping.serialize(object, requestContentType,
findEncoderCharsetOrReturnDefault(requestContentType), null,
objectMappingConfig(), restAssuredConfig().getEncoderConfig());
...
}
All below kinds of object has no problem with serialization.
String
POJO Object
Map Object
JsonObject (from GSON library)
But
File ---serialize---> full path of FILE
FileInputStream ---serialize---> exception (from Gson/Jackson)
When you add 2 methods, then Rest-Assured correctly map to body(File body) and body(InputStream body) --> no serialization for them --> no issue.

Related

Post Request via JxBrowser does not hand over data

I try a POST Request with the new JxBrowser Version. Unfortunately the data in the body is not handed over.
I guess I am just not using JxBrowser 7 properly.
GET Request does work.
// Post Request
protected void postRequestFromScout(JxBrowserEvent event) {
String url = event.getUrl();
Map<String, String> postData = event.getPostData();
getBrowser().navigation().loadUrl(LoadRequest.newBuilder()
.setUrl(url)
.setPostData(toPostDataString(postData))
.build());
}
// data in POST Request Body as String
protected String toPostDataString(Map<String, String> postData) {
StringBuilder sb = new StringBuilder();
for (Entry<String, String> entry : postData.entrySet()) {
sb
.append(entry.getKey())
.append("=")
.append(IOUtility.urlEncode(entry.getValue()))
.append("&");
}
sb.deleteCharAt(sb.length() - 1);
return sb.toString();
}
I obviously need to hand over the data in this way:
LoadUrlParams.newBuilder(url)
.postData(toPostDataString(postData))
.build();
As we are using a Compiler based on Java 7 in our Project, this is not a solution for me right now and I will check for another one if possible, but it surely works when used with Java 8.

Map one field value to other field using Gson on condition like if first one is null or having value zero

I am trying to map one field value to other if that is null or zero. I am using Gson library to parse Json. Is there any way to map values on condition base. I tried using JsonAdapter where I can put condition for that field but unable to find out solution how to get other field value there to put in that.
Write your own deserializer implementing JsonDeserializer<Element> something like below according json response-
class CustomDeserializer implements JsonDeserializer<Element> {
#Override
public Element deserialize(final JsonElement json, final Type typeOfT, final JsonDeserializationContext context) throws JsonParseException {
JsonObject jobject = json.getAsJsonObject();
JsonObject object = jobject.get("key").getAsJsonObject();
if(object == null){
}
return null;
}
}
Usage -
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(Element.class, new CustomDeserializer());
Gson gson = gsonBuilder.create();

How to generate JSON-Schema from Swagger API Declaration

I have Swagger API Declaration for services using Swagger v 1.2.
My original feeling about Swagger was that it is very close to JSON Schema (Draft 3 and lately Draft 4) and it shall be relatively easy to generate JSON Schema for request and response objects.
However, while part of the Swagger reuses JSON Schema structures, it turned out that it uses only a subset of features, and it also introduces its own inheritance in Models (using subTypes and discriminator).
Question: Is there any existing project or piece of code which can generate usable JSON Schema from Swagger API Declaration?
Optimally JSON Schema Draft 4 and using Python (but I will be happy to find anything).
After longer fight with using Swagger for specifying REST API and reusing it in related test suites, I will share my own experience with it (answering my own question).
Swagger supports only subset of JSON Schema Draft 4
Specification of Swagger 1.2 and 2.0 states, it supports only subset of JSON Schema Draft 4 (s. here). This means, that:
one cannot rely, that each valid JSON Schema can be completely supported by Swagger.
thinking of XML, Swagger supports only canonical representation of subset of JSON structures provided by JSON Schema Draft 4.
In other words:
Swagger (1.2 and 2.0) does not support usage of many JSON structures, which are valid in terms of JSON Schema Draft 4 (the same applies to Draft 3).
Swagger does not support general XML data structures, only very restricted structures are allowed.
In practice, you cannot start with designing your data in JSON or XML, with Swagger you have to start and end with Swagger.
Getting JSON Schema is theoretically possible, but not easy
I have spent some time coding a library, which would take Swagger API Specification and create JSON Schema Draft 4. I gave up for couple of reasons:
it was not easy at all
got disappointed finding, that I can use only subset of what JSON Schema provides. We had some JSON payload already proposed and had to start modifying it just to fit what Swagger specification framework allows.
Apart from having really nice looking UI for showing and testing the API (yes, everybody agrees, it is visually very pleasing), I have found it weird, that a specification framework is not allowing us to use what we want, but adds unexpected restrictions to our design.
If you want full JSON or XML Schema support, use RAML
Researching other API specification frameworks, I have found RAML. As it is built from ground up by supporting any JSON Schema Draft 3/4 or W3C XML Schema 1.0 data structures, the experience was excellent - having structure of my payload designed, I was able authoring API specification very quickly and following validation of real requests and responses against defined schemas was very easy, as the schemas are essentials components of the specification without adding any restrictions on them.
RAML was at version 0.8 that time (version 1.0 was not released yet).
Correcting the question leads to real solution
Good question makes half of the solution. My question was wrong as it missed fulfilling my real expectations. Corrected question would be:
What specification framework and technique to use, to specify REST API using payload defined by arbitrary JSON Schema Draft 4 or W3C XML Schema 1.0.
My answer to such a question would be:
Design your payload in JSON Schema Draft 4 or W3C XML Schema
Describe your REST API by means of RAML (v0.8 at the moment).
There might be other specification frameworks usable, but Swagger (neither v1.2 nor v2.0) is definitely not the case. Apart from providing really a lot of features (code generation, very nice looking documentation of the API and much more), it simply fails in providing solution to the updated question stated above.
There is a python tool to do the same by the name openapi2jsonschema.
You can simply install it using pip.
The readme for openapi2 shows the simplest way to use it:
openapi2jsonschema https://raw.githubusercontent.com/kubernetes/kubernetes/master/api/openapi-spec/swagger.json
Hope this helps.
I just wrote a tool pyswagger seems fit your need.
It loads Swagger API declaration, and able to convert python object to/from Swagger primitives. Also provide a set of client implementations (including requests & tornado.httpclient.AsyncHTTPClient) that able to make request to Swagger-enabled service directly.
This tool tends to solve the first problem I encountered when adapting Swagger in python, and is still pretty new now. Welcome for any suggestion.
I've had some success doing it like this:
swagger.yaml -> proto -> jsonschema
I used openapi2proto to generate proto files from Swagger yaml, then protoc-gen-jsonschema to generate the JSONSchemas from that. It's good enough to get a typed JSONSchema, but proto3 doesn't support "required" types so you miss out on this.
2023 Answer Update:
Open https://editor.swagger.io/
Paste your swagger.yaml file
Click Edit -> Convert and save as JSON as shown below.
Enjoy!
Install OpenApi to Jsonschema extractor:
Open terminal - do the following commands
sudo yum install python-pip
pip install openapi2jsonschema
download the openApi yaml file to a folder
cd to the downloaded folder and then run this command
openapi2jsonschema --strict <openapi yaml filename>
I've written one recursive function for creating json schema from Swagger definition.
For example, consider the swagger specification petstore-minimal
The definition Pet is converted into the below json-schema
{
"description": null,
"type": "object",
"properties": {
"name": {
"description": null,
"type": "string"
},
"id": {
"format": "int64",
"description": null,
"type": "integer"
},
"tag": {
"description": null,
"type": "string"
}
}
}
Ofcourse, this is a very minimal json schema, but can be achieved much more with the function that I wrote. Let me explain the way I did this, I used the following maven dependency to fetch the definitions from swagger specification
<dependency>
<groupId>io.swagger.parser.v3</groupId>
<artifactId>swagger-parser</artifactId>
<version>2.1.2</version>
</dependency>
To create the json schema, I used the below maven dependency
<dependency>
<groupId>net.minidev</groupId>
<artifactId>json-smart</artifactId>
<version>2.4.7</version>
</dependency>
Now to the coding part, the input is the location of the swagger spec and also the definition name which we need to convert to json schema
public static void main(String[] args) {
String jsonSchema = SwaggerUtil.generateJsonSchemaFromSwaggerSpec("path to swagger spec", "Pet");
System.out.println(jsonSchema);
}
Now, we need to process the swagger definition passed as the input and recursively process the properties of the definition
public static String generateJsonSchemaFromSwaggerSpec(String swaggerPath, String fieldName){
Swagger swagger = new SwaggerParser().read(swaggerPath);
Map<String, Model> definitions = swagger.getDefinitions();
Model schemaGenerationDefinition = definitions.get(fieldName);
Map<String, Property> propertyMap = schemaGenerationDefinition.getProperties();
Map<String, JsonProperty> customJsonPropertyMap = new HashMap<>();
propertyMap.forEach((propertyName, property) -> {
JsonProperty jsonProperty = processSwaggerProperties(propertyName, property, definitions);
customJsonPropertyMap.put(propertyName, jsonProperty);
});
JsonObjectProperty objectProperty = new JsonObjectProperty(customJsonPropertyMap, schemaGenerationDefinition.getDescription());
JSONObject generatedObject = objectProperty.toJsonObject();
String jsonSchema = generatedObject.toJSONString();
return jsonSchema;
}
private static JsonProperty processReference(String referenceName, String type, Map<String, Model> definitions){
Model model = definitions.get(referenceName);
Map<String, Property> propertyMap = model.getProperties();
Map<String, JsonProperty> jsonPropertyMap = new HashMap<>();
propertyMap.forEach((propertyName, property) -> {
JsonProperty jsonProperty = processSwaggerProperties(propertyName, property, definitions);
jsonPropertyMap.put(propertyName, jsonProperty);
});
if (type.equalsIgnoreCase("array")){
JsonArrayProperty jsonProperty = new JsonArrayProperty(model.getDescription());
jsonProperty.loadPropertiesFromMap(jsonPropertyMap);
return jsonProperty;
}else{
JsonObjectProperty objectProperty = new JsonObjectProperty(jsonPropertyMap, model.getDescription());
return objectProperty;
}
}
private static JsonProperty processSwaggerProperties(String propertyName, Property property, Map<String, Model> propertyDefinitions){
String definitionRefPath = "";
String type = "";
JsonProperty jsonProperty = null;
if (property.getType().equalsIgnoreCase("ref")){
definitionRefPath = ((RefProperty) property).getOriginalRef();
type = "object";
}else if (property.getType().equalsIgnoreCase("array")){
type = "array";
Property childProperty = ((ArrayProperty) property).getItems();
if (childProperty instanceof RefProperty){
RefProperty refProperty = (RefProperty) ((ArrayProperty) property).getItems();
definitionRefPath = refProperty.getOriginalRef();
}else{
JsonArrayProperty arrayProperty = new JsonArrayProperty(property.getDescription());
arrayProperty.loadChildProperty(childProperty);
return arrayProperty;
}
}else{
jsonProperty = PropertyFactory.createJsonProperty(property);
return jsonProperty;
}
String[] splitResult = definitionRefPath.split("/");
if (splitResult.length == 3) {
String propertyPath = splitResult[2];
System.out.println(propertyPath);
jsonProperty = processReference(propertyPath, type, propertyDefinitions);
}
return jsonProperty;
}
So for creating the json schema, I created my own custom json schema classes. that is for each of the json schema data types.. Also wrote from factory class to create the required json type
public class PropertyFactory {
public static JsonProperty createJsonProperty(Property property){
JsonProperty jsonProperty = null;
switch (property.getType()){
case "number":
jsonProperty = new JsonNumberProperty(property.getFormat(), property.getDescription());
break;
case "string":
jsonProperty = new JsonStringProperty(property.getDescription());
break;
case "boolean":
jsonProperty = new JsonBooleanProperty(property.getDescription());
break;
case "integer":
jsonProperty = new JsonIntegerProperty(property.getFormat(), property.getDescription());
if (property instanceof IntegerProperty){
IntegerProperty integerProperty = (IntegerProperty) property;
if (integerProperty.getMinimum() != null)
((JsonIntegerProperty) jsonProperty).setMinimum(integerProperty.getMinimum());
if (integerProperty.getMaximum() != null)
((JsonIntegerProperty) jsonProperty).setMaximum(integerProperty.getMaximum());
}else if (property instanceof LongProperty){
LongProperty longProperty = (LongProperty) property;
if (longProperty.getMinimum() != null)
((JsonIntegerProperty) jsonProperty).setMinimum(longProperty.getMinimum());
if (longProperty.getMaximum() != null)
((JsonIntegerProperty) jsonProperty).setMaximum(longProperty.getMaximum());
}
break;
default:
System.out.println("Unhandled type");
}
return jsonProperty;
}
}
Below are the abstractions I created for each of the json datatypes
public abstract class JsonProperty {
protected String type;
protected JSONArray required;
protected String description;
protected JsonProperty(String type, String description){
this.type = type;
this.description = description;
}
protected abstract JSONObject toJsonObject();
}
public class JsonArrayProperty extends JsonProperty{
private JsonProperty items;
public JsonArrayProperty(String description){
super("array", description);
}
#Override
protected JSONObject toJsonObject() {
JSONObject jsonObject = new JSONObject();
jsonObject.put("type", this.type);
jsonObject.put("description", this.description);
jsonObject.put("items", this.items.toJsonObject());
return jsonObject;
}
public void loadPropertiesFromMap(Map<String, JsonProperty> propertyMap){
this.items = new JsonObjectProperty(propertyMap, this.description);
}
public void loadChildProperty(Property childProperty){
this.items = PropertyFactory.createJsonProperty(childProperty);
}}
public class JsonObjectProperty extends JsonProperty{
private Map<String, JsonProperty> properties;
public JsonObjectProperty(Map<String, JsonProperty> properties, String description){
super("object", description);
this.properties = properties;
}
#Override
public JSONObject toJsonObject() {
JSONObject jsonObject = new JSONObject();
jsonObject.put("type", this.type);
jsonObject.put("description", this.description);
JSONObject propertyObject = new JSONObject();
this.properties.forEach((propertyName, jsonProperty) -> {
if (jsonProperty != null) {
JSONObject object = jsonProperty.toJsonObject();
propertyObject.put(propertyName, object);
}
});
jsonObject.put("properties", propertyObject);
return jsonObject;
}
public Map<String, JsonProperty> getProperties() {
return properties;
}
public void setProperties(Map<String, JsonProperty> properties) {
this.properties = properties;
}
}
public class JsonNumberProperty extends JsonProperty {
protected String format;
public JsonNumberProperty(String format, String description) {
super("number", description);
this.format = format;
}
public JsonNumberProperty(String type, String format, String description){
super(type, description);
this.format = format;
}
#Override
protected JSONObject toJsonObject() {
JSONObject jsonObject = new JSONObject();
jsonObject.put("type", this.type);
jsonObject.put("description", this.description);
if (this.format != null)
jsonObject.put("format", this.format);
return jsonObject;
}
}
public class JsonIntegerProperty extends JsonNumberProperty{
private String pattern;
private BigDecimal minimum;
private BigDecimal maximum;
public JsonIntegerProperty(String format, String description) {
super("integer", format, description);
}
#Override
protected JSONObject toJsonObject() {
JSONObject jsonObject = new JSONObject();
jsonObject.put("type", this.type);
jsonObject.put("description", this.description);
if (this.format != null)
jsonObject.put("format", this.format);
if (this.minimum != null)
jsonObject.put("minimum", this.minimum);
if (this.maximum != null)
jsonObject.put("maximum", this.maximum);
return jsonObject;
}
public void setPattern(String pattern) {
this.pattern = pattern;
}
public void setMinimum(BigDecimal minimum) {
this.minimum = minimum;
}
public void setMaximum(BigDecimal maximum) {
this.maximum = maximum;
}
}
public class JsonStringProperty extends JsonProperty{
public JsonStringProperty(String description) {
super("string", description);
}
#Override
protected JSONObject toJsonObject() {
JSONObject jsonObject = new JSONObject();
jsonObject.put("type", this.type);
jsonObject.put("description", this.description);
return jsonObject;
}
}
NOTE
This is a custom implementation that I did for my needs, if you come across any additional datatypes, you can simply create the type by extending the JsonProperty class and providing the toJsonObject implementation.
Happy Coding
using Swagger UI for documentation the highlighted link returns json object for your api schema :

Posting multipart form data to seam+RESTeasy fails marshalling to InputStream

I'm trying to post image data to a seam+RESTeasy endpoint and I'm getting a very cryptic error during JBoss startup. The HTTP request I'm sending has a content-type of multipart/form-data which has a single image/jpeg part with name "attachment". My service method looks like this:
#POST
#Path("uploadSymptomsImage/{appointmentGUID}")
#Consumes(MediaType.MULTIPART_FORM_DATA)
#Produces("application/json")
public String uploadSymptomsImage( #FormParam("attachment") InputStream fileInputStream,
#PathParam("appointmentGUID") String strAppointmentGUID )
{ ...
The error that I get is during startup:
Caused by: java.lang.RuntimeException: Unable to find a constructor that takes a String param or a valueOf() or fromString() method for javax.ws.rs.FormParam("attachment") on public java.lang.String com....AppointmentRestService.uploadSymptomsImage(java.io.InputStream,java.lang.String) for basetype: java.io.InputStream
at org.jboss.resteasy.core.StringParameterInjector.initialize(StringParameterInjector.java:206) [:]
at org.jboss.resteasy.core.StringParameterInjector.<init>(StringParameterInjector.java:57) [:]
at org.jboss.resteasy.core.FormParamInjector.<init>(FormParamInjector.java:22) [:]
My understanding was that media types could be automatically marshalled to InputStream. I've also tried java.io.File, java.io.Reader - both with same error. When I replace with byte[] or String I get a zero length array, or null as the parameter value.
How would you go about debugging this? Also, is it possible to access the raw request or pre-marshalled values?
Any suggestions here would be greatly appreciated.
You should retrieve the contents using MultipartFormDataInput. See the following example:
#POST
#Path("uploadSymptomsImage/{appointmentGUID}")
#Consumes(MediaType.MULTIPART_FORM_DATA)
#Produces("application/json")
public String uploadSymptomsImage(#PathParam("appointmentGUID") String strAppointmentGUID,
MultipartFormDataInput formData) {
Map<String, List<InputPart>> formDataMap = formData.getFormDataMap();
List<InputPart> attachments = formDataMap.get("attachment");
for(InputPart attachment : attachments) {
String fileName = extractFilename(attachment);
if(fileName.isEmpty()) continue;
InputStream in = attachment.getBody(new GenericType<InputStream>() {});
// Interact with stream
}
// Respond
}
The extractFilename method is a helper method I wrote:
private static String extractFilename(final InputPart attachment) {
Preconditions.checkNotNull(attachment);
MultivaluedMap<String, String> headers = attachment.getHeaders();
String contentDispositionHeader = headers.getFirst("Content-Disposition");
Preconditions.checkNotNull(contentDispositionHeader);
for(String headerPart : contentDispositionHeader.split(";(\\s)+")) {
String[] split = headerPart.split("=");
if(split.length == 2 && split[0].equalsIgnoreCase("filename")) {
return split[1].replace("\"", "");
}
}
return null;
}

ASP.Net MVC: how to create a JsonResult based on raw Json Data

Having a string containing the following raw Json data (simplified for the sake of the question):
var MyString = "{ 'val': 'apple' }";
How can I create a JsonResult object representing MyString?
I tried to use the Json(object) method. but it handles the raw json data as an string -logically :P-. So the returned HTTP response looks like:
"{ 'val': 'apple' }"
instead of the given raw Json Data:
{ 'val': 'apple' }
this is what I want to achieve:
The Json() method on Controller is actually a helper method that creates a new JsonResult. If we look at the source code for this class*, we can see that it's not really doing that much -- just setting the content type to application/json, serializing your data object using a JavaScriptSerializer, and writing it the resulting string.. You can duplicate this behavior (minus the serialization, since you've already done that) by returning a ContentResult from your controller instead.
public ActionResult JsonData(int id) {
var jsonStringFromSomewhere = "{ 'val': 'apple' }";
// Content() creates a ContentResult just as Json() creates a JsonResult
return Content(jsonStringFromSomewhere, "application/json");
}
* Starting in MVC2, JsonResult also throws an exception if the user is making an HTTP GET request (as opposed to say a POST). Allowing users to retrieve JSON using an HTTP GET has security implications which you should be aware of before you permit this in your own app.
The way I have generated json data from a string is by using JavaScriptResult in the controller:
public JavaScriptResult jsonList( string jsonString)
{
jsonString = "var jsonobject = new Array(" + jsonString + ");";
return JavaScript(jsonString)
}
Then when you request pass the json string to that action in your controller, the result will be a file with javascript headers.
I think you can use the JavaScriptSerializer class for this
var js = new System.Web.Script.Serialization.JavaScriptSerializer();
var jsonObject = js.Deserialize("{ 'val': 'apple' }", typeof(object));

Resources