Spring REST repository shows wrong URL - spring

I developed sample application with Spring Boot. I've one abstract class (Employee) and two concrete subclasss for example full time and part time employee.
I preferred a joined type of inheritance and 3 table created by JPA provider.
Also I created REST repository for Employee. Looks like below:
package com.caysever.repository;
import com.caysever.model.Employee;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
/**
* Created by alican on 04.05.2017.
*/
#RepositoryRestResource(path = "employee")
public interface EmployeeRepository extends JpaRepository<Employee, Long>{
}
When I invoke the **/employee** URL in browser, I'm getting content as below:
{
"fullTimeEmployees" : [ {
"name" : "Alican",
"surname" : "Akkuş",
"birthDay" : "2017-05-04T12:37:20.189+0000",
"gender" : "MALE",
"totalWorkingHoursOfWeek" : 40,
"_links" : {
"self" : {
"href" : "http://localhost:8080/fullTimeEmployee/1"
},
"fullTimeEmployee" : {
"href" : "http://localhost:8080/fullTimeEmployee/1"
}
}
}
When I invoked this URL for first employee localhost:8080/fullTimeEmployee/1 , I getting the 404 status code, not found. But I will getting the first employee with this URL localhost:8080/employee/1.
You can see all codes at GitHub -> https://github.com/AlicanAkkus/jpa-inheritance-strategy
Why Spring REST generates the fullTimeEmployee URL?

I think that with #RepositoryRestResource you are modifying the export details, such as using /empoyee instead of the default value of /fullTimeEmployee
Try with
#RepositoryRestResource(collectionResourceRel = "fullTimeEmployees", path = "fullTimeEmployees")
Or if you want to use /employee
#RepositoryRestResource(collectionResourceRel = "employee", path = "employee")
The path is sets the segment under which this resource is to be exported and the collectionResourceRel sets the value to use when generating links to the collection resource.
Hope this helps

A workaround for this is to add repository interfaces for the concrete classes, sharing the path of the superclass repository.
#RepositoryRestResource(collectionResourceRel = "employee", path = "employee")
public interface FullTimeEmployeeRepository extends JpaRepository<FullTimeEmployee, Long> {
}
#RepositoryRestResource(collectionResourceRel = "employee", path = "employee")
public interface PartTimeEmployeeRepository extends JpaRepository<PartTimeEmployee, Long> {
}
This will generate the links with the "employee" path regardless of the subclass type.
"_links" : {
"self" : {
"href" : "http://localhost:8080/employee/1"
},
"fullTimeEmployee" : {
"href" : "http://localhost:8080/employee/1"
}
}
I don't know if there is another way to work around the issue.

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 .

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> { }

Spring Data Rest, SpringFox and JpaRepository custom finders

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!

How to disable or edit the spring-data-rest HATEOAS default profile link

I currently have an entity in my project named "profile" (Using JPA); however api/profile directs you to the application levels detail. I don't have an option to rename the object's repository using #RepositoryRestResource at this time. Is there a way in the configuration to disable or rename the application level deatils endpoint in Spring Data REST?
"profile" : [ {
"href" : "http://localhost:8080/api/profile{?page,size,sort}",
"templated" : true
}, {
"href" : "http://localhost:8080/api/profile"
} ],
Selecting the profile link directs me to
}, {
"rel" : "profile",
"href" : "http://localhost:8080/api/profile/profile"
},
Using spring 4.2.1 RELEASE, spring-data-jpa 1.9 & spring-data-rest-core-2.4.0 RELEASE
You can edit the root resource links with a ResourceProcessor<RepositoryLinksResource>
Like this :
#Component
public class RootResourceProcessor implements ResourceProcessor<RepositoryLinksResource> {
#Autowired
private final EntityLinks entityLinks;
#Override
public RepositoryLinksResource process(final RepositoryLinksResource resource) {
final Link link = entityLinks.linkToCollectionResource(YourResource.class);
resource.getLinks(); //the root links
return resource;
}
}

Spring Mongo Repository Polymorphism

How do I define Repository interface for polymorphic classes
Ex.
abstract class Source { public String name }
class InternalSource extends Source { public int internalId }
class ExternalSource extends Source { public String contact }
Now I know I cannot define a repository interface like
interface SourceRepo extends Repository<? extends Source, String>{....}
or
interface SourceRepo extends Repository<Source, String> { ....}
Is defining simple plain interface and have an implmentation class is the only way?
Well letting spring to associate mongo document to java class mapping through '_class' attribute would work fine.
Mongo document would like some like this
{_id : "xxx", name : "abc", internalId : 123, _class = "...InternalSource" }
{_id : "xxx", name : "abc", contact: "John doe", _class = "...ExternalSource"}

Resources