Best way to Automate API (JSON) contract validation - java

I am trying to find the effective way to automate the API contract validation. Validations should cover
Field Mandatory or Optional
Field length
Field Type
Structure
Values not allowed
We are using Java with RESTAssured. For few APIs, in the past I have developed individual tests for each field & each validation (like below).
Ex: Test Employee Name is Mandatory (Submit an API request with empty Employee Name)
Test Employee Name is String (Submit an API request with number in Employee Name)
Test Employee name field length (Submit an API request with empty Employee Name more than allowed length)
This approach works but I guess there might be effective ways. Looking for suggestions.

I think using parameterized test will solve a part of your problem. The main idea is:
Provide a csv file (or any kinds), each line can contain data test and corresponding error message.
One line = one test.
Example:
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
public class DemoTest {
#ParameterizedTest
#CsvSource({
",name cannot be blank",
"lucas,name cannot less than 6 chars"
})
void test1(String name, String errorMsg){
System.out.printf("name=%s, errorMsg=%s", name, errorMsg);
}
}
The example goes with Junit5, you can do the same with TestNG #DataProvider

What you are referring to is JSON schema , you can create a schema for your api response mentioning which all fields are mandatory and then use it with postman or restassured .
You can generate schema using :
https://www.jsonschema.net/login

Related

Difference between #PathVariable, #RequestParam, and #RequestBody

I understand what the #PathVariable, #RequestParam and #RequestBody does in Spring, but not clear on which scenarios we have to use them as they are used for extracting value from the URI. Why we have to send data like localhost:8080/getBooks/time and localhost:8080/getBooks?book=time.
Example 1:
#RequestParam is used mainly for filtering purposes
Lets say you want to get George Martin's book:
GET localhost:8080/books?author=georgemartin
Here we pass author=georgemartin as request parameter. This will supposedly get all of Martin's books, example game of thrones series.
This will be used mainly for GET operation.
Example 2:
#PathVariable is used mainly for getting individual objects or piece of data
Lets say you want to get a book by its id:
GET localhost:8080/books/1
Here we pass 1 as path variable. This will supposedly get the 1 book with id 1, example first part of game of thrones' book.
This will be used mainly for DELETE/GET operation.
Example 3:
#RequestBody is used mainly for saving object(s)(or piece of data)
Lets say you want to add a book:
POST localhost:8080/books/
With request body having following attributes:
{
"author":"George Martin",
"Book":"Game of thrones"
...
...
}
This will add a book to the db. This would be used mainly for PUT/POST operation.
Note: never use verb naming for endpoint, instead, use plural nouns. So books/ is ideal instead of getbooks/.
Reference/Read more:
https://stackoverflow.blog/2020/03/02/best-practices-for-rest-api-design/#h-use-nouns-instead-of-verbs-in-endpoint-paths
https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/bind/annotation/RequestBody.html
https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/bind/annotation/RequestParam.html
https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/bind/annotation/PathVariable.html
#PathVariable is for parts of the path (i.e. /person/{id})
#RequestParam is for the GET query parameters (i.e. /person?name="Bob").
#RequestBody is for the actual body of a request.
#RequestBody used with POST Verb whereas
#RequestParam and #pathVariable used with GET Verb
#RequstParam : It extract the value from query string
used for fitering,sorting and pagination
In Request Param the values can be encrypted
localhost:8080/getBooks?start=1&end=100
#pathVariable : It extract value from URI Path
In Path variable the value cannot be encoded
Its used get the data based on the value
Reference:
https://www.baeldung.com/spring-requestparam-vs-pathvariable

Creating JsonLd + Hydra based Generic Client API in java. Is there any projects exist for reference?

I am creating Client API in Java using :+ Apache Jena FrameWork+ Hydra(for Hypermedia driven) + my private vocab similar to Markus Lanther Event-API Vocab instead of schema.org(for Ontology/Vocabulary part)
Section 1 :
After looking this Markus Lanther EventDemo repo and hydra-java.I found that they are creating classes for each hydra:Class that can break client in future .For example :
A Person class (Person.java)
public class Person
{
String name;
};
But in future requirement name is also a class eg:
public class Name
{
String firstName;
String LastName;
};
So to fulfill this requirement I have to update Person class like this:
public class Person
{
Name name;
};
Question 1:
Is my understanding correct or not of this Section? If yes then what is the way to deal with this part ?
Section 2:
To avoid above problem I created a GenericResource class(GenericResource.java)
public class GenericResource
{
private Model model;
public void addProperty(String propertyName,Object propertyValue)
{
propertyName = "myvocab:"+propertyName;
//Because he will pass propertyName only eg: "name" and I will map it to "myvocab:name"
//Some logic to add propertyName and propertyValue to model
}
public GenericResource retriveProperty(String propertyName)
{
propertyName = "myvocab:"+propertyName;
//Some logic to query and retrieve propertyName data from this Object add it to new GenericResource Object and return
}
public GenericResouce performAction(String actionName,String postData)
{
//Some logic to make http call give response in return
}
}
But again I stuck in lots of problem :
Problem 1: It is not necessary that every propertyName is mapped to myvocab:propertyName. Some may be mapped to some other vocab eg: hydra:propertyName, schema:propertyName, rdfs:propertyName, newVocab:propertyName, etc.
Problem 2: How to validate whether this propertyName belongs to this class ?
Suggestion: Put type field/variable in GenericResource class.And then check supportedProperty in vocab corresponding to that class.To more clarity assume above Person class which is also defined in vocab and having supportedProperty : [name,age,etc] .So my GenericResource have type "Person" and at time of addProperty or some other operation , I will query through vocab for that property is in supportedProperty list or in supportedOperation list in case of performAction().
Is it correct way ? Any other suggestion will be most welcomed?
Question 1: Is my understanding correct or not of this Section? If yes
then what is the way to deal with this part ?
Yes, that seems to be correct. Just because hydra-java decided to creates classes doesn't mean you have to do the same in your implementation though. I would rather write a mapper and annotate an internal class that can then stay stable (you need to update the mapping instead). Your GenericResource approach also looks good btw.
Problem 1: It is not necessary that every propertyName is mapped to
myvocab:propertyName. Some may be mapped to some other vocab eg:
hydra:propertyName, schema:propertyName, rdfs:propertyName,
newVocab:propertyName, etc.
Why don't you store and access the properties with full URLs, i.e., including the vocab? You can of course implement some convenience methods to simplify the work with your vocab.
Problem 2: How to validate whether this propertyName belongs to this
class
Suggestion: Put type field/variable in GenericResource class
JSON-LD's #type in node objects (not in #value objects) corresponds to rdf:type. So simply add it as every other property.
And then check supportedProperty in vocab corresponding to that class.
Please keep in mind that supportedProperty only tells you which properties are known to be supported. It doesn't tell you which aren't. In other words, it is valid to have properties other than the ones listed as supportedProperty on an object/resource.
Ad Q1:
For the flexibility you want, the client has to be prepared for semantic and structural changes.
In HTML that is possible. The server can change the structure of an html form in the way outlined by you, by having a firstName and lastName field rather than just a name field. The client does not break, rather it adjusts its UI, following the new semantics. The trick is that the UI is generated, not fixed.
A client which tries to unmarshal the incoming message into a fixed representation, such as a Java bean, is out of luck, and I do not think there is any solution how you could deserialize into a Java bean and survive a change like yours.
If you do not try to deserialize, but stick to reading and processing the incoming message into a more flexible representation, then you can achieve the kind of evolvability you're after. The client must be able to handle the flexible representation accordingly. It could generate UIs rather than binding data to fixed markup, which means, it makes no assumptions about the semantics and structure of the data. If the client absolutely has to know what a data element means, then the server cannot change the related semantics, it can only add new items with the new semantics while keeping the old ones around.
If there were a way how a server could hand out a new structure with a code-on-demand adapter for existing clients, then the server would gain a lot of evolvability. But I am not aware of any such solutions yet.
Ad Q2:
If your goal is to read an incoming json-ld response into a Jena Model on the client side, please see https://jena.apache.org/documentation/io/rdf-input.html
Model model = ModelFactory.createDefaultModel() ;
String base = null;
model.read(inputStream, base, "JSON-LD");
Thus your client will not break in the sense that it cannot read the incoming response. I think that is what your GenericResource achieves, too. But you could use Jena directly on the client side. Basically, you would avoid unmarshalling into a fixed type.

Bean Validation - constructor/factory parameter

I have a dto object which keeps an IP Range using first and last fields. Simple CRUD operations are made with this class using dropwizard (jersey-jackson-hibernate validator)
public class IpRangeDto {
#JsonCreator
public static IpRangeDto fromCidr(#JsonProperty("cidr") String cidr) {
//Resolve CIDR and assign first and last fields
}
#NotNull
#IpAddress // My custom validator
private String first;
#NotNull
#IpAddress
private String last;
}
For the sake of user-friendliness I had decided to add an alternative way to create this object, which is by using CIDR. So the client could send either first and last fields in JSON or only the cidr field. So the way to do it is as above, using #JsonCreator. And it works just fine.
"ipRange":{
"first": "15.0.0.1",
"last": "15.0.0.255",
}
"ipRange":{
"cidr": "15.0.0.0/24"
}
I want to validate this CIDR value that it's the right format so I can return 422 with proper error message. If I throw exception in the constructor/factory method then jersey-jackson returns 400 directly (even if I throw ConstraintViolationException, it's encapsulated by JsonProcessingException).
I could simply ignore the exceptions, and leave the fields empty which will return 422 because of #NotNull constraints but then the error message will not be as clear as it should be.
I tried adding my #Cidr validator next to the #JsonProperty parameter but that doesn't seem to be effective. My understanding is that validation occurs after Jackson is finished with creating Dtos, so with my #JsonCreator approach there might not be any solution to this problem. So I'm open to refactoring suggestions as well.
I am not an expert on the exact integration of Bean Validation into jackson, but I think it is just doing actual property validation. This means as you already pointed out, the entities are created first and then the properties are validated.
Bean Validation (as of version 1.1) also offers so called method validation, in which case you could place your Cidr constraint onto the string parameter of the method, but as said, I don't think that there is an integration in jackson for that.
And one more thing ;-) - static methods and properties are generally excluded from validation in Bean Validation (see also http://beanvalidation.org/1.1/spec/#d0e2815).
Regarding a workaround, one thing comes to mind (even though it feels a bit complicated). Write a custom class level IpRange constraint. In a class constraint you would get passed a IpRangeDto instance and it is up to you to validate the whole object and select the right error message for any violations. Provided you would add a cidr property to the dto which gets set when fromCidr is called, you would have then all information you need for the validation and selection of a proper error message.

How do I debug what's wrong when the Endpoints framework stops generating the WEB-INF/*.api-file?

Given a Google Cloud Endpoints project in Eclipse with the servlet-class annotated with #Api(name="helloworld"), the Endpoints framework generates a file named war/WEB-INF/helloworld-v1.api when the project compiles successfully. Sometimes this file is not generated even if there are no compilation errors though - only what I will call "GAE Endpoints code convention errors".
Example - working:
public class TestEntity {
public String Text;
public TestEntity(String text){
Text = text;
}
}
#ApiMethod
public TestEntity getTestEntity(){
return new TestEntity("Hello world");
}
Example - NOT working:
// The TestEntity-class is unchanged
#ApiMethod
public TestEntity getTestEntity(String input){
return new TestEntity("Hello world");
}
The problem with the latter example is that I take a String parameter as input without annotating it with #Named. I know that in this example, but there might be other cases where this is not so obvious.
Is there anywhere where I can read some sort of error log on why the .api file is not generated?
Although I am a fan of code by convention, it really takes the programming efficiency a step back if I cannot get feedback on what I do wrong. Eclipse provides compiler error feedback. The Google Cloud Endpoints Framework should provide Code-By-Convention-Rule-Breaking feedback.
There isn't currently good logging or error messaging when code generation fails, though it's one of the (if not most) requested features. In the interim, here's a list of the common failure cases:
The return type is invalid. Return types must be objects conforming to JavaBean conventions, and types like Object, String, and Integer are not allowed.
One or more argument types are invalid. Methods may accept at most one object in the POST body, and this object should also conform to JavaBean conventions. Methods may accept zero or more arguments via the query string (using the #Named annotation) and these must be scalar types (e.g. String, Integer).
An API, method, or parameter has an invalid name. APIs, methods, and parameters should be named to match the following regular expression: [a-z]+[A-Za-z0-9]*. Convention also suggests using lowerCamelCase for naming (though alllowercase is allowed).

Can converted #PathVariables reference each other?

I've got a Spring #RequestMapping with a couple of #PathVariables, and the first one is necessary to narrow down to the second one - you can see in the example below that I need to get the Department in order to get the Module. Using plain String #PathVariables I can do it like this:
#RequestMapping("/admin/{dept}/{mod}/")
public String showModule(#PathVariable String dept, #PathVariable String mod) {
Department department = dao.findDepartment(dept);
Module module = department.findModule(mod);
return "view";
}
But I'm keen to use Spring's Converter API to be able to specify the Department directly as the #PathVariable. So this works after I've registered a custom Converter class:
#RequestMapping("/admin/{dept}/")
public String showDept(#PathVariable Department dept) {
return "view";
}
But the Converter API doesn't give access outside of the single argument being converted, so it's not possible to implement the Converter for Module. Is there another API I can use? I'm eyeing up HandlerMethodArgumentResolver - has anyone solved a problem like this, or are you sticking to String #PathVariables?
I'm using Spring 3.1.
I haven't done it like this but one way I thought of was to make a separate converter for the both of them:
#RequestMapping("/admin/{deptAndModule}/")
public String showDept(#PathVariable DepartmentAndModule deptAndModule) {
return "view";
}
And have the converter able to take an input of the form "deptid-modid" e.g. "ch-c104". It wouldn't be possible to separate them with a slash as the request wouldn't match the RequestMapping pattern of /admin/*/.
In my case, the requirements have changed slightly so that module codes are fully unique and don't need to be scoped to department. So I don't need to do this any more. If I did, I would probably eschew the automatic Module conversion and do it manually in the method.

Categories