Spring Data Rest, SpringFox and JpaRepository custom finders - spring

NB: using Spring Boot 1.4.2 + SpringFox 2.6.0
Hi, I'm having an issue with Swagger 2 forms on my API documentation over a #RepositoryRestResource. The code below works fine (REST access OK):
#RepositoryRestResource(collectionResourceRel = "people", path = "people")
public interface PersonRepository extends JpaRepository<Person, Long> {
Person findByLastName(#Param("name") String name);
}
And the HATEOAS links are right too: calling URL /api/people/search
ends up with this (notice parameter "name"):
{
"_links": {
"findByLastName": {
"href": "http://localhost:8080/api/people/search/findByLastName{?name}",
"templated": true
},
"self": {
"href": "http://localhost:8080/api/people/search"
}
}
}
The REST API is ok: URL /api/people/search/findByLastName?name=foobar returns data when executed with a browser
BUT in Swagger the GET parameter type is interpreted as "body" instead of "query" and the form submission (curl ... -d 'foobar'...) fails in 404, attempting to submit "name" as request body.
So I tried to set Swagger explicitly, like this:
#RepositoryRestResource(collectionResourceRel = "people", path = "people")
public interface PersonRepository extends JpaRepository<Person, Long> {
#ApiOperation("Find somebody by it's last name")
#ApiImplicitParams({
#ApiImplicitParam(name = "name", paramType = "query")
})
Person findByLastName(#Param("name") #ApiParam(name = "name") String name);
}
without any success, despite the fact that "name" is well retained in the form as the parameter name in this example :-(
body parameter type on GET query
Does anyone know what could be done to make that Swagger form to work? Thx for your help

This is it : #Param configures Spring Data REST, while #RequestParam fits Swagger
#RepositoryRestResource(collectionResourceRel = "people", path = "people")
public interface PersonRepository extends JpaRepository<Person, Long> {
// #Param Spring Data REST : Use #Param or compile with -parameters on JDK 8
// #RequestParam Swagger : paramType=query cf. $Api*Param
Person findByLastName(#Param("name") #RequestParam("name") String name);
}
Me happy!

Related

How to customize Spring Data REST to use a custom path for a repository resource?

I'm developing a microservice using SPRING DATA REST where I need to sub-resources to be accessible only thru main resource like the following structure :
http://localhost:8080/posts
http://localhost:8080/posts/1/comments
and block direct access to sub-resource directly like this http://localhost:8080/comments/*.
where comments must be accessible only thru related poste , I did the following in my repository :
#RepositoryRestResource(collectionResourceRel = "posts", path = "posts")
public interface PostRepository extends PagingAndSortingRepository<Post, Long> {
...
}
for comments :
#RepositoryRestResource(collectionResourceRel = "comments", path = "comments")
public interface CommentRepository extends PagingAndSortingRepository<Comment, Long> {
...
}
now by default SPRING DATA REST returns the following results when i goto to : http://localhost:8080
{
"id": 1,
"content": "some post text .........................",
"author":"abc",
"_links": {
"self": {
"href": "http://localhost:8080/posts/1"
},
"attributes": {
"href": "http://localhost:8080/posts/1/comments"
}
}
}
now if i want to post a comment i need to do the following :
http://{server:port}/comment METHOD:POST
{"author":"abc","content":"PQROHSFHFSHOFSHOSF", "post":"http://{server:port}/post/1"}
but what i need to achienve is to POST to an url like this http://{server:port}/posts/1/comment where the POST is the root resource NOT like the previous path http://{server:port}/comment
http://{server:port}/posts/1/comment METHOD:POST
{"author":"abc","content":"PQROHSFHFSHOFSHOSF", "post":"http://{server:port}/post/1"}
i now that this is possible if a create a custom comment #controller but i want to use the already building features of SPRING DATA REST and Hateoas support .

ServletRequestParameter exception, parameter is not present

I am creating an API, using Spring Boot and using PostMan to debug and test the system. Now I am having a difficulty in sending a class object as a parameter.
I wish to send an Entity passed as a parameter and display whether the entity has a specified certification or not.
Currently I am using Postman and using the raw "JSON" format to display my Entity Object, however I am getting an error
"resolved [org.springframework.web.bind.MissingServletRequestParameterException: Required Entity parameter 'store' is not present]"
Can anyone point me in the direction of what the problem could be
My JSON (this is the Entity CLass Object that I created, I am not able to recieve a response from the API, the API should send the string "Passed Check"
{
"id": 1,
"name": "asdasd",
"phoneNumber": "000",
"address": "asdasdasdasdasd",
"latitude": 0.0,
"longitude": 0.0,
"dateAdded": "06/08/2020"
}
Certification Controller
#Controller
public class CertController {
#Autowired
private CertificationRepository certRepo;
#Autowired
private StoreRepository StoreRepository;
#GetMapping(value = "/getCertification")
#GetMapping(value = "/getCertObject")
public #ResponseBody
String getCertificationPOJO(#RequestParam Entity store)
{
return "Passed check";
}
}
Postman Error
In the code you shared, I see a few issues.
Missing Path to Controller
Add #RequestMapping("/cert") on CertController.
#Controller
#RequestMapping("/cert")
public class CertController {
}
Wrong Parameter Type
getCertificationPOJO method should have #RequestBody instead of #RequestParam annotation
#GetMapping(value = "/getCertObject")
public #ResponseBody String getCertificationPOJO(#RequestBody Entity store) {
return "Passed check";
}
You can easily follow this guide: Building a RESTful Web Service

SPRING HATEOS LINKS (HTTPS vs HTTP)

I have a simple rest service which returns the response when a GET operation is performed along with the HATEOS links. When the service is deployed on the server and is accessed through a load balancer url (Https), the links section contains the url with "http" instead of "https". Any example solution/code for this particular scneario would help me.
For Example:
If a GET operation is performed by using the url(https://servicename.example.com) then I would expect the same url to be in the links section. But I receive something like below.
Response Body:
{
"fieldA": null,
"fieldB": null,
"links": [
{
"rel": "self",
"href": "http://servicename.example.com" // It has to be https
}
]
}
Below is the line of code that adds the links to the Rest Resource that will be exposed to the consumer.
resource.getLinks().add(linkTo(methodOn(ExampleController.class).methodInExampleController(arg a, arg b)).withSelfRel());
The resource that is being exposed to the consumer extends another class"ResourceSupport" where a field called links is declared which is of type List.
Example of Resource class:
imnport com.examplepackage.ResourceSupport
public SampleResource extends ResourceSupport{
private String fieldA,
private String fieldB,
//setters and getters
}
Example of the class where HATEOS Links are declared
import org.springframework.hateoas.Identifiable;
import org.springframework.hateoas.Link;
public ResourceSupport implements Identifiable<Link>{
#JsonInclude(Include.NON_EMPTY)
private final List<Link> links = new ArrayList();
#JsonProperty("links")
public List<Link> getLinks() {
return this.links;
}
}
SPRING HATEOS version: spring-hateos0.24.0.RELEASE

Spring Data Rest - customize endpoint name

By default, Spring data REST use camelCase for endpoint names.
For example, if the repository is
#RepositoryRestResource(collectionResourceRel = "users", path = "users")
public interface UserRepository extends PagingAndSortingRepository<User, Integer> {
List<User> findByUsername(#Param("username") String username);
}
Then the endpoint is http://localhost:8080/users/search/findByUsername?username=test
How can I customize the endpoint so it use snake_case, became like this: http://localhost:8080/users/search/find_by_username?username=test
Or even different method name
change find_by_username : http://localhost:8080/users/search/by_username?username=test
(stripping the find_by_username): http://localhost:8080/users/search?username=test
Thanks
The #RestResource annotation also gives us the ability to customize the URL path mapped to a repository method and the link id in the JSON returned by the HATEOAS resource discovery.
To do that, we use the optional parameters of the annotation:
path for the URL path
rel for the link id
By executing a cUrl to http://localhost:8080/users/search/, we can now see our new method listed with other resources:
{
"_links": {
"findByUsername": {
"href": "http://localhost:8080/users/search/findByUsername{?username}"
},
"self": {
"href": "http://localhost:8080/users/search/"
}
}
}
So to customize the rest url endpoint, we can simply add the #RestResource annotation:
#RestResource(path = "byUsername", rel = "customFindMethod")
List<User> findByUsername(#Param("username") String username);
If we do the resource discovery again, the resulting JSON will confirm our changes:
{
"_links": {
"customFindMethod": {
"href": "http://localhost:8080/users/search/byUsername{?username}",
"templated": true
},
"self": {
"href": "http://localhost:8080/users/search/"
}
}
}
For more details you can check her

Override link path for subclasses in Spring Boot

I have a Spring Boot 2.x application backed by MongoDB. I'm trying to add some simple inheritance to my domain model along the lines of:
Parent: Person.java
// JSON annotations to assist in serializing requests to the right class
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "_class")
#JsonSubTypes({
#JsonSubTypes.Type(value = Buyer.class, name = "com.company.domain.Buyer"),
#JsonSubTypes.Type(value = Seller.class, name = "com.company.domain.Seller")
})
#Document(collection = "people")
public abstract class Person {
...
}
Subclass 1: Buyer.java
#Document(collection = "people")
public class Buyer extends Person {
...
}
Subclass 1: Seller.java
#Document(collection = "people")
public class Seller extends Person {
...
}
Essentially I would like Buyers and Sellers to be stored in the same Mongo collection and use the same REST path to operate upon them:
Repository: PeopleRepository.java
#RepositoryRestResource(path = "people", collectionResourceRel = "people")
public interface PeopleRepository extends MongoRepository<Person, String> {
}
This almost works except the HATEOAS links that come back look like:
{
_links: {
self: {
href: http://localhost/services/buyer/5b96c785ba3e18ac91aa8cc9
}
}
}
What I need is for the "buyer" in the href to instead become "people" so that it lines up with the repository endpoint above.
I have tried adding an annotation #ExposesResourceFor(Buyer.class) to the repository which didn't seem to change anything (and I would need another annotation for Seller.class but it's not possible to add two #ExposesResourceFor annotations). I was able to get the links to work by making a second repository just for Sellers:
#RepositoryRestResource(path = "people", collectionResourceRel = "people", export = false)
public interface SellerRepository extends MongoRepository<Seller, String> {
}
...but even with export set to false this seems to interfere with the other repository. There seems to be a 50/50 chance on whether the application will bind the endpoint to the SellerRepository or the PeopleRepository.
Is there anyway to set the resource path for subclasses here?
It looks like I was finally able to get this to work by adding additional repositories (which extend the base repository). It's important not to annotate these repositories.
#RepositoryRestResource(path = "people", collectionResourceRel = "people")
public interface PeopleRepository<T extends Person> extends MongoRepository<T, String> {
...
}
public interface SellerRepository extends PeopleRepository<Seller> { }
public interface BuyerRespository extends PeopleRespository<Buyer> { }

Resources