Spring Boot REST - how to POST/PATCH in many-to-many relationship - spring

I Have a problem with adding many-to-many relationship in my REST api.
Let' say we have two entities with many-to-many relationship - Employee and Task (employee have Set<Task> and task have Set<Employee>).
Some specific task is accessible via this endpoint:
http://localhost:8080/api/tasks/2
Tasks assigned to Employee with id 88 is accessible via:
http://localhost:8080/api/employees/88/tasks
The goal is to POST/PATCH this link to the endpoint.
Could you give me a hint, how this endpoint should look like in controller?
I tried something like this, but it's not working.
#PatchMapping("/{employeeId}/tasks")
public Task addTask(#RequestBody Task task, #PathVariable Long taskId) { ... }
Second problem - I would linke to use Postman. Could you tell me which Content-Type should I choose? And how should this link be formatted?
Looking forward for your answers!
EDIT
Do I have to add another constuctor that takes uri?

By definition the PATCH method applies a partial update to a given resource, while the PUT method is used to replace a given resource entirely. The keyword here is that both PATCH and PUT are specific to a given resource.
The POST method is used to create a new resource.
Therefore, it makes sense that if you want to update just a few fields in your resource and you don't need to update it entirely, to use the PATCH method instead of the PUT method.
The PATCH request body describes how the resource shall be updated, with a series of operations. One format that you can use to describe these operations is the JSON Patch.
Since the PATCH operation is specific to a given resource, and making use of the json-patch library, your controller method should be something like:
#PatchMapping("/{employeeId}/tasks/{taskId}")
public Task updateTask(#RequestBody JsonPatch taskPatch, #PathVariable Long employeeId, #PathVariable Long taskId) { ... }
Note that this a different thing than POST, with a different method (updateTask). For example, if you want to update one field from your task resource (given by taskId), your jsonPatch sent in the request body from your client (can be Postman) would be something like
[{
"op":"replace",
"path":"/field",
"value":"newValue"
}]
There are different operations, such as add, remove, replace, copy and test.
Now in your code you will need to apply this patch to the existing resource. This reference shows how can you do that:
https://www.baeldung.com/spring-rest-json-patch
I hope this helps.

Related

Best practice of creation GET methods with many parameters(filters)

I have the GET method in my Spring REST controller. This method returns the list of users by the filter.
I have a few ways to implement it:
Add #PathVariable like - /users/{type}/{age}/{name}/...(bad approach in this case)
Add #RequestParam like - /users?type=type,age=age,name=name...(usual approach in this case)
Use RequestDto (the best approach) like
public class UsersRequestDto {
private String type;
private int age;
private String name;
...
}
But I can not use GET method for this. I must use POST method with #RequestBody
And it breaks the rules. My method doesn't change state and doesn't create any entities. It workes as the GET method but in reality, it is POST.
And I have 2 ways:
Use the GET method with many parameters
Use the POST method with DTO which works as the GET method and confuses users.
Which way is better?
Short version: you might be looking for How to bind #RequestParam to object in Spring. (See also: https://stackoverflow.com/a/16942352/54734 )
On the web, we would have an html form with a GET action. When the form is submitted, the browser would process the input controls and create the application/x-www-form-urlencoded representation of the form data. For a GET action, that representation is used as the query string.
Using GET, and encoding all of the information into the query string, allows us to take advantage of general purpose caching of the results.
But the query parameters aren't accessible by themselves - they are actually embedded within the larger context of the HTTP request. We don't usually see that because, once again, general purpose components can do a lot of the heavy lifting.
So we don't see the parser that extracts the target-uri from the request, or the parser that splits the target URI into its separate components, or the parser that splits the query part into a sequence of key value pairs....
In general, what we do is ride the "general purpose" implementation as far as we can, then get off and do the rest of the work ourselves. If the framework offered no better support for object mapping, that could mean implementing that mapping ourselves.
So if our framework lacked the capability to map the query string directly to an object representation, we would hand roll that part of the implementation ourselves (either by copying each parameter "by hand", or writing our own reflection code to do the mapping automagically).
But it seems that Spring has that capability already built into it; and that it is the default option (no annotation required); you just have to be sure that the object implementation provides the interface that Spring needs to execute the mapping.
How many different parameters are you including in your query?
Personally, I prefer the option of a GET method with many different parameters. It has other benefits such as being cacheable as well. Also, compare it to something like a the URL that a Google search generates - lots of query string parameters.
The POST option feels dirty - it's a violation of what a POST should actually do (creating or updating a resource).
See these discussions: https://softwareengineering.stackexchange.com/questions/233164/how-do-searches-fit-into-a-restful-interface and REST API Best practices: Where to put parameters?
1st of all when you are using RequestParam then key will be added with & symbol not with comma(,) .
when you want to filter ( as you have mentioned) something then best approach would be to use RequestParam.
To minimize the code you can opt to "MultiValueMap" or "HttpservletRequest" .
1)HttpServletRequest
#GetMapping("/user")
public List<User> getFilteredUser(HttpServletRequest httpservlet)
httpservlet.getQuesryString() //will return all request param.
2)MultiValueMap
#RequestParam MultiValueMap<String,String> params
NOTE:- Generally POST is for create/update record.

Api Platform - returning data from external API

I'm currently using API Platform to display some data in Elasticsearch. This works fine, but I now have another feature that I'm looking at.
My application needs to deal with a 3rd-party API that needs to hit an endpoint and return some data.
Within my app, I'd like to be able to hit (/api/logistics/{action} - where action is an endpoint, such as login) and this then hits my app layer and returns data (3rd-party can be re-named)
The API calls to the 3rd party are fine, but I'm unsure how to display the response.
I've seen https://api-platform.com/docs/core/data-providers/ which looks like i can create a custom response.
Do i still need to create an entity/model and configure the #ApiResource() with a controller that uses my Data Provider?
If so, then what do i need to add in my annotation, since I won't have an id identifier
I'm fairly new to API Platform and I've not used the Data Provider functionality before
I will not be storing the data from the 3rd party API, just doing a HTTP call, retrieving the response and hopefully displaying it via Api Platform
Thanks
You are mosly right about the dataprovider. But as the docs page General Design Considerations states, "you have to write a plain old PHP object (POPO) representing the input and output of your endpoint. This is the class that is marked with the #ApiResource annotation. This class doesn't have to be mapped with Doctrine ORM, or any other persistence system."
So no, it does not need to be an Entity, but there must be a class marked with the #ApiResource annotation (but putting it in the Entity folder may help to make the #ApiResource() tag work - or adding the folder of your class in api/config/packages/api_platform.yaml).
For an item "get" endpoint your POPO needs an id. The poperty - or if there is only a getter, the getter - must be marked with the #ApiProperty(identifier=true) tag. Usually the easiest way to make one is by imploding/encoding some strings from the response of the external api call that together are unique for the response and will not change. Your dataprovider will have to explode/decode the id and use the components to make the external api call.
For a "post" operation you need a datapersister instead of a dataprovider. Apip will instatiate and populate your POPO and pass it to the datapersister and from there you can make the call to the external api and return an object as the result. If your object is not the same type of POPO you should specify "output"=TheOutputClass::class or put the operation on the output class and specify "input"=TheInputClass::class (replace TheOutputClass or TheInputClass by the actual class name)
For "put" and "patch" you need both a dataprovider, a datapersister and an id. They can have different input and output classes, see the docs about DTOs.
A collectionoperations with method "get" may seem convenient because you can just pass it any query string but your CollectionDataProvider must return an iterable.

What is the most ideal way to add validation to Spring REST Service Request Parameters

I have a Spring-REST service that has support for GET, POST, PUT requests and they all have been mapped in a #Controller (Sorry for stating the obvious, just new to the technology)
Now each method (RequestMapping) has its own parameters like one takes in id
other takes in name and third one takes in secretKey
I want to validate these request parameters in my own custom manner
Now tried looking up as many tutorials online as possible but did not come across any solution that would best serve my situation.
Here is what I mean:
I saw a tutorial for POST request parametes by using #RequestParam or #Valid but that does not work for GET requests (That's what I read)
I saw most people recommending JS303 but that does not suit my need as I need to validate the secretKey against the DB (id and name may be I can use JSR #Size but even the id and name would need further validation)
I also saw some recommending #Validator but that would mean I will need a validator class for each and and every parameter like IdValidator, NameValidator etc
Here is something that I am hoping to accomplish:
One Validator (Can be either something that implements Validator or COnstraintValidator) however its implementation should cater to validation of all kinds of requests (can definitely have multiple methods inside it based on what request its validating) and should throw a CustomException that I created
I am not posting what I have tried because it is actually too much code that I just copy pasted from what I searched online. If you want I can post the links that I copied the code from.
P.S. I am not an expert on Spring, but trying to learn

Using Spring Data REST to handle complex aggregate roots

Right now I can't get the concept behind Spring Data REST if it comes to complex aggregate roots. If I understand Domain Driven Design correctly (which is AFAIK the base principle for spring data?), you only expose aggregate roots through repositories.
Let's say I have two classes Post and Comment. Both are entities and Post has a #OneToMany List<Comment> comments.
Since Post is obviously the aggregate root I'd like to access it through a PostRepository. If I create #RepositoryRestResource public interface PostRepository extends CrudRepository<Post, Long> REST access to Post works fine.
Now comments is renderd inline and is not exposed as a sub resource like /posts/{post}/comments. This happens only if I introduce a CommentRepository (which I shouldn't do if I want to stick to DDD).
So how do you use Spring Data REST properly with complex domain objects? Let's say you have to check that all comments does not contain more than X characters alltogether. This would clearly be some invariant handled by the Post aggregate root. Where would you place the logic for Post.addComment()? How do you expose other classes as sub resources so I can access /posts/{post}/comments/{comment} without introducing unnecessary repositories?
For starters, if there is some constraint on Comment, then I would put that constraint in the constructor call. That way, you don't depend on any external validation frameworks or mechanisms to enforce your requirements. If you are driven to setter-based solutions (such as via Jackson), then you can ALSO put those constraints in the setter.
This way, Post doesn't have to worry about enforcing constraints on Comment.
Additionally, if you use Spring Data REST and only define a PostRepository, since the lifecycle of the comments are jointly linked to the aggregate root Post, the flow should be:
Get a Post and its collection of Comment objects.
Append your new Comment to the collection.
PUT the new Post and its updated collection of Comment objects to that resource.
Worried about collisions? That's what conditional operations are for, using standard HTTP headers. If you add a #Version based attribute to your Post domain object, then every time a given Post is updated with a new Comment, the version will increase.
When you GET the resource, Spring Data REST will include an E-Tag header.
That way, your PUT can be conditionalized with an HTTP If-Match: <etag> header. If someone else has updated the entity, you'll get back a 412 Status code, indicating you should refresh and try again.
NOTE: These conditional operations work for PUT, PATCH, and DELETE calls.

Spring Data-Rest POST to sub-resource

Lets say I have the following structure:
#Entity
class Person extends AbstractPersistable<Long> {
String name
String surname
}
#Entity
class Task extends AbstractPersistable<Long> {
String description
#ManyToOne
Person person
}
If I follow proper HAL guidelines I'm not supposed to expose entity id's. Since I don't have a bi-directional relationship I cant PUT or PATCH to http://localhost:8080/persons.
Even if I did create the relation, I probably wouldn't want to first POST the Task to /tasks and then PUT to /persons, (mobile clients are going to kill me). But even then I don't have the Task ID even from the returned Entity so I can PUT to the Person entity. (I obviously can string parse but I don't think it's appropriate).
I probably wouldnt want to have a list of 1000 tasks in the Person entity either. So not exporting the Task entity is not really an option (and this means PATCH will not work)
So how am I supposed to associate the Person with the Task if I cannot get his id? What is the correct approach?
If you want to associate a Task with a Person you need the link to the person.
Lets say the person URI is http://localhost/persons/1
Then you could assign the person to a task by just passing that URI in the person attribute.
So a post to Task could look like this:
{
"description": "some text",
"person": "http://localhost/persons/1"
}
Spring-data-rest will lookup the person and take care of the rest.
In HAL Links are used to reference related resources not ids and a HAL entity should always return a Link to itself, which serves as its unique identifier.
If I'm not mistaken you can also annotate fields with DBRef and links should be generated for you.
If you want related resources to actually show up inline with data you'll have to draft a Projection. See more here:
Spring Boot REST Resource not showing linked objects (sets)
Last but not least - if you want Projections to also contain Links you'll have to make ResourceProcessor's for them, see here:
How to add links to Spring Data REST projections?

Resources