How to add HATEOAS links in a sub resource - spring

I have a parent resource called the AdminResource and a child resource called the AdminModuleResource.
The resource of the parent is correctly fitted with HATEOAS links:
{
"firstname" : "Stephane",
"lastname" : "Eybert",
"email" : "mittiprovence#yahoo.se",
"password" : "e41de4c55873f9c000f4cdaac6efd3aa",
"passwordSalt" : "7bc7bf5f94fef7c7106afe5c3a40a2",
"links" : [ {
"rel" : "self",
"href" : "http://localhost/admins/3683"
}, {
"rel" : "modules",
"href" : "http://localhost/admins/3683/modules"
} ],
"id" : 3683
}
The resource of the child is also correctly fitted with HATEOAS links:
{
"module" : "BTS",
"adminResource" : {
"firstname" : "Stephane",
"lastname" : "Eybert",
"email" : "mittiprovence#yahoo.se",
"password" : "e41de4c55873f9c000f4cdaac6efd3aa",
"passwordSalt" : "7bc7bf5f94fef7c7106afe5c3a40a2",
"links" : [ ],
"id" : 3683
},
"links" : [ {
"rel" : "self",
"href" : "http://localhost/modules"
} ],
"id" : 1087
}
But its parent resource has lost its links.
For now, when inside my child admin module resource, the parent admin resource does not have its links. Indeed the toResource method of the assembler only provides the links for the child admin module resource.
public AdminModuleResource toResource(AdminModule adminModule) {
AdminModuleResource adminModuleResource = new AdminModuleResource();
adminModuleResource.fromAdminModule(adminModule);
adminModuleResource.add(linkTo(AdminModuleController.class).slash(adminModuleResource.getId()).withSelfRel());
return adminModuleResource;
}
public AdminResource toResource(Admin admin) {
AdminResource adminResource = createResourceWithId(admin.getId(), admin);
adminResource.fromAdmin(admin);
adminResource.add(linkTo(AdminController.class).slash(admin.getId()).slash(UriMappingConstants.MODULES).withRel(UriMappingConstants.MODULES));
return adminResource;
}
Any idea how I can add the links to the parent admin resource even when inside the child admin module resource ?
EDIT: Here is how I build the resources:
public void fromAdminModule(AdminModule adminModule) {
this.setResourceId(adminModule.getId());
this.setModule(adminModule.getModule());
AdminResource adminResource = new AdminResource();
adminResource.fromAdmin(adminModule.getAdmin());
this.adminResource = adminResource;
}
public void fromAdmin(Admin admin) {
this.setResourceId(admin.getId());
this.setFirstname(admin.getFirstname());
this.setLastname(admin.getLastname());
this.setEmail(admin.getEmail().toString());
this.setPassword(admin.getPassword());
}
Thanks !
Stephane

Just stumbled on this question, even though it is quite old, but may be worth answering for others implementing similar functionality. Basically, you create embedded resources for AdminModuleResource on your AdminResource and build the links for these embedded resources within your AdminResourceAssembler. The code below is a simplified version of what is posted on this answer
On AdminResource add:
#JsonUnwrapped
private Resources<EmbeddedWrapper> embeddeds;
// + setters/getters
On AdminResourceAssembler add:
EmbeddedWrappers wrapper = new EmbeddedWrappers(true);
List<EmbeddedWrapper> wrappers = (List<EmbeddedWrapper>) super.buildEmbeddables(entity);
Set<AdminModuleResource> moduleResources = adminResource.getModuleResources( );
if(!moduleResources.isEmpty( ))
wrappers.add(wrapper.wrap(adminModuleResourceAssembler.toResources(moduleResources)));
adminResource.setEmbeddeds(new Resources<>(wrappers));

Related

What is the best practice to store a specification of an Entity in Spring?

My database contain products in a single table called Product, and each product might have certain fields with their specification, e.g.
//Bicycle product
{
"name" : "Bicycle",
"category" : "Bicycles"
"specification" : {
"color" : "red"
}
}
//Wheel product
{
"name" : "Wheel",
"category" : "Spare Parts",
"specification" : {
"diameter" : "7.5"
}
}
So i've come up with idea of making a field of type Map<String, String> (which creates a another table called specifications) in my Product entity to contain those specifications. But i don't like this approach, because all of the additional fields would be of String type, and because Spring will create a bean out of this field, I wont be able to specify the type of value as an abstract class (like this Map<String, Object>).
I thought of creating additional fields, like this:
#ElementCollection
private Map<String, String> string_features;
#ElementCollection
private Map<String, Double> double_features;
// ...
But it looks kind of ugly and I think there is a better way to do it. And also, if the specification field is of a different Entity type, I will have to create another map for that specific entity, e.g.
//Bicycle product
{
"name" : "Bicycle",
"category" : "Bicycles"
"specification" : {
"color" : "red",
"wheel" : {
"name" : "Wheel",
}
}
}
If the value can be only be numbers and strings, maybe you can save the value as strings and then use a regex to check if the string is a number before returning the value.
Otherwise, you need a way to recognize the type.
I think I would change it to this:
//Bicycle product
{
"name" : "Bicycle",
"category" : "Bicycles"
"specifications" : [
{ name: "color", value: "red", type: "string"},
{ name: "diameter", value: "7.5", type: "double"},
...
]
}
You can map it as:
#ElementCollection
private List<Specification> specifications;
...
#Embaddable
class Specification {
String name;
String value;
String type;
// ... getter/setter and everything else
#Transient
Object getConvertedValue() {
if ("double".equals(type)) {
return Double.parse(value);
}
// String is the default
return value;
}
}
The nice thing is that you can have as many types as you want.

Spring - How to correctly show relationship data in view (table) using REST api

I have a User (id, username) entity which has many-to-many relationship with Roles (id, name) entity. I am trying to show the User data in a ajax Datatable. Now, if a User has six roles, it shows six [object Object] for all six roles. I dont know how to correctly show the role name instead of object Object.
This is what I have:
.DataTable(
{
"pagingType" : "full_numbers",
"sAjaxSource" : "/api/AppUser/all",
"sAjaxDataProp" : "",
"aoColumns" : [
{
"data" : "id"
},
{
"data" : "username"
},
{
"data" : "userenabled"
},
{
"data" : "useremail"
},
{
"data" : "userfirstname"
},
{
"data" : "userlastname"
},
{
"data" : "useraddress"
},
{
"data" : "roles"
}
This is how it looks like in Data Table:
Here is my REST Controller piece:
#RestController
#RequestMapping("/api/AppUser")
public class AppUserRestAPIs {
#GetMapping(value = "/all", produces = "application/json")
public List<AppUser> getResource() {
return appUserJPARepository.findAll();
}
}
I know it must be trivial but feeling lost and could not find a single example on how to represent relationship data in view (html) using REST api. Searched almost everywhere. What I am missing here? Will appreciate any pointers here.
Answering my own question:
Found it ! Here - https://editor.datatables.net/examples/advanced/joinArray.html
So instead of:
{
"data" : "roles"
}
I have to use:
{
"data" : "roles",
render : "[, ].name"
}
All worked perfectly but now I am clueless what if I don't use Datatable. Not sure if I have to put another question for it.
Use function to flat roles list:
Instead of
{
"data" : "roles"
}
Try this :
{
"data": null,
"render": function(data, type, row, meta) {
var flatrole = '';
//loop through all the roles to build output string
for (var role in data.roles) {
flatrole = flatrole + role.name + " ";
}
return flatrole;
}
}

Unwrapping a Kotlin hashmap in Thymeleaf inside of Spring Boot 2

I have a Kotlin function which creates a model with a hashmap as shown below
#GetMapping("/")
fun index(model: Model): Mono<String> {
model.addAttribute("images", imageService.findAllImages()?.flatMap { image ->
Mono.just(image)
.zipWith(repository.findByImageId(image?.id!!).collectList())
.map({ imageAndComments: Tuple2<Image?, MutableList<learningspringboot.images.Comment>> ->
hashMapOf<String, Any?>(
"id" to imageAndComments.t1?.id,
"name" to imageAndComments.t1?.name,
"comments" to imageAndComments.t2)
}).log("findAllImages")
})
model.addAttribute("extra", "DevTools can also detech code changes.")
return Mono.just("index")
}
Image.kt
package learningspringboot.images
import org.springframework.data.annotation.Id
data class Image(#Id var id: String? = null, var name: String? = null)
Comment.kt
package learningspringboot.images
import org.springframework.data.annotation.Id
data class Comment #JvmOverloads constructor(#Id private var id: String? = null, private var imageId: String? = null, private var comment: String? = null) {
}
In my Thymeleaf template I have
<ul><li th:each = "Comment :${image.comments}" th:text = "${image.comments}"></li></ul>
Which gives me this lines like
[Comment(id=5a623d5d2298352bc4929866, imageId=0d46b575-b6ce-48e2-988a-ebe62ebc2ceb, comment=test), Comment(id=5a623d8b2298352bc4929867, imageId=0d46b575-b6ce-48e2-988a-ebe62ebc2ceb, comment=test23)]
Which shows the comment record as is with the MongoDB keys/ids and everything else. This is not what I want.
I also have this in my Thymeleaf template
<ul><li th:each = "Comment :${image.comments}" th:text = "${comment == null ? 'empty' : comment.Comment}"></li></ul>
Which shows the word empty for each comment record.
The comment record in the database looks like
{ "_id" : ObjectId("5a623d5d2298352bc4929866"), "imageId" : "0d46b575-b6ce-48e2-988a-ebe62ebc2ceb", "comment" : "test", "_class" : "learningspringboot.comments.Comment" }
The image records in the database looks like
{ "_id" : ObjectId("5a623d5d2298352bc4929866"), "imageId" : "0d46b575-b6ce-48e2-988a-ebe62ebc2ceb", "comment" : "test", "_class" : "learningspringboot.comments.Comment" }
{ "_id" : ObjectId("5a623d8b2298352bc4929867"), "imageId" : "0d46b575-b6ce-48e2-988a-ebe62ebc2ceb", "comment" : "test23", "_class" : "learningspringboot.comments.Comment" }
How can I unwrap the comments records so that I only see the "comment" values and not the "_id" or "imageId" values?
The problem is that I have a hashmap<string>,arraylist<image>.
So I simply need to loop through all the image elements in the array list using thymeleaf. I'm pretty sure that this has been done before, I'll just need to find a good example and do some reading.
I was able to use the following code
<th:block th:each="Comment : ${image.comments}">
<ul th:each="comment : ${Comment}">
<li th:text="${comment.comment}"></li>
</ul>
</th:block>
And now I can move on.

How to add html5smartimage inside a custom widget?(AEM 5.6.1)

I am trying to add a html5smartimage in a multifield using ExtJS. The label shows up but I am not able to add images. The code I am using is as below.
this.image=new CQ.html5.form.SmartImage({
fieldLabel : "Image",
allowBlank : false,
allowUpload:"{Boolean}false",
border:"{Boolean}true",
cropParameter:"",
ddGroups:"[media]",
disableInfo:"{Boolean}true",
disableZoom:"{Boolean}true",
fileNameParameter:"",
fileReferenceParameter:"./image/fileReference",
height:"{Long}500",
mapParameter:"",
name:"file",
requestSuffix:"",
rotateParameter:"",
title:"Image",
listeners : {
change : {
scope : this,
fn : this.updateHidden
},
dialogclose : {
scope : this,
fn : this.updateHidden
}
}
});
Please suggest a solution.

how to use mongodb query option in monk nodejs

I have a collection name projects and I am trying to retrieve everything except its url like this query
db.projects.find({name:"arisha"},{url:0}).pretty()
This query is working perfectly and returning everything except url but my question is how to achieve this in
Node module for MongoDB name monk.
I am using this code but its not working and returning every field:
var projs = db.get('projects');
projs.find({creator : req.session.user._id},{url:0}, function (err,data) {
console.log(data);
if(!err) {
res.locals.projs = data;
console.log(data);
res.render("projects.ejs",{title: "Projects | Bridge"});
}
});
I did not get where the problem is, please help and thanks in advance :)
Sample document:
{
"name" : "arisha",
"date" : {
"day" : 18,
"month" : 4,
"year" : 2015
},
"creator" : "552edb6f8617322203701ad1",
"url" : "EyjPdYoW",
"members" : [
"552edb6f8617322203701ad1"
],
"_id" : ObjectId("5532994ba8ffdca31258bd1a")
}
To exclude the url field in monk, try the following syntax:
var db = require('monk')('localhost/mydb');
var projs = db.get('projects');
projs.find({ creator : req.session.user._id }, "-url", function (err, data) {
// exclude url field
});
EDIT:
To exclude multiple fields, use the following projection syntax:
projs.find({ creator : req.session.user._id }, { fields: { url: 0, creator: 0 } }, function(err, data) {
// exclude the fields url and creator
});
Alternatively (as you had discovered), you could also do:
projs.find({ creator : req.session.user._id }, "-url -creator", function (err, data) {
// exclude url and creator fields
});

Resources