I'm trying to get my head around this RESTful scenario in Jersey: I have two resources, User and Item, and i want to retrieve all the items connected to a certain user and all the users connected to a certain item. In short, there exists a many-to-many relationship between User and Item.
I came across this RESTful design:
All the items connected to a certain user: GET .../users/{user_id}/items
All the users connected to a certain item: GET .../items/{item_id}/users
How can I implement this in Jersey? I found this solution, but it's related to sub-resources nested in the root resource, whereas in my case User and Item are both root resources accessible via their own URIs.
The following should work.
#Path("users")
public class UserResource {
#GET
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
#Path("{user_id}/items")
public Response getItems(#PathParam("user_id") String userId) {
//get the items and return a response
}
}
#Path("items")
public class ItemResource {
#GET
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
#Path("{item_id}/users")
public Response getUsers(#PathParam("item_id") String itemId) {
//get the users and return a response
}
}
I decided to implement the solution suggested here, that is to create specific resources that represent the relantionships described above.
The code that models the items-related-to-users relationship is this:
#Path("users/{userId}/items")
public class RelatedItemResource {
#GET
#Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
public List<Item> getRelatedItems(#PathParam("userId") String userId) {
// returns list of related items
}
// Other methods
}
The code that models the users-related-to-items relationship is this:
#Path("items/{itemId}/users")
public class RelatedUserResource {
#GET
#Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
public List<User> getRelatedUsers(#PathParam("itemId") String itemId) {
// returns the list of related users
}
// Other methods
}
For all users connected to a certain item, this sounds more like a search criteria to get a list of users. So i would suggest this path : /users?item={itemid}. Given this notation you can really build a flexible search criteria endpoint where every query parameter is optional and is respected if some value is given in it.
Related
I am developing a REST based application using the Java & Quarkus. I am using the annotations from OpenAPI to create the swagger-ui. I have multiple resource file within my application. I would like to order them as per my choice also I would like to order the api endpoints within each of the resource files as per my choice. How can I achieve this?
I saw some examples examples related to it, but they are based on the Spring, so I am a bit confused about how to achieve the same thing using the Quarkus.
For example, I have created 2 resource files. Lets say the extensions endpoints are appearing on top of the fruits in the Swagger-UI. I need to ensure the fruits appear first then the extensions. Also the endpoints within the extensions and fruits should appear in the described order then how can I achieve this?
#Path("/api")
#Tag(
name = "Fuits Controller",
description = "Fruits Information")
public class FruitResource {
#GET
#Path("/get/fruits")
public String list() {
return fruits;
}
#POST
#Path("/post/fruits")
public String list() {
return fruits;
}
}
#Path("/api")
#Tag(
name = "Extensions Controller",
description = "Extensions Information")
public interface ExtensionResource {
#POST
#Path("/post/extensions")
public String list() {
return extension;
}
#GET
#Path("/get/extension")
public String list() {
return extension;
}
#POST
#Path("/post/extension")
public String list() {
return extension;
}
}
I have a requirement to implement an HTTP PATCH method in a Spring MVC application. I followed this tutorial: https://www.baeldung.com/http-put-patch-difference-spring.
This is the piece of code:
#RequestMapping(value = "/heavyresource/{id}", method = RequestMethod.PATCH, consumes = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<?> partialUpdateGeneric(
#RequestBody Map<String, Object> updates,
#PathVariable("id") String id) {
heavyResourceRepository.save(updates, id);
return ResponseEntity.ok("resource updated");
}
The problem is that my repository (JPARepository) does not have a method "save" where I can pass a map and an id.
I tried this implementation on my own:
#PatchMapping("/heavyresource/{id}")
public Beer patchUpdate(#RequestBody HeavyResource heavyResource) {
return heavyResourceRepository.save(heavyResource);
}
But it does not work properly because if I pass only one property (that's the point in PATCH) it let's all the others properties as null and I need to update only the property that was passed. Even thinking in DTOs I was not able to implement.
Thanks!
In my project, I have a need to filter out fields based on roles. Unfortunately it is not working for me.
Application.Java
environment.jersey().getResourceConfig()
.packages("com.test")
.register(SecurityEntityFilteringFeature.class).register(RolesAllowedDynamicFeature.class)
.register(JacksonFeature.class);
Entity that needs to be filtered
#RolesAllowed("edit")
public List<Participant> getParticipants() {
return participants;
}
Resource
#Api("test api")
#Path("/api/v1/test")
#Produces(MediaType.APPLICATION_JSON)
#Slf4j
#RolesAllowed("participate")
public class TestResource {
#GET
#Path("/{id}")
#ApiOperation(value = "Retrieve by Id", response = TestResponse.class)
#UnitOfWork
public Response getById(#PathParam("id") String testtId) {
return testService.findById(testId));
}
}
My logged in user is having 2 permissions (edit, participate) and it works well at resource level. But it is not working at entity field level. At field level the logged in role is not considered and it is always filtering that participant field.
I have a web service that looks like:
#Path("/ws")
public class Ws {
#GET public Record getOne(#QueryParam("id") Integer id) { return record(id); }
#GET public List<Record> getAll() { return allRecords(); }
}
The idea is that I can either call:
http://ws:8080/ws?id=1 to get a specific record
http://ws:8080/ws to get all available records
However when I use the second URL, the first #GET method is called with a null id.
Is there a way to achieve what I want without using different paths?
I think this can be achieved with Spring using the #RequestMapping(params={"id"}) and #RequestMapping annotations for the first and second methods respectively but I can't use Spring in that project.
Since the path is the same, you cannot map it to a different method. If you change the path using REST style mapping
#Path("/ws")
public class Ws {
#GET #Path("/{id}") public Response getOne(#PathParam("id") Integer id) { return Response.status(200).entity(record(id)).build(); }
#GET public Response getAll() { return Response.status(200).entity(allRecords()).build(); }
then you should use:
http://ws:8080/ws/1 to get a specific record
http://ws:8080/ws to get all available records
I am kind of new to RESTful web services and need some help. I have service which will return a list of products. The URL looks like the following:
/example/product/6666,6667?expand=sellers&storeIds=2,1
To define this service I have this interface:
#Path("/example")
public interface Service {
#GET
#Path("/products/{pIds}")
#Produces( "application/json" )
public ServiceResponse<ProductsList> getProducts(
#PathParam("pIds") String productsIds,
#QueryParam("expand") String expand,
#QueryParam("storeIds") String storeIds) throws Exception;
}
I am assuming here that I am getting the productsIds as a string and that I will need to manually split this string into a list of ids, with delimiter as a comma.
Is there a way to instead get the parameters as list, instead of doing manually it from my side? Or is there a library that I can use to do this in an automated way?
Thanks
You can deserialize the product id's directly into a list with some minor change to your service definition. Try this instead:
#Path("/example")
public interface Service {
#GET
#Path("/products/{pIds}")
#Produces( "application/json" )
public ServiceResponse<ProductsList> getProducts(
#PathParam("pIds") List<String> productsIds,
#QueryParam("expand") String expand,
#QueryParam("storeIds") String storeIds) throws Exception;
}
Change String productsIds to List<String> productsIds.
On a side note, I would recommend passing in the product id's as a query parameter. Your URI should identify a unique resource (in this case products) and it should be stateless.