Spring Data Rest and excerpt Projections - spring

I am using the inlineAddress sample of the Spring Data Rest documentation.
/persons return the address inline as expected.
Now I add a projection to the AddressRepository
#RepositoryRestResource(excerptProjection = AddressProjection.class)
Which is as below
#Projection(name = "AddressesProjection", types = Address.class)
public interface AddressProjection {
public String getStreet();
}
This is causing the /persons call to have an address projection as _embedded
{
"_embedded" : {
"persons" : [ {
"firstName" : "dfdf",
"lastName" : "2",
"addresses" : [ {
"street" : "tx",
"state" : "tx",
"country" : "dfd"
} ],
"_embedded" : {
"addresses" : [ {
"street" : "tx",
"_links" : {
"self" : {
"href" : "/api/addresses/1{?projection}",
"templated" : true
}
}
} ]
},
"_links" : {
"self" : {
"href" : " api/persons/1{?projection}",
"templated" : true
},
"addresses" : {
"href" : " /api/persons/1/addresses"
}
}
} ]
}
}
I dont know if this is expected. This behaviour is causing repeated information when I have a oneToMany relation like order/Comments and have projection on both order and comments and when I access order/1/comments I see the order also embedded for each comments.

I have a similar issue with spring-data-rest 2.5.6. So I'd like to add this.
If :
an A entity has a #OneToMany relationship to a B entity
the B entity's repository #RepositoryRestResource contains an excerptProjection
Then spring-data-rest will embed the B entity's list in any A entity (in _embedded).
If there is no excerptProjection, the list won't be embedded.
I'd like to be able to choose what I want to be embedded, but at moment, I found no solution to do so.

For anybody that is looking for the answer to this issue, I actually did find a solution.
Based on the example in Spring-RestBucks you will need to have a custom RepresentationModelProcessor on A entity.
Also consider the official Spring HATEOAS Documentation on RepresentationModelProcessor.
Applying to the above example, you would do:
public class PersonRepresentationProcessor implements RepresentationModelProcessor<EntityModel<Person>> {
private final EntityLinks entityLinks;
#Override
public EntityModel<Person> process(EntityModel<Person> personModel) {
// create new EntityModel without the embedded collection
TypedEntityLinks<Person> typedPersonLink = entityLinks.forType(Person::getId);
EntityModel<Person> newPerson = new EntityModel<>(personModel.getContent(),
personModel.getLinks());
// add more links or other modifications
return newPerson;
}
}

Related

How to create an EntityModel for type with nested resources using Spring HATEOAS?

I have an address entity model, EntityModel<Address>:
{
"id" : 10662,
"streetNumber" : 4823,
"steetName" : "Bakersfield",
"city" : "anytown",
"state" : "anystate",
"zip" : "12345",
"_links" : {
"self" : {
"href" : "http://dev:9001/api/addresses/10662"
},
"address" : {
"href" : "http://dev:9001/api/addresses/10662"
}
}
}
I have a Java client that gets this address and assigns it to a new student:
EntityModel<Address> address = client.getAddress(10662);
Student student = new Student();
student.setAddress(address.getContent());
//set other student props
I would like to have the client then send a RESTful POST request to the server to save the student. But how can I create a EntityModel with a link to the address?
I know how to set links at the Student level
EntityModel<Student> studentEntity = EntityModel.of(
student,
WebMvcLinkBuilder.linkTo(student.getId()).withSelfRel()
);
but not sure how to set a link to the student's address

Naming a Query Methods for MongoRepository with multiple conditions in SpringBoot

According to the documentation, I'm trying to get a working name for a method of a Spring MongoRepository that have to meet two different conditions. In plain words, the query sounds like: "look up for a determinate object by his id and it is one of mine or I want to share it"
The object:
{
"_id" : ObjectId("5c497..."),
"owner" : "myUserId",
"shared" : false,
}
The query:
db.getCollection('collection').find({$or : [
{ $and : [ { '_id' : ObjectId("5c497...") }, { 'owner' : 'myUserId' } ] },
{ $and : [ { '_id' : ObjectId("5c497...") }, { 'shared' : true } ] }
]})
I solved the problem with #Query
#Query("{\$or: [{\$and: [{'_id' : ?0}, {'owner' : ?1}]}, {\$and: [{'_id' : ?0}, {'shared' : true}]} ]}")
fun findIfAvailable(id: ObjectId, owner: String, shared: Boolean = true): Mono<MyObj>
But now, I wondered if it's possible to writing a method name for simplify the code ( and learn how use it ).
I tried without success findByIdAndOwnerOrShared, findByIdAndOwnerOrIdAndShared and so on
Thank you

Spring mongodb Find document if a single field matches in a list within a document

I have some data stored as
{
"_id" : ObjectId("abc"),
"_class" : "com.xxx.Team",
"name" : "Team 1",
"members" : [
{"userId" : 1, "email" : "a#x.com" },
{"userId" : 2, "email" : "b#x.com" },
]
}
{
"_id" : ObjectId("xyz"),
"_class" : "com.xxx.Team",
"name" : "Team 2",
"members" : [
{"userId" : 2, "email" : "b#x.com" },
{"userId" : 3, "email" : "c#x.com" }
]
}
I have 2 POJO classes Team (mapped to entire document),TeamMember (mapped to members inside a document).
Now I want to find to which team a specific user belongs to. For example if I search for a#x.com it should return me the document for Team 1. Similarly searching for b#x.com should return both of them as its in both the documents.
As I am very new to spring, not able to find out how to solve this.
Note: I am using MongoTemplate
somthing like this will do
final QueryBuilder queryBuilder = QueryBuilder.start();
//queryBuilder.and("members.email").is("a#x.com") This will work as well. try it out.
queryBuilder.and("members.email").in(Arrays.asList("a#x.com"))
final BasicDBObject projection = new BasicDBObject();
projection.put("fieldRequired", 1);
try (final DBCursor cursor = mongoTemplate.getCollection(collectionName).find(queryBuilder.get(), projection)
.batchSize(this.readBatchSize)) {
while (cursor.hasNext()) {
DBObject next = cursor.next();
........
// read the fields using next.get("field")
.........
}
}
batchsize and projection is not mandatory. Use projection if you don't want to fetch the whole document. You can specify which field in the document you want to fetch in the result.
You can use below code with the MongoTemplate
Query findQuery = new Query();
Criteria findCriteria = Criteria.where("members.email").is("b#x.com");
findQuery.addCriteria(findCriteria);
List<Team> teams = mongoTemplate.find(findQuery, Team.class);

Pagingandsortingrepositrory in spring data , primary key in the entity is not present in the response

I have declared pagingandSortingRepository for a JPA entity and the response to findall() is below
"_embedded" : {
"assetDashboardCustomers" : [ {
"utilization" : "80",
"_links" : {
"self" : {
"href" : "http://localhost:8080/utilbycustomer/Customer01"
},
"assetDashboardCustomer" : {
"href" : "http://localhost:8080/utilbycustomer/Customer01"
}
}
},
I do not want _links and self [HATEOAS] details, but just the plain JSON.
What is the property I should set so that I get a plain JSON in the response
The first comment answers your first question - you cannot turn off
the links. The second comment answers you 2nd question which is how to
have the ID in the JSON body.
--- By Alan hay in the comments

Accessing and updating association resources with Spring Data REST and MongoDB

I don't fully understand how creating and accessing association resources works with Spring Data REST and MongoDB. I have noticed a few strange behaviours, and I am hoping to get some clarification here.
I have a simple object model where one resource type (Request) contains a list of objects of another resource type (Point). Both resources are top-level resources, i.e. have their own Repository interfaces.
I would like to be able to add a new Point to a Request by POSTing to /requests/{id}/points with a link to an existing point (as I know from this question that I can't just POST a point as a JSON payload).
Furthermore, I would like to be able to GET /requests/{id}/points to retrieve all the points associated with a request.
Here is my object model (getters/setters omitted):
Request.java
public class Request {
#Id
private String id;
private String name;
#DBRef
private List<Point> points = new ArrayList<>();
}
RequestRepository.java
public interface RequestRepository extends MongoRepository<Request, String> {
}
Point.java
#Data
public class Point {
#Id
private String id;
private String name;
}
PointRepository.java
public interface PointRepository extends MongoRepository<Point, String> {
}
Let's say that I POST a new Request. Then doing a GET on /requests gives me this:
{
"_links" : {
"self" : {
"href" : "http://localhost:8080/requests",
"templated" : false
}
},
"_embedded" : {
"requests" : [ {
"name" : "request 1",
"_links" : {
"self" : {
"href" : "http://localhost:8080/requests/55e5dc47b7605d55c7c361ba",
"templated" : false
},
"request" : {
"href" : "http://localhost:8080/requests/55e5dc47b7605d55c7c361ba",
"templated" : false
},
"points" : {
"href" : "http://localhost:8080/requests/55e5dc47b7605d55c7c361ba/points",
"templated" : false
}
}
} ]
}
}
So I have a points link in there. Good. Now, if I do a GET on /requests/55e5dc47b7605d55c7c361ba, I notice strange thing #1. There is no points link anymore:
{
"name" : "request 1",
"_links" : {
"self" : {
"href" : "http://localhost:8080/requests/55e5dc47b7605d55c7c361ba",
"templated" : false
},
"request" : {
"href" : "http://localhost:8080/requests/55e5dc47b7605d55c7c361ba",
"templated" : false
}
}
}
But anyway forget about that for now, let's POST a point (which gets ID 55e5e225b76012c2e08f1e3e) and link it to the request:
curl -X PUT -H "ContentType: text/uri-list" http://localhost:8080/requests/55e5dc47b7605d55c7c361ba/points
-d "http://localhost:8080/points/55e5e225b76012c2e08f1e3e"
204 No Content
That seemed to work. Ok, so now if I do a GET on http://localhost:8080/requests/55e5dc47b7605d55c7c361ba/points, I expect to see the point I just added, right? Enter strange thing #2, I get the following response:
{
"_links" : {
"self" : {
"href" : "http://localhost:8080/requests/55e5dc47b7605d55c7c361ba/points",
"templated" : false
}
}
}
So at this point I realise I've done something wrong. Could somebody please explain to me what's happening here? Do I have a fundamental misunderstanding of the way SDR is working? Should I even be using DBRefs? Or is this simply not possible with MongoDB?
Thanks in advance, and sorry for the long question!
Edit
The Points are in fact being added as DBRefs (verified by looking at the db manually), but it appears that SDR is not giving them back. If I explicitly ask for http://localhost:8080/requests/55e5dc47b7605d55c7c361ba/points/55e5e225b76012c2e08f1e3e, then I get the following response:
{
"_links" : {
"self" : {
"href" : "http://localhost:8080/points/55e5e225b76012c2e08f1e3e",
"templated" : false
},
"point" : {
"href" : "http://localhost:8080/points/55e5e225b76012c2e08f1e3e",
"templated" : false
}
}
}
Which is also strange... where is my Point.name?
Both aspects are caused by a glitch in Spring Framework 4.2.0, which Spring Boot 1.3 M4 has upgraded to which causes Spring Data RESTs custom serializers not applied in some cases. This is already fixed in Spring 4.1.2. To upgrade your Gradle project to that version, upgrade to the Boot plugin to a 1.3 and then fix the Spring version to 4.2.1 using the following declaration:
ext['spring.version'] = '4.2.1.RELEASE'
Make sure you properly initialize the collection with an empty one as a null value will cause 404 Not Found being returned. An empty list will produce correct HAL output.
Generally speaking association resources should be used with already existing resources, esp. with Spring Data MongoDB there's no other choice as it doesn't have persistence by reachability. I.e. you'd need to persist the related instance first (aka. POST to it's collection resource and get a Location header back) and the assign the just created resource (aka. PUT or POST the just obtained Location to the association resource), which seems to be what you're doing.

Resources